每日一道算法面試題(4):leetcode4 尋找兩個有序數組的中位數

題目:給定兩個大小爲 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

算法思路:

  1. 傳統思路:用一個循環遍歷兩個數字,找到中間數字的位置,要注意區分 m+n 是奇數偶數的場景,用兩個變量分別記錄兩個數字遍歷的位置;算法比較簡單,但算法複雜度爲 O(m+n),不符合題目要求;
  2. 二分算法:根據題目算法複雜度的要求 O(log(m + n)),應該藉助二分算法的思路才能滿足要求,每次排除一半的數據。題目是求中位數,其實就是求第 k 小數的一種特殊情況,而求第 k 小數有一種算法。由於數列是有序的,我們可以一半兒一半兒的排除。假設我們要找第 k 小數,我們可以每次循環排除掉 k/2 個數:比較兩個數組的第 k/2 個數字,如果 k 是奇數,向下取整;如果哪個小,就表明該數組的前 k/2 個數字都不是第 k 小數字,所以可以排除;依次類推,每次排除剩餘的一半數據。注意 m+n 的和要考慮奇數和偶數,偶數時要找到中間的兩個數字並取平均值;若是奇數,只需要找到一箇中間數字即可。

算法代碼:根據算法思路2,寫出的算法具體代碼如下:

public static double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int m = nums1.length;
        int n = nums2.length;
        // 區分奇數偶數
        int middleLeft = (m + n + 1) / 2;
        if ((m + n) % 2 == 1) { // 奇數情況
            return getMiddleData(nums1, 0, m - 1, nums2, 0, n - 1, middleLeft);;
        }
        // 偶數情況
        int middleRight = (m + n + 2) /2;
        return (getMiddleData(nums1, 0, m - 1, nums2, 0, n - 1, middleLeft)
                + getMiddleData(nums1, 0, m - 1, nums2, 0, n - 1, middleRight)) * 0.5;
    }

    public static int getMiddleData(int[] num1, int start1, int end1, int[] num2, int start2, int end2, int k) {
        int len1 = end1 - start1 + 1;
        int len2 = end2 - start2 + 1;
        //讓 len1 的長度小於 len2,這樣就能保證如果有數組空了,一定是 len1
        if (len1 > len2) {
            return getMiddleData(num2, start2, end2, num1, start1, end1, k);
        }

        if (len1 <= 0) {
            return num2[start2 + k - 1];
        }

        if (k == 1) {
            return Math.min(num1[start1], num2[start2]);
        }

        int middleValue = k / 2; // 二分查找,每次排除一半數據
        // 定位數組1的位置
        int indexNum1 = len1 > middleValue ? (start1 + middleValue - 1) : end1;
        // 定位數組2的位置
        int indexNum2 = len2 > middleValue ? (start2 + middleValue - 1) : end2;
        if (num1[indexNum1] > num2[indexNum2]) { // 排除數組2 start2到indexNum2的數據
            k = k - (indexNum2 - start2 + 1); // 剩餘的待定位數據數量
            return getMiddleData(num1, start1, end1, num2, indexNum2 + 1, end2, k);
        } else {  // 排除數組1 start1到indexNum1的數據
            k = k - (indexNum1 - start1 + 1); // 剩餘的待定位數據數量
            return getMiddleData(num1, indexNum1 + 1, end1, num2, start2, end2, k);
        }
    }

如果你有疑問或更好的算法思路,歡迎留言交流!!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章