Leecode4 求两个有序数组的中位数

有两个大小为 m 和 n 的排序数组 nums1 和 nums2 。
 
请找出两个排序数组的中位数并且总的运行时间复杂度为 O(log (m+n)) 。
 
示例 1:
 
nums1 = [1, 3]
nums2 = [2]
 
中位数是 2.0
 
 
示例 2:
 
nums1 = [1, 2]
nums2 = [3, 4]
 
中位数是 (2 + 3)/2 = 2.5

这里做法还是参考Leecode上高票大佬思路,希望尽量写得简明易懂。利用了二分查找的思想。
思路:

1,首先明确中位数,对于一个有序数组来说,当数组元素个数为奇数时,中位数为中间的数。当元素个数为偶数时,中位数为中间两个数的平均值。由此,可以看出中位数的一个性质是数组中位于中位数左边的数都小于中位数右边的数。根据这个性质,可以作为求解中位数的一个条件。

2, 注意到nums1和nums2是两个有序数组。所以这里将nums1和nums2合并排序求解中位数是一个很烂的方法。

3,先判断两个数组,若其中有一个为空。那么只需要求解一个数组的中位数即可。

4,若两个数组都不空。那么将nums1以i为界,分成两半,这里i定义为

i = m/2
//同理,j可以定义为
 j = n /2
//这样就有了如下示意

      left_part          |        right_part
A[0], A[1], ..., A[i-1]  |  A[i], A[i+1], ..., A[m-1]
B[0], B[1], ..., B[j-1]  |  B[j], B[j+1], ..., B[n-1]


/*根据中位数的必要条件:
1,左边元素个数等于右边元素个数
2,左边元素的最大值小于右边元素的最小值
满足以上两点。中位数= (max(左边元素)+min(右边元素))>>1.
根据上面这两个必要条件,可以得到:
1,i+j = (m+n)/2或者(m+n+1)/2(分别对应n是偶数或n是奇数)
上面的表达式可以改写为
i+j =m-i+n-j (or: m-i+n-j+1)
当n>=m时,我们可以令i从0到m,那么 j = (m + n )/2 - i or j = (m + n + 1)/2 - i 
若m>n,j可能会为负
2,A[i-1]<= B[i] && B[j-1]<=A[i]
总结来说:就是在0到m中找一个数i,使得A[i-1]<= B[i] && B[j-1]<=A[i]成立。

这个时候就会有三种情况。
1,A[i-1]>B[j]
说明左侧的数大于右侧的数,此时i大了,我们可以看到i减小,j会增大,意味着,我们减小iA会减小。且j增大,那么就会达到A[i-1]<=B[j]的状态。所以最大边界imax= i-1;
2,B[j-1]>A[i]
同理,此时i小了,我们要增大i。令最小边界imin = i+1;
3,A[i-1]<B[i] && B[j-1]<A[i]
满足条件,进行下一步。

5,找到满足条件的i后,判断n+m的奇偶性。
n+m奇?max(A[i-1],B[j-1]) : (max(A[i-1],B[j-1])+min(A[i],B[j]))/2
代码如下:


class Solution 
{
	public double findMedianSortedArrays(int[] nums1, int[] nums2)   
	{
		int l1 = nums1.length, l2 = nums2.length;
		int m,n; //m用来存储长度较小的数组长度,n用来存储较大的数组长度。
		int [] A = new int[]; 
		int [] B = new int[]; //A用来存储较小的数组。B用来存储较大的数组。  
		if (l1>l2)        
		{
			m = l2;
			n = l1;
			A = nums2;
			B = nums1;
		}   
		else         
		{
			m = l1;
			n =  l2;
			A = nums1;
			B = nums2;     
		}       
		int iMin =0 ,iMax = m ,halfLen = (m+n+1)>>>1; //i从0~m进行取值。中间值为(m+n+1)/2。
														//这里+1主要是考虑到无论m+n奇还是偶,都可以进行下面的运算。
		   while (iMin<= iMax) //二分查找的前提。
		   {
			  int i = (iMin + iMax)>>>1;
			   int j = halfLen-i;//j 用 i表示。
				if (i>0 && A[i-1]>B[j]) //因为这里满足条件i要减小。所以要保证i>0;  
				{
					iMax = i -1; 
				}
				else if (i < m && A[i]<B[j-1])   //这里满足条件i要增大,所以要保证不越界。             
				{
					   iMin = i +1; 
				}
				else                
				{
					int maxLeft = 0;
					if (i == 0) //A数组为空                   
					{
						maxLeft = B[j-1];   
					}
					else if (j == 0)    //B数组为空                    
					{
						 maxLeft = A[i -1];
					}
					else    //二者都不空
					{
						 maxLeft = Math.max(A[i-1], B[j-1]);
					}
					
					if ((m + n) % 2 == 1) //m+n是奇数的话直接返回这个值就是中位数。没必要算右边的最小值。
					{
						return maxLeft
					}
					int minRight = 0;
					if (i == m)//A数组为空
					{
						minRight = B[j];
					}
					else if (j == n)//B数组为空
					{
						minRight = A[i];
					}
					else
					{
						minRight = Math.min(B[j],A[i]);
					}
					return (maxLeft+minRight)/2.0;
				}             
		   }    
		
		return 0.0;//没有找到返回0.
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值