博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Leetcode 4 Median of Two Sorted Arrays 两排序数组的中位数
阅读量:6257 次
发布时间:2019-06-22

本文共 10423 字,大约阅读时间需要 34 分钟。

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)).

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

难度为Hard.

这个题目描述很清晰, 给出两个排序好的数组, 求这两个数组的中位数. 在解这个题的过程中, 会碰到以下的问题:

  1. 先合起来重新排序是不可行的, 时间复杂度太高, 为O((m+n)log(m+n))

  2. 先归并排序也是不可行的, 时间复杂度为O(m+n)

  3. 用类似桶排的方法时间复杂度为O(m+n), 不可行

  4. 可能会碰到多种case, nums1全部大于或全部小于nums2(1,2,3 4,5,6), nums1和nums2交错(2,4,6 1,3,5), 最大最小都属于其中一个序列(1,10 3,4,5), 等等

  5. 总数为奇数和偶数的处理可能会不太一样

  6. 中位数, 或者中位点旁边的两个数, 可能都位于某个数组, 也可能各自分布在两个数组中.

题目中给定的复杂度, 只能用二分查找的方法, 但是怎么在两个数组上应用呢? 我有两种通过的方法.

第一种的思路是:

  • 假定中位数两边的数是L, R. 对总数为奇数的情况, L=R.

  • 假定L或者R在nums1中, 按二分查找定位.

  • 二分查找的中点游标为P, 在nums1中, 我们知道比P小, 比P大的有多少, 我们还需要知道, 在nums2中, P处于什么位置

  • 给定P, 在nums2中二分查找, 找到比P大, 比P小的元素数目.

  • 这样在两个数组中, 比P大的数目n1和比P小的数目n2就确定了.

  • 我们可以调整P的位置, 直到找到L和R的值为止

由于L和R可能位于nums1或者nums2中, 所以还需要假定L或R在nums2中的情况再做一次. 同样有些细节问题需要解决.

最终程序如下:

