剑指Offer——数组中的逆序对(归并排序的应用)
public class Solution {
private int count = 0;
/*讨论说可以用分治法——归并排序的思路喔*/
public int InversePairs(int [] array) {
if(array.length <= 0)return 0;
mergeSortAndCount(array);
return count % 1000000007;
}
private void mergeSortAndCount(int[] a) {
if(a.length == 1)return;
int[] b, c;
if(a.length % 2 == 0) {//数组元素个数为偶数的情况
b = new int[a.length / 2];
c = new int[a.length / 2];
for(int i = 0, j = a.length / 2, k = 0; i < a.length / 2 && j < a.length; i++, j++, k++) {
b[k] = a[i];
c[k] = a[j];
}
} else {
b = new int[a.length / 2 + 1];
c = new int[a.length / 2];
for(int i = 0, j = a.length / 2 + 1, k = 0; i <= a.length / 2 || j < a.length; i++, j++, k++) {
//这里b数组要初始化的长度是长一点的
b[k] = a[i];
if(j < a.length)c[k] = a[j];
}
}
mergeSortAndCount(b);
mergeSortAndCount(c);
mergeAndCount(b, c, a);//把已经排好序的b和c数组合并并且计算逆序,合并到a数组中去。
}
private void mergeAndCount(int[] b, int[] c, int[] a) {
int i = 0, j = 0, k = 0;
while(i < b.length && j < c.length) {
if(b[i] <= c[j]) {//正常情况,非逆序
a[k ++] = b[i ++];
} else {//b的元素大于c的情况,逆序
count = count + b.length - i;//因为这个数组是排好序的,b[i]元素大于c[j],意味着b[i]-b[a.length - 1]的元素都大于它
a[k ++] = c[j ++];
}
}
while(i < b.length)a[k ++] = b[i ++];
while(j < c.length)a[k ++] = c[j ++];
}
}
改造后的归并:
这里全程就用到一个辅助数组,和一个本身就要排序的数组。
内部在处理递归的时候用的都是index,然后要说明的是,辅助数组和排序数组的身份是互换的。本来应该是:在原数组的两部分排序好的数组要合并嘛,就借一个辅助数组放合并的数据,然后再复制回原来的数组。
但现在我们用了一个整数i,来交替完成这个过程,反正只要最后一次合并是合并到要排序的那个数组就好了嘛。
这里有个也是只用了一个辅助数组,但不是交替用的例子,这个其实好理解点hh:
https://blog.csdn.net/abc7845129630/article/details/52740746
然后上代码:
public class Solution {
private long count = 0;//类变量,逆序对的数量
/*减少辅助数组的归并法,或者说只用一个辅助数组的归并法*/
public int InversePairs(int [] array) {
if(array.length <= 0)return 0;
int i = 0;
int[] help = new int[array.length];//辅助数组,就用这一个就够了,所以空间复杂度为O(n)
mergeAndCountSort(array, 0, array.length - 1, i, help);
return (int)(count % 1000000007);
}
/**
* 对a数组中的begin到end元素进行归并排序,如果i是奇数则排序后的数组合并到b数组中去(b[begin-end]有序);否则就存到a数组中去(a[begin-end]有序)
* 之所以这样做是因为可以避免利用了辅助数组后还有复制元素到原来数组的操作
* 因为我们最后要排序的是a数组嘛,所以在主程序中调用这个sort应该传i=0或者是其他偶数
* @param a
* @param begin
* @param end
* @param i
* @param b
*/
private void mergeAndCountSort(int[] a, int begin, int end, int i, int[] b) {
if (begin == end) {// 递归终结条件
// 一开始是没有加这句的,如果排序的数组是偶数个的话,就没事,奇数个的话就有事。
if (i % 2 == 1) b[begin] = a[begin];// 奇数,这个结果要到b数组中去,否则不用动
} else {
int middle = (begin + end) / 2;// 中间index
// 分别对左半部分和右半部分做递归的归并排序,i + 1保证了下次用另一个数组做辅助数组
mergeAndCountSort(a, begin, middle, i + 1, b);
mergeAndCountSort(a, middle + 1, end, i + 1, b);
if (i % 2 == 1) {
// i是奇数,那么归并后数组要合并到b数组中去
mergeAndCount(a, begin, middle, end, b);
} else {
// i是偶数,那么归并后的数组要合并到a数组去
mergeAndCount(b, begin, middle, end, a);
}
}
}
/**
* 带有数逆序对的合并有序数组的方法,合并的过程中顺便数逆序对
* 将a数组中的[begin-middle]有序对和[middle + 1-end]有序对合并,合并结果到result数组中去,result数组的[begin-end]有序
* @param a
* @param begin
* @param middle
* @param end
* @param result
*/
private void mergeAndCount(int[] a, int begin, int middle, int end, int[] result) {
int i = begin, j = middle + 1, k = begin;
while(i <= middle && j <= end) {
if(a[i] <= a[j]) {
//左边的元素小于右边的,正常情况,不是逆序对
result[k++] = a[i++];
} else {
//左边的元素大于右边的,逆序情况
//因为这个数组是排好序的,所以意味着左边部分后面的元素也大于这个右边部分的这个元素,然后这里要提前加上去
//因为小的元素要被加到result中去,等等其他的就不会碰到它了
count = count + (middle - i + 1);
result[k++] = a[j++];
}
}
while(i <= middle) result[k++] = a[i++];
while(j <= end) result[k++] = a[j++];
}
}
我刚改完这个归并的时候,在本地跑可以,然后排序也有效果,结果特么还是一样的结果????
%50一样卡在那个位置……我佛了。
查了很久,结合那个数据猜发现,这个给的例子就算是在新的网页中打开,也是还没结束的:

