《大话数据结构》笔记二(排序)

1 冒泡排序(优化)
2 选择排序
3 直接插入排序
4 希尔排序
5 堆排序
6 归并排序(优化)
7 快速排序(优化)

#define MAXSIZE 10000  /* 用于要排序数组个数最大值,可根据需要修改 */
typedef struct
{
    int r[MAXSIZE+1];   /* 用于存储要排序数组,r[0]用作哨兵或临时变量 */
    int length;         /* 用于记录顺序表的长度 */
}SqList;

1 冒泡排序

两两比较相邻的数据,如果反序则交换,直到没有反序的记录
//改进冒泡排序
//在性能上有一些提升,避免因已经有序的情况下的无意义循环判断

void maoPao(SqList *L){
    Status flag = TRUE;
    //若flag为true则退出循环
    for (int i = 1; i < L->length && flag; i++)
    {
        flag = FALSE;
        for (int j = L->length - 1; j >= 1; j--)
        {
            if (L->r[j] > L->r[j+1])
            {
                int temp = L->r[i];
                L->r[i] = L->r[j];
                L->r[j] = temp;
                flag = TRUE;
            }
        }
    }
}

2 选择排序

//通过n-i次关键字间的比较,从n-i+1个数据中选出关键字最小的数据,并和第i(1<=i<=n)个数据交换

void xuanZe(SqList *L){
    int min;
    for (int i = 1; i < L->length; i++)
    {
        //将当前下标定义为最小值下标
        min = i;
        //循环当前下标之后的所有数据
        for (int j = i+1; j <= L->length; j++)
        {
            //j=2 L[1]>L[2] 9>1
            //min=2
            //j=3 L[2]>L[3] 1>5
            //min不变,还是2
            if (L->r[min] > L->r[j])
            {
                min = j;
            }
        }
        //全比较过后,交换值
        if (i != min)
        {
            int temp=L->r[i];
            L->r[i]=L->r[min];
            L->r[min]=temp;
        }
    }
}

3 直接插入排序

//将一个数据插入到已经排好序的有序表中,得到一个新的有序表

//r[6] = {0,5,3,4,6,2}
void chaRu(SqList *L){
    int i,j;
    for (i = 2; i < L->length; i++)
    {
        //i=2,053462,3<5
        //i=3,035462,4<5接着执行
        if (L->r[i] < L->r[i-1])
        {
            //设置哨兵
            //r0 = 3
            L->r[0] = L->r[i];
            //j=1 5>3成立,走循环体
            //j=0 3>3不成立,出循环,此时j为0
            for (j=i-1; L->r[j] > L->r[0]; j--)
            {
                //r2=5
                L->r[j+1] = L->r[j];
            }
            //r1=3
            L->r[j+1] = L->r[0];
        }
    }
}
4 希尔排序

//实际上是优化的直接插入排序,采取跳跃分割的策略,将相距某个"增量"的记录组成一个子序列,保证子序列内分别进行直接插入排序结果基本有序(大的基本在后面)
//时间复杂度为O(n³/2),比O(n²)要好点

void xiEr(SqList *L){
    int i,j;
    int zeng = L->length;
    do{
        //增量序列
        zeng = zeng/3 + 1;
        //分段排序,与直接插入排序相同
        for (i = zeng+1; i <= L->length; i++)
        {
            //判断是否交换
            if (L->r[i] < L->r[i-zeng])
            {
                L->r[0] = L->r[i];
                for (j = i-zeng; j>0 && L->r[j] > L->r[0]; j-=zeng)
                {
                    L->r[j+zeng] = L->r[j];
                }
                L->r[j+zeng] = L->r[0];
            }
        }
    //增量为1时,停止循环
    } while (zeng > 1);
}
5 堆排序

//对简单选择排序进行的一种改造,时间复杂度为O(nlogn),比冒泡,选择,插入的O(n²)好很多
//堆是一种近似完全二叉树:利用完全二叉树的深度是(log2n)+1的特性
//每个结点的值都大于或等于左右孩子结点的值,为大顶堆
//每个结点的值都小于或等于左右孩子结点的值,为小顶堆
//将待排序的序列构造成一个大顶堆,整个序列的最大值就是堆顶的根结点,将它移走,然后将剩余的n-1个序列重新构造成一个大顶堆,反复执行
//就是将堆数组的根结点与末尾元素交换,末尾元素就变成最大值了

//将数组调整为大顶堆
void daDingDui(SqList *L,int s,int m);
void dui(SqList *L){
    int i;
    //把有子节点的结点调整成大顶堆
    for (i = L->length/2 ; i>0 ; i--)
    {
        daDingDui(L, i, L->length);
    }
    for (i = L->length; i>1 ; i--)
    {
        //将堆顶数据和当前未经排序子序列的最后一个数据交换
        //把最大值换到尾巴处
        int temp=L->r[i];
        L->r[i]=L->r[1];
        L->r[1]=temp;
        //排除最后一个最大数据,再重新调整成大顶堆
        daDingDui(L, 1, i-1);
    }
}

