欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 科技 > 能源 > 排序2(万字详细版)

排序2(万字详细版)

2025/2/6 17:41:03 来源:https://blog.csdn.net/shajjs/article/details/144155710  浏览:    关键词:排序2(万字详细版)

一 快速排序

        快速排序是Hoare于1962年提出的⼀种⼆叉树结构的交换排序⽅法,其基本思想为:任取待排序元素 序列中的某元素作为基准值,按照该排序码将待排序集合分割成两⼦序列,左⼦序列中所有元素均⼩ 于基准值,右⼦序列中所有元素均⼤于基准值,然后最左右⼦序列重复该过程,直到所有元素都排列 在相应位置上为⽌。

        快速排序也分为三个常用的,分别是hoare版本,挖坑法,lomuto前后指针。

1.1 hoare版本

        

算法思路 :

1)创建左右指针,确定基准值

2)从右向左找出⽐基准值⼩的数据,从左向右找出⽐基准值⼤的数据,左右指针数据交换,进⼊下次 循环

问题1:为什么跳出循环后right位置的值⼀定不⼤于key?

left > right 时,即right⾛到left的左侧,⽽left扫描过的数据均不⼤于key,因此right此时指

向的数据⼀定不⼤于key。

        

        先看第二个函数,基本思路就是每次找到基准值,然后再左右递归直至出现left>right的情况。

        我们来拿这个数组来试一下。

        首先先让right找到比key小的,就是3,再让left找到比key大的。

        此时left<right,交换就得到下面这个数组了,让left++和right--

        然后right在移动,left再移动。

        

        此时right<left,出循环了,此时让arr[right]和arr[key]位置的值交换一下,此时就找到基准值的位置了。

        然后再返回到另外一个函数中继续执行这个,进行递归调用,每次都是通过这种方法找到每一部分的基准值,进行对它们的调整。

        swap(&arr[left++],&arr[right--]);这个语句中的++和--可以不写吗?答案是不可以,因为如果一个无序数组全是2 2 2 2 的时候,此时left和right都不会移动了,此时就陷入了死循环,还有上面的必须严格大于arr[key]才能交换,否则时间复杂度可能达到o(n的平方)。



        1.2 挖坑法

        

        

思路:

创建左右指针。⾸先从右向左找出⽐基准⼩的数据,找到后⽴即放⼊左边坑中,当前位置变为新

的"坑",然后从左向右找出⽐基准⼤的数据,找到后⽴即放⼊右边坑中,当前位置变为新的"坑",结 束循环后将最开始存储的分界值放⼊当前的"坑"中,返回当前"坑"下标(即分界值下标)。

        

        我们还用这个图来讲解一下吧。

        此时我们先让坑位hole指向left,key的作用是存储开始坑位的值的,首先我们要先让right移动,找到比key小的值。

        

        找到了3比key要小,所以直接让3把原来的坑位给覆盖掉,此时就实现了小的元素在左边了,再让right位置重新生成坑位,然后left开始找到比key大的。

        此时发现没找到就出了循环,然后再让坑位等于key即可,此时把hole返回。

        现在我们有几个问题来解决一下,为什么开始的时候不能让left++呢?

        

        我们可以拿这个例子来看一下,如果直接让left++的话,此时在3的位置就会停下来,此时right和left都指向3那个位置,此时再执行下面的语句的话,此时我们就用3把1给覆盖了,然后hole指向3的位置,此时出去循环又执行了arr[hole]=key这个语句了,此时就出现了问题,发现基准值1的左边有比1大的数,此时就出现了问题,如果不++的话,就没有这个问题了。

        还有第二个问题,为什么left=right也要停止呢?

        还用上面那个,如果left=right还不停止,此时right=-1了,此时让arr[hole]=arr[right],就会出现数组越界了。

        这里的arr[right]和arr[left]为什么都要有=呢,单纯大于或者小于key不行吗?

        答案是不行的,我现在举个简单的例子。

        

        让我们对这一个数组来排序,如果仅仅大于或者小于的话,此时就无法进入到right--和left++那个循环之中,此时整个系统就陷入了死循环了。

