算法笔记_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 ...
随机推荐
- Bzoj 1014&Luogu 4036 火星人Prefix(FHQ-Treap)
题面 洛谷 Bzoj 题解 首先,这种带修改的是不能用$SA$的,然后,我们做$SA$的题一般也能二分+$Hash$,所以不妨考虑用$FHQ-Treap$维护树,然后查询就用二分+$Hash$. $H ...
- 手机上编程,编写android apk
韩梦飞沙 yue31313 韩亚飞 han_meng_fei_sha 313134555@qq.com C4Droid:在手机上直接编程,然后导出为apk
- eclipse git解决冲突
转自 http://blog.csdn.net/rosten/article/details/17068285 1.工程->Team->同步: 2.从远程pull至本地,就会出现如下内容 ...
- [BZOJ 1901] Dynamic Rankings
Link: BZOJ 1901 传送门 Solution: 带修改主席树的模板题 对于静态区间第$k$大直接上主席树就行了 但加上修改后会发现修改时复杂度不满足要求了: 去掉/增加第$i$位上的值时要 ...
- python基础之if,while,for
流程控制之if判断 根据女性年龄不同的不同叫法,如: age = 24 if age < 18: print('小妹妹好') elif age <28: print('小姐姐好') els ...
- canvas之arcTo
arc与arcTo,从名字都能看出来相似.arcTo也是画曲线的方法,而且他画出的曲线也是正圆的一段弧线.但他的参数和arc简直是不共戴天~ ctx.arcTo(x1,y1,x2,y2,radius) ...
- python框架django中结合vue进行前后端分离
一:创建django项目 1.django-admin startproject mysite # 创建mysite项目 2.django-admin startapp app01# 创建app01应 ...
- postgres10配置huge_pages
操作系统 修改/boot/grub2/grub.cfg 定位到第一个'menuentry 'CentOS Linux',在"linux16 /vmlinuz"最后面添加 numa= ...
- ylbtech-LanguageSamples-PythonSample
ylbtech-Microsoft-CSharpSamples:ylbtech-LanguageSamples-PythonSample 1.A,示例(Sample) 返回顶部 本示例演示如何使用 C ...
- Javascript函数式编程的一些例子[转载]
函数式编程风格 通常来讲,函数式编程的谓词(关系运算符,如大于,小于,等于的判断等),以及运算(如加减乘数等)都会以函数的形式出现,比如: a > b通常表示为: gt(a, b)/ ...