void daDingDui(SqList *L,int s,int m){
    int temp,j;
    temp= L->r[s];
    //沿关键字较大的孩子结点向下筛选
    //根据完全二叉树的按层序递增排列特点
    //从j=2s开始,j=m结束
    for (j=2*s ; j<=m ; j*=2)
    {
        //j为左孩子下标,j+1为右孩子下标,找出最大的
        if (j<m && L->r[j] < L->r[j+1])
        {
            ++j;
        }
        //比较根结点大小
        if (temp >= L->r[j])
        {
            break;
        }
        //把最大值给根结点
        L->r[s] = L->r[j];
        s = j;
    }
    //把刚开始保存的较小值给子结点
    L->r[s] = temp;
}

6 归并排序

//归并--将两个或两个以上的有序表组合成一个新的有序表
//2路归并排序:n个数据,两两归并,直到得到一个长度为n的有序序列
//总的时间复杂度为O(nlogn),空间复杂度为O(n+logn)
//比较占用内存,但是稳定,效率高

//归并函数
void realGuiBing(int SR[],int TR1[],int s,int t);
//排序函数
void Merge(int SR[],int TR[],int s,int m,int t);

//调用归并函数
void guiBing(SqList *L){
    realGuiBing(L->r,L->r,1,L->length);
}

//递归函数,将 SR[s...t] 归并排序为 TR1[s...t]
void realGuiBing(int SR[],int TR1[],int s,int t){
    int m;
    int TR2[MAXSIZE +1];
    //相等就不用排序了
    if (s == t)
    {
        TR1[s] = SR[s];
    }
    else
    {
        //将SR[s...t]平分为SR[s...m]和SR[m+1...t]
        m = (s+t)/2;
        //递归将SR[s...m]归并为有序的TR2[s...m]
        realGuiBing(SR,TR2,s,m);
        //递归将SR[m+1...t]归并为有序的TR2[m+1...t]
        realGuiBing(SR,TR2,m+1,t);
        //将TR2[s...m]和TR2[m+1...t]归并到TR1[s...t]
        Merge(TR2,TR1,s,m,t);
    }
}

//关键函数,排序并合并
//将有序的SR[i...m] 和 SR[m+1...n]归并为有序的TR[i...n]
void Merge(int SR[],int TR[],int s,int m,int t){
    int j,k,l;
    //将SR中数据由小到大归并入TR
    for (j = m+1, k = s; s <= m && j <= t; k++)
    {
        if (SR[s] < SR[j])
        {
            TR[k] = SR[s++];
        }
        else
        {
            TR[k] = SR[j++];
        }
    }
    //将剩余的SR[s...m]复制到TR
    if (s <= m)
    {
        for (l = 0; l <= m-s; l++)
        {
            TR[k+1] = SR[s+1];
        }
    }
    //将剩余的SR[j...t]复制到TR
    if (j <= t)
    {
        for (l = 0; l < t-j; l++)
        {
            TR[k+1] = SR[j+1];
        }
    }
}

//优化归并排序,非递归实现

//非递归增加的函数
void MergeAdd(int SR[], int TR[],int s,int n);
//归并排序
void guiBing2(SqList *L){
    //申请内存空间
    int *TR = (int *)malloc(L->length *sizeof(int));
    int k = 1;
    while (k < L->length)
    {
        MergeAdd(L->r, TR, k, L->length);
        //子序列长度加倍
        k = 2*k;
        MergeAdd(TR, L->r, k, L->length);
        //加倍
        k = 2*k;
    }
}

void MergeAdd(int SR[], int TR[],int s,int n){
    int i = 1;
    int j;
    while (i <= n - 2 * s + 1)
    {
        //两两归并
        Merge(SR, TR, i, i + s - 1, i + 2 *s -1);
        i = i +2 *s;
    }
    //归并最后两个序列
    if (i < n-s+1)
    {
        Merge(SR, TR, i, i+s-1, n);
    }
    //剩下的单数直接并入
    else
    {
        for (j = i; j <= n; j++)
        {
            TR[j] = SR[j];
        }
    }
}

//希尔排序为直接插入排序的升级,属于插入排序类
//堆排序为简单选择排序的升级,属于选择排序类
//快速排序为冒泡排序的升级,属于交换排序类

7 快速排序

//通过一趟排序将待排数据分割成独立的两部分,其中一部分key均比另一部分key小,分别对这两部分记录继续排序,最终完成排序
//时间复杂度平均为O(nlogn)

