排序算法-----归并排序算法 详解及实现(C++版)

算法描述

归并排序的思想是采用分治策略(二分),首先将序列(递归地)分解成若干子序列,然后合并,先使每个子序列有序,再合并两个子序列为一个有序序列,直到所有子序列合并成一个有序序列。

归并排序的特点

首先,归并排序是一种稳定的排序,所谓“稳定”,是指给定的待排序列中如果含有若干个相等的元素,在排序后,相等的元素之间的相对位置不会被改变。如:原始序列{5a,3a,4,2,6,1,3b,3c,7,5b},稳定的排序后输出应为{1,2,3a,3b,3c,4,5a,5b,6,7},注意排序后的多个3和多个5与待排前的相对位置是一致的。这对排序数据包含多个信息而要求按其中的某一个信息排序,要求其它信息尽量按输入的顺序排序时很重要!
其次,归并排序的比较次数小于快速排序的比较次数,移动次数一般多于快速排序的移动次数。

归并排序的过程

在这里插入图片描述
如上图(引用原图)所示,首先是分解的过程:可以理解为就是递归的拆分子序列的过程(其结构像一颗完全二叉树),递归深度为log2n。
其真正的排序核心在于“治”,即合并的过程:将两个已经排序的子序列(起初最小的子序列为只包含一个数的子序列,显然也属于排序好的子序列)合并为两个已经有序的子序列。其步骤如下图(引用原图)所示。
在这里插入图片描述

代码实现

归并步骤:
第一步:申请空间,其大小为两个已经排序序列之和,用来存放合并后的序列。
第二步:设定两个指针,最初位置分别为两个已经排序序列的起始位置。
第三步:比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置。
重复步骤三,直到某一指针超出序列尾。
第四部:将另一序列剩下的所有元素直接复制到合并序列尾部。

//C++ 版本 递归实现
#include<iostream>
using namespace std;

/*********************函数参数说明**********************
传入参数:数组的首地址 int* number
          第一个已排序序列的起始索引 int start,第一个已排序序列的终止索引 int mid;
		  (第二个已排序序列的起始索引 mid + 1),第二个已排序序列的终止索引 int end;
********************************************************/

void Merge(int* numbers, int start, int mid, int end) {
	int* temp = new int[end - start + 1];	//第一步,申请空间,大小为两个排序序列之和
	int fistSectionIndex = start;			//第二步,设定两个待排序列的起始位置的索引
	int secondSectionIndex = mid + 1;
	int tempIndex = 0;	//所申请空间的索引

	while (fistSectionIndex <= mid && secondSectionIndex <= end) {	//直到两个序列中有一个到达终止位置
		if (numbers[fistSectionIndex] <= numbers[secondSectionIndex])
			temp[tempIndex++] = numbers[fistSectionIndex++];
		else
			temp[tempIndex++] = numbers[secondSectionIndex++];
	}

	while (fistSectionIndex <= mid)
		temp[tempIndex++] = numbers[fistSectionIndex++];

	while (secondSectionIndex <= end)
		temp[tempIndex++] = numbers[secondSectionIndex++];

	for (int j = 0; j < tempIndex; ++j)		//将合并且排序好的元素,复制到原来的数组中,释放临时数组空间
		numbers[start + j] = temp[j];

	delete[] temp;
}


void MergeSort(int* numbers, int start, int end) {
	if (numbers == NULL || start >= end)
		return;
	
	int mid = (start + end) / 2;

	MergeSort(numbers, start, mid);		//递归排序numbers[start,mid](首先从上往下递归分解到最底层元素个数为1的情况)
	MergeSort(numbers, mid + 1, end);	//递归排序numbers[mid + 1,end](首先从上往下递归分解到最底层元素个数为1的情况)

	Merge(numbers, start, mid, end);	//然后递归的从下往上合并排序
}

int main() {
	int a[10] = { 5,6,1,8,3,4,9,7,2,3 };
	cout << "归并排序前:";
	for (int i = 0; i < 10; i++)
		cout << a[i] << ' ';
	cout << endl;

	MergeSort(a, 0, 9);

	cout << "归并排序后:";
	for (int i = 0; i < 10; i++)
		cout << a[i] << ' ';
	cout << endl;
}

运行结果:
在这里插入图片描述

经典应用

求一段区间的逆序对数:
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。例如,在数组{7,5,6,4}中,一共存在5个逆序对,分别是(7,6),(7,5),(7,4),(6,4),(5,4)。

  • 12
    点赞
  • 56
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值