排序算法 - 归并排序

偏执的太偏执、 2023-08-17 16:43 43阅读 0赞

基本思路

归并排序的基本思想是:首先将a[0..n-1]看成是n个长度为1的有序表,将相邻的k(k≥2)个有序子表成对归并,得到n/k个长度为k的有序子表;然后再将这些有序子表继续归并,得到n/k2个长度为k2的有序子表,如此反复进行下去,最后得到一个长度为n的有序表。

若k=2,即归并在相邻的两个有序子表中进行的,称为二路归并排序。若k>2,即归并操作在相邻的多个有序子表中进行,则叫多路归并排序。

例如

对于{2,5,1,7,10,6,9,4,3,8}序列,其自底向上的排序过程如下图所示,图中方括号内是一个有序子序列。

1475571-20190815215458788-423458569.png

  循环log2n次,length依次取1、2、…、log2n。每次执行以下步骤:

  ① 分解:将原序列分解成length长度的若干子序列。

  ② 求解子问题:将相邻的两个子序列调用Merge算法合并成一个有序子序列。

  ③ 合并:由于整个序列存放在数组中a中,排序过程是就地进行的,合并步骤不需要执行任何操作。

自顶向下的排序过程如下图所示

1475571-20190815215439644-970972319.png

  ① 分解:将序列a[low..high]一分为二,即求mid=(low+high)/2;递归地对两个子序列a[low..mid]和a[mid+1..high]进行继续分解。其终结条件是子序列长度为1(因为一个元素的子表一定是有序表)。

  ② 合并:与分解过程相反,将已排序的两个子序列a[low..mid]和a[mid+1..high]归并为一个有序序列a[low..high]。

算法代码

  1. 1 //对区间a[low..mid]和区间a[mid+1..high]进行排序
  2. 2 void Merge(int a[], int low, int mid, int high)
  3. 3 {
  4. 4 int i = low, j = mid + 1;
  5. 5 int k = 0;
  6. 6 int *temp = (int *)malloc((high - low + 1) * sizeof(int));
  7. 7 while (i <= mid && j <= high)
  8. 8 if (a[i] <= a[j]) //将第1子表中的元素放入temp中
  9. 9 {
  10. 10 temp[k] = a[i];
  11. 11 i++;
  12. 12 k++;
  13. 13 }
  14. 14 else //将第2子表中的元素放入temp中
  15. 15 {
  16. 16 temp[k] = a[j];
  17. 17 j++;
  18. 18 k++;
  19. 19 }
  20. 20 while (i <= mid) //将第1子表余下部分复制到temp
  21. 21 {
  22. 22 temp[k] = a[i];
  23. 23 i++;
  24. 24 k++;
  25. 25 }
  26. 26 while (j <= high) //将第2子表余下部分复制到temp
  27. 27 {
  28. 28 temp[k] = a[j];
  29. 29 j++;
  30. 30 k++;
  31. 31 }
  32. 32 for (k = 0, i = low; i <= high; k++, i++) //将temp复制回a中对应的位置
  33. 33 a[i] = temp[k];
  34. 34 free(temp); //释放temp所占内存空间
  35. 35 }

自底向上的二路归并排序算法

  1. 1 void MergePass(int a[], int length, int n) //一趟二路归并排序
  2. 2 {
  3. 3 int i;
  4. 4
  5. 5 for (i = 0; i + 2 * length - 1 < n; i = i + 2 * length) //归并length长的两相邻子表
  6. 6 {
  7. 7 Merge(a, i, i + length - 1, i + 2 * length - 1);
  8. 8 }
  9. 9
  10. 10 if (i + length - 1 < n) //余下两个子表,后者长度小于length
  11. 11 {
  12. 12 Merge(a, i, i + length - 1, n - 1); //归并这两个子表
  13. 13 }
  14. 14 }
  15. 15
  16. 16 void MergeSort(int arr[], int n) //二路归并算法
  17. 17 {
  18. 18 for (int length = 1; length < n; length = 2 * length)
  19. 19 {
  20. 20 MergePass(arr, length, n);
  21. 21 }
  22. 22 }

自顶向下的二路归并排序算法

  1. 1 void MergeSort(int a[], int low, int high)
  2. 2 //二路归并算法
  3. 3 {
  4. 4 int mid;
  5. 5 if (low < high) //子序列有两个或以上元素
  6. 6 {
  7. 7 mid = (low + high) / 2; //取中间位置
  8. 8 MergeSort2(a, low, mid); //对a[low..mid]子序列排序
  9. 9 MergeSort2(a, mid + 1, high); //对a[mid+1..high]子序列排序
  10. 10 Merge(a, low, mid, high); //将两子序列合并,见前面的算法
  11. 11 }
  12. 12 }

算法分析

对于上述二路归并排序算法,当有n个元素时,需要log2n趟归并,每一趟归并,其元素比较次数不超过n-1,元素移动次数都是n,因此归并排序的时间复杂度为O(nlog2n)。

转载于:https://www.cnblogs.com/WindSun/p/11360870.html

发表评论

表情:
评论列表 (有 0 条评论,43人围观)

还没有评论,来说两句吧...

相关阅读

    相关 排序算法——归并排序

    引言 归并排序可以使用递归或迭代的方式来实现,时间复杂度是都是 O(N \ logN)。 归并排序的核心是将待排序数组分组,可以整体二分,也可以设置步长迭代切分。归并排

    相关 排序算法-归并排序

    归并排序也是一个比较快速的排序算法,其思想是运用分治的思想,先对要排序的数进行分,每次从中间分成两部分,然后知道分成最小,然后在把他们合起来,边合起来边排序,最后有序,每次分的

    相关 排序算法——归并排序

    前言 将待排序序列R\[0...n-1\]看成是n个长度为1的有序序列,将相邻的有序表成对归并,得到n/2个长度为2的有序表;将这些有序序列再次归并,得到n/4个长度为4

    相关 排序算法归并排序

    一、前言     归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用 分治法(Divide and Conquer) 的一个非常典型的应用。 --------