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

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

题意

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

进阶:你 能设计一个时间复杂度为 $O(log (m+n)) $的算法解决此问题吗?

思路

取两个数组第k个元素

假设存在两个数组 A , B A,B A,B A [ k / 2 − 1 ] A[k/2-1] A[k/21] B [ k / 2 − 1 ] B[k/2-1] B[k/21]前有 [ 0 ⋯ k / 2 − 2 ] [0 \cdots k/2-2] [0k/22],各有 k / 2 − 1 k/2-1 k/21个元素,共 k − 2 k-2 k2个元素。比较 A [ k / 2 − 1 ] A[k/2-1] A[k/21] B [ k / 2 − 1 ] B[k/2-1] B[k/21]。假设 A [ k / 2 − 1 ] A[k/2-1] A[k/21]是较小者,那么 A [ k / 2 − 1 ] A[k/2-1] A[k/21]不是第k个元素,因为 A [ k / 2 − 1 ] A[k/2-1] A[k/21]之前最多也只有 k − 2 k-2 k2 个元素, A [ k / 2 − 1 ] A[k/2-1] A[k/21]最多也只是第 k − 1 k-1 k1位元素。从而得到 A [ 0 ⋯ k / 2 − 1 ] A[0 \cdots k / 2-1] A[0k/21] 均不是第 k k k 小元素。将 A [ 0 ⋯ k / 2 − 1 ] A[0\cdots k/2-1] A[0k/21]丢弃,得到新的数组。这样搜索范围就缩小了一半。同时原来的查找范围也从查找第 k k k个元素,变成了第 k ′ k' k个元素, k ′ = k − k / 2 k'=k-k/2 k=kk/2。然后重复上述的查找过程,直到 k = 1 k=1 k=1,找到结果 。

为什么每次二分的增量为 k − 2 2 \frac{k-2}{2} 2k2 而不是  k 2 \frac{k}{2} 2k或者  k − 1 2 \frac{k-1}{2} 2k1

如果 i n c r = k / 2 incr=k/2 incr=k/2或者 i n c r = ( k − 1 ) / 2 incr=(k-1)/2 incr=(k1)/2,那么被舍弃的数组段,可能包含第 k k k个元素。

如果 i n c r = ( k − 1 ) / 2 incr=(k-1)/2 incr=(k1)/2,那么 A [ 0... ( k − 1 ) / 2 ] A[0...(k-1)/2] A[0...(k1)/2] B [ 0... ( k − 1 ) / 2 ] B[0...(k-1)/2] B[0...(k1)/2],各有 ( k − 1 ) / 2 + 1 (k-1)/2+1 (k1)/2+1个元素。现在假设 A [ ( k − 1 ) / 2 ] > B [ ( k − 1 ) / 2 ] A[(k-1)/2] > B[(k-1)/2] A[(k1)/2]>B[(k1)/2] 那么 B [ 0... ( k − 1 ) / 2 ] B[0...(k-1)/2] B[0...(k1)/2]数据段被舍弃。若k为奇数, A [ ( k − 1 ) / 2 ] A[(k-1)/2] A[(k1)/2] B [ ( k − 1 ) / 2 ] B[(k-1)/2] B[(k1)/2]有可能为合并后有序数组的第 k + 1 k+1 k+1位,那么被舍弃的 A [ 0... ( k − 1 ) / 2 ] A[0...(k-1)/2] A[0...(k1)/2]可能就包含了第 k k k位元素,把所求的答案舍弃了。

i n c r = k / 2 incr=k/2 incr=k/2同理,同样可能舍弃所求答案。

i n c r = ( k − 2 ) / 2 incr=(k-2)/2 incr=(k2)/2 A [ ( k − 2 ) / 2 ] A[(k-2)/2] A[(k2)/2] B [ ( k − 2 ) / 2 ] B[(k-2)/2] B[(k2)/2]较大者被保留,较大者有可能为第 k k k元素,被舍弃的较小数据段不可能包含第 k k k个元素。

AC 代码

class Solution {

    public static double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int len = nums1.length + nums2.length;
        if (len % 2 == 0) {
            return 1.0 * (findKthElement(nums1, nums2, len / 2) + findKthElement(nums1, nums2, len / 2 + 1)) / 2;
        } else {
            return findKthElement(nums1, nums2, len / 2 + 1);
        }
    }

    public static double findKthElement(int[] nums1, int[] nums2, int k) {
        // p1,p2为指针,指的是nums1[0..p1-1]元素排除不含第k个元素,nums1[p1..]可能有第k个元素
        int p1 = 0, p2 = 0;
        // incr1,incr2为增量偏移,nums1[p1+incr1]为nums1数组中,下一次判断的轴枢点
        int incr1 = 0, incr2 = 0;
        while (true) {
            //nums1中所有元素均被排除,第k个元素只能存在于数组nums2中
            if (p1 == nums1.length) {
                return nums2[p2 + k - 1];
            } else if (p2 == nums2.length) {
                return nums1[p1 + k - 1];
            } else if (k == 1) {
                //两个数组nums1和nums2中找到第1个元素,只需要比较数组头部的元素
                return Math.min(nums1[p1], nums2[p2]);
            }
			//指针偏移的增量不能使得指针超过数组的长度
            incr1 = Math.min(k / 2 - 1, nums1.length - 1 - p1);
            incr2 = Math.min(k / 2 - 1, nums2.length - 1 - p2);
            if (nums1[p1 + incr1] < nums2[p2 + incr2]) {
                //p1指针从判断的轴枢点,向后移动一个单位,nums1[p1..p1+incr1]中不包含第k个元素
                p1 += (incr1 + 1);
                k -= (incr1 + 1);
            } else {
                p2 += (incr2 + 1);
                k -= (incr2 + 1);
            }
        }
    }

    public static void main(String[] args) {
        System.out.println(findMedianSortedArrays(new int[] { 1, 3 }, new int[] { 2 }));
        // System.out.println(findKthElement(new int[] { }, new int[] { }, 1));
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值