归并排序与求逆序数

一、归并排序

#include<iostream>
using namespace std;
int a[100], b[100];
/*
	a原数组,b中间数组,前半段的起始,
	m前半段的终止,也是两段的中点,e后半段的结束

*/
void merge(int a[], int l, int m, int e, int b[]) {
	/*
		- 假设按从小到大排序 -
		现有两段已排序的序列 p1和p2分别指向这两个序列的开头 
		p指向中间数组的开头,中间数组是用来临时保存排序结果的,最后排序后要拷贝回到a中
		p1 和 p2 分别从前往后扫,谁的小就插到b中,然后后移
	*/
	int p1 = l, p2 = m + 1, p = 0;
	//两段序列同时往后扫 直到其中的一个序列扫完了
	while (p1 <= m && p2 <= e) {
		/*
			如果p1指向的序列1中的元素小于p2指向的序列2中的元素,
			那么将p1指向的元素添加到p指向的b的位置,然后p1和p后移;反之,对p2和p操作
		*/
		if (a[p1] < a[p2])
			b[p++] = a[p1++];
		else
			b[p++] = a[p2++];
	}
	//两段有序数列的其中一段已经填充到b中,可能还剩下一段序列还没填充完
	while (p1 <= m) {
		b[p++] = a[p1++];
	}
	while (p2 <= e) {
		b[p++] = a[p2++];
	}
	//将中间数组的结果拷贝回到原数组a中 归并步骤才算完成
	for (int i = 0; i < e - l + 1; i++)
		a[l + i] = b[i];
}
/*
	归并排序,下标从l到r,a原数组,b中间数组
	中心思想:对数组a从l到r进行排序,分为两个规模小的相同问题:
	对前半部分从l到mid排序,对后半部分从mid+1到r排序,
	最后对两段排序后的内容进行归并,使整个数组有序。按照这个规律不断递归下去。
*/
void mergesort(int a[], int l, int r, int b[]) {
	/*
		利用递归实现归并排序,不断地将数组分为前半和后半两部分,
		当每部分的起始坐标等于结束坐标时,这一段内只有1个元素,不需要排序。
		所以只有当起始坐标小于结束坐标的时候,即这一段内有2个及以上的元素时,才涉及到排序
	*/
	if (l < r) {
		//取得中点
		int mid = l + (r - l) / 2;
		//对前半段递归排序
		mergesort(a, l, mid, b);
		//对后半段递归排序
		mergesort(a, mid + 1, r, b);
		//将排序后的两部分进行归并,使整体有序
		merge(a, l, mid, r, b);
	}
}
int main() {
	int n;
	cin >> n;
	for (int i = 0; i < n; i++)
		cin >> a[i];
	mergesort(a, 0, n - 1, b);
	for (int i = 0; i < n; i++)
		cout << a[i] << " ";
	cout << endl;
	return 0;
}

二、利用归并排序的变形求逆序数

#include<iostream>
using namespace std;
int a[100], b[100];
//归并函数 这里排序是按从大到小
void merge(int a[], int l, int m, int e, int b[], int& count) {
	int p1 = l, p2 = m + 1, p = 0;
	while (p1 <= m && p2 <= e) {
		//前面的大于后面的,产生了逆序数,
		//逆序的对数等于当前p2到尾部e的所有数的个数
		if (a[p1] > a[p2]) {
			count += e - p2 + 1;
			b[p++] = a[p1++];
		}
		//前面小于等于后面 没有逆序产生
		else {
			b[p++] = a[p2++];
		}
	}
	//两个序列中的一个已经空了,一个巴掌拍不响,没有逆序了
	while(p1 <= m) {
		b[p++] = a[p1++];
	}
	while (p2 <= e) {
		b[p++] = a[p2++];
	}
	//拷贝回到原数组中
	for (int i = l; i <= e; i++)
		a[i] = b[i - l];
}
//求数组a中,下标从l到r的总逆序对数
void mergesort_count(int a[], int l, int r, int b[], int& count) {
	/*
		先求前半部分的逆序数,再求后半部分的逆序数,
		最后求这两部分之间产生的逆序数
	*/
	if (l < r) {
		int mid = l + (r - l) / 2;
		//求前半部分逆序数
		mergesort_count(a, l, mid, b, count);
		//求后半部分逆序数
		mergesort_count(a, mid + 1, r, b, count);
		//将两部分归并,并求两部分之间的逆序数
		merge(a, l, mid, r, b, count);
	}
}
int main() {
	int n;
	cin >> n;
	for (int i = 0; i < n; i++)
		cin >> a[i];
	int count = 0;
	mergesort_count(a, 0, n - 1, b, count);
	cout << "逆序数的对数:" << count << endl;
	for (int i = 0; i < n; i++)
		cout << a[i] << " ";
	cout << endl;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值