剑指OFFER-数组中的逆序对
Question
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007。
题目保证输入的数组中没有的相同的数字
数据范围:
对于%50的数据,size<=10^4
对于%75的数据,size<=10^5
对于%100的数据,size<=2*10^5
关键词:逆序对 归并排序
Solution
思路过程:
暴力破解就是每次用一个数字去比较剩下所有数字,这样时间复杂度是O(n^2),估计至少一半的数据会超时(AC25%)。
继续思考,如果从后往前,比如[3,1,2]这个数组,先对最后两个找到逆序对=0,排好这两个[2,1],然后3跟排好的[2,1]挨个儿比较(这里的数组已经排好序了,可以用折半查找来做减少时间),遇到第一个比他小的,那么剩下所有的([1])都比他小,逆序对加剩下的长度(=1)。这样理论上,最大的时间复杂度是N^2
(开始我记错了折半查找的复杂度,以为是log(N),其实是N^2,因为还要加上数组移动的时间,可恶),不过需要额外的空间保存排序好的数组。
这个方法仍然没有通过(AC50%),姑且写一个python版本的在最后。
好多人是使用了归并排序算法,相当于第二种思路一开始折半来排序,数组所有逆序对 = 左半边的逆序对 + 右半边的逆序对 + 左右之间的逆序对。前两个部分递归完成,最后一个部分由于左右也是排完序返回,所以也可以拿指针找到右边第一个大于他的数字,后面的长度就是该指针的逆序对。其实这是一个完整的归并排序。结果这个算法还是没有通过(AC75%) : )。
参考:https://www.jianshu.com/p/bbbab7fa77a2
归并排序
时间复杂度:O(N·log(N))
空间复杂度:O(N)
- Python
class Solution:
def InversePairs(self, data):
ans, new_data = self.BinarySearch(data)
return ans%1000000007
def BinarySearch(self, data):
if len(data)<2:
return 0, data
mid = len(data)//2
left, new_left = self.BinarySearch(data[:mid])
right, new_right = self.BinarySearch(data[mid:])
merge, new_data = self.MergeSearch(new_left,new_right)
return left + right + merge, new_data
def MergeSearch(self, a, b):
new_data = []
count = 0
i, j = 0, 0
while i<len(a) and j<len(b):
if a[i]<=b[j]:
new_data.append(a[i])
i += 1
else:
new_data.append(b[j])
count += len(a) - i
j += 1
new_data += a[i:] + b[j:]
return count, new_data
- C++
class Solution {
public:
int InversePairs(vector<int> data) {
int ans = MergeSort(data, 0, data.size()-1);
return ans%1000000007;
}
int MergeSort(vector<int> &data, int start, int end){
if(start >= end)return 0;
int mid = (start+end)/2;
int left_count = MergeSort(data, start, mid);
int right_count = MergeSort(data, mid+1, end);
//MergeOne(data, start, mid, end);
int merge_count = 0;
vector<int> temp;
int k=0, i=start, j=mid+1;
while(i<=mid && j<= end){
if(data[i] <= data[j]){
temp.push_back(data[i]);
i++;
}
else{
temp.push_back(data[j]);
merge_count = merge_count + mid-i+1;
j++;
}
}
while(i<= mid){
temp.push_back(data[i]);
i++;
}
while(j<=end){
temp.push_back(data[j]);
j++;
}
for(int l=0; l<temp.size(); l++){
data[start+l] = temp[l];
}
return left_count+right_count+merge_count;
}
};
插入排序(未通过,待改)
时间复杂度:O(N*log(N))
空间复杂度:O(N)
- Python
class Solution:
def InversePairs(self, data):
if len(data)<2:
return 0
count = 0
if data[-1] > data[-2]:
sort_d = [data[-1], data[-2]]
else:
sort_d = [data[-2], data[-1]]
count += 1
for i in range(3, len(data)+1):
temp, sort_d = self.find(data[-i], sort_d)
count += temp
return count
def find(self, x, d):
left = 0
right = len(d)-1
while left<right:
mid = (left+right)//2
if d[mid]>x and d[mid+1]<x:
return len(d)-mid-1, d[:mid+1] + [x] + d[mid+1:]
if d[mid-1]>x and d[mid]<x:
return len(d)-mid, d[:mid] + [x] + d[mid:]
if d[mid]>x:
left = mid+1
else:
right = mid
if left==0:
return len(d), [x] + d[:]
if right==len(d)-1:
return 0, d[:] + [x]
return len(d)-right-1, d[:right+1] + [x] + d[right+1:]