//快速排序
void realKuaiSu(SqList *L,int low,int high);
//找到枢轴值
int fenKai(SqList *L ,int low,int high);
//调用快速排序
void kuaiSu(SqList *L){
    realKuaiSu(L,1,L->length);
}
//快速排序
void realKuaiSu(SqList *L,int low,int high){
    int pivot;
    if (low < high)
    {
        //将L->r[low...high]一分为二,算出枢轴值pivot
        pivot = fenKai(L,low,high);
        //对低子表递归排序
        realKuaiSu(L,low,pivot-1);
        //对高子表递归排序
        realKuaiSu(L,pivot+1,high);
    }
}

//找到枢轴值,交换顺序表L中子表的数据
//将选取的枢轴值不断变换,将比它小的换到它的左边,比它大的换到它的右边,它也在交换中不断更改自己的位置,直到完全满足这个要求为止
int fenKai(SqList *L ,int low,int high){
    int pivotkey;
    //用子表的第一个数据做枢轴数据
    pivotkey = L -> r[low];
    //从表的两端交替向中间扫描
    while (low < high)
    {
        //比枢轴数据小的数据交换到低端
        while (low < high && L->r[high] >= pivotkey)
        {
            high--;
            int temp=L->r[low];
            L->r[low]=L->r[high];
            L->r[high]=temp;
        }
        //将比枢轴大的数据交换到高端
        while (low < high && L->r[low] <= pivotkey)
        {
            low++;
            int temp=L->r[low];
            L->r[low]=L->r[high];
            L->r[high]=temp;
        }
    }
    return low;
}

//快速排序优化

//1.选取枢轴pivotkey的优化
//三数取中法:取左,中,右三个数据先排序,将中间数作为枢轴
//九数取中法
int getPivotkey(SqList *L,int high,int low){
    int pivotkey;
    //计算数组中间元素的下标
    int m = low + (high - low)/2;
    //交换左右端数据,保证左端较小
    if (L->r[low] > L->r[high])
    {
        int temp=L->r[low];
        L->r[low]=L->r[high];
        L->r[high]=temp;
    }
    //交换中间与右端数据,保证中间较小
    if (L->r[m] > L->r[high])
    {
        int temp=L->r[m];
        L->r[m]=L->r[high];
        L->r[high]=temp;
    }
    //交换中间与左端数据,保证左端较小
    if (L->r[m] > L->r[low])
    {
        int temp=L->r[m];
        L->r[m]=L->r[low];
        L->r[low]=temp;
    }
    //L.r[row]为三个关键字中间值
    pivotkey = L->r[low];
    return pivotkey;
}

//2.优化不必要的交换
int youHuaKuaiSu(SqList *L,int low,int high){
    //取到枢轴
    int pivotkey = getPivotkey(L, high, low);
    //将枢轴备份
    L->r[0] = pivotkey;
    //从表的两端交替向中间扫描
    while (low < high)
    {
        //比枢轴数据小的数据交换到低端
        while (low < high && L->r[high] >= pivotkey)
        {
            high--;
            //采用替换不是交换
            L->r[low] = L->r[high];
        }
        //将比枢轴大的数据交换到高端
        while (low < high && L->r[low] <= pivotkey)
        {
            low++;
            //采用替换不是交换
            L->r[high] = L->r[low];
        }
    }
    //将枢轴数值替换回L.r[low]
    L->r[low] = L->r[0];
    //返回枢轴所在位置
    return low;
}

//因为递归的使用,非常大的数组用快速排序,直接插入排序是简单排序中性能最好的

经过优化的快速排序是性能最好的排序算法
从最好情况看,选择冒泡和直接插入排序
从最坏情况看,选择堆排序与归并排序
从稳定性来看,选择归并排序
如果执行算法的软件所处的环境非常在乎内存使用量的多少,选择堆排序等

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,826评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,968评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,234评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,562评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,611评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,482评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,271评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,166评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,608评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,814评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,926评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,644评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,249评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,866评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,991评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,063评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,871评论 2 354

推荐阅读更多精彩内容

  • 第一章 绪论 什么是数据结构? 数据结构的定义:数据结构是相互之间存在一种或多种特定关系的数据元素的集合。 第二章...
    SeanCheney阅读 5,771评论 0 19
  • 一、 单项选择题(共71题) 对n个元素的序列进行冒泡排序时,最少的比较次数是( )。A. n ...
    貝影阅读 9,067评论 0 10
  • 因为之前就复习完数据结构了,所以为了保持记忆,整理了一份复习纲要,复习的时候可以看着纲要想具体内容。 树 树的基本...
    牛富贵儿阅读 6,875评论 3 10
  • 概述 排序有内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部...
    蚁前阅读 5,184评论 0 52
  • 概述:排序有内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部...
    每天刷两次牙阅读 3,732评论 0 15