【力扣LeetCode】4 寻找两个有序数组的中位数

题目描述(难度难)

给定两个大小为 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

链接

https://leetcode-cn.com/problems/median-of-two-sorted-arrays/

思路

题目要求时间复杂度为:O(log(m + n))。显然,需要采用二分的方案。
通过分析,我们要得到的是中位数,中位数即是左边和右边个数相同,而我们是知道两个数组的总个数的,所以,对一个数组进行二分查找,一个数组的一个切分点确定时,第二个数组的切分店也随之确定。这样的复杂度为O(log(min(m,n)))。具体实现时,要特别注意,当查找到两端点时怎么操作,因为完全有可能一个数组全部的数据都在中位数的左边或者右边。
正确详细思路参考(具体解法不推荐):
https://blog.csdn.net/hk2291976/article/details/51107778
这种思路是错误的(漏掉了很多情况):
https://www.cnblogs.com/TenosDoIt/p/3554479.html

具体分析如下:

nums1 和 A 均表示第一个数组
nums2 和 B 均表示第二个数组

二分查找
首先确定子问题。根据中位数的定义,中位数的左右两侧数字个数相同,且其左边的数字比其小,右边的数字比其大。构造两个子集,分别是左右子集,假定取nums1中的前i (i ∈[0, m])个放在左子集,nums2的前j (j ∈[0, n])个放在左子集,此时左子集数字个数为i + j,右子集数字个数为m + n - i - j。关系是:
在这里插入图片描述

假设我们遍历 i,偶数时为j = (m + n) / 2 - i,奇数时为j = (m + n + 1) / 2 - i,但如果m > nj会为负数,所以要求m <= n。其实偶数时 j 表示成奇数时的式子也是可以的,因为C语言里边的除法是整除,所以加上1不会影响j的结果。所以j = (m + n + 1) / 2 - im <= n
另一个要求是:

    A[i - 1] <= B[j]   
    B[j - 1] <= A[i]

i = (begin + end) / 2

  • 如果A[i - 1] > B[j],则说明需要减小i,减小i的同时j也会增大,这样A[i - 1]的值就会减小,B[j]的值会增大,向着满足条件的方向靠近。而且因为从iend之间A是递增的,所以iend之间的都不符合(i越大,A[i - 1]越大,B[j]越小),故直接将end置为i - 1
  • 如果B[j - 1] > A[i],则说明需要减小j,减小j的意味着增大i,这样A[i]的值就会增大,B[j + 1]的值会减小,向着满足条件的方向靠近。而且因为从begini之间A是递增的,所以begini之间的都不符合(i越小,A[i - 1]越小,B[j]越大),故直接将begin置为i + 1
  • 如果两个条件都满足说明已经遍历到正确的中间位置,进行后续逻辑判断即可。需要注意的是边界情况,在判断时一定要保证数组索引在范围之内,对于不符合的情况,进入到最终的判断逻辑中进行处理。

代码

class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
    	int len1 = nums1.size();
    	int len2 = nums2.size();
    	if(len1 > len2){
    		return findMedianSortedArrays(nums2, nums1);
    	}
    	int start = 0;
		int end = len1;
		int halfLen = (len1 + len2 + 1) / 2; 
    	while(start <= end){
    		int i = (start + end) / 2;
    		int j = halfLen - i;
    		if(i < end && nums2[j-1] > nums1[i]){
    			start = i + 1;
    		}
    		else if(i > start && nums1[i-1] > nums2[j]){
    			end = i - 1;
    		}
    		else{
    			int maxLeft = 0;
    			if(i == 0){
    				maxLeft = nums2[j-1];
    			}
    			else if(j == 0){
    				maxLeft = nums1[i-1];
    			}
    			else{
    				maxLeft = nums1[i-1] > nums2[j-1] ? 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 = nums1[i] < nums2[j] ? nums1[i] : nums2[j];
    			}
    			return (maxLeft + minRight) / 2.0;
    		}
    	}
    	return 0;
	}
};

错误的思路写得很难受的代码,不想扔放上来反省自己

class SolutionError {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
    	if(nums1.size() == 0){
    		return nums2.size() % 2 ? nums2[nums2.size()/2] : (nums2[nums2.size()/2-1] + nums2[nums2.size()/2]) / 2.0;
    	}
    	if(nums2.size() == 0){
    		return nums1.size() % 2 ? nums1[nums1.size()/2] : (nums1[nums1.size()/2-1] + nums1[nums1.size()/2]) / 2.0;
    	}
        if(nums1.size() == 1 && nums2.size() == 1){  // 递归结束条件
        	return (nums1[0] + nums2[0]) / 2.0;
        }
        if(nums1.size() == 1){
        	int index = nums2.size() / 2;
			if(nums2.size() % 2 == 0){
				if(nums1[0] >= nums2[index]){
					return nums2[index];
				}
				else if(nums1[0] <= nums2[index-1]){
					return nums2[index-1];
				}
				else{
					return nums1[0];
				}
			}
			else{
				if(nums1[0] >= nums2[index-1] && nums1[0] <= nums2[index+1]){
					return (nums1[0] + nums2[index]) / 2.0;
				}
				else if(nums1[0] < nums2[index-1]){
					return (nums2[index-1] + nums2[index]) / 2.0;
				}
				else{
					return (nums2[index] + nums2[index+1]) / 2.0;
				}
			}
        }
        if(nums2.size() == 1){
        	int index = nums1.size() / 2;
			if(nums1.size() % 2 == 0){
				if(nums2[0] >= nums1[index]){
					return nums1[index];
				}
				else if(nums2[0] <= nums1[index-1]){
					return nums1[index-1];
				}
				else{
					return nums2[0];
				}
			}
			else{
				if(nums2[0] >= nums1[index-1] && nums2[0] <= nums1[index+1]){
					return (nums2[0] + nums1[index]) / 2.0;
				}
				else if(nums2[0] < nums1[index-1]){
					return (nums1[index-1] + nums1[index]) / 2.0;
				}
				else{
					return (nums1[index] + nums1[index+1]) / 2.0;
				}
			}
        }
        double median1 = nums1.size() % 2 ? nums1[nums1.size()/2] : (nums1[nums1.size()/2-1] + nums1[nums1.size()/2]) / 2.0;
        double median2 = nums2.size() % 2 ? nums2[nums2.size()/2] : (nums2[nums2.size()/2-1] + nums2[nums2.size()/2]) / 2.0;
        if(median1 == median2){
        	return median1;
        }
        else if(median1 > median2){
        	int cutlen = nums1.size() < nums2.size() ? nums1.size()/2 : nums2.size()/2;
        	for(int i = 0; i < cutlen; i++){
        		nums1.erase(nums1.begin());
        		nums2.pop_back();
        	}
        	return findMedianSortedArrays(nums1, nums2);
        }
        else{
        	int cutlen = nums1.size() < nums2.size() ? nums1.size()/2 : nums2.size()/2;
        	for(int i = 0; i < cutlen; i++){
        		nums1.pop_back();
        		nums2.erase(nums2.begin());
        	}
        	return findMedianSortedArrays(nums1, nums2);
        }
    }
};
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值