1.3 双指针法

        

        这个是通过两个指针来寻找,一个cur去前面探路,找到比基准值小的,找到之后我们要判断这个++prev是否等于cur,如果等于先不交换,因为防止做一些不必要的交换,我们主要就是让小的在基准值前面就行了,cur>right的时候,说明此时已经找完了,此时prev指向的一定是最后一个小于基准值的数据,然后和基准值交换一下就完成了。

        我们来通过下图来讲解一下。

        

        我们现在这个图就可以很好解释++prev为什么不能等于cur了,我们来移动一下。

        这里发现1就符合,但是++prev此时和cur相等,不能交换,如果此时交换,发现2也要交换,此时效率就会很低,如果是一个降序的,那么此时时间复杂度就会达到o(n的平方)了,效率很低,所以我们要限制一下,接下来我们继续移动。

        这时候,我们发现此时大于了基准值了,此时只让cur移动。

        找到下一个比基准值小的,此时++prev!=cur了,此时就要交换了,此时prev还是要执行++,此时就指向了7的位置,直接就把小的换到前面了,大的就去后面了,然后再继续进行这个过程,最后就完成了排序。

        



        二 . 归并排序

        

归并排序算法思想:

        归并排序(MERGE-SORT)是建⽴在归并操作上的⼀种有效的排序算法,该算法是采⽤分治法(Divide and Conquer)的⼀个⾮常典型的应⽤。将已有序的⼦序列合并,得到完全有序的序列;即先使每个

        ⼦序列有序,再使⼦序列段间有序。若将两个有序表合并成⼀个有序表,称为⼆路归并。 归并排序核 ⼼步骤:

        

        这个排序的思想就是通过把一个无序的数组给分解,一直分解到不能再分解,然后再进行比较排序,逐渐合并的过程。

        代码实现:

        

       

        我们先看一下这个过程,一直分解把一个数组分解剩下一个元素,也就是left=right的时候。

        此时我们先看左边,此时分解完成了,此时begin1和end1都指向6,begin2和end2都指向了10的位置,此时我们就要比较6和10的大小进行排序,此时把数据存入到tmp数组中,下面的两个while语句是防止哪个序列中的元素没有完全放入数组中,最后的for循环是吧tmp数组中的内容拷贝给arr数组。

        此时执行完了这次函数栈帧的代码了,此时就会返回到上一层,再执行右边的递归然后把1和7完成了排序,此时返回到上一层函数栈帧了此时begin1就指向了6,end1指向了10,begin2指向了1,end2指向了7,此时就要排这几个的序列了,发现是1 2 7 10的序列,再次返回到上层函数栈帧,再对右边的序列进行递归,循环这个过程最终就会完成了排序了。

        



三 . 非比较排序

        

计数排序

计数排序⼜称为鸽巢原理,是对哈希直接定址法的变形应⽤。 操作步骤:

1)统计相同元素出现次数

2)根据统计的结果将序列回收到原来的序列中

        

        这个就是把相应的数放在他所对应的下标处,让这个下标++,表示数的数量,然后再对它进行打印。

        但是现在又有了一个问题,如果我们的数是{100 , 101 , 110}类似于这样的数据,我们说的是要把它们放在对应的下标下面,但是这个难道要创建到下标为110的吗,如果这样的话,前面的100个位置不久荒废了吗,造成了极大的空间浪费,我们应该怎么解决呢?

        我来提出一个想法,我们只开辟max-min+1个空间大小的数组即可,让第0个位置存放min的位置,中间存放中间数据,max-min+1的位置存放最大的那个数据即可,此时我们上面那个数组只需要开辟110-100+1个位置即可,此时0的位置可以放100,1的位置放101,以此类推最后一个10的位置就放110了,我们只需要开辟11个空间,极大的节约了空间。

        这样我们就完成了代码的编写了,我们首先要找到最大值和最小值,为count数组分配指定的空间,然后再次遍历我们的数组,让其值所在的对应位置++,统计每个值的数量,此时我们就完成了100放在0处,110放在10处了,统计完成数量之后,我们再次进入循环,此时i要小于range,此时我们要遍历count数组了,此时较小的值就放在的下标比较小的位置了,此时我们再次通过一个while循环,把它们的值放入我们的arr数组中,此时它们的值正好就是等于i+min的值的,此时通过while循环控制放入的数量,此时就完成了排序。

        


四. 算法时间效率的比较

        

