文章目錄
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] 暴力解法、分治思想、樹狀數組