4. 寻找两个正序数组的中位数(优先队列 / (优化:归并排序))

寻找两个正序数组的中位数

给定两个大小为 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


解题思路

我们可以将该题理解为求两个有序数组中第 K 小的元素,可使用优先队列(小根堆)来解决。

先将两个数组中的元素加入到优先队列中。

  • 若队列的长度(len)为偶数:则将中间位置(len / 2 和 len / 2 - 1)的两个元素记录下来并计算中位数。
  • 若队列的长度(len)为奇数:则将中间位置(len / 2)的元素记录下来,即为中位数。

在将元素出队时,只需要前len / 2个元素出队即可,后面的元素不需要进行操作。


代码

class Solution {

    public double findMedianSortedArrays(int[] nums1, int[] nums2) {

        int m = nums1.length;
        int n = nums2.length;
        int sum = m + n;
        PriorityQueue<Integer> pqNums = new PriorityQueue<>();
        for (int i=0;i<m;i++) {

            pqNums.offer(nums1[i]);
        }
        for (int i=0;i<n;i++) {

            pqNums.offer(nums2[i]);
        }
        return getMid(pqNums);
    }

    //求中位数
    private double getMid(PriorityQueue<Integer> pqNums) {

        int flag = 0;
        double a = 0.0;
        double b = 0.0;
        int len = pqNums.size();
        if (pqNums.size() % 2 == 0) {

            while (flag <= len / 2) {

                if (flag == (len / 2) - 1) {

                    a = pqNums.poll();
                } else if (flag == len / 2) {

                    b = pqNums.poll();
                } else {

                    pqNums.poll();
                }
                flag++;    
            }
            return a / 2.0 + b / 2.0;
        } else {

            while (flag <= len / 2) {
                
                a = pqNums.poll();
                flag++;
            }
            return a * 1.0;
        }
    }
}

空间复杂度:定义了一个优先队列,长度为 m + n,所以复杂度为O(m + n)

优化(使用归并排序来进行数组的合并)

在进行归并排序将两个数组合并时,不用将两个数组全部遍历完。当 sum 为奇数:只需要遍历到 Merge 数组中元素的个数为 sum / 2 + 1 即可,然后返回此时 Merge 数组中最后一个元素即为中位数;当 sum 为偶数:要分别遍历到数组中元素个数为 sum / 2 + 1 和 sum / 2 时,分别取两种情况下的数组的最后一个元素,然后进行中位数计算。

分而治之(divide - conquer):有三个步骤

  1. 分解: 把待排序的 n 个元素的序列分解成两个子序列, 每个子序列包括 n/2 个元素。
  2. 治理: 对每个子序列分别调用归并排序MergeSort, 进行递归操作。
  3. 合并: 合并两个排好序的子序列,生成排序结果。

归并排序是一种稳定的排序,可用顺序存储结构。

代码

class Solution {

    public double findMedianSortedArrays(int[] nums1, int[] nums2) {

        int sum = nums1.length + nums2.length;
        if (sum % 2 != 0) {

            return getMid(nums1, nums2, sum / 2) * 1.0;
        } else {

            return getMid(nums1, nums2, sum / 2 - 1) / 2.0 
            			+ getMid(nums1, nums2, sum / 2) / 2.0;
        }
    }

    //归并排序(其中k为两个数组合并到的长度,再往后的元素就不需要合并进来了(节省时间))
    private double getMid(int[] nums1, int[] nums2, int k) {

        int[] Merge = new int[nums1.length + nums2.length];
        int i = 0, j = 0, m = 0;

        while(i < nums1.length&&j < nums2.length&&m <= k) {

            if (nums1[i] < nums2[j]) {

                Merge[m++] = nums1[i++];
            } else {

                Merge[m++] = nums2[j++];
            }
        }
        while (i < nums1.length&&m <= k) { //若nums1有剩余

            Merge[m++] = nums1[i++];
        }
        while (j < nums2.length&&m <= k) {

            Merge[m++] = nums2[j++];
        }
        return Merge[m - 1];
    }
}

空间复杂度:需要一个辅助向量来暂存两有序子文件归并的结果,故其辅助空间复杂度为O(m + n)
时间复杂度:O((m + n) / 2) = O(n + m)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值