There are two sorted arrays nums1 and nums2 of size m and n respectively.
Find the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)).
You may assume nums1 and nums2 cannot be both empty.
Example 1:
nums1 = [1, 3] nums2 = [2] The median is 2.0
Example 2:
nums1 = [1, 2] nums2 = [3, 4] The median is (2 + 3)/2 = 2.5
1、记住,有序数组一定用二分查找法,两个有序数组就控制变量,二分查找短的,另一个根据要求二分查找
2、两个有序数组找第K大的数就不断删除前面p个小于K的数,对于删除之后的数组查找第K-p的数,一直删到K==1
3、对于无序数组查找第K大的数两个思路:
- 第一个快速排序划分法,当划分位置position==K-1则返回A[position-1]
- 第二个思路是堆排序,不断建立大顶堆,执行n-k+1次
本题提供两个接法:
- 第一个控制变量二分查找,只二分查找短的数组,长的数组控制变量 C1+C2=k
- 第二个接法将题目变形成在两个有序数组中查找第K大的数,通过删去前面p个数,查找第K-p的数,不断缩短两个数组的长度。
#include<iostream>
#include<vector>
using namespace std;
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int n = nums1.size(), m = nums2.size();
if (n > m)
return findMedianSortedArrays(nums2, nums1);
int total = m + n;
if (total & 0x1)
return findKth(nums1, nums2, total / 2+1);
else
return (findKth(nums1, nums2, (total) / 2)+findKth(nums1, nums2, total / 2+1))/2.0;
}
public:
int findKth2(vector<int> &nums1, vector<int> &nums2, int k)
{
//方法一,二分查找,固定一个数组二分,另一个根据等式c1+c2=k确定数组二切分点
//注意边界,这里保证n<m恒成立,二分小的数组,防止大的越界
//保证两个数组和为奇数 可以虚拟加入#
//通过index的映射虚拟加入,(index-1)/2
//#号为切割点,左边为(index-1)/2 右边为index/2
//理想状态下L1<R2 L2<R1 并且此时C1+C2=k 那么第k个数是min(L1,L2)
//如果L1>R2 说明L1应该往左移变小 C1左边二分
//如果L2>R1 说明R1应该右移变大 C1右边二分
//为了满足最后的判断条件
//C1==0时,说明数组1的值太大了在2中搜索就好了,但是要将L1置为一个小数
//C1==2*n时,说明数组1的值太小了,在2中搜索就好了,但是要将R1置为大数
//C2==0 将L2置为小数
//C2==2*m,将R2置为大数
//最后二分结束,出现L1==R2 L2==R1,max(L1,L2)即为第k个数
int n = nums1.size(), m = nums2.size();
if (n > m)
//保证第一个为短的
return findKth2(nums2, nums1, k);
if (n == 0)
return nums2[k - 1];
if (m == 0)
return nums1[k - 1];
if (k > m + n)
return -1;
int L1, L2, R1, R2, C1, C2;
int low = 0, high = 2 * n;
while (low <= high)
{
//首先确定数组切割位置
C1 = (low + high) / 2;
//假装认为原来为n+m,现在为2n+2m+2
C2 = 2*k - C1; //数组扩充 所以第k个数变成第2*k个数
C2 = (C2 <= 0 ? 0 : C2);
C2 = (C2 >= m * 2 ? m * 2 : C2); //当C2切割的时候越界,需要限制一下大小,这时候说明数很靠后
//给切分数组之后的左右部分赋值
L1 = (C1 <= 0 ? INT_MIN : nums1[(C1 - 1) / 2]);
R1 = (C1 >= 2 * n ? INT_MAX : nums1[C1 / 2]);
L2 = (C2 <= 0 ? INT_MIN : nums2[(C2 - 1) / 2]);
R2 = (C2 >= 2 * m ? INT_MAX : nums2[C2 / 2]); //分割边界 即#2#4#6# 当C2=6时,L=6 R=999
//开始更改切割位置
if (L1 > R2)
//L1大了,应该往左边找
high = C1 - 1;
else if (L2 > R1)
//R1小了往右边找
low = C1 + 1;
else
//此时L1==R2 L2==R1
//此时第k个数找到了 max(L1,L2)
break;
}
return L1>L2? L1:L2;
}
int findKth(vector<int> &nums1, vector<int> &nums2, int k)
{
//递归求解,如果求数组第k个数,前面删去x个,那么就是求删数之后第k-x个的数
//仍然采用数组切分的方法,但是不是二分切割
//在A数组中中切第k/2的数,在B数组中也也切分第k/2的数
//1、如果满足A[k/2-1]<B[k/2] && B[k/2-1]<A[k/2]
//或者A[k/2-1]==B[k/2-1](一定存在A[k/2-1]<B[k/2] && B[k/2-1]<A[k/2])
//则max(A[k/2-1], B[k/2-1])即为第k的数
//2、但是如果存在A[k/2-1]<B[k/2-1]
//说明A[0]...A[k/2-1]这k/2个数,一定要会小于第k个数
//因为A[k/2-1](有k/2个数), B[k/2-1](有k/2个数),
//这两个最大值可能是第k个数,那么小的那个最多可能是第k-1的数
//A[k/2-1]<B[k/2-1]那么肯定的,A[k/2-1]<=M[k-2](M为归并排序结果)
//所以,我们可以将这k/2个数删掉,删掉前k/2个数,那么就相当于找第k-k/2的数
//3、当A[k/2-1]>B[k/2-1]
//显然B[k/2-1]<=M[k-2]恒成立,删掉B中前k/2个数即可
//所以可以递归进行递归删除前面较小的数
//一直删除较少的数,那么A的长度、B的长度、k值就会一直变小
//那么当A.size()==0 A删完了,那么第k个数一定在B中 为 B[k-1]
//同理B.size()==0 B删完了,那么第k个数一定在A中, 为 A[k-1]
//如果k比较垃圾,k删完了 那么就是找第0个数
//min(A[0], B[0])
//之前的分析都是构建在A(n)、B(m)数组个数都是大于k/2的假设上面建立的
//如果说某一个数组,A[k/2-1] 或者B[k/2-1]越界了
//那么就不能用k/2划分数组了,就要用控制变量法
//一定要满足划分C1+C2=k才能保障找到的
//A[C1]与B[C2]进行对比是和M[k-2]与M[k-1]相关的对比
//如果大的越界 即大的数组元素个数都 < k/2 那就没得玩了,直接返回-1报错拉到
//此时显然n+m<k
//如果小的越界了,还能玩一玩 假设A小,n<m&&n<k/2
//那我们就查看A中最大的数A[A.size()]是否满足第k个数的条件
//如果不满足直接将A全部删除,第k个数变成k-A.size() 在B中 B[k-A.size()-1]
//所以这时候划分为C1=n, C2=k-n
int n = nums1.size(), m = nums2.size();
if (n + m < k || k < 1)
//俩个都太短了,垃圾
return -1;
if (n > m)
//保证第一个为短的
return findKth(nums2, nums1, k);
if (n == 0)
//如果一个被删完了,那么第k个数在领个数组里
return nums2[k - 1];
if (m == 0)
return nums1[k - 1];
if (k == 1)
//k比较垃圾被删完了
//返回俩个数组第一个较小的数
return nums1[0] < nums2[0] ? nums1[0] : nums2[0];
//是时候表演真正的技术了 开始递归
int pa, pb; //俩个数组的左半部分
pa = (k / 2 < n ? k / 2 : n); //防止越界
pb = k - pa; //控制变量
if (nums1[pa - 1] == nums2[pb - 1])
//两个一样,则返回较大的 其实一样大,两个都行
return nums1[pa - 1];
else if (nums1[pa - 1] < nums2[pb - 1])
//nums1数组前半部分比较小,删去
return findKth(vector<int> (nums1.begin()+pa, nums1.end()), nums2, k - pa);
else
return findKth(nums1, vector<int>(nums2.begin()+pb, nums2.end()), k - pb);
}
};
void main()
{
/*vector<int> nums1 = { 1,2,3,4,11,22 };
vector<int> nums2 = { 0,2 };*/
vector<int> nums1 = { 10000001 };
vector<int> nums2 = { 10000000 };
Solution *s = new Solution();
double k = s->findMedianSortedArrays(nums1, nums2);
//int k = s->findKth(nums1, nums2, 10);
printf("%.3lf\n", k);
delete s;
}