python经典逆序对问题_剑指offer-51数组中的逆序对-python

《剑指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。

Snipaste_2020-03-22_17-03-21.png

先把它分解成两个长度为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])

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值