没有输出的输入是不完整的

0%

剑指offer-数组中的逆序对

本文为剑指 offer 系列第三十五篇。
在数组中寻找逆序对是归并排序的一个非常典型的应用。

题目描述

在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007

解题思路

思路1

设置一个计数器,双层for循环,碰到逆序对则计数器+1,最后返回结果即可。

思路2

利用数据交换的排序方法来变相计算逆序对的数目。这里采用的是冒泡排序。

思路3

基于归并排序来进行计数。关于归并排序大家可以查具体的算法书的介绍。
这里我说一下为什么归并排序相比较于思路1和思路2要好的多。
我们知道归并排序首先是将数据进行分块,直到分到每个小块只有一个元素,然后将小块合并有序的大块。合并的时候采用有序链表合并的方法进行合并,针对于本问题而言,也就是在合并的时候进行逆序对的计算。
下面举例说明一下。比如我们合并的某一步骤,分别得到A = {3,5,6}和 B = {1,2,8}两个序列。
在合并的时候我们对两个数组进行从头的遍历进行合并,合并结果按照从小到大进行排列。

  1. 首先由于A中的3是要大于B中的1的,有序序列为{1},因为A中元素是升序的,A的第一个元素就大于1,所以A中所有的数据都是大于1的,从而仅仅是和1比较的时候就有3个逆序对,这样就可以直接通过A的前后索引计算出来,而不是一一比较再得到。
  2. 然后比较A中的3和B中的2.更新有序序列为{1,2},此时针对于B可以得到逆序对有3个。
  3. 然后比较A中的3和B中的8.更新有序序列为{1,2,3},此时没有新的逆序对增加
  4. 然后比较A中的5和B中的8,更新有序序列为{1,2,3,5},此时没有新的逆序对增加
  5. 然后比较A中的6和B中的8,更新有序序列为{1,2,3,5,6},此时没有新的逆序对增加
  6. 由于A已经遍历完成,将B中剩余元素添加到有序序列中即可。也就得到{1,2,3,5,6,8}。
  7. 最后得到上面两个序列的逆序对有6个。
    对于其他的有序数组也采用类似的方式进行逆序对的计算。从而解决这个问题。

解题代码

思路1代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Solution {
public:
int InversePairs(vector<int> data) {
// 方法1 双层for循环
long res = 0;
for(int i = 0;i<data.size();i++){
for(int j = i+1;j<data.size();j++){
if(data[i]>data[j]) res++;
}
}
return res%1000000007;
}

};

时间复杂度为O(n^2),空间复杂度为O(1)

思路2代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Solution {
public:
int InversePairs(vector<int> data) {
//方法2: 冒泡排序 O(n^2) 通过率为50%
if(data.size() == 0) return 0;
return helperByBubbleSort(data);
}
// 方法2的辅助函数
int helperByBubbleSort(vector<int> data){
long res = 0;
bool notfinish = true;
int len = data.size();
for(int i = 0; i < len-1 && notfinish; i++){
notfinish = false;
for(int j = 0; j < len-i-1; j++){
if(data[j] > data[j+1]){
swap(data[j],data[j+1]);
notfinish = true;
res++;
}
}
}
return res%1000000007;
}

};

时间复杂度最坏的情况为O(n^2),最好的情况为O(n),空间复杂度为O(1)

思路3代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class Solution {
public:
int InversePairs(vector<int> data) {
//方法3: 归并排序
if(data.size() == 0) return 0;
return helperByMergeSort(data,0,data.size()-1)%1000000007;
}
// 方法3的辅助函数
int helperByMergeSort(vector<int>& data,int start,int end){
if(start == end) return 0;
int mid = start+((end-start)>>1);
int left = helperByMergeSort(data,start,mid);
int right = helperByMergeSort(data,mid+1,end);
vector<int> temp(end-start+1,0);
int i = start, j = mid+1, count = 0, index = 0;
while(i <= mid && j<=end){
if(data[i] > data[j]){
count = (count + mid - i + 1)%1000000007;
temp[index++] = data[j++];
}else{
temp[index++]= data[i++];
}
}
// 如果存在一部分没有取完
while(i<=mid){
temp[index++] = data[i++];
}
while(j<=end){
temp[index++] = data[j++];
}
//把排好序的结果放回原数组中
for(int i = start; i<= end; i++){
data[i] = temp[i-start];
}
return (count+left+right)%1000000007;
}
};

时间复杂度O(nlogn),空间复杂度为O(n)

参考

1.剑指Offer-37-数组中逆序对
2. 剑指Offer(三十五):数组中的逆序对
以上,本题结束!