寻找两个有序数组的中位数(Java)

寻找两个有序数组的中位数

题目:

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

思想:

找中位数只需把两数组进行排序,找到中位数即可。因为是有序数组,因此,可以对两个数组元素进行比较从而完成原地排序,并不需要新建一个新的数组,并且只需要排序总长度len的前一半即可。因为当总长度为偶数时,中位数等于nums[len/2]和nums[len/2-1]的平均值,当总长度为奇数时,中位数nums[len/2]。代码后面的注释很详细。

代码:

class Solution {
    public double findMedianSortedArrays(int[]nums1,int[] nums2) {
	int num = 0;                   
	int prenum = 0;        //当总长度为偶数,中位数等于num和prenum的平均值,当总长度为奇数,中位数为num
	double d = 0;          //返回最终的中位数
	int len1 = nums1.length;          
	int len2 = nums2.length;        
	if(len1 == 0){         //首先判断nums1或nums2为空的情况,返回不为空的数组的中位数即可 	
	    if(len2%2 == 0)               
	        d = (double)(nums2[len2/2]+nums2[len2/2-1])/2; 
	    else                 
	        d = nums2[len2/2];            
	    return d;                    
	}        
	else if(len2 == 0){
	    if(len1%2 == 0)
	        d = (double)(nums1[len1/2]+nums1[len1/2-1])/2;
	    else                 
	        d = nums1[len1/2];
	    return d;        
	}        
	int i = 0;    //当nums1和nums2数组都不为空时,设num1和nums2的索引分别为i和j,并初始化为0 
	int j = 0;        
	int number = 0;    //记录当前nums1和nums2的参与排序的元素个数        
	int mid = (len1+len2)/2;     //当总长度为偶数,中位数为索引为mid和mid-1的元素的平均数,当总长度为奇数,中位数取索引为mid的元素,因此只需要找到前mid个最小的数排序即可       
	while(i<len1&&j<len2&&number<=mid){ //当nums1或nums2遍历完或找到计算中位数所需的位数即退出循环
	    if(nums1[i] <= nums2[j]){      //num1当前元素小时,nums1当前索引加一,并且number加一 
	        prenum = num;                
	        num = nums1[i];                
	        i++;                
	        number++;            
	    }            
	    else{      //num2当前元素小时,nums2当前索引加一,并且number加一                
	    	prenum = num;                
	    	num = nums2[j];                
	    	j++;                
	    	number++;            
	    }        
	}                   
	if(i == len1){     //判断如果nums1全部遍历完了,接下来只需遍历nums2即可
	    while(number<=mid){
	        prenum = num;                
	        num = nums2[j];                
	        j++;                
	        number++;
	     }        
	 }       
	 else if(j == len2){      //判断如果nums2全部遍历完了,接下来只需遍历nums1即可 
	     while(number<=mid){
	         prenum = num;                
	         num = nums1[i];               
	         i++;                
	         number++;            
	     }        
	 }         
	 if(number == mid+1){    //最后找到找到中位数所需的所有位数后计算中位数            
	     int remain = (len1+len2)%2;
	     if(remain == 0)                
	         d = (double)(prenum+num)/2;
	     else                
	         d = num;        
	 }        
	 return d;     //返回中位数     
     }
}
题目描述: 给定两个大小为 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 解题思路: 题目要求时间复杂度为 O(log(m+n)),很明显是要用到二分查找的思想。 首先,我们需要确定中位数的位置。对于两个长度分别为 m 和 n 的有序数组,它们的中位数位置为 (m+n+1)/2 和 (m+n+2)/2,因为当 m+n 为奇数时,这两个位置的值是相同的;当 m+n 为偶数时,这两个位置的值分别为中间两个数。 然后,我们需要在两个数组中分别找到第 k/2 个数(k 为中位数位置),比较它们的大小,如果 nums1[k/2-1] < nums2[k/2-1],则说明中位数位于 nums1 的右半部分和 nums2 的左半部分之间,此时可以舍弃 nums1 的左半部分,将 k 减去 nums1 的左半部分的长度,继续在 nums1 的右半部分和 nums2 的左半部分中寻找第 k/2 个数;反之,如果 nums1[k/2-1] >= nums2[k/2-1],则说明中位数位于 nums1 的左半部分和 nums2 的右半部分之间,此时可以舍弃 nums2 的左半部分,将 k 减去 nums2 的左半部分的长度,继续在 nums1 的左半部分和 nums2 的右半部分中寻找第 k/2 个数。 当 k=1 时,中位数两个数组中的最小值。 Java代码实现: class Solution { public double findMedianSortedArrays(int[] nums1, int[] nums2) { int m = nums1.length; int n = nums2.length; int k = (m + n + 1) / 2; double median = findKth(nums1, 0, m - 1, nums2, 0, n - 1, k); if ((m + n) % 2 == 0) { int k2 = k + 1; double median2 = findKth(nums1, 0, m - 1, nums2, 0, n - 1, k2); median = (median + median2) / 2; } return median; } private double findKth(int[] nums1, int start1, int end1, int[] nums2, int start2, int end2, int k) { int len1 = end1 - start1 + 1; int len2 = end2 - start2 + 1; if (len1 > len2) { return findKth(nums2, start2, end2, nums1, start1, end1, k); } if (len1 == 0) { return nums2[start2 + k - 1]; } if (k == 1) { return Math.min(nums1[start1], nums2[start2]); } int i = start1 + Math.min(len1, k / 2) - 1; int j = start2 + Math.min(len2, k / 2) - 1; if (nums1[i] > nums2[j]) { return findKth(nums1, start1, end1, nums2, j + 1, end2, k - (j - start2 + 1)); } else { return findKth(nums1, i + 1, end1, nums2, start2, end2, k - (i - start1 + 1)); } } }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值