进阶指南--超快速排序(归并+逆序对)

归并排序:

  将数组切成两半,然后左边排完 右边排完 (二分递归)  合并排列(用临时数组:对左右两边的数谁大谁进去)

逆序对

对于i,j (i < j ) 如果 a[i] < a[j] 则构成一个逆序对

那思考:归并排序 你知道左边一个单调递增数列,右边一个单调递增数列,mid是左右分界,属于左边,那对于右边的数 a[j] 而言,在合并的时候,如果发现自己小于前面的一个数 a[i] 进了数组, 那说明 a[i]~a[mid] 都比a[j] 大,构成了(mid-i+1) 个逆序对
这样子,在合并的时候记录逆序对,通过归并排序就记录了逆序对的个数

超快速排序

相邻交换,你会发现如果交换一次 a[i],a[i+1],(a[i] > a[i+1])就等价于减少一个逆序对,因为要最小次数,那你每次选相邻两个 a[i] > a[i+1] 即为最优,那么这样子做的结果就是把逆序对变为0,结果数即为逆序对的总个数

#include <bits/stdc++.h>
using namespace std;
const int MA = 5e5+10;
int a[MA],b[MA];
long long cnt;//小心溢出!! 
void merge (int l,int r){
	if(l >= r) return ;
	//切成两半 
	int mid = (l+r)>>1;
	merge(l,mid);
	merge(mid+1,r);
	//开始合并两个有序队列 
	int i = l,j = mid+1; 
	for (int k = l; k <= r; k++){
		//边界和正常情况 
		if(j > r ||  (i <= mid && a[i]<a[j] ) ) b[k] = a[i++];//左小于右仍有序
		else   b[k] = a[j++], cnt += mid - i + 1; 
	} 
	for (int k = l; k <= r; k++) a[k] = b[k];
	return;
} 
int main()
{
	int n;
	while(scanf("%d",&n)==1 && n){
		for (int i = 0; i < n; i++) scanf("%d",&a[i]);
		cnt = 0;
		merge(0,n-1);
		cout<<cnt<<endl; 
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值