归并排序(递归和非递归)

ゝ一纸荒年。 2024-03-24 17:10 135阅读 0赞

归并排序

  • 一.递归
  • 二.非递归
    • 1.基础
    • 2.改进

一.递归

归并实际上就是把一个数组每次二分,分别排序每个子数组再将有序的子数组进行合并。(归并排序需要一个额外的数组来存放每个子数组,并在排序结束后将其拷贝到原数组)。

在这里插入图片描述

  1. #include<stdio.h>
  2. #include<string.h>
  3. #include<time.h>
  4. #include<stdlib.h>
  5. void _MergeSort(int* a, int begin, int end, int* tmp)
  6. {
  7. if (begin >= end)
  8. return;
  9. int mid = (begin+end) / 2;
  10. _MergeSort(a, begin, mid,tmp);
  11. _MergeSort(a, mid + 1, end,tmp);
  12. int begin1 = begin, begin2 = mid + 1;
  13. int end1 = mid, end2 = end;
  14. int i = begin;
  15. while (begin1 <= end1 && begin2 <= end2)
  16. {
  17. if (a[begin1] < a[begin2])
  18. {
  19. tmp[i++] = a[begin1++];
  20. }
  21. else
  22. {
  23. tmp[i++] = a[begin2++];
  24. }
  25. }
  26. while (begin1 <= end1)
  27. {
  28. tmp[i++] = a[begin1++];
  29. }
  30. while (begin2 <= end2)
  31. {
  32. tmp[i++] = a[begin2++];
  33. }
  34. memcpy(a+begin, tmp+begin, sizeof(int)*(end - begin + 1));
  35. }
  36. void MergeSort(int* a, int n)
  37. {
  38. int* tmp = (int*)malloc(sizeof(int) * n);
  39. if (tmp == NULL)
  40. {
  41. perror("malloc fail");
  42. return;
  43. }
  44. _MergeSort(a,0,n-1,tmp);
  45. free(tmp);
  46. }
  47. int main()
  48. {
  49. int a[20000];
  50. srand((unsigned int)time(NULL));
  51. for (int i = 0; i < 20000; i++)
  52. {
  53. a[i] = rand() % 1000;
  54. }
  55. MergeSort(a,20000);
  56. int st = 0;
  57. for (int i = 0; i < 19998; i++)
  58. {
  59. if (a[i] > a[i + 1])
  60. {
  61. printf("%d->%d,%d\n", a[i], a[i + 1], i);
  62. st = 1;
  63. break;
  64. }
  65. }
  66. if (st)
  67. {
  68. printf("out of order");
  69. }
  70. else
  71. {
  72. printf("order");
  73. }
  74. return 0;
  75. }

二.非递归

1.基础

归并是不断划分数组,直到每个数组里只有一个元素,那么我们就可以根据这个特性来进行非递归改造。

我们两个小组合并为一个大组进行循环每组里的元素个数为2*gap,例如我们从gap=1开始(模拟归并最后一层),那么下标为0和下标为1的就为一个大组,将这组里的两个元素归并(排序),赋值到tmp数组里,再将tmp拷贝到数组a里。

在这里插入图片描述

当然这只能对2,4,8…这些特殊2的指数倍个数的数组起作用(不然会发生越界),接下来就是处理边界问题。

2.改进

begin1是一大组里前面那个小组的开始位置,end1是该小组的结束位置。同理begin2是一大组里后面那个小组的开始位置,end2是该小组的结束位置。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  1. #include<stdio.h>
  2. #include<time.h>
  3. #include<stdlib.h>
  4. #include<string.h>
  5. void MergeSort(int* a, int n)
  6. {
  7. int* tmp = (int*)malloc(sizeof(int) * n);
  8. if (tmp == NULL)
  9. {
  10. perror("malloc fail\n");
  11. return;
  12. }
  13. int gap = 1;
  14. while (gap < n)
  15. {
  16. for (int i = 0; i < n; i += 2 * gap)
  17. {
  18. int begin1 = i, end1 = begin1 + gap-1;
  19. int begin2 = i+gap, end2 = begin2 + gap-1;
  20. int j = begin1;
  21. //修正
  22. //第一二种情况(ps:因为begin=i,所以不可能越界)
  23. if (end1 >= n || begin2 >= n)
  24. {
  25. break;//如果是这种情况就说明第二个小组完全越界,没必要进行归并了,直接跳出
  26. }
  27. if (end2 >= n)//第三种情况
  28. {
  29. end2 = n - 1;
  30. //这说明第二组有一部分没越界,那么我们只需要拷贝这一部分
  31. }
  32. while (begin1 <= end1 && begin2 <= end2)
  33. {
  34. if (a[begin1] < a[begin2])
  35. tmp[j++] = a[begin1++];
  36. else
  37. tmp[j++] = a[begin2++];
  38. }
  39. while (begin1 <= end1) tmp[j++] = a[begin1++];
  40. while (begin2 <= end2) tmp[j++] = a[begin2++];
  41. memcpy(a + i, tmp + i, sizeof(int) *(end2-i+1));//因为如果是第三种情况,那么第二组只能拷贝一部分
  42. }
  43. gap *= 2;
  44. }
  45. }
  46. int main()
  47. {
  48. int a[20000];
  49. srand((unsigned int)time(NULL));
  50. for (int i = 0; i < 20000; i++)
  51. {
  52. a[i] = rand() % 1000;
  53. }
  54. MergeSort(a, 20000);
  55. /*for (int i = 0; i < 4; i++)
  56. printf("%d ", a[i]);*/
  57. int st = 0;
  58. for (int i = 0; i < 19998; i++)
  59. {
  60. if (a[i] > a[i + 1])
  61. {
  62. printf("%d->%d,%d\n", a[i], a[i + 1], i);
  63. st = 1;
  64. break;
  65. }
  66. }
  67. if (st)
  68. {
  69. printf("out of order");
  70. }
  71. else
  72. {
  73. printf("order");
  74. }
  75. return 0;
  76. }

发表评论

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

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

相关阅读