xietao3

探索之旅,学习之路

你好,我是谢涛!欢迎来到我的个人主页.
90后代码搬运工,目前就职于上海天天果园,任职iOS高级开发工程师。


算法-归并排序

阅读量真是少,又不想投稿,怎么办

前言

归并排序思路简单,速度仅次于快速排序,为稳定排序算法,一般用于对总体无序,但是各子项相对有序的数列。

核心思想

比较a[i]和a[j]的大小,若a[i]≤a[j],则将第一个有序表中的元素a[i]复制到r[k]中,并令i和k分别加上1;否则将第二个有序表中的元素a[j]复制到r[k]中,并令j和k分别加上1,如此循环下去,直到其中一个有序表取完,然后再将另一个有序表中剩余的元素复制到r中从下标k到下标t的单元。归并排序的算法我们通常用递归实现,先把待排序区间[s,t]以中点二分,接着把左边子区间排序,再把右边子区间排序,最后把左区间和右区间用一次归并操作合并成有序的区间[s,t]。

示例

分治

  1. 首先我们拿到原始数组{100,5,3,11,33,6,8,7,1,3},然后将数组对半分。

  2. {100,5,3,11,33},{6,8,7,1,3},继续半分。

  3. {100,5,3},{11,33};{6,8,7,},{1,3},多于两个值的集合,继续半分。

  4. {100,5},{3};{11,33};{6,8},{7};{1,3},拆分完成后,每个集合先内部排序,再按拆分的顺序合并。

归并

  1. {100,5}->{5,100}对比1次,对比顺序:5:100,调整后数组变为{5,100,3,11,33,6,8,7,1,3}

  2. {5,100},{3}->{3,5,100}对比1次,对比顺序:5:3,调整后数组变为{3,5,100,11,33,6,8,7,1,3}

  3. {11,33}无变化对比1次,对比顺序:11:33,调整后数组变为{3,5,100,11,33,6,8,7,1,3}

  4. {3,5,100},{11,33}->{3,5,11,33,100}对比4次,对比顺序:3:11,5:11,100:11,100:33,调整后数组变为{3,5,11,33,100,6,8,7,1,3}

  5. {6,8}无变化对比1次,对比顺序:6:8,调整后数组变为{3,5,11,33,100,6,8,7,1,3}

  6. {6,8},{7}->{6,7,8}对比2次,对比顺序:6:8,8:7,调整后数组变为{3,5,11,33,100,6,7,8,1,3}

  7. {1,3}无变化对比1次,对比顺序:1:3,调整后数组变为{3,5,11,33,100,6,7,8,1,3}

  8. {6,7,8},{1,3}->{1,3,6,7,8}对比2次,对比顺序: 6:1,6:3,调整后数组变为{3,5,11,33,100,1,3,6,7,8}

  9. {3,5,11,33,100},{1,3,6,7,8}->{1,3,3,5,6,7,8,11,33,100}对比7次,对比顺序:1:3,3:3,5:3,5:6,11:6,11:7,11:8,整个数组完成排序。

C语言代码

#pragma mark - 归并排序
void mergeList (int arr[], int first, int middle, int last ,int temp[]) {
    printf("待排序数组\n");

    for (int i = first; i <= last; i++) {
        printf("%d\t",arr[i]);
    }
    
    int leftStartIndex = first;
    int leftEndIndex = middle;
    
    int rightStartIndex = middle + 1;
    int rightEndIndex = last;
    
    int tempIndex = 0;

    // 寻找两个子序列,顺序遍历,将值小的复制到临时数组中,直到其中一个子序列遍历完毕
    while (leftStartIndex <= leftEndIndex && rightStartIndex <= rightEndIndex) {
        // 值小的就复制到临时数组中
        printf("\nleft:%d right:%d\n",arr[leftStartIndex],arr[rightStartIndex]);
        if (arr[leftStartIndex] <= arr[rightStartIndex]) {
            printf("temp加入left:%d\n",arr[leftStartIndex]);
            temp[tempIndex++] = arr[leftStartIndex++];
        } else {
            printf("temp加入right:%d\n",arr[rightStartIndex]);
            temp[tempIndex++] = arr[rightStartIndex++];
        }
    }
    
    // 有可能左子序列更长,因此将剩下的部分直接复制到临时数组中
    while (leftStartIndex <= leftEndIndex) {
        temp[tempIndex++] = arr[leftStartIndex++];
    }
    
    // 有可能右子序列更长,因此将剩下的部分直接复制到临时数组中
    while (rightStartIndex <= rightEndIndex) {
        temp[tempIndex++] = arr[rightStartIndex++];
    }
    
    int i = 0;
    while (first+i <= last) {
        arr[first+i] = temp[i];
        i++;
    }
    
    printf("进行排序数组\n");
    for (int i = first; i <= last; i++) {
        printf("%d\t",arr[i]);
    }
    printf("\n更新数组\n");
    printfArr(arr, 10);
}

void mergeSort (int arr[], int first, int last, int temp[]) {
    if (first == 0 && last == 9) {
        printfArr(arr, 10);
    }
    if (last > first) {
        int middle = (first+last)/2;
        // 归并左方子列
        mergeSort(arr, first, middle, temp);
        // 归并右方子列
        mergeSort(arr, middle+1, last, temp);
        // 合并
        mergeList(arr, first, middle, last, temp);
    }
}

时间复杂度

时间复杂度为O(nlogn) 这是该算法平均的时间性能,空间复杂度为 O(n),比较操作的次数介于(nlogn) / 2和nlogn - n + 1。

结语

从新写一遍算法,理解果然更透彻了,本文没有使用图片,感觉用文字就已经足够描述了😅。


最后给出最近几篇关于排序算法的源代码

最近的文章

领导力+开营式

前言人生是一个持续学习的过程,领导力同样需要学习。在学习领导力的过程中,在实践中体会感悟往往会比单纯的理论知识学习更加有效。生命年轮团队成员互相分享成长经历、七年为一个阶段,以每一个阶段影响最深的一件事作画,并且向团队成员叙述事情经过。乐*毅:小时候来家里过夜的小伙伴被父亲赶走、和父亲一起被骗至荒地遇险、学习不好,坐最后一排,不被老师喜欢、冒充父母签字,害怕考试,受老师打击、展现画画天赋,参加比赛、生二胎索*赛家里有果园,玩蚂蚁、喜欢娃娃,过家家,跳皮筋、初三换校,熟悉新环境耽误了学习、徘...…

继续阅读
更早的文章

算法-堆排序

赶紧让你的大脑来一套广播体操前言慢慢的,我们脱离了”小学生“算法,开始接触一些需要绕圈子的思路,赶紧沉浸入堆排序的头脑风暴当中吧。简介堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。可以利用数组的特点快速定位指定索引的元素。堆分为大根堆和小根堆,是完全二叉树。大根堆的要求是每个节点的值都不大于其父节点的值,即A[PARENT[i]] >= A[i]。在数组的非降序排序中,需要使用的就是大根堆,因为根据大根堆的要求可知,最大的值一...…

继续阅读