[leetcode] 4-寻找两个有序数组的中位数
给定两个大小为 m 和 n 的有序数组 nums1 和 nums2。
请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。
你可以假设 nums1 和 nums2 不会同时为空。
Input: nums1 = [1, 3]
nums2 = [2]
Output: 则中位数是 2.0
2.思路
1.外排
由于数组是有序的,因此可以用外排,将两个数组marge成另外的数组help[]。从有序数组help中找出中位数就是一件简单的事了。(缺点是需要额外的空间来构造一个有序的新数组。)
实际时间复杂度为O(m+n),空间复杂度为O(m+n)。
2.双指针法
定义两个指针分别指向两个数组的开头。若数组总数num为奇数,则查找第num/2个数;若总数为偶数,则找那两个/2.
(这里不提供代码)
时间复杂度O(m+n),空间复杂度O(1)。
3. 二分法查找
一般时间复杂度为O(logn)的算法,都是需要用到分治思想(且一般为二分)。
两个有序数组求中位数,问题一般化为,求两个有序数组的第k个数,当k = (m+n)/2时为原问题的解。
怎么求第k个数?分别求出第一个和第二个数组的第 k / 2个数 a 和 b,然后比较 a 和 b,当a < b ,说明第 k 个数位于 a数组的第 k / 2个数后半段,或者b数组的 第 k / 2 个数前半段,问题规模缩小了一半,然后递归处理就行。
时间复杂度O(log m+n),空间复杂度O(1)
3.题解
外排法
class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int m=nums1.length;
int n=nums2.length;
int i=0;
int j=0;
int k=0;
int[] help=new int[n+m];
while(i<=m-1&&j<=n-1){
help[k++]=nums1[i]<=nums2[j]?nums1[i++]:nums2[j++];
}
while(i<m){
help[k++]=nums1[i++];
}
while(j<n){
help[k++]=nums2[j++];
}
k=help.length;
if(k%2==0){
return (help[k/2]+help[k/2-1])/2.0;
}
else{
return help[k/2]*1.0;
}
}
}
二分查找法
class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int m = nums1.length;
int n = nums2.length;
//处理任何一个nums为空数组的情况
if (m == 0) {
if (n % 2 != 0)
return 1.0 * nums2[n / 2];
return (nums2[n / 2] + nums2[n / 2 - 1]) / 2.0;
}
if (n == 0) {
if (m % 2 != 0)
return 1.0 * nums1[m / 2];
return (nums1[m / 2] + nums1[m / 2 - 1]) / 2.0;
}
int total = m + n;
//总数为奇数,找第 total / 2 + 1 个数
if ((total & 1) == 1) {
return find_kth(nums1, 0, nums2, 0, total / 2 + 1);
}
//总数为偶数,找第 total / 2 个数和第total / 2 + 1个数的平均值
return (find_kth(nums1, 0, nums2, 0, total / 2) + find_kth(nums1, 0, nums2, 0, total / 2 + 1)) / 2.0;
}
//寻找a 和 b 数组中,第k个数字
double find_kth(int[] a, int a_begin, int[] b, int b_begin, int k) {
//当a 或 b 超过数组长度,则第k个数为另外一个数组第k个数
if (a_begin >= a.length)
return b[b_begin + k - 1];
if (b_begin >= b.length)
return a[a_begin + k - 1];
//k为1时,两数组最小的那个为第一个数
if (k == 1)
return Math.min(a[a_begin], b[b_begin]);
int mid_a = Integer.MAX_VALUE;
int mid_b = Integer.MAX_VALUE;
//mid_a / mid_b 分别表示 a数组、b数组中第 k / 2 个数
if (a_begin + k / 2 - 1 < a.length)
mid_a = a[a_begin + k / 2 - 1];
if (b_begin + k / 2 - 1 < b.length)
mid_b = b[b_begin + k / 2 - 1];
//如果a数组的第 k / 2 个数小于b数组的第 k / 2 个数,表示总的第 k 个数位于 a的第k / 2个数的后半段,或者是b的第 k / 2个数的前半段
//由于范围缩小了 k / 2 个数,此时总的第 k 个数实际上等于新的范围内的第 k - k / 2个数,依次递归
if (mid_a < mid_b)
return find_kth(a, a_begin + k / 2, b, b_begin, k - k / 2);
//否则相反
return find_kth(a, a_begin, b, b_begin + k / 2, k - k / 2);
}
}
花五年时间成为某个领域的专家