分治-求逆序数
在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,
那么它们就称为一个逆序。一个排列中逆序的总数就称为这个排列的逆序数。
比如: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()