算法笔记_065:分治法求逆序对(Java)
目录
1 问题描述
给定一个随机数数组,求取这个数组中的逆序对总个数。要求时间效率尽可能高。
那么,何为逆序对?
引用自百度百科:
例如,数组(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)+"毫秒");
}
}
运行结果(运行3次):
使用蛮力法得到结果: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)+"毫秒");
}
}
运行结果(运行3次):
分治法得到结果:612226489, 耗时:36毫秒 分治法得到结果:610481152, 耗时:35毫秒 分治法得到结果:612161208, 耗时:32毫秒
参考资料:
1. 归并排序求逆序对
算法笔记_065:分治法求逆序对(Java)的更多相关文章
- 算法笔记_044:表达式计算求值(Java)
目录 1 问题描述 2 解决方案 1 问题描述 问题描述 输入一个只包含加减乖除和括号的合法表达式,求表达式的值.其中除表示整除. 输入格式 输入一行,包含一个表达式. 输出格式 输出这个表达式的 ...
- 分治法求一个N个元素数组的逆序数
背景 逆序数:也就是说,对于n个不同的元素,先规定各元素之间有一个标准次序(例如n个 不同的自然数,可规定从小到大为标准次序),于是在这n个元素的任一排列中,当某两个元素的先后次序与标准次序不同时, ...
- HDU 4911 http://acm.hdu.edu.cn/showproblem.php?pid=4911(线段树求逆序对)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4911 解题报告: 给出一个长度为n的序列,然后给出一个k,要你求最多做k次相邻的数字交换后,逆序数最少 ...
- 求逆序对常用的两种算法 ----归并排 & 树状数组
网上看了一些归并排求逆序对的文章,又看了一些树状数组的,觉得自己也写一篇试试看吧,然后本文大体也就讲个思路(没有例题),但是还是会有个程序框架的 好了下面是正文 归并排求逆序对 树状数组求逆序对 一. ...
- 《github一天一道算法题》:分治法求数组最大连续子序列和
看书.思考.写代码. /*************************************** * copyright@hustyangju * blog: http://blog.csdn. ...
- wikioi 1688 求逆序对
/*=========================================================== wikioi 1688 求逆序对 时间限制: 1 s 空间限制: 12800 ...
- 归并排序&&归并排序求逆序对
归并排序 归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用.将已有序的子序列合并,得到完全有序的序 ...
- 归并排序(归并排序求逆序对数)--16--归并排序--Leetcode面试题51.数组中的逆序对
面试题51. 数组中的逆序对 在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对.输入一个数组,求出这个数组中的逆序对的总数. 示例 1: 输入: [7,5,6,4] 输出 ...
- 浙江工商大学15年校赛I题 Inversion 【归并排序求逆序对】
Inversion Time Limit 1s Memory Limit 131072KB Judge Program Standard Ratio(Solve/Submit) 15.00%(3/20 ...
随机推荐
- CodeForces 731E Funny Game
博弈,$dp$. 设$f[i]$表示 如果先手第一次出手取到位置$i$,直到游戏结束,双方均采取最优策略,先手-后手得分的差值. 那么$f[i]=min(sum[i]-sum[j]+maxf[j+1] ...
- 服务器Tomcat WAS JBoss
做任何web项目,都离不开服务器,有钱的公司用WebSphere.WebLogic,没钱公司用nginx+tomcat,不要小瞧nginx+tomcat麻雀虽小,五脏俱全. 服务器的知识,在笔试.面试 ...
- 取得指定Schema下的表
MYSQL中取得指定Schema下所有表定义的SQL语句如下(假设Schema名为demoschema): SHOWTABLES FROM demoschema MSSQLServer中的系统表sys ...
- CSS 笔记——文本字体
5. 文本字体 -> 文本 (1)text-indent 基本语法 text-indent : length 语法取值 length : 百分比数字 | 由浮点数字和单位标识符组成的长度值,允许 ...
- Linux-Oracle 安装配置步骤
一.打开 VMware 安装 VMware 解压 ORACLER11.2_redhat.rar 到 D:\...\machine\oracle-linux 打开 VMware, 选择 打开虚拟机 找到 ...
- [BZOJ3576]江南乐
挺好的题 我们算出每个数的sg值后异或起来即可 对于$n$,我们要求$sg_n$ 朴素的想法是枚举把$n$个石子分成$m$堆,有$m-n\%m$堆大小为$\left\lfloor\frac nm\ri ...
- 20162312实验四Java Android简易开发
实验准备 Android Studio 的下载: Android Studio 安装教程 准备中遇到的问题 最大的问题就是电脑无法虚拟化,因为微星的型号太多,我只好在网上找了许多方案一个个试,最后终于 ...
- python基础之类与对象,继承与派生
类与对象 对象的本质也就是一个名称空间而已,用于存放自己独有的属性,而类中存放的是对象共有的属性. __init__会在调用类时自动触发 调用类时发生两件事: 1.创建一个空对象stu1 2.自动触发 ...
- textField代理方法
//textField代理方法// 返回no 不能输入文字- (BOOL)textField:(UITextField*)textField shouldChangeCharactersInRange ...
- iOS 系统原生分享图片 文字 音乐 纯视频 网页
为了方便使用,我封装了一个分享的工具类LFSystemShareUtil.工程要引Social.framework. LFSystemShareUtil.h #import <Foundatio ...