LeetCode-4-算法-寻找两个有序数组的中位数(困难)二分查找

23 篇文章 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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/median-of-two-sorted-arrays
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

审题:

中位数:一组有序数据中居于中间位置的数,奇数个最中间数字,偶数个中间两个数字的平均数。

思考:

因为对效率有要求,使用二分搜索。

解题:

解法一:二分搜索。

因为求两个数组的中位数,通过对比两个数组的中位数,然后调整对比的位置,保证两个数组左右两边的数字个数相同,直到最后找到平衡点:B[j−1]≤A[i] 且 A[i−1]≤B[j],需要注意的是,有些数组可能只有一位,或者没有,还有数组长度奇数偶数的问题。

	public double findMedianSortedArraysI(int[] A, int[] B) {
		int m = A.length;
		int n = B.length;
		// 保证B数组是最长或者最长的之一。这样可以减少判断次数。
		if (m > n) {
			int[] temp = A;
			A = B;
			B = temp;
			int tep = m;
			n = m;
			m = tep;
		}
		int iMin = 0;
		int iMax = m;
		int halfLen = (m + n + 1) / 2;
		while (iMin <= iMax) {
			// 求出数组A的中位数的下标位置
			int i = (iMin + iMax) / 2;
			// B数组的位置可以由A的i得出,因为i和j左边的和==右边的和(个数)。
			int j = halfLen - i;
			if (i < iMax && B[j - 1] > A[i]) {// 太小,A[i]这个位置的数字,比B中间的数字小,需要变大i的值,i值变j值也变。
				iMin = i + 1;// A数组A[i]左边太小,排除,再剩下较大的那一段找,B相应也许相左移动。
			} else if (i > iMax && A[i - 1] > B[j]) {// 太大,A最大的数字比B中间的数字大,j需要右移。
				// iMin大了i就大了,j就小了
				iMax = i - 1;
			} else {// 平衡点。B[j−1]≤A[i] 且 A[i−1]≤B[j]:
				int maxLeft = 0;
				if (i == 0) {// 0或者1位
					maxLeft = B[j - 1];
				} else if (j == 0) {// 两个都是0或者1位。
					maxLeft = A[i - 1];
				} else {
					maxLeft = Math.max(A[i - 1], B[j - 1]);
				}
				// 奇数个的情况
				if ((m + n) % 2 == 1) {
					return maxLeft;
				}
				// 偶数的情况需要求两个数字的平均数
				int minRight = 0;
				if (i == m) {
					minRight = B[j];
				} else if (j == n) {
					minRight = A[i];
				} else {
					minRight = Math.min(B[j], A[i]);
				}

				return (maxLeft + minRight) / 2.0;
			}
		}

		return 0.0;
	}

方法二:参考

 public double findMedianSortedArraysII(int[] nums1, int[] nums2) {
        int m = nums1.length;
        int n = nums2.length;
        int left = (m + n + 1) / 2;
        int right = (m + n + 2) / 2;
        return (findKth(nums1, 0, nums2, 0, left) + findKth(nums1, 0, nums2, 0, right)) / 2.0;
    }
    //i: nums1的起始位置 j: nums2的起始位置
    public int findKth(int[] nums1, int i, int[] nums2, int j, int k){
        if( i >= nums1.length) return nums2[j + k - 1];//nums1为空数组
        if( j >= nums2.length) return nums1[i + k - 1];//nums2为空数组
        if(k == 1){
            return Math.min(nums1[i], nums2[j]);
        }
        int midVal1 = (i + k / 2 - 1 < nums1.length) ? nums1[i + k / 2 - 1] : Integer.MAX_VALUE;
        int midVal2 = (j + k / 2 - 1 < nums2.length) ? nums2[j + k / 2 - 1] : Integer.MAX_VALUE;
        if(midVal1 < midVal2){
            return findKth(nums1, i + k / 2, nums2, j , k - k / 2);
        }else{
            return findKth(nums1, i, nums2, j + k / 2 , k - k / 2);
        }        
    }

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值