归并排序 python C C++ 代码及解析

一,概念及其介绍

归并排序(Merge sort)是建立在归并操作上的一种有效、稳定的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;若将两个有序表合并成一个有序表,称为二路归并

二,复杂度说明

时间复杂度: O(nlogn)

对于自顶向下的归并排序, 归并排序通过不断将数组对半划分,直到每个子数组只有一个元素(这需要 logn次划分)。然后,合并这些子数组的过程中,每个元素都需要被处理一次,总共需要处理 n个元素。所以总的操作次数约为 nlog n ,因此时间复杂度为 O(nlog n)

空间复杂度:  O(n)

空间复杂度为 O(n) 是因为在合并子数组的过程中,需要额外创建一个与原数组大小相同的辅助数组来存放合并后的结果。所以空间复杂度主要取决于这个辅助数组的大小,即为 O(n)。

三,过程图示

归并排序是递归算法的一个实例,这个算法中基本的操作是合并两个已排序的数组,取两个输入数组 A 和 B,一个输出数组 C,以及三个计数器 i、j、k,它们初始位置置于对应数组的开始端。

A[i] 和 B[j] 中较小者拷贝到 C 中的下一个位置,相关计数器向前推进一步。

当两个输入数组有一个用完时候,则将另外一个数组中剩余部分拷贝到 C 中

四.代码及解析

4.1 python 

思路:

使用两个函数完成,一个函数用于连接两个有序列表,返回一个有序的新列表,一个函数用于递归将已知列表分为左右两部分,递归分割,再进行连接,当列表中只有一个元素时则一定是有序的

知识点:

1.列表的append用于向列表添加一个元素

2.列表的extend用于向列表添加一个可迭代对象

# merge_sort的主要作用是通过递归地将输入的数组不断地分割成更小的子数组,
# 直到子数组的长度为 1 (此时子数组本身就是有序的),然后调用 merge 函数将这些有序的子数组合并起来,
# 最终实现对整个数组的排序
def merge_sort(arr):if len(arr) <= 1:return arrmid = len(arr) // 2left_half = merge_sort(arr[:mid])right_half = merge_sort(arr[mid:])return merge(left_half, right_half)# arr = [12, 11, 13, 5, 6]
# merge函数用于连接两个有序数列。并存放到新数列中
def merge(left, right):merged = []left_index = 0right_index = 0# 两两对比,小的放入新列表,所在列表的下标指向往后移动,大的所在列表下标指向不变while left_index < len(left) and right_index < len(right):if left[left_index] < right[right_index]:merged.append(left[left_index])left_index += 1else:merged.append(right[right_index])right_index += 1# 利用切片将剩下的元素放到新列表中merged.extend(left[left_index:])merged.extend(right[right_index:])return merged# 测试示例
arr = [12, 11, 13, 5, 6]
sorted_arr = merge_sort(arr)
print("排序后的数组:", sorted_arr)
排序后的数组: [5, 6, 11, 12, 13]

4.2 C

思路:

使用两个函数,一个函数用于递归将数组左右两部分割开,再递归分割,直到分割到只有一个元素时一定是有序的,再进行两个有序数组的连接,这里再定义另一个函数作用是连接两个有序数组

注意点:

1.直接传入数组进行操作,所以两个函数都不需要返回值

2.merge函数利用两个新的数组,分别存放左右两边的原数组,再进行比较,依次放入原数组

3.int m = l + (r - l) / 2; 这个表达式中使用 r - l 是为了计算数组区间 [l, r] 的长度。

通过 (r - l) 得到区间的长度后再除以 2,就能得到区间长度的一半。然后加上起始索引 l ,就可以得到区间的中间位置 m ,从而将数组划分为左右两个子区间 [l, m] 和 [m + 1, r] ,以便进行归并排序的递归操作

