归并排序

31 篇文章 0 订阅

归并排序是一种高效算法,按照分治的三步法,归并排序的三个步骤如下:

划分问题:把序列划分为尽量相等的两半。

递归求解:把两半元素分别排序。

合并问题:把两个有序表合并为一个。

总结为一句话就是,先排列最小的两个相邻的数然后合并两个相邻的序列。

程序流程图如下:


程序实现如下:

#include<iostream>
using namespace std;

void merg_sort(int* A, int x, int y, int* T){//把[x,y)区间内的元素按照从小到大的顺序排好序
	if (y - x > 1){
		int m = x + (y - x) / 2;
		int p = x, q = m, i = x;
		merg_sort(A, x, m, T);
		merg_sort(A, m, y, T);
		while (p < m || q < y){
			if (q >= y || (p < m&&A[p] <= A[q]))T[i++] = A[p++];
			else T[i++] = A[q++];
		}
		for (i = x; i < y; i++)A[i] = T[i];
	}
}
int main()
{
	int A[100], n, T[100];
	cin >> n;
	for (int i = 0; i < n; i++)cin >> A[i];
	merg_sort(A, 0, n, T);
	for (int i = 0; i < n; i++)cout << A[i]<<" ";
	cout << endl;
	return 0;
}

下面说明如何判断什么情况下复制左半数组的元素,什么时候复制右半数组的元素:

如果有且仅有一个半数组非空,那么非空数组剩下的数肯定是剩下的数中最小的,因此复制非空数组中的数。当半数组都非空时,选取两个半数组中较小的数,或者相等时任意一个。

由于while循环已经判断至少有一个半数组非空,所以“序列2空”表示“序列1非空序列2空”此时选序列1中的数;由于短路原理,如果执行到“序列2非空,序列1非空,A[p]<=A[q]“说明序列2非空,此时只需判断序列1非空并且序列1中元素小于等于序列2中元素。

拓展部分:

归并排序有一个重要用途,就是统计逆序对。

问题描述:

给一列排序,求它的逆序对数,即有多少个有序对(i,j),使得i<j但是。n可以高达1000000.

也许有些读者说可以用一个双重循环算出来,但这样枚举的算法时间渐进复杂度将有O(n2),也就是说该算法实现的程序将会超时。而归并排序的时间渐进复杂度为O(nlogn),此时不失为一个不错的选择。

根据分治三步法:

划分问题:把序列划分为左右两部分

递归求解:求解左右两部分的逆序对

合并问题:统计i在左边但j在右边的逆序对数

问题的关键在于如何合并问题,也就是如何求出i在左边,j在右边的逆序对数目统计的常用方法是分类。对于右边的每个j,把这些逆序对按照j所在位置的不同进行分类,每一个j在左边大于的元素的数目f(j)即为该分类的数目,所有的f(j)之和便是答案。

在归并排序中,由于左右两边的元素都是排好序的,所以在将某个右边元素复制到T时,此时左边所剩的元素都大于当前右边的元素。所以只需在复制右边元素的时候累加左边所剩元素个数即可统计出逆序对的数目。具体方法是把”else T[i++]=A[q++];”改为”{T[i++]=A[q++];cnt+=m-p;}”。




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值