Leetcode 4 尋找兩個正序陣列的中位數
阿新 • • 發佈:2021-02-07
技術標籤:Leetcode每日打卡_每日一道leetcode演算法資料結構
Leetcode 4 尋找兩個正序陣列的中位數
題目描述
給定兩個大小為 m 和 n 的正序(從小到大)陣列 nums1 和 nums2。請你找出並返回這兩個正序陣列的中位數。
進階:你能設計一個時間複雜度為 O(log (m+n)) 的演算法解決此問題嗎?
來源:力扣(LeetCode)題目連結
著作權歸領釦網路所有。商業轉載請聯絡官方授權,非商業轉載請註明出處。
題解1
class Solution {
public:
double findMedianSortedArrays (vector<int>& nums1, vector<int>& nums2) {
auto size1 = nums1.size();
auto size2 = nums2.size();
//有空陣列的情況,避免訪問出錯
if(size1 > 0 && size2 > 0){
//兩陣列可以直接相連的情況(注意-size1還是size2)
if(nums1.back() <= nums2.front() || nums1.front () >= nums2.back() ){
//num2中的數值大於num1
if(nums1.back() <= nums2.front()){
//偶數個
if(((size1 + size2) & 1) == 0){
//如果num1的元素數大於num2,說明
// num1+num2 < 2*num1,即兩個待求數在num1中
//這裡證明: 一奇一偶加和為奇 所以如果加和是偶數必然是兩個奇數或兩個偶數,所以size1和size2 至少差2,所以能確定在某一個數組中,而不是分散開
//如果size1 > size2,則在num1中
//如果size1 = size2 則在num1和num2中
//如果size1 < size2 則在num2中
if(size1 > size2){
return (double)((double)nums1[(size1+size2)/2]+(double)nums1[(size1+size2)/2-1])/(double)2;
}else if(size1 == size2){
return (double)((double)nums1[size1-1]+(float)nums2[0])/(double)2;
}else{
return (double)((double)nums2[(size1+size2)/2-size1]+(double)nums2[(size1+size2)/2-1-size1])/(double)2;
}
}else{
//奇數比較簡單,所求數在size大的陣列中
if(size1 > size2){
return (double)nums1[(size1+size2)/2];
}else if(size1 < size2){
return (double)nums2[(size1+size2)/2-size1];
}
}
}
if(nums2.back() <= nums1.front()){
if(((size1 + size2) & 1) == 0){
if(size1 > size2){
return (double)((double)nums1[(size1+size2)/2-size2]+(double)nums1[(size1+size2)/2-1-size2])/(double)2;
}else if(size1 == size2){
return (double)((double)nums2[size2-1]+(double)nums1[0])/(double)2;
}else{
return (double)((double)nums2[(size1+size2)/2]+(double)nums2[(size1+size2)/2-1])/(double)2;
}
}else{
if(size1 > size2){
return (double)nums1[(size1+size2)/2-size2];
}else if(size1 < size2){
return (double)nums2[(size1+size2)/2];
}
}
}
}else{
//如果是兩陣列穿插合併
double temp = 0, maxx = 0;
int j = 0, f = 0;
for(int i = 0; i <= (size1+size2)/2; i++){
//temp是倒數第二個數
//max是倒數第一個數
//注意越界
temp = maxx;
if(j < size1 && f < size2)
maxx = nums1[j] <= nums2[f] ? nums1[j++]:nums2[f++];
else if(j >= size1)
maxx = nums2[f++];
else maxx = nums1[j++];
}
//如果是偶數
if(((size1+size2) & 1) == 0) return (double)(temp+maxx)/(double)2;
else return maxx;
}
//如果有一方無元素
}else if (size1 == 0){
if(! (size2 & 1)){
return ((double)nums2[size2/2] + (double)nums2[size2/2-1]) /(double) 2;
}else return (double)nums2[size2/2];
}else if (size2 == 0){
if(! (size1 & 1)){
return ((double)nums1[size1/2] + (double)nums1[size1/2-1]) /(double) 2;
}else return (double)nums1[size1/2];
}
//leetcode強制return
return 0.00000;
}
};
題解2(二分法)(參考程式碼)
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2)
{
int m = nums1.size();
int n = nums2.size();
//中位數 = (left + right)/2
int left = (m + n + 1) / 2;
int right = (m + n + 2) / 2;
return (findKth(nums1, 0, nums2, 0, left) + findKth(nums1, 0, nums2, 0, right)) / 2.0;
}
//在兩個有序陣列中找到第k個元素(例如找第一個元素,k=1,即nums[0])
//i: nums1的起始位置 j: nums2的起始位置(i,j都是從0開始)
int findKth(vector<int>& nums1, int i, vector<int>& nums2, int j, int k)
{
//若nums1為空(或是說其中數字全被淘汰了)
//在nums2中找第k個元素,此時nums2起始位置是j,所以是j+k-1
if(i >= nums1.size()) return nums2[j + k - 1];
//nums2同理
if(j >= nums2.size()) return nums1[i + k - 1];
//遞迴出口
if(k == 1) return std::min(nums1[i], nums2[j]);
//這兩個陣列的第K/2小的數字,若不足k/2個數字則賦值整型最大值,以便淘汰另一陣列的前k/2個數字
int midVal1 = (i + k/2 - 1 < nums1.size()) ? nums1[i + k/2 - 1] : INT_MAX;
int midVal2 = (j + k/2 - 1 < nums2.size()) ? nums2[j + k/2 - 1] : INT_MAX;
//二分法核心部分
if(midVal1 < midVal2)
return findKth(nums1, i + k/2, nums2, j, k - k/2);
else
return findKth(nums1, i, nums2, j + k/2, k - k/2);
}
題解2相當於每次縮小了一半的查詢規模:
eg:1 2 3 4 5 6
7 8 9 10 11 12
第一次比較時,選定了k = 6, 7
以6為例
首先確定了 3 < 9 所以 3之前的數也都小於 9 (正序陣列性質)
故只需要比較3之後和9之前的數 去找剩下的3個
時空複雜度為O(log(m+n))
思路不好想,但是想清楚之後,很容易寫出遞迴式