• 【数据结构】手撕归并排序(含非递归)


    目录

    一,归并排序(递归)

    1,基本思想

     2,思路实现

    二,归并排序(非递归)

    1,思路实现

    2,归并排序的特性总结:


    一,归并排序(递归)

    1,基本思想

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

    已有序的子序列合并,得到完全有序的序列;

    先使每个子序列有序,再使子序列段间有序,将两个有序表合并成一个有序表,称为二路归并;

    归并排序核心步骤:

     2,思路实现

    这个归并排序乍一看像一颗二叉树,事实也是如此,如上图所示我们需要不断的拆分直至拆成一个元素此时就是有序的然后再合并合并的时候不要选择原地合并(原地合并时间复杂度很高)需要开辟与数组同等大小的空间用来存放数据

    主函数整体框架:

    1. //归并排序
    2. void MergerSort(int* arr, int begin, int end)
    3. {
    4. if (begin >= end)
    5. {
    6. return;
    7. }
    8. //开辟同等大小数组
    9. int* tmp = (int*)malloc((end - begin + 1)*sizeof(int));
    10. //归并
    11. Merger(arr, tmp, begin, end);
    12. free(tmp);
    13. tmp = NULL;
    14. }

    然后我们就要开始实现 Merger 函数,是数据归并了;

    把数组拆分成一个数据后开始合并,刚开始一 一合并,然后二 二合并,然后四 四合并,直至全数组合并完;

    1. //归并
    2. void Merger(int* arr, int* tmp,int begin,int end)
    3. {
    4. int mid = (begin + end) / 2;
    5. if (begin == end)
    6. {
    7. return;
    8. }
    9. //排序【begin,mid】& 【mid+1,end】
    10. Merger(arr, tmp, begin,mid);
    11. Merger(arr, tmp, mid+1, end);
    12. int begin1 = begin, end1 = mid;
    13. int begin2 = mid + 1, end2 = end;
    14. int i = 0;
    15. while (begin1 <= end1 && begin2 <= end2)
    16. {
    17. if (arr[begin1] <= arr[begin2])
    18. {
    19. tmp[i++] = arr[begin1++];
    20. }
    21. else
    22. {
    23. tmp[i++] = arr[begin2++];
    24. }
    25. }
    26. while(begin1 <= end1)
    27. {
    28. tmp[i++] = arr[begin1++];
    29. }
    30. while (begin2 <= end2)
    31. {
    32. tmp[i++] = arr[begin2++];
    33. }
    34. //进行拷贝
    35. memcpy(arr + begin, tmp, (end - begin+1)*sizeof(int));
    36. }

    然后我们运行测试一下:

    可以看到是有序的,选择排序就 OK 了;

    其实跟二叉树的前序遍历有异曲同工之处,前后知识都是连贯起来的;

    二,归并排序(非递归

    1,思路实现

    现在我们来拿捏一下非递归版的归并排序,其实也还是换汤不换药;

    其实新思路是这个图的下半部分,我们先让数据一 一合并,然后再二 二合并,然后再四 四合并程倍数增长有人问如果越界了怎么办?没关系我们后面会做越界处理的

    直接上代码!

    1. //归并排序(非递归)
    2. void MergerSortNon(int* arr, int begin, int end)
    3. {
    4. if (begin >= end)
    5. {
    6. return;
    7. }
    8. //开辟同等大小数组
    9. int* tmp = (int*)malloc((end - begin + 1) * sizeof(int));
    10. int gap = 1;
    11. int j = 0;
    12. while (gap < end)
    13. {
    14. for (j = 0; j < end; j += 2 * gap)
    15. {
    16. int begin1 = j, end1 = begin1+gap-1;
    17. int begin2 =end1+1, end2 = begin2+gap-1;
    18. int i = 0;
    19. //处理边界问题
    20. if (end1 >= end)
    21. {
    22. break;
    23. }
    24. if (end2 >end)
    25. {
    26. end2 = end;
    27. }
    28. while (begin1 <= end1 && begin2 <= end2)
    29. {
    30. if (arr[begin1] <= arr[begin2])
    31. {
    32. tmp[i++] = arr[begin1++];
    33. }
    34. else
    35. {
    36. tmp[i++] = arr[begin2++];
    37. }
    38. }
    39. while (begin1 <= end1)
    40. {
    41. tmp[i++] = arr[begin1++];
    42. }
    43. while (begin2 <= end2)
    44. {
    45. tmp[i++] = arr[begin2++];
    46. }
    47. //进行拷贝
    48. memcpy(arr + j, tmp, (end2 - j+ 1) * sizeof(int));
    49. }
    50. gap *= 2;
    51. }
    52. free(tmp);
    53. tmp = NULL;
    54. }

    我们来运行测试一下:

    可以看到是有序的,选择排序就 OK 了;

    2,归并排序的特性总结:

    1, 归并的缺点在于需要O(N)的空间复杂度,归并排序的思考更多的是解决在磁盘中的外排序问题

    2, 时间复杂度:O(N*logN)

    3, 空间复杂度:O(N)

    4, 稳定性:稳定

    第四阶段就到这里了,带大家继续吃肉!

    后面博主会陆续更新;

    如有不足之处欢迎来补充交流!

    完结。。

  • 相关阅读:
    车间工厂看板还搞不定,数据可视化包教包会
    刚爆火就下线的 ZAO 换脸,背后是另一场技术人的狂欢
    从零开始带你实现一套自己的CI/CD(一)Jenkins
    System Generator学习——使用 AXI 接口和 IP 集成器
    Nacos注册中心6-Client端(获取调用服务的提供者列表)
    机器学习|决策树|如何计算信息增益|方法总结
    警惕看不见的重试机制:为什么使用RPC必须考虑幂等性
    79-基于STM32单片机的条形码扫描识别系统(实物图+源码+原理图+PCB+论文)
    Arthas快速入门
    机器学习期中考试
  • 原文地址:https://blog.csdn.net/m0_71676870/article/details/133578649