void TestOP()
{
    srand(time(0));
    const int N = 100000;
    int* a1 = (int*)malloc(sizeof(int) * N);
    int* a2 = (int*)malloc(sizeof(int) * N);
    int* a3 = (int*)malloc(sizeof(int) * N);
    int* a4 = (int*)malloc(sizeof(int) * N);
    int* a5 = (int*)malloc(sizeof(int) * N);
    int* a6 = (int*)malloc(sizeof(int) * N);
    int* a7 = (int*)malloc(sizeof(int) * N);
    int* a8 = (int*)malloc(sizeof(int)* N);
    int* a9 =(int*)malloc(sizeof(int)*N);
    int* a10=(int*)malloc(sizeof(int)*N);
    for (int i = 0; i < N; ++i)
    {
        a1[i] = rand();
        a2[i] = a1[i];
        a3[i] = a1[i];
        a4[i] = a1[i];
        a5[i] = a1[i];
        a6[i] = a1[i];
        a7[i] = a1[i];
        a8[i] =  a1[i];
        a9[i]=a1[i];
    }
    int begin1 = clock();
    InsertSort(a1, N);
    int end1 = clock();

    int begin2 = clock();
    ShellSort(a2, N);
    int end2 = clock();
    int begin3 = clock();
    SeclectSort(a3, N);
    int end3 = clock();
    int begin4 = clock();
    HeapSort(a4, N);
    int end4 = clock();

    int begin5 = clock();
    QuickSort(a5, 0, N-1);
    int end5 = clock();
    int begin6 = clock();
    MergeSort(a6, N);
    int end6 = clock();
    int begin7 = clock();
    BubbleSort(a7, N);
    int end7 = clock();
    int begin8=clock();
    QuickSort2(a8,0,N-1);
    int end8=clock();
    int begin9=clock();
    QuickSort3(a9,0,N-1);
    int end9=clock();
    int begin10=clock();
    CountSort(a10   ,N);
    int end10=clock();
    printf("InsertSort:%d ms\n", end1 - begin1);
    printf("ShellSort:%d ms\n", end2 - begin2);
    printf("SelectSort:%d ms\n", end3 - begin3);
    printf("HeapSort:%d ms\n", end4 - begin4);
    printf("horeQuickSort:%d ms\n", end5 - begin5);
    printf("MergeSort:%d ms\n", end6 - begin6);
    printf("BubbleSort:%d ms\n", end7 - begin7);
    printf("wa keng QuickSort:%d ms\n", end8 - begin8);
    printf("QucickSort3:%d ms\n", end9 - begin9);
    printf("CountSort:%d ms\n", end10 - begin10);
    free(a1);
    free(a2);
    free(a3);
    free(a4);
    free(a5);
    free(a6);
    free(a7);
    free(a8);
    free(a9);
    free(a10);
}

        代码太长了,我就不截图了,此时我们可以用这个代码可以清晰的看到算法时间效率的差异

        

        如图所示,发现同时处理100000条数据的时候希尔排序,堆排序,三种快排和归并排序和非比较排序的效率是比较高的。

        

        五.源代码

        

#include<stdio.h>
#include<time.h>
#include<stdlib.h>
//╤яеепР
void swap(int* a,int* b){
    int temp=*a;
    *a=*b;
    *b=temp;
}
void AdjustDown(int* arr,int parent,int n){
    int child=parent*2+1;
    while(child<n){
        if(child+1<n&&arr[child]<arr[child+1]){
            child++;
        }
        if(arr[parent]<arr[child]){
            swap(&arr[parent],&arr[child]);
            parent=child;
            child=parent*2+1;
        }
        else{
            break;
        }
    }
}
void HeapSort(int* arr,int n){
    for(int i=(n-1-1)/2;i>=0;i--){
        AdjustDown(arr,i,n);
    }
    int end=n-1;
    while(end>0){
        swap(&arr[0],&arr[end]);
        AdjustDown(arr,0,end);
        end--;
    }
}
void InsertSort(int* arr,int n){
    for(int i=0;i<n-1;i++){
        int end=i;
        int tmp=arr[end+1];
        while(end>=0){
            if(arr[end]>tmp){
                arr[end+1]=arr[end];
                end--;
            }
            else{
                break;
            }
        }
        arr[end+1]=tmp;
    }
}
//оё╤ШеепР
void ShellSort(int* arr, int n){
    int gap=n;
    while(gap>1){
        gap=gap/3+1;
        for(int i=0;i<n-gap;i++){
            int end=i;
            int tmp=arr[end+gap];
            while(end>=0){
                if(arr[end]>tmp){
                    arr[end+gap]=arr[end];
                    end-=gap;
                }
                else{
                    break;
                }
            }
            arr[end+gap]=tmp;
        }
    }
    }
