1、题目描述
给定两个大小分别为 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
2、分析
题目需要找到两个正序数组的中位数,也就是如果将这两个数组合并在一起,需要找到最中间的一个或者两个数。
比较一般的做法,可以选择将两个数组合并之后再排序,再取出中间数字,但是这样子做的缺点就是时间复杂度高。
我的做法是:只要找到中间小的一个或者两个数就可以,其他的较大的数通通不算,这样子的就可以直接砍掉一半的计算量。具体分析就是:
(1)首先如果(m+n)是偶数,则需要两个(m+n)/2 和 (m+n)/2 - 1;如果是偶数,则只需要(m+n)/2
(2)因此我们需要拿到前(m+n)/ 2 小的数字,可以存放再arr数组中。
(3)设置两个指针分别指向两个数组,如果哪个数值小,它就进到arr,然后该数组的指针后移一位,知道拿到第m+n/2 - 1
(4)最后再判断m+n是奇数还是偶数,返回运算结果
3、完整代码
class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int mn = nums1.length + nums2.length; //存放前mn/2小的数字
double[] arr = new double[mn];
int i=0,j=0; //分别指向两个数组的指针
for(int k=0 ;k<=mn/2;k++){
//nums1为空,或者nums1已经被拿完了的时候
if(i>=nums1.length||nums1.length==0) arr[k]=(double)nums2[j++];
//nums1为空,或者nums1已经被拿完了的时候
else if(j>=nums2.length||nums2.length==0) arr[k]=(double)nums1[i++];
//拿比较小的
else if(nums1[i]>=nums2[j]) arr[k]=(double)nums2[j++];
else arr[k]=(double)nums1[i++];
}
//判断奇偶返回值
if(mn%2!=0) return arr[mn/2];
if(mn%2==0) return (arr[(mn/2)-1]+arr[mn/2])/2;
return 0;
}
}
4、其他大神的代码
代码是力扣的官方题解,用的是二分查找。
大家可以去看一下视频!
4. 寻找两个正序数组的中位数 - 力扣(Leetcode)
class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int length1 = nums1.length, length2 = nums2.length;
int totalLength = length1 + length2;
if (totalLength % 2 == 1) {
int midIndex = totalLength / 2;
double median = getKthElement(nums1, nums2, midIndex + 1);
return median;
} else {
int midIndex1 = totalLength / 2 - 1, midIndex2 = totalLength / 2;
double median = (getKthElement(nums1, nums2, midIndex1 + 1) + getKthElement(nums1, nums2, midIndex2 + 1)) / 2.0;
return median;
}
}
public int getKthElement(int[] nums1, int[] nums2, int k) {
/* 主要思路:要找到第 k (k>1) 小的元素,那么就取 pivot1 = nums1[k/2-1] 和 pivot2 = nums2[k/2-1] 进行比较
* 这里的 "/" 表示整除
* nums1 中小于等于 pivot1 的元素有 nums1[0 .. k/2-2] 共计 k/2-1 个
* nums2 中小于等于 pivot2 的元素有 nums2[0 .. k/2-2] 共计 k/2-1 个
* 取 pivot = min(pivot1, pivot2),两个数组中小于等于 pivot 的元素共计不会超过 (k/2-1) + (k/2-1) <= k-2 个
* 这样 pivot 本身最大也只能是第 k-1 小的元素
* 如果 pivot = pivot1,那么 nums1[0 .. k/2-1] 都不可能是第 k 小的元素。把这些元素全部 "删除",剩下的作为新的 nums1 数组
* 如果 pivot = pivot2,那么 nums2[0 .. k/2-1] 都不可能是第 k 小的元素。把这些元素全部 "删除",剩下的作为新的 nums2 数组
* 由于我们 "删除" 了一些元素(这些元素都比第 k 小的元素要小),因此需要修改 k 的值,减去删除的数的个数
*/
int length1 = nums1.length, length2 = nums2.length;
int index1 = 0, index2 = 0;
int kthElement = 0;
while (true) {
// 边界情况
if (index1 == length1) {
return nums2[index2 + k - 1];
}
if (index2 == length2) {
return nums1[index1 + k - 1];
}
if (k == 1) {
return Math.min(nums1[index1], nums2[index2]);
}
// 正常情况
int half = k / 2;
int newIndex1 = Math.min(index1 + half, length1) - 1;
int newIndex2 = Math.min(index2 + half, length2) - 1;
int pivot1 = nums1[newIndex1], pivot2 = nums2[newIndex2];
if (pivot1 <= pivot2) {
k -= (newIndex1 - index1 + 1);
index1 = newIndex1 + 1;
} else {
k -= (newIndex2 - index2 + 1);
index2 = newIndex2 + 1;
}
}
}
}