public class Solution {    /**     * AC Time Complexity: O(logm*logn)     */    public double findMedianSortedArrays(int[] nums1, int[] nums2) {        int l1 = nums1.length;        int l2 = nums2.length;        if (l1 == 0) {            if (l2 % 2 == 1) {                return nums2[l2 / 2];            } else {                return ((double) nums2[l2 / 2] + nums2[l2 / 2 - 1]) / 2;            }        }        if (l2 == 0) {            if (l1 % 2 == 1) {                return nums1[l1 / 2];            } else {                return ((double) nums1[l1 / 2] + nums1[l1 / 2 - 1]) / 2;            }        }        List
retList = new ArrayList
(); getMidLR(nums1, nums2, retList); getMidLR(nums2, nums1, retList); if (retList.size() == 0) { if (l2 % 2 == 1) { return nums2[l2 / 2]; } else { return ((double) nums2[l2 / 2] + nums2[l2 / 2 - 1]) / 2; } } Integer sum = 0; for (Integer r : retList) { sum += r; } return (double) sum / retList.size(); } public void getMidLR(int[] nums1, int[] nums2, List
ret) { Integer midL = null; Integer midR = null; int m = nums1.length; int n = nums2.length; int l = 0; int r = m - 1; int p = m / 2; while (true) { int numSmall = 0; int numBig = 0; int tl = 0; int tr = n - 1; int tp = n / 2; int curVal = nums1[p]; while (true) { if (tp == 0) { if (nums2[0] > curVal) { numSmall = 0; numBig = n; break; } else if (nums2[0] == curVal) { numSmall = 0; numBig = n - 1; break; } } if (tp == n - 1) { if (nums2[n - 1] < curVal) { numSmall = n; numBig = 0; break; } else if (nums2[n - 1] == curVal) { numSmall = n - 1; numBig = 0; break; } } if (nums2[tp] == curVal) { numSmall = tp; numBig = n - tp - 1; break; } if (nums2[tp] < curVal && curVal < nums2[tp + 1]) { numSmall = tp + 1; numBig = n - tp - 1; break; } if (tl == tr) { break; } if (nums2[tp] > curVal) { // tp move left tr = tp; tp = (tl + tr) / 2; } else { // tp move right tl = tp; tp = (tl + tr) / 2; if (tl == tp && tl < tr) { tl++; tp++; } } }// end inner while int s = numSmall + p; int b = numBig + m - p - 1; if (s == b) { midL = midR = curVal; break; } if (s == (b + 1)) { midR = curVal; } else if ((s + 1) == b) { midL = curVal; } if (midL != null && midR != null) { break; } // move p if (l == r) { break; } if (s > b) { // p move left r = p; p = (l + r) / 2; } else { // p move right l = p; p = (l + r) / 2; if (l == p && l < r) { l++; p++; } } } if (midL != null) { ret.add(midL); } if (midR != null) { ret.add(midR); } } public static void main(String[] args) { Solution s = new Solution(); int[] nums11 = { 1, 3 }; int[] nums12 = { 2 }; System.out.println(s.findMedianSortedArrays(nums12, nums11)); int[] nums21 = { 1, 2 }; int[] nums22 = { 3, 4 }; System.out.println(s.findMedianSortedArrays(nums22, nums21)); int[] nums31 = { 1, 2, 3 }; int[] nums32 = { 3, 4 }; System.out.println(s.findMedianSortedArrays(nums32, nums31)); int[] nums41 = {}; int[] nums42 = { 1 }; System.out.println(s.findMedianSortedArrays(nums41, nums42)); int[] nums51 = { 1, 2 }; int[] nums52 = { 1, 2 }; System.out.println(s.findMedianSortedArrays(nums51, nums52)); }}

但问题是, 这个算法的时间复杂度是O(logm*logn), 虽然能AC, 但比题目中要求的O(log(m+n))要高.

这里还有第二种解法:

因为如果分别做二分的话, 必然会是O(logm*logn)的复杂度, 要达到题目中要求的复杂度, 需要两个数组一起做二分.

如下实现一个方法找两个数组中第n大的数:

假定需要找nums1的下标(s1,e1)范围内nums2的下标(s2,e2)范围内的第n个大小的数, 我们先把nums1和nums2各自的中点p1, p2 找出:

图片描述

假定p1元素>=p2元素, 否则把nums1和nums2交换位置.

图片描述

对于图中黄色的部分, 即s1~p1, s2~p2, 肯定是小于等于p1元素的, 这两块元素的数目可以求出, 定义为lmargin, 如果nth<=lmargin, 那么第nth元素必然小于p1元素, p1右边的元素可以抛弃.

图片描述

问题就变成找上图的第nth元素

同样的, 依照类似的想法,

图片描述

如果p1~e1, p2~e2这两部分元素的数目, 大于(元素总数-nth), 这就说明, 第nth元素, 是大于p2元素的. p2以前这块, s2~p2是可以被抛弃的.

图片描述

问题就变成, 找上图的第nth - (p2 - s2) 个元素.

如此可以迭代下去, 到其中一对游标相遇的时候, 就很好解决了.

如上, 找到第n大的数, 问题就等于是解决了. 可以顺利找到中位数.

基本思路是这样, 还有些细节问题需要解决.

AC的代码如下:

public class Solution2 {    /**     * AC Time Complexity: O(log(m+n))     */    public double findMedianSortedArrays(int[] nums1, int[] nums2) {        int l1 = nums1.length;        int l2 = nums2.length;        // judge for empty conditions        if (l1 == 0) {            if (l2 % 2 == 1) {                return nums2[l2 / 2];            } else {                return ((double) nums2[l2 / 2] + nums2[l2 / 2 - 1]) / 2;            }        }        if (l2 == 0) {            if (l1 % 2 == 1) {                return nums1[l1 / 2];            } else {                return ((double) nums1[l1 / 2] + nums1[l1 / 2 - 1]) / 2;            }        }        boolean isOdd = (l1 + l2) % 2 == 1;        if (isOdd) {            // if odd, just return the center one            return findNth(nums1, 0, l1 - 1, nums2, 0, l2 - 1, (l1 + l2) / 2 + 1);        } else {            // if even, return the average of the center two            return ((double) findNth(nums1, 0, l1 - 1, nums2, 0, l2 - 1, (l1 + l2) / 2)                    + (double) findNth(nums1, 0, l1 - 1, nums2, 0, l2 - 1, (l1 + l2) / 2 + 1)) / 2;        }    }    public int findNth(int[] nums1, int s1, int e1, int[] nums2, int s2, int e2, int nth) {        // if one or two array indexes are trapped to one element        if (s1 == e1) {            if (s2 == e2) {                return nth == 1 ? Math.min(nums1[s1], nums2[s2]) : Math.max(nums1[s1], nums2[s2]);            }            int nval = nums2[nth + s2 - 1];            if (nval <= nums1[s1]) {                return nval;            } else {                return Math.max(nums1[s1], nums2[nth + s2 - 2]);            }        }        // the other trapped condition, rotate to do as the above        if (s2 == e2) {            return findNth(nums2, s2, e2, nums1, s1, e1, nth);        }        int p1 = (s1 + e1) / 2;        int p2 = (s2 + e2) / 2;        if (nums1[p1] >= nums2[p2]) {            boolean f1 = false;            boolean f2 = false;            // number of all elements before p1(in nums1) or p2(in nums2)            // under condition nums1[p1]>=nums2[p2],            // all of theses elements are smaller or equal than nums1[p1]            int lmargin = p1 - s1 + 1 + p2 - s2 + 1;            // new indexes            int ns1 = s1;            int ne1 = e1;            int ns2 = s2;            int ne2 = e2;            int nnth = nth;            // if nth
0) { // nums1: the right of p1(including) can be discarded ne1 = p1 - 1; f1 = true; } else if (nth <= lmargin) { // nums1: the right of p1(excluding) can be discarded ne1 = p1; f1 = true; } // number of all elements after p1(in nums1) or p2(in nums2) // under condition nums1[p1]>=nums2[p2], // all of theses elements are greater or equal than nums2[p2] int rmargin = e1 - p1 + 1 + e2 - p2 + 1; // we are finding the nth element from the beginning as well as rnth // element from the end of the two int rnth = e1 - s1 + 1 + e2 - s2 + 1 - nth + 1; // if rnth
rnth && e2 > p2) { // nums2: the left of p2(including) can be discarded nnth = nth - (p2 - s2) - 1; ns2 = p2 + 1; f2 = true; } else if (rmargin >= rnth) { // nums2: the left of p2(excluding) can be discarded nnth = nth - (p2 - s2); ns2 = p2; f2 = true; } if (f1 || f2) { // something changes, go on with the new index return findNth(nums1, ns1, ne1, nums2, ns2, ne2, nnth); } } // else reverse and find return findNth(nums2, s2, e2, nums1, s1, e1, nth); }}

这个算法的时间复杂度可以认为是O(log(m+n)).

但其实, 我对这两个算法都不太满意, 都比较冗长复杂, 暂时还没有想到更简洁, 效率更高的算法.

转载地址:http://cmxsa.baihongyu.com/

你可能感兴趣的文章
SFB 项目经验-45-用培训课件当运维文档,聪明
查看>>
使用Kubernetes创建PHP留言板系统
查看>>
时间管理,从洗碗开始
查看>>
我用EDM卖约会秘籍的半个月
查看>>
运营这个职业的诞生缘由「社区运营入门系列④」
查看>>
在VMM2012R2中使用二代虚拟机创建的模板无法创建虚拟机的解决方法
查看>>
大道至简 电话号码重新成为O2O新宠
查看>>
Office 365离线安装
查看>>
jar包与was版本不兼容怎么办
查看>>
将Windows Server 2008 R2网络升级到Windows Server 2012
查看>>
修改计算机名的注意事项
查看>>
WIN7关闭共享后怎样去掉图标上的小锁
查看>>
SRV记录注册不成功的可能的原因
查看>>
一步完成 MySQL 向 Redis 迁移
查看>>
【VMC实验室】在QCloud上创建您的SQL Cluster(4)
查看>>
我的友情链接
查看>>
卢松松:每个网站都该有个监测服务
查看>>
Memcache与MySQL并肩作战
查看>>
使用Android模拟器测试Linux驱动(1)
查看>>
验证码广告:站长增加收入新渠道
查看>>