题目描述:
这题还是有点难度的,想了比较久,最开始的时候没看见复杂度要求,心想不就是把两个数组合并起来就可以了么,为什么tag是困难,结果看见复杂度要求,才知道要二分做,一开始二分又想错了方向,尝试建立两个数组各自的中位数的联系,结果写了一大堆发现自己走错了路,最后参考别人的思路才做出。
解题思路:本题核心函数就一个: double findKth(vector<int> &nums1, int i, vector<int> &nums2, int j, int k)
表示找到两个排序数组最后合并排序后的第k个数,其中 int i和int j表示还没搜索过的元素的起点(比如初始的时候i和j都等于0)
那么怎么在没有合并的情况下,怎么找到第k个数呢?
思路是这样的:判断 nums1 数组中第k/2个数 和 nums2 数组中第k/2个数的大小关系,比如 nums1[k/2-1] < nums2[k/2-1],那么第k个数肯定不在nums1[0] 到 nums1[k/2-1]这个区间内,因此,直接把nums1[0] 到 nums1[k/2-1]这个区间排除,在对剩下元素的递归处理。这样说可能有点抽象,举个具体的例子来说明下。
比如:nums1 里面有 10 个元素,nums2里面也有 10 个元素,现在问题要求第16个元素是多少( k = 16)?
首先,k/2 = 8 ,找到nums1中第8个元素nums1[7],和nums2中第k - k/2 = 8个元素nums2[7],如果nums1[7]<nums2[7],那么答案肯定不在nums1[0]到num1[7]之间,因为即使最坏的情况,nums2[0]到nums2[6]全在nums1[0]到nums1[7]里面,那么这里面一共也才15个元素 < (k = 16),所以直接把nums1[0]到nums1[7] 排除掉,对剩下的elems递归处理。
本题的核心算法就是上面一段,还有个难点就是关于坐标的换算,我主要就是卡在了这个地方,具体看以下代码,可以结合上一段举的例子来理解:
class Solution {
public:
/**
* @param A: An integer array.
* @param B: An integer array.
* @return: a double whose format is *.5 or *.0
*/
double findMedianSortedArrays(vector<int> A, vector<int> B) {
// write your code here
int sizeA = A.size(), sizeB = B.size();
if (sizeA <= 0 && sizeB <= 0) {
return 0;
}
int total = sizeA + sizeB;
if (total % 2 == 1) {
return findKth(A, 0, B, 0, total / 2 + 1);
}
else {//偶数则取中间两个值的平均数
return (findKth(A, 0, B, 0, total / 2) + findKth(A, 0, B, 0, total / 2 + 1)) / 2;
}
}
double findKth(vector<int> &nums1, int i, vector<int> &nums2, int j, int k) {
//一定要保证nums1的长度小于nums2的长度,不然对nums2的访问可能会越界
if (nums1.size() - i > nums2.size() - j) {
return findKth(nums2, j, nums1, i, k);
}
// 判断小的数组是否为空,为空的话,直接在另一个数组找第K个即可
if (nums1.size() == i) {//递归结束情况1
return nums2[j + k - 1];
}
// 当K = 1时,表示我们要找第一个元素,只要比较两个数组的第一个元素,返回较小的那个即可
if (k == 1) {//递归结束情况2
return min(nums1[i], nums2[j]);
}
//pa表示在nums1中定位的元素的坐标加上一(下面访问的时候会减掉),pb同理
int pa = min(i + k / 2, int(nums1.size())), pb = j + k - (pa - i);//nums1中的元素不够k/2的话,那么就有多少取多少(nums1.size()) //pa - i :在nums1中已取得元素的个数 //k - (pa - i):剩下的还需要在nums2中取得元素的个数
if (nums1[pa - 1] < nums2[pb - 1]) {
return findKth(nums1, pa, nums2, j, k - pa + i);
}
else if (nums1[pa - 1] > nums2[pb - 1]) {
return findKth(nums1, i, nums2, pb, k - pb + j);
}
else {
return nums1[pa - 1];//相等的话说明说明这就是答案,直接返回即可
}
}
};
还有为什么要二分呢,其实你nums1中取k/3个,nums2中取2k/3个,然后比较nums1[k/3-1]和nums2[2k/3-1]也可以,因为明显二分最快。