4. 寻找两个有序数组的中位数
给定两个大小为 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))的时间复杂度,这就代表不能使用简单的数组合并排序然后取数字的方法,而log级别的时间复杂度就代表需要使用二分法。获取n个的中位数,可以理解为获取第(n+1)/2个数字和第(n+2)/2个数字,对于奇数这两个数字是同一个数字,而偶数则为两个相邻的数字。那么就将题目的获取中位数转变为获取第k小数字。
那么如何获取两个数组中第k小的数字呢?我们可以使用二分法,每次对两个数组的第k/2个数字进行比较,因为数组是有序的,因此每一次比较后,相对较小的那个数字与其所在数组之前的所有数字均不可能为第k小的数字,于是每一次比较都可以将k减小一半
例如:
a[1,3,5,7,9]
b[2,4,6,8,10]
k = 5
k/2=2
a[2]<b[2]
因此1、3两个数字不是第k小的数字,因此将其排除,情况就变成了
在a[5,7,9]与b[2,4,6,8,10]两个数组中寻找第(k-2)=3小的数字。
之后k分别为3、2、1,情况变为从a[5,7,9]与b[6,8,10]中寻找第1小的数字
那么便可以得到第5小的数字为5
下面是代码:
class Solution {
public:
//获取中位数,奇数个可以理解为获取第k/2小的数字
//偶数则为获取第k/2个数字与k/2+1个数字
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int n = nums1.size();
int m = nums2.size();
//将奇偶统一,奇数a与b相等,偶数则相差1
int a = (n+m+1)/2;
int b = (n+m+2)/2;
return (findKthmin(nums1,nums2,0,0,a)+findKthmin(nums1,nums2,0,0,b))/2;
}
double findKthmin(vector<int>& nums1,vector<int>& nums2,int a,int b,int k){
int n = nums1.size()-a;
int m = nums2.size()-b;
//保证nums1个数均比nums2少
if(n>m) return findKthmin(nums2,nums1,b,a,k);
//如果nums1全部排除,那么直接从nums2中选择对应数字返回
if(n==0) return nums2[b+k-1];
//k为1时,选择较小的一个返回
if(k==1) return min(nums1[a],nums2[b]);
//获取待比较数字的下标,注意-1
//将n与k/2比较,防止数组越界可能
//注意此处两个待比较数字的排除范围不一定相等,不能当做k/2来处理
int i = a + min(n,k/2) -1;
int j = b + min(m,k/2) -1;
//如果第一数组比第二数组的大,那么代表第二数组比较数字之前的数字均不可能是第k小的数字
if(nums1[i]>nums2[j]){
//排除的是第二数组,因此第一数组仍然从本次比较的位置开始,仍然为a
//下标b所在位置的数字同样在排除范围内,因此下次从j+1开始
//排除的数字个数由b的位置决定,排除了(j-b)+1个数字
return findKthmin(nums1,nums2,a,j+1,k-min(m,k/2));
}
else{
//与之相反
return findKthmin(nums1,nums2,i+1,b,k-min(n,k/2));
}
}
};
本文介绍了一种在O(log(m+n))时间复杂度内找到两个有序数组中位数的算法,通过二分查找法,每次排除一半的元素,直至找到第k小的元素。
1188

被折叠的 条评论
为什么被折叠?



