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

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

}

}

逆序对java_逆序对相关推荐

  1. OpenCV求逆(伪逆)矩阵函数

    转自 double invert(InputArray src, OutputArraydst, int flags=DECOMP_LU); 功能:用以求取一个矩阵的逆或者伪逆. src: 输入,浮点 ...

  2. 3.4 矩阵 $A,A^T,A^TA,AA^T$ 秩相等,左逆和右逆

    矩阵 A,AT,ATA,AATA,A^T,A^TA,AA^TA,AT,ATA,AAT 秩相等,左逆和右逆 令 r=rankAr=rank Ar=rankA ,因为零空间秩为 n−rn-rn−r ,零空 ...

  3. 矩阵的逆、伪逆、左右逆,最小二乘,投影矩阵

    主要内容: 矩阵的逆.伪逆.左右逆 矩阵的左逆与最小二乘 左右逆与投影矩阵 一.矩阵的逆.伪逆.左右逆 1.矩阵的逆 定义: 设A是数域上的一个n阶方阵,若在相同数域上存在另一个n阶矩阵B,使得: A ...

  4. 51Nod-1019 逆序数【逆序偶+归并排序】

    1019 逆序数  在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序.一个排列中逆序的总数就称为这个排列的逆序数. 如2 4 3 1中,2 1,4 3, ...

  5. 矩阵分析:广义逆矩阵,{1}逆,MP逆,D逆

    1,广义逆矩阵 设  满足下列四个  方程: (1)(2) (3)(4) 的某个几个或全部,则称  为  的广义逆矩阵. 满足全部四个方程的广义逆矩阵  称为  的  逆(存在且唯一). 设 ,若   ...

  6. (数学概念)矩阵的逆、伪逆、左右逆,最小二乘,投影矩阵

    主要内容: 矩阵的逆.伪逆.左右逆 矩阵的左逆与最小二乘 左右逆与投影矩阵 一.矩阵的逆.伪逆.左右逆 1.矩阵的逆 定义: 设A是数域上的一个n阶方阵,若在相同数域上存在另一个n阶矩阵B,使得: A ...

  7. 线性代数学习笔记10-4:左右逆、伪逆/M-P广义逆(从四个子空间和SVD角度理解)

    下面讨论m×nm\times nm×n的秩为rrr的矩阵 对于不同情况,讨论逆矩阵 两侧逆矩阵 2-sided inverse 这也是一般所说的"逆矩阵"的含义 方阵A\bolds ...

  8. 逆序输出三位数python_逆的部首|逆的拼音|逆的组词|逆的意思 - 查字典

    逆 nì 拼音: nì 注音: ㄋㄧˋ 部首笔划:3 总笔划:9 繁体字:逆 汉字结构:半包围结构 简体部首:辶 造字法:形声 笔顺:捺撇横折竖撇捺折捺 逆的意思.基本信息 五笔86:UBTP 五笔9 ...

  9. 字符串逆序+单词逆序

    字符串逆序+单词逆序 文章目录 字符串逆序+单词逆序 一.字符串逆序 二.单词逆序 三.感谢以及交流 一.字符串逆序 问题描述: 输入一个字符串arr,将其内容颠倒过来,并输出. 数据范围0<l ...

最新文章

  1. 《中国大数据产业白皮书及百强榜单》:一览中国大数据产业发展全局
  2. 解析三层架构(1)---为什么要分层?
  3. 洛谷P2068 统计和题解
  4. PyCharm的配置与安装
  5. Golang 的字符编码与 regexp
  6. Windows Server 2012改造成Windows8的方法(转载)
  7. python编码效率高吗_【原创】杠精的日常-讨论python快排的效率
  8. SVN客户端--TortoiseSVN使用说明
  9. Git submodule 的笔记
  10. 很久以前的C语言笔记
  11. CSS隐藏内容的三种方法比较
  12. 如何查看某个查询用了多少TempDB空间
  13. PHP 霸主地位被动摇,JIT 是穷途末路后的绝地反击?
  14. 数据结构作业——ギリギリ eye(贪心+优先队列/贪心+并查集)
  15. 中国电子学会c语言考试题库,计算机基础考试试题及答案
  16. 最详细的Android SDK下载安装及配置教程
  17. simulink如何更新版本的文件(mdl或slx),How to load models created with a newer version of Simulink
  18. android没有apk文件怎么打开方式,ios怎么打开apk文件,安卓无法打开apk文件
  19. 2022「博客新星」年度评选TOP100名单
  20. HTML压缩(JSP的GZIP实现)

热门文章

  1. java项目类上有黑色的点_图像处理 – 如何使用javacv / opencv识别黑色多边形上的点?...
  2. android选择多个文件_一分钟合并多个Excel、PDF文件,3种方法任你选择,好用到没朋友...
  3. datagridview选中获取行号_DataGridView控件显示行号的正确代码及分析
  4. python数据分析简答题_Python数据分析与数据可视化-中国大学mooc-试题题目及答案...
  5. windows如何生成ssh密钥
  6. BugkuCTF-MISC题蜜雪冰城~
  7. android 标题栏进度圈使用方法,Android 自定义标题栏 显示网页加载进度的方法实例...
  8. 虚拟机linux中怎样打开qt,虚拟机中在Centos 4.7中安装qt-x11-opensource-4.4.3
  9. shell mysql eof_shell EOF
  10. 单片机拼字程序怎么做_餐饮怎么用微信小程序?餐饮行业怎么做小程序