leetcode 4. 寻找两个有序数组的中位数(Java版)

43 篇文章 0 订阅
28 篇文章 0 订阅

题目描述(题目难度,困难)

给定两个大小为 m 和 n 的有序数组 nums1nums2

请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。

你可以假设 nums1nums2 不会同时为空。

示例 1:

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

示例 2:

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

示例代码

下面是我时间复杂度为O(log(min(m,n)))的Java代码,代码虽然看起来比较长,但其实大部分是重复逻辑(^&^)。

class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        //因为题目说两数组不会同时为空,所以这种情况忽略
    	//有一个数组为空的情况
    	if(nums1 == null || nums1.length == 0)
    		if(nums2.length % 2 == 0) return (nums2[nums2.length/2-1]+nums2[nums2.length/2])/2.0;
    		else return nums2[nums2.length/2];
    	if(nums2 == null || nums2.length == 0)
    		if(nums1.length % 2 == 0) return (nums1[nums1.length/2-1]+nums1[nums1.length/2])/2.0;
    		else return nums1[nums1.length/2];
    	//两数组不存在交叉
    	int len = nums1.length + nums2.length;//总长度
    	int m = len/2;
    	if(nums1[nums1.length-1] <= nums2[0]){
    		if(len % 2 == 0){
    			if(m-1 > nums1.length-1) return (nums2[m-nums1.length-1]+nums2[m-nums1.length])/2.0;
    			else if(m <= nums1.length-1) return (nums1[m-1]+nums1[m])/2.0;
    			else return (nums1[m-1]+nums2[m-nums1.length])/2.0;
    		}else{
    			if(m > nums1.length-1) return nums2[m-nums1.length];
    			else return nums1[m];
    		}
    	}
    	if(nums2[nums2.length-1] <= nums1[0]){
    		if(len % 2 == 0){
    			if(m-1 > nums2.length-1) return (nums1[m-nums2.length-1]+nums1[m-nums2.length])/2.0;
    			else if(m <= nums2.length-1) return (nums2[m-1]+nums2[m])/2.0;
    			else return (nums2[m-1]+nums1[m-nums1.length])/2.0;
    		}else{
    			if(m > nums2.length-1) return nums1[m-nums2.length];
    			else return nums2[m];
    		}
    	}
    	//两数组存在交叉,使用类二分查找
    	int i0 = 0,  j0 = 0;//i,j 分别为两数组搜索区间的起始下标
		int len1 = nums1.length, len2 = nums2.length;//len1,len2 分别表示两数组搜索区间的长度
		int i, j,L;
    	if(len % 2 == 0){//两个值m-1,m
    		while(true){
    			i = i0 + len1/2;
    			j = j0 + len2/2;
    			L = i + j +1;
    			if(nums1[i] < nums2[j]){//nums2[j]为大值
    				if(L > m){//大值指针前移
    					if((len2=j-j0) == 0) return (nums1[i+m-L]+nums1[i+m-L+1])/2.0;
    				}else{
    					if(L == m && (i >= nums1.length-1 || nums1[i+1] >= nums2[j])){
    						if(j <= 0 || nums1[i] > nums2[j-1]) return (nums1[i]+nums2[j])/2.0;
    						else return (nums2[j-1]+nums2[j])/2.0;
    					}
    					//小值指针后移
    					if((len1 -= i-i0+1) == 0) return (nums2[j+m-L]+nums2[j+m-L-1])/2.0;
    					i0 = i+1;
    				}
    			}else{//nums1[i]为大值
    				if(L > m){
    					if((len1 = i-i0) == 0) return (nums2[j+m-L]+nums2[j+m-L+1])/2.0;
    				}else{
    					if(L == m && (j >= nums2.length-1 || nums2[j+1] >= nums1[i])){
    						if(i <= 0 || nums2[j] > nums1[i-1]) return (nums1[i]+nums2[j])/2.0;
    						else return (nums1[i-1]+nums1[i])/2.0;
    					}
    					if((len2 -= j-j0+1) == 0) return (nums1[i+m-L]+nums1[i+m-L-1])/2.0;
    					j0 = j+1;
    				}
    			}
    		}
    	}else{//只有一个值
    		while(true){
    			i = i0 + len1/2;
    			j = j0 + len2/2;
    			L = i + j +1;
    			if(nums1[i] < nums2[j]){//nums2[j]为大值
    				if(L > m){//大值指针前移
    					if((len2 = j-j0) == 0) return nums1[i+m-L+1];
    				}else{
    					if(L == m && (i >= nums1.length-1 || nums1[i+1] >= nums2[j])) return nums2[j];
    					//小值指针后移
    					if((len1 -= i-i0+1) == 0) return nums2[j+m-L];
    					i0 = i+1;
    				}
    			}else{//nums1[i]为大值
    				if(L > m){
    					if((len1 = i-i0) == 0) return nums2[j+m-L+1];
    				}else{
    					if(L == m && (j >= nums2.length-1 || nums2[j+1] >= nums1[i])) return nums1[i];
    					if((len2 -= j-j0+1) == 0) return nums1[i+m-L];
    					j0 = j+1;
    				}
    			}
    		}
    	}
    }
}

思路解析

首先约定符号,假设 a,b 分别为两数组 A,B 的元素,i,j 为 a,b 在各自数组的下标。
整个有序序列(两数组归并后),记为 M,先假设 M.length 为奇数
假设 a 在 M 中的实际下标为 la,那么所有小于等于 la 的下标值我们可以记为 La。这个La,我们称其为 a 在 M 中的下标下界
在 M 中的中位数下标为 target,即我们要的就是 M[target]
对于这个题目存在下述两个事实:

  1. 假设 a = max(a, b),则 La = i+j+1(注意如果 B[j+1] >= a,则la == La;如果B[j+1] < a,则la >= La)
  2. 如果 La > target,则在 A 数组中,a 后面的所有元素包括 a 都不可能是 M[target]。
    如果 La <= target,则在 B 数组中,b 前面的所有元素包括 b 都不可能是 M[target](这个难理解一点,给出证明)。

关于第二点第二条的证明如下:
因为 B 数组中 b 前面的元素只有 j 个,A 数组中在 b 前面的元素最多有 i 个,所以 b 在 M 中的下标最大只能是 i+j。
即 lb <= i+j < i+j+1 = La,即 lb < La
又 La <= target,所以 lb < target,b 都不可能了,b 前面的元素更不可能了。

所以当 La > target,指向 a 的指针前移。当 La <= target,指向 b 的指针后移。

那么我们可以设计一个类似二分查找的算法,移动指针的时候每次移动待搜索区间的一半。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值