文章目录

  • 一、归并排序递归法
    • 1.归并排序的基本思想
    • 2.归并排序的代码实现
  • 二、归并排序非递归
    • 1.可否使用栈来模拟?
    • 2.直接改非递归(简化版)
    • 3.处理边界之一把梭哈
    • 4.处理边界之归并一次拷贝一次

一、归并排序递归法

1.归并排序的基本思想

归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide andConquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。 归并排序核心步骤:

也就是说,先将排序给劈成两半,然后对分别对左边和右边使用归并使得左右都变得有序。然后我们在对左右子数组进行归并即可

2.归并排序的代码实现

void _MergeSort(int* a, int begin, int end, int* tmp)
{if (begin >= end){return;}int mid = (begin + end) / 2;_MergeSort(a, begin, mid, tmp);_MergeSort(a, mid + 1, end, tmp);int begin1 = begin, end1 = mid;int begin2 = mid + 1, end2 = end;int i = begin;while (begin1 <= end1 && begin2 <= end2){if (a[begin1] < a[begin2]){tmp[i++] = a[begin1++];}else{tmp[i++] = a[begin2++];}}while (begin1 <= end1){tmp[i++] = a[begin1++];}while (begin2 <= end2){tmp[i++] = a[begin2++];}memcpy(a + begin, tmp + begin, sizeof(int) * (end - begin + 1));}
//归并排序
void MergeSort(int* a, int n)
{int* tmp = (int*)malloc(sizeof(int) * n);if (tmp == NULL){perror("malloc fail");return;}_MergeSort(a, 0, n - 1, tmp);free(tmp);return 0;
}

二、归并排序非递归

1.可否使用栈来模拟?

其实是不可以的,因为这里类似于一个后序遍历,而快排之所以可以使用栈来模拟,是因为他是一个先序遍历。
对于快速排序,他是一趟搞定一个,当递归结束而不返回的时候,已经排好了。
而对于归并排序,他是先搞左右两边的。最后还需要获取原来的区间的。显然栈无法实现这样的功能。

2.直接改非递归(简化版)

我们可以这样思考,如果我们利用循环,先将前一个元素认为是一组给归并,然后依次归并后,再将两个元素设置为一组依次归并。让每组元素个数依次乘以2。只要起始值不越界。就可以一直依次拷贝下去。我们可以这样做,但是这种方式存在一个很明显的问题。只可以归并前2的次方的数组。否则将会产生越界的风险

//归并排序非递归
void MergeSortNonR(int* a, int n)
{int* tmp = (int*)malloc(sizeof(int) * n);if (tmp == NULL){perror("malloc fail");return;}int gap = 1;while (gap < n){for (int i = 0; i < n; i += 2 * gap){int begin1 = i, end1 = i + gap - 1;int begin2 = i + gap, end2 = i + 2 * gap - 1;int j = begin1;while (begin1 <= end1 && begin2 <= end2){if (a[begin1] < a[begin2]){tmp[j++] = a[begin1++];}else{tmp[j++] = a[begin2++];}}while (begin1 <= end1){tmp[j++] = a[begin1++];}while (begin2 <= end2){tmp[j++] = a[begin2++];}}gap *= 2;memcpy(a, tmp, sizeof(int) * n);}free(tmp);
}

3.处理边界之一把梭哈

我们有了上面的代码作为基础,我们发现上面存在的问题是会出现越界。需要注意的是,在上面的代码中,我们并不是跟递归一样。每处理一组拷贝一组,我们是处理一次间距后才进行拷贝,也就是一把梭哈的。这就会出现一些潜在的问题。
为了处理边界问题,我们不妨先将代码的可能出现的越界情况给分析出来

我们一共有九个元素,不难发现,除了begin1之外都有可能产生越界

为了方便我们分析,我们将上面的情况分为三类
1.end1越界,此时我们直接不归并就可以了。但是需要注意的是,我们是一把梭哈的,所以我们还是需要将[begin1,n-1]这段区间给拷贝下去。否则出现问题。为了拷贝给tmp,我们可以这样做,修正end1为n-1,然后将beigin2和end2给搞成不存在的区间
2.end1不越界,但begin2越界。这样的话,我们只需要将begin2和end2所控制的区间给不存在即可。
3.end1,begin2不越界,但end2越界。这样的话,我们就直接将end2给修正为n-2即可
结果如下所示,这样一来我们就成功的修正了边界

