LeetCode | 4. Median of Two Sorted Arrays
題目連結:https://leetcode.com/problems/median-of-two-sorted-arrays/
題目難度:Hard
題目描述:
There are two sorted arraysnums1 andnums2 of size m and n respectively.
Find the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)).
You may assumenums1 andnums2 cannot be both empty.
Example 1:
nums1 = [1, 3]nums2 = [2] The median is 2.0
Example 2:
nums1 = [1, 2]nums2 = [3, 4] The median is (2 + 3)/2 = 2.5
相關主題:Array ,Binary Search ,Divide and Conquer
思路
假設數列的元素數目為
,若
是奇數,則中位數是第
個數;若
是偶數,則中位數是第
個數和第
個數的平均數。一種思路是直接從開頭搜尋兩個陣列,並記錄搜尋的元素數目。但這樣做的時間複雜度為
,不滿足題目
的要求。
自己想了一個
的思路,類似於“尋找第 K 大的數”,只不過尋找的範圍是兩個排序好的陣列。假設兩個陣列為
A
和B
,元素數目分別為m
和n
,就可以每次取A[m/2]
和B[n/2]
進行比較,然後每次遞迴縮小搜尋的範圍。這樣做好像有些複雜,沒有考慮清楚該怎麼寫這個遞迴。
參考了一下官方的解決方案,沒看懂。之後我找到了 Frank Dai 的《LeetCode 題解》 (題目2.1.5),發現他的思路跟我的有些像。
經過對比,我發現他們的思路和我的思路最大的區別就在於取的索引的不同。在每次迭代時,我的思路是取A
、B
的兩個中間位置進行判斷,即i = A.size()/2
、j = B.size()/2
,這樣在考慮第 K 大的數究竟落到哪個範圍時就更麻煩。而且我在考慮的時候,還嘗試一次把第 K 大和第 K+1 大兩個元素都找出來,又把問題變複雜了一點。而參考的兩個思路在考慮時,i
和j
都滿足由 K 來約束的關係(例如i + j = K
),這樣在確定第 K 大的數落在哪個範圍時會更簡單一些。結合他們的方法,下面重新整理一下思路。
給定兩個有序陣列A
、B
,元素個數分別為m
和n
,尋找它們的並集中第k
大的數。我們在A
中找到前i = k/2
大的數,在B
中找到前j = k - i
大的數,這樣就可以確保索引位置是跟k
聯絡在一起的。始終保證兩個陣列中B
的元素較多,考慮到A
中元素的數目有可能小於k/2
,所以取i = min(m, k/2)
。起始的搜尋範圍是A[0...end]
和B[0..end]
。一般會有下面有三種情況:
-
A[i-1] < B[j-1]
:說明第k
大的數一定不在A[0...i-1]
這個範圍內,將搜尋範圍縮小至A[i...end]
和B[0...end]
,尋找第k-i
大的數; -
A[i-1] > B[j-1]
:說明第k
大的數一定不在B[0...j-1]
這個範圍內,將搜尋範圍縮小至A[0...end]
和B[j...end]
,尋找第k-j
大的數; -
A[i-1] == B[j-1]
:說明已經找到了第k
大的數,返回A[i-1]
或者B[j-1]
。
考慮遞迴函式的退出條件:
-
如果
m == 0
,說明陣列A
的搜尋範圍是 0,則返回B[k-1]
; -
如果
k == 1
,這時i = k/2
的值為0,無法索引A[i-1]
,返回min(A[0], B[0])
; -
如果
A[i-1] == B[j-1]
,返回A[i-1]
或者B[j-1]
。
下面是一個具體的例子:
-
初始情況:
A = {1, 3, 5, 7}
,B = {2, 4, 6, 8, 10}
,m = 4
,n = 5
, 因為總元素數目為奇數,設定初始k = (m+n)/2 = 5
。取i = k/2 = 2
,j = k - i = 3
,這會將陣列A
、B
各分為紅、藍兩個部分:
此時,
A[i-1]
的值為 3,B[j-1]
的值為 6,A[i-1] < B[j-1]
,此時第k
大的數一定不在A
的紅色部分中,將這部分從搜尋範圍內剔除掉,並更新k = k - i = 3
:
-
更新後
A = {5, 7}
,B = {2, 4, 6, 8, 10}
,m = 2
,n = 5
,k = 3
。取i = k/2 = 1
,j = k - i = 2
,再次將陣列A
、B
各分為紅、藍兩個部分:
此時,
A[i-1]
的值為 5,B[j-1]
的值為 4,A[i-1] > B[j-1]
,此時第k
大的數一定不在B
的紅色部分中,將這部分從搜尋範圍內剔除掉,並更新k = k - j = 1
:
-
更新後
A = {5, 7}
,B = {6, 8, 10}
,m = 1
,n = 3
,k = 1
。由於k == 1
,返回min(A[0], B[0]) = min(5, 6) = 5
,最終得到中位數為 5。
時間複雜度:
空間複雜度:
// C++ int find_the_kth_largest_num(vector<int>::const_iterator A, int m, vector<int>::const_iterator B, int n, int k) { if (m > n) return find_the_kth_largest_num(B, n, A, m, k); if (m == 0) return *(B + k - 1); if (k == 1) return min(*A, *B); int i = min(m, k/2), j = k - i; if (*(A + i - 1) < *(B + j - 1)) { return find_the_kth_largest_num(A + i, m - i, B, n, k - i); } else if (*(A + i - 1) > *(B + j - 1)) { return find_the_kth_largest_num(A, m, B + j, n - j, k - j); } else { return *(A + i - 1); } } class Solution { public: double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) { int m = nums1.size(), n = nums2.size(); if ((m + n) % 2 != 0) { return find_the_kth_largest_num(nums1.begin(), m, nums2.begin(), n, (m + n) / 2 + 1); } else { double a = find_the_kth_largest_num(nums1.begin(), m, nums2.begin(), n, (m + n) / 2); double b = find_the_kth_largest_num(nums1.begin(), m, nums2.begin(), n, (m + n) / 2 + 1); return (a + b) / 2.; } } };
2019年03月31日