題目:
給定兩個大小為 m 和 n 的正序(從小到大)數組 nums1 和 nums2。
請你找出這兩個正序數組的中位數,並且要求算法的時間復雜度為 O(log(m + n))。
你可以假設 nums1 和 nums2 不會同時為空。
示例 1:
nums1 = [1, 3]
nums2 = [2]
則中位數是 2.0
示例 2:
nums1 = [1, 2]
nums2 = [3, 4]
則中位數是 (2 + 3)/2 = 2.5
思路:
思路:
看示例得知,中位數的概念是奇數情況:為中間位;偶數情況為中間2位之和平均數
python 中的% 和/ 的意思
%取余數
/取整數,
本題的難點是限制時間復雜度為 O(log(m + n))
方法一:
一般解法,先不考慮時間復雜度要求,如果除法運算想保留小數點,被除數保留小數點即可,這樣就會有完整結果。
class Solution(object):
def findMedianSortedArrays(self, nums1, nums2):
"""
:type nums1: List[int]
:type nums2: List[int]
:rtype: float
"""
tmp = []
result = 0
for i in nums1:
tmp.append(i)
for n in nums2:
tmp.append(n)
tmp.sort() #排序
p = len(tmp)%2
if p ==0: #偶數
result = (tmp[len(tmp)/2-1]+tmp[len(tmp)/2])*/2.0
else: #奇數
result = tmp[(len(tmp)+1)/2-1]
return result
執行用時 :48 ms, 在所有 Python 提交中擊敗了76.69%的用戶
內存消耗 :12.8 MB, 在所有 Python 提交中擊敗了11.11%的用戶
或者是選用合並再sort,代碼更簡潔些。
class Solution(object):
def findMedianSortedArrays(self, nums1, nums2):
"""
:type nums1: List[int]
:type nums2: List[int]
:rtype: float
"""
nums = nums1 + nums2
nums.sort()
count = len(nums)
if count % 2 == 0:
return (nums[int(count / 2 - 1)] + nums[int(count / 2)]) / 2.0
else:
return nums[int(count / 2)]
方法二:
想不出來關於時間復雜度的方法實現,這里摘自一個題解大大的思路(作者:louis-38),我覺得很棒,從另一個角度分析了二分法
在統計中,中位數被用來:
將一個集合划分為兩個長度相等的子集,其中一個子集中的元素總是大於或等於另一個子集中的元素。
和中位數無關的元素
假設一個集合A被中位數划分成兩個長度相等的子集A1和A2,A1中的元素總是小於或等於A2中的元素。
如上圖所示的集合A,其中位數是4.5。
當我們同時從A1和A2中移除任意n個元素(不移除A1最大元素和A2最小元素),集合A的中位數是不變的。
即我們移除A1中的1,2,3這三個元素中的任意n個,同時移除A2中的6,7,8這三個元素的任意n個,集合A的中位數還是4.5。
所以我稱1,2,3,6,7,8這幾個元素是和中位數無關的元素。即對稱地移除他們時,對集合的中位數沒有影響。
元素個數為奇數的情況稍有不同,但是也是類似的。
中位數的位置
回到這個題目上。
基於上述的原理,我們是否可以通過排除和中位數無關的元素,最終得到中位數?
此解法的重點是:要知道中位數的位置。(這不是扯淡嗎,我知道中位數的位置直接返回不就完了。- -!)
我們這里不是求中位數的確切位置,而是求中位數的大概位置。
假設題目給出的兩個有序集合為B1和B2,他們的長度為m和n。
設 midM = (m-1)/2(暫不考慮m=0的邊界情況)
設 midN = (n-1)/2
如果n%2=0:
設midNP = midN + 1
否則midNP = midN
那么這兩個集合的並集的中位數的值median,必定是:2<=median<=7。否則median無法將B1和B2的並集切分為等長的兩部分。(證明過程就不給出了,數學比較差,但是代碼運行結果證明這個推論是正確的。)
所以,根據上述推論可以得出,1,8是和中位數無關的元素。
消除元素1和8從而得到更短的兩個集合,如下圖。其並集中位數不變,還是4.5。
接下來重復以上的步驟。由於每次排除元素的數量是較短集合的一半元素,所以經過log(min(m,n))次迭代之后,到達邊界條件。
過程如動圖:
該算法的時間復雜度為O(log(min(m,n)))。
邊界條件處理:
最終到達邊界的情況下,nums1的長度可能是1或者2。對於這兩種情況,我們只需拿nums1剩下的數和nums2中間幾位數進行排序,就能得到中位數。
由於進行排序的數組長度最長是6,故其時間復雜度為O(1)。
class Solution(object):
def findMedianSortedArrays(self, nums1, nums2):
"""
:type nums1: List[int]
:type nums2: List[int]
:rtype: float
"""
m = len(nums1)
n = len(nums2)
if n < m:
temp = nums1
nums1 = nums2
nums2 = temp
m = len(nums1)
n = len(nums2)
mid_m = (m - 1) / 2
mid_n = (n - 1) / 2
if m == 0:
return nums2[mid_n] if (n % 2) == 1 else (nums2[mid_n] + nums2[mid_n + 1]) / 2.0
if m == 1 or m == 2:
if n < 3:
nums1.extend(nums2)
elif n % 2 == 1:
nums1.extend(nums2[mid_n - 1:mid_n + 2])
else:
nums1.extend(nums2[mid_n - 1:mid_n + 3])
nums1.sort()
m = len(nums1)
mid_m = (m - 1) / 2
return nums1[mid_m] if m % 2 == 1 else (nums1[mid_m] + nums1[mid_m + 1]) / 2.0
mid_np = mid_n if n % 2 == 1 else mid_n + 1
if nums1[mid_m] == nums2[mid_np]:
return nums1[mid_m]
if nums1[mid_m] < nums2[mid_np]:
return self.findMedianSortedArrays(nums1[mid_m:],nums2[:n-mid_m])
return self.findMedianSortedArrays(nums1[:m-mid_m], nums2[mid_m:])
下面是依據官方題解簡化后的代碼,比較簡潔,但是理解的話需要參考下官方的思路
class Solution(object):
def findMedianSortedArrays(self, nums1, nums2):
"""
:type nums1: List[int]
:type nums2: List[int]
:rtype: float
"""
def getkth(k, l1, l2):
if l1 == m: return nums2[l2 + k - 1]
if l2 == n: return nums1[l1 + k - 1]
if k == 1: return min(nums1[l1], nums2[l2])
# 每次比較從開始起第 k/2-1 個數,並去掉較小的數組的前面部分
new_l1, new_l2 = min(l1 + k / 2, m), min(l2 + k / 2, n)
if nums1[new_l1 - 1] < nums2[new_l2 - 1]:
return getkth(k - new_l1 + l1, new_l1, l2)
else:
return getkth(k - new_l2 + l2, l1, new_l2)
m, n, s = len(nums1), len(nums2), len(nums1) + len(nums2)
if s % 2:
return getkth((s + 1) / 2, 0, 0)
return (getkth(s / 2, 0, 0) + getkth(s / 2 + 1, 0, 0)) / 2.0