题目描述:
给定两个大小分别为 m
和 n
的正序(从小到大)数组 nums1
和 nums2
。请你找出并返回这两个正序数组的 中位数 。
算法的时间复杂度应该为 O(log (m+n))
。
示例1:
输入:nums1 = [1,3], nums2 = [2] 输出:2.00000 解释:合并数组 = [1,2,3] ,中位数 2示例2:
输入:nums1 = [1,2], nums2 = [3,4] 输出:2.50000 解释:合并数组 = [1,2,3,4] ,中位数 (2 + 3) / 2 = 2.5
提示:
nums1.length == m
nums2.length == n
0 <= m <= 1000
0 <= n <= 1000
1 <= m + n <= 2000
-106 <= nums1[i], nums2[i] <= 106
思路分析一:合并数组
1、将两个数组合并为一个数组(开辟新数组),再对这个合并后的数组进行排序。
2、输出新数组的中位数。若为奇数,则直接输出;若为偶数,则输出中间两个数的平均数。
class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int sum = nums1.length+nums2.length;
int[] arrs = new int[sum];
System.arraycopy(nums1,0,arrs,0,nums1.length);
System.arraycopy(nums2,0,arrs,nums1.length,nums2.length);
Arrays.sort(arrs);
if(sum % 2 == 1){
int index = (sum-1)/2;
return arrs[index];
}else{
return (double)(arrs[sum/2]+arrs[sum/2-1])/2;
}
}
}
当然,最后判断的条件也可以用 三目运算符 简化为如下:
class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int sum = nums1.length+nums2.length;
int[] arrs = new int[sum];
System.arraycopy(nums1,0,arrs,0,nums1.length);
System.arraycopy(nums2,0,arrs,nums1.length,nums2.length);
Arrays.sort(arrs);
return sum % 2 == 1 ? arrs[(sum-1)/2] : (double)(arrs[sum/2]+arrs[sum/2-1])/2;
}
}
这种方式简单粗暴,但是由于需要新建数组,时间复杂度高。
思路分析二:双指针判定
1、情况1:有一个数组为空。
当数组a为空时,直接对数组b进行判定。
b数组为奇数个,输出中间数b.length/2
b数组为偶数个,输出 (b[b.length/2 - 1] + b[b.length/2]) /2
注意:由于nums1/nums2数组为int类型的数组,在做除法运算时需要注意类型的转换,'/2.0',或者进行强制类型转换。
2、情况2:两个数组都为非空数组
定义两个指针[min1]和[min2],分别指向 数组nums1 和 数组nums2 的首位。由于nums1和nums2是由小到大有序排列的,所以只需要判定两个指针指向的数字。
当nums1[min1]<nums2[min2]时,min1++,同时计数count++。
当计数count到达nums1+nums2的中间时,停止进行判断。所以我们需要对nums1+nums2进行判断其为奇数还是偶数。
详细的解释放在下面代码中逐行进行注释。
public class likou004 {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
// 用指针的方法循环判定
int min1 = 0, min2 = 0;
int max1 = nums1.length, max2 = nums2.length;
// 如果其中一个数组为空,则直接返回另一个数组的中间数
if(max1==0) return max2%2==0?(nums2[max2/2-1]+nums2[max2/2])/2.0:nums2[(max2-1)/2];
if(max2==0) return max1%2==0?(nums1[max1/2-1]+nums1[max1/2])/2.0:nums1[(max1-1)/2];
// 当两个数组都为非空时,采用双指针求解。
// 定义两个指针,分别指向nums1和nums2的首位,比较nums1和nums2的大小
// 定义一个计数,当 计数=(num1+num2)/2 时,当前指针指向的位置即为中位数
// 需要对nums1和nums2进行奇数偶数判定。
// 偶数:(nums1[指针1] + nums2[指针2]) / 2
// 奇数:当前指针位置
int count = 0; // 计数
double ans = 0; // 当前指针指向的值,用于返回
// 先进行偶数判定
if((max1+max2) %2 == 0){
while(count < (max1+max2)/2){
// 若其中一个数组已经遍历完成,则对另一个数组的指针进行移位操作。
if(min1==max1){
ans = nums2[min2++];
}
if(min2==max2){
ans = nums1[min1++];
}
// 若nums1[指针1] < nums2[指针2]
// 由于是从小到大排序,故当前值ans赋值nums1[指针1]
// 赋值结束,指针1向右移1位
if(min1<max1 && min2<max2){
ans = nums1[min1]<nums2[min2] ? nums1[min1++] : nums2[min2++];
}
// 每循环1次,计数+1
count++;
}
// 当其中一个数组遍历结束时,ans为另一个数组的两位数之和
if(min1==max1) return (ans+nums2[min2])/2.0;
if(min2==max2) return (ans+nums1[min1])/2.0;
// 中间值在两个数组之间时的取值方式
// 当前ans为两个中位数中较小的那个
// 通过nums1[指针1]和nums2[指针2]进行大小判定,小的那个即为下一位中位数
return nums1[min1]<nums2[min2] ? (ans+nums1[min1])/2.0 : (ans+nums2[min2])/2.0;
// 数组为奇数时
}else {
while(count <= (max1+max2)/2){
if(min1==max1){
ans = nums2[min2++];
}
if(min2==max2){
ans = nums1[min1++];
}
if(min1<max1 && min2<max2){
ans = nums1[min1]<nums2[min2] ? nums1[min1++] : nums2[min2++];
}
count++;
}
return ans;
}
}
}