求两有序数组的中位数

该博客介绍了如何在给定两个已排序的数组nums1和nums2时,通过二分查找算法寻找它们的中位数。在O(log(m+n))的时间复杂度内,首先检查数组长度,若其中一个为空,则直接计算另一个数组的中位数;否则,对较短的数组进行二分查找,找到中位数所在的位置,然后根据数组长度的奇偶性计算中位数的值。最终,算法返回中位数的正确结果6.0。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

给定两个大小为 m 和 n 的正序(从小到大)数组 nums1 和 nums2

找出并返回这两个正序数组的中位数

时间复杂度O(log(m+n))

解法:二分法查找

public class Main {
	public static void main(String[] args) {
		Solution solution = new Solution();
		int[] nums1 = {1,3,7,9};
		int[] nums2 = {4,6,7};
		System.out.println("mid "+solution.method(nums1, nums2));
	}
}

class Solution {
	public double method(int[] nums1, int[] nums2) {
		int length1 = nums1.length;
		int length2 = nums2.length;
		/* 如果其中一个数组长度为0, 直接计算另有一个数组的中位数即可 */
		if (length1 == 0) {
			if (length2 %2 == 0) {
				return (nums2[length2/2-1] + nums2[length2/2])/2.0;
			}
			else {
				return nums2[length2/2];
			}
		}
		else if (length2 == 0) {
			if (length1 %2 == 0) {
				return (nums1[length1/2-1] + nums1[length1/2])/2.0;
			}
			else {
				return nums1[length1/2];
			}
		}
		else {
			/* 两个数组的长度都不为0的情况,使用二分查找 */
			return binarySerchMid(nums1, nums2);
		}
	}

	/*
	  假设两个数组的长度分别为m和n, 两数组归并之后, 数组A的[0,i)个元素位于中位数左侧, (i,m]的元素位于中位数右侧
	  数组B的[0,j)个元素位于中位数左侧, (j,n]的元素位于中位数右侧(A[i]和B[j]的相对位置还需进一步确定), 则有
	  (i+j) = (m+n+1)/2
	  其中m+n+1是为了解决m+n不是偶数的情况.
	  基于上述结论, 只要对其中一个数组使用二分查找出i, 即可根据j = (m+n+1)/2-i计算出j
	  假设中间位置左侧的最大值为MaxLeft, 右侧的最大值为MinRight
	  其中有2种情况需要特殊处理:
	  a. A所有元素都>=中位数, 此时i=0
	  b. B所有元素都>=中位数, 此时j=0
	*/
	private double binarySerchMid(int[] nums1,int[] nums2) {
		int length1 = nums1.length;
		int length2 = nums2.length;
		int midLength = (length1+length2+1)/2;
		/* 为了减少查找次数, 使用长度较小的数组进行二分查找
		   此处交换是为了使nums1始终是长度较小者
		 */
		if (length1 > length2) {
			int[] temp = nums1;
			nums1 = nums2;
			nums2 = temp;
			int tempLeng = length1;
			length1 = length2;
			length2 = tempLeng;
		}

		int midA = 0;
		int midB = 0;
		/* 二分法的左边界和右边界 */
		int left = 0, right = length1;
		/* 两数组按序合并后的中间两个数为maxLeft和minRight */
		int maxLeft = 0, minRight = 0;
		while (left <= right) {
			midA = (left + right)/2;
			midB = midLength - midA;
			
			if ((midA < length1) && (nums1[midA] < nums2[midB-1])) {
				/* midA小了(需防止越界, 下同) */
				left = midA+1;
			}
			else if ((midA > 0) && (nums1[midA-1] > nums2[midB])) {
				/* midA大了 */
				right = midA-1;
			}
			else {
				/* 二分法查找成功 */
				break;
			}
		}

		if (midA == 0) {
			/* nums1全部>=中位数 */
			maxLeft = nums2[midB-1];
		}
		else if (midB == 0) {
			/* nums2全部>=中位数 */
			maxLeft = nums1[midA-1];
		}
		else {
			maxLeft = Math.max(nums1[midA-1], nums2[midB-1]);
		}
		if ((length1+length2)%2 != 0) {
			/* 总长度为奇数, 中位数 = 中间位置元素 */
			return maxLeft;
		}
		else {
			/* 总长度为偶数, 中位数为中间两数的平均值 */
			if (midA == length1) {
				/* nums1整体<中位数 */
				minRight = nums2[midB];
			}
			else if (midB == length2) {
				/* nums2整体<中位数 */
				minRight = nums1[midA];
			}
			else {
				minRight = Math.min(nums1[midA], nums2[midB]);
			}

			return (maxLeft+minRight)/2.0;
		}
	}
}

输出:

6.0

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值