void BubbleSort(int* arr,int n){
    for (int i = 0; i < n - 1; i++) {
    for (int j = 0; j < n - i - 1; j++) {
        if (arr[j] > arr[j + 1]) {
            int temp = arr[j];
            arr[j] = arr[j + 1];
            arr[j + 1] = temp;
        }
    }
}
}
void SeclectSort(int* arr,int n){
    int begin=0,end=n-1;
    while(begin<end){
        int min=begin,max=begin;
        for(int i=begin+1;i<=end;i++){
            if(arr[min]>arr[i]){
                min=i;
            }
            if(arr[max]<arr[i]){
                max=i;
            }
        }
        if(max==begin){
            max=min;
        }
        swap(&arr[min],&arr[begin]);
        swap(&arr[max],&arr[end]);
        begin++;
        end--;
    }
}
//双指针法快排
int _QuickSort3(int* arr,int left,int right){
    int prev=left,cur=left+1;
    int key=left;
    while(cur<=right){
        if(arr[cur]<arr[key]&&++prev!=cur){
            swap(&arr[prev],&arr[cur]);
        }
        ++cur;
    }
    swap(&arr[prev],&arr[key]);
    return prev;
}
void QuickSort3(int* arr,int left,int right){
    if(left>right){
        return;
    }
    int key=_QuickSort3(arr,left,right);
    QuickSort3(arr,left,key-1);
    QuickSort3(arr,key+1,right);
}
//挖坑法
int _QuickSort2(int* arr, int left, int right)
{
    int hole = left;
    int key = arr[hole];
    //left++;

    while (left < right)
    {
        while (left < right && arr[right] >= key)
        {
            right--;
        }
        arr[hole] = arr[right];
        hole = right;
        while (left < right && arr[left] <= key)
        {
            left++;
        }
        arr[hole] = arr[left];
        hole = left;
    }
    arr[hole] = key;
    return hole;
}
void QuickSort2(int* arr,int left,int right){
    if(left>=right){
        return;
    }
    int key=_QuickSort2(arr,left,right);
    QuickSort2(arr,left,key-1);
    QuickSort2(arr,key+1,right);
}
//霍尔快排
int _QuickSort(int* arr, int left, int right)
{
    int key=left;
    ++left;
    while(left<=right){
        while(left<=right&&arr[right]>arr[key]){
            right--;
        }
        while(left<=right&&arr[left]<arr[key]){
            left++;
        }
        if(left<=right){
            swap(&arr[left++],&arr[right--]);
        }
    }
    swap(&arr[right],&arr[key]);
    return right;
}

