4. Median of Two Sorted Arrays
最近又遇到这道题,就查了一下资料,找到一个好方法但是原文代码格式有点问题,顺便在这儿做做整理
大致思路
我们有两个已排序序列,需要找到中位数,在这里我们改一下,需要找到第k位数(奇数偶数之后讨论),嫌麻烦可以直接看代码里的注释
命名
-
我们把两个有序序列称为
nums1
nums2
,长度分别为m
n
-
从
nums1
nums2
中取出来的子序列分别称为l1
l2
,长度分别为count1
count2
-
l1
l2
中各自的最大数分别为a
b
(即子序列最后一个数)
如图:
过程
首先我们明确,m+n为奇数数,中位数即为第(m+n)/2,若m+n为偶数,中位数是第(m+n)/2、第(m+n)/2+1个数的平均数
我们先不管合并后的序列,在两个序列中抽取k个数,而为保持divide&conquer的效率,每个序列抽取k/2
取出的两个子序列(长度和为k
)l1
、l2
中,若l1
的最大值a
小于l2
的最大值b
,则这k个数按顺序排列后,l1
中的任何一个数不可能排在第k位(因为l2
中至少有一个数b
排在a
之后)
所以,我们可以把l1
完全舍弃,(私以为nums2中的除l2部分也可以一并舍去,在findKth中添加数组边界i1 j1 i2 j2即可)此外,需要注意,舍弃掉l1
后我们需要更新k
值(k-count1
)
若a>b
,情况类似
若a==b
,如果两者相等,则按序排列两个子数组后,第k位的数即为子序列的最大数
这里可能会误以为整个序列的kth数一定是两个子序列的最大数a、b中的一个,但如果a<b,a后面的数仍可能小于b,即b也可能不是整个序列的kth数
代码
class Solution
{
public:
double findKth(vector<int>& nums1,vector<int>& nums2,int i,int j,int k)
{
//cout<<i<<" "<<j<<" k:"<<k<<endl;
int m=nums1.size()-i;
int n=nums2.size()-j;
//始终假设nums2的长度大于等于nums1的长度,见后面的注释
if (m > n)
return findKth(nums2, nums1, j,i, k);
//只有nums2,返回其kth元素
if (m == 0)
return nums2[j+k - 1];
//需要返回第一个元素,这种情况直接比较
if (k == 1)
return min(nums1[i], nums2[j]);
/*
我们先不管合并后的序列,在两个序列中抽取k个数,
为保持divide&conquer的效率,每个序列抽取k/2
*/
int count1 = min(k / 2, m);
int count2 = k - count1; //nums2的长度大于nums1的长度
/*
取出的两个子序列(count和为k)l1、l2中,若l1的最大值小于l2的最大值,
则这k个数按顺序排列后,l1中的任何一个数不可能排在第k位(因为l2中至少有一个数排在l1最大值之后),
所以,我们把l1完全舍弃,(私以为nums2中的除l2部分也可以一并舍去,在findKth中添加数组边界i1 j1 i2 j2即可)
此外,需要注意,舍弃掉l1后我们需要更新k值(k-count1)
*/
if (nums1[i+count1 - 1] < nums2[j+count2 - 1])
//舍弃l1,更新k值
return findKth(nums1, nums2, i+count1, j,k - count1);
/*
同理,如果l1中的最大数大于l2中的最大数,舍去全部l2(也可以舍弃nums1中除l1的部分??)
*/
else if (nums1[i+count1 - 1] > nums2[j+count2 - 1])
return findKth(nums1,nums2,i,j+count2, k - count2);
/*
如果两者相等,则按序排列两个子数组后,第k位的数即为子序列的最大数,
这里可能会误以为整个序列的kth数一定是两个子序列的最大数a、b中的一个,
但如果a<b,a后面的数仍可能小于b,即b也可能不是整个序列的kth数
*/
else
return nums1[i+count1 - 1];
}
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2)
{
int m=nums1.size();
int n=nums2.size();
int total = m + n;
double result=0;
//是奇数(最后一个二进制位是1) 1 2 3 => 2
if (total & 0x1)
result=findKth(nums1,nums2,0,0, total / 2 + 1);
//偶数 1 2 4 5 => (2+4)/2
else{
//cout<<findKth(nums1, nums2,0,0, total / 2)<<" "<<findKth(nums1, nums2,0,0, total / 2 + 1)<<endl;
int r1=findKth(nums1, nums2,0,0, total / 2);
int r2=findKth(nums1, nums2,0,0, total / 2 + 1);
//cout<<"result: "<<r1<<" "<<r2<<endl;
result=(r1+r2) / 2.0;
}
return result;
}
};