(剑指Offer)面试题36:数组中的逆序对
题目:
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。
思路:
1、顺序扫描
顺序扫描整个数组,每扫描到一个数字,就将该数字跟后面的数字比较,如果大于的话,则这两个数字构成了逆序对。(比较简单,这里就不贴出代码)
时间复杂度:O(n^2)
2、归并思想
将数组不断地等分为两个子数组,然后自下而上地统计两个子数组各自的逆序对以及合并后的逆序对。
假设两个子数组左右分别为A,B,长度分为lenA,lenB,子数组已排好序,那么如何计算逆序对的个数呢?
当pA=lenA-1;pB=lenB-1;A[pA]>A[pB],说明A的最后一个数比B所有的数都大,此时个数为lenB,pA--;如果A[pA]<B[pB],说明左边的数不一定比右边的大,pB--;依次计算直至pA或pB为0,这样就得到了两个数组合并时的逆序对,再加上左子数组的逆序对和右子数组的逆序对,就等于整个数组的逆序对。
(代码中巧妙地利用了一个辅助数组来排序,参考下面的代码)
时间复杂度:O(nlogn)
代码:
#include <iostream>
using namespace std;
int InversePairsCore(int *data,int *copy,int start,int end){
if(start==end){
copy[start]=data[start];
return 0;
}
int length=(end-start)>>1;
int left=InversePairsCore(copy,data,start,start+length);
int right=InversePairsCore(copy,data,start+length+1,end);
int i=start+length;
int j=end;
int copyIndex=end;
int count=0;
while(i>=start && j>=start+length+1){
if(data[i]>data[j]){
copy[copyIndex]=data[i];
copyIndex--;
i--;
count+=j-(start+length);
}
else{
copy[copyIndex]=data[j];
copyIndex--;
j--;
}
}
for(;i>=start;i--)
copy[copyIndex--]=data[i];
for(;j>=start+length+1;j--)
copy[copyIndex--]=data[j];
return left+right+count;
}
int InversePairs(int *data,int length){
if(data==NULL || length<=0)
return 0;
int* copy=new int[length];
for(int i=0;i<length;i++)
copy[i]=data[i];
int count=InversePairsCore(data,copy,0,length-1);
delete[] copy;
return count;
}
int main()
{
int A[]={7,5,6,4};
int length=sizeof(A)/sizeof(A[0]);
cout << InversePairs(A,length) << endl;
return 0;
}
在线测试OJ:
http://www.nowcoder.com/books/coding-interviews/96bd6684e04a44eb80e6a68efc0ec6c5?rp=2
AC代码:
class Solution {
public:
int InversePairs(vector<int> data) {
int length=data.size();
if(length<=1)
return 0;
int start=0;
int end=length-1;
int result=InversePairsCore(data,start,end);
return result;
}
int InversePairsCore(vector<int> &data,int start,int end){
if(start>=end)
return 0;
int mid=start+((end-start)>>1);
int left=InversePairsCore(data,start,mid);
int right=InversePairsCore(data,mid+1,end);
int i=start;
int j=mid+1;
int count=0;
vector<int> tmp;
while(i<=mid && j<=end){
if(data[i]>data[j]){
count+=end-j+1;
tmp.push_back(data[i]);
i++;
}
else{
tmp.push_back(data[j]);
j++;
}
}
while(i<=mid)
tmp.push_back(data[i++]);
while(j<=end)
tmp.push_back(data[j++]);
for(int k=start;k<=end;k++)
data[k]=tmp[k-start];
return left+count+right;
}
};
class Solution {
public:
int InversePairs(vector<int> data) {
int length=data.size();
if(length<=0)
return 0;
vector<int> copy(data.begin(),data.end());
int start=0;
int end=length-1;
int count=InversePairsCore(data,copy,start,end);
return count;
}
int InversePairsCore(vector<int> &data,vector<int> ©,int start,int end){
if(start==end){
copy[start]=data[start];
return 0;
}
int length=(end-start)>>1;
int left=InversePairsCore(copy,data,start,start+length);
int right=InversePairsCore(copy,data,start+length+1,end);
int i=start+length;
int j=end;
int copyIndex=end;
int count=0;
while(i>=start && j>=start+length+1){
if(data[i]>data[j]){
copy[copyIndex--]=data[i--];
count+=j-(start+length);
}
else
copy[copyIndex--]=data[j--];
}
for(;i>=start;i--)
copy[copyIndex--]=data[i];
for(;j>=start+length+1;j--)
copy[copyIndex--]=data[j];
return left+count+right;
}
};
(剑指Offer)面试题36:数组中的逆序对的更多相关文章
- 【剑指offer】题目36 数组中的逆序对
数组中任取两个数字,如果前面的数字大于后面的数字称为一个逆序对 如:1,2,1,2,1 有3个逆序对 思路:知道O(N2)肯定是错的.开始想hash,试图找到O(n)的算法,想了很久,找不到.后来想到 ...
- 剑指Offer - 九度1348 - 数组中的逆序对
剑指Offer - 九度1348 - 数组中的逆序对2014-01-30 23:19 题目描述: 在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对.输入一个数组,求出这个 ...
- 剑指offer(35)数组中的逆序对
题目描述 在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对.输入一个数组,求出这个数组中的逆序对的总数P.并将P对1000000007取模的结果输出. 即输出P%1000 ...
- 《剑指offer》51:数组中的逆序对
题目描述 在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对.输入一个数组,求出这个数组中的逆序对的总数P.并将P对1000000007取模的结果输出. 即输出P%1000 ...
- 【剑指Offer】35、数组中的逆序对
题目描述: 在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对.输入一个数组,求出这个数组中的逆序对的总数P.并将P对1000000007取模的结果输出. 即输出P ...
- 剑指Offer:面试题29——数组中出现次数超过一半的数字(java实现)
PS:在前几天的面试中,被问到了这个题.然而当时只能用最低效的方法来解. 问题描述: 数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字.例如输入一个长度为9的数组{1,2,3,2,2,2, ...
- 剑指offer 面试题56. 数组中只出现一次的两个数字
题目描述 一个整型数组里除了两个数字之外,其他的数字都出现了两次.请写程序找出这两个只出现一次的数字. 方法1:用set记录出现过的数字 class Solution { public: void F ...
- 剑指Offer - 九度1370 - 数组中出现次数超过一半的数字
剑指Offer - 九度1370 - 数组中出现次数超过一半的数字2013-11-23 03:55 题目描述: 数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字.例如输入一个长度为9的数组 ...
- 剑指Offer - 九度1351 - 数组中只出现一次的数字
剑指Offer - 九度1351 - 数组中只出现一次的数字2013-11-23 01:23 题目描述: 一个整型数组里除了两个数字之外,其他的数字都出现了两次.请写程序找出这两个只出现一次的数字. ...
- 剑指offer:二维数组中的查找
目录 题目 解题思路 具体代码 题目 题目链接 剑指offer:二维数组中的查找 题目描述 在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺 ...
随机推荐
- 【自动化测试】Selenium的智能等待
dr.implicitly_wait(30) --- 智能等待 http://www.cnblogs.com/fnng/p/3214112.html =========== selenium 调用键盘 ...
- Spring IOC 三种注入方式
1. 接口注入 2. setter注入 3. 构造器注入 对象与对象之间的关系可以简单的理解为对象之间的依赖关系:A类需要B类的一个实例来进行某些操作,比如在A类的方法中需要调用B类 ...
- 【解题报告】POJ-1108 Split Windows
原题地址:http://poj.org/problem?id=1108 ============================== 题目大意: 一棵树表示一个窗口,它的叶子节点都是大写字母,非叶子节 ...
- linux下动态库编译的依赖问题
这里主要是想试验一下,对一个具有多层嵌套的动态库进行编译时,是否要把最底层的库也包含进来的问题,结论是:只要直接依赖的库名称,不需要最底层库名称. 一,目录结构ZZZ├── add│ ├── ad ...
- 三种map的循环
for(Map.Entry<String, List> entry : map.entrySet()) { System.out.println(entry.getKey()); List ...
- Pitcher Rotation
题意: n个人m个对手给出每个人能战胜每个敌人的概率,现在有g个比赛,每个人赛完后要休息4天(可重复用),求能获得胜利的最大期望个数. 分析: 因为只有每个人5天就能用一次,所以对于每个人来说,只有得 ...
- Java 分割文件 注意事项
public static void main(String args[]) throws Exception { if (args.length < 1) { System.exit(0); ...
- Chapter10:泛型算法
泛型算法的基础是迭代器. 迭代器令算法不依赖于容器,但是算法依赖于元素类型的操作.也即:算法永远不会执行容器的操作. 那么,如果想向容器中添加元素或者执行其他的一些操作呢?标准库提供了插入迭代器来完成 ...
- (转载)OC学习篇之---Foundation框架中的其他类(NSNumber,NSDate,NSExcetion)
前一篇说到了Foundation框架中的NSDirctionary类,这一一篇来看一下Foundation的其他常用的类:NSNumber,NSDate,NSException. 注:其实按照Java ...
- 星星字体 ps教程
本教程主要使用Photoshop制作绚丽星空装饰的艺术字教程,这个教程很简单,只需要一些简单技巧,即可做出海报.书籍杂志的封面效果.其中的字体.笔刷和背景均可以更换 教程所需要的素材链接:http:/ ...