[LeetCode] 4. 寻找两个正序数组的中位数
作者:互联网
目录
给定两个大小为 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
分析
- 如果对时间复杂度的要求有log,通常都要用到二分查找,求第k小数,两个数组每次循环可排除其中一个数组的k/2个数,然后调整k
- 分别切分两数组,左部分长度和与右部分长度和为固定值,使用二分法确定切点
解法一
- 求第k小数,两个数组每次循环可排除其中一个数组的k/2个数,然后调整k,若其中一个数组长度小于k/2,排除这个数组再调整k
- 时间复杂度O(log(m+n)),每循环一次减少k/2个元素,时间复杂度为O(log(k)),k = (m+n)/2
- 空间复杂度O(1),递归为尾递归,不需要不停堆栈
class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int m = nums1.length;
int n = nums2.length;
int k1 = (m + n + 1) /2;
int k2 = (m + n + 2) /2;
// 将偶数和奇数情况合并, 若为奇数则求两次相同的k
return (getKth(nums1, 0, m -1, nums2, 0, n -1, k1)
+ getKth(nums1, 0, m -1, nums2, 0, n -1, k2)) /2;
}
private double 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
if(len1 > len2) return getKth(nums2, start2, end2, nums1, start1, end1, k);
// len1为空时, 返回len2的第k小数
if(len1 == 0) return nums2[start2 + k -1];
// k为1时, 返回两数组最小值
if(k == 1) return Math.min(nums1[start1], nums2[start2]);
// 当len1小于k/2时, 指针移至末位
int i = start1 + Math.min(len1, k /2) - 1;
int j = start2 + Math.min(len1, k /2) - 1;
// 比较k/2-1处元素大小,移除元素
if( nums1[i] < nums2[j]){
return getKth(nums1, i + 1, end1, nums2, start2, end2,
k - (i - start1 + 1));
}
else {
return getKth(nums1, start1, end1, nums2, j + 1, end2,
k - (j - start2 + 1));
}
}
}
//class Test{
// public static void main(String[] args) {
// int[] nums1 = {1,2};
// int[] nums2 = {3,4};
// Solution s = new Solution();
// double res = s.findMedianSortedArrays(nums1, nums2);
// System.out.println(res);
// }
//}
解法二
-
切分两数组, 左部分长度和与右部分长度和为固定值, 使用二分法确定切点
长度和为偶数
-
左半部分长度和 = 右半部分长度和
i + j = m - i + n - j, 即j = (m + n) / 2 - i
-
左半部分最大值小于右半部分最小值
max(A[i - 1], B[j - 1]) <= min(A[i], B[j]), 即A[i - 1] < B[j]且B[j - 1] < A[i]
长度和为奇数
-
左半部分长度和 = 右半部分长度和 + 1
i + j = m - i + n - j, 即j = (m + n + 1) / 2 - i, 取整同上为j = (m + n) / 2 - i
-
左半部分最大值小于右半部分最小值
同上为A[i - 1] < B[j]且B[j - 1] < A[i]
减小i, 增加j, 需保证i != 0, j != n
增加i, 减小j, 需保证i != m, j != 0
i = 0, j = 0, i = m, j = n单独考虑
-
-
时间复杂度O(log(min(m,n))),对较短数组进行二分查找
-
空间复杂度O(1)
class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int m = nums1.length;
int n = nums2.length;
// 保证m<=n
if(m > n) return findMedianSortedArrays(nums2, nums1);
int iMin = 0, iMax = m;
while (iMin <= iMax){
int i = (iMin + iMax) / 2;
int j = (m + n + 1) / 2 - i;
// i需要增大
if(j != 0 && i != m && nums2[j - 1] > nums1[i]) iMin = i + 1;
// i需要减小
else if(i != 0 && j != n && nums1[i-1] > nums2[j]) iMax = i - 1;
else{
// 达到要求或边界条件
int maxLeft = 0;
if(i == 0) maxLeft = nums2[j - 1];
else if(j == 0) maxLeft = nums1[i - 1];
else maxLeft = Math.max(nums1[i - 1], nums2[j - 1]);
// 奇数情况不需要考虑右半部分
if((m + n) % 2 != 0) return maxLeft;
int minRight = 0;
if(i == m) minRight = nums2[j];
else if(j == n) minRight = nums1[i];
else minRight = Math.min(nums2[j], nums1[i]);
return (maxLeft + minRight) / 2.0;
}
}
return 0.0;
}
}
标签:正序,int,中位数,nums1,start2,数组,return,LeetCode,nums2 来源: https://www.cnblogs.com/lijiong/p/13508390.html