【算法】排序算法——归并排序

╰+哭是因爲堅強的太久メ 2022-05-25 06:09 568阅读 0赞

#

【fishing-pan:https://blog.csdn.net/u013921430转载请注明出处】

前言

归并排序是分治法在排序问题上的运用,因此为了更好地了解归并排序,首先了解一下分治法。分治法的基本思想是:将原问题分解为几个规模较小但是类似于原问题的子问题,递归地求解这些子问题,然后合并子问题的解来建立原问题的解。

分治模式在每层递归时有三个步骤:

分解原问题为若干子问题,这些子问题是原问题的规模较小的实例;

解决子问题,递归地求解子问题;若子问题足够小,直接求解;

合并子问题的解,获得原问题的解。

归并排序算法遵循分治模式,操作如下:

分解:分解待排序的具有n个元素的序列,成为具有n/2个元素的两个子序列;

解决:使用归并排序递归地排序子序列;

合并:合并已排序的两个子序列产生已排序的答案。

归并排序

在归并排序中,首先将初始序列不断分解,当分解至子序列长度为1时,便开始递归排序。如下图所示,图中初始序列本分解为8个长度为1的子序列,而后开始归并排序;

20180509124319952 图 1

在进行排序时,需要一个函数merge()进行操作,在这个函数中有三个下标,分别指向两个子序列中的元素以及归并产生的有序序列中的位置;以上图第二层中的9、11与5、8序列为例,merge()函数执行的操作可以用下图表示。

20180509130445567 图 2

在上图中,第一次进行对比后将5放入归并序列中,然后将j与k各往后移动一位。在第二次对比完后,j已经移动到序列尾部,此时只剩下第一个序列中的元素,因为元素已经是有序的,所以直接放入归并序列即可。可以从这一步看出,merge()的时间复杂度为Θ(n)。

编写代码

首先设计一个函数merge_sort()对序列进行二分,当二分至子序列中只含有一个元素时,因为单个元素自身就是“有序”的,无需进行比较;然后将两个具有单个元素的子序列进行归并,这时候就需要调用merge()函数进行归并排序,返回有序的序列。如此往复,直至所有的元素都被排序。思路理清楚了,可以开始写代码;

  1. bool merge_sort(vector<int> &a,int begin,int end)
  2. {
  3. //--若只传递了一个参数,无需进行排序,直接返回
  4. if (begin >= end)
  5. {
  6. return true;
  7. }
  8. int mid = (end + begin) / 2;
  9. merge_sort(a, begin, mid);
  10. merge_sort(a, mid + 1, end);
  11. //--经过上面的归并后两个子序列内部已经是有序的,若两个子序列中第二个子序列的第一个元素已经大于第一个子序列的最后一个元素,则不需要排序;
  12. if (a[mid] <= a[mid + 1])
  13. {
  14. return true;
  15. }
  16. merge(a, begin, mid, mid + 1, end);
  17. return true;
  18. }

merge()函数的功能在上面已经介绍过了,输入参数是主序列以及四个序号,四个序列号分别表示两个子序列的区间。首先创建两个临时序列用于存放子序列,然后开始比较临时序列中的元素,将元素依次存放入主序列当中,实现代码如下;

  1. void merge(vector<int> &a, int l_begin, int l_end,int r_begin,int r_end)
  2. {
  3. //if (l_begin == l_end&&r_begin == r_end) //--如果只有一个元素,则只需比较一次比较了;
  4. //{
  5. // if (a[l_begin]>a[r_begin])
  6. // {
  7. // int s = a[r_begin];
  8. // a[r_begin] = a[l_begin];
  9. // a[l_begin] = s;
  10. // }
  11. // return ;
  12. //}
  13. //
  14. int leng1 = l_end - l_begin + 1;
  15. int leng2 = r_end - r_begin + 1;
  16. //--创建两个临时的vector进行存放序列;
  17. vector<int> temp_L;
  18. vector<int> temp_R;
  19. for (int i = 0; i < leng1; i++)
  20. {
  21. temp_L.push_back(a[l_begin+i]);
  22. }
  23. for (int i = 0; i < leng2; i++)
  24. {
  25. temp_R.push_back(a[r_begin + i]);
  26. }
  27. int i = 0;
  28. int j = 0;
  29. while (i < leng1 && j < leng2)
  30. {
  31. if (temp_L[i] <= temp_R[j])
  32. {
  33. a[l_begin + i + j] = temp_L[i];
  34. i++;
  35. }
  36. else
  37. {
  38. a[l_begin + i + j] = temp_R[j];
  39. j++;
  40. }
  41. }
  42. //--当某个其中一个序列的元素已经全部被放置进入归并后的序列,此时直接将第另一个剩下部分序列放入归并序列即可;
  43. while (i <leng1)
  44. {
  45. a[l_begin + i + j] = temp_L[i];
  46. i++;
  47. }
  48. while (j <leng2)
  49. {
  50. a[l_begin + i + j] = temp_R[j];
  51. j++;
  52. }
  53. return ;
  54. }

