力扣—寻找两个有序数组的中位数,下面看一下题目
题目叙述
给定两个大小为 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
给出代码:
class Solution {
public:
//寻找2个数组中第k小的数,注意是2个数组, k = 7
int getKthElement(const vector<int>& nums1, const vector<int>& nums2, int k) {
/* 主要思路:要找到第 k (k>1) 小的元素,那么就取 pivot1 = nums1[k/2-1] 和 pivot2 = nums2[k/2-1] 进行比较
* 这里的 "/" 表示整除
* nums1 中小于等于 pivot1 的元素有 nums1[0 .. k/2-2] 共计 k/2-1 个
* nums2 中小于等于 pivot2 的元素有 nums2[0 .. k/2-2] 共计 k/2-1 个
* 取 pivot = min(pivot1, pivot2),两个数组中小于等于 pivot 的元素共计不会超过 (k/2-1) + (k/2-1) <= k-2 个
* 这样 pivot 本身最大也只能是第 k-1 小的元素
* 如果 pivot = pivot1,那么 nums1[0 .. k/2-1] 都不可能是第 k 小的元素。把这些元素全部 "删除",剩下的作为新的 nums1 数组
* 如果 pivot = pivot2,那么 nums2[0 .. k/2-1] 都不可能是第 k 小的元素。把这些元素全部 "删除",剩下的作为新的 nums2 数组
* 由于我们 "删除" 了一些元素(这些元素都比第 k 小的元素要小),因此需要修改 k 的值,减去删除的数的个数
*/
int m = nums1.size(); //4
int n = nums2.size(); //9
int index1 = 0, index2 = 0;
while (true) {
// 边界情况
//num1数组为空
if (index1 == m) {
return nums2[index2 + k - 1];
}
//num2数组为空
if (index2 == n) {
return nums1[index1 + k - 1];
}
//两个数组总共2个元素
if (k == 1) {
return min(nums1[index1], nums2[index2]);
}
// 正常情况
int newIndex1 = min(index1 + k / 2 - 1, m - 1); //0+2,3 取2--2,0+1,3--取1--取3
int newIndex2 = min(index2 + k / 2 - 1, n - 1); //0+2,8 取2---3+1,8 --取4--3
int pivot1 = nums1[newIndex1];
int pivot2 = nums2[newIndex2];
if (pivot1 <= pivot2) {
k -= newIndex1 - index1 + 1; //k-=1-0+1=2
index1 = newIndex1 + 1; //index1 = 2 +1 = 3
}
else {
k -= newIndex2 - index2 + 1; //k-=2-0+1 k =4 --- k-=3-3+1 = 1
index2 = newIndex2 + 1; // index2 = 3--index2=4
}
}
}
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int totalLength = nums1.size() + nums2.size();
if (totalLength % 2 == 1) { //奇数
return getKthElement(nums1, nums2, (totalLength + 1) / 2);
}
else {//偶数
return (getKthElement(nums1, nums2, totalLength / 2) + getKthElement(nums1, nums2, totalLength / 2 + 1)) / 2.0;
}
}
};
看到上面的解释挺多的,只看也看不出啥,还是自己推敲一遍。例如有如下2个数组
num1: 1 3 4 9
num2: 1 2 3 4 5 6 7 8 9
总和为13,则k=7
m = 4,n = 9,index1=0,index2=0
newIndex1= min(0+7/2-1,4-1) = 2,newIndex2 = min(0+7/2-1,9) = 2(newIndex1相当于调位之后的下标,index1是递增的下标)
pivot1=num1[2] =4 , pivot2=num2[2]=3
4 > 3 , k -= 2-0+1 = 4 , index2 = 2 + 1 = 3(此时num1[2]是第6小的,但现在要找第7小的,只需要找到比num1[2]大1个的)
//—————————————————————-
newIndex1 = min(0+4/2-1,4-1) = 1 , newIndex2 = min(3+4/2-1,9) = 4(这里num2一下跳了2个位,因为是根据k值进行二分,不用每次跳一个位,这样减少遍历个数)
pivot1 = num1[1] = 3 , pivot2 = num2[4] = 5
3 < 5 , k-=1-0+1=2 , index1 = 2
//————————————————
newIndex1 = min(2+2/2-1,4-1) = 2 , newIndex2 = min(3+2/2-1,9) = 3
pivot1 = num1[4] = 3 , pivot2 = num2[3] = 4
4 == 4 ,k-=2-2+1=1 ,然后取个4,k=0,则找到了。题目用k/2进行二分查找,根据k/2选择跳位比较,然后在慢慢缩小范围。