当前位置: 首页 > news >正文

算法 -归并排序

博客主页:【夜泉_ly】
本文专栏:【算法】
欢迎点赞👍收藏⭐关注❤️

在这里插入图片描述

文章目录

  • 🔀 归并排序
    • 📖 简介
    • 🖼️ 示意图
    • 💡 实现思路
    • 💻 代码实现
    • 💡 实现思路2 - 非递归
    • 💻 代码实现

🔀 归并排序

📖 简介

  • 稳定 的排序
  • 时间复杂度 O ( n log ⁡ n ) O(n \log n) O(nlogn)
  • 空间复杂度 O ( n ) O(n) O(n),由于需要额外的临时数组来存储合并结果。
  • 常用于,处理 链表排序文件排序

🖼️ 示意图

归并排序 − 示意图 归并排序-示意图 归并排序示意图
在这里插入图片描述
请添加图片描述

💡 实现思路

先来看看具体是怎么排的:
第一步:先拿到需要整理的牌,将它们摊开。
在这里插入图片描述

第二步:将这些牌分成较小的组,每组包含一张牌:
在这里插入图片描述

第三步:对相邻的两组依次进行排序:
请添加图片描述

第四步:把刚刚相邻的两组看做为一组:
在这里插入图片描述

第五步及以后:重复上面的第三步、四步:
请添加图片描述
在这里插入图片描述
请添加图片描述
最后,由于只剩一组,排序结束。
怎么样,很简单吧 ~~
现在,我们来看看我们需要做什么。
在上面,看似用了很多步,其实很多步骤是重复的。
而重复,代表着可以试试递归。
总结一下:

  • 步骤一、分组
  • 步骤二、合并
  • 步骤三、判断是否结束

那么我们需要关注的是什么?

  • 一、怎么分组
  • 二、怎么合并
  • 三、看看什么时候结束

展开讲讲:

void merge_sort(int* arr, int left, int right)

一、怎么分?
那必须二分,想都不用想。
至于为什么?
首先是方便,其次是方便,然后是方便,
最后是效率稍高。

	int mid = (left + right) >> 1;

二、怎么合?
把上面的图抓下来:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这几张,就是分完后的样子。
我这里排的是升序,
可以看见,这里的每一组也都是升序:
在这里插入图片描述
这样看,合并就简单了。
因为我们只需要合并两个有序序列。
而如何保证这两个序列有序?
很简单,先递归:

	merge_sort(arr, left, mid);merge_sort(arr, mid + 1, right);

再合并:

	vector<int> tmp(right - left + 1);int l = left, r = mid + 1, cur = 0;while (l <= mid && r <= right) tmp[cur++] = arr[l] <= arr[r] ? arr[l++] : arr[r++];while (l <= mid) tmp[cur++] = arr[l++];while (r <= right) tmp[cur++] = arr[r++];for (int i = 0; i < cur; i++) arr[i + left] = tmp[i];

递归至最后,长度变为 1 ,此时必为有序。
而如何保证最后一层长度为 1 ?
这就需要下面一步了:
三、怎么停?
看看传参:(int* arr, int left, int right)
嗯,长度 == right - left + 1;
那么我们想让长度大于等于 1。
即,我们不想长度小于 1:

	if (left >= right) return;

把这段放在函数开头,
然后
在这里插入图片描述

💻 代码实现

void merge_sort(int* arr, int left, int right)
{// 递归结束条件if (left >= right) return;// 找到中间位置int mid = (left + right) >> 1;// 左右递归merge_sort(arr, left, mid);merge_sort(arr, mid + 1, right);// 辅助数组,存合并结果vector<int> tmp(right - left + 1);// 合并两个有序序列int l = left, r = mid + 1, cur = 0;while (l <= mid && r <= right) tmp[cur++] = arr[l] <= arr[r] ? arr[l++] : arr[r++];while (l <= mid) tmp[cur++] = arr[l++];   // 将左侧剩余元素拷贝while (r <= right) tmp[cur++] = arr[r++]; // 将右侧剩余元素拷贝// 将合并结果拷回原数组for (int i = 0; i < cur; i++) arr[i + left] = tmp[i];
}