运行结果

原始序列

70

排序结果

70 1

算法分析

时间复杂度

观察图1可以知道在每一层中每个元素都需要merge()函数进行归并排序,每一层的时间复杂度为Θ(n),而在分解序列时,只需要找到序列的中点,这一步的时间复杂度为Θ(1),为低阶项;进行二分归并时,总共会有70 2层,所以时间复杂度为70 3=70 4,忽略低阶项后,算法的时间复杂度为70 5。(注:分析时间复杂度时常用70 6表示70 770 6但是在数学中代表70 8)。

空间复杂度

从代码中不难看出,在进行归并时需要一个临时的空间存放序列,所以不是原地排序,排序的空间复杂度为70 9

排序稳定性

归并排序是稳定排序。

完整代码

为了方便大家测试,在这里提供所有代码;

  1. //------------------------------------
  2. //----潘正宇,归并排序
  3. //----2018.01.25
  4. //------------------------------------
  5. #include <iostream>
  6. #include <fstream>
  7. #include <iomanip>
  8. #include <string>
  9. #include <vector>
  10. #include <sstream>
  11. using namespace std;
  12. void merge(vector<int> &a, int l_begin, int l_end, int r_begin, int r_end);
  13. bool merge_sort(vector<int> &a,int begin,int end)
  14. {
  15. //--若只传递了一个参数,无需进行排序,直接返回
  16. if (begin >= end)
  17. {
  18. return true;
  19. }
  20. int mid = (end + begin) / 2;
  21. merge_sort(a, begin, mid);
  22. merge_sort(a, mid + 1, end);
  23. //--经过上面的归并后两个子序列内部已经是有序的,若两个子序列中第二个子序列的第一个元素已经大于第一个子序列的最后一个元素,则不需要排序;
  24. if (a[mid] <= a[mid + 1])
  25. {
  26. return true;
  27. }
  28. merge(a, begin, mid, mid + 1, end);
  29. return true;
  30. }
  31. void merge(vector<int> &a, int l_begin, int l_end,int r_begin,int r_end)
  32. {
  33. //if (l_begin == l_end&&r_begin == r_end) //--如果只有一个元素,则只需比较一次比较了;
  34. //{
  35. // if (a[l_begin]>a[r_begin])
  36. // {
  37. // int s = a[r_begin];
  38. // a[r_begin] = a[l_begin];
  39. // a[l_begin] = s;
  40. // }
  41. // return ;
  42. //}
  43. //
  44. int leng1 = l_end - l_begin + 1;
  45. int leng2 = r_end - r_begin + 1;
  46. //--创建两个临时的vector进行存放序列;
  47. vector<int> temp_L;
  48. vector<int> temp_R;
  49. for (int i = 0; i < leng1; i++)
  50. {
  51. temp_L.push_back(a[l_begin+i]);
  52. }
  53. for (int i = 0; i < leng2; i++)
  54. {
  55. temp_R.push_back(a[r_begin + i]);
  56. }
  57. int i = 0;
  58. int j = 0;
  59. while (i < leng1 && j < leng2)
  60. {
  61. if (temp_L[i] <= temp_R[j])
  62. {
  63. a[l_begin + i + j] = temp_L[i];
  64. i++;
  65. }
  66. else
  67. {
  68. a[l_begin + i + j] = temp_R[j];
  69. j++;
  70. }
  71. }
  72. //--当某个其中一个序列的元素已经全部被放置进入归并后的序列,此时直接将第另一个剩下部分序列放入归并序列即可;
  73. while (i <leng1)
  74. {
  75. a[l_begin + i + j] = temp_L[i];
  76. i++;
  77. }
  78. while (j <leng2)
  79. {
  80. a[l_begin + i + j] = temp_R[j];
  81. j++;
  82. }
  83. return ;
  84. }
  85. void main()
  86. {
  87. ifstream inmyfile("123.txt");
  88. ofstream outmyfile("1234.txt");
  89. string line;
  90. vector<int> A;
  91. if (inmyfile)
  92. {
  93. int x;
  94. while (getline(inmyfile,line))
  95. {
  96. istringstream is(line);
  97. string s;
  98. while (is>>s)
  99. {
  100. x = atoi(s.c_str());
  101. A.push_back(x);
  102. }
  103. }
  104. }
  105. else
  106. {
  107. cout << "get input file was fail" << endl;
  108. }
  109. int len = A.size();
  110. merge_sort(A, 0, len-1);
  111. for (int i = 0; i < len; i++)
  112. {
  113. outmyfile << A[i] << " ";
  114. }
  115. cout << endl;
  116. cout << A[10];
  117. system("pause");
  118. }

已完。。

发表评论

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

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

相关阅读

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

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

    相关 排序算法-归并排序

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

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

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

    相关 排序算法归并排序

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