//快速排序
void QuickSort(int* arr, int left, int right)
{
    if(left>=right){
        return;
    }
    int key=_QuickSort(arr,left,right);
    QuickSort(arr,left,key-1);
    QuickSort(arr,key+1,right);
}
//归并排序
void _MergeSort(int* arr, int left, int right,int* tmp)
{
    if (left >= right)
    {
        return;
    }
    int mid = (left + right) / 2;
    //根据mid划分成两个序列:[left,mid] [mid+1,right]
    _MergeSort(arr, left, mid,tmp);
    _MergeSort(arr, mid + 1, right,tmp);

    //合并[left,mid] [mid+1,right]
    int begin1 = left, end1 = mid;
    int begin2 = mid + 1, end2 = right;
    int index = begin1;
    while (begin1 <= end1 && begin2 <= end2)
    {
        if (arr[begin1] < arr[begin2])
        {
            tmp[index++] = arr[begin1++];
        }
        else {
            tmp[index++] = arr[begin2++];
        }
    }
    //可能存在第一个序列中的数据没有全部放到tmp中
    //可能存在第二个序列中的数据没有全部放到tmp中
    while (begin1 <= end1)
    {
        tmp[index++] = arr[begin1++];
    }
    while (begin2 <= end2)
    {
        tmp[index++] = arr[begin2++];
    }
    //将tmp中数据挪回到arr中
    for (int i = left; i <= right; i++)
    {
        arr[i] = tmp[i];
    }
}
//归并排序
void MergeSort(int* a, int n)
{
    int* tmp = (int*)malloc(sizeof(int) * n);
    _MergeSort(a, 0, n - 1, tmp);
    free(tmp);
}
//非比较排序
void CountSort(int* arr,int n){
    int max=0,min=0;
    for(int i=0;i<n;i++){
        if(max<arr[i]){
            max=arr[i];
        }
        if(min>arr[i]){
            min=arr[i];
        }
    }
    int range=max-min+1;
    int* count=(int*)calloc(range,sizeof(int));
    for(int i=0;i<n;i++){
        count[arr[i]-min]++;
    }
    int index=0;
    for(int i=0;i<range;i++){
        while(count[i]--){
            arr[index++]=i+min;
        }
    }
}
void printf1(int* arr,int n){
    for(int i=0;i<n;i++){
        printf("%d ",arr[i]);
    }
}
void TestOP()
{
    srand(time(0));
    const int N = 100000;
    int* a1 = (int*)malloc(sizeof(int) * N);
    int* a2 = (int*)malloc(sizeof(int) * N);
    int* a3 = (int*)malloc(sizeof(int) * N);
    int* a4 = (int*)malloc(sizeof(int) * N);
    int* a5 = (int*)malloc(sizeof(int) * N);
    int* a6 = (int*)malloc(sizeof(int) * N);
    int* a7 = (int*)malloc(sizeof(int) * N);
    int* a8 = (int*)malloc(sizeof(int)* N);
    int* a9 =(int*)malloc(sizeof(int)*N);
    int* a10=(int*)malloc(sizeof(int)*N);
    for (int i = 0; i < N; ++i)
    {
        a1[i] = rand();
        a2[i] = a1[i];
        a3[i] = a1[i];
        a4[i] = a1[i];
        a5[i] = a1[i];
        a6[i] = a1[i];
        a7[i] = a1[i];
        a8[i] =  a1[i];
        a9[i]=a1[i];
    }
    int begin1 = clock();
    InsertSort(a1, N);
    int end1 = clock();

    int begin2 = clock();
    ShellSort(a2, N);
    int end2 = clock();
    int begin3 = clock();
    SeclectSort(a3, N);
    int end3 = clock();
    int begin4 = clock();
    HeapSort(a4, N);
    int end4 = clock();

    int begin5 = clock();
    QuickSort(a5, 0, N-1);
    int end5 = clock();
    int begin6 = clock();
    MergeSort(a6, N);
    int end6 = clock();
    int begin7 = clock();
    BubbleSort(a7, N);
    int end7 = clock();
    int begin8=clock();
    QuickSort2(a8,0,N-1);
    int end8=clock();
    int begin9=clock();
    QuickSort3(a9,0,N-1);
    int end9=clock();
    int begin10=clock();
    CountSort(a10   ,N);
    int end10=clock();
    printf("InsertSort:%d ms\n", end1 - begin1);
    printf("ShellSort:%d ms\n", end2 - begin2);
    printf("SelectSort:%d ms\n", end3 - begin3);
    printf("HeapSort:%d ms\n", end4 - begin4);
    printf("horeQuickSort:%d ms\n", end5 - begin5);
    printf("MergeSort:%d ms\n", end6 - begin6);
    printf("BubbleSort:%d ms\n", end7 - begin7);
    printf("wa keng QuickSort:%d ms\n", end8 - begin8);
    printf("QucickSort3:%d ms\n", end9 - begin9);
    printf("CountSort:%d ms\n", end10 - begin10);
    free(a1);
    free(a2);
    free(a3);
    free(a4);
    free(a5);
    free(a6);
    free(a7);
    free(a8);
    free(a9);
    free(a10);
}

int main(){
    /*int arr[6];
    for(int i=0;i<6;i++){
        scanf("%d",&arr[i]);
    }
    int n=sizeof(arr)/sizeof(arr[0]);
    CountSort(arr,n);*/
    //InsertSort(arr,n);
    //HeapSort(arr,n);
    //ShellSort(arr,n);
    //BubbleSort(arr,n);
    //SeclectSort(arr,n);
    //QuickSort2(arr,0,n-1);
    //MergeSort(arr,n);*/
    //printf1(arr,n);
    TestOP();
}
 

六. 结束语

         感谢大家的查看,希望可以帮助到大家,做的不是太好还请见谅,其中有什么不懂的可以留言询问,我都会一一回答。  感谢大家的一键三连。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com