首页 > 代码库 > 归并排序

归并排序

归并排序

// 归并排序
 void Merge1(int arr[], int p, int mid, int r)
 {
	 int n1 = mid - p + 1; @1
	 int n2 = r - mid;

	 int *pLeft = new int[n1](); @2
	 int *pRight = new int[n2]();
	 for (int i = 0; i < n1; ++i) @3
	 {
		 pLeft[i] = arr[i+p];
	 }
	 for (int j = 0; j < n2; ++j) @4
	 {
		 pRight[j] = arr[j+mid+1];
	 }
	 int i = 0, j = 0; @5
	 int k = p; @6
	 while (i < n1 && j < n2) @7
	 {
		 if (pLeft[i] <= pRight[j]) @8
		 {
			 arr[k++] = pLeft[i++];
		 }else
		 {
			 arr[k++] = pRight[j++];
		 }
	 }
	 while (i < n1) @9
	 {
		 arr[k++] = pLeft[i++];
	 }
	 while (j < n2) @10
	 {
		 arr[k++] = pRight[j++];
	 }
	 delete []pLeft; @11
	 delete []pRight;
 }

 void MegerSort(int arr[], int lhs, int rhs)
 {
	 if (lhs < rhs)
	 {
		 int mid = (lhs + rhs)/2;
		 MegerSort(arr, lhs, mid);
		 MegerSort(arr, mid+1, rhs);
		 Merge1(arr, lhs, mid, rhs);
	 }	 
 }

归并排序和快速排序一样,也是基于分治法的排序,其平均复杂度O(nlgn),属于稳定排序。

核心思路:把一个待排序的序列划分成两个子序列,然后再把子序列继续划分下去,直到子序列的长度为1或为空跳出递归,然后开始合并,最开始的归并是把两个元素合成一个长度为2的有序序列,然后再往外层跳转,合并成一个长度为4的有序序列。把两个有序的子序列合并成一个序列是关键

@1:先确定子序列的长度

@2:开启辅助空间,存放原始的待排序列

@3和@4:把两个子序列分别复制到辅助空间,这里要考虑边界问题

@5和@6:i记录的是左序列的当前位置,j记录的是右序列的当前位置,k记录的是目标数组的下一个待排序位置。

@7:两个子序列都从左往右移动,每一次比较i和j所指向的位置的元素,小的复制到目标序列中,然后位置更新。i的最大值是n1-1,j的最大值是n2-1

@8:小于等于号是维护稳定性的关键,因为是非递减序列,所以左侧子序列较小,右侧子序列较大,如果两个相等的话,先让左侧子序列的元素复制到目标数组,两个相同的元素的相对位置不会改变

@9和@10:如果i到达n1或是j到达n2,就跳出了@7循环,然后进入@9或@10循环,意味着某个子序列已经全部进入目标数组,另一个子序列可以直接复制过去,这在排两个有序链表的时候,直接把另一个链表的指针复制到目标链表就可以了,这里是数组,还要一个一个的复制。

@11:删除辅助空间


归并排序优化

void Merge2(int arr[], int p, int q, int r)
 {
	 int n1 = q - p + 1;
	 int n2 = r - q;

	 int *pLeft = new int[n1+1](); @1
	 int *pRight = new int[n2+1]();
	 for (int i = 0; i < n1; ++i)
	 {
		 pLeft[i] = arr[i+p];
	 }
	 for (int j = 0; j < n2; ++j)
	 {
		 pRight[j] = arr[j+q+1];
	 }
	 pLeft[n1] = 0xFFFFFF; @2
	 pRight[n2] = 0xFFFFFF;
	 int i = 0, j = 0;
	 for (int k = p; k <= r; ++k) @3
	 {
		 if (pLeft[i] <= pRight[j])
		 {
			 arr[k] = pLeft[i++]; @4
		 }else
		 {
			 arr[k] = pRight[j++]; @5
		 }
	 }

	 delete []pLeft;
	 delete []pRight;
 }

这个不以两个子序列的区间为循环控制条件,而是以整个目标数组的区间[p, r]为循环控制条件。

@1:开辟多余一个空间

@2:存放足够大的值

@3:同样i和j指向子序列的当前待排序元素 位置,而循环控制条件为[p, r],因为任何一个子序列到达最后一个元素(也就是那个足够大的值位置)后,另外一个都不能大于它。假如左子序列先到达终点,则则判断条件始终会转向@5,进而把右子序列的剩余元素复制到目标数组;假如右子序列先到达终点,则判断条件始终转向@4。