也就是说,这个逆序对的个数,应该是可以超过Int的范围的……
所以就像上面贴的代码一样,把int的count改成long,返回的时候再转换成int才通过。
这里讲一下这个归并法的一些要注意的地方:
1. 就是刚刚说的,逆序个数会大于int的最大值的问题,要用long。
2. 在递归中,本来begin == end就可以返回了,因为就有序了嘛,但这里因为涉及到复制来复制去,还有加个i的判断,如果为奇数要把这个数搞到b数组中去。(测试过,如果待排序的数字个数是偶数个好像就没有问题,是奇数个就会有问题)
3. 就是在数逆序对的时候的问题了,因为是排好序的,而且小的那个会进入目标数组排队,所以发现了左边的元素大于右边的元素时,不是只count++:
else {
//左边的元素大于右边的,逆序情况
//因为这个数组是排好序的,所以意味着左边部分后面的元素也大于这个右边部分的这个元素,然后这里要提前加上去
//因为小的元素要被加到result中去,等等其他的就不会碰到它了
count = count + (middle - i + 1);
剑指Offer——数组中的逆序对(归并排序的应用)的更多相关文章
- [剑指OFFER] 数组中的逆序对
题目描述 在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对.输入一个数组,求出这个数组中的逆序对的总数. 分析:利用归并排序的思想,分成2部分,每一部分按照从大到 ...
- 剑指Offer——数组中的逆序对
题目描述: 在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对.输入一个数组,求出这个数组中的逆序对的总数P.并将P对1000000007取模的结果输出. 即输出P%100 ...
- 用js刷剑指offer(数组中的逆序对)
题目描述 题目描述 在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对.输入一个数组,求出这个数组中的逆序对的总数P.并将P对1000000007取模的结果输出. 即输出P ...
- 剑指 Offer——数组中的逆序对
1. 题目 2. 解答 借助于归并排序的分治思想,在每次合并的时候统计逆序对.因为要合并的两个数组都是有序的,如果左半部分数组当前值大于右半部分数组当前值,那么左半部分数组当前值右边的数就都大于右半部 ...
- 剑指offer_数组中的逆序对
题目描述 在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对.输入一个数组,求出这个数组中的逆序对的总数P. 并将P对1000000007取模的结果输出. 即输出P%100 ...
- 剑指Offer-34.数组中的逆序对(C++/Java)
题目: 在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对.输入一个数组,求出这个数组中的逆序对的总数P.并将P对1000000007取模的结果输出. 即输出P%10000 ...
- 剑指Offer34 数组中的逆序对
/************************************************************************* > File Name: 34_Invers ...
- 剑指 Offer 51. 数组中的逆序对 + 归并排序 + 树状数组
剑指 Offer 51. 数组中的逆序对 Offer_51 题目描述 方法一:暴力法(双层循环,超时) package com.walegarrett.offer; /** * @Author Wal ...
- 剑指offer-数组中的逆序对-数组-python
题目描述 在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对.输入一个数组,求出这个数组中的逆序对的总数P.并将P对1000000007取模的结果输出. 即输出P%1000 ...
随机推荐
- android开发之Bitmap 、byte[] 、 Drawable之间的相互转换
一.相关概念 1.Drawable就是一个可画的对象,其可能是一张位图(BitmapDrawable),也可能是一个图形(ShapeDrawable),还有可能是一个图层(LayerDrawable) ...
- 杂项:Webpack
ylbtech-杂项:Webpack 本质上,webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler).当 webpack 处理应用程序时,它会递归地 ...
- 查看,修改,上传monmap命令
标签(空格分隔): ceph,ceph运维,monmap 查看集群monmap命令 从集群获取monmap: # ceph mon getmap -o monmap 查看上一步下载的monmap: # ...
- [.net]手机APP与IIS服务器联调配置
前端时间写过一段时间接口,在后期的时候,出现了一些无法通过查看日志来找出问题所在的bug.于是,将手机APP连接到IIS服务器上进行调试,下面是配置的具体步骤 1. 配置IIS 添加网站,将物理路径 ...
- 【java并发编程艺术学习】(二)第一章 java并发编程的挑战
章节介绍 主要介绍并发编程时间中可能遇到的问题,以及如何解决. 主要问题 1.上下文切换问题 时间片是cpu分配给每个线程的时间,时间片非常短. cpu通过时间片分配算法来循环执行任务,当前任务执行一 ...
- c语言基础 c和指针
句子 c规定数组名代表数组首元素的地址 如果&a 则代表整个数组 没有内存哪来的指针 数据类型的本质:固定大小内存的别名 变量的本质:(一段连续)内存空间的别名,内存空间的标号 指针是一种数据 ...
- linux命令-rpm查询包
安装了哪些rpm包呢 [root@wangshaojun Packages]# rpm -qa /////查看全部安装的包 [root@wangshaojun Packages]# rpm -qa l ...
- 通过HBase Shell与HBase交互
出处:http://www.taobaotest.com/blogs/1604 业务开发测试HBase之旅二:通过HBase Shell与HBase交互 yedu 发表于:2011-10-11 浏览: ...
- 红米用adb连接显示unauthorized的解决办法
网上能搜到的各种办法都试过了, 没一个可行: 1.大部份是用来解决usb不识别的,也就是adb devices不显示.但现在是有显示,只是unauthorized 2.我习惯用tcp连接,少根线,多台 ...
- python包管理
如果是python 项目目录,例如pycharm里新建的python项目,则可以通过from,import导入目录下的文件夹. 如果是普通文件目录,则代码里不能相对方式导入该目录下的文件夹,需要加入要 ...