LeetCode Hot 100 --- 寻找两个正序数组的中位数(java详解)

Posted 小样5411

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LeetCode Hot 100 --- 寻找两个正序数组的中位数(java详解)相关的知识,希望对你有一定的参考价值。

题目

解法一

时间复杂度:O(m+n)
空间复杂度:O(m+n)
这个也是我自己想出来的解法,不过确实最慢解。。。代码注释也比较详细

class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        //首先,对两个数组进行合并
        //判空
        if(nums1 == null || nums1.length == 0){
            //获取数组的中位数
            return getMidValue(nums2);
        }
        if(nums2 == null || nums2.length == 0){
            //获取数组的中位数
            return getMidValue(nums1);
        }
        //两个都不为空,则进行合并操作
        int m = nums1.length;
        int n = nums2.length;
        int[] mergeArr = new int[m+n];
        int count = 0;//合并数组下标
        int p1 = 0;//初始化两个指针(下标)
        int p2 = 0;
        //下面合并过程很像有序链表的合并
        while(p1 < nums1.length && p2 < nums2.length){
            if(nums1[p1] < nums2[p2]){
                mergeArr[count]=nums1[p1];
                count++;
                p1++;
            }else{
                mergeArr[count]=nums2[p2];
                count++;
                p2++;
            }
        }
        //若nums1数组没有走完,则将剩余加入合并数组
        while(p1 < nums1.length){
            mergeArr[count]=nums1[p1];
            count++;
            p1++;
        }
        //若nums2数组没有走完,则将剩余加入合并数组
        while(p2 < nums2.length){
            mergeArr[count]=nums2[p2];
            count++;
            p2++;
        }
        //完成合并后,list即为合并的数组,转成array数组,找出中位数即可
        return getMidValue(mergeArr);
    }
    //获取数组的中位数
    private double getMidValue(int[] arr){
        int len = arr.length;
        //数组长度为偶数
        if(len % 2 == 0){
            return (arr[len/2]+arr[len/2-1])/2.0; //这里偶数长度,除以2.0很重要,表示保留一位小数,直接5/2结果是2.00000
        }else{
            //数组长度为奇数
            return arr[len/2];
        }
    }
}

解法二(优化解法一)

时间复杂度:O(m+n)
空间复杂度:O(1)

class Solution {
   public double findMedianSortedArrays(int[] nums1, int[] nums2) {
    int m = nums1.length;
    int n = nums2.length;
    int len = m + n;
    int left = 0, right = 0;
    int aStart = 0, bStart = 0;//下标,aStart对应nums1,bStart对应nums2
    for (int i = 0; i <= len / 2; i++) {
        left = right;
        if (aStart < m && (bStart >= n || nums1[aStart] < nums2[bStart])) {
            right = nums1[aStart++];
        } else {
            right = nums2[bStart++];
        }
    }
    if (len % 2 == 0)
        return (left + right) / 2.0;
    else
        return right;
    }
}

下面是详细解析

用 len 表示合并后数组的长度,如果是奇数,需要遍历 len/2 + 1 次。如果是偶数,我们需要知道第 len/2和 len/2+1 个数,也是需要遍历 len/2+1 次。所以遍历的话,奇数和偶数都是 len/2+1 次
那么循环就可以写成for(int i = 0 ; i <= len/2 ; i++),正好 len/2+1 次

如上,返回中位数,奇数需要最后一次遍历的结果就可以,偶数需要最后一次和上一次遍历的结果。所以我们用两个变量 left 和 right,right 保存当前循环的结果,在每次循环前将 right 的值赋给 left。这样在最后一次循环的时候,left 将得到 right 的值,也就是上一次循环的结果,接下来 right 更新为最后一次的结果,也就是left会跟着right
画图解析执行过程

代码中bStart >= n是为了控制,第二个数组短,第一个长情况,如下

解法三(二分查找)

时间复杂度:O(log(min(M,N)))
空间复杂度:O(1)

这个可以参考解法链接,很详细,方法三最优解,但理解起来有点难度

public class Solution {

    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        if (nums1.length > nums2.length) {
            int[] temp = nums1;
            nums1 = nums2;
            nums2 = temp;
        }

        int m = nums1.length;
        int n = nums2.length;

        // 分割线左边的所有元素需要满足的个数 m + (n - m + 1) / 2;
        int totalLeft = (m + n + 1) / 2;

        // 在 nums1 的区间 [0, m] 里查找恰当的分割线,
        // 使得 nums1[i - 1] <= nums2[j] && nums2[j - 1] <= nums1[i]
        int left = 0;
        int right = m;

        while (left < right) {
            int i = left + (right - left + 1) / 2;
            int j = totalLeft - i;
            if (nums1[i - 1] > nums2[j]) {
                // 下一轮搜索的区间 [left, i - 1]
                right = i - 1;
            } else {
                // 下一轮搜索的区间 [i, right]
                left = i;
            }
        }

        int i = left;
        int j = totalLeft - i;

        int nums1LeftMax = i == 0 ? Integer.MIN_VALUE : nums1[i - 1];
        int nums1RightMin = i == m ? Integer.MAX_VALUE : nums1[i];
        int nums2LeftMax = j == 0 ? Integer.MIN_VALUE : nums2[j - 1];
        int nums2RightMin = j == n ? Integer.MAX_VALUE : nums2[j];

        if (((m + n) % 2) == 1) {
            return Math.max(nums1LeftMax, nums2LeftMax);
        } else {
            return (double) ((Math.max(nums1LeftMax, nums2LeftMax) + Math.min(nums1RightMin, nums2RightMin))) / 2;
        }
    }
}

以上是关于LeetCode Hot 100 --- 寻找两个正序数组的中位数(java详解)的主要内容,如果未能解决你的问题,请参考以下文章

LeetCode 热题 HOT 100

leetcode的Hot100系列--3. 无重复字符的最长子串--滑动窗口

LeetCode Hot100(11-15)

leetcode hot100 easy

#yyds干货盘点# LeetCode 热题 HOT 100:四数之和

#yyds干货盘点# LeetCode 热题 HOT 100:编辑距离