寻找排序数组的中位数
描述:
给定两个大小为 m 和 n 的有序数组 nums1 和 nums2。
请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。
你可以假设 nums1 和 nums2 不会同时为空。
合并方法O(m+n)
最简单的思路,直接将两个数组合并,时间复杂度为O(m+n),然后判断合并后总数为奇数还是偶数,直接找到中位数。
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int m = nums1.size();
int n = nums2.size();
vector<int> nums;
int i=0;
int j=0;
if (m == 0)
{
if (n % 2 == 0)
return (nums2[n / 2 - 1] + nums2[n / 2]) / 2.0;
else
return nums2[n / 2];
}
if (n == 0)
{
if (m % 2 == 0)
return (nums1[m / 2 - 1] + nums1[m / 2]) / 2.0;
else
return nums1[m / 2];
}
for(; i<m && j<n;)
{
if(nums1[i]<nums2[j])
{
nums.push_back(nums1[i]);
i++;
}
else
{
nums.push_back(nums2[j]);
j++;
}
}
while(i<m)
{
nums.push_back(nums1[i++]);
}
while(j<n)
nums.push_back(nums2[j++]);
if((m+n)%2==0)
return (nums[(m+n) / 2 - 1] + nums[(m+n) / 2]) / 2.0;
else
return nums[(m+n)/2];
}
};
这种方法简单,但是效率不高。
O(k)的方法
不过我们仅仅需要第 k 大的元素,是不需要“排序”这么复杂的操作的。可以用一个计数器, 记录当前已经找到第 m 大的元素了。同时我们使用两个指针 pA 和 pB,分别指向 A 和 B 数组的第 一个元素,使用类似于mergesort的原理,如果数组A当前元素小,那么 pA++,同时 m++;如果数 组B当前元素小,那么 pB++,同时 m++。最终当 m 等于 k 的时候,就得到了我们的答案,O(k) 时 间,O(1) 空间。
O(log(m+n))的方法
假设 A 和 B 的元素个数都大于 k/2,我们将 A 的第 k/2 个元素(即 A[k/2-1])和 B 的第 k/2 个元素(即 B[k/2-1])进行比较,有以下三种情况(为了简化这里先假设 k 为偶数,所得到的结论 对于 k 是奇数也是成立的):
• A[k/2-1] == B[k/2-1]
• A[k/2-1] > B[k/2-1]
• A[k/2-1] < B[k/2-1]
如果 A[k/2-1] < B[k/2-1],意味着 A[0] 到 A[k/2-1] 的肯定在 A∪B 的 topk 元素的范围内,换句话说,A[k/2-1] 不可能大于 A∪B 的第 k 大元素。
因此,我们可以放心的删除 A 数组的这 k/2 个元素。同理,当 A[k/2-1] > B[k/2-1] 时,可 以删除 B 数组的 k/2 个元素。
当 A[k/2-1] == B[k/2-1] 时,说明找到了第 k 大的元素,直接返回 A[k/2-1] 或 B[k/2-1] 即可。
因此,我们可以写一个递归函数。那么函数什么时候应该终止呢?
• 当 A 或 B 是空时,直接返回 B[k-1] 或 A[k-1];
• 当 k=1 是,返回 min(A[0], B[0]);
• 当 A[k/2-1] == B[k/2-1] 时,返回 A[k/2-1] 或 B[k/2-1]
(这一段转载而来)
class Solution {
public:
int find_kth(vector<int> nums1, int start1, int end1, vector<int> nums2, int start2, int end2, int k)
{
//保证m一定是小于等于n的
int len1 = end1-start1+1;
int len2 = end2-start2+1;
if(len1>len2)
return find_kth(nums2, start2, end2, nums1, start1, end1, k);
if(len1==0)
return nums2[start2+k-1];
if(k==1)
return min(nums1[start1], nums2[start2]);
//比较第k/2个元素的大小
int i = start1 + min(len1, k/2)-1;
int j = start2 + min(len2, k/2)-1;
if(nums1[i]>nums2[j])
return find_kth(nums1, start1, end1, nums2, j+1, end2, k-(j-start2+1));
else
return find_kth(nums1, i+1, end1, nums2, start2, end2, k-(i-start1+1));
}
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int n = nums1.size();
int m = nums2.size();
int left = (n+m+1)>>1;
int right = (n+m+2)>>1;
return (find_kth(nums1, 0, n-1, nums2, 0, m-1, left)+find_kth(nums1, 0, n-1, nums2, 0, m-1, right))/2.0;
}
};