七种排序算法总结(思想+代码+时间复杂度)

第一组(较为简单的排序方法):①直接(或折半)插入排序、②希尔排序、③起泡排序、④简单排序。

第二组(较为复杂的排序方法) : ⑤快速排序、⑥堆排序、⑦归并排序

下面介绍这七种排序算法的思想和代码实现以及时间复杂度。

1 、折半插入排序算法

    该算法是在b[1······i-1]有序序列中用折半查找查找第一个比b[i]大的数的下标k,如果没有,则b[1······i]有序,继续下一个数b[i+1]的查找,否则,下标从k到i-1的数往后移,b[i]插入位置k;

/*****************在数组b中,进行折半插入排序*******************/

void binsertsort(int N,int b[])

{

    for(int i=1; i<N; i++)

    {

        int l=0,r=i-1; //折半查找

        while(l<=r)        //b[1······i-1]是有序系列

        {

            int mid=(r+l)/2;

            if(b[mid]>b[i])

                r=mid-1;

            else

                l=mid+1;

        }

        int t=b[i];//保存下待插入值,以防被覆盖

        for(int j=i-1; j>=r+1; j--)

            b[j+1]=b[j];    //下标从r+1到i-1的数后移

        b[r+1]=t;

    }

}

时间复杂度:折半插入排序虽然在b[1······i-1]是有序系列中查找b[i]的插入位置时时间复杂度为logN,但是最后还得移动比b[i]大的数据,所以每个数据的查找插入的时间复杂度为N,加上外循环N次,所以该算法的时间复杂度为T(N)=O(N*N);

2、希尔排序算法

希尔排序,也叫递减增量排序,是插入排序的改进版。先将整个待排记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录基本有序时再对全体记录进行一次直接插入排序,我这里的增量一般开始设置增量为数组长度在一半,然后每趟增量都缩小一半。最后一趟t=1时,数组元素已经基本有序了,这趟排序其实就是折半插入排序了。

/*****************在数组b中,进行希尔排序*******************/

void xierpaixu(int N,int b[])

{

    int t=N/2;

//一般开始设置增量为数组长度在一半,然后不断缩小一半,直到为1;

    while(t>=1)

    {

        //每一趟希尔排序

        //趟在长度其实就是每一趟的组数

        for(int i=0; i<t; i++)

        {

            //组内为直接插入排序

            for(int j=i+t; j<N; j+=t)

            {

                int r=b[j]; //r为组内待插入数

                int k;

                for(k=j-t; k>=i&&b[k]>r; k-=t )

                    b[k+t]=b[k];   //比待插入数据大,则调换

                b[k+t]=r;     //id前面的数小于等于r

            }

        }

        t/=2;//趟长度缩小一半

    }

}

 

时间复杂度:希尔排序的时间复杂度是所取择“增量”序列的函数,到目前为止尚未有人求得一种最后的增量序列,但大量的研究已经得出一些局部的结论,有人指出当增量序列为dt[k]= 2t-k+1-1,即趟数t不断缩小一半,时间复杂度为0(N^3/2)。本实验就是利用了这种做法,所以对于本实验,其时间复杂度为T(N)=0(N^3/2),希尔排序在第一组(低效排序算法)中的时间效率是最好的一个。

3、起泡排序算法

  该算法外循环n-1趟,每趟从右往左比较,通过与相邻元素的比较和交换来把小的数交换到最前面。这个过程类似于水泡向上升一样,因此而得名

 /*****************在数组b中,进行起泡排序*******************/

void qipaopaixu(int N,int b[])

{

    for(int i=0; i<N;i++)

    {

        //n趟比较

        for(int j=N-1; j>i;j--)

        {

            //如果后面在数比前面在数小则交换这两个数

            if(b[j]<b[j-1])

            {

                int tt=b[j-1];

                b[j-1]=b[j];

                b[j]=tt;

            }

        }

    }

}

时间复杂度:很明显,起泡排序算法的由内外两个for循环实现,时间复杂度为T(N)=O(N*N)

4、简单选择排序

选择排序的思想其实和冒泡排序有点类似,都是在一次排序后把最小的元素放到最前面。但是过程不同,冒泡排序是通过相邻的比较和交换。而选择排序是通过对整体的选择,即每次对剩余元素查找最小的元素加到位置i,只是选择排序只有在确定了最小数的前提下才进行交换,大大减少了交换的次数。

/*****************在数组b中,进行选择排序*******************/

void jiandanxuanzepaixu(int N,int b[])

{

    for(int i=0; i<N-1; i++)

    {

        int minl=b[i],id=i;

        for(int j=i+1; j<N; j++) //依次找出N-i-1个数中在最小值放在i的位置上

        {

            if(b[j]<minl){

                minl=b[j];

                id=j;

            }

        }

        for(int k=id;k>i;k--)

            b[k]=b[k-1];   //元素后移

        b[i]=minl;

    }

}

时间复杂度:简单选择排序第一次内循环比较N - 1次,然后是N-2次,N-3次,……,最后一次内循环比较1次。共比较的次数是 (N - 1) + (N - 2) + ... + 1,求等差数列和,得 (N - 1 + 1)* N / 2 = N^2 / 2。
舍去最高项系数,其时间复杂度为 O(N*N)

