說實話這題我搞超久的,主要就是卡在時間複雜度的問題,因為題目有規定時間複雜度要壓縮到O(log(m+n))一般的合併方法時間複雜度是 O(m+n),但這需要用二分查找把運算量控制在對數等級,害我還要回去再熟悉時間複雜度的計算原理,難怪題目等級是困難,另一個點就是要小心控制變量 i 和 j,同時要正確處理分割點的選擇,還要考慮各種特殊情況,例如長度是 0 的陣列或極端的邊界情況但控制索引的問題還是沒時間複雜度卡的久。
理解題目:
題目要我從兩個有排序的陣列裡找中位數,然後要求時間複雜度是 O(log(m+n)),所以要用二分法找我要的,因為要快速地找中位數。
關鍵:
已排序陣列,給兩個已經排序的陣列,找到兩個陣列合併後的中位數,不用真的合併兩個陣列,是透過計算來得到中位數。
時間複雜度O(log(m+n)),暗示要用 二分查找 來縮短搜尋範圍不是線性合併。
思路:
不考慮時間複雜度的問題,最直觀的解法就是把兩個已經排序的陣列合併成一個新的排序陣列,然後直接找中位數,但這種解法的時間複雜度是 O(m+n),因為合併過程要遍歷兩個陣列的所有元素,可是這裡的要求是 O(log(m+n)),所以需要想辦法更快地找到中位數。
透過 二分查找 的方式,在兩個陣列上同時切割來找中位數,可在其中一個較小的陣列上進行二分查找,這樣就可以更有效地減少計算量。
主要就是把兩個陣列分成兩部分,確認左邊的比右邊的小,這樣就能保證找到正確的中位數位置。
步驟:
先確定左右部分,把兩個陣列分成「左半部分」和「右半部分」,想辦法讓左邊的部分包含 (m+n)//2 個元素,右邊的部分包含其他剩下的元素。
目地是確認左邊部分的最大值小於右邊部分的最小值,這樣就可以找到中位數,再來就是怎麼切割; 可以在nums1 上用二分查找,如果試著切割 nums1 的某個位置,那剩下的數量就要在 nums2 上補回來。
設變數 i 來表示在 nums1 中切割的位置,在 nums2 裏的位置就是 j = (m + n) // 2 - i。
每次調整 i 值來確認左邊的最大值小於右邊的最小值
用二分查找來調整 i 的位置,如果 nums1[i] 太大,這樣就要減少 i,如果 nums2[j] 太大,那就要增加 i,一直調整到 i 找到適合的位置,一找到了對的切割位置,再根據數組長是不是奇數或偶數來算中位數,如果總長為奇數,中位數就是左邊部分的最大值,如果總長是偶數,中位數是左邊的最大值和右邊的最小值平均值,再來就是判斷小的數組,在短的數組上二分查找,這樣效率比較高。反正二分查找主要就是用左右部分的大小關係來找到中位數的位置,確定左半部分的最大值比右半部分的最小值小,停止條件就是找到讓左半部分最大值小於右半部分最小值的切割點。
核心技巧是用二分查找縮小搜尋範圍,以此來達到 O(log(m+n)) 的時間複雜度,可以把問題分解成在較小陣列上操作,這樣就能有效解決合併後找中位數的問題。
程式碼:
class Solution(object):
def findMedianSortedArrays(self, nums1, nums2):
if len(nums1) > len(nums2):
nums1, nums2 = nums2, nums1 # 確保 nums1 是較短的數組
m, n = len(nums1), len(nums2)
left, right = 0, m
half_len = (m + n + 1) // 2
while left <= right:
i = (left + right) // 2
j = half_len - i
if i > 0 and nums1[i - 1] > nums2[j]:
right = i - 1 # i 太大了,減小 i
elif i < m and nums2[j - 1] > nums1[i]:
left = i + 1 # i 太小了,增大 i
else:
# 找到了合適的 i
max_of_left = 0
if i == 0:
max_of_left = nums2[j - 1]
elif j == 0:
max_of_left = nums1[i - 1]
else:
max_of_left = max(nums1[i - 1], nums2[j - 1])
if (m + n) % 2 == 1:
return max_of_left # 總長度是奇數,直接返回左半部分最大值
min_of_right = 0
if i == m:
min_of_right = nums2[j]
elif j == n:
min_of_right = nums1[i]
else:
min_of_right = min(nums1[i], nums2[j])
return (max_of_left + min_of_right) / 2.0 # 總長度是偶數,返回兩邊的平均值