求两个有序数组的中位数,要求时间复杂度是O(log(m+n)),m+n是两个数列的长度之和
在网上参考了大神的解法,真的太棒了,本菜鸡只会说一句卧*,和大家分享一下。求中位数就是求第k小数的一种特殊情况而已,下面详细介绍求第k小数的算法
现在假设s1={1,3,4,9},s2={1,2,3,4,5,6,7,8,9,10},而我们要求的是第7小的数,即K=7,那么我们可以这样。
第一步 求k/2
此时我们找到两个数列的第三小的数字,因为3<4,我们可以判断第7小的数一定不在s2的3之前。于是我们的比较缩小了,下一次从s2的4开始比较,前面三位不用管
第二步 此时k变为k=7-3=4,因为删去了三个数,这三个数小于第7小的数。
步骤和1类似
第三步 k=4-2=2
这时4和4相等,统一删去s2的4,那么
第四步 此时k=1,注意k=1是我们递归跳出的条件之一。这时候我们比较指针所指位置,返回较小的数即可,那么答案就是s1的4
若干其他情况
1.如果数组的长度s1<k/2,那么这个时候,我们直接将s1的比较指针指向s1的最后一个元素即可,如果最后s1全都被删去了,那么我们直接返回s2中的第(k-s1.length)个元素即可,例如
此时直接返回s2的第五个元素
2.为了统一,我们要保证s1的长度小于s2的长度,原因见代码注释
代码!!!
注意!!!这里给的是求中位数的算法
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int n = nums1.length;
int m = nums2.length;
int left = (n + m + 1) / 2;
int right = (n + m + 2) / 2;
//将偶数和奇数的情况合并,如果是奇数,会求两次同样的 k 。
return (getKth(nums1, 0, n - 1, nums2, 0, m - 1, left) + getKth(nums1, 0, n - 1, nums2, 0, m - 1, right)) * 0.5;
}
private int getKth(int[] nums1, int start1, int end1, int[] nums2, int start2, int end2, int 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];
//如果len1为空的话,直接返回
if (k == 1) return Math.min(nums1[start1], nums2[start2]);
//如果取第1小的数,那么取两个数组第一个元素的最小值
int i = start1 + Math.min(len1, k / 2) - 1;
//从start到第k小的数,注意要-1
int j = start2 + Math.min(len2, k / 2) - 1;
//如果数组长度小于k/2的话则指向最后一个元素
if (nums1[i] > nums2[j]) {
return getKth(nums1, start1, end1, nums2, j + 1, end2, k - (j - start2 + 1));
}
else {
return getKth(nums1, i + 1, end1, nums2, start2, end2, k - (i - start1 + 1));
}
}
总结
遇到时间复杂度为对数的题,基本可以确定用二分法,本题中相当于对s1和s2轮流减半,即O(log(m+n))