【Leetcode】4. 寻找两个正序数组的中位数

这篇博客介绍了如何在O(log(m+n))的时间复杂度内找到两个正序数组的中位数。首先,通过数组归并法给出非最优解,然后详细解释了使用二分法的最优解,确保在处理不同长度数组时保持效率。最优解通过不断调整数组切分点,确保左半部分元素小于等于右半部分,最终找到满足条件的中位数。

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

题目描述

在这里插入图片描述

// 4. 寻找两个正序数组的中位数


// 给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。
// 请你找出并返回这两个正序数组的 中位数 。
// 要求时间复杂度为 O(log(m + n))

题解

数组归并法

///////////////////////////// 数组归并 ///////////////////////////


// 力扣
// 非最优解
// 参考归并排序的merge函数:https://blog.csdn.net/fisherish/article/details/113835444
// 执行用时:3 ms, 在所有 Java 提交中击败了81.99%的用户
// 内存消耗:39.5 MB, 在所有 Java 提交中击败了86.00%的用户
class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int len1 = nums1.length;
        int len2 = nums2.length;
        int i = 0, j = 0;
        int[] res = new int[len1+len2];
        int t = 0;
        double result = 0d;
        while (i < len1 && j < len2) { 
            if (nums1[i] <= nums2[j])
                res[t++] = nums1[i++];
            else {
                res[t++] = nums2[j++];
            }
        }
        while (i < len1)
            res[t++] = nums1[i++];
        while (j < len2)
            res[t++] = nums2[j++];
        
        if ((len1 + len2) % 2 == 0)
            result = (res[(len1 + len2) / 2 - 1] + res[(len1 + len2) / 2]) / 2d;
        else 
            result = res[(len1 + len2) / 2] * 1d;
        return result;
    }
}

二分法(最优解)

//////////////////////////// 二分查找 /////////////////////////////


// 最优解
// 
// 获取nums1的长度len1,nums2的长度len2,
// 第一个if是两个数组交换,如果nums1比nums2长,把nums1和nums2互换,
// 这样nums1短nums2长。
//
// nums1 [2, 3, 4, 5] len1
// nums2 [4, 5, 7] len2

// 		min  i  max
// nums1 [4, 5, 7] len1
// nums2 [2, 3, 4, 5] len2
// 	 mid=4	  j 

// 将min初始化为0,max初始化为nums1的长度,
// mid为固定常数,为两数组在合并长度下的中点索引(或中间位靠左的索引),
// while循环,终止条件为min和max指针相遇,
// 定义二分法在两个数组的中点切分点i和j,i和j分别将nums1和nums2数组二分为:

// 				left_part               |                right_part
// nums1[0], nums1[1], ..., nums1[i-1]  |  nums1[i], nums1[i+1], ..., nums1[len1-1]
// nums2[0], nums2[1], ..., nums2[j-1]  |  nums2[j], nums2[j+1], ..., nums2[len2-1]

// 如果两个数组总长度为偶数,这其实左半部分(包括num1和nums2)的长度就等于
// 右半部分长度,如果两个数组总长度为奇数,左半部分长度等于右半部分长度+1。
// 我们需要保证左半部分的元素小于等于右半部分的元素。
// 又由于同数组里的元素本来就升序,同数组内本来就是左边小右边大,那我们只需要维护
// 左右半边中,上下不同数组之间的元素大小关系即可。
// 
// 第一个if else段落,在保证i不越界前提下,如果左半边nums2中最大数nums2[j - 1],
// 比右半边nums1中最小数nums1[i]还要大,nums1[i]不符合条件,将左指针min右移到i + 1。
// 在i不越界的前提下,如果左半边nums1中最大数nums1[i - 1]比右半边
// nums2中最小数nums2[j]还要大,nums1[i - 1]不符合条件,右指针max左移到i - 1。
// 如此反复,直到左半边都小于等于右半边的元素,执行else。
// 
// 第二层if else 段落:定义maxLeft用于存储左半边最大值。
// 这时如果中点切分点i为0,即左半边在nums1中无元素。令nums2[j - 1]赋给maxLeft。
// 如果中点切分点j为0,即左半边在nums2中无元素,令nums1[i - 1]赋给maxLeft。
// 如果nums1和nums2在左半边都有值,两数组在左半边的最大值里取最大,赋给maxLeft。
// 即maxLeft = Math.max(nums1[i - 1], nums2[j - 1])。
//
// 如果两个数组长度为奇数,中位数就是maxLeft,直接返回maxLeft
// 如果两个数组长度为偶数,按照同样的逻辑取右半边最小值minRight,
// 最后返回左半边最大值maxLeft和右半边最小值minRight的均值,
// (maxLeft + minRight) / 2.0 就是中位数。

// 执行用时:2 ms, 在所有 Java 提交中击败了100.00%的用户
// 内存消耗:39.6 MB, 在所有 Java 提交中击败了83.45%的用户
class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
		int len1 = nums1.length;
		int len2 = nums2.length;
		if (len1 > len2) {
			int[] temp = nums1; nums1 = nums2; nums2 = temp;
			int tmp = len1; len1 = len2; len2 = tmp;
		}
		int min = 0, max = len1, mid = (len1 + len2 + 1) / 2;
		while (min <= max) {
			int i = (min + max) / 2;
			int j = mid - i;
			if (i < max && nums2[j - 1] > nums1[i]) {
				min = i + 1;
			}
			else if (i > min && nums1[i - 1] > nums2[j]) {
				max = i - 1;
			}
			else {
				int maxLeft = 0;
				if (i == 0) maxLeft = nums2[j - 1];
				else if (j == 0) maxLeft = nums1[i - 1];
				else maxLeft = Math.max(nums1[i - 1], nums2[j - 1]);
				
				if ((len1 + len2) % 2 == 1) return maxLeft;
				int minRight = 0;
				if (i == len1) minRight = nums2[j];
				else if (j == len2) minRight = nums1[i];
				else minRight = Math.min(nums2[j], nums1[i]);
				
				return (maxLeft + minRight) / 2.0;
			}
		}
		return 0;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

锥栗

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值