題目:
給定兩個大小為 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
來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/median-of-two-sorted-arrays
著作權歸領扣網絡所有。商業轉載請聯系官方授權,非商業轉載請注明出處。
首先明確幾個點:
1、兩個數組都是有序的
如
[1,3,5]
[2,4,6]
合並后中位數為3、4,分別分布在數組1和數組2中,那么以3、4為分隔點,在數量上:數組1的左邊元素個數 + 數組2的左邊 = 數組1的右邊 + 數組2右邊元素個數
再如:
[1,3,5]
[0,2,4,6]
合並后中位數為3,只在數組1中,那我們將3放入數組2中,[0,2,3,4,6]
剛才的推論依然成立
中位數左邊元素的數量等於中位數右邊元素的數量
2、 數組1中,中位數左邊最大的元素一定小於中位數右邊最小的元素,當數組2中也有中位數(如沒有則將中位數放入數組2中),數組2中位數右邊最小的元素一定大於數組1中位數左邊的元素,數組2中位數左邊的元素一定小於數組1中位數右邊的元素
即:
例1:6>1, 2<5
例2:4>1,2<5
2、一個數組有n個元素,則有n+1個插槽
3、一見log(m+n)便知與二分查找有關
代碼
class Solution {
/**
* @param Integer[] $nums1
* @param Integer[] $nums2
* @return Float
*/
function findMedianSortedArrays($nums1, $nums2) {
$n1 = count($nums1);
$n2 = count($nums2);
if($n1>$n2){
return $this -> findMedianSortedArrays($nums2, $nums1); // 數組1的長度永遠不會比數組2更長
}
$k = intval(($n1+$n2+1)/2); // 總槽點數量二分之一
$left = 0;
$right = $n1;
while($left < $right){
$m1 = intval($left + ($right-$left)/2); // 此處實現二分查找
// 第一次查找時:當數組1是奇數個,我們認為m1是中位數,當數組1是偶數個,認為m1是中位數中較大的那個。
$m2 = $k - $m1; // 兩個數組中位數的左邊元素數量相加等於總槽點數量的二分之一
if($nums1[$m1] < $nums2[$m2-1]){ // m1為數組1的的中位數或者較大的中位數 小於 數組2中位數左邊的最大值m2-1
$left = $m1 + 1; // 此時說明數組1可以繼續向右查找,進入下一次二分查找
}else{
$right = $m1; // 否則已經找到中位數的位置,退出循環
}
}
// 上面while循環的核心思想是:
// 因為規范了 數組1的長度永遠不會比數組2更長, 所以當數組1長度為0時,數組2的中位數就是要求的中位數
// 當數組1的長度為3時,數組2長度遠大於3,那么數組1對於所求的中位數影響有限。即最后得到的中位數如果在數組2中,則一定在第一次求得的中位數附近不超過3。如果中位數在數組1中,查詢次數更不會超過3,所以滿足 $left < $right 循環即可遍歷所有可能。
$m1 = $left;
$m2 = $k - $m1;
// 處理邊界問題
$c1 = max($m1<=0 ? PHP_INT_MIN : $nums1[$m1-1], $m2<=0 ? PHP_INT_MIN : $nums2[$m2-1]);
if(($n1+$n2)%2 == 1){
return $c1;
}
$c2 = min($m1 >= $n1 ? PHP_INT_MAX : $nums1[$m1], $m2 >= $n2 ? PHP_INT_MAX : $nums2[$m2]);
return ($c1+$c2) * 0.5;
}
}