代码如下:

//归并排序非递归
void MergeSortNonR(int* a, int n)
{int* tmp = (int*)malloc(sizeof(int) * n);if (tmp == NULL){perror("malloc fail");return;}int gap = 1;while (gap < n){for (int i = 0; i < n; i += 2 * gap){int begin1 = i, end1 = i + gap - 1;int begin2 = i + gap, end2 = i + 2 * gap - 1;//修正边界if (end1 > n - 1){end1 = n - 1;begin2 = n;end2 = n - 1;}else if (begin2 > n - 1){begin2 = n;end2 = n - 1;}else if (end2 > n - 1){end2 = n - 1;}//打印边界//printf("[%d,%d][%d,%d] ", begin1, end1, begin2, end2);int j = begin1;while (begin1 <= end1 && begin2 <= end2){if (a[begin1] < a[begin2]){tmp[j++] = a[begin1++];}else{tmp[j++] = a[begin2++];}}while (begin1 <= end1){tmp[j++] = a[begin1++];}while (begin2 <= end2){tmp[j++] = a[begin2++];}}gap *= 2;//printf("\n");memcpy(a, tmp, sizeof(int) * n);}free(tmp);
}

4.处理边界之归并一次拷贝一次

这样的话,我们就可以简化很多了。我们对于end1越界和begin1越界,我们直接break就可以了。唯一需要注意的是拷贝的元素个数是end2-i+1,因为i是起始位置下标,end2是末位置。

//归并排序非递归
void MergeSortNonR(int* a, int n)
{int* tmp = (int*)malloc(sizeof(int) * n);if (tmp == NULL){perror("malloc fail");return;}int gap = 1;while (gap < n){for (int i = 0; i < n; i += 2 * gap){int begin1 = i, end1 = i + gap - 1;int begin2 = i + gap, end2 = i + 2 * gap - 1;//修正边界if (end1 > n - 1 || begin2 > n - 1){break;}else if (end2 > n - 1){end2 = n - 1;}printf("[%d,%d][%d,%d] ", begin1, end1, begin2, end2);int j = begin1;while (begin1 <= end1 && begin2 <= end2){if (a[begin1] < a[begin2]){tmp[j++] = a[begin1++];}else{tmp[j++] = a[begin2++];}}while (begin1 <= end1){tmp[j++] = a[begin1++];}while (begin2 <= end2){tmp[j++] = a[begin2++];}memcpy(a + i, tmp + i, sizeof(int) * (end2 - i + 1));}gap *= 2;printf("\n");}free(tmp);
}

打印结果为
我们可以发现,对于不符合区间的部分,我们就直接不管这块的区间了


本期内容到此位置
如果对你有帮助的话,不要忘记点赞加收藏哦!!!

【数据结构】第十三站:排序(下)归并排序相关推荐

  1. 数据结构和算法之排序一:归并排序

    我们不得不承认一个事实,java学习过程中如果我们掌握了各种编程手段和工具,确实可以做一些开发,这就是一些培训机构敢告诉你几个月就能掌握一门语言的原因.但是随着时间的发展,我们总会感觉,这一类人如果不 ...

  2. 【数据结构与算法】高级排序(希尔排序、归并排序、快速排序)完整思路,并用代码封装排序函数

    本系列文章[数据结构与算法]所有完整代码已上传 github,想要完整代码的小伙伴可以直接去那获取,可以的话欢迎点个Star哦~下面放上跳转链接 https://github.com/Lpyexplo ...

  3. 在路上---学习篇(一)Python 数据结构和算法 (4) --希尔排序、归并排序

    独白: 希尔排序是经过优化的插入排序算法,之前所学的排序在空间上都是使用列表本身.而归并排序是利用增加新的空间,来换取时间复杂度的减少.这俩者理念完全不一样,注定造成的所消耗的时间不同以及空间上的不同 ...

  4. 数据结构之外部排序:归并排序法

    外部排序:归并排序法 思维导图: 外部归并排序的原理: 外部归并排序的性能: 归并排序法的优化: 思维导图: 外部归并排序的原理: 第一步: 第二步: 问题:内存缓存区大小固定,外存数据元素分块后仍然 ...

  5. 02_Python算法+数据结构笔记-冒泡排序-选择排序-插入排序-快排-二叉树

    b站视频:路飞IT学城 清华计算机博士带你学习Python算法+数据结构_哔哩哔哩_bilibili 文章目录 #11 排序介绍 #12 冒泡排序介绍 #13 冒泡排序 #14 选择排序 #15 插入 ...

  6. 在Object-C中学习数据结构与算法之排序算法

    笔者在学习数据结构与算法时,尝试着将排序算法以动画的形式呈现出来更加方便理解记忆,本文配合Demo 在Object-C中学习数据结构与算法之排序算法阅读更佳. 目录 选择排序 冒泡排序 插入排序 快速 ...

  7. C 数据结构之十大排序

    C 数据结构之十大排序 排序算法是<数据结构与算法>中最基本的算法之一. 排序算法可以分为内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳 ...

  8. 数据结构排序算法实验报告_[数据结构与算法系列]排序算法(二)

    我的上一篇文章向大家介绍了排序算法中的冒泡排序.插入排序和选择排序.它们都是平均时间复杂度为 O(n^2) 的排序算法,同时还为大家讲解了什么是原地排序和什么是排序的稳定性.下图是这三种算法的比较,不 ...

  9. 十大排序算法:冒泡排序、选择排序、插入排序、希尔排序、归并排序、快速排序、堆排序、计数排序、桶排序、基数排序

    冒泡排序.选择排序.插入排序.希尔排序.归并排序.快速排序.堆排序.计数排序.桶排序.基数排序的动图与源代码. 目录 关于时间复杂度 冒泡排序 选择排序 插入排序 希尔排序 归并排序 快速排序 堆排序 ...

  10. 数据结构与算法之排序算法

    数据结构与算法之排序算法 排序算法的介绍 ​ 排序也称排序算法(Sort Algorithm),排序是将一组数据,依指定的顺序进行排序的过程. 排序的分类 1)内部排序:指将需要处理的数据都加载到内部 ...

最新文章

  1. 第2题——DNA片段
  2. 目标检测带标签样本增广工具
  3. 使用 MQTTnet 快速实现 MQTT 通信
  4. 冷热rx-java可观察
  5. mssql 远程无法连接mysql_在本地 怎么远程连接MSSQL数据库
  6. Open-Falcon 监控系统监控 MySQL/Redis/MongoDB 状态监控
  7. 看完这篇垃圾回收,和面试官扯皮没问题了
  8. POI处理超过65536条记录
  9. Java 多线程 简单实例 (Thread)
  10. JavaScript 盖尔-沙普利算法
  11. FPGA原理图设计----Arria II 系列FPGA设计(SATA)
  12. Swift和Object-C的区别和优缺点
  13. 四年STM32研发的工作感悟
  14. SAS逻辑回归之多分类
  15. Voters in Appalachia Struggling to Identify With Presidential Candidates
  16. 大专毕业,从6个月开发转入测试岗位的一些感悟——写在测试岗位3年之际
  17. Java 获取姓氏并获取姓氏的笔画数
  18. Android避坑指南,Gson与Kotlin碰撞出一个不安全的操作
  19. linux分子结构,使用命令babel转换分子结构文件
  20. 34. 在排序数组中查找元素的第一个和最后一个位置给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。如果数组中不存在目标

热门文章

  1. 手机5g什么时候普及_5G 网络什么时候普及,现在购买 4G 手机划算吗?
  2. 2015阿里系统工程师面试经验分享(广州站)
  3. Canvas3——绘制渐变图形与绘制变形图形
  4. 测试是ufs3.0的软件,UFS3.0极限测试 安装1000个App后手机会不会变卡
  5. 北京卓镭激光完成近亿元B轮融资,君联资本领投...
  6. matlab中示波器种类,什么是示波器?示波器的种类和使用方法图解
  7. 清除dns缓存cmd命令行方式
  8. linux mate主题目录,七大顶级Linux桌面:Cinnamon和MATE_服务器_服务器产业-中关村在线...
  9. Java开发面试常见的技术问题整理
  10. Hadoop Streaming 实战: 实用Partitioner类KeyFieldBasedPartitioner