一.归并排序原理(Wikipedia)
归并排序本质是分治思想的应用,并且各层分治递归可以同时进行
1.申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
2.设定两个指针,最初位置分别为两个已经排序序列的起始位置
3.比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
4.重复步骤3直到某一指针到达序列尾
5.将另一序列剩下的所有元素直接复制到合并序列尾
二.过程
原始数据
seg = 1时
我的算法参考的是wikipedia上的算法,与VisuAlgo上所示的稍有区别,VisuAlgo上是先2个数字合并,然后合并成4个有序数列,然后在对后面的2个合并,在合并成4个有序数列,把这两组有序数列合并成一个长度为8的有序数列。
wikipedia上,是先将所有的单个数字合并成有序的长度为2的有序数组,然后在将所长度为2的分别合并成长度为4的有序数组,循环知道合成长度为Len的有序数组。个人认为wikipedia上的方法更加容易表达,因为不用回退可以一次性处理完所有相同长度的数据。
wikipedia上代码的排序过程如图所示:
3.代码(参考了wikipedia上的代码,只需要第一次分配一次空间)
1 #include <iostream> 2 #include <vector> 3 #include <algorithm> 4 using namespace std; 5 6 template <typename T> 7 void MergeSort( vector<T> & nums ) { 8 int seg = 1; 9 int len = nums.size(); 10 vector<T> CopyNums(nums); 11 for(int seg = 1; seg < len; seg += seg ){ 12 for(int start = 0; start < len; start += seg + seg ){ 13 int low = start; 14 int mid = min( start + seg, len );//对于最后一次归并 15 int high = min( start + seg + seg, len );//最后一次归并长度会大于len 16 int k = low; 17 int start1 = low; 18 int end1 = mid; 19 int start2 = mid; 20 int end2 = high; 21 while( start1 < end1 && start2 < end2 ){ 22 CopyNums[k++] = nums[start1]<nums[start2]? nums[start1++]:nums[start2++]; 23 } 24 while( start1 < end1 ){ 25 CopyNums[k++] = nums[start1++];//如果start1后还有元素没有归并,则将start1后的元素进行归并 26 } 27 while( start2 < end2 ){ 28 CopyNums[k++] = nums[start2++];////如果start2后还有元素没有归并,则将start2后的元素进行归并 29 } 30 } 31 nums.swap(CopyNums);//在C++ STL中,耗时是常量,因为实际并没有交换他们的值,而只是交换了指针。 32 //(i.e., the containers exchange references to their data, without actually performing any element copy or movement)37 } 38 } 39 40 int main(){ 41 vector<int> nums{3,44,38,5,47,15,36,26,27,2,46,4,19,50,48}; 42 cout<<" Before Sort:" ; 43 for( auto m: nums){ 44 cout << m <<" "; 45 } 46 cout<<endl; 47 MergeSort( nums ); 48 cout<< " After Sort:"; 49 for( auto m: nums){ 50 cout << m <<" "; 51 } 52 cout<<endl; 53 }
4.总结
1.空间复杂度,因为只有开始分配了一次与原始数据相同长度的空间,所以空间复杂度为O(n);
2.时间复杂度,总的比较次数在(nlogn)/2和nlogn-n+1之间;
3.归并排序是稳定的排序算法。