leetcode:
4 |
题目:给两个有序数组,长度为n和m,在 O(log (m+n))时间内找出两个数组中所有数字的中位数。
题解(参考讨论区大佬解法):
递归分治方法,先上代码:
#include <stdio.h>
double findK(int *nums1, int nums1Size, int *nums2, int nums2Size, int k){
if(!nums1Size) return nums2[k-1];
if(!nums2Size) return nums1[k-1];
if(k==1) return nums1[0] < nums2[0] ? nums1[0]:nums2[0];
if(nums1Size < k/2) return findK(nums1, nums1Size, nums2+k/2, nums2Size-k/2, k-k/2);
if(nums2Size < k/2) return findK(nums1+k/2, nums1Size-k/2, nums2, nums2Size, k-k/2);
if(nums1[k/2-1] > nums2[k/2-1]) return findK(nums1, nums1Size, nums2+k/2, nums2Size-k/2, k-k/2);
if(nums1[k/2-1] < nums2[k/2-1]) return findK(nums1+k/2, nums1Size-k/2, nums2, nums2Size, k-k/2);
if(k&1) return findK(nums1+k/2, nums1Size-k/2, nums2+k/2, nums2Size-k/2, 1);
return nums1[k/2-1];
}
double findMedianSortedArrays(int* nums1, int nums1Size, int* nums2, int nums2Size) {
int sum = nums1Size + nums2Size;
if(sum & 1)
return findK(nums1, nums1Size, nums2, nums2Size, sum/2+1);
else
return (findK(nums1, nums1Size, nums2, nums2Size, sum/2)+findK(nums1, nums1Size, nums2, nums2Size, sum/2+1)) / 2.0;
}
int main(){
int nums1[] = {1};
int nums2[] = {1};
double ans = findMedianSortedArrays(nums1, 1, nums2, 1);
printf("%lf\n", ans);
}
使用findK函数递归寻找第k个数,每次至少去掉1/4~1/2的数据,在剩余的数中继续用同样的方法查找。具体解释:
如果总数是奇数个,那么中位数就是第(sum/2)个数,即findK(sum/2);如果总数是偶数个,那么中位数就是第(sum/2)个数和第(sum/2+1)个数的平均数,即(findK(sum/2)+findK(sum/2+1))/2。
findK函数:
1、如果其中一个数组长度小于k/2,那么第k个数肯定大于另一个数组中前k/2的数,因为第一个数组所有数个数加上第二个数组前k/2数的个数 < k。所以可以将第二个数组中前k/2个数去掉,继续递归寻找;
2、如果两个数组长度都大于k/2,那么比较两个数组中第k/2个数的大小,如果第一个数组中第k/2个数大于第二个数组,则第k个数肯定大于第二个数组中第k/2个数,所以可以将第二个数组前k/2个数去掉,继续递归寻找;
3、如果两个数组第k/2个数相同,那么得看k是奇数还是偶数。
(1)如果k是奇数,那么两个数组都去掉前k/2个数(一共去掉了k-1个数),两个数组中剩余数字中最小的那个就是结果;
(2)如果是偶数,那么这个相同的数字就是总体第k个数字和第k+1个数字,直接返回即可。