《剑指offer》python实现系列,全目录
题目描述:
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。如数组【7,6,5,4】中,一共有5个逆序对,分别是(7,6)(7,5)(7,4)(6,4)(5,4)
输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007
第一想法:
遍历每个元素,,如果已经遍历过,就跳过。
将这个元素与它之前的所有元素比较,统计比它大的元素个数。
1
2
3
4
5
6
7
8
9
10
11#暴力法,复杂度O(n^2)
def InversePairs(self, data):
if len(data)<=1:
return 0
count=0
length=len(data)
for i in range(length-1):
for j in range(i+1,length):
if data[i]>data[j]:
count+=1
return count % 1000000007
剑指offer方法:
基于归并排序,递归,只是在排序过程中要统计逆序对个数
以【7,5,6,4】为例,设逆序对个数为N。
先把它分解成两个长度为2的子数组(如a),再把这两个子数组分别拆分成两个长度为1的子数组(如b)。
接下来边合并相邻的子数组,一边统计逆序对的数目。在第一对长度为1的子数组{7}、{5}中,7大于5,因此(7,5)组成一个逆序对,N=1。
同样,在第二对长度为1的子数组{6}、{4}中,也有逆序对(6,4),N+1=2。
由于我们已经统计了这两对子数组内部的逆序对,因此需要把这两对子数组排序(如c),以免在以后的统计过程中再重复统计。
如何对两对子数组排序并再排序过程中更新N?
设置两个指针分别指向两个子数组中的最大值,p1指向7,p2指向6
比较p1和p2指向的值,如果p1大于p2,因为p2指向的是最大值,所以第二个子数组中有几个元素就有几对逆序对(第二个子数组当前有两个元素,逆序对加2,N=N+2=4),7>6,比较完之后将p1指向的值放入辅助数组里,辅助数组里现在有一个数字7,然后将p1向前移动一位指向5
再次判断p1和p2指向的值,p1小于p2,因为p1指向的是第一个子数组中最大值,所以子数组中没有能和当前p2指向的6构成逆序对的数,将p2指向的值放入辅助数组,并向前移动一位指向4,此时辅助数组内为6,7
继续判断p1(指向5)和p2(指向4),5>4,第二个子数组中只有一个数字,逆序对加1,4+1=5。然后将5放入辅助数组,第一个子数组遍历完毕,只剩下第二个子数组,当前只有一个4,将4也放入辅助数组,函数结束。辅助数组此时为4,5,6,7.逆序对为5.
辅助数组有啥用?我们只要N就行了啊
答:用于保存排序好的结果,在递归排序的时候要用到上一步归并的结果
就比归并排序多了一步
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
37class Solution:
def __init__(self):
self.count = 0
def InversePairs(self, data):
if len(data)<=0:
return 0
self.mergeSort(data)
return self.count % 1000000007
def mergeSort(self,data):
if len(data) < 2:
return data
midIndex = len(data)>>1
leftlist = self.mergeSort(data[:midIndex])
rightlist = self.mergeSort(data[midIndex:])
return self.merge(leftlist,rightlist)
def merge(self,leftlist,rightlist):
resultlist = [] #辅助数组记录排序后的结果
i,j =len(leftlist)-1,len(rightlist)-1
#注意这里ij初始化为最后一个元素,因为是从后往前比较,便于计算count
while i>=0 and j >= 0:
if leftlist[i] > rightlist[j]:
self.count += (j+1)#就是比归并排序多了这一步
resultlist.append(leftlist[i]) #按从大到小排
i -= 1
else:
resultlist.append(rightlist[j])
j -= 1
#接上剩余的元素
resultlist = resultlist[::-1] #注意做个翻转
resultlist = leftlist[:i+1] + resultlist
resultlist = rightlist[:j+1] + resultlist
return resultlist
Solution().InversePairs([7,5,6,4])
原超时代码
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#超时了,可能是list.insert(idnex,num)要比list.append()复杂度高
class Solution:
def __init__(self):
self.count = 0
def InversePairs(self, data):
if len(data)<=0:
return 0
self.mergeSort(data)
return self.count % 1000000007
def mergeSort(self,data):
if len(data) < 2:
return data
midIndex = len(data)>>1
leftlist = self.mergeSort(data[:midIndex])
rightlist = self.mergeSort(data[midIndex:])
return self.merge(leftlist,rightlist)
def merge(self,leftlist,rightlist):
resultlist = [] #辅助数组记录排序后的结果
i,j =len(leftlist)-1,len(rightlist)-1
#注意这里ij初始化为最后一个元素,因为是从后往前比较,便于计算count
while i>=0 and j >= 0:
if leftlist[i] > rightlist[j]:
self.count += (j+1) #就是比归并排序多了这一步
resultlist.insert(0,leftlist[i]) #头插法
i -= 1
else:
resultlist.insert(0,rightlist[j])
j -= 1
#接上剩余的元素
resultlist = leftlist[:i+1] + resultlist
resultlist = rightlist[:j+1] + resultlist
return resultlist
Solution().InversePairs([1,2,3,4,5,6,7,0])