C语言,排序

news2024/11/25 9:33:17

前言

        排序,可以说是数据结构中必不可缺的一环。我们创造数据存储它,要想知道数据之间的联系,比较是必不可少的。不然,费劲心思得来的数据若是不能有更多的意义,那么拿到了又有什么用?

        排序是计算机内经常进行的一种操作,其目的是将一组“无序”的记录序列调整为“有序”的记录序列。分为内部排序和外部排序,若整个排序过程不需要访问外存便能完成,则称此类排序问题为内部排序。反之,若参加排序的记录数量很大,整个序列的排序过程不可能在内存中完成,则称此类排序问题为外部排序。内部排序的过程是一个逐步扩大记录的有序序列长度的过程。

一. 排序的种类

        快速排序、希尔排序、堆排序、直接选择排序不是稳定的排序算法,而基数排序、冒泡排序、直接插入排序、折半插入排序、归并排序是稳定的排序算法。

        稳定排序:假设在待排序的文件中,存在两个或两个以上的记录具有相同的关键字,在用某种排序法排序后,若这些相同关键字的元素的相对次序仍然不变,则这种排序方法是稳定的。其中冒泡,插入,基数,归并属于稳定排序,选择,快速,希尔,归属于不稳定排序。

        具体为什么这些排序是稳定排序,或者是不稳定排序,会在之后进行图片演示。

        根据上述介绍可以知道,总共的排序分为8种,接下来我会挑出重要的和大家讲解。

二. 排序的实现

        一下排序都是按照排升序的方式进行的,请读者注意。如果想要让排序的功能更加丰富,推荐读者像qsort这样的标准函数一样,传入比较的函数指针,由于只是讲解比较原理,故简化了。

1. 插入排序

1.1. 原理

图1-1 插入排序原理图

        如图1-1所示,开始的时候数据的储存入第A行所示,我们需要将从第二个数据的位置开始,向前一次插入数据,将小的放在前面。从目标位置向前遍历的时候,如果目标数据小于比较数据,就将比较的数据向后移动一格。因此我们需要提前记录目标位置的值。

        从A行的第二列开始,2小于5,就将5向后移动一位,到顶了,就将2赋给第一列。同理,到了第三列的4,4小于5,就将5向后挪动一列,再继续比较,4大于2,就不需要挪动2,将4赋给第二列。按照上述规律,依次插入,直到到第E行,将第六列的3插入到第三列为止。

1.2. 代码

// 插入排序
void InsertSort(int* a, int n)
{
    // a, 不能为空
    assert(a);

    for(int i = 0; i < n - 1; ++i)
    {
        // 一次插入排序
        int end = i;
        int tmp = a[end + 1];
        while(end >= 0)
        {
            if(tmp < a[end]) // 升序中, 小于插入的值就挪动数据
            {
                a[end + 1] = a[end];
                end--;
            }
            else    // 反之跳出循环赋值
            {
                break;
            }
        }
        a[end + 1] = tmp;
    }
}

2. 希尔排序

