LeetCode Top 100 Liked Questions 4.Median of Two Sorted Arrays(Java版; Hard)

welcome to my blog

LeetCode Top 100 Liked Questions 4.Median of Two Sorted Arrays(Java版; Hard)

题目描述

There are two sorted arrays nums1 and nums2 of size m and n respectively.

Find the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)).

You may assume nums1 and nums2 cannot be both empty.

Example 1:

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

The median is 2.0
Example 2:

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

The median is (2 + 3)/2 = 2.5
(1)在两个排序数组中找到第k小的数

class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int n = nums1.length, m = nums2.length;
        int count = n + m;
        if((count&1)==1){
            return core(nums1, nums2, 0, 0, count/2+1);
        }else{
            return (core(nums1, nums2, 0, 0, count/2) + core(nums1, nums2, 0, 0, count/2+1))/2.0;
        }
    }

    private double core(int[] nums1, int[] nums2, int i, int j, int k){
        //base case
        int n = nums1.length, m = nums2.length;
        if(i>=n){
            return nums2[j+k-1];
        }else if(j>=m){
            return nums1[i+k-1];
        }
        if(k==1){
            return Math.min(nums1[i], nums2[j]);
        }
        //第k/2个数的索引
        int mid1 = i + k/2 - 1;
        int mid2 = j + k/2 - 1;
        int a = mid1 >= n ? Integer.MAX_VALUE : nums1[mid1];
        int b = mid2 >= m ? Integer.MAX_VALUE : nums2[mid2];
        if(a<b){
            return core(nums1, nums2, mid1+1, j, k-k/2);
        }else{
            return core(nums1, nums2, i, mid2+1, k-k/2);
        }

    }
}
第一次做,看了题解才会的, 对切割位置进行二分; m个元素有m+1个切割位置, 切割位置取值为0,1,2,…,m, 当切割位置为i时表示左边有i个元素, 同时i作为索引时指向切割后右边的第一个元素; 是对短数组的切割位置进行二分, 不是长数组; 规定, 元素总数为偶数时, 左一左二的元素数量等于右一右二; 元素总数为奇数时, 左一左二的元素数量比右一右二多一个; while循环中的越界检查也非常重要
设arr1长度为m, arr2长度为n, m+n是奇数
将arr1分成两部分, 将arr2分成两部分, arr1的两部分对应左一和右一, arr2的两部分对应左二和右二
如果左一<=右二, 同时左二<=右一, 那么左一左二的元素都小于右一右二的元素,此时如果左一左二的元素个数相加等于(m+n+1)/2, 由于m+n是奇数, 所以中间的元素是第(m+n+1)/2个, 那么中位数就等于左一左二中的最大值

如果左一的最大值大于右二的最小值, 说明左一中的元素过多, 需要减少(分割线左移:这个操作涉及二分), 与此同时, 由于左一和左二的元素个数和为(m+n+1)/2, 所以左二中的元素个数增加

如果左二的最大值大于右一的最小值, 说明左二中的元素过多, 需要减少(分割线左移:这个操作涉及二分), 与此同时, 由于左一和左二的元素个数和为(m+n+1)/2, 所以左一中的元素个数增加

如果左一是整个arr1时, 此时没有右一, 中位数是左一左二的最大值
如果右一是整个arr1时, 此时没有左一, 中位数是左二的最大值
如果左二是整个arr2时, 此时没有右二, 中位数左一左二的最大值
如果右二是整个arr2时, 此时没有左二, 中位数是左一的最大值

=======================================================================================================
=======================================================================================================

设arr1长度为m, arr2长度为n, m+n是偶数
将arr1分成两部分, 将arr2分成两部分, arr1的两部分对应左一和右一, arr2的两部分对应左二和右二
如果左一<=右二, 同时左二<=右一, 那么左一左二的元素都小于右一右二的元素, 此时如果左一左二的元素个数相加等于(m+n)/2, 由于m+n是偶数, 所以中间的元素有两个, 分别是第(m+n)/2个和第(m+n)/2+1个, 分别对应左一左二中的最大值和右一右二中的最小值

如果左一的最大值大于右二的最小值, 说明左一中的元素过多, 需要减少(分割线左移:这个过程涉及二分), 与此同时, 由于左一和左二的元素个数和为(m+n)/2, 所以左二中的元素个数增加

如果左二的最大值大于右一的最小值, 说明左二中的元素过多, 需要减少(分割线左移:这个过程涉及二分), 与此同时, 由于左一和左二的元素个数和为(m+n)/2, 所以左一中的元素个数增加

如果左一是整个arr1时, 此时没有右一, 中位数是左一左二最大值和右二最小值的平均值
如果右一是整个arr1时, 此时没有左一, 中位数是左二最大值和右一右二最小值的平均值
如果左二是整个arr2时, 此时没有右二, 中位数是左一左二最大值和右二最小值的平均值
如果右二是整个arr2时, 此时没有右一, 中位数是左一最大值和左二右二最小值的平均值

