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

23 篇文章 1 订阅
1 篇文章 0 订阅

 方法一:合并两个数组,然后求中位数,时间复杂度为:O(m+n)

class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
       int length1 = nums1.length;
        int length2 = nums2.length;
        int[] result = new int[length1 + length2];
        int i = 0,j = 0,k = 0;
        while (i < length1 && j < length2){
            if (nums1[i] < nums2[j]){
                result[k] = nums1[i];
                i++;
            }else {
                result[k] = nums2[j];
                j++;
            }
            k++;
        }
        while (i < length1){
            result[k] = nums1[i];
            k++;
            i++;
        }
        while (j < length2){
            result[k] = nums2[j];
            k++;
            j++;
        }
        if (k % 2 == 0){
            return (result[k/2] + result[k/2 - 1]) / 2.0;
        }else {
            return result[k/2];
        }
    }
}

方法二:分治法

一、中位数的作用

将一个集合划分为两个长度相等的子集,其中一个子集中的元素总是大于另一个子集中的元素。

二、双数组划分

对数组A、B分别在\large ij处进行划分,并且将A和B的左半部分放入到left_part集合当中,把A和B的右半部分放入到right_part集合当中。

          left_part          |        right_part
    A[0], A[1], ..., A[i-1]  |  A[i], A[i+1], ..., A[m-1]
    B[0], B[1], ..., B[j-1]  |  B[j], B[j+1], ..., B[n-1]

 根据上边的划分可以推出:

\large len(\text{left\_part}) = len(\text{right\_part})               \large m+n是偶数

\large len(left\_part ) > len(right\_part )            \large m+n是奇数  (左边比右边多一个数)

\large \max(\text{left\_part}) \leq \min(\text{right\_part})          

那么中位数也就可以得到:

\large median=\frac{\max(\text{left\_part}) +min(\text{right\_part})}{2}                 \large m+n是偶数

\large median=max(left\_part )                                                       \large m+n是奇数

要想得到上边的结论,条件有两个:

