【LeetCode刷题练习】(4.)寻找两个有序数组的中位数Median of Two Sorted Arrays

寻找两个有序数组的中位数 Median of Two Sorted Arrays

题目
给定两个大小为 m 和 n 的有序数组 nums1 和 nums2。
请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。
你可以假设 nums1 和 nums2 不会同时为空。

示例

nums1 = [1, 3]
nums2 = [2]
则中位数是 2.0

nums1 = [1, 2]
nums2 = [3, 4]
则中位数是 (2 + 3)/2 = 2.5

思路
参考链接:寻找两个有序数组的中位数 C / C++

在这道题中,首先需要明确 中位数定义,即将一个集合划分为两个长度相等的子集,其中一个子集中的元素总是大于另一个子集中的元素。而题目难点在于时间复杂度要求为O(log(m + n)),而如果简单使用两数组合并求中位数则时间复杂度通常为O(m+n),不符合题意要求。参考数据结构中对于不同方式查找的时间复杂度要求,可以很明显发现利用二分查找法最简单且符合题目要求。

常用算法时间复杂度参考链接:常用排序算法和查找算法的时间复杂度和空间复杂度

上述链接中详细介绍了二分切割法在两个数组中的应用,我就不再过多赘述了。而其中值得我借鉴的一个思路在于,利用虚拟 ‘#’ 填充,使得数组中两数组长度恒为偶数,即让m转换成2m+1 ,n转换成2n+1, 两数之和就变成了2m+2n+2,恒为偶数。把2个数组看做一个虚拟的数组A,A有2m+2n+2个元素,割在m+n+1处,所以我们只需找到m+n+1位置的元素和m+n+2位置的元素就行了。

左边:A[m+n+1] = Max(LMax1,LMax2)
右边:A[m+n+2] = Min(RMin1,RMin2)
==>Mid = (A[m+n+1]+A[m+n+2])/2 = (Max(LMax1,LMax2) + Min(RMin1,RMin2) )/2

另,此处涉及C/C++中关于整型上下限INT_MAX和INT_MIN的用法,参考链接:【C/C++】整型上下限INT_MAX INT_MIN及其运算

初解
参考上述思路得到代码:

#define max(a,b) (((a)>(b))?(a):(b))
#define min(a,b) (((a)<(b))?(a):(b))
class Solution {
public:    
	double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {         
		int n=nums1.size();         
		int m=nums2.size();         
		if(n>m){ //若nums2长度小于nums1,交换两数组,保证nums1长度最短             
			return findMedianSortedArrays(nums2, nums1);         
		}         
		int LMax1,LMax2,RMin1,RMin2,c1,c2;
		// Ci 为第i个数组的割,比如C1为2时表示第1个数组只有2个元素。LMaxi为第i个数组割后的左元素。RMini为第i个数组割后的右元素。         
		int low=0,high=2*n; //加入虚拟#后nums1长度为2*n         
		while(low<=high){  //二分切割            
			c1 = (low+high)/2;  //c1是二分的结果            
			c2 = m+n-c1;            
			/*关于c2 = m + n - c1;的理解:            
			1、两个虚拟数组[#2#3#5#][#1#4#7#9#]合并之后的数组A[#1#2#3#4#5#7#9#],长度为2*(m+n)+2,多余了一个#,忽略掉这个#,有效长度为2*(m+n)+1;            
			2、求数组A的中位数转换成了求2*(m+n)+1长度数组的中位数,也就是求第m+n+1位置的元素的值;            
			3、c1、c2都是下标,从0开始,那么减去1,就是c2=m+n-c1;*/            
			LMax1 = (c1 == 0) ? INT_MIN : nums1[(c1 - 1) / 2];  //c1=0则LMax1为数组nums1第一个元素,即左边数组为空,假定LMax1 = INT_MIN,来保证LMax2<RMin1恒成立            
			RMin1 = (c1 == 2 * n) ? INT_MAX : nums1[c1 / 2];    //c1=2*n则Rmin1为数组nums1最后一个元素,即右边数组为空,假定RMin1= INT_MAX,来保证LMax2<RMin1恒成立         
			LMax2 = (c2 == 0) ? INT_MIN : nums2[(c2 - 1) / 2];  //nums2数组同理            
			RMin2 = (c2 == 2 * m) ? INT_MAX : nums2[c2 / 2];            
			if(LMax1 > RMin2){  //二分法移动low和high                
				high = c1-1;            
			}else if(LMax1 > RMin1){                
				low = c1+1;            
			}else{                
				break;            
			}         
		}         
		return (max(LMax1,LMax2)+min(RMin1,RMin2))/2.0;       
	}
};

运行结果
在这里插入图片描述
But,提交后不成功,执行错误,原因是测试用例覆盖不完全:
在这里插入图片描述
在这里插入图片描述
修正代码
原因竟然是最后else if时判断条件写错了…

#define max(a,b) (((a)>(b))?(a):(b))
#define min(a,b) (((a)<(b))?(a):(b))
class Solution {
public:    
	double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {         
		int n=nums1.size();         
		int m=nums2.size();         
		if(n>m){ //若nums2长度小于nums1,交换两数组,保证nums1长度最短             
			return findMedianSortedArrays(nums2, nums1);         
		}         
		int LMax1,LMax2,RMin1,RMin2,c1,c2;          
		// Ci 为第i个数组的割,比如C1为2时表示第1个数组只有2个元素。LMaxi为第i个数组割后的左元素。RMini为第i个数组割后的右元素。         
		int low=0,high=2*n; //加入虚拟#后nums1长度为2*n         
		while(low<=high){  //二分切割            
			c1 = (low+high)/2;  //c1是二分的结果            
			c2 = m+n-c1;            
			/*关于c2 = m + n - c1;的理解:            
			1、两个虚拟数组[#2#3#5#][#1#4#7#9#]合并之后的数组A[#1#2#3#4#5#7#9#],长度为2*(m+n)+2,多余了一个#,忽略掉这个#,有效长度为2*(m+n)+1;            
			2、求数组A的中位数转换成了求2*(m+n)+1长度数组的中位数,也就是求第m+n+1位置的元素的值;            
			3、c1、c2都是下标,从0开始,那么减去1,就是c2=m+n-c1;*/            
			LMax1 = (c1 == 0) ? INT_MIN : nums1[(c1 - 1) / 2]; //c1=0则LMax1为数组nums1第一个元素,即左边数组为空,假定LMax1 = INT_MIN,来保证LMax2<RMin1恒成立            
			RMin1 = (c1 == 2 * n) ? INT_MAX : nums1[c1 / 2];    //c1=2*n则Rmin1为数组nums1最后一个元素,即右边数组为空,假定RMin1= INT_MAX,来保证LMax2<RMin1恒成立
			LMax2 = (c2 == 0) ? INT_MIN : nums2[(c2 - 1) / 2];  //nums2数组同理            
			RMin2 = (c2 == 2 * m) ? INT_MAX : nums2[c2 / 2];            
			if(LMax1 > RMin2){  //二分法移动low和high                
				high = c1-1;            
			}else if(LMax2 > RMin1){                
				low = c1+1;            
			}else{                
				break;            
			}         
		}         
		return (max(LMax1,LMax2)+min(RMin1,RMin2))/2.0;       
	}
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值