LeetCode第四题(寻找两个正序数组的中位数)

141 阅读2分钟

1、先正序归并,后寻找(合并两个数组)

class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int[] nums = new int[nums1.length + nums2.length];
        //nums1为空
        if(nums1.length == 0){
            if(nums2.length % 2 == 0){
                return (nums2[nums2.length / 2 - 1] + nums2[nums2.length / 2]) / 2.0;
            }else{
                return nums2[nums2.length / 2];
            }
        }
        //nums2为空
        if(nums2.length == 0){
            if(nums1.length % 2 == 0){
                return (nums1[nums1.length / 2 - 1] + nums1[nums1.length / 2]) / 2.0;
            }else{
                return nums1[nums1.length / 2];
            }
        }
        //将nums1和nums2正序归并为nums
        int count = 0, i = 0, j = 0;
        while(count != nums1.length + nums2.length){
            if(i == nums1.length){
                while(j != nums2.length){
                    nums[count++] = nums2[j++];
                }
                break; 
            }
            if(j == nums2.length){
                while(i != nums1.length){
                    nums[count++] = nums1[i++];
                }
                break; 
            }
            if (nums1[i] < nums2[j]) {
                nums[count++] = nums1[i++];
            }else{
                nums[count++] = nums2[j++];
            }
        }
        //count = nums1.length + nums2.length
        if(count % 2 == 0){
            return (nums[count / 2 - 1] + nums[count / 2]) / 2.0;
        }else{
            return nums[count / 2];
        }
    }
}

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

2、利用归并思想直接找到中位数,没有合并两个数组

class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int len = nums1.length + nums2.length;
        double left = 0, right = 0;//保存这两个正序数组的中间两个数 
        int point1 = 0, point2 = 0;
        //只需遍历len/2 + 1次
        for(int i = 0; i <= len / 2; i++){
            left = right;
            if(point1 < nums1.length && (point2 >= nums2.length || nums1[point1] < nums2[point2])){
                right = nums1[point1++];
            }else{
                right = nums2[point2++];
            }
        }
        if(len % 2 == 0){
            return (left + right) / 2.0;
        }else{
            return right; 
        }
    }
}

时间复杂度:O((m+n)/2)

3、利用求第k小数思路

简要说明:由于数列是有序的,我们可以每次排除一半数,即假设我们要找第k小数,我们可以每次循环排除掉k/2个数,直至k=1,则下一个数为第k小数。本题中,只需寻找第中间一个数或者第中间两个数。

疑问:为什么每次减少k/2个数,当k=1时,下一个数为第k小数?(抛开第四题本身)

回答:假设在一个长度为n的正序数组中求第k小数,根据上述算法可以得知:(1)i次循环后k=1;(2)i次循环中减少的元素个数之和为k-1个,利用递推式Di = Di-1 - Di-1 / 2(向下取整),其中D1 = k,Di = 1可证

class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int len1 = nums1.length, len2 = nums2.length;
        int len = nums1.length + nums2.length;
        double median = 0.0;
        if(len % 2 == 0){
            int indexLeft = len / 2 - 1, indexRight = len / 2;//中间两个数的下标
            median = (getKthNum(nums1,nums2,indexLeft + 1) + getKthNum(nums1,nums2,indexRight + 1)) / 2.0;
        }
        else{
            int index = len / 2;//中间一个数的下标
            median = getKthNum(nums1,nums2,index + 1);
        } 
        return median;
    }
    //求第k小数
    public int getKthNum(int[] nums1, int[] nums2, int k){
        int len1 = nums1.length, len2 = nums2.length;
        int left1 = 0, left2 = 0;//数组nums1,nums2的左边界
        while(true){
            //只有数组nums2有元素
            if(left1 == len1)
                return nums2[left2 + k - 1];
            //只有数组nums2有元素
            if(left2 == len2)
                return nums1[left1 + k - 1];
            //找到第k小数
            if(k == 1)
                return Math.min(nums1[left1],nums2[left2]);
            //数组nums1和nums2均有元素
            int mid = k / 2;
            int newLeft1 = Math.min(left1 + mid,len1) - 1;//数组nums1的新左边界,其中min()保证不越界
            int newLeft2 = Math.min(left2 + mid,len2) - 1;//数组nums2的新左边界,其中min()保证不越界
            if(nums1[newLeft1] <= nums2[newLeft2]){
                k -= (newLeft1 - left1 + 1);//根据边界之差来确定排除元素的个数
                left1 = newLeft1 + 1;
            }
                
            else{
                k -= (newLeft2 - left2 + 1);//根据边界之差来确定排除元素的个数
                left2 = newLeft2 + 1;
            }
            //k -= mid;//更新k值(有问题:mid可能大于数组中的剩余元素个数)
        }
    }
}

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