\large i+j=m-i+n-j(或者:\large i+j=m-i+n-j+1

如果\large n\geqslant m,只需要\large i=[0,m]\large j=\frac{m+n+1}{2}-i

\large B[j-1]\leqslant A[i]并且\large A[i-1]\leqslant B[j]

为了简化分析:假设\large A[i-1],B[j-1],A[i],B[i]总是存在的;具体情况放在后边讨论。

为什么要求\large n\geqslant m成立? 是为了确保j的值不为负数。

三、明确需求

说了这么多,我们需要做的就是在\large [0,m]中找到目标对象\large i,使得以下条件成立:

\large B[j-1]\leqslant A[i]并且\large A[i-1]\leqslant B[j],其中\large j=\frac{m+n+1}{2}-i

四、怎么去找

采用二分查找。

1.设 \large \text{imin} = 0,\text{imax} = m,然后开始在\large [imin,imax]中进行搜索。

2.令\large i=\frac{imin+imax}{2},j=\frac{m+n+1}{2}-i

3.现在\large len(\text{left\_part}) = len(\text{right\_part});那么有三种情况需要说明:

  • \large B[j-1]\leqslant A[i]并且\large A[i-1]\leqslant B[j],这个条件就是搜索结束的标记。
  • \large B[j-1]> A[i],说明\large A[i]划分的太小了,需要把\large i向后移动,以便增大\large A[i];所以新的搜索范围就变为:\large [i+1,imax]
  • \large A[i-1]>B[j],说明\large A[i]划分的太大了,需要把i向前移动,以便减小\large A[i];所以新的搜索范围就变为:\large [imin,i-1]

4.当搜索范围改变时,继续循环判断即可。

当找到目标对象\large i时,中位数为:

\large max(A[i-1],B[j-1]),当\large m+n为奇数的时候

\large \frac{max(A[i-1],B[j-1])+min(A[i],B[j])}{2},当\large m+n为偶数的时候

 五、处理边界值

\large i=0,i=m,j=0,j=n,此时\large A[i-1],B[j-1],A[i],B[i]可能发生越界。但是通过分析可以得到,只要\large i不越界,\large j就不会越界。

越界就是发生在重新确定\large imax\large imin的时候,就是在\large i-1或者\large i+1之前判断一下当前\large i是否在\large [0,m]内。

六、代码

class Solution {
   public  double findMedianSortedArrays(int[] nums1, int[] nums2){
        int m = nums1.length;
        int n = nums2.length;
        //一定要保证n≥m成立
        if (m > n){
            //交换两个数组
            int[] temp = nums1;
            nums1 = nums2;
            nums2=temp;

            //交换m和n
            int temp2 = m;
            m = n;
            n = temp2;
        }

        int imin = 0,imax = m,half = (m + n + 1) / 2;
        while (imin <= imax){
            int i = (imin + imax) / 2;
            int j = half - i;
            if (i > imin && nums1[i-1] > nums2[j]){
                //nums1[i]太大了,需要小一点
                imax = i - 1;
            }else if (i < imax && nums2[j-1] > nums1[i]){
                //nums1[i]太小了,需要大一点
                imin = i + 1;
            }else {
                //nums1[i]符合划分

                //找左边的最大值
                int leftMax = 0;
                if (i == 0){
                    leftMax = nums2[j-1];
                }else if (j == 0){
                    leftMax = nums1[i-1];
                }else {
                    leftMax = Math.max(nums1[i-1], nums2[j-1]);
                }
                if ((m + n) % 2 == 1){
                    return leftMax;
                }
                //找右边的最小值
                int rightMin = 0;
                if (i == m){
                    rightMin = nums2[j];
                }else if (j == n){
                    rightMin = nums1[i];
                }else {
                    rightMin = Math.min(nums1[i],nums2[j]);
                }
                return (leftMax + rightMin) / 2.0;
            }
        }
        return 0.0;
    }
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
题目描述: 给定两个大小为 m 和 n 的有序数组 nums1 和 nums2。 请出这两个有序数组中位数,并且要求算法的时间复杂度为 O(log(m+n))。 你可以假设 nums1 和 nums2 不会同时为空。 示例 1: nums1 = [1, 3] nums2 = [2] 则中位数是 2.0 示例 2: nums1 = [1, 2] nums2 = [3, 4] 则中位数是 (2 + 3)/2 = 2.5 解题思路: 题目要求时间复杂度为 O(log(m+n)),很明显是要用到二分查的思想。 首先,我们需要确定中位数的位置。对于两个长度分别为 m 和 n 的有序数组,它们的中位数位置为 (m+n+1)/2 和 (m+n+2)/2,因为当 m+n 为奇数时,这两个位置的值是相同的;当 m+n 为偶数时,这两个位置的值分别为中间两个数。 然后,我们需要在两个数组中分别到第 k/2 个数(k 为中位数位置),比较它们的大小,如果 nums1[k/2-1] < nums2[k/2-1],则说明中位数位于 nums1 的右半部分和 nums2 的左半部分之间,此时可以舍弃 nums1 的左半部分,将 k 减去 nums1 的左半部分的长度,继续在 nums1 的右半部分和 nums2 的左半部分中寻找第 k/2 个数;反之,如果 nums1[k/2-1] >= nums2[k/2-1],则说明中位数位于 nums1 的左半部分和 nums2 的右半部分之间,此时可以舍弃 nums2 的左半部分,将 k 减去 nums2 的左半部分的长度,继续在 nums1 的左半部分和 nums2 的右半部分中寻找第 k/2 个数。 当 k=1 时,中位数两个数组中的最小值。 Java代码实现: class Solution { public double findMedianSortedArrays(int[] nums1, int[] nums2) { int m = nums1.length; int n = nums2.length; int k = (m + n + 1) / 2; double median = findKth(nums1, 0, m - 1, nums2, 0, n - 1, k); if ((m + n) % 2 == 0) { int k2 = k + 1; double median2 = findKth(nums1, 0, m - 1, nums2, 0, n - 1, k2); median = (median + median2) / 2; } return median; } private double findKth(int[] nums1, int start1, int end1, int[] nums2, int start2, int end2, int k) { int len1 = end1 - start1 + 1; int len2 = end2 - start2 + 1; if (len1 > len2) { return findKth(nums2, start2, end2, nums1, start1, end1, k); } if (len1 == 0) { return nums2[start2 + k - 1]; } if (k == 1) { return Math.min(nums1[start1], nums2[start2]); } int i = start1 + Math.min(len1, k / 2) - 1; int j = start2 + Math.min(len2, k / 2) - 1; if (nums1[i] > nums2[j]) { return findKth(nums1, start1, end1, nums2, j + 1, end2, k - (j - start2 + 1)); } else { return findKth(nums1, i + 1, end1, nums2, start2, end2, k - (i - start1 + 1)); } } }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值