作为程序员的小Q,他的数列和其他人的不太一样,他有个数。 老板问了小Q一共 m次,每次给出一个整数, 要求小Q把这些数每分为一组,然后把每组进行翻转,小Q想知道每次操作后整个序列中的逆序对个数是多少呢

作为程序员的小Q,他的数列和其他人的不太一样,他有个数。

老板问了小Q一共 m次,每次给出一个整数, 要求小Q把这些数每分为一组,然后把每组进行翻转,小Q想知道每次操作后整个序列中的逆序对个数是多少呢?

 

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

假设有一个数组{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;
        }
                        
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值