7622:求排列的逆序数

7622:求排列的逆序数

传送门
归并排序的改编运用
原版归并
CPP
图解 归并排序(CPP)
JAVA
图解 归并排序(JAVA)

归并排序的思想大概就是:
将要排序的序列分成两段,对左右两段分别排序,再合并为有序序列,对于每一段等待排序的序列再次进行以上操作,直到分段到其中一段只有两个元素,再分段左右两段就是一个元素,开始进行排序,其实就是交换两个数了。从排列两个数的序列开始,合并排列好的两段序列,直到之前分的所有两段都合并好。这显然需要递归实现。想象其中一个状态,排列好了左右两段的,接下来要做的事就是合并左右两个有序段将其变为有序。
很好,把自己绕晕了👍
看别人画的归并排序的图解,一开始做的事情其实是分段,把整个序列分成左右两段,对于每一小段又分成左右两端,直到分的两段中只有一个元素,分段到此结束。开始对最小的两段合并,合并之后就可以回溯到上一阶分的段,直到这样回溯,把最开始分的那两段都合并了(你咋知道合并到了最开始的那两段呢?因为每分两段,之后都会有 merge(a,l,r,mid);合并的指令,只是要等到上面那两段都分段、合并好了。其实就是回溯完所有的递归函数,就完成了所有段的归并)

不知道说的是否确切,就像一个递归回溯的过程,

	merge_sort(a,l,mid);
	merge_sort(a,mid+1,r);
	merge(a,l,r,mid);	

在这里插入图片描述
只可意会不可言传😭
这道题呢,就是要走归并排序走过的路,对于可以分的每两段,把 对这两段合并的过程 变成…,其实是真合并,因为只有真合并才能进行下去,走完所有的路。合并的过程中计数 左段和右段 左大右小的 情况种数。

#include <bits/stdc++.h>
using namespace std;
typedef long long int ll;
int n;
ll a[100009];
ll cnt=0;
void merge(ll a[],int l,int r){
	ll temp[100009];//用来盛放合并好的有序序列 归并排序时间复杂度为nlogn,空间换时间 
	int k=0;
	int mid=l+(r-l)/2;
	int i=l; int j=mid+1;
	while(i<=mid&&j<=r){//  82 94 
//		if(a[i]<=a[j]){//从小到大顺序排列 
//			temp[k++]=a[i++];
//		} 
//		else{
从小到大顺序排列 ,当左边数大于右边数,左边数右侧的mid-i+1个数 都会大于这个右边数 
//			temp[k++]=a[j++];
//			cnt+=(mid-i+1); 
//		}
		if(a[i]<=a[j]){//从大到小顺序排列 
			temp[k++]=a[j++];
		} 
		else{
//从大到小顺序排列 ,当左边数大于右边数,右边数右侧的r-j+1个数会小于这个左边数 
			temp[k++]=a[i++];
			cnt+=(r-j+1); 
		} 
	}
	while(i<=mid){
		temp[k++]=a[i++];
	}
	while(j<=r){
		temp[k++]=a[j++];
	}
	for(int h=0;h<r-l+1;h++){
		a[h+l]=temp[h];
	}
}
void mergeSort(ll a[],int l,int r){
	if(l>=r)return;//递归要有终止条件噢 
	int mid=l+(r-l)/2;
	mergeSort(a,l,mid);
	mergeSort(a,mid+1,r);
	merge(a,l,r);
}
int main(int argc, char** argv) {
	cin>>n;
	for(int i=0;i<n;i++){
		cin>>a[i];
	}
	 mergeSort(a,0,n-1);
	// 将数组分成两半,左半边的逆序数加上右半边的逆序数,
//	再加上左边取一个、右边取一个的逆序数 ,通过归并排序先为两半边排好序 
	cout<<cnt;
		return 0;
	}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值