归并排序(Merge Sort)与快速排序思想类似:将待排序数据分成两部分,继续将两个子部分进行递归的归并排序;然后将已经有序的两个子部分进行合并,最终完成排序。其时间复杂度与快速排序均为O(nlogn),但是归并排序除了递归调用间接使用了辅助空间栈,还需要额外的O(n)空间进行临时存储。从此角度归并排序略逊于快速排序,但是归并排序是一种稳定的排序算法,快速排序则不然。

所谓稳定排序,表示对于具有相同值的多个元素,其间的先后顺序保持不变。对于基本数据类型而言,一个排序算法是否稳定,影响很小,但是对于结构体数组,稳定排序就十分重要。例如对于student结构体按照关键字score进行非降序排序:

// A structure data definition
typedef struct __Student
{char name[16];int score;
}Student;
// Array of students
name :  A      B     C     D
score:  80     70    75    70Stable sort in ascending order:
name :  B      D     C     A
score:  70     70    75    80Unstable sort in ascending order:
name :  D      B     C     A
score:  70     70    75    80

其中稳定排序可以保证B始终在D之前;而非稳定排序,则无法保证。

1)数组的归并排序

归并排序的思想实际上是一种分治法,即将待排序数据分成两部分,分别对两部分排序,然后将这两部分合并。下面以非降序排序为例:

// Split arr[] into two parts [begin,mid), [mid,end)
// and using merge_core to merge this two parts
// Total Time O(nlogn)
void merge_sort(int arr[], int begin, int end)
{if (end-begin < 2) return;int mid = (begin+end)>>1;merge_sort(arr,begin,mid);merge_sort(arr,mid,end);merge_core(arr,begin,mid,end);
}   // Time O(logn)

其中arr[]为待排序数组,对于一个长度为N的数组,直接调用merge_sort(arr,0,N);则可以排序。

归并排序总体分为两步,首先分成两部分,然后对每个部分进行排序,最后合并。当然也可以分成三部分或其他,然而通常是分成两部分,因此又称为二路归并。merge_core可以将两个有序数组合并成一个,具体操作如图所示:

/* merge core: combine two parts which are sorted in ascending order* arr[]: ..., 1, 4, 8, 2, 3, 7, 9, ...*      begin__|        |__mid      |__end* part1: 1, 4, 8       part2: 2, 3, 7, 9 ** combination:* part1:[1]            [4]       [8]* part2: |   [2]  [3]   |   [7]   |   [9]*        |    |    |    |    |    |    |* tmp  :[1]  [2]  [3]  [4]  [7]  [8]  [9]** at last, copyback tmp to arr[begin,end)* */

合并的前提是,两个数组已经是有序的。其代码为:

void merge_core(int arr[], int begin, int mid, int end)
{int i=begin, j=mid, k=0;int *tmp = (int*)malloc(sizeof(int)*(end-begin));for(; i<mid && j<end; tmp[k++]=(arr[i]<arr[j]?arr[i++]:arr[j++]));for(; i<mid; tmp[k++]=arr[i++]);for(; j<end; tmp[k++]=arr[j++]);for(i=begin, k=0; i<end; arr[i++]=tmp[k++]);free(tmp);
}   // Time O(n), Space O(n)

其中第6,7两行,将剩余的部分追加到tmp[]中,然后将tmp[]写回到arr[]。因此,对于数组使用归并排序,需要辅助空间O(n)。由于是尾部调用merge_core,当然可以将其写入到merge_sort尾部,这里为了思路清晰,将其分成两部分书写。

2)链表的归并排序

事实上,归并排序更适合对链表排序,因为在合并两个链表时,不需要额外的辅助空间存储,而且也不需要对数据拷贝,直接移动指针即可。唯一的不便是:需要每次寻找到链表的中间节点,然后以此将该链表分割成两部分。寻找中间节点,可以定义两个指针fast和Mid,fast每次移动两步,mid每次移动一步,当fast到链表尾部时,mid此时处于链表中间(不用考虑奇偶情况):

// Merge sort for single list as ascending order
// single list node define
typedef struct __ListNode
{int val;struct __ListNode *next;
}ListNode;// Merge sort for single list without head node
ListNode *merge_sort(ListNode *head)
{if (head==NULL || head->next==NULL) return head;ListNode *fast, *mid, H;// find mid node between head and endfor (H.next=head, fast=mid=&H; fast && fast->next;){mid = mid->next;fast = fast->next->next;}fast = mid->next;mid->next = NULL;   // cut down mid part from head listmid = fast;head = merge_sort(head);mid = merge_sort(mid);return merge_core(head,mid);
}

注意,找到链表的中间节点后,务必将其指向NULL,以保证确实将链表分成两部分。然后将两个链表head与mid进行合并。由于合并后可能会修改链表头结点,因此要返回新的链表头结点。下面是合并操作:

// merge single list without head node (ascending order)
ListNode *merge_core(ListNode *i, ListNode *j)
{ListNode H, *p;for (p=&H; i && j; p=p->next){if (i->val < j->val){p->next = i;i = i->next;}else{p->next = j;j = j->next;}}p->next = (i ? i:j);return H.next;
}

链表合并时,不需要像数组那样,直接可以将链表尾部p->next指向剩余的i或j,即可完成合并。可以看出,归并排序更适合于对链表排序,而快速排序适合于数组排序。

