劍指offer系列-面試題51. 數組中的逆序對(python)

1. 題目

在數組中的兩個數字,如果前面一個數字大於後面的數字,則這兩個數字組成一個逆序對。輸入一個數組,求出這個數組中的逆序對的總數

2. 解題思路

詳情見 暴力解法、分治思想、樹狀數組

2.1 暴力法

這個沒什麼好說的

2.3 動態規劃

f(n) = f(n-1) + m,前面的數組中比當前元素大的元素的個數。

2.3 分治思想(藉助歸併排序統計逆序數)

說明:理解這個算法需要對「歸併排序」比較熟悉。掌握如果編寫遞歸函數,每一次都一分爲二拆分數組的子區間,然後在方法棧彈出的時候,一步一步合併兩個有序數組,最後完成排序工作。

3. 代碼實現

3.1 暴力法

O(n2),空間複雜度S(n),超時。

class Solution:
    def reversePairs(self, nums: List[int]) -> int:
        size = len(nums)
        if size < 2:
            return 0
        res = 0
        for i in range(0, size - 1):
            for j in range(i + 1, size):
                if nums[i] > nums[j]:
                    res += 1
        return res

3.2 動態規劃

O(n2),空間複雜度S(n),超時。

class Solution:
    def reversePairs(self, nums: List[int]) -> int:
        """
        1. 動態規劃
            狀態轉移方程爲:
                f(i) = f(i-1) + m;m表示前面的數組中比nums[i]大的數字的個數
        """
        def count_large_number(arr, elem):
            count = 0
            for val in arr:
            	if val > elem:
            		count += 1
            return count
        
        if not nums: return 0
        dp, i = [0 for i in nums], 1

        while i < len(nums):
            m = count_large_number(nums, nums[i])
            dp[i] = dp[i-1] + m
            i += 1

        return dp[-1]

3.3 分治思想(藉助歸併排序統計逆序數)

class Solution:

    def reversePairs(self, nums: List[int]) -> int:
        size = len(nums)
        if size < 2:
            return 0

        # 用於歸併的輔助數組
        temp = [0 for _ in range(size)]
        return self.count_reverse_pairs(nums, 0, size - 1, temp)

    def count_reverse_pairs(self, nums, left, right, temp):
        # 在數組 nums 的區間 [left, right] 統計逆序對
        if left == right:
            return 0
        mid = (left + right) >> 1
        left_pairs = self.count_reverse_pairs(nums, left, mid, temp)
        right_pairs = self.count_reverse_pairs(nums, mid + 1, right, temp)

        reverse_pairs = left_pairs + right_pairs
        # 代碼走到這裏的時候,[left, mid] 和 [mid + 1, right] 已經完成了排序並且計算好逆序對

        if nums[mid] <= nums[mid + 1]:
            # 此時不用計算橫跨兩個區間的逆序對,直接返回 reverse_pairs
            return reverse_pairs

        reverse_cross_pairs = self.merge_and_count(nums, left, mid, right, temp)
        return reverse_pairs + reverse_cross_pairs

    def merge_and_count(self, nums, left, mid, right, temp):
        """
        [left, mid] 有序,[mid + 1, right] 有序

        前:[2, 3, 5, 8],後:[4, 6, 7, 12]
        只在後面數組元素出列的時候,數一數前面這個數組還剩下多少個數字,
        由於"前"數組和"後"數組都有序,
        此時"前"數組剩下的元素個數 mid - i + 1 就是與"後"數組元素出列的這個元素構成的逆序對個數

        """
        for i in range(left, right + 1):
            temp[i] = nums[i]

        i = left
        j = mid + 1
        res = 0
        for k in range(left, right + 1):
            if i > mid:
                nums[k] = temp[j]
                j += 1
            elif j > right:
                nums[k] = temp[i]
                i += 1
            elif temp[i] <= temp[j]:
                # 此時前數組元素出列,不統計逆序對
                nums[k] = temp[i]
                i += 1
            else:
                # assert temp[i] > temp[j]
                # 此時後數組元素出列,統計逆序對,快就快在這裏,一次可以統計出一個區間的個數的逆序對
                nums[k] = temp[j]
                j += 1
                # 例:[7, 8, 9][4, 6, 9],4 與 7 以及 7 後面所有的數都構成逆序對
                res += (mid - i + 1)
        return res

作者:liweiwei1419
鏈接:https://leetcode-cn.com/problems/shu-zu-zhong-de-ni-xu-dui-lcof/solution/bao-li-jie-fa-fen-zhi-si-xiang-shu-zhuang-shu-zu-b/
來源:力扣(LeetCode)
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。

4. 總結

這道題我不會做。

5. 參考文獻

[1] 劍指offer叢書
[2] 劍指Offer——名企面試官精講典型編程題
[3] 暴力解法、分治思想、樹狀數組

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