• LeetCode No4 寻找两个正序数组的中位数


    题目

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

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

    示例 1:

    输入:nums1 = [1,3], nums2 = [2]
    输出:2.00000
    解释:合并数组 = [1,2,3] ,中位数 2

    示例 2:

    输入:nums1 = [1,2], nums2 = [3,4]
    输出:2.50000
    解释:合并数组 = [1,2,3,4] ,中位数 (2 + 3) / 2 = 2.5

    提示:

    nums1.length == m
    nums2.length == n
    0 <= m <= 1000
    0 <= n <= 1000
    1 <= m + n <= 2000
    -10^6 <= nums1[i], nums2[i] <= 10^6

    思路

    暴力解法

    如果不考虑时间复杂度的要求,这道题可以用暴力去解决,根据中位数的定义,中位数一定是有序序列的中间一位或者两位的平均值,那么我只要同时开始遍历两个数组,找到中间的两个数,然后根据两个数组长度总和的奇偶,奇数的话就取小值,偶数就取两者的平均数。

    二分查找

    暴力的解法固然可以AC,但是还是不满足题目中对于时间复杂度的要求,而且对于这种在有序的数组中找数的题目,第一反应都是用二分去做,只是这题比较特殊,是要在两个数组里面求合并后的值,当然我们可以合并数组后再二分查找,但事实上,既然暴力解法我们都不需要真正的合并数组,那使用二分查找的时候肯定也可以不用。
    于是我们再看下中位数的定义:

    中位数(Median)又称中值,统计学中的专有名词,是按顺序排列的一组数据中居于中间位置的数,代表一个样本、种群或概率分布中的一个数值,其可将数值集合划分为相等的上下两部分。

    既然中位数可以将数值集合划分为相等的两部分,那么我们先在一个数组里面看中位数,将数组从中间分割开来,中位数一定在坐标i和i-1两个数中,如果是数组长度为奇数那么取min(nums[i-1], nums[i]),而如果是偶数则为 (nums[i-1]+nums[i])/2。

        1 3 5 | 8(i) 9
    
        2 4 6 | 7(i) 10 12
    

    同样如果是两个数组,将两个数组从中间分隔开,使得左边的数据个数=右边的数据个数或者左边比右边多一个,那这个时候基本上就能确定中位数的位置了

        1 3 5 | 8(i) 9
        2 4 6 | 7(j) 10 12
    

    当然仅仅只是数量上满足条件当然还不够,如果中位数就在分割线附近的话,那么分割线附近的4个数,在合并两个数组后应该是一个连续字串,也就是需要满足
    nums1[i-1]<=nums2[j] && nums1[j-1]<=nums2[i]
    这个条件,于是我们二分查找的条件也就出来了,但是还是需要注意如下四种边界情况。

    1 2 3(i) |
    | 4(j) 5 6 7
    
    | 4(i) 5 6
    1 2 3 4(j) |
    
    1 2 3(i) |
    | 4(j) 5 6 
    
    | 4(i) 5 6 
    1 2 3(j) |
    

    AC代码

    暴力解法

    点击查看代码
    class Solution {
        public double findMedianSortedArrays(int[] nums1, int[] nums2) {
            int n = nums1.length;
            int m = nums2.length;
            boolean flag = (n+m)%2==0;
            int mid = (n+m)/2+1;
            int i = 0, j = 0;
            int a = 0, b = 0;
            int cnt = 1;
            while( i<n || j < m ) {
                a = b;
                if( i!=n ) {
                    if( j!=m ) {
                        if( nums1[i] < nums2[j] ) {
                            b = nums1[i];
                            i ++;
                        } else {
                            b = nums2[j];
                            j ++;
                        }
                    } else {
                        b = nums1[i];
                        i ++;
                    }
                } else {
                    b = nums2[j];
                    j ++;
                }
                if( cnt == mid ) {
                    break;
                }
                cnt ++;
            }
            if( flag ) {
                return (a+b)/2.0;
            }
            return b;
        }
    }
    

    二分查找

    点击查看代码
    class Solution {
        public double findMedianSortedArrays(int[] nums1, int[] nums2) {
            int n = nums1.length;
            int m = nums2.length;
            if( n > m ) {
                return findMedianSortedArrays(nums2, nums1);
            }
            int left = 0;
            int rigth = n;
            int i = 0;
            int j = 0;
    
            // 定义的分割线需要满足 nums1[i-1]<=nums2[j] && nums1[j-1]<=nums2[i]
            // 1 3 5 | 8(i) 9
            // 2 4 6 | 7(j) 10 12
            // 左边数字个数应该为 (n+m+1)/2 = i + j
            while( left<rigth ) {
                i = (left + rigth +1 ) / 2;
                j = (n+m+1)/2-i;
                // 二分确定能满足条件的分割线
                if( nums1[i-1]>nums2[j] ) {
                    rigth = i - 1;
                } else {
                    left = i;
                }
            }
            i = left;
            j = (n+m+1)/2-i;
            // 边界处理
            int numsI1 = i==0? Integer.MIN_VALUE: nums1[i-1];
            int numsI = i==n? Integer.MAX_VALUE: nums1[i];
            int numsJ1 = j==0? Integer.MIN_VALUE: nums2[j-1];
            int numsJ = j==m? Integer.MAX_VALUE: nums2[j];
            if( (n+m)%2 == 1 ) {
                return Math.max(numsI1, numsJ1);
            } else {
                return (Math.max(numsI1, numsJ1) + Math.min(numsI, numsJ)) * 0.5;
            }
        }
    }
    
  • 相关阅读:
    1.8其他命令
    1.7远程管理常用命令
    1.6.系统信息相关命令
    1.5linux用户权限相关命令
    python 进程创建和共享内容的方法
    python 操作数据库
    python 类方法中参数使用默认值的方法
    异常处理
    推导列表
    装饰器 装饰带参数的函数和添加函数
  • 原文地址:https://www.cnblogs.com/Asimple/p/16120589.html
Copyright © 2020-2023  润新知