class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        if(nums1==null)
            return nums2.length%2==1 ? nums2[nums2.length/2]*1.0 : (nums2[nums2.length/2-1]+nums2[nums2.length/2])/2.0;
        if(nums2==null)
            return nums1.length%2==1 ? nums1[nums1.length/2]*1.0 : (nums1[nums1.length/2-1] + nums1[nums1.length/2])/2.0;
        int m = nums1.length, n = nums2.length;
        //让nums1作为短的数组, 后面对短的数组进行二分
        if(m>n){
            int[] temp = nums1; nums1 = nums2; nums2 = temp;
            int tmp = m; m = n; n = tmp;
        }
        //右一最小值索引
        int i;
        //右二最小值索引
        int j;
        //left和right表示切割的位置, 0表示切割后左边有0个元素, 1表示切割后左边有1个元素
        int left = 0, right = m;
        //循环条件
        while(left <= right){
            //对短数组的切割边界进行二分
            i = (left + right )/2; //i作为切割线表示左边有i个元素, 作为索引指向切割后右边第一个元素
            //令j也指向右边第一个, 那么左一左二的元素个数为i+j满足 i+j = (m+n)/2
            j = (m+n+1)/2 - i; //m+n是偶数时, 左一左二的元素个数和右一右二相等; m+n是奇数时, 左一左二比右一右二多一个

            //左一大于右二, 将nums1分割线右边界左移,缩小左一范围
            //要保证左一有元素, 所以i>left
            if(i > left && nums1[i-1] > nums2[j])
                right = i - 1;
            //左二大于右一, 将nums1分割线左边界右移,扩大左一范围, 从而缩小左二范围
            //要保证右一有元素, 所以i<right
            else if(i < right && nums2[j-1] > nums1[i])
                left = i + 1;
                //左一<=右二 && 左二<=右一
            else{
                int leftMax;
                //右一是整个nums1
                if(i==0)
                    leftMax = nums2[j-1];
                //右二是整个nums2
                else if(j==0)
                    leftMax = nums1[i-1];
                else
                    leftMax = Math.max(nums1[i-1], nums2[j-1]);
                if((m+n)%2==1)
                    return leftMax;

                int rightMin;
                //左一是整个nums1
                if(i==m)
                    rightMin = nums2[j];
                //左二是整个nums2
                else if(j==n)
                    rightMin = nums1[i];
                else
                    rightMin = Math.min(nums1[i], nums2[j]);
                return (leftMax+rightMin)/2.0;
            }
        }
        return 0.0;
    }
}
第一次做, 模拟归并过程, 但并不创建辅助数组, 只需要记录两个位置的元素, 一个是len/2-1, 另一个是len/2; while循环到len/2-1即可, 我最开始循环到len/2处, 这样每次循环需要保存上一次循环的数, 容易写错. 循环到len/2-1处,之后再单独寻找len/2处的元素就方便多了; 不过不满足时间复杂度O(logm+n)的要求, 得用二分法
/*
将两个有序数组归并到中点处, 模拟归并过程, 不需要真正的进行归并
*/
class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        if(nums1==null)
            return nums2.length%2==1 ? nums2[nums2.length/2]*1.0 : (nums2[nums2.length/2-1]+nums2[nums2.length/2])/2.0;
        if(nums2==null)
            return nums1.length%2==1 ? nums1[nums1.length/2]*1.0 : (nums1[nums1.length/2-1]+nums1[nums1.length/2])/2.0;
        int len = nums1.length + nums2.length;
        int p1 =0, p2=0, i=0;
        int curr = 0;
        //循环到len/2-1处
        while(p1<nums1.length && p2<nums2.length && i<len/2){
            if(nums1[p1] <= nums2[p2]){
                curr = nums1[p1]; 
                p1++;
            }
            else{
                curr = nums2[p2];
                p2++;
            }
            i++;
        }
        while(p1<nums1.length && i<len/2){
            curr = nums1[p1];
            p1++;
            i++;
        }
        while(p2<nums2.length && i<len/2){
            curr = nums2[p2];
            p2++;
            i++;
        }
        
        int next=0;
        if(p1==nums1.length)
            next = nums2[p2];
        else if(p2==nums2.length)
            next = nums1[p1];
        else
            next = Math.min(nums1[p1], nums2[p2]);
        
        return len%2==1 ? next*1.0 : (curr + next)/2.0;
    }
}
题解
class Solution {
    public double findMedianSortedArrays(int[] A, int[] B) {
        int m = A.length;
        int n = B.length;
        if (m > n) { // to ensure m<=n
            int[] temp = A; A = B; B = temp;
            int tmp = m; m = n; n = tmp;
        }
        int iMin = 0, iMax = m, halfLen = (m + n + 1) / 2;
        while (iMin <= iMax) {
            int i = (iMin + iMax) / 2;
            int j = halfLen - i;
            if (i < iMax && B[j-1] > A[i]){
                iMin = i + 1; // i is too small
            }
            else if (i > iMin && A[i-1] > B[j]) {
                iMax = i - 1; // i is too big
            }
            else { // i is perfect
                int maxLeft = 0;
                if (i == 0) { maxLeft = B[j-1]; }
                else if (j == 0) { maxLeft = A[i-1]; }
                else { maxLeft = Math.max(A[i-1], B[j-1]); }
                if ( (m + n) % 2 == 1 ) { return maxLeft; }

                int minRight = 0;
                if (i == m) { minRight = B[j]; }
                else if (j == n) { minRight = A[i]; }
                else { minRight = Math.min(B[j], A[i]); }

                return (maxLeft + minRight) / 2.0;
            }
        }
        return 0.0;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值