[Leetcode解題] 4. Median of Two Sorted Arrays - 使用分治法(Divide-and-conquer)算法
7 July 2023
hard
divide-and-conquer
apple
amazon
題目
4. Median of Two Sorted Arrays 兩個已排序陣列的中位數
給定兩個大小分別為 $m$ 和 $n$ 的已排序陣列 $nums1$ 和 $nums2$,找出這兩個陣列的中位數。
整體運行時間複雜度應為 $O(log(m+n))$。
解題思路
這個問題可以轉換為在兩個已排序陣列中尋找第 $K$ 個元素,其中 $K$ 為兩個陣列元素總數的中位數位置。
為了達到 $O(log(m+n))$ 的時間複雜度,我們可以使用一種分治法(Divide-and-conquer)的思路。
- 首先,我們找到兩個陣列的中間元素($K/2$),分別為 $mid1$ 和 $mid2$。
- 比較 $mid1$ 和 $mid2$ 的值:
- 如果 $mid1$ 小於 $mid2$,那麼 $nums1$ 中的前半部分不可能包含第 $K$ 大的元素。所以,我們可以將 $nums1$ 的前半部分丟棄,並更新 $K$ 的值。
- 如果 $mid1$ 大於等於 $mid2$,那麼 $nums2$ 中的前半部分不可能包含第 $K$ 大的元素。所以,我們可以將 $nums2$ 的前半部分丟棄,並更新 $K$ 的值。
- 重複上述步驟,直到 $K$ 的值等於 $1$。
- 當 $K$ 的值等於 $1$ 時,我們找到了第 $K$ 大的元素,即為 $min(nums1[mid1], nums2[mid2])$。
範例
在這個例子中,我們可以根據上述步驟找到第 5 大的元素:
nums1 = [1, 3, 5, 7, 9]
nums2 = [2, 4, 6, 8, 10]
- $mid1$ 為 $nums1[k/2] = 5$,$mid2$ 為 $nums2[k/2] = 6$。
- $mid1$ 小於 $mid2$,所以我們丟棄 $nums1$ 的前半部分,更新 $K$ 的值為 $5-2=3$。
- 再次進行比較,$mid1$ 為 $nums1[k/2] = 7$,$mid2$ 為 $nums2[k/2] = 6$。
- $mid1$ 大於等於 $mid2$,所以我們丟棄 $nums2$ 的前半部分,更新 $K$ 的值為 3-2=1。
- 當 $K$ 的值等於 $1$ 時,我們找到了第 $K$ 大的元素,即為 $5$。 所以,在這個例子中,兩個已排序陣列中的第 $5$ 大元素為 $5$。
算法
- 定義一個輔助函數
findKthInSortedArrays(nums1, nums2, s1, s2, k)。 - 在
findKthInSortedArrays中,處理特殊情況:如果 nums1 中沒有剩餘元素,則返回nums2[s2+k];如果 nums2 中沒有剩餘元素,則返回nums1[s1+k]。 - 如果
k=0,則返回 $min(nums1[s1], nums2[s2])$。 - 計算 $mid = (k-1)/2$。
- 防止 $mid1$ 和 $mid2$ 超出陣列界限,設置 $mid1 = min(s1+mid, nums1.size()-1)$ 和 $mid2 = min(s2+mid, nums2.size()-1)$。
- 如果 $nums1[mid1] > nums2[mid2]$,則遞歸調用
findKthInSortedArrays(nums1, nums2, s1, mid2+1, k-(mid2-s2+1))。 - 如果 $nums1[mid1] <= nums2[mid2]$,則遞歸調用
findKthInSortedArrays(nums1, nums2, mid1+1, s2, k-(mid1-s1+1))。 - 在主函數
findMedianSortedArrays(nums1, nums2)中,計算mid_left = findKthInSortedArrays(nums1, nums2, 0, 0, (nums1.size()+nums2.size()-1)/2)和mid_right = findKthInSortedArrays(nums1, nums2, 0, 0, (nums1.size()+nums2.size())/2)。 - 返回 (mid_left+mid_right)/2 作為中位數。
實作
class Solution:
def findKthInSortedArrays(self, nums1, nums2, s1, s2, k):
if len(nums1) <= s1:
return nums2[s2 + k]
if len(nums2) <= s2:
return nums1[s1 + k]
if k == 0:
return min(nums1[s1], nums2[s2])
mid = (k - 1) // 2
mid1 = min(s1 + mid, len(nums1) - 1)
mid2 = min(s2 + mid, len(nums2) - 1)
if nums1[mid1] > nums2[mid2]:
return self.findKthInSortedArrays(nums1, nums2, s1, mid2 + 1, k - (mid2 - s2 + 1))
else:
return self.findKthInSortedArrays(nums1, nums2, mid1 + 1, s2, k - (mid1 - s1 + 1))
def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
mid_left = self.findKthInSortedArrays(nums1, nums2, 0, 0, (len(nums1) + len(nums2) - 1) // 2)
mid_right = self.findKthInSortedArrays(nums1, nums2, 0, 0, (len(nums1) + len(nums2)) // 2)
return (mid_left + mid_right) / 2
複雜度
- 時間複雜度:由於使用了分治法,每次遞歸將問題規模縮小為原來的一半,因此運行時間複雜度為 $O(log(m+n))$。
- 空間複雜度:遞歸過程中需要使用額外的堆疊空間,空間複雜度為 $O(log(m+n))$。