几种排序算法的讲解(二)

来源:互联网 发布:壮语翻译软件 编辑:程序博客网 时间:2024/06/11 06:53

四、希尔排序

希尔排序其实是直接插入排序算法的一种变形,实质是分组插入排序。又称缩小增量排序。
该方法首先要理解分组操作,其实是以间隔分组,即每间隔几个数后就分做一组,然后进行插入排序。接着把间隔缩短一半,以此下去,直到间隔没有,才停下操作。

如:有10个数据,开始的时候每间隔10/2 = 5 划为一组,即第1个数与第6个数为一组,第2个数与第7个数为一组。。。。。。以此类推,然后对每一组的数进行插入排序。排序好后缩短间隔,5/2 = 2,即每间隔2个数就划为一组,以此操作直到间隔为0,无法缩小间隔为止。

如,对10、49、23、1、5、50、44、8、23、2、6这10个数据进行希尔排序。
第一次分组的时候: inter = 10/2 = 5
希尔排序
第一次分组如图,分成了5组,{A1,B1},{A2,B2},{A3,B3},{A4,B4},{A5,B5}这五个组,接着对每一组进行直接插入排序。(没间隔几个数,就分成几组)

第二次分组的时候: inter = 5/2 = 2
希尔排序
现在分成了2组,同上,对每组进行直接插入排序

第三次分组的时候: inter = 2/2 = 1
这里写图片描述
现在只有一组了,仍然进行插入排序

第四次无法分组了: inter = 1/2 = 0

希尔排序实现的代码如下:

#include <bits/stdc++.h>using namespace std;const int maxn = 10000;int a[maxn];int main(){    int n;    while(cin>>n)    {        for(int i=1; i<=n; i++) cin>>a[i];        //开始不断地缩短间隔,直到间隔为0。第一次间隔为n/2        for(int inter = n/2; inter>0; inter/=2)        {            //每一次缩短间隔,其间隔数等于所分的组数,排序的时候从第1组到第inter组进行插入排序。            for(int gap=1; gap<=inter; gap++)            {                //下面是插入排序的代码,稍微改变了一下递增递减量,因为是每次间隔inter进行排序。                //大致的插入排序代码并没有改变                for(int i=gap+inter; i<=n; i+=inter)                {                    int cur;                    for(cur=i-inter; cur>=1; cur-=inter)                    {                        if(a[cur]<a[i])break;                    }                    if(cur!=i-inter)                    {                        for(int k=i-inter; k>cur; k-=inter)                            swap(a[k+inter], a[k]);                    }                }            }        }        for(int i=1; i<=n; i++)            cout<<a[i]<<" ";        cout<<endl;    }    return 0;}

为了让代码尽可能地贴近所描述的语言,上面的代码写得过于复杂,看得有些头疼,我们可以把该代码缩减为以下代码:

#include <bits/stdc++.h>using namespace std;const int maxn = 10000;int a[maxn];int main(){    int n;    while(cin>>n)    {        for(int i=1; i<=n; i++) cin>>a[i];        //缩短间隔,分组。        for(int inter=n/2; inter>0; inter/=2)        {            //从第inter个数开始,这个与普通的直接插入排序从第二个数开始的意味相同            for(int i=inter; i<=n; i++)            {                //每个数与自己组里的数进行直接插入排序                for(int cur=i-inter; cur>=1 && a[cur]>a[cur+inter]; cur-=inter)                    swap(a[cur], a[cur+inter]);            }        }        for(int i=1; i<=n; i++)            cout<<a[i]<<" ";        cout<<endl;    }    return 0;}

代码精简,不过不易懂,再次也不多讲解这一个代码,读者自己思考一番吧。

五、堆排序

堆排序算法在处理大数据的效率是前面四种算法不可比的。
那么要学堆排序,首先要对树有个了解,准确地说是对二叉树要又了解。堆排序就是一个特殊的完全二叉树。

我们用数组存储一堆数据,然后要把数组当做一个二叉树来看。二叉树特点是,一个父节点连接两个儿子节点。开始的时候把一堆数存入数组里,然后把数组看成二叉树,即存数到数组里后就建立好了一个二叉树,此时的树里面的值都是混乱的。

如何去排序一棵二叉树?以排序一个最小堆为例。

一棵树的“某一个节点”父子间的值所在的位置不对的时候,即两个儿子中有值小于父亲的值时,要排序父子关系的话,也会影响后面子孙的位置。

我们首先要对这种牵连一人而祸害一窝的行为用一种方式进行处理(即用一个函数),然后遍历每一个节点,用这种行为处理方式处理每一个节点。

下面说说怎么建立这种处理方式函数。
要找一种规律,就像数学思维一样,任意取一个值设为变量x,绕着x这个值进行某些变化直到某条件成立为止。
这里就任意取一个树的节点作为“变量节点 ”,从这个节点开始进行排序,首先找出该节点和该节点的两个儿子之间的最小的,让最小的与节点的值换一换。
如果没有换值,那么停止,证明了该节点与他的儿子顺序是正确的;如果换值了,那么接下来取与节点交换的儿子作为“变量节点”,继续以上操作,直到要么是到了最后的叶处即到底了,要么是儿子与父亲的值没有交换为止。可见这里是一个递归操作。

好的,找到了处理一个节点关系的方式(函数),那么我们现在要遍历每个节点,用这个方式处理每一个节点,以达到排序一个数组的目的。
那么,如何遍历才是好呢?我们找到的处理方式是用来处理有儿子的节点,是吧。那么从只有儿子没有孙子的节点开始处理。
这样从底部打好基础,节省了很多的麻烦。
根据树的定义,有N个节点,那么儿子是叶的节点是从第n/2个节点开始的,前面的节点都有子孙了。那就是从第n/2个节点开始遍历到根节点。
说来说去,有点绕,结合下面的代码有助于消化堆排序。

#include <bits/stdc++.h>using namespace std;const int maxn = 100000;int a[maxn];//这个就是一个牵一发而动全身的处理方式,一个递归函数//参数即当前的节点的位置,还有树的节点数void softdown(int cur, int n){    //flag是为了判断该节点的关系是否正常,如果儿子的值与父亲的值没有交换过,则flag = 1;    //temp 为了记录最小值的位置    int flag = 0, temp;    while(cur*2<=n && flag==0)    {        //首先判断左儿子与父亲的值        if(a[cur]>a[cur*2]) temp = cur*2;        else temp = cur;        //判断是否有右儿子存在        if(cur*2+1 <= n)        {            if(a[temp]>a[cur*2+1]) temp = cur*2+1;        }        //如果发现最小值的位置不在父节点处,那么交换节点值,继续下一个节点进行处理        //否则递归停止        if(cur != temp) swap(a[cur], a[temp]);        else flag = 1;        cur = temp;    }}void create(int n)      //排序二叉树{    for(int i=n/2; i>=1; i--)        softdown(i, n);}//因为根节点的值是最小的,所以每次输出根节点的值//然后把最后一位与根节点值交换,顺便让长度-1.再排序一次树。//这样就成了数组里存的值是从大到小排序,但输出的时候是从小到大。void Cout(int n)        //输出函数{    while(n)    {        cout<<a[1]<<" ";        swap(a[1], a[n]);        n--;        softdown(1, n);    }}int main(){    int n;    while(cin>>n)    {        for(int i=1; i<=n; i++) cin>>a[i];        create(n);        Cout(n);        cout<<endl;        //直接输出数组里的数进行比较一番        for(int i=1; i<=n; i++) cout<<a[i]<<" ";        cout<<endl;    }    return 0;}
原创粉丝点击