leetcode----4.寻找两个正序数组的中位数(二分法)

4.寻找两个正序数组的中位数(二分法)

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

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

思路:

一开始就能想到两个简单的思路:1. 将两个数组合并后再排序,计算出中位数的位置,直接返回; 2. 使用两个指针分别指向这两个数组的头部,然后根据计算的中位数位置开始移动当前两个指针指向的值中的小值,直到找到中位数。这个方法涉及到的边界问题很复杂。虽然可以通过,但是时间复杂度不满足要求。

还有种方法为二分法:

寻找中位数其实就是寻找一组数中间位置的数,这样就可以将问题转化为求第k小元素。k为中位数的位置。数组长度为奇数或为偶数时,计算方法稍有不同,但是偶数情况的中位数为第k小和第k+1小的元素求和再除2

这里两个有序数组AB,利用二分法的思想,首先取k=k/2,比较A[k\2 - 1]B[k\2 - 1],对于这两个中的较小值,最多只会有 ( k / 2 − 1 ) + ( k / 2 − 1 ) ≤ k − 2 (k/2-1)+(k/2-1) \leq k-2 (k/21)+(k/21)k2个元素比它小,那么它就不能是第 k小的数了。可以直接排除掉。

这里会出现三种情况:

  • A[k\2 - 1] > B[k\2 - 1],此时排除掉A[0]A[k\2 - 1]的元素,它们都不可能是第k小的元素。
  • A[k\2 - 1] < B[k\2 - 1],此时排除掉B[0]B[k\2 - 1]的元素,它们都不可能是第k小的元素。
  • A[k\2 - 1] = B[k\2 - 1],可以归到第一种情况处理。

这样处理一轮,我们就排除了一些元素,接下来需要减少k的值(因为我们排除的都是不大于第k小元素的),在排除后的数组上继续寻找第k小元素,注意,这里的k值不一定是k-k/2。有以下三种特殊情况:

  • A[k\2 - 1]B[k\2 - 1]越界,可以选择越界数组的最后一个元素和另一个数组作比较,防止程序出错。此时需要按照实际排除的元素个数来减少k的值,不能直接k -= k/2;
  • 如果一个数组为空,说明该数组中的所有元素都被排除,我们可以直接返回另一个数组中第 k 小的元素。
  • k==1,返回排除过后两个数组首元素的较小值即为所求。

实际代码中使用两个指针分别指向两个数组,排除的过程其实就是两个指针后移的过程。代码如下:

class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int totalLen = nums1.length + nums2.length;
        //中位数位置
        int k = (int) (totalLen + 1) / 2 ;
        //偶数
        if(totalLen % 2 == 0) {
            double mid = (getMinK(nums1, nums2, k) + getMinK(nums1, nums2, k + 1)) / 2;
            return mid;
        } else {
            //奇数
            double mid = getMinK(nums1, nums2, k);
            return mid;
        }
    }

    private double getMinK(int[] nums1, int[] nums2, int k){
        
        int len1 = nums1.length, len2 = nums2.length;
        int index1 = 0, index2 = 0;

        while(true){
            //边界情况
            //若A数组已经遍历完了,则当前指向B数组的指针再往后移k-1步,就是所求
            if(index1 == len1){
                return nums2[index2 + k - 1];
            }

            if(index2 == len2){
                return nums1[index1 + k - 1];
            }

            if(k == 1){
                return Math.min(nums1[index1], nums2[index2]);
            }
            
            int mid = k / 2;
            int newIndex1 = Math.min(len1, index1 + mid) - 1;
            int newIndex2 = Math.min(len2, index2 + mid) - 1;
            int p1 = nums1[newIndex1], p2 = nums2[newIndex2];
            if(p1 <= p2){
                k -= (newIndex1 - index1 + 1);
                index1 = newIndex1 + 1;
            } else {
                k -= (newIndex2 - index2 + 1);
                index2 = newIndex2 + 1;
            }
        }
    }
}

若哪里总结有误,还望指正!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值