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

题目

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

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

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

方法一 归并数组

public static double findMedianSortedArrays(int[] nums1, int[] nums2) {
            int nums1Length = nums1.length;
            int nums2Length = nums2.length;
            int i=0;
            int j = 0;//表示nums1,nums2的起始索引
            int[] temp = new int[nums1Length+nums2Length];
            int t = 0; //指向temp的初始索引
            //遍历两个数组,依次按照顺序填充到temp里面去,
            //直到有一方遍历完成
            while (i<=nums1Length-1 && j<=nums2Length-1){
                if(nums1[i]<=nums2[j]){
                      //说明nums1里面的元素小于等于nums2里面的元素
                    temp[t]= nums1[i];
                    t++;
                    i++;
                }else{
                    temp[t] = nums2[j];
                    t++;
                    j++;
                }
            }
            while (i<=nums1Length-1){
                temp[t] = nums1[i];
                t++;
                i++;
            }
            while (j<=nums2Length-1){
                temp[t]  = nums2[j];
                t++;
                j++;
            }
            //获取temp数组的长度
        int tempLength = temp.length;
        double centerNum;
        if(tempLength%2 == 1){
            //如果数组是奇数
            centerNum = (double) temp[(0+temp.length)/2];
            return centerNum;
        }else{
            //如果数组是偶数
            int mid = (0+tempLength)/2;//计算数组中值
            centerNum = ((double)temp[mid]+(double) temp[mid-1])/2;
            return  centerNum;
        }

方法二 直接for循环查找中位数

 public static double findMedianSortedArrays1(int[] nums1, int[] nums2) {
        int m = nums1.length;
        int n = nums2.length;
        int aStart = 0; //指向nums1的初始索引
        int bStart = 0; //指向nums2的初始索引
        int len = m+n;
        int left = -1; int right = -1;
        for (int i = 0; i <= len/2; i++) {
            left = right;
            if(aStart<m && ( bStart>=n || nums1[aStart]<nums2[bStart] )){
                right = nums1[aStart];
                aStart++;
            }else{
                right = nums2[bStart];
                bStart++;
            }
        }

        if(len%2==1){
            return  right;
        }else{
            return  ((double) (left+right))/2;
        }
    }

思路如下

用 len 表示合并后数组的长度,如果是奇数,我们需要知道第 (len+1)/2 个数就可以了,如果遍历的话需要遍历 int(len/2 )

  • 1 次。如果是偶数,我们需要知道第 len/2和 len/2+1 个数,也是需要遍历 len/2+1 次。所以遍历的话,奇数和偶数都是 len/2+1 次。

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

循环中该怎么写,什么时候 A 数组后移,什么时候 B 数组后移。用 aStart 和 bStart 分别表示当前指向 A 数组和 B
数组的位置。如果 aStart 还没有到最后并且此时 A 位置的数字小于 B
位置的数组,那么就可以后移了。也就是aStart<m&&A[aStart]< B[bStart]。

但如果 B 数组此刻已经没有数字了,继续取数字 B[ bStart ],则会越界,所以判断下 bStart 是否大于数组长度了,这样 ||
后边的就不会执行了,也就不会导致错误了,所以增加为 aStart<m&&(bStart) >=
n||A[aStart]<B[bStart]) 。

方法三 二分查找法

 //第k数解法
    public static double findMedianSortedArrays2(int[] nums1, int[] nums2) {

        int length1 = nums1.length, length2 = nums2.length;
        int totalLength = length1 + length2;
        if (totalLength % 2 == 1) {
            int midIndex = totalLength / 2;
            double median = getKthElement(nums1, nums2, midIndex + 1);
            return median;
        } else {
            int midIndex1 = totalLength / 2 - 1, midIndex2 = totalLength / 2;
            double median = (getKthElement(nums1, nums2, midIndex1 + 1) + getKthElement(nums1, nums2, midIndex2 + 1)) / 2.0;
            return median;
        }

    }

    //找中位数(二分查找法)
    public  static  int getKthElement(int[] nums1,int[] nums2,int k){
        //获取两个数组的长度
        int m = nums1.length;
        int n = nums1.length;
        //定义两个索引用来遍历数组
        int index1 = 0;
        int index2 = 0;
        while (true){
            //退出循环条件
            if(k==1){
                return  Math.min(nums1[index1],nums2[index2]);
            }
            //如果一个数组为空,说明该数组中的所有元素都被排除了,我们可以直接返回另一个数组中第k小的元素
            if(index1 == m){
                return  nums2[index2+k-1];
            }
            if(index2 == n){
                return  nums1[index1+k-1];
            }

            //为了防止数组越界
            int half = k/2;
            int newIndex1 = Math.min(m,index1+half)-1;
            int newIndex2 = Math.min(n,index2+half)-1;
            if(nums1[newIndex1]<=nums2[newIndex2]){
                //更新k
                k-=half;
                //更新index的指向
                index1=newIndex1+1;
            }else{
                //更新k
                k-=half;
                //更新index的指向
                index2=newIndex2+1;
            }

        }
    }
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值