求逆序数图解(分治)--算法学习

问题

考虑1,2,…,n (n <= 100000)的排列i1,i2,…,in,如果其中存在j,k,满足 j <
k 且 ij > ik, 那么就称(ij
,ik)是这个排列的一个逆序。
一个排列含有逆序的个数称为这个排列的逆序数。例如排列 263451 含有8个
逆序(2,1),(6,3),(6,4),(6,5),(6,1),(3,1),(4,1),(5,1),因此该排列的逆序数就是8。
现给定1,2,…,n的一个排列,求它的逆序数。

思路

笨办法:O(n2)
分治O(nlogn): 1) 将数组分成两半,分别求出左半边的逆序数和右半边的逆序数
2) 再算有多少逆序是由左半边取一个数和右半边取一个数构成(要求O(n)实
现)
2) 的关键:左半边和右半边都是排好序的。比如,都是从大到小排序的。这
样,左右半边只需要从头到尾各扫一遍,就可以找出由两边各取一个数构成的
逆序个数

图解

左右两边排好序
左右两组数据的第一个数为指针
在这里插入图片描述

i<j,j移动到11,还是比10 大,继续移动到5,发现5比10小。
在这里插入图片描述
所以10的逆序数就是5和2两组

然后i,动,8比5,2都大,所以5,2也是8的两组数据,同理7也是
继续移动,到了3,发现3比5小,所以j开始移动到2
在这里插入图片描述
所以只有2是3的逆序数

由归并排序改进得到,加上计算逆序的步骤
MergeSortAndCount: 归并排序并计算逆序数

代码

#include <iostream>
using namespace std;
long long sum = 0;//逆序数的个数 
void MergeSort(int * arr,int s,int e,int *temp);//归并排序
void MergeAndCountNum(int * arr,int s,int mid,int e,int *temp); //归并有序序列并计算逆序数的个数 
int main(){
 int a[100001],b[100001],n;
 cin >> n;
 for(int i=0;i<n;++i)
  cin >> a[i];
 MergeSort(a,0,n-1,b);
 cout << sum ;
 return 0;
} 
void MergeSort(int * arr,int s,int e,int * temp){//归并排序 
 if(s < e){ 
 int mid = s + (e - s)/2;
  MergeSort(arr,s,mid,temp);
  MergeSort(arr,mid+1,e,temp);
  MergeAndCountNum(arr,s,mid,e,temp);
 }
}
void MergeAndCountNum(int * arr,int s,int mid,int e,int * temp){//归并有序序列并计算逆序数的个数 
 int p1 = s, p2 = mid +1,index = 0;
 while(p1 <= mid && p2 <= e){
  if(arr[p1] < arr[p2])
   temp[index++] = arr[p1++];
  else{
   temp[index++] = arr[p2++];
   sum += mid - p1 +1;
  }
 }
 while(p1 <= mid)
  temp[index++] = arr[p1++];
 while(p2 <= e)
  temp[index++] = arr[p2++];
 for(int i = 0;i<e - s +1;++i)
  arr[s+i] = temp[i];
}
发布了83 篇原创文章 · 获赞 142 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/weixin_45822638/article/details/105034834