leetcode51. 數組中的逆序對

問題描述
  在數組中的兩個數字,如果前面一個數字大於後面的數字,則這兩個數字組成一個逆序對。輸入一個數組,求出這個數組中的逆序對的總數。
在這裏插入圖片描述
思路
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了,白白瞎搗鼓半小時,菜是原罪呀。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章