逆序对java_逆序对

求逆序对问题用归并排序的时间复杂度比暴力算法更低。

假设有一个数组{8,1,2,5,7,4,3,6}

首先归并排序第一次对数组进行分割      8 1 2 5      7 4 3 6

二次分割      8 1      25      74      36

三次分割      8      1      2      5      7      4      3      6

第一次合并     18     25     74     36      reorder[1]=2, order[1]=2

//用reorder[i]来记录第i次合并中存在的逆序对数,order[i]来记录第i次合并中存在的顺序对数。

第二次合并     1258     3467     reorder[2]=5, order[2]=3

第三次合并     12345678          reorder[3]=6, order[3]=10

那么数组{8,1,2,5,7,4,3,6}中存在的逆序对就等于reorder[1]+reorder[2]+reorder[3]=13

将数组{8,1,2,5,7,4,3,6}每2^2个为一组进行翻转{5,2,1,8,6,3,4,7}

首先归并排序第一次对数组进行分割      5 2 1 8      6 3 4 7

二次分割      5 2      18      63      47

三次分割      5      2      1      8      6     3      4      7

第一次合并     25     18    36     47      reorder[1]=2, order[1]=2

第二次合并     1258     3467     reorder[2]=3, order[2]=5

第三次合并     12345678          reorder[3]=6, order[3]=10

那么数组{5,2,1,8,6,3,4,7}中存在的逆序对就等于reorder[1]+reorder[2]+reorder[3]=11

由此我们可以观察到对数组每2^2个进行翻转时,reorder[1]和order[1]进行了互换,reorder[2]和order[2]亦是如此。

所以对数组每2^i个进行翻转时,我们可以把1~i的reorder和order数组元素互换即可继续通过计算reorder数组的累加和来求得数组的逆序对数。

import java.util.ArrayList;

import java.util.Scanner;

public class Main {

public static void main(String[] args)

{

Scanner sc = new Scanner(System.in);

int n = sc.nextInt();

int N = 1 << n;

int[] a = new int[N];

int[] b = new int[N];//用来存储数组的逆序,对逆序的数组进行一次归并排序可以直接得到order数组

int[] order = new int[n + 1];//为了便于计算,所以设置order下标是1~n,因此数组大小为n+1

int[] reorder = new int[n + 1];

for (int i = 0; i < N; i++)

{

a[i] = sc.nextInt();

b[N - i - 1] = a[i];

}

MergeSort(a, 0, N - 1, reorder, n);//对整个数组进行归并排序,n表示对reorder[1]~reorder[n]进行初始化

MergeSort(b, 0, N - 1, order, n);//对整个逆序数组进行归并排序,完成对order[1]~order[n]的初始化

int m = sc.nextInt();

while(m-- > 0)

{

int count = 0;

int q = sc.nextInt();

for (int i = 1; i <= q; i++) //像之前讲的,将1~q的reorder[i]和order[i]进行互换

{

int temp = reorder[i];

reorder[i] = order[i];

order[i] = temp;

}

for (int i = 1; i <= n; i++)

{

count+= reorder[i];//累加reorder数组,求得对数组中每2^q个元素进行翻转后的逆序对数

}

System.out.println(count);

}

}

public static void MergeSort(int[] a , int left, int right, int[] reorder, int index)

{

if(left < right)

{

int mid = (right + left) / 2;

MergeSort(a, left, mid, reorder,index - 1);

MergeSort(a, mid + 1, right, reorder,index -1);

if(a[mid] > a[mid+1])//如果a[mid]<=a[mid+1],则原数组有序,不需要合并

Merge(a, left, right,reorder, index);

}

}

public static void Merge(int[] a, int left, int right,int[] reorder, int index)//index表示对reorder[index]进行初始化

{

int mid = (right + left) / 2;

int Length1 = mid - left + 1;

int Length2 = right - mid;

int[] l = new int[Length1];//存储a[left]~a[mid]

int[] r = new int[Length2];//存储a[mid+1]~a[right]

System.arraycopy(a, left, l, 0, Length1);//对l进行初始化

System.arraycopy(a, mid + 1, r, 0, Length2);//对r进行初始化

int i = 0;

int j = 0;

int c= 0;

int k = left;

while(i < Length1 && j < Length2)

{

if(l[i] <= r[j])

{

a[k] = l[i];

i++;

}

else

{

a[k] = r[j];

j++;

c += Length1 - i;//当l[i]>r[j]时,因为l是递增序列,所以l[i]~l[Length1-1]均>r[j],所以有Length1-i个元素大于r[j]

}

k++;

}

System.arraycopy(l, i, a, k, Length1 - i);//前面归并排序MergeSort中调用Merge合并的条件是a[mid]>a[mid+1],因为当a[mid]<=a[mid+1]时说明原数组有序,无需合并。l[Length1-1]>r[Length2-1],即l数组的最大值大于r数组的最大值,所以当r中的数全部进入a数组后,l数组中仍有剩余。

reorder[index] += c;

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值