两个排序数组的中位数
给定两个大小为 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
分析
复杂度为O(m+n)的解法:归并排序。实际就是顺次比较两个数组的元素,取第(nums1.length+nums2.length)/2(下文中以k代替)个元素即可。
考虑O(log(m+n))的复杂度,采用二分的思想:两个数组分别取第k/2个元素(此时先不考虑边界问题),nums1[k/2],nums2[k/2],判断二者的值
- nums[k/2] < nums2[k/2] :此时中位数位于数组1的右半段和数组2的左半段中,考虑此时数组1左端有k/2个小于中位数的数,数组2右端有k/2个大于中位数的数,因此将两端直接剔除。
- nums[k/2] > nums2[k/2]:此时中位数位于数组1的左半段和数组2的右半段中,和上面情况相反。
- nums[k/2] == nums2[k/2]:此时中位数恰好等于任一数组的中点值
详细则需要考虑边界,即数组长度是否大于k/2的问题。
代码
class Solution {
//找第K小元素的变种
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int size1 = nums1.length,size2 = nums2.length;
int size = size1+size2;
//分奇偶处理
if(size%2 == 1){
return findKth(nums1,0,nums2,0,size/2+1);
}else{
return (findKth(nums1,0,nums2,0,size/2) + findKth(nums1,0,nums2,0,size/2+1))/2;
}
}
/*
nums1:数组1
i:数组1起始位置
nums2:数组2
j:数组2起始位置
k:第k小元素
*/
private double findKth(int[] nums1,int i,int[] nums2,int j,int k){
//始终让数组1的长度小
if(nums1.length-i> nums2.length - j){
return findKth(nums2,j,nums1,i,k);
}
//数组1(小的数组)是否为空,空的话,直接在另一个数组中找第K个
if(nums1.length == i){
return nums2[j+k-1];
}
//k==1时,表示找第一个元素,比较两个数组第一个元素,返回小的即可
if(k==1){
return Math.min(nums1[i],nums2[j]);
}
//数组1中取mid1个元素,取k/2和数组长度的小者
int mid1 = Math.min(i+k/2,nums1.length);
//在数组2中取剩余的 k-mid1个元素,
int mid2 = j + k - mid1 + i;
//判断中位数的位置
if(nums1[mid1-1] < nums2[mid2 - 1]){
//此时中位数位于数组1右半段和数组2左半段
return findKth(nums1,mid1,nums2,j,k - mid1 + i);
}else if(nums1[mid1 - 1]> nums2[mid2 - 1]){
//此时中位数位于数组1左半段和数组2右半段
return findKth(nums1,i,nums2,mid2,k-mid2+j);
}else{
//此时恰好取到中位数
return nums1[mid1-1];
}
}
}