2.1. 介绍

        希尔希尔(Shell's Sort)是插入排序的一种又称“缩小增量排序”(Diminishing Increment Sort),是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法。该方法因 D.L.Shell 于 1959 年提出而得名。

        希尔排序是一种比较厉害的排序,虽然没有稳定性,但是排序的时间复杂度是小于O(N^2)的。所以,希尔排序是一种很厉害的排序。快的表现和快速排序、堆排序、归并排序是一个等级的。

2.2. 原理

图2-1 希尔排序原理图

        如果插入排序是希尔排序的一种特殊情况,插入排序相当于希尔排序增量为1的时候的排序。那么为什么诞生的希尔排序有它的意义呢?从第一趟增量为5的时候,相当于给数据分成了5个部分,大大减小了排序的范围。在第二趟的排序中,会造成的效果就是,交换的时候每次增量不超过上一次排序的增量也就是5。因此提高了效率。

        在性能上,希尔排序的时间复杂度区间在O(N^(3/2))到O(N*logN)之间,不需要大量的辅助空间,因此数据排序在中等规模中表现良好。但是对于规模非常大的数据时不是最佳选择,数据量大的时候仍然推荐快速排序。至于时间复杂度和降低时间复杂度的方法是需要非常复杂的数学模型的,专家们正在研究,如今仍然是数学难题。

2.3. 代码

// 希尔排序
void ShellSort(int* a, int n)
{
    // a, 不能为空
    assert(a);

    // 希尔排序相当于插入排序中有多次预排序
    int gap = n;

    while(gap > 1) //  控制预排序间隔,通常分为三块
    {
        gap = gap / 3 + 1;
        for(int i = 0; i < n - gap; ++i)
        {
            // 一次插入排序
            int end = i;
            int tmp = a[end + gap];
            while(end >= 0)
            {
                if(tmp < a[end]) // 升序中, 小于插入的值就挪动数据
                {
                    a[end + gap] = a[end];
                    end -= gap;
                }
                else    // 反之跳出循环赋值
                {
                    break;
                }
            }
            a[end + gap] = tmp;
        }
    }  
}

3. 选择排序

3.1. 原理

        直接选择排序(Straight Select Sorting) 也是一种简单的排序方法,它的基本思想是:第一次从R[0]~R[n-1]中选取最小值和最大值,与R[0]、R[n-1]交换,第二次从R[1]~R[n-2]中选取最小值和最大值,与R[1]、R[n-2]交换,....,第i次从R[i-1]~R[n-1-i]中选取最小值和最大值,与R[i-1]、R[n-1-i]交换,.....,重复n/2次,得到一个按排序码从小到大排列的有序序列。

        因为非常的简单,就是反复遍历数组将最小值最大值分别放到数组首和数组尾,所以时间复杂度很稳定,但也很大,一直都是O(N^2)。

3.2. 代码

void swap(int* a, int p1, int p2)
{
    int tmp = a[p1];
    a[p1] = a[p2];
    a[p2] = tmp;
}

// 选择排序
void SelectSort(int* a, int n)
{
    assert(a);

    int begin = 0, end = n - 1;
    while(begin < end)  // 一次循环找到最大值和最小值,
    {
        int mini = begin, maxi = end;
        for(int i = begin; i <= end; ++i) // 一次循环找到最大值和最小值
        {
            if(a[i] < a[mini]) // 找最小
            {
                mini = i;
            }
            if(a[i] > a[maxi]) // 找最大
            {
                maxi = i;
            }
        }

        if(maxi == begin) // 如果最大值在第一位,就需要改变交换的位置
        {
            maxi = mini;
        }

        swap(a, begin, mini); // 交换数据
        swap(a, end, maxi);

        ++begin; // 控制结束循环的条件
        --end;
    }
}

4. 堆排序

4.1. 原理

        原理相关的可以参考之前在二叉树部分推出的堆排序,是一样的。利用二叉树的原理建堆,将最大的数放在堆顶,然后将堆顶的数据放到末尾之后重新从堆头调整数据,这样堆的数据每次都会减少一个,直到全部完成排序。

        建堆的方式选择向下建堆,完成排序的时间复杂度是O(N*logN)。

4.2. 代码

// 堆排序
void AdjustDown(int* a, int n, int parent)
{
    assert(a);

    // 假设左孩子更大, 公式:child = parent * 2 + 1;
    int child = parent * 2 + 1;

    // 孩子节点没越界就继续比较
    while(child < n)
    {
        // 调整左右孩子,取更小的
        if(child + 1 < n && a[child + 1] > a[child])
        {
            ++child;
        }

        // 如果满足条件就交换父子位置并继续向下遍历
        if(a[parent] < a[child])
        {
            swap(a, parent, child);
            parent = child;
            child = parent * 2 + 1;
        }
        else// 反之,跳出循环(无需继续调整)
        {
            break;
        }
    }
}

void HeapSort(int* a, int n)
{
    assert(a);

    //向下调整建堆(大堆)
    int i = n / 2 - 1;
    while(i >= 0)
    {
        AdjustDown(a, n, i);
        --i;
    }

    // 得到降序数组
    while(--n)
    {
        swap(a, 0, n);
        AdjustDown(a, n, 0);
    }
}

5. 冒泡排序

5.1. 原理

        冒泡排序(Bubble Sort)是最简单和最通用的排序方法,其基本思想是:在待排序的一组数中,将相邻的两个数进行比较,若前面的数比后面的数大就交换两数,否则不交换;如此下去,直至最终完成排序。由此可得,在排序过程中,大的数据往下沉,小的数据往上浮,就像气泡一样,于是将这种排序算法形象地称为冒泡排序。

图5-1 冒泡排序原理图

        算法的原理如图5-1所示,在待排序的一组数中,将相邻的两个数进行比较,若前面的数比后面的数大就交换两数,否则不交换。比如第一趟排序,比较26和28,26小于28故不做改变,到了28和24,28大于24,所以就交换数据位置,再向下比较28和11,仍然需要交换,这样就完成了一轮,28不在需要比较。然后依次排序。

5.2. 代码

// 冒泡排序
void BubbleSort(int* a, int n)
{
    for(int i = 0; i < n - 1; ++i)  // 循环趟数
    {
        for(int j = 1; j < n - i; ++j)  // 循环一趟
        {
            if(a[j] < a[j - 1]) // 把大的元素往后放,使数据提增
            {
                swap(a, j, j - 1);
            }
        }
    }
}

6. 快速排序

6.1. 原理

        快速排序算法通过多次比较和交换来实现排序,其排序流程如下:

        (1)首先设定一个分界值,通过该分界值将数组分成左右两部分。

        (2)将大于或等于分界值的数据集中到数组右边,小于分界值的数据集中到数组的左边。此时,左边部分中各元素都小于分界值,而右边部分中各元素都大于或等于分界值。

        (3)然后,左边和右边的数据可以独立排序。对于左侧的数组数据,又可以取一个分界值,将该部分数据分成左右两部分,同样在左边放置较小值,右边放置较大值。右侧的数组数据也可以做类似处理。

        (4)重复上述过程,可以看出,这是一个递归定义。通过递归将左侧部分排好序后,再递归排好右侧部分的顺序。当左、右两个部分各数据排序完成后,整个数组的排序也就完成了。

图6-1 快速排序举例图

        快速排序在分部中分为三种方法:最标准的霍尔法、挖坑法以及前后指针法。如图6-1所示,快速排序的方法是挖坑法。首先在数组中记录分界值(这里是49),设置头指针和尾指针。如果要将数据排列为升序,那么尾指针从后向前遍历找小于分界值的数(找到了27)停下来,将这个放到之前记录分界值位置的部分,也可以说是头指针部分,然后将27填充到49处,表示填坑。这样27的位置相当于挖了一个坑。然后就然头指针找大于分界值的数(这里找到的是65),然后将65填充到尾指针的位置处(27)。之后移动尾指针,重复这个循环,直到头指针和尾指针相遇,将记录的分界值填入。结果如图6-1中的一次划分后所示。每次划分都能够确定一个值的具体位置,这里确定的是49。之后将49前后的数据分为新的空间继续进行划分即可完成排序。

        霍尔法,是使用头指针从左找大,尾指针从后找小,然后交换位置。相遇之后交换分界值的位置,和挖坑法最大的不同之处在于49的位置是最后移动的。

        前后指针法,前指针找到小于分界值的数就和后指针位置的数交换,然后后指针也向前推进。与前两者最大的不同是,指针的移动是按照同方向进行的。

6.2. 代码

int Midi(int* a, int left, int right)
{
    int midi = (left + right) / 2;
    if(a[left] > a[midi])
    {
        if(a[midi] > a[right])
        {
            return midi;
        }
        else if(a[left] > a[right])
        {
            return right;
        }
        else
        {
            return left;
        }
    }
    else // a[left] < a[midi]
    {
        if(a[midi] < a[right])
        {
            return midi;
        }
        else if(a[left] < a[right])
        {
            return right;
        }
        else
        {
            return left;
        }
    }
}

// 快速排序递归实现
// 快速排序hoare版本
int PartSort1(int* a, int left, int right)
{
    int key = left;
    int begin = left + 1, end = right;
    while(begin < end)
    {
        while(begin < end && a[end] >= a[key])
        {
            --end;
        }
        while(begin < end && a[begin] <= a[key])
        {
            ++begin;
        }
        swap(a, begin, end);
    }
    swap(a, key, end);
    return end;
}

// 快速排序挖坑法
int PartSort2(int* a, int left, int right)
{
    int key = a[left];
    int begin = left, end = right;
    while(begin < end)
    {
        while(begin < end && a[end] >= key)
        {
            --end;
        }
        a[begin] = a[end];
        while(begin < end && a[begin] <= key)
        {
            ++begin;
        }
        a[end] = a[begin];
    }
    a[end] = key;
    return end;
}

// 快速排序前后指针法
int PartSort3(int* a, int left, int right)
{
    int key = a[left];
    int prev = left, cur = left + 1;
    while(cur <= right)
    {
        if(a[cur] < key)
        {
            ++prev;
            swap(a, prev, cur);
        }
        ++cur;
    }
    swap(a, left, prev);
    return prev;
}


void QuickSort(int* a, int left, int right)
{
    assert(a);

    //区间过小直接返回
    if(left >= right)
    {
        return;
    }
    //如果区间过小使用插入排序
    // if(right - left  < 10)
    // {
    //     SelectSort(a, right - left + 1);
    // }

    // 三数取中确定key
    int key = Midi(a, left, right);
    swap(a, left, key);

    key = PartSort3(a, left, right);
    QuickSort(a, left, key - 1);
    QuickSort(a, key + 1, right);
}

6.3. 非递归的实现

        非递归的实现就需要解决区间的问题,所以我们需要向之前数据结构中,队列和栈中借过来存储区间位置即可。代码如下:


// 快速排序 非递归实现
void QuickSortNonR(int* a, int left, int right)
{
    assert(a);
    Stack st;
    StackInit(&st);
    StackPush(&st, right);
    StackPush(&st, left);
    
    while(!StackEmpty(&st))
    {
        int begin = StackTop(&st);
        StackPop(&st);
        int end = StackTop(&st);
        StackPop(&st);

        // 三数取中确定key
        int keyi = Midi(a, begin, end);
        swap(a, begin, keyi);

        keyi = PartSort2(a, begin, end);
        //区间过小就不入栈
        if(begin < keyi - 1)
        {
            StackPush(&st, keyi - 1);
            StackPush(&st, begin);
        }

        if(keyi < end)
        {
            StackPush(&st, end);
            StackPush(&st, keyi + 1);
        }
    }

    StackDestroy(&st);
}

7. 归并排序

7.1. 原理

图7-1 归并排序原理图

        归并排序,就是将两组都是有序的数据合成一组的排序。所以对于原来的数组如图7-1所示,先拆分为不可继续分割的区间,然后分别将其合并,排列成有序数组,为下一次归并做准备。例如这里的10和4,组合之后成为[4,10],3和6组合之后成为[3,6]。然后再将这两个区间组合成[3,4,6,10]。直到组合完成所有数据。

        ps:实际在代码中比较需要建立额外的位置存放排序的数据,另外还需要每次排列完成数据后将数据赋值给原来的数组。相互比较的时候也需要注意,会有一个区间的数据没完全存入,需要分出一步完成该过程。

7.2. 代码

// 归并排序,子函数
void _MergeSort(int* a, int* tmp, int left, int right)
{
    // 区间过小,直接返回
    if(left >= right)
    {
        return;
    }

    // 分区间
    int midi = (left + right) / 2;
    // [left, midi] [mide + 1, right]

    _MergeSort(a, tmp, left, midi);
    _MergeSort(a, tmp, midi + 1, right);

    // 归并
    int begin1 = left, end1 = midi;
    int begin2 = midi + 1, end2 = right;
    int i = begin1;
    // 控制比较次数
    while(begin1 <= end1 && begin2 <= end2)
    {
        //小的先存
        if(a[begin1] < a[begin2])
        {
            tmp[i++] = a[begin1++];
        }
        else
        {
            tmp[i++] = a[begin2++];
        }
    }

    // 存下剩余部分
    while(begin1 <= end1)
    {
        tmp[i++] = a[begin1++];
    }
    while(begin2 <= end2)
    {
        tmp[i++] = a[begin2++];
    }

    // 将暂存数据返回
    memcpy(a + left, tmp + left, sizeof(int) * (right - left + 1));
}

// 归并排序
void MergeSort(int* a, int n)
{
    // 检查数组存在
    assert(a);

    // 开辟额外的空间暂存排序后数据
    int* tmp = (int*)malloc(sizeof(int) * n);
    if(tmp == NULL)
    {
        perror("malloc fail");
        return;
    }

    // 分装子函数,实现归并功能
    _MergeSort(a, tmp, 0, n - 1);
    free(tmp);
}

7.3. 非递归实现

        和快速排序一样,需要解决的是区域划分的问题。那么如何划分区间呢?

        方法是设置区间的大小值,每次排序都按照这个大小划分区间,没完成一次归并就将这个区间的值翻倍,如图7-1所示,分解区间,开始区间的大小为1,之后是2/4/8。当然划分区间需要调整,因为数组的大小可能并不是分区间的倍数。如果数组剩下的元素不满于一个区间,就不需要继续排序,如果有一个区间但是不足第二个,就需要修剪第二个区间的范围。

        代码如下:

// 归并排序, 非递归
void MergeSortNonR(int* a, int n)
{
    assert(a);

    // 开辟额外的空间暂存排序后数据
    int* tmp = (int*)malloc(sizeof(int) * n);
    if(tmp == NULL)
    {
        perror("malloc fail");
        return;
    }

    int gap = 1; // 表示区间间隔
    while(gap < n)
    {
        // 将所有区间分为n / gap + 1 份, 每次比较两个区间
        for(int i = 0; i < n; i += 2 * gap)
        {
            int begin1 = i, end1 = begin1 + gap - 1;
            int begin2 = end1 + 1, end2 = begin2 + gap - 1;
            int j = begin1;

            if(n - 1 < begin2)// 剩余区间过小,不比较
            {
                break;
            }
            else if(n - 1 < end2) // 如果区间2在数组结束之前,end2就是数组尾
            {
                end2 = n - 1;
            }

            //归并
            // 控制比较次数
            while(begin1 <= end1 && begin2 <= end2)
            {
                //小的先存
                if(a[begin1] < a[begin2])
                {
                    tmp[j++] = a[begin1++];
                }
                else
                {
                    tmp[j++] = a[begin2++];
                }
            }

            // 存下剩余部分
            while(begin1 <= end1)
            {
                tmp[j++] = a[begin1++];
            }
            while(begin2 <= end2)
            {
                tmp[j++] = a[begin2++];
            }

            // 将暂存数据返回
            memcpy(a + i, tmp + i, sizeof(int) * (2 * gap));
        }

        gap *= 2; // 循环的递增
    }
    free(tmp);
}

作者结语

        说到底,排序并不是需要用论文的方式记录的东西,这些排序的存在都已经有很长时间了,可以说计算机行业家喻户晓,所以只能算是整理了自己的学习过程。

        写的也比较简单,原理然后接代码,从文本的角度来讲还是较难理解的,但是接触过的都能回到意思。这也是这篇博客的不足之处,本来是给小白学习的,但是小白却可能看不懂。

        无论如何,博客已经出炉了,希望各大高手指点指点。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1795888.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Meta Llama 3 RMSNorm(Root Mean Square Layer Normalization)

Meta Llama 3 RMSNorm&#xff08;Root Mean Square Layer Normalization&#xff09; flyfish 目录 Meta Llama 3 RMSNorm&#xff08;Root Mean Square Layer Normalization&#xff09;先看LayerNorm和BatchNorm举个例子计算 LayerNormRMSNorm 的整个计算过程实际代码实现结…

webpack-性能优化-提取css

CDN 分发网络 Content Delivery Network 或 Content Distribution Network 的缩写 一般把静态资源或第三方资源放到CDN上。 可以在 output的publicPath配置cdn的地址&#xff0c;打包后所有的脚本的前缀都变为这个cdn地址了,一般不会这样使用 output: {filename: "[name…

Mysql学习(三)——SQL通用语法之DML

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 DML添加数据修改数据删除数据 总结 DML DML用来对数据库中表的数据记录进行增删改操作。 添加数据 -- 给指定字段添加数据 insert into 表名(字段1&#xff0c;字…

20240603每日通信--------springboot使用netty-socketio集成即时通信WebSocket

简单效果图 群聊&#xff0c;私聊&#xff0c;广播都可以支持。 基础概念&#xff1a; springbootnetty-socketioWebSocket POM文件&#xff1a; <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/…

英伟达市值超越苹果;ChatGPT、Perplexity、Claude 同时大崩溃丨 RTE 开发者日报 Vol.220

开发者朋友们大家好&#xff1a; 这里是 「RTE 开发者日报」 &#xff0c;每天和大家一起看新闻、聊八卦。我们的社区编辑团队会整理分享 RTE&#xff08;Real-Time Engagement&#xff09; 领域内「有话题的新闻」、「有态度的观点」、「有意思的数据」、「有思考的文章」、「…

大数据的数据采集

大数据采集是指从各种来源收集大量数据的过程&#xff0c;这些数据通常是结构化或非结构化的&#xff0c;并且可能来自不同的平台、设备或应用程序。大数据采集是大数据分析和处理的第一步&#xff0c;对于企业决策、市场分析、产品改进等方面具有重要意义。以下是大数据采集的…

关于python包导入问题的重思考

将顶层目录直接设置为一个包 像这样&#xff0c;每一个文件从顶层包开始导入 这样可以解决我的问题&#xff0c;但是要注意的时&#xff0c;要避免使用出现上下级出现同名包的情况&#xff0c;比如&#xff1a; AutoServer--AutoServer--__init__.py--__init__.py这种情况下…

MongoDB CRUD操作:地理位置查询

MongoDB CRUD操作&#xff1a;地理位置查询 文章目录 MongoDB CRUD操作&#xff1a;地理位置查询地理空间数据GeoJSON对象传统坐标对通过数组指定&#xff08;首选&#xff09;通过嵌入文档指定 地理空间索引2dsphere2d 地理空间查询地理空间查询运算符地理空间聚合阶段 地理空…

Kaggle——Deep Learning(使用 TensorFlow 和 Keras 为结构化数据构建和训练神经网络)

1.单个神经元 创建一个具有1个线性单元的网络 #线性单元 from tensorflow import keras from tensorflow.keras import layers #创建一个具有1个线性单元的网络 modelkeras.Sequential([layers.Dense(units1,input_shape[3]) ]) 2.深度神经网络 构建序列模型 #构建序列模型 …

【vue3|第6期】如何正确地更新和替换响应式对象reactive

日期&#xff1a;2024年6月5日 作者&#xff1a;Commas 签名&#xff1a;(ง •_•)ง 积跬步以致千里,积小流以成江海…… 注释&#xff1a;如果您觉得有所帮助&#xff0c;帮忙点个赞&#xff0c;也可以关注我&#xff0c;我们一起成长&#xff1b;如果有不对的地方&#xff…

【Linux取经路】守护进程

文章目录 一、前台进程和后台进程二、Linux 的进程间关系三、setsid——将当前进程设置为守护进程四、daemon——设置为守护进程五、结语 一、前台进程和后台进程 Linux 中每一次用户登录都是一个 session&#xff0c;一个 session 中只能有一个前台进程在运行&#xff0c;键盘…

AppInventor2有没有删除后的撤销功能?

问&#xff1a;不小心删除了组件&#xff0c;能撤回吗&#xff1f; 答&#xff1a;界面&#xff08;组件&#xff09;设计界面&#xff0c;没有撤销功能。代码&#xff08;逻辑&#xff09;设计视图&#xff0c;可以使用 CtrlZ 撤销&#xff0c;CtrlY 反撤销。 界面设计没有撤…

搜索与图论:树的重心

搜索与图论&#xff1a;树的重心 题目描述参考代码 题目描述 输入样例 9 1 2 1 7 1 4 2 8 2 5 4 3 3 9 4 6输出样例 4参考代码 #include <cstring> #include <iostream> #include <algorithm>using namespace std;const int N 100010, M N * 2;int n, m…

JavaWeb_SpringBootWeb案例

环境搭建&#xff1a; 开发规范 接口风格-Restful&#xff1a; 统一响应结果-Result&#xff1a; 开发流程&#xff1a; 第一步应该根据需求定义表结构和定义接口文档 注意&#xff1a; 本文代码从上往下一直添加功能&#xff0c;后面的模块下的代码包括前面的模块&#xff0c…

新能源管理系统主要包括哪些方面的功能?

随着全球对可持续发展和环境保护的日益重视&#xff0c;新能源管理系统已成为现代能源领域的核心组成部分。这一系统不仅涉及对新能源的收集、存储和管理&#xff0c;还包括对整个能源网络进行高效、智能的监控和控制。以下是新能源管理系统主要包含的几方面功能&#xff1a; 一…

ESP32 Error creating RestrictedPinnedToCore

随缘记&#xff0c;刚遇到&#xff0c;等以后就可能不想来写笔记了。 目前要使用到音频数据&#xff0c;所以去用ESP-ADF&#xff0c;但在使用例程上出现了这个API有问题&#xff0c;要去打补丁。 但是我打补丁的时候git bash里显示not apply&#xff0c;不能打上。 网上看到…

谷歌账号的注册到使用GitHub

一、浏览器扩展 浏览器扩展谷歌学术 二、注册谷歌邮箱 https://support.google.com/accounts/answer/27441?hlzh-hans 1.打开无痕模式&#xff08;ctrlshiftn&#xff09; 2.输入网址 3.选择个人账号 4.填写信息&#xff08;随便填就行&#xff09; &#xff08;以上步骤有时…

FTP

文章目录 概述主动模式和被动模式的工作过程注意事项 概述 文件传输协议 FTP&#xff08;File Transfer Protocol&#xff09;在 TCP/IP 协议族中属于应用层协议&#xff0c;是文件传输标准。主要功能是向用户提供本地和远程主机之间的文件传输&#xff0c;尤其在进行版本升级…

【YOLOV8】2.目标检测-训练自己的数据集

Yolo8出来一段时间了,包含了目标检测、实例分割、人体姿态预测、旋转目标检测、图像分类等功能,所以想花点时间总结记录一下这几个功能的使用方法和自定义数据集需要注意的一些问题,本篇是第二篇,目标检测功能,自定义数据集的训练。 YOLO(You Only Look Once)是一种流行的…

基于element ui 城市选择之间的级联选择

通过el-select实现城市的级联选择效果如图所示 代码实现 <template><div><el-form :model"ruleForminfo"><el-form-item label"居住地址" required><el-col :span"6"><el-form-item ><el-select v-mode…