首页 > 编程语言 >算法与数据结构——归并排序

算法与数据结构——归并排序

时间:2024-09-27 13:33:59浏览次数:7  
标签:归并 排序 递归 mid right 数组 数据结构 left

归并排序

归并排序(merge sort)是一种基于分治策略的排序算法,包含下图所示的“划分”和“合并”阶段。

  • 划分阶段:通过递归不断地将数组从中点处分开,将长数组的排序问题转换为短数组的排序问题。
  • *合并阶段**:当子数组长度为1时终止划分,开始合并,持续地讲左右两个较短的有序数组合并为一个较长的有序数组,直至结束。

算法流程

“划分阶段”从顶至底递归将数组从中点切分为两个子数组。

  1. 计算数组中点mid,递归划分左子数组(区间[left, mid])和右子数组(区间[mid + 1, right])。
  2. 递归执行步骤1.,直至子数组区间长度为1时终止。

“合并阶段”从底至顶将左子数组和右子数组合并为一个有序数组。注意,从长度为1的子数组开始合并,合并阶段中的每个子数组都是有序的。










观察可以发现,归并排序与二叉树后序遍历的递归顺序是一致的。

  • 后序遍历:先递归左子树,在递归右子树,最后处理根节点。
  • 归并排序:先递归左子数组,在递归右子数组,最后处理合并。

注意nums的待合并区间为[left,right],而tmp对应的区间为[0,right - left]

/*合并左子数组与右子数组*/
void merge(vector<int> &nums, int left, int mid, int right){
	// 左子数组区间为 [left, mid], 右子数组区间为 [mid+1, right]
	// 创建一个临时数组tmp,用于存放合并后的结果
	vector<int> tmp(right - left + 1);
	// 初始化左子数组和右子数组的起始索引
	int i = left, j = mid + 1, k = 0;
	// 当左右子数组都还有元素时,进行比较并将较小的元素复制到临时数组中
	while (i <= mid && j <= right){
		if (nums[i] < nums[j])
			tmp[k++] = nums[i++];
		else
			tmp[k++] = nums[j++];
	}
	// 将左子数组和右子数组的剩余元素复制到临时数组中
	while (i <= mid)
		tmp[k++] = nums[i++];
	while (j <= right)
		tmp[k++] = nums[j++];
	// 将临时数组tmp中的元素复制回nums对应区间
	for (k = 0; k < tmp.size(); k++){
		nums[left + k] = tmp[k];
	}
}
/*归并排序*/
void mergeSort(vector<int> &nums, int left, int right){
	// 终止条件
	if (left >= right)
		return; // 当子数组长度为1时终止递归
	// 划分阶段
	int mid = (left + right) / 2; // 计算中点
	mergeSort(nums, left, mid);	// 递归左子数组
	mergeSort(nums, mid + 1, right); // 递归右子数组
	// 合并阶段
	merge(nums, left, mid, right);
}

算法特性

  • 时间复杂度O(nlogn)、非自适应排序:划分产生高度为logn的递归树,每层合并的总操作数量为n,因此总体时间复杂度为O(nlogn)。
  • 空间复杂度O(n)、非原地排序:递归深度logn,使用O(logn)大小的栈帧空间。合并操作需要借助数组实现,使用O(n)大小的额外空间。
  • 稳定排序:在合并过程中,相等元素的次序保持不变。

链表排序

对于链表,归并排序相较于其他排序算法具有显著优势,可以将链表排序任务的空间复杂度优化至O(1)

  • 划分阶段:可以使用“迭代”替代“递归”来实现链表划分工作,从而省去递归使用的栈帧空间。
  • 合并阶段:在链表中,节点增删操作仅需改变引用(指针)即可实现,因此合并阶段(将两个短有序链表合并为一个长有序链表)无需创建额外链表。

标签:归并,排序,递归,mid,right,数组,数据结构,left
From: https://www.cnblogs.com/1873cy/p/18435125

相关文章

  • MATLAB排序
    在MATLAB中,可以使用sort函数对矩阵进行排序。以下是一些基本用法和示例,帮助你理解如何按特定维度对矩阵进行排序。1.基本用法1.1对向量排序A=[3,1,4,1,5,9];B=sort(A);%默认按升序排序结果为:B=1134591.2对矩阵排序对于矩阵......
  • 数据结构——链表
    c++数据结构p3链表链表的每一个节点都是独立分配出来的从当前节点能够找到下一个节点Node(链表)=date(数据域)+next(地址域)地址域:存储的是下一个节点的地址最后一个地址域是nullptrstructNode{intdata;Node*next;}特点:每一个节点都是在堆内存上独立new出来......
  • redis有序集合多字段排序
    首先,redis有序集合本身是不支持多字段排序的例如ZADDusers25AliceZADDusers25BobZADDusers10Carol只能通过前面的分数这一个维度来实现,如果现在引入了另一个字段,可以在分数值(利用阿拉伯数字)上做手脚例如,时间维度2023-01-012023-01-022023-01-03这......
  • log型数据结构优化DP解题报告(uoj)
    交作业用T220417最长公共上升子序列不难看出状态同最长公共子序列,但由于上升条件限制,加一个限制:\(f_{i,j}\)表示\(a_{1...i}\)匹配\(b_{1...j}\)且\(a_i\)必须做结尾的最长公共上升子序列长度转移方程为\(f_{i,j}=f_{i,j-1}\)(if\(a_i\neqb_j\))\(f_{i,j}=\max_{k......
  • 数据结构之——队列
    一、队列概述        队列是一种操作受限的线性表,其限制条件为允许在表的一端进行插入,而在表的另一端进行删除。插入的一端叫做队尾,删除的一端叫做队头。向队列中插入新元素的行为称为进队,从队列中删除元素的行为称为出队。例如军训的时候,都排成一列,有头有尾。假设你......
  • 【JAVA-数据结构】包装类&简单认识泛型(1)
        这篇包含包装类和泛型相关知识,会用两篇文章进行讲解。1包装类        在Java中,由于基本类型不是继承自Object,为了在泛型代码中可以支持基本类型,Java给每个基本类型都对应了一个包装类型。1.1基本数据类型和对应的包装类除了Integer和Character......
  • 揭秘合并排序:分治排序初学者指南
    归并排序由约翰·冯·诺依曼于1945年提出,主要是为了提高大型数据集的排序效率。冯·诺依曼的算法旨在使用分而治之的方法提供一致且可预测的排序过程。这种策略允许归并排序有效地处理小型和大型数据集,保证在所有情况下都能实现稳定的排序,时间复杂度为o(nlogn)。合并排序采用......
  • 数据结构:实现链式结构二叉树(Tree) 手把手带你入门数据结构~
    文章目录前言一、链式结构二叉树的概念1.定义2.节点结构3.操作4.优势与劣势二、链式结构二叉树的实现1.树结构的定义2.树的遍历(1)前序遍历(2)中序遍历(3)后序遍历3.二叉树结点个数4.二叉树叶子结点个数5.二叉树第k层结点个数6.二叉树的深度/高度7.二叉树查找值为......
  • 【算法】贪心+堆排序实现大根堆及标准库容器类的融合使用
    ......
  • Map 数据结构
    Map是一种键值队的集合,和对象Object类似。两者的区别:一、Map和Object的区别键的类型:在Map中,键可以是任何类型(包括对象、函数、undefined、NaN等等);而在Object中,键只能是字符串或者符号。有序性:在Map中,键值对是按照插入(添加)的顺序排列的;而Object不能保证顺序二、创建:创......