编程求逆序数
逆序数,也就是一个排列中当某一对元素的先后次序与标准次序(从小到大)不同时,就说它构成一个逆序。
如:3 5 2 1 4 的逆序数:2+3+1+0+0=6 ,3与2,1; 5与2,1,4; 2与1,就是找出排列第i个数后有几个比它小的数,有几个就有几个逆序数,遍历该排列,找出所有逆序数并求和就是该排列的总逆序数。
话不多说,直接上代码
#include<stdio.h>
int count=0;
int main()
{
int n = 5;
int a[n] = {3,5,2,1,4};
for(int i = 0; i < n; i++)
for(int j = i; j < n; j++)
{
if(a[i]>a[j])
count++;
}
printf("count = %d\n",count);
return 0;
}
上述方法的时间复杂度为O(N^2)
下面是基于归并排序的求逆序数的方法,比较关键的步骤是合并,合并步骤是将已两个排好序的数组进行合并,当a[i]>a[j]时,i到mid间的数都大于a[j],把a[j]放到a[i]前面时,就产生了mid-i+1个逆序数,此时做累加记录,若a[i]<a[j]时不产生逆序数。
#include<stdio.h>
#define N 10
int count = 0;
void merge(int a[],int left,int mid,int right)
{
int i = left,j = mid+1,k = left;
int b[N];
while((i<=mid) && (j<=right))
{
if(a[i]>a[j])
{
b[k] = a[j++];
count += mid-i+1;
}
else
{
b[k] = a[i++];
}
k++;
}
while(i<=mid)
b[k++] = a[i++];
while(j<=right)
b[k++] = a[j++];
for(int t = left;t<=right; t++)
a[t] = b[t];
}
void reverse_count(int a[],int left,int right)
{
if(left<right)
{
int mid = (left+right)/2;
reverse_count(a,left,mid);
reverse_count(a,mid+1,right);
merge(a,left,mid,right);
}
}
int main()
{
int n = 5;
int a[n] = {3,5,2,1,4};
reverse_count(a,0,4);
printf("count = %d\n",count);
return 0;
}
该方法基于归并排序,时间复杂度为O(NlogN)。
2021.4.14,笔试题,以下是java版本;
原题:输入n,m,
对前2^n个数操作m次,第m次输入一个数k,表示对这2^n个数,没2^k次进行一次翻转;求逆序对数
例如,输入:
n=2,m=3,接着输入
k=2,
k=1
k=2
输出:
6
4
2
解释:
初始序列为:1,2,3,4
第一次操作:4,3,2,1,逆序数对为6
第二次操作:3,4,1,2,逆序数对为4
第三次操作:2,1,4,3,逆序数对为2
图解:
import java.util.ArrayList;
import java.util.Scanner;
class Solusion2{
private final int maxlen = 1000001;
private int[] tmp = new int[maxlen];
private int cnt = 0;
public void nixu(ArrayList<Integer> arr,int left,int right){
if(left>=right){
return;
}
int mid = left + (right-left)/2;
nixu(arr,left,mid);
nixu(arr,mid+1,right);
merge(arr,left,mid,right);
}
public void merge(ArrayList<Integer> arr,int left,int mid,int right){
int p = left,q=mid+1;
int k = 0;
while(p<=mid && q<=right){
if(arr.get(p)>arr.get(q)){
//arr[q]和mid前面每一个数都能组成逆序数对
this.cnt += mid-p+1;
tmp[k++] = arr.get(q);
q++;
}else{
tmp[k++] = arr.get(p);
p++;
}
}
while(p<=mid){
tmp[k++] = arr.get(p);
p++;
}
while(q<=right){
tmp[k++] = arr.get(q);
q++;
}
for(int i=0;i<k;i++){
arr.set(left+i,tmp[i]);
}
}
public void setCnt(int cnt){
this.cnt = cnt;
}
public int getCnt(){
return this.cnt;
}
}
public class problem1 {
final int maxlen = 1000001;
public static void main(String[] args) {
Solusion2 solusion2 = new Solusion2();
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int m = sc.nextInt();
ArrayList<Integer> arr = new ArrayList<>();
for(int i=0;i<Math.pow(2,n);i++){
arr.add(i+1);
}
for(int i=0;i<m;i++){
int j = sc.nextInt();
int powlen = (int) Math.pow(2,j);
//反转
int p = 0;
while(p<arr.size()){
int left=p,right=p+powlen-1;
if(right>arr.size()){
break;
}
while(left<right)
{
int tmp = arr.get(left);
arr.set(left, arr.get(right));
arr.set(right, tmp);
left++;
right--;
}
p+=powlen;
}
//暴力法求逆序对
// int cnt = 0;
// for(int q=1;q<arr.size();q++){
// for(int w=0;w<q;w++){
// if(arr.get(w)>arr.get(q)){
// cnt++;
// }
// }
// }
ArrayList<Integer> arr1 = new ArrayList<>(arr);
solusion2.nixu(arr,0,arr.size()-1);
System.out.println(solusion2.getCnt());
solusion2.setCnt(0);
arr.clear();
arr.addAll(arr1);
}
sc.close();
}
}