数组中的逆序对
描述
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P mod 1000000007
数据范围: 对于 50 的数据,
s
i
z
e
≤
1
0
4
size≤10^4
size≤104
对于 100%的数据,
s
i
z
e
≤
1
0
5
size≤10^5
size≤105
数组中所有数字的值满足 0≤val≤10000000
要求:空间复杂度 O(n),时间复杂度 O(nlogn)
输入描述:
题目保证输入的数组中没有的相同的数字
分析
暴力法:
首先确定一个基准元素arr[i],依次判断索引大于i的元素,其空间复杂度为
O
(
1
)
O(1)
O(1),时间复杂度为
O
(
N
2
)
O(N^2)
O(N2)
显然与题目中的要求在时间复杂度上不匹配。
题解
这个题目的一个比较理想的做法是采用分治策略,采用归并排序类似的思路进行合并,其具体步骤如下。
这个题目和分治的重点一样,重点不在分,而在于合。
(1)分:将数组分为左右两个长度相当的部分,直至其长度为1。
(2)合:按照归并排序的思想进行归并排序,不过在合的时候需要在比较的时候,计数左边部分比右边部分大的组合。
例如:
如果两个区间为[4, 3] 和[1, 2]
那么逆序数为(4,1),(4,2),(3,1),(3,2),同样的如果区间变为有序,比如[3,4] 和 [1,2]的结果是一样的,也就是说区间有序和无序结果是一样的。
但是如果区间有序会有什么好处吗?当然,如果区间有序,比如[3,4] 和 [1,2]
如果3 > 1, 显然3后面的所有数都是大于1, 这里为 4 > 1,所以我们可以在合并的时候利用这个规则。
通过上述分析可以看出其时间复杂度为 O ( N l o g N ) O(N log N) O(NlogN),空间复杂度为 O ( n ) O(n) O(n)。
代码
class Solution:
def merge(self, left, right):
length_left = len(left)
length_right = len(right)
out = []
i, j = 0, 0
while i < length_left and j < length_right:
if left[i] > right[j]:
self.res += length_left - i
out.append(right[j])
j += 1
else:
out.append(left[i])
i += 1
if i == length_left:
while j < length_right:
out.append(right[j])
j += 1
if j == length_right:
while i < length_left:
out.append(left[i])
i += 1
return out
def divide_merge(self, data):
length = len(data)
# if length == 1:
# return data,[]
if length == 3:
if data[1] > data[2]:
self.res += 1
return [data[0]],[data[2],data[1]]
else:
return [data[0]], data[1:]
if length == 2:
return [data[0]], [data[1]]
mid = length // 2
left_0, right_0 = self.divide_merge(data[:mid])
left = self.merge(left_0, right_0)
left_1, right_1 = self.divide_merge(data[mid:])
right = self.merge(left_1, right_1)
out = self.merge(left, right)
return out,[]
def InversePairs(self, data):
# write code here
self.res = 0
self.divide_merge(data)
return self.res % 1000000007