在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,
那么它们就称为一个逆序。一个排列中逆序的总数就称为这个排列的逆序数。
比如:74386,逆序为:74,73,76,43,86,所以逆序数为:5
1.直接计数法虽然简单直观,但是其时间复杂度是 O(n^2),如果数据量很大,程序会崩溃
2.一个更快(但稍复杂)的计算方法是在归并排序的同时计算逆序数,该算法耗时最小。
思路:假设需要求74386的逆序数,利用分治的思想,先左半边743的逆序数a,然后再求86的
逆序数b,最后再求左右各取一个数所构成的逆序数c,最后总的逆序数为:a+b+c
这里提醒一下,其实求743的逆序数a的过程,其实也经历了前面3个步骤,即先求左,再求右,
最后再左右各取1个数,最后的求和整个过程。
这样就可以利用递归的思想来解决这样的问题,边排序,边统计逆序数。
着重解释一下根据左右各取一个元素求逆序数的思路。864 | 73
左右都是从大到小排序好了,如果8,变量i >7,变量j,那么7之后的数都满足逆序数,
接下来i+1移动到6,6<7,不满足逆序数,j+1移动到3,6>3,那么3之后的数都满足逆序数,
通过这样的循环遍历从而求到逆序数的值。
Python代码实现:
1 import random
2 import time
3
4
5 def mergeSortAndCount(l,start,end):
6 if (start>=end):
7 return 0
8 mid = (start+end)//2
9 # 左边从大到小的排序,排序的同时,计算逆序数的值
10 leftCount = mergeSortAndCount(l,start,mid)
11 # 右边从大到小的排序,排序的同时,计算逆序数的值
12 rightCount = mergeSortAndCount(l,mid+1,end)
13 # i左边列表的游标,j游标列表的游标,count逆序数的计数变量
14 i,j,count = start,mid+1,0
15
16 while i <= mid and j <= end :
17 if l[i] > l[j]:
18 count += end - j + 1
19 i += 1
20 else:
21 # l[i],l[j] = l[j],l[i]
22 j += 1
23 # 先用tempList存储从大到小排序,最后在根据位置替换原list
24 i, j = start, mid + 1
25 tempList = []
26 while i <= mid and j <= end:
27 if l[i] > l[j]:
28 tempList.append(l[i])
29 i += 1
30 else:
31 # l[i],l[j] = l[j],l[i]
32 tempList.append(l[j])
33 j += 1
34 #如果还剩余的元素,则把它补充进tempList列表
35 if i <= mid:
36 tempList.extend(l[i:mid+1])
37 if j <= end:
38 tempList.extend(l[j:end+1])
39 l[start:end+1] = tempList
40
41 return leftCount + rightCount + count
42
43 def main():
44 numstr = input("请输入一段数值:")
45 numList = []
46 for i in numstr:
47 numList.append(int(i))
48 #n = 1000
49 #numList = list(range(1,n))
50 #random.shuffle(numList)
51 #print(numList)
52 begin_time = time.perf_counter()
53 rtn = mergeSortAndCount(numList,0,len(numList)-1)
54 end_time = time.perf_counter()
55 print("逆序数个数:%d"%rtn)
56 print("共耗时:%f"%(end_time-begin_time))
57 print(numList)
58
59 if __name__ == "__main__":
60 main()