問題描述
在數組中的兩個數字,如果前面一個數字大於後面的數字,則這兩個數字組成一個逆序對。輸入一個數組,求出這個數組中的逆序對的總數。
思路
1.暴力遍歷:對每一個數和後面的數進行單獨比較,符合條件+1,然後變量res記錄個數,思路簡單,代碼如下
#雙重循環版本
class Solution:
def reversePairs(self, nums: List[int]) -> int:
res = 0
for i in range(len(nums)-1):
for j in range(i,len(nums)):
if nums[i]>nums[j]:
res+=1
return res
#遞歸版本
class Solution:
def reversePairs(self, nums: List[int]) -> int:
if len(nums) < 2:
return 0
current = nums[0]
res = 0
for i in nums:
if current>i:
res+=1
return self.reversePairs(nums[1:])+res
兩種版本,時間複雜度爲O(n^2),遞歸還可能超過最大遞歸層數,能用循環解決就不要遞歸了吧,雖然遞歸容易理解,run完後結果:
超出時間限制,其實也是意料之中,此題難度爲困難,豈不是吾等渣渣能兩分鐘解決的。
2.歸併排序:正常人確實挺難想到的,我是看了提示,往歸併這方面想了,後面的邏輯確實是自己想的,歸併的過程如下圖所示:
歸併的過程,不斷從兩個排序好的數組裏,取數,用雙指針法,如果左邊大於右邊,怎取右邊值,知道兩個數組指針都到最後了,得到一個新的排序好的數組,這個過程怎麼計算出逆序的個數呢。
如7和5的歸併過程,每次從右邊數組取數的時候,我們需要記錄左邊數組還有多少個數,這些數都可以和右邊取出來的數組成逆序對,因爲這些數肯定都比它大。
再如[5,7] ,[4,6]的歸併,通過4,5比較得知先取出4,左邊數組還有2個值,即[5,4],[5,7]都可以組成逆序對,然後5,6比較,取出5,不作操作;再比較6,7,取出6,因爲從右邊取值所以看左邊數組大小爲1,即[7,6]組成逆序對;
然後加上上面的[7,5]和[6,4]一共是五隊逆序對,答案正確。思路有了,那代碼如下,如果不熟悉歸併排序過程的可能有點難理解吧,建議熟悉了歸併排序後再看。
class Solution:
def __init__(self):
self.res = 0
def reversePairs(self, nums):
if nums:
self.sort(nums)
return self.res
def sort(self, nums):
l = 0
r = len(nums)-1
if l >= r:
return nums
else:
mid = (l+r)//2
left_nums = self.sort(nums[l:mid+1])
# print(left_nums)
right_nums = self.sort(nums[mid + 1:])
#print(right_nums)
res, res_num = self.merge(left_nums, right_nums)
print(res)
self.res += res
return res_num
def merge(self, l_nums, r_nums):
num = 0
new_nums = []
l_val = l_nums.pop(0)
r_val = r_nums.pop(0)
while True:
if l_val > r_val:
new_nums.append(r_val)
num += len(l_nums) + 1
if r_nums:
r_val = r_nums.pop(0)
else:
new_nums.append(l_val)
break
else:
new_nums.append(l_val)
if l_nums:
l_val = l_nums.pop(0)
else:
new_nums.append(r_val)
break
if l_nums:
new_nums.extend(l_nums)
if r_nums:
new_nums.extend(r_nums)
return num, new_nums
代碼思路 主要框架用的是歸併排序的寫法,因爲在後面需要統計逆序對的個數,所以無法採用原地歸併,自己寫一寫就知道了,所以本文的歸併排序是一個非本地的排序。
首先是用二分法實現排序的主體算法,主要的優化是在merge的過程,歸併的過程,其他思路和歸併一模一樣,先全部拆分爲長度爲1的數組,然後進行歸併,怎麼統計逆序對的思路上面講了,這個merge方法邏輯其實寫的比較混亂,if-else用的有點多,建議感興趣的可以重新寫,這裏的merge每次返回了一個num和res_num,num代表當前合併組成的逆序對個數,res_num代表返回歸併好的有序數組,將每次返回的num加入到self.res變量,得到總的逆序對個數,其實是實例變量作爲一個全局變量來使用,最後返回self.res即可。
另外大家一定要注意對邊界值判斷的問題,第一次在本地調通後,測試第十個案例的時候是個空數組,然後報了遞歸最大次數的錯誤,沒有測試案例,還以爲是python的最大遞歸次數只能999次造成的,然後設置了最大遞歸次數爲50000,因爲數組最長才50000加了這句:
import sys
sys.setrecursionlimit(1000000)
然而,並沒什麼用,依據第十個報錯,看了看官方提供的python程序,一運行發現可以,明天這應該不是遞歸的問題,我的思路沒什麼問題,看了看測試案例爲[],在前面加了個對[]的判斷就ok了,白白瞎搗鼓半小時,菜是原罪呀。