在这里插入图片描述
写的时候记得注意边界,
最好把上图红色部分画出来。
不熟的话,别只用脑子想,容易红温。

在这里插入图片描述

💡 实现思路2 - 非递归

刚刚的递归,帮我们简化了什么?
分组。
递归帮我们把组分好了。

那么非递归,我们还需要做什么?
分组。

怎么分?
为了保证每次的各组都是有序的,
最小的长度为 1 ,之后两两合并。

具体怎么做?
先把下面这个图画出来:
在这里插入图片描述
然后确定每组长度:

for (int len = 1; len < n; len *= 2) {

从一开始,每次乘二。
len < n,怎么来的?
很简单,len 是子数组的长度。
子数组是排好了的。
len >= n, 说明数组就排好了。

之后,确定两组的左边界:

for (int left = 0; left < n - len; left += 2 * len) {

有了左边界长度
就可以补全图中红字部分:

在这里插入图片描述

int mid = left + len - 1, right = min(n, left + 2 * len) - 1;
int l = left, r = mid + 1;

之后的合并,就和递归一样了。

最后补充说明一下边界情况:
一、left < n - len
唔。。。n - len 是为了放溢出。
便于理解的方式是这样写:left + len < n
倒过来看,就是 left + len >= n
说明了啥?
说明右边的数组不存在!
在这里插入图片描述
因此,继续循环的条件是 left < n - len
二、right = min(n, left + 2 * len) - 1
把上面的图稍稍改造,就能明白为什么这样写了:
在这里插入图片描述

💻 代码实现

void merge_sort(int* arr, int n)
{vector<int> tmp(n);for (int len = 1; len < n; len *= 2){for (int left = 0; left< n - len; left += 2 * len){int mid = left + len - 1;int right = min(n, left + 2 * len) - 1;int l = left, r = mid + 1, cur = left;while (l <= mid && r <= right)tmp[cur++] = arr[l] <= arr[r] ? arr[l++] : arr[r++];while (l <= mid) tmp[cur++] = arr[l++];while (r <= right) tmp[cur++] = arr[r++];for (cur = left; cur <= right; cur++) arr[cur] = tmp[cur];}}
}

在这里插入图片描述


希望本篇文章对你有所帮助!并激发你进一步探索编程的兴趣!
本人仅是个C语言初学者,如果你有任何疑问或建议,欢迎随时留言讨论!让我们一起学习,共同进步!


http://www.mrgr.cn/news/83126.html

相关文章:

  • esp32开发笔记之一:esp32开发环境搭建vscode+ubuntu
  • js实现链接下载本地,图片点击非查看也是下载本地,文件流blob文件下载本地
  • go语言学习 笔记 1(变量,语法,数据类型)
  • thinnkphp5.1和 thinkphp6以及nginx,apache 解决跨域问题
  • Couldn‘t resolve host name for http://mirrorlist.centos.org
  • R语言的循环实现
  • 华为OD E卷(100分)50-预订酒店
  • 示波器Oscilloscope的使用方法
  • (长期更新)《零基础入门 ArcGIS(ArcScene) 》实验七----城市三维建模与分析(超超超详细!!!)
  • 学习记录:C++ 中 const 引用的使用及其好处
  • Spring AMQP-保证消费者消息的可靠性
  • 通俗易懂之线性回归时序预测PyTorch实践
  • 在 Ubuntu 22.04 上部署 AppArmor 应用安全教程
  • 现场展示deepseek VS openAI o1模型大对比
  • 论文笔记:FDTI: Fine-grained Deep Traffic Inference with Roadnet-enriched Graph
  • STM32供电参考设计
  • Windows下安装最新版的OpenSSL,并解决OpenSSL不是当前版本的问题,或者安装不正确的问题
  • 如何在 Ubuntu 22.04 上配置 Logrotate 高级教程
  • SpringBoot操作spark处理hdfs文件
  • 机器学习之随机森林算法实现和特征重要性排名可视化
  • B树及其Java实现详解
  • 《Spring Framework实战》7:4.1.2.容器概述
  • 【Rust自学】11.1. 编写和运行测试
  • 如何使用vue引入three.js
  • 人工智能的发展领域之GPU加速计算的应用概述、架构介绍与教学过程
  • 7ZIP 常见使用问题解决办法