4. 寻找两个正序数组的中位数

给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。

算法的时间复杂度应该为 O(log (m+n)) 。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/median-of-two-sorted-arrays
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

官方题解

class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        if (nums1.length > nums2.length) {
            return findMedianSortedArrays(nums2, nums1);
        }

        int m = nums1.length;
        int n = nums2.length;
        int left = 0, right = m;
        // median1:前一部分的最大值
        // median2:后一部分的最小值
        int median1 = 0, median2 = 0;

        while (left <= right) {
            // 前一部分包含 nums1[0 .. i-1] 和 nums2[0 .. j-1]
            // 后一部分包含 nums1[i .. m-1] 和 nums2[j .. n-1]
            int i = (left + right) / 2;
            int j = (m + n + 1) / 2 - i;

            // nums_im1, nums_i, nums_jm1, nums_j 分别表示 nums1[i-1], nums1[i], nums2[j-1], nums2[j]
            int iLeft = (i == 0 ? Integer.MIN_VALUE : nums1[i - 1]);
            int iRight = (i == m ? Integer.MAX_VALUE : nums1[i]);
            int jLeft = (j == 0 ? Integer.MIN_VALUE : nums2[j - 1]);
            int jRight = (j == n ? Integer.MAX_VALUE : nums2[j]);

            if (iLeft <= jRight) {
                median1 = Math.max(iLeft, jLeft);
                median2 = Math.min(iRight, jRight);
                left = i + 1;
            } else {
                right = i - 1;
            }
        }

        return (m + n) % 2 == 0 ? (median1 + median2) / 2.0 : median1;
    }
}
class Solution {
    private int findKth(int[] num1, int[] num2, int k) {
        if (num1.length == 0) {
            return num2[k - 1];
        }

        if (num2.length == 0) {
            return num1[k - 1];
        }

        int[] shortArray = num1.length < num2.length ? num1 : num2;
        int[] longArray = shortArray == num1 ? num2 : num1;

        if (k <= shortArray.length) {
            return findUpMedian(shortArray, 0, k - 1, longArray, 0, k - 1);
        } else if (k <= longArray.length) {
            // 1 2 3 4 5
            // 1 [2 3 4 5 6 [7]] 8 9
            int longStart = k - shortArray.length - 1;
            if (longArray[longStart] >= shortArray[shortArray.length - 1]) {
                return longArray[longStart];
            }
            return findUpMedian(shortArray, 0, shortArray.length - 1, longArray, longStart + 1, k - 1);
        } else {
            // 1 2 [3 4 5]
            // 1 2 3 4 [5 6 7] | 8 9 [10] 11 12
            int shortStart = k - longArray.length - 1;
            int longStart = k - shortArray.length - 1;
            if (shortArray[shortStart] >= longArray[longArray.length - 1]) {
                return shortArray[shortStart];
            }

            if (longArray[longStart] >= shortArray[shortArray.length - 1]) {
                return longArray[longStart];
            }

            return findUpMedian(shortArray, shortStart + 1, shortArray.length - 1, longArray, longStart + 1, longArray.length - 1);
        }
    }

    /**
     * 发现两个等长增序数组的中位数
     */
    private int findUpMedian(int[] num1, int start1, int end1, int[] num2, int start2, int end2) {
        while (start1 < end1) {
            int m1 = (start1 + end1) >> 1;
            int m2 = (start2 + end2) >> 1;
            int offset = (end1 - start1 + 1) & 1 ^ 1;

            // 1 2 3 4 5
            // 1 2 3 4 5

            // 1 2 3 4 5 6
            // 1 2 3 4 5 6
            if (num1[m1] < num2[m2]) {
                start1 = m1 + offset;
                end2 = m2;
            } else {
                start2 = m2 + offset;
                end1 = m1;
            }
        }
        return Math.min(num1[start1], num2[start2]);
    }

    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int length = (nums1.length + nums2.length);

        if (length % 2 == 0) {
            return (findKth(nums1, nums2, length / 2) + findKth(nums1, nums2, length / 2 + 1)) / 2.0;
        } else {
            return findKth(nums1, nums2, length / 2 + 1);
        }
    }
}

二分法寻找两个有序数组的中位数

class Solution {
    private int findKth(int[] num1, int[] num2, int k) {
        if (num1.length == 0) {
            return num2[k - 1];
        }

        if (num2.length == 0) {
            return num1[k - 1];
        }

        int[] shortArray = num1.length < num2.length ? num1 : num2;
        int[] longArray = shortArray == num1 ? num2 : num1;

        if (k <= shortArray.length) {
            return findUpMedian(shortArray, 0, k - 1, longArray, 0, k - 1);
        } else if (k <= longArray.length) {
            // 1 2 3 4 5
            // 1 [2 3 4 5 6 [7]] 8 9
            int longStart = k - shortArray.length - 1;
            if (longArray[longStart] >= shortArray[shortArray.length - 1]) {
                return longArray[longStart];
            }
            return findUpMedian(shortArray, 0, shortArray.length - 1, longArray, longStart + 1, k - 1);
        } else {
            // 1 2 [3 4 5]
            // 1 2 3 4 [5 6 7] | 8 9 [10] 11 12
            int shortStart = k - longArray.length - 1;
            int longStart = k - shortArray.length - 1;
            if (shortArray[shortStart] >= longArray[longArray.length - 1]) {
                return shortArray[shortStart];
            }

            if (longArray[longStart] >= shortArray[shortArray.length - 1]) {
                return longArray[longStart];
            }

            return findUpMedian(shortArray, shortStart + 1, shortArray.length - 1, longArray, longStart + 1, longArray.length - 1);
        }
    }

    /**
     * 发现两个等长增序数组的中位数
     */
    private int findUpMedian(int[] num1, int start1, int end1, int[] num2, int start2, int end2) {
        if (num1[start1] >= num2[end2]) {
            return num2[end2];
        }

        if (num1[end1] <= num2[start2]) {
            return num1[end1];
        }

        int left = 1, right = end1 - start1;
        int partition = end1 - start1 + 1;

        while (left <= right) {
            int mid = (left + right) / 2;
            int i = start1 + mid;
            int j = start2 + (partition - mid);
            int a = num1[i - 1];
            int b = num1[i];
            int c = num2[j - 1];
            int d = num2[j];
            if (a <= d && c <= b) {
                return Math.max(num1[i -1], num2[j - 1]);
            } else if (a > d) {
                right = mid - 1;
            } else {
                left = mid + 1;
            }
        }
        return -1;
    }

    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int length = (nums1.length + nums2.length);

        if (length % 2 == 0) {
            return (findKth(nums1, nums2, length / 2) + findKth(nums1, nums2, length / 2 + 1)) / 2.0;
        } else {
            return findKth(nums1, nums2, length / 2 + 1);
        }
    }
}
posted @ 2021-11-27 23:06  Tianyiya  阅读(41)  评论(0)    收藏  举报