注:本文涉及的源码:merge sort : https://git.oschina.net/eudiwffe/codingstudy/blob/master/src/sort/mergesort.c

转载于:https://www.cnblogs.com/eudiwffe/p/6254394.html

[算法]——归并排序(Merge Sort)相关推荐

  1. 经典排序算法 - 归并排序Merge sort

    经典排序算法 - 归并排序Merge sort 原理,把原始数组分成若干子数组,对每一个子数组进行排序, 继续把子数组与子数组合并,合并后仍然有序,直到全部合并完,形成有序的数组 举例 无序数组[6 ...

  2. C语言以递归实现归并排序Merge Sort算法(附完整源码)

    以递归实现归并排序Merge Sort 以递归实现归并排序Merge Sort算法的完整源码(定义,实现,main函数测试) 以递归实现归并排序Merge Sort算法的完整源码(定义,实现,main ...

  3. C语言归并排序Merge Sort算法(附完整源码)

    归并排序Merge Sort 归并排序Merge Sort算法的完整源码(定义,实现,main函数测试) 归并排序Merge Sort算法的完整源码(定义,实现,main函数测试) #include ...

  4. 排序算法二:归并排序(Merge sort)

    归并排序(Merge sort)用到了分治思想,即分-治-合三步,算法平均时间复杂度是O(nlgn). (一)算法实现 1 private void merge_sort(int[] array, i ...

  5. python选择排序算法图解_python基本算法之实现归并排序(Merge sort)

    0.前言 评判一个算法的好坏的标准: 时间复杂度 空间复杂度 1.归并排序算法是什么? 冒泡排序(Bubble Sort)是一种建立在归并操作上面的一种有效的排序算法,由John von neuman ...

  6. python 归并排序算法_python基本算法之实现归并排序(Merge sort)

    0.前言 评判一个算法的好坏的标准: 时间复杂度 空间复杂度 1.归并排序算法是什么? 冒泡排序(Bubble Sort)是一种建立在归并操作上面的一种有效的排序算法,由John von neuman ...

  7. 归并python_python基本算法之实现归并排序(Merge sort)

    0.前言 评判一个算法的好坏的标准: 时间复杂度 空间复杂度 1.归并排序算法是什么? 冒泡排序(Bubble Sort)是一种建立在归并操作上面的一种有效的排序算法,由John von neuman ...

  8. 归并有效排序算法matlab,科学网—[用MATLAB写算法]之排序算法2)归并排序merge sort - 徐勇刚的博文...

    归并排序(merge sort)是一种利用分治策略(divide and conquer)进行排序的算法,算法复杂度为 $\Theta (nlog_{2}n)$ . filename: merge_s ...

  9. python实现基本算法之归并排序(Merge sort)

    基本算法之归并排序(Merge sort) 基本算法-04.归并排序(Merge sort)算法 .往期请看选择排序,插入排序,归并排序,快速排序等等都发布的!欢迎大家批评指正! 文章目录 基本算法之 ...

  10. 归并排序(merge sort)算法实现

    归并排序(merge sort)体现了分治的思想,即将一个待排序数组分为两部分,对这两个部分进行归并排序,排序后,再对两个已经排序好的数组进行合并.这种思想可以用递归方式很容易实现.归并排序的时间复杂 ...

最新文章

  1. oracle rac ora 12537,解决11gR2 Rac ORA-12537: TNS:connection closed 一例
  2. python官网打不开-python为什么打不开
  3. 2017-9-14-Linux移植:加快Linux主机的启动速度
  4. 5 款阿里常用代码检测工具,免费用!
  5. zipoutputstream压缩文件响应到浏览器_现代浏览器内部机制之导航这件小事
  6. Spring Cloud中,Eureka常见问题总结
  7. c++判断一个数字是否为buzz number的算法(附完整源码)
  8. 相关的类代表的颜色和文本对齐方式
  9. CodeForces - 364A Matrix(思维+数学)
  10. java中compile函数用法_【转】关于java中Pattern.compile函数的相关解释
  11. linux里强制覆盖,Linux cp 强制覆盖(示例代码)
  12. 电脑工具栏怎么调整到下面_PDF格式的合同怎么盖公章?这不是在为难我胖虎吗?...
  13. windows无法格式化u盘_U盘无法格式化的解决方法
  14. 【机器学习原理】KNN分类算法
  15. 写给自己的一封信--平顶山学院20届计科学生大学两年成长经历回忆
  16. 关于个人的年度小目标
  17. 让oracle开机自动启动,设置CentOS下开机自动启动Oracle
  18. Android音乐浮窗播放器
  19. c语言综合合计实验报告,C语言设计实验报告(第一次)
  20. TPC817隔离光耦使用小结

热门文章

  1. 关于浮点数的误差理解
  2. 【 hdu3949 XOR】
  3. [YTU]_2475( C++习题 多重继承)
  4. Python3远程监控程序实现
  5. Jupyter Notebook修改默认工作路径
  6. Linux下使用expect实现跳板机自动跳转/免密登录/自动登录(转)
  7. 解决远程连接mysql错误1130
  8. jquery tab点击切换的问题
  9. javascript---》arguments对象
  10. thinkphp3.0部分总结