002.Median of Two Sorted Arrays

Median of Two Sorted Arrays

question

给定两个大小为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。

请你找出这两个正序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。

你可以假设 nums1 和 nums2 不会同时为空。

tag

Array binary-search divide-and-conquer

示例 1:

nums1 = [1, 3]
nums2 = [2]

则中位数是 2.0

示例 2:

nums1 = [1, 2]
nums2 = [3, 4]

则中位数是 (2 + 3)/2 = 2.5

test

生成两个随机数组 参考Sum of two number

my answer

First

  • 思路:先将两个数组拼接为一个数组,然后对其进行从小到大的sort,最后将问题变成在一个数组中寻找中位数(估计时间复杂度会超)关键点在于数组的排序
int[] nums1 = new int[]{0,8,11};
int[] nums2 = new int[]{1,6,9,30};

//将两个数组拼接为一个数组
int[] allnums = new int[nums1.length + nums2.length];
System.arraycopy(nums1,0,allnums,0,nums1.length);
System.arraycopy(nums2,0,allnums,nums1.length,nums2.length);

// 冒泡排序 -- 光是执行这一段的时间复杂度就已经是 O(n2)了
for(int i=0;i<allnums.length-1;i++){   
   for(int j=i+1;j<allnums.length;j++){   
       if (allnums[i]>allnums[j]){   
           int temp=allnums[i];   
           allnums[i]=allnums[j];   
           allnums[j]=temp;   
       }
    }
}

//寻找中位数下标
if(allnums.length % 2 == 1){
    //中位数下标为 1个
    //length-1/2 + 1 -1
    int m = (allnums.length-1)/2;
    return allnums[m];
}else{
    //中位数下标为 2个
    //length/2 -1  length/2+1 -1
    int m1 = allnums.length/2 - 1;
    int m2 = allnums.length/2;
    return Math.round((allnums[m1] + allnums[m2])*100/2)/100; 
}

时间复杂度为O((n+m)2)….

脑海里实在没有更效率的办法了,看看别人的解法吧

Second

还是合并后找到中位数,不过用到一个更好的排序方式 – 归并排序

归并排序:将两个 有序数列合并成一个有序数列,我们称之为”归并”。

两个有序数组的合并也是归并排序中的 一部分 ,然后根据奇数,还是偶数,返回中位数。


public double findMedianSortedArrays(int[] nums1, int[] nums2) {
    int[] nums;
    int m = nums1.length;
    int n = nums2.length;

    //m 为空时
    if (m == 0) {
        if (n % 2 == 0) {
            return (nums2[n / 2 - 1] + nums2[n / 2]) / 2.0;
        } else {

            return nums2[n / 2];
        }
    }
    //n 为空时
    if (n == 0) {
        if (m % 2 == 0) {
            return (nums1[m / 2 - 1] + nums1[m / 2]) / 2.0;
        } else {
            return nums1[m / 2];
        }
    }

    //两个有序数组的 归并排序
    int count = 0;
    int i = 0, j = 0;
    while (count != (m + n)) {
        if (i == m) {
            while (j != n) {
                nums[count++] = nums2[j++];
            }
            break;
        }
        if (j == n) {
            while (i != m) {
                nums[count++] = nums1[i++];
            }
            break;
        }

        if (nums1[i] < nums2[j]) {
            nums[count++] = nums1[i++];
        } else {
            nums[count++] = nums2[j++];
        }
    }

    //合并完成后 找到中位数
    if (count % 2 == 0) {
        return (nums[count / 2 - 1] + nums[count / 2]) / 2.0;
    } else {
        return nums[count / 2];
    }
}

时间复杂度为O((n+m)),算是一个优化版的暴力法,但还是达不到题目要求的O(log(m + n))

第一遍小结

1.在学习第二种解的时候,了解到一个新的排序方式: 归并排序,两个有序数组的合并是归并排序中的 一部分 ,但是对于一个完整的乱序整数列来说,归并排序有更完成的写法(https://zhuanlan.zhihu.com/p/36075856)

在本题中,归并排序并不复杂,因为是两个已经排序好的数列,每两个元素一组进行比较。

  1. 这道题并没有结束,题目要求是把复杂度降到log级别,之后再回过头来看