高效排序算法——希尔排序、堆排序、归并排序、快速排序

比起初级的排序算法,高级的排序算法效率会高很多,但是也更难理解。快速排序是最常用的高级排序方式,我们就先讲讲它吧。

快速排序

快速排序实际上有很多个版本,但是基本思想都是分而治之,方法是找到数组中的一个数作为参考数,将比参考数小的数全都放到左边去,然后将参考数放到最后一次发生交换的地方,这样子参考数左边的数就全都比参考数小,参考数右边的数就全都比参考数大了,然后利用递归的思想,对参考数的左边和右边分别再次进行如上的操作,这样最后就能得到一个排好序的数组。代码如下:

复制代码
void quick_sort(int num[],int left,int right){
    int i,last;
    if(left<right){
        swap(num,left,(left+right)/2);    //将中间数作为基准数放到区间开头
        last=left;    //最后发生交换的位置定义为last
        for(i=left+1;i<=right;i++)    //从基准数右边开始搜索
            if(num[i]<num[left])    //小于基准数的数都放到左边
                swap(num,++last,i);    //每次交换后last+1
        swap(num,left,last);    //将基准数移走,那么left-last之间的数都是小于基准数的了
        quick_sort(num,left,last-1);    //递归,因为此时小于基准数的数都还是无序的
        quick_sort(num,last+1,right);    //此时基准数作为分界线,左边都是小于它的数,
    }                                    //右边都是大于它的数
}
复制代码

模拟一下代码的运行过程通常有利于代码的理解,我们拿10个数作为参考看一下吧

原数组:9 8 7 6 5 4 3 2 1 0

选取5为参考数,和首位交换 5 8 7 6 9 4 3 2 1 0

将比5小的放到左边,得到 5 4 3 2 1 0 8 7 6 9

将5和最后发生交换的0交换,得到 0 4 3 2 1 5 8 7 6 9

发现此时5左边的数都比5小,5右边的数都比5大了

然后递归,分别对0 4 3 2 1和8 7 6 9进行排序

左边数组选取3为参考得到3 4 0 2 1

然后将比3小的放到左边,得到 3 0 2 1 4

交换3和1,得到1 0 2 3 4

此时递归排序1 0 2

0作为参考 得到 0 1 2

左边完成排序

右边的 8 7 6 9选取7为参考

得到 7 8 6 9

将小于7的往左靠,得到7 6 8 9

交换最后发生交换的6,得到 6 7 8 9

右边完成排序。

以上就是一个人工演算快速排序的过程的例子,希望可以帮助大家理解,因为快速排序在排序算法中非常重要。

希尔排序

希尔排序是插入排序的一种改进方案,插入排序的速度并不稳定,在数组原本就较为有序的情况下进行插入排序效率是要比其他排序方法都高的,希尔排序的方法就是先将数组变得比较有序,然后再进行一次完整的插入排序。他的方案是定义一个增量(原本的插入排序相当于增量为1的希尔排序),也就是说每隔几个数进行插入排序。习惯性地,我们先将增量设置为数组大小的二分之一,然后每次增量取为原来的一半,增量不一定要这样取,但是要保证最后一次排序的时候增量为1,关于优化增量来优化希尔排序的方法网上也有资料,可以自行查阅,以下给出每次取一半的方法的代码:

复制代码
void shell_sort(int num[], int n){
    int gap=n/2,i,j;    //gab表示增量,第一次取为数组大小的1/2
    do{
        for(i=gap;i<n;i++)    //这边就是跳着的插入排序
            for(j=i-gap;j>=0&&num[j]>num[j+gap];j-=gap)
                swap(num,j,j+gap);
    }while(gap/=2);    //每次增量都变为原来的1/2
}
复制代码

归并排序

理论上来讲,归并排序效率是高于快速排序的,但是它需要创建辅助空间,需要多次赋值运算,所以和快速排序的效率不相上下。归并排序的方法是将一个数组进行多次二等分,对于二分的元素分别把左边的数组和右边的数组复制到两个临时数组里(要求这两边的数组都是有序的),然后对于临时的两个数组,选择最小的数插入回去。(我的表达能力不好,这些话我感觉自己读都读不懂,那么我举个例子好了)比如说对于左右分别有序的1 4 7 2 3 5

1 4 7放到临时数组a里,然后2 3 5放到临时数组b里,然后对他们的第一个元素进行比较,即1和2比较,发现1比较小,就把1还给需要排序的数组的第一个位置,然后2和4比较,2小,就将2还回,之后4和3比较,3小将3还会,然后4和5比较,4小,4还回,之后7和5比较,5还回,此时数组a还有一个7,也要还回,按着这个还回的顺序,就得到了1 2 3 4 5 7。思维有了,那么我们要如何保证左右都有序呢?就是利用递归的方法,如果一个数组只有两个数,那么它左右两边也算是有序吧?这样的话,如果有四个数,那就让这两边的两个数先排序,然后再归并。讲了这么多,看看代码吧:

复制代码
void merge(int num[],int l,int mid,int r){
    int leftsize=mid-l+1;    //举个例子,从num[0]-num[9]十个数排序,mid=4
    int rightsize=r-mid;    //但是num[0]-num[4]有5个数,所以left要加1
    int *left=(int*)malloc(leftsize*sizeof(int));
    int *right=(int*)malloc(rightsize*sizeof(int));
    int i,j,k;
    for(i=0;i<leftsize;i++)
        left[i]=num[l+i];
    for(j=0;j<rightsize;j++)
        right[j]=num[mid+j+1];
    i=j=0;
    k=l;
    while(i<leftsize&&j<rightsize)
        if(left[i]<right[j])
            num[k++]=left[i++];
        else
            num[k++]=right[j++];
    while(i<leftsize)
        num[k++]=left[i++];
    while(j<rightsize)
        num[k++]=right[j++];
    free(left);
    free(right);
}
void merge_sort(int num[],int l,int r){
    if(l<r){
        int mid=(l+r)/2;
        merge_sort(num,l,mid);
        merge_sort(num,mid+1,r);
        merge(num,l,mid,r);
    }
}
复制代码

总结

关于排序的算法算是讲完了,如果每个代码都能独立写出来,那就是能理解了,这个很能锻炼编程思维,大家加油吧~

原文地址:https://www.cnblogs.com/renjiaqi/p/10236043.html