java实现求逆序对
1 问题描述
给定一个随机数数组,求取这个数组中的逆序对总个数。要求时间效率尽可能高。
那么,何为逆序对?
引用自百度百科:
设 A 为一个有 n 个数字的有序集 (n>1),其中所有数字各不相同。
如果存在正整数 i, j 使得 1 ≤ i < j ≤ n 而且 A[i] > A[j],则 <A[i], A[j]> 这个有序对称为 A 的一个逆序对,也称作逆序数。
例如,数组(3,1,4,5,2)的逆序对有(3,1),(3,2),(4,2),(5,2),共4个。
2 解决方案
2.1 蛮力法
初步一看,使用蛮力是最直接也最简单的方法,但是时间效率为O(n^2)。
即从第1个元素,开始依次和后面每一个元素进行大小比较,若大于,则逆序对个数加1。
package com.liuzhen.systemExe;
public class Main{
//蛮力法求取数组A中逆序对数
public int bruteReverseCount(int[] A) {
int result = 0;
for(int i = 0;i < A.length;i++) {
for(int j = i;j < A.length;j++) {
if(A[i] > A[j])
result++;
}
}
return result;
}
//获取一个随机数数组
public int[] getRandomArray(int n) {
int[] result = new int[n];
for(int i = 0;i < n;i++) {
result[i] = (int)( Math.random() * 50); //生成0~50之间的随机数
}
return result;
}
public static void main(String[] args){
long t1 = System.currentTimeMillis();
Main test = new Main();
int[] A = test.getRandomArray(50000);
int result = test.bruteReverseCount(A);
long t2 = System.currentTimeMillis();
System.out.println("使用蛮力法得到结果:"+result+", 耗时:"+(t2 - t1)+"毫秒");
}
}
运行三次的结果:
使用蛮力法得到结果:612226389, 耗时:8094毫秒
使用蛮力法得到结果:610311942, 耗时:8015毫秒
使用蛮力法得到结果:610657465, 耗时:8079毫秒
2.2 分治法(归并排序)
除了蛮力法,此处可以借用归并排序的思想来解决此题,此时时间复杂度为O(n*logn)。归并排序,具体是先进行对半划分,直到最后左半边数组只有一个元素,右半边数组中也只有一个元素时,此时开始进行回溯合并。那么,计算逆序对个数的关键,就在于此处的回溯合并过程,当左半边元素(PS:回溯过程中,左半边和右半边元素均已是升序排序)中出现大于右半边元素时,那么左半边这个元素及其后面的所有元素均大于这个右半边元素,记这些元素个数为len,那么逆序对个数要自增len。
package com.liuzhen.systemExe;
public class Main{
public long count = 0; //全局变量,使用合并排序,计算逆序对数
//使用归并排序方法计算数组A中的逆序对数
public void getReverseCount(int[] A) {
if(A.length > 1) {
int[] leftA = getHalfArray(A, 0); //数组A的左半边元素
int[] rightA = getHalfArray(A, 1); //数组A的右半边元素
getReverseCount(leftA);
getReverseCount(rightA);
mergeArray(A, leftA, rightA);
}
}
//根据judge值判断,获取数组A的左半边元素或者右半边元素
public int[] getHalfArray(int[] A, int judge) {
int[] result;
if(judge == 0) { //返回数组A的左半边
result = new int[A.length / 2];
for(int i = 0;i < A.length / 2;i++)
result[i] = A[i];
} else { //返回数组的右半边
result= new int[A.length - A.length / 2];
for(int i = 0;i < A.length - A.length / 2;i++)
result[i] = A[A.length / 2 + i];
}
return result;
}
//合并数组A的左半边和右半边元素,并按照非降序序列排列
public void mergeArray(int[] A, int[] leftA, int[] rightA) {
int len = 0;
int i = 0;
int j = 0;
int lenL = leftA.length;
int lenR = rightA.length;
while(i < lenL && j < lenR) {
if(leftA[i] > rightA[j]) {
A[len++] = rightA[j++]; //将rightA[j]放在leftA[i]元素之前,那么leftA[i]之后lenL - i个元素均大于rightA[j]
count += (lenL - i); //合并之前,leftA中元素是非降序排列,rightA中元素也是非降序排列。所以,此时就新增lenL - i个逆序对
} else {
A[len++] = leftA[i++];
}
}
while(i < lenL)
A[len++] = leftA[i++];
while(j < lenR)
A[len++] = rightA[j++];
}
//获取一个随机数数组
public int[] getRandomArray(int n) {
int[] result = new int[n];
for(int i = 0;i < n;i++) {
result[i] = (int)( Math.random() * 50); //生成0~50之间的随机数
}
return result;
}
public static void main(String[] args){
long t1 = System.currentTimeMillis();
Main test = new Main();
int[] A = test.getRandomArray(50000);
test.getReverseCount(A);
long t2 = System.currentTimeMillis();
System.out.println("分治法得到结果:"+test.count+", 耗时:"+(t2 - t1)+"毫秒");
}
}
运行三次结果:
分治法得到结果:612226489, 耗时:36毫秒
分治法得到结果:610481152, 耗时:35毫秒
分治法得到结果:612161208, 耗时:32毫秒
java实现求逆序对的更多相关文章
- 算法笔记_065:分治法求逆序对(Java)
目录 1 问题描述 2 解决方案 2.1 蛮力法 2.2 分治法(归并排序) 1 问题描述 给定一个随机数数组,求取这个数组中的逆序对总个数.要求时间效率尽可能高. 那么,何为逆序对? 引用自百度 ...
- 归并排序&&归并排序求逆序对
归并排序 归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用.将已有序的子序列合并,得到完全有序的序 ...
- HDU 3743 Frosh Week(归并排序求逆序对)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3743 题目意思就是给你一个长为n的序列,让你求逆序对.我用的是归并排序来求的.归并排序有一个合并的过程 ...
- AC日记——codevs 1688 求逆序对
1688 求逆序对 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 黄金 Gold 题解 查看运行结果 题目描述 Description 给定一个序列a1,a2,…, ...
- POJ2299Ultra-QuickSort(归并排序 + 树状数组求逆序对)
树状数组求逆序对 转载http://www.cnblogs.com/shenshuyang/archive/2012/07/14/2591859.html 转载: 树状数组,具体的说是 离散化+树 ...
- codevs1688 求逆序对
题目描述 Description 给定一个序列a1,a2,…,an,如果存在i<j并且ai>aj,那么我们称之为逆序对,求逆序对的数目 数据范围:N<=105.Ai<=105. ...
- HDU 4911 http://acm.hdu.edu.cn/showproblem.php?pid=4911(线段树求逆序对)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4911 解题报告: 给出一个长度为n的序列,然后给出一个k,要你求最多做k次相邻的数字交换后,逆序数最少 ...
- SGU 180 Inversions(离散化 + 线段树求逆序对)
题目链接:http://acm.sgu.ru/problem.php?contest=0&problem=180 解题报告:一个裸的求逆序对的题,离散化+线段树,也可以用离散化+树状数组.因为 ...
- 树状数组求逆序对:POJ 2299、3067
前几天开始看树状数组了,然后开始找题来刷. 首先是 POJ 2299 Ultra-QuickSort: http://poj.org/problem?id=2299 这题是指给你一个无序序列,只能交换 ...
随机推荐
- [hdu3364]xor方程组消元
题意:n个灯,m个开关,给定每个开关控制的灯,全部的灯初始时全部熄灭,开关按一下其所控制的灯的状态全部反转,开关最多只能按一下.问达到目标状态的方案数. 思路:xor方程组的模型. 1 2 3 4 5 ...
- MySQL索引知识介绍
前言: 索引是MySQL数据库中的重要对象之一,索引的目的在于提高查询效率.可以类比字典中的目录,查找字典内容时可以根据目录查找到数据的存放位置,然后直接获取即可.索引是表的目录,在查找内容之前可以先 ...
- HTML简单的伪装与造假
利用浏览器制止台能简单的修改内容,致使其造成伪装. 打开网页控制台后,不要管那些眼花撩乱的代码,我们直接寻找控制台顶部的 Tab 栏 找到 Console 这个 Tab,点击它,把代码 documen ...
- Redis数据类型简介(十分钟快速学习Redis)
如何在ubuntu18.04上安装和保护redis 如何连接到Redis数据库 如何管理Redis数据库和Keys 如何在Redis中管理副本和客户端 如何在Redis中管理字符串 如何在Redis中 ...
- Python 常用编码规范
一.简明概述 1.编码 如无特殊情况, 文件一律使用 UTF-8 编码 如无特殊情况, 文件头部必须加入#-*-coding:utf-8-*-标识 2.代码格式 2.1.缩进 统一使用 4 个空格进行 ...
- hdu5687 字典树
hdu5687 #include <bits/stdc++.h> using namespace std; , n = ; struct node { ]; int cnt; void i ...
- TypeError: Cannot assign to read only property 'exports' of object '#<Object>'
我的项目在mac上运行的很好,结果windows电脑,就一直报这个错误 解决方案: babel增加 @babel/plugin-transform-modules-commonjs 参考文章: htt ...
- js 简单有效判断日期有效性(含闰年)
原文:https://zhidao.baidu.com/question/1701946584925153620.html 要想精确验证,最容易想到的方法就是通过月份判断日期是否合法(1~28/29/ ...
- 【MySQL】MyISAM和InnoDB存储引擎区别详解
MyISAM 主键索引(引擎的索引文件和数据文件是分离的) (图片来自:https://blog.csdn.net/u010922732/article/details/82994253) 非主键索引 ...
- URL特殊字符转义
在URL中,某些特殊字符会被转义成其它项,为了使这些特殊字符能正确表达,需用%加该字符的ASCII码在URL中显示.