要求:
给定两个大小为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。 请你找出这两个正序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。 假设 nums1 和 nums2 不会同时为空。
示例:
nums1 = [1, 3]
nums2 = [2]
中位数是 2.0
nums1 = [1, 2]
nums2 = [3, 4]
中位数是 (2 + 3)/2 = 2.5
思路:
这个哥们的思路太厉害了,分析的也很到位: windliang: leetcode-cn.com/problems/me… 求中位数也可以理解为求第k小数的一种特殊情况,精简一下求第k小数的算法。 已知两个数组均为有序数组,先不考虑特殊情况,比较两个数组里的第k/2个数字。奇数默认向下取整,注意是第k/2个数字,不是下标为k/2的数字。
- 如果A[k/2] < B[k/2],那么A[1],A[2],...A[k/2]都不会是第k小的数字。因为A数组中比A[k/2]小的数字有k/2-1个,假设B[k/2]之前的数字都比A[k/2]小,那最多也只有k/2-1个数字,加起来一共k-2个数字,所以A[k/2]最多是第k-1小的数字,所以比他小的都可以删除,至少A数组中在它前面的都比它小都可以排除。
- 如果A[k/2]=B[k/2],随便排除哪一个都可以,都是一样的效果。
- 有时候会遇到某个数组剩余的长度比k/2小,这时候就把某个数组全部删除掉了,把删掉的元素个数去掉,在剩下的另一个数组里按索引查找就行。对数组长度和k/2进行一个比较,Math.min(k/2, len)
- 为了统一设置,每次都保证数组1的长度小于数组2的长度。
- 数组长度可能为奇数也可能为偶书,那就求两次求均值。
代码:
public double findMedian(int[] nums1, int[] nums2) {
int n = nums1.length;
int m = nums2.length;
int left = (n+m+1)/2;
int right = (n+m+2)/2;
//奇数和偶数的情况合并,若为奇数就是求两次
return (getKth(nums1,0,n-1,nums2,0,m-1,left) + getKth(nums1,0,n-1,nums2,0,m-1,right))/2
}
public int getKth(int[] nums1, int start1, int end1, int[] nums2, int start2, int end2, int k) {
//输入两个数组,限定好左边界和右边界,每次更新,k代表每次寻找的第K小的数字
int len1 = end1 - start1 + 1;
int len2 = end2 - start2 + 1;
//保证len1<len2,这样有数组空了就一定是len1了
if (len1 > len2) return getKth(nums2, start2, end2, nums1, start1, end1, k);
if (len1 == 0) return nums2[start2+k-1];
if (k == 1) return Math.min(nums1[start1], nums2[start2]);
int i = start1 + Math.min(len1, k/2) - 1;
int j = start2 + Math.min(len2, k/2) - 1;
if (nums1[i] > nums[j]) {
return getKth(nums1,start1,end1,nums2,j+1,end2, k-Math.min(len2, k/2));
} else {
return getKth(nums1,i+1,end1,nums2,start2,end2, k-Math.min(len1, k/2));
}
}