• 七大排序速查版


    通用结构体:

    View Code
    typedef struct
    {
    int r[MAXSIZE+1]; //下标从1开始用,0为哨兵或其他用
    int length;
    }SqList;



    一.选择排序

    1.1简单选择排序:

    (1)思路:

    通过n-i次关键字比较,从n-i+1个记录中选出关键字最小的记录,并和第i个记录交换.

    (2)代码:

    View Code
    void SelectSort(Sqlist *L)
    {
    int i, j, min;
    for(i=1; i<L->length; i++)
    {
    for(j=i+1; j<L->length; j++)
    {
    min = i;
    if(L->r[j] < L->r[min])
    min = j;
    }
    if(i != min)
    swap(L,i,min);
    }

    }

    (3)时间复杂度:

    由两个for循环知,时间复杂度为(n^2).

    1.2堆排

    (1)相关定义:

    堆是一种完全二叉树,当结点值>=其左右孩子结点时,为大顶堆;反之,小顶堆.

    (2)产生背景:

    由简单排序改良而来.简单选择排序第1次从n个数中取最小需要n-1次比较,但第2次从n-1个中选又要n-1-1次比较,没利用好之前的比较.堆排则利用了,体现在建完大顶堆后,结点值大于孩子结点,所以重新调整时,比较的次数就大大缩减.

    (3)使用步骤:

    1.先建大顶堆 2.输出最大值并重调

    (4)代码:

    View Code
    //从上往下调整,使得L->r[s..m]满足大顶堆的定义
    void HeapAdjust(SqList *L, int s, int m)
    {
    int temp, j;
    temp = L->r[s];
    for(j=2*s; j<m; j*=2)
    {
    if(j<m && L->r[j]<L->r[j+1])
    j++;
    if(temp > L->r[s])
    break;
    L->r[s] = L->r[j];
    s = j;
    }
    L->r[s] = temp;
    }

    void HeapSort(SqList *L)
    {
    int i;
    for(i=L->length/2; i>0; i--)
    {
    HeapAdjust(L,i,L->length); //从下往上,从右往左调,这样for循环结束后,堆已变成大顶堆;
    }

    for(i=L->length; i>0; i--)
    {
    swap(L,1,i); //输出堆顶元素,然后将最后一个与其交换
    HeapAdjust(L,1,i-1); //从新调整堆;
    }
    }

    (5)时间复杂度

    由完全二叉树性质知,深度为log2n +1,即调用一次调整要logn时间,而堆排要调用n-1次,所以时间复杂度为(nlogn).

    二.交换排序

    2.1冒泡排序

    (1)思路:

    从下往上冒泡,每次都将最小的冒到最上,即第i次则将第i小的数冒泡到第i个位置上

    (2)代码:

    View Code

    (3)事件复杂度:

    由两个for循环可知,时间复杂度为(n^2).

    2.2快排

    (1)思路:

    基于分治策略,将一个大的序列按某个值比较,按值大于或小于筛选分成两个小的序列,然后按之前的原则分别再继续细分,直到长度为1则排序完成.

    (2)代码:

    View Code
    int Partion(SqList *L, int low, int high)
    {
    int pivot;
    pivot = L->r[low]; //为达到更高效率,比较好的是取头,尾,中三个数中第二大的数为'枢轴值'
    if(low < high)
    {
    while(low<high && L->r[high]>=pivot)
    high--;
    L->r[row] = L->r[high];

    while(low<high && L->r[low]<=pivot)
    low++;
    L->r[high] = L->r[low];
    }
    L->r[low] = pivot;
    return low; //返回'枢轴值'
    }

    void QuikSort(SqList *L, int low, int high)
    {
    int pivot;
    if(low < high)
    {
    pivot = partion(L,low,high); //将原序列一份为二
    QuikSort(L,low,pivot); //递归调用左边区间
    QuikeSort(L,pivot+1,high); //递归调用右区间
    }
    }

    (3)时间复杂度

    最优情况为:枢轴值为序列中第n/2大的树,则有:

    T(n) = 2T(n/2) + f(n)    //f(n)为将一个序列划分为更小的序列所需的时间,这里f(n)=n

    用主方法可知,时间复杂度为(nlogn).

    最差情况为:序列为从小到大排好的,而且枢轴值为第一个,则退化为冒泡排序,时间复杂度为(n^2).

    三.插入排序

    3.1直接插入排序

    (1)思路:

    将一个记录插入到已经排好序的有序序列中,从而得到一个新的记录数加1的有序序列.

    (2)代码:

    View Code
    void InsertSort(SqList *L)
    {
    int i,j;
    for(i=2; i<L->length; i++)
    {
    if(L->r[i] < L->r[i-1]) //将L->r[i]插入到有序序列中
    {
    L->r[0] = L->r[i]; //哨兵,暂存值
    for(j=i-1; L->r[j]>L->r[0]; j--)
    {
    L->r[j+1] = L->r[j]; //比L->r[i]大的,向后挪,腾出位子来
    }
    L->r[j+1] = L->r[0]; //j+1是由于for循环最后多减了1
    }

    }
    }

    (3)时间复杂度:(n^2).

    3.2希尔排序

    (1)思路:

    直接插入排序的改良版.先用比较大的间隔的数去使用插入排序,然后逐步缩减间隔大小,直到为1.即让序列每次排序后越来越接近有序序列.

    (2)代码:

    View Code
    void ShellSort(SqList *L)
    {
    int i, j;
    int increment = L->length;
    while(increment>1)
    {
    increment = increment/3 +1; //相对来说比较好的一个间隔数
    for(i=increment+1; i<=L->length; i++)
    {
    if(L->r[i] < L->r[i-increment])
    {
    L->r[0] = L->r[i];
    for(j=i-increment; L->r[j]>L->r[0]; j=j-increment)
    {
    L->r[j+increment] = L->r[j];
    }
    L->r[j+increment] = L->r[0];
    }
    }
    }
    }

    (3)时间复杂度:

    大量的研究表明,当增量序列为dlta[k]=2t-k+1-1(0≤k≤t≤⌊log2(n+1)⌋)时,可以获得不错的效率,其时间复杂度为O(n3/2),要好于直接排序的O(n2)。

    四.归并排序

    归并排序

    (1)思路:

    典型的分治策略,分而治之,分到1个1个,再两两合并成有序序列.

    (2)步骤:

    需要一个临时数组来装分出来的两个序列.

    (2)代码:

    View Code
    void MergeSort(SqList *L)
    {
    MSort(L->r, L->r, 1, L->length);
    }

    void MSort(int SR[],int TR1[], int s, int t) //将SR[s..t]归并排序为TR1[s..t]
    {
    int m;
    int TR2[MAXSIZE+1]; //临时数组,用来在相邻递归函数之间传递数据

    if(s==t) //3递归结束条件,即每个数组只有一个元素
    TR1[s] = SR[s];
    else
    {
    m = (s+t)/2; //1平分SR,然后从两个序列中继续递归调用
    MSort(SR,TR2,s,m);
    MSort(SR,TR2,m+1,t);
    Merge(TR2,TR1,s,m,t); //2将TR2[s..m]和TR2[m+1..t]合并到TR1[s..t]中

    }
    }

    void Merge(int SR[], int TR[], int i, int m, int n)
    {
    int j, k, l;
    for(k=i,j=m+1; (i<=m)&&(j<=n); k++)
    {
    if(SR[i]<SR[j])
    TR[K] = SR[i++];
    else
    TR[K] = SR[j++];
    }

    //跳出for循环则说明有一组已经全部插入TR[]中,所以将剩下的全部复制到TR[]后面即可
    if(i<=m) //i<=m,则说明j>n即j已经插完
    for(l=1; l<=m; l++)
    TR[k+1] = SR[i+1];

    if(j<=n)
    for(l=1; l<=n; l++)
    TR[k+1] = SR[j+1];
    }

    (3)时间复杂度:

    T(n) = 2T(n/2) + n-1    //n-1为将n个数分成一个一个以及两两合并的时间,由于n个要比较n-1次

    由主方法可知,时间复杂度为(nlogn).

  • 相关阅读:
    Window Phone 8手电筒
    Wp检查手机网络状态
    Wp 导航到手机定位设置页面
    Wp8 读取手机信息
    移动端丨-webkit-overflow-scrolling:touch属性导致页面卡住
    小程序丨canvas内容自适应不同尺寸屏幕
    钉钉自带浏览器版本过低,导致Object.assign不兼容...
    HTTP中GET与POST的区别
    git丨Push rejected: Push to origin/master was rejected
    小程序丨嵌套循环
  • 原文地址:https://www.cnblogs.com/Quains/p/2430607.html
Copyright © 2020-2023  润新知