#include <stdio.h>// 合并两个已排序的子数组为一个排序数组
void merge(int arr[], int l, int m, int r) {int n1 = m - l + 1; // 左边子数组的大小int n2 = r - m;     // 右边子数组的大小// 创建临时数组int L[n1], R[n2];// 复制数据到临时数组for (int i = 0; i < n1; i++)L[i] = arr[l + i];for (int j = 0; j < n2; j++)R[j] = arr[m + 1 + j];int i = 0, j = 0, k = l;// 合并临时数组回到原始数组while (i < n1 && j < n2) {if (L[i] <= R[j]) {arr[k++] = L[i++];} else {arr[k++] = R[j++];}}// 复制剩余的元素while (i < n1) {arr[k++] = L[i++];}while (j < n2) {arr[k++] = R[j++];}
}// 归并排序函数
void mergeSort(int arr[], int l, int r) {if (l < r) {int m = l + (r - l) / 2; // 取中间元素作为分割点// 对左边的子数组进行排序mergeSort(arr, l, m);// 对右边的子数组进行排序mergeSort(arr, m + 1, r);// 合并两个已排序的子数组merge(arr, l, m, r);}
}// 打印数组函数
void printArray(int arr[], int size) {for (int i = 0; i < size; i++)printf("%d ", arr[i]);printf("\n");
}// 测试案例
int main() {int arr[] = {12, 11, 13, 5, 6};int arr_size = sizeof(arr) / sizeof(arr[0]);printf("before sort: \n");printArray(arr, arr_size);mergeSort(arr, 0, arr_size - 1);printf("after sort: \n");printArray(arr, arr_size);return 0;
}
before sort:
12 11 13 5 6
after sort:
5 6 11 12 13

4.3 C++

与C语言思路一致,只有一些语法上的小差别

#include <iostream>// 合并两个已排序的子数组为一个排序数组
void merge(int arr[], int l, int m, int r) {int n1 = m - l + 1;int n2 = r - m;int L[n1], R[n2];for (int i = 0; i < n1; i++)L[i] = arr[l + i];for (int j = 0; j < n2; j++)R[j] = arr[m + 1 + j];int i = 0, j = 0, k = l;while (i < n1 && j < n2) {if (L[i] <= R[j]) {arr[k++] = L[i++];} else {arr[k++] = R[j++];}}while (i < n1) {arr[k++] = L[i++];}while (j < n2) {arr[k++] = R[j++];}
}// 归并排序函数
void mergeSort(int arr[], int l, int r) {if (l < r) {int m = l + (r - l) / 2;mergeSort(arr, l, m);mergeSort(arr, m + 1, r);merge(arr, l, m, r);}
}// 打印数组函数
void printArray(int arr[], int size) {for (int i = 0; i < size; i++)std::cout << arr[i] << " ";std::cout << std::endl;
}// 测试案例
int main() {int arr[] = {12, 11, 13, 5, 6};int arr_size = sizeof(arr) / sizeof(arr[0]);std::cout << "排序前的数组为: ";printArray(arr, arr_size);mergeSort(arr, 0, arr_size - 1);std::cout << "排序后的数组为: ";printArray(arr, arr_size);return 0;
}
排序前的数组为: 12 11 13 5 6 
排序后的数组为: 5 6 11 12 13 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/3280884.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

二叉树——链式结构的实现

首先是分为三个文件进行实现&#xff1a;tree.h、tree.c、test.c tree.h 用链表来表示⼀棵⼆叉树&#xff0c;即用链来指示元素的逻辑关系。通常的方法是链表中每个结点由三个域组成&#xff0c;数据域和左右指针域&#xff0c;左右指针分别用来给出该结点左孩⼦和右孩⼦所在…

一键解析:由于找不到xinput1_3.dll,无法继续执行代码的问题,有效修复xinput1_3.dll文件

xinput1_3.dll是一个重要的动态链接库文件&#xff0c;它是DirectX软件包的一部分&#xff0c;主要负责处理游戏和多媒体应用程序中的输入功能。当用户尝试启动某些游戏或应用程序时&#xff0c;可能会遇到一个错误提示&#xff0c;指出“由于找不到xinput1_3.dll&#xff0c;无…

TypeScript 的主要特点和重要作用

还是大剑师兰特&#xff1a;曾是美国某知名大学计算机专业研究生&#xff0c;现为航空航海领域高级前端工程师&#xff1b;CSDN知名博主&#xff0c;GIS领域优质创作者&#xff0c;深耕openlayers、leaflet、mapbox、cesium&#xff0c;canvas&#xff0c;webgl&#xff0c;ech…

《昇思25天学习打卡营第三十三天|7月26号》

昇思25天学习打卡营 在昇思25天学习打卡营的第33天7月26号&#xff0c;我深入学习了Python编程。通过课程的系统学习和实践编程项目&#xff0c;我逐渐掌握了Python语言的基本语法和核心概念。 特别是在函数定义和数据结构的应用上&#xff0c;我学习到了一些新的东西。以为平…

苹果手机怎么录屏?一键操作,轻松掌握录屏技巧

最近新换了一台苹果手机&#xff0c;但苹果手机和安卓手机有挺多不相同的地方&#xff0c;就比如苹果手机怎么录屏我一直都没找到&#xff0c;有没有经常使用苹果手机的朋友可以帮帮我&#xff1f;先谢谢大家啦&#xff01;” 苹果手机作为全球领先的智能手机品牌&#xff0c;…

layui 乱入前端

功能包含 本实例代码为部分傻瓜框架&#xff0c;插入引用layui。因为样式必须保证跟系统一致&#xff0c;所以大部分功能都是自定义的。代码仅供需要用layui框架&#xff0c;但原项目又不是layui搭建的提供解题思路。代码较为通用 自定义分页功能自定义筛选列功能行内编辑下拉、…

面试经典算法150题系列-数组/字符串操作之多数元素

序言&#xff1a;今天是第五题啦&#xff0c;前面四题的解法还清楚吗&#xff1f;可以到面试算法题系列150题专栏 进行复习呀。 温故而知新&#xff0c;可以为师矣&#xff01;加油&#xff0c;未来的技术大牛们。 多数元素 给定一个大小为 n 的数组 nums &#xff0c;返回其…

“华数杯”全国大学生数学建模竞赛含金量如何?

“华数杯”全国大学生数学建模竞赛是由华中师范大学主办的一项全国性的大学生数学建模竞赛。该竞赛旨在提高大学生的数学建模能力和实践能力,增强大学生的创新意识和团队协作精神。 搜集一些评价,有人说该竞赛的含金量较高,但是也有一些人认为其认可度不高,报名费用较贵。…

javascript 构造函数

1.定义一个构造函数 命名是大驼峰 不需要显式得返回 this对象 构造函数已返回 2.使用这个构造函数构建对象

锅总浅析链路追踪技术

链路追踪是什么&#xff1f;常用的链路追踪工具有哪些&#xff1f;它们的异同、架构、工作流程及关键指标有哪些&#xff1f;希望读完本文能帮您解答这些疑惑&#xff01; 一、链路追踪简介 链路追踪技术&#xff08;Distributed Tracing&#xff09;是一种用于监控和分析分布…

代码随想录算法训练营day29 | 134. 加油站 、135. 分发糖果、860.柠檬水找零、406.根据身高重建队列

碎碎念&#xff1a;加油 参考&#xff1a;代码随想录 134. 加油站 题目链接 134. 加油站 思想 局部最优&#xff1a; 一旦currentSum为负数&#xff0c;起始位置至少要是i1。 全局最优&#xff1a; 最后可以跑完一圈。 局部最优可以推出全局最优且找不到反例&#xff0c;所…

CST软件进行时域自适应网格设置步骤

这一期&#xff0c;我们回答一个大家非常关注的网格的问题。仿真软件的网格质量直接决定仿真的精度和效率&#xff0c;设置合理的网格才能将仿真做的又快有准。CST的微波工作室有多种求解器&#xff0c;如果用频域求解器&#xff08;F&#xff09;来仿真&#xff0c;有限元算法…

TCP的可靠机制

TCP的可靠机制 前言 要了解TCP的可靠机制&#xff0c;我们必须要先熟悉TCP的报文&#xff0c;在这篇文章中有详细介绍TCP的报文 &#xff1a; 并且确认应答机制也在该文章中提到&#xff0c;所以这篇文章就不会再介绍确认应答了。 超时重传 我们都知道&#xff0c;报文在网…

详解Qt 之QMdiArea 和 QMdiSubWindow

文章目录 前言QMdiArea概念作用为什么需要 QMdiAreaQMdiArea 的主要函数和成员函数列表 QMdiSubWindow概念作用为什么需要 QMdiSubWindowQMdiSubWindow 的主要函数和成员函数列表 示例代码 更多用法... 总结 前言 在复杂的应用程序中&#xff0c;尤其是那些需要同时管理多个子…

RabbitMQ快速入门(MQ的概念、安装RabbitMQ、在 SpringBoot 项目中集成 RabbitMQ )

文章目录 1. 补充知识&#xff1a;同步通讯和异步通讯1.1 同步通讯1.2 异步通讯 2. 同步调用的缺点2.1 业务耦合2.2 性能较差2.3 级联失败 3. 什么情况下使用同步调用4. 异步调用5. 异步调用的优点和缺点5.1 异步调用的优点5.1.1 解除耦合&#xff0c;拓展性强5.1.2 无需等待&a…

SQL必知必会

SQL必知必会 一些SQL知识&#xff0c;出自极客时间陈旸老师《SQL必知必会》 https://time.geekbang.org/column/intro/100029501 基础 视图 视图作为一张虚拟表&#xff0c;帮我们封装了底层与数据表的接口。它相当于是一张表或多张表的数据结果集。视图的这一特点&#x…

DMB,DSB,ISB三个指令区别

此部分说明三个指令的具体区别&#xff08;在指令流水线上说明&#xff09;&#xff0c;这三个指令主要目的在于确保程序在多处理器环境下的稳定性和一致性&#xff0c;避免由于指令乱序和内存操作重排引起的不可预测行为 一个简化的流水线&#xff0c;包含以下阶段&#xff1…

【git】git常用命令提交规范

Git 是程序员工作中不可或缺的版本控制工具&#xff0c;以下是一些优化后的常用 Git 命令列表&#xff0c;旨在帮助你更高效地使用 Git 进行版本控制。 基础操作 拉取代码 git clone xxx.git创建分支 git branch dev切换分支 git checkout dev # 或者 git switch dev创建并切换…

Mirror学习笔记(一) 简介

文章目录 一、常规学习&#xff1a;Mirror核心功能有服务器和主机 二、时间戳批处理时间戳 三、TCP和UDP四、CCU(同时在线人数)五、SyncDirection(同步方向)六、RTT&#xff08;往返时间&#xff09;七、Connection Quality&#xff08;连接质量&#xff09;八、Lag Compensati…

Android mLruProcesses的分布结构

AMS中的进程管理 final ArrayList<ProcessRecord> mLruProcesses new ArrayList<ProcessRecord>(); 在AMS的内部属性中使用mLruProcesses集合保存所有的进程信息&#xff0c;AMS将所有进程按照优先级从低到高的顺序保存着对应的ProcessRecord信息&#xff0c;即排…