1. 程式人生 > 其它 >Leetcode 4 尋找兩個正序陣列的中位數

Leetcode 4 尋找兩個正序陣列的中位數

技術標籤: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))

思路不好想,但是想清楚之後,很容易寫出遞迴式