【数据结构】归并排序

1、介绍

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

  1. 划分阶段:通过递归不断地将数组从中点处分开,将长数组的排序问题转换为短数组的排序问题。

  2. 合并阶段:当子数组长度为 1 时终止划分,开始合并,持续地将左右两个较短的有序数组合并为一个较长的有序数组,直至结束。

2、算法流程

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

  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;
	}
	//划分阶段
	int mid = left + (right - left) / 2;   //计算划分中点
	mergeSort(nums, left, mid);//递归左子数组
	mergeSort(nums, mid + 1, right);//递归右子数组

	//合并阶段
	merge(nums,left, mid, right);
}

3、算法特性

  • 时间复杂度为 O(nlog⁡n)、非自适应排序:划分产生高度为 log⁡n 的递归树,每层合并的总操作数量为 n ,因此总体时间复杂度为 O(nlog⁡n) 。

  • 空间复杂度为 O(n)、非原地排序:递归深度为 log⁡n ,使用 O(log⁡n) 大小的栈帧空间。合并操作需要借助辅助数组实现,使用 O(n) 大小的额外空间。

  • 稳定排序:在合并过程中,相等元素的次序保持不变。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值