题目
- 给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的中位数。
LeetCode链接
题解
- 这题更通用的形式是,给定两个排序好的数组,找到两者所有元素中第k小的元素,且所有元素是允许重复的;
- 方法1:直接merge两个数组,然后求第k小的元素,算法复杂度O(m+n);
- 方法2:类似于merge sort,分别使用两个指针pA和pB,如果数组A当前元素小,那么pA++,同时m++;如果数组B当前元素小,那么pB++,同时m++。当m等于k时,就找到了第k小的元素;
- 方法3:找第k小的数,如果每次都能够删除一个一定比k小的数,那么只需要删除k次即可。如何利用有序数组呢,第一次删k/2个,第二次删k/4个,……,需要注意的是输出的个数不能长于当前数组的长度,具体如下:假设A和B的元素个数都大于k/2,将A数组的A[k/2-1]和B数组的B[k/2-1]进行比较,存在3种情况。
- A[k/2-1]<B[k/2-1],说明A[k/2-1]不可能是第k小数,因为即使B[0]、……、B[k/2-2]都小于A[k/2-1],也只能说A[k/2-1]是第k-1小数,即A[0]、……、A[k/2-2]这些数都必定小于第k小数,可以被删除。此外,此时也不能说明B[k/2-1]是第k小数,因为可能存在A[k/2]<B[k/2-1]的情形;
- A[k/2-1]==B[k/2-1],由于允许重复,故此时找到了第k小数,为A[k/2-1]或B[k/2-1];
- A[k/2-1]>B[k/2-1],与情况1类似。
- 删除了k/2个元素后,现在在剩余的元素里找第k/2小数,可以用递归进行实现,但注意递归退出条件。
- 当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]。
源码
int find_kth(int* nums1, int nums1Size, int* nums2, int nums2Size, int k);
double findMedianSortedArrays(int* nums1, int nums1Size, int* nums2, int nums2Size){
int total = nums1Size + nums2Size;
if (total & 0x1)
{
return find_kth(nums1, nums1Size, nums2, nums2Size, total / 2 + 1) / 1.0;
}
else
{
return (find_kth(nums1, nums1Size, nums2, nums2Size, total / 2) + find_kth(nums1, nums1Size, nums2, nums2Size, total / 2 + 1)) / 2.0;
}
}
int find_kth(int* nums1, int nums1Size, int* nums2, int nums2Size, int k)
{
if (nums1Size > nums2Size)
{
return find_kth(nums2, nums2Size, nums1, nums1Size, k);
}
if (nums1Size == 0)
{
return nums2[k - 1];
}
if (k == 1)
{
return nums1[k - 1] < nums2[k - 1] ? nums1[k - 1] : nums2[k - 1];
}
int ia = k/2 <nums1Size ? k / 2 : nums1Size;
int ib = k - ia;
if (nums1[ia - 1] > nums2[ib - 1])
{
return find_kth(nums1, nums1Size, nums2 + ib, nums2Size - ib, k - ib);
}
else if (nums1[ia - 1] < nums2[ib - 1])
{
return find_kth(nums1 + ia, nums1Size - ia, nums2, nums2Size, k - ia);
}
else
{
return nums1[ia - 1];
}
}