leetcode-4. 寻找两个有序数组的中位数

给定两个大小为 m 和 n 的有序数组 nums1 和 nums2。
请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。
你可以假设 nums1 和 nums2 不会同时为空。
示例 1:
nums1 = [1, 3]
nums2 = [2]
则中位数是 2.0
示例 2:
nums1 = [1, 2]
nums2 = [3, 4]
则中位数是 (2 + 3)/2 = 2.5

解题思路一

简单粗暴,先将两个数组合并,两个有序数组的合并也是归并排序中的一部分。然后根据奇数,还是偶数,返回中位数。

<?php

class Solution
{
    /**
     * 4. 寻找两个有序数组的中位数
     * @param array $nums1
     * @param array $nums2
     * @return float|int|mixed
     */
    function findMedianSortedArrays($nums1, $nums2)
    {
        $nums = []; // 合并后的新数组
        $m = count($nums1);
        $n = count($nums2);
        if ($m == 0) {//第一个数组为空
            if ($n % 2 == 0) { // 第二个数组长度为偶数
                return ($nums2[$n / 2] + $nums2[($n / 2) - 1]) / 2;
            } else {
                return $nums2[$n / 2];
            }
        }
        if ($n == 0) {//第二个数组为空
            if ($m % 2 == 0) { // 第一个数组长度为偶数
                return ($nums1[$m / 2] + $nums1[($m / 2) - 1]) / 2;
            } else {
                return $nums1[($m) / 2];
            }
        }

        $count = 0;
        $i = 0;
        $j = 0;
        while ($count != ($m + $n)) {
            if ($i == $m) {
                while ($j != $n) {
                    $nums[$count++] = $nums2[$j++]; // $nums[$count]=$nums2[$j] $count+1 $j+1

                }
                break;//跳出整个循环

            }
            if ($j == $n) {
                while ($i != $m) {
                    $nums[$count++] = $nums1[$i++];
                }
                break;
            }
            if ($nums1[$i] < $nums2[$j]) {
                $nums[$count++] = $nums1[$i++];
            } else {
                $nums[$count++] = $nums2[$j++];
            }

        }

        if ($count % 2 == 0) { // 数组长度为偶数
            return ($nums[($count / 2) - 1] + $nums[$count / 2]) / 2;
        } else {
            return $nums[$count / 2];
        }

    }

}


$solution = new Solution();
$nums1 = [1, 3, 5, 6];
$nums2 = [2];
echo ($solution->findMedianSortedArrays($nums1, $nums2)) . PHP_EOL;
$nums1 = [1, 3];
$nums2 = [2];
echo ($solution->findMedianSortedArrays($nums1, $nums2)) . PHP_EOL;
$nums1 = [1, 2];
$nums2 = [3, 4];
echo($solution->findMedianSortedArrays($nums1, $nums2)).PHP_EOL;
$nums1 = [];
$nums2 = [1];
echo($solution->findMedianSortedArrays($nums1, $nums2));
结果:
3
2
2.5
1

时间复杂度:遍历全部数组 (m+n)
空间复杂度:开辟了一个数组,保存合并后的两个数组 O(m+n)

解题思路二

上边的解题思路,时间复杂度达不到题目的要求 O(log(m+n)。看到 log,很明显,我们只有用到二分的方法才能达到。我们不妨用另一种思路,题目是求中位数,其实就是求第 k 小数的一种特殊情况,而求第 k 小数有一种算法。

数列是有序的,其实我们可以一半儿一半儿的排除。假设我们要找第 k 小数,我们可以每次循环排除掉 k/2 个数。

为了防止数组长度小于 k/2,所以每次比较 min(k/2,len(数组) 对应的数字,把小的那个对应的数组的数字排除,将两个新数组进入递归,并且 k 要减去排除的数字的个数。递归出口就是当 k=1 或者其中一个数字长度是 0 了。

<?php

class Solution
{
    /**
     * 4. 寻找两个有序数组的中位数
     * @param array $nums1
     * @param array $nums2
     * @return float|int|mixed
     */
    function findMedianSortedArrays($nums1, $nums2)
    {
        $m = count($nums1);
        $n = count($nums2);
        //将偶数和奇数的情况合并,如果是奇数,会求两次同样的 k 。
        $left = intval(($m + $n + 1) / 2); //中间最小的两个数之左边数位置
        $right = intval(($m + $n + 2) / 2);//中间最小的两个数之右边数位置
        return ($this->getKth($nums1, 0, $m - 1, $nums2, 0, $n - 1, $left)
                + $this->getKth($nums1, 0, $m - 1, $nums2, 0, $n - 1, $right))
            * 0.5;
    }

    /**
     * 求第k小
     * @param $nums1
     * @param $start1
     * @param $end1
     * @param $nums2
     * @param $start2
     * @param $end2
     * @param $k
     * @return mixed|string
     */
    private function getKth($nums1, $start1, $end1, $nums2, $start2, $end2, $k)
    {
        $len1 = $end1 - $start1 + 1;
        $len2 = $end2 - $start2 + 1;
        //让 len1 的长度小于 len2,这样就能保证如果有数组空了,一定是 len1
        if ($len1 > $len2) {
            return $this->getKth($nums2, $start2, $end2, $nums1, $start1, $end1, $k);
        }
        if ($len1 == 0) {//其中一个数组为空的情况
            return $nums2[$start2 + $k - 1];
        }
        if ($k == 1) {// 求第1小的情况
            return min($nums1[$start1], $nums2[$start2]);
        }
        $i = $start1 + min($len1, intval($k / 2)) - 1;
        $j = $start2 + min($len2, intval($k / 2)) - 1;

        if ($nums1[$i] > $nums2[$j]) {
            return $this->getKth($nums1, $start1, $end1, $nums2, $j + 1, $end2, $k - ($j - $start2 + 1));
        } else {
            return $this->getKth($nums1, $i + 1, $end1, $nums2, $start2, $end2, $k - ($i - $start1 + 1));
        }

    }

}


$solution = new Solution();
$nums1 = [1, 3, 5, 6];
$nums2 = [2];
echo ($solution->findMedianSortedArrays($nums1, $nums2)) . PHP_EOL;
$nums1 = [1, 3];
$nums2 = [2];
echo ($solution->findMedianSortedArrays($nums1, $nums2)) . PHP_EOL;
$nums1 = [1, 2];
$nums2 = [3, 4];
echo ($solution->findMedianSortedArrays($nums1, $nums2)) . PHP_EOL;
$nums1 = [];
$nums2 = [1];
echo($solution->findMedianSortedArrays($nums1, $nums2));
结果:
3
2
2.5
1

时间复杂度:每进行一次循环,我们就减少 k/2 个元素,所以时间复杂度是 O(log(k),而 k=(m+n)/2,所以最终的复杂也就是 O(log(m+n)。
空间复杂度为 O(1)。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,544评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,430评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,764评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,193评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,216评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,182评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,063评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,917评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,329评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,543评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,722评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,425评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,019评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,671评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,825评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,729评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,614评论 2 353

推荐阅读更多精彩内容