当前位置:   article > 正文

数据结构-归并排序递归/非递归的代码实现(详解)_归并排序非递归实现

归并排序非递归实现

目录

归并排序的递归实现:

基本思想:

动图演示:

代码实现:

思路详解:

归并排序的非递归实现:

代码实现1:

思路详解:


归并排序的递归实现:

基本思想:
 

归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分(Divide andConquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并

动图演示:

 

 

代码实现:

  1. void _MergeSort(int* a, int begin, int end, int* tmp)
  2. {
  3. if (begin == end)//递归的结束条件
  4. {
  5. return;
  6. }
  7. int midi = (begin + end) / 2;//作为分割线
  8. _MergeSort(a, begin, midi,tmp);//递归左区间,让左区间有序
  9. _MergeSort(a, midi + 1, end,tmp);//递归右区间,让右区间有序
  10. int begin1 = begin;//左右区间都有序后,归并两区间内的数据
  11. int end1 = midi;
  12. int begin2 = midi + 1;
  13. int end2 = end;
  14. int i = begin;
  15. while (begin1 <= end1 && begin2 <= end2)//归并两区间内的数组数据,两数组中有一方遍历结束就结束了
  16. {
  17. if (a[begin1] <=a[begin2])//较小者插入tmp数组中
  18. {
  19. tmp[i++] = a[begin1++];
  20. }
  21. else
  22. {
  23. tmp[i++] = a[begin2++];
  24. }
  25. }
  26. while (begin1 <= end1)//若是两数组长度不一致,且数组1长度>数组2,则把数组1的剩余的所有数据插入到tmp中
  27. {
  28. tmp[i++] = a[begin1++];
  29. }
  30. while (begin2 <= end2)//数组2长度>数组1,把数组2剩余的所有数据插入到tmp中
  31. {
  32. tmp[i++] = a[begin2++];
  33. }
  34. memcpy(a + begin, tmp + begin, sizeof(int) * (end - begin + 1));//整个区间在tmp数组归并有序后拷贝至a数组中原来的位置
  35. }
  36. void MergeSort(int* a, int n)
  37. {
  38. int* tmp = (int*)malloc(sizeof(int) * n);//用来归并
  39. _MergeSort(a, 0, n - 1, tmp);
  40. return 0;
  41. }

 

 

思路详解:

递归的结束条件:begin==end(即区间仅有1个值)那么整个区间已经有序,无需递归让它有序了

采用临时数组tmp的原因:直接在原数组内进行归并会导致覆盖 

对于无序的区间:

1 将整个区间划分为左右区间

2 递归使得左区间有序 递归使得右区间有序

3 左右区间都有序后,对左右区间中的数组数据进行归并:(按升序)

  每一轮比较得两数组中较小者,插入到临时数组tmp中

  结束条件:两数组中有一方遍历完循环就结束了

  注意:可能存在一个数组遍历完,另一方还没遍历完的情况,所以需要让未遍历完的数组直接全部插入到tmp数组中,无需再比较

4 将再tmp数组中归并得到的整个有序区间拷贝至原数组空间

归并排序的非递归实现:

代码实现1:

  1. void MergeSortNonR(int* a, int n)
  2. {
  3. int* tmp = (int*)malloc(sizeof(int) * n);
  4. int gap = 1;//一一归,两两归,四四归……
  5. while (gap < n)
  6. {
  7. //某数字归:
  8. int i = 0;
  9. int j = 0;
  10. for (i = 0; i < n; i+=2*gap)//某数字归中的所有大组的归并有序
  11. {
  12. //一大组中的两小组的归并有序
  13. int begin1 = i;
  14. int end1 = i + gap - 1;
  15. int begin2 = i + gap;
  16. int end2 = i + 2 * gap - 1;
  17. if (end1 >= n || begin2>=n)//两小组中仅有存在第一组,且第一组的个数不足,第二组完全不存在 or//两小组中第一组完整存在(个数足够),第二组完全不存在
  18. {
  19. break;//不用进行归并,第一组的有效数据直接留在原数组
  20. }
  21. else if (end2 >= n)//第一组完全存在,第二组存在一部分(个数不足)
  22. {
  23. end2 = n - 1;//更正end2的范围
  24. }
  25. while (begin1 <= end1 && begin2 <= end2)//两组数据的归并
  26. {
  27. if (a[begin1] <= a[begin2])
  28. {
  29. tmp[j++] = a[begin1++];
  30. }
  31. else
  32. {
  33. tmp[j++] = a[begin2++];
  34. }
  35. }
  36. while (begin1 <= end1)
  37. {
  38. tmp[j++] = a[begin1++];
  39. }
  40. while (begin2 <= end2)
  41. {
  42. tmp[j++] = a[begin2++];
  43. }
  44. memcpy(a + i, tmp + i, sizeof(int) * (end2 - i + 1));//归并一大组,拷贝一大组
  45. }
  46. gap *= 2;//某数字归的迭代,比如由--归变成两两归……
  47. }
  48. free(tmp);
  49. }

思路详解:

 

注意:两小组(用于归并的两个组)的下标可能发生越界,即本来规定的每小组的数据个数应该是那么多,但是可能出现某小组个数不足的情况,那这时候下标就会越界:

有两种解法方法:

法一:归并一组,拷贝一组,两小组中有一方不存在的那这两小组不用归并,直接让存在的那一组留在原数组,如情况1、情况2

       若是两小组都存在,但是有一组是部分存在,那就修正越界的那一部分对两小组有效的数据归并 ,如情况3

如上面的代码实现1

法二:整组归并到tmp数组后再整组拷贝至原数组

对于情况1:不用执行两组的归并(有一组不存在),且修正第一组的范围,让第一组的所有有效数据直接拷贝插入到tmp

                    不执行归并的方法:将第二组的范围设为不存在

对于情况2:不执行两小组的归并,只需将 将第二组的范围设为不存在,让第一组的所有有效数据直接拷贝插入到tmp

对于情况3: 两小组均有存在的有效数据,可以进行归并,只需修正第二组越界的范围

如下面的代码实现2

代码实现2:

  1. void MergeSortNonR(int* a, int n)
  2. {
  3. int* tmp = (int*)malloc(sizeof(int) * n);
  4. int gap = 1;
  5. while (gap < n)
  6. {
  7. int i = 0;
  8. int j = 0;
  9. for (i = 0; i < n; i+=2*gap)
  10. {
  11. int begin1 = i;
  12. int end1 = i + gap - 1;
  13. int begin2 = i + gap;
  14. int end2 = i + 2 * gap - 1;
  15. if (end1 >= n)//两小组不会进行归并,且需修改更正第一组的有效范围,让第一组直接拷贝到tmp数组中
  16. {
  17. end1 = n - 1;
  18. begin2 = n;
  19. end2 = n - 1;
  20. }
  21. else if (begin2 >= n)//两小组不会进行归并,第一组直接拷贝到tmp数组中
  22. {
  23. begin2 = n;
  24. end2 = n - 1;
  25. }
  26. else if(end2>=n)//两小组会进行归并,只需修正第二组越界的范围
  27. {
  28. end2 = n - 1;
  29. }
  30. while (begin1 <= end1 && begin2 <= end2)
  31. {
  32. if (a[begin1] <= a[begin2])
  33. {
  34. tmp[j++] = a[begin1++];
  35. }
  36. else
  37. {
  38. tmp[j++] = a[begin2++];
  39. }
  40. }
  41. while (begin1 <= end1)
  42. {
  43. tmp[j++] = a[begin1++];
  44. }
  45. while (begin2 <= end2)
  46. {
  47. tmp[j++] = a[begin2++];
  48. }
  49. }
  50. memcpy(a, tmp, sizeof(int) * n);//整组归并有序后整组拷贝至原数组
  51. gap *= 2;
  52. }
  53. free(tmp);
  54. }

 总结:

归并排序的非递归实现的坑点比较多,有代码实现1和代码实现2

代码实现1就是归并一组,拷贝一组,代码实现2是整租归并有序后再整组拷贝至原数组 

坑点:注意三种数组范围越界的情况         

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/天景科技苑/article/detail/751584
推荐阅读
相关标签
  

闽ICP备14008679号