基础数据结构与算法8:高级排序算法

0.测试框架

  • 创建随机数数组void Create_RandomArr(int* arr,int n){}
// 产生随机数数组
// 随机产生 0~n-1 之间的随机数
void Create_RandomArr(int* arr,int n){
        srand(time(NULL));
        for(int i=0;i<n;i++){
                arr[i] = rand()%n;
        }
}
  • 判断数组是否有序bool Judge_order(int* arr,int n,bool flag){}
// 判断数组是否有序
// true 默认升序 false 默认降序
bool Judge_order(int* arr,int n,bool flag){
        for(int i=0;i<n-1;i++){
                if(flag){
                        if(arr[i] > arr[i+1]) return false;
                }else{
                        if(arr[i] < arr[i+1]) return false;
                }
        }
        return true;
}
  • 函数:交换两个数void swap(int* a,int* b){}
// 交换两个数
void swap(int* a,int* b){
        int temp = *a;
        *a = *b;
        *b = temp;
}
  • 函数:打印数组void PrintArr(int* arr,int n){}
// 打印数组
void PrintArr(int* arr,int n){
        for(int i=0;i<n;i++){
                printf("%d ",arr[i]);
        }
        printf("\n");
}
  • qsort()函数的回调函数int cmp(const void* a,const void* b){}
// qsort()回调函数
int cmp(const void* a,const void* b){
        return *(int*)a - *(int*) b;
}

1.归并排序

1.1 方法

将给出的随机数组进行拆分,直到单个元素,形成有序数组,不断将两个有序数组进行合并。

  • 递归形式:(完全二叉树)
    左 -> 返回上一层函数 -> 右 -> 返回上一层函数 -> 返回上上层函数


    归并排序过程
  • 迭代形式:
    不在进行拆分,根据步长gap(1,2,4,8,...),对于两个步长内的两个有序数组(每个数组的长度为gap)进行合并,直到步长超过或等于数组长度。
    步长从1开始,故下次迭代中每个步长内的数组均为有序数组。
    lenmerge()后,数组的剩余长度

    归并排序过程2

迭代形式的三种情况

1.2 实现

  1. 实现两个有序数组的合并
void merge(int* arr1,int n1,int* arr2,int n2){
        int i=0,j=0,k=0;
        int res[n1+n2];
        while(i<n1 && j<n2){
                if(arr1[i]<arr2[j]){
                        res[k++] = arr1[i++];
                }else{
                        res[k++] = arr2[j++];
                }
        }
        if(i<n1){
                while(i<n1) res[k++] = arr1[i++];
        }
        if(j<n2){
                while(j<n2) res[k++] = arr2[j++];
        }
        for(int i=0;i<k;i++){
                arr1[i] = res[i];
        }
}

优化:

void merge2(int* arr,int n,int mid){
        int res[n];
        int i=0,j=mid,k=0;
        while(i<mid && j<n){
                res[k++] = (arr[i] < arr[j])?arr[i++]:arr[j++];
        }
        if(i<mid) memcpy(res+k,arr+i,(mid-i)*sizeof(int));
        if(j<n) memcpy(res+k,arr+j,(n-j)*sizeof(int));

        memcpy(arr,res,n*sizeof(int));
}
  1. 拆分并合并数组
  • 递归形式
    对应merge()
void Merge_sorting(int* arr,int n){
        if(n>1){
                int mid = n/2;
                Merge_sorting(arr,mid);              // 左 [0,mid)
                Merge_sorting(arr+mid,n-mid);        // 右 [mid,n)
                merge(arr,mid,arr+mid,n-mid);
        }
}

对应merge2()

void Merge_sorting2(int* arr,int n){
        if(n >1){
                int mid = n/2;
                Merge_sorting2(arr,mid);
                Merge_sorting2(arr+mid,n-mid);
                merge2(arr,n,mid);
        }
}
  • 迭代形式
int min(int a,int b) {return a<b?a:b;}

根据步长进行遍历。

void submerge(int* arr,int n,int gap){
        int len = n;
        for(int i=0;i+gap<n;i += 2*gap){
                merge2(arr+i,min(2*gap,len),gap);
                len -= 2*gap;
        }
}

划分步长

void Merge_sorting3(int* arr,int n){
        for(int i=1;i <=n;i *= 2){
                submerge(arr,n,i);
        }
}

1.3 复杂度

  • 时间复杂度
    一共拆分log2n次,每次比较n个元素,一共比较nlog2n次

  • 空间复杂度 O(n)

2.快速排序

2.1 方法

将每次迭代过程中的数组的首元素作为基准数key,先从尾进行遍历,找到第一个小于基准数的元素arr[j],然后从头开始遍历,找到第一个大于基准数的元素arr[i],交换两个元素的值,直到两个数遍历到相同位置,在遍历过程中i<j,将基准数与最终位置所在的数进行交换-确定基准数key的位置,在依次确定其他基准数的位置。

快速排序过程

2.2 实现

1.确定基准数在数组中的位置,返回确定位置的下标

int partition(int* arr,int n){
        int key = arr[0];
        int i=0,j=n-1;
        while(i<j){
                while(i<j && arr[j] >= key) j--;
                if(i>=j) break;
                while(i<j && arr[i] <= key) i++;
                if(i>=j) break;

                swap(arr+i,arr+j);
        }
        swap(arr,arr+i);
        return i;
}

2.根据基准数的位置,对于基准数左右两部分分别进行重新排序

void Quick_sorting(int* arr,int n){
        if(n>1){
                int index = partition(arr,n);
                Quick_sorting(arr,index);
                Quick_sorting(arr+index+1,n-index-1);
        }
}

2.3 复杂度

  • 时间复杂度
    一共拆分log2n次,每次比较n个元素,一共比较nlog2n次
  • 空间复杂度 O(log2n)

3.希尔排序

3.1 方法

选择一定的步长gap,将列表分为若干个子列表sublist,对每个子列表sublist进行插入排序,依次减少步长gap,重复上述操作,直到gap1.

希尔排序过程

3.2 实现

1.划分gap并执行排序

void insert3(int* arr,int n,int gap){
        int insert_data = arr[n-1];
        for(int i=n-gap-1;i>=0;i-=gap){
                if(arr[i] > insert_data){
                        arr[i+gap] = arr[i];
                }else{
                        break;
                }
        }
}

2.根据gap执行插入排序

void Insert_sorting3(int* arr,int n,int gap){
        for(int i=gap;i<=n;i++){
                insert3(arr,i,gap);
        }
}

3.根据gap插入

void Shell_sorting(int* arr,int n){
        for(int i = n/2;i>=1;i /= 2){
                Insert_sorting3(arr,n,i);
        }
}

3.3 复杂度

  • 时间复杂度 O(nlog2n)
  • 空间复杂度 O(1)

比较

No 算法 时间复杂度 空间复杂度 稳定性
1 归并排序 O(nlog2n) O(n) 稳定
2 快速排序 O(nlog2n) O(nlog2n) 不稳定
3 希尔排序 O(n1.3) O(1) 不稳定

算法选择标准

No 准则 排序算法
1 很少的元素 插入排序
2 几乎有序的元素 插入排序
3 关注最坏情况 堆排序
4 希望能够得到一个好的平均情况下性能 快速排序
5 元素是从一个密集集合中抽出的 桶排序
6 希望尽可能少的写代码 插入排序
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,639评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,277评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,221评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,474评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,570评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,816评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,957评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,718评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,176评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,511评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,646评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,322评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,934评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,755评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,987评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,358评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,514评论 2 348

推荐阅读更多精彩内容