[leetcode] 4-寻找两个有序数组的中位数

给定两个大小为 m 和 n 的有序数组 nums1 和 nums2。

请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。

你可以假设 nums1 和 nums2 不会同时为空。

Input: nums1 = [1, 3]
nums2 = [2]

Output: 则中位数是 2.0

2.思路

1.外排
由于数组是有序的,因此可以用外排,将两个数组marge成另外的数组help[]。从有序数组help中找出中位数就是一件简单的事了。(缺点是需要额外的空间来构造一个有序的新数组。)

实际时间复杂度为O(m+n),空间复杂度为O(m+n)。

2.双指针法
定义两个指针分别指向两个数组的开头。若数组总数num为奇数,则查找第num/2个数;若总数为偶数,则找那两个/2.
(这里不提供代码)

时间复杂度O(m+n),空间复杂度O(1)。

3. 二分法查找
一般时间复杂度为O(logn)的算法,都是需要用到分治思想(且一般为二分)。
两个有序数组求中位数,问题一般化为,求两个有序数组的第k个数,当k = (m+n)/2时为原问题的解。
怎么求第k个数?分别求出第一个和第二个数组的第 k / 2个数 a 和 b,然后比较 a 和 b,当a < b ,说明第 k 个数位于 a数组的第 k / 2个数后半段,或者b数组的 第 k / 2 个数前半段,问题规模缩小了一半,然后递归处理就行。

时间复杂度O(log m+n),空间复杂度O(1)

源码来源:https://leetcode-cn.com/problems/median-of-two-sorted-arrays/solution/zhen-zheng-ologmnde-jie-fa-na-xie-shuo-gui-bing-pa/

3.题解

外排法

class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
       int m=nums1.length;
       int n=nums2.length;
       int i=0;
       int j=0;
       int k=0;
       int[] help=new int[n+m];
       while(i<=m-1&&j<=n-1){
           help[k++]=nums1[i]<=nums2[j]?nums1[i++]:nums2[j++];
       }
       while(i<m){
           help[k++]=nums1[i++];
       }
       while(j<n){
           help[k++]=nums2[j++];
       }
        k=help.length;
        if(k%2==0){
            return (help[k/2]+help[k/2-1])/2.0;
        }
        else{
            return help[k/2]*1.0;
        }

    }
}

二分查找法

class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int m = nums1.length;
        int n = nums2.length;
        //处理任何一个nums为空数组的情况
        if (m == 0) {
            if (n % 2 != 0)
                return 1.0 * nums2[n / 2];
            return (nums2[n / 2] + nums2[n / 2 - 1]) / 2.0;
        }
        if (n == 0) {
            if (m % 2 != 0)
                return 1.0 * nums1[m / 2];
            return (nums1[m / 2] + nums1[m / 2 - 1]) / 2.0;
        }
        int total = m + n;
        //总数为奇数,找第 total / 2 + 1 个数
        if ((total & 1) == 1) {
            return find_kth(nums1, 0, nums2, 0, total / 2 + 1);
        }
        //总数为偶数,找第 total / 2 个数和第total / 2 + 1个数的平均值
        return (find_kth(nums1, 0, nums2, 0, total / 2) + find_kth(nums1, 0, nums2, 0, total / 2 + 1)) / 2.0;

    }

    //寻找a 和 b 数组中,第k个数字
    double find_kth(int[] a, int a_begin, int[] b, int b_begin, int k) {
        //当a 或 b 超过数组长度,则第k个数为另外一个数组第k个数
        if (a_begin >= a.length)
            return b[b_begin + k - 1];
        if (b_begin >= b.length)
            return a[a_begin + k - 1];
        //k为1时,两数组最小的那个为第一个数
        if (k == 1)
            return Math.min(a[a_begin], b[b_begin]);

        int mid_a = Integer.MAX_VALUE;
        int mid_b = Integer.MAX_VALUE;
        //mid_a / mid_b 分别表示 a数组、b数组中第 k / 2 个数
        if (a_begin + k / 2 - 1 < a.length)
            mid_a = a[a_begin + k / 2 - 1];
        if (b_begin + k / 2 - 1 < b.length)
            mid_b = b[b_begin + k / 2 - 1];
        //如果a数组的第 k / 2 个数小于b数组的第 k / 2 个数,表示总的第 k 个数位于 a的第k / 2个数的后半段,或者是b的第 k / 2个数的前半段
        //由于范围缩小了 k / 2 个数,此时总的第 k 个数实际上等于新的范围内的第 k - k / 2个数,依次递归
        if (mid_a < mid_b)
            return find_kth(a, a_begin + k / 2, b, b_begin, k - k / 2);
        //否则相反
        return find_kth(a, a_begin, b, b_begin + k / 2, k - k / 2);
    }
}

posted @ 2019-10-21 22:15  Virtuals  阅读(298)  评论(0编辑  收藏  举报