5、快速排序算法

在待排序的n个记录中任取一个记录(这里取第一个记录)作为枢纽,设其关键字为t。经过一趟排序后把所有关键字小于t的记录换到前面,把所有关键字大于t的记录换到后面,结果将待排序记录分成两个子表,最后枢纽放置在分界处的位置。然后,分别对左右子表重复上述过程,直至每个子表只有一个记录时,排序完成。

/*****************在数组b中,进行快速排序*******************/

int fsort(int x,int y,int b[])

{

    int t=b[x];

    while(x<y)

    {

        while(x<y&&b[y]>=t)  

            y--;

        b[x]=b[y];     //将比枢纽小的记录移到低端

        while(x<y&&b[x]<=t)

            x++;   

        b[y]=b[x];     //将比枢纽大的记录移到高端

    }

    b[x]=t;         //枢纽对应位置为x

    return x;

}

void qqsort(int x,int y,int b[])

{

    if(x<y)

    {

        int mid=fsort(x,y,b);

        qqsort(x,mid-1,b);  //对左子表递归排序

        qqsort(mid+1,y,b);//对右子表表递归排序

    }

}

时间复杂度:

最好的情况:每一趟排序后都能将记录均匀的分割成两个长度大致相等的字表,类似折半查找。这种情况下时间复杂度为O(N*logN);

最坏的情况:在带排序序列已经排好序的情况下,其递归树成为单支树,每次只得到一个比上次少一个记录的子序列。这种情况关键字的比较次数为:0+1+……+n-1=n(n-1)/2,时间复杂度为O(N*N)

理论上平均情况下,快速排序的时间复杂度为O(N*logN);

6、归并排序

    归并排序的思想是:长度为N的数组,可以看成是N个有序的子序列,每个子序列的长度为1,然后两两归并,得到长度为2或为1的有序序列;再两两归并……,如此重复,直到得到一个长度为N的有序系列为至。当然,这里要用到递归的过程。

/*****************在数组b中,进行归并排序*******************/

void ziguibing(int l,int r,int mid,int t[],int b[])  //两个有序序列的合并

{

    int i=l,j=mid+1,k=l;

    while(i<=mid&&j<=r)

    {

        if(b[i]<=b[j])       //较小的排在前面

            t[k++]=b[i++];

        else

            t[k++]=b[j++];

    }

    while(i<=mid) t[k++]=b[i++];  //剩余的b[l······mid]有序系列直接复制到t

    while(j<=r) t[k++]=b[j++];    //剩余的b[j······r]有序系列直接复制到t

   for(int i=l;i<=r;i++)

    b[i]=t[i];

}

void guibingpaixu(int l,int r,int t[],int b[])  //

{

    if(l==r)

        t[l]=b[l];      //序列长度为1时是有序的序列

    else

    {

        int mid=(l+r)/2;

        guibingpaixu(l,mid,t,b);  //mid左边递归归并排序

        guibingpaixu(mid+1,r,t,b);//mid右边递归归并排序

        ziguibing(l,r,mid,t,b);  //mid两边归并排序

    }

}

时间复杂度:堆排序的时间复杂度,主要在初始化堆过程和每次选取最大数后重新建堆的过程;构建堆的过程的时间复杂度为n,调堆的时间复杂度为logn;每次选取最大数后重新建堆的过程:循环  n -1 次,每次都是从根节点往下循环查找,所以每一次时间是 logn,总时间:logn(n-1) = nlogn  - logn ;综合得堆排序的时间复杂度为:O(N*logN)。

7、堆排序

堆排序思想:将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。然后将剩余N-1个元素重新构造成一个堆,这样会得到N个元素的次小值。如此反复执行,便能得到一个有序序列了

/*****************在数组b中,进行堆排序*******************/

void tiaozhengdui(int x,int n,int b[])  //调整堆

{

    int t=b[x];



    for(int i=2*x; i<=n; i*=2)

    {

        if(i<n&&b[i]<b[i+1])

            ++i;    //i为x的孩子结点记录的较大者

        if(t>b[i])

            break; //表示b[x...N]已经调整为大根堆

        b[x]=b[i];

        x=i;

    }

    b[x]=t;    //根插入位置x

}

void jianchudui(int N,int b[])

{

/*序号小于等于N/2的结点作为根的子树不一定是堆,需要调整为堆*/

for(int i=N/2; i>0; i--)  

        tiaozhengdui(i,N,b);

}

void duipaixu(int N,int b[])  //堆排序

{

    jianchudui(N,b);

    for(int i=N; i>1; i--)

    {

        int t=b[1];    //把大根堆堆顶元素与子序列b[1...i]最后一个元素互换。

        b[1]=b[i];

        b[i]=t;

        tiaozhengdui(1,i-1,b);   //将剩余i-1个元素重新调整成一个堆

    }

}

时间复杂度:当有N个记录时,需进行[log2N]趟归并排序,每一趟归并,其关键字比较次数不超过N,元素移到次数都是N,因此,归并排序的时间复杂度为O(Nlog2N)。

猜你喜欢

转载自blog.csdn.net/clz16251102113/article/details/81437148