算法4. 寻找两个正序数组的中位数

149 阅读3分钟

「这是我参与2022首次更文挑战的第28天,活动详情查看:2022首次更文挑战」。

一、题目

给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。

示例 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

示例 3:

输入:nums1 = [0,0], nums2 = [0,0]

输出:0.00000

示例 4:

输入:nums1 = [], nums2 = [1]

输出:1.00000

示例 5:

输入:nums1 = [2], nums2 = []

输出:2.00000

提示:

nums1.length == m

nums2.length == n

0 <= m <= 1000

0 <= n <= 1000

1 <= m + n <= 2000

-106 <= nums1[i], nums2[i] <= 106

进阶:你能设计一个时间复杂度为 O(log (m+n)) 的算法解决此问题吗?

二、我的解答

第一次解答

class Solution {    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int num1Length;
        int num2Length;
        if (nums1==null){
            num1Length=0;
        }else {
            num1Length= nums1.length;
        }
        if (nums2==null){
            num2Length=0;
        }else {
            num2Length= nums2.length;
        }
        if((num1Length+num2Length)==0){
            return 0;
        }
        int[] nums3=new int[num1Length+num2Length];
        double median;
        System.arraycopy(nums1,0,nums3,0,num1Length);
        System.arraycopy(nums2,0,nums3,num1Length,num2Length);
        for (int i=0;i<nums3.length-1;i++){
            //第一趟会把最大的移到最后,比如4321,第一遍i=0,之后,就会变成3214,一共n个数,最多n-1趟就够了
            for (int j=0;j<nums3.length-1;j++){
                if (nums3[j]>nums3[j+1]){
                    int temp=nums3[j];
                    nums3[j]=nums3[j+1];
                    nums3[j+1]=temp;
                }
            }
        }
        int index=nums3.length/2;
        if(nums3.length%2==0){
            median=(nums3[index-1]+nums3[index])/2.0;
        }else {
            median=nums3[index];
        }
        return median;

    }
}

思路及算法

简单粗暴,先将两个数组合并,然后冒泡排序。最后根据奇数,还是偶数,返回中位数。

但是这种暴力求解的方式,显然没有使用到题目给出的数组有序这一条件。其时间复杂度取决于所选的排序算法的时间复杂度。我这里使用的是冒泡排序,所以其算法复杂度应该为O((m+n)^2),这里的m和n分别为数组num1和num2的长度。其时间复杂度不符合题目时间复杂度限制为 O(log (m+n))的要求,所以我们的算法是错误的。

三、系统解答

方法一:有序数组的归并排序

使用归并的方式,合并两个有序数组,得到一个大的有序数组。大的有序数组的中间位置的元素,即为中位数。

这种方式其实是和我暴力求解方式是一样的,只不过,它利用了数组有序这个条件,借鉴归并排序的关键步骤【合并链各个有序数组】,将时间复杂度降到O(m+n)。在其过程中,不用完全合并,就能得到答案。

但这一步优化并不能改进算法的时间复杂度到O(log (m+n))

代码略

方法二:二分查找

中位数:在只有一个有序数组的时候,中位数把数组分割成两个部分

根据定义,分数组长度为奇数和偶数讨论。

数组长度为偶数时,中位数有两个,分割线两边,其中一个是左边数组的最大值,另一个是右边数组的最小值。

数组长度为基数时,中位数有1个,分割线在中位数的左边或者右边。 

思路:只需要给出两个有序数组的一个恰当的分割线,中位数的值就由这个分割线的两侧的数决定。如何确定分割线的位置就可以使用二分查找法。

代码略

这部分听是听明白了,但是讲是不知道怎么讲。感兴趣的可以去官方网站查看视频讲解