LeetCode 寻找两个有序数组的中位数

路昭
• 阅读 2246

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


题目来源:https://leetcode-cn.com/problems/median-of-two-sorted-arrays/submissions/

题目


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

解题思路


  1. 使用方法:递归;
  2. 本题数组有序,要求中位数,但可将本题转换为求第 k 个最小数。若为奇数,则第 k 位所在的数就是中位数;若为偶数,则第 k 小的数与第 k + 1 小的数之和的一半就是中位数;
  3. 因为数组是有序的,两个数组比较求第 k 小的数,不需逐个比较,一个个剔除。可以考虑一半一半的排除,每次排除 k/2 个数字。
  4. 每次比较两个数组中第 k/2 位数(当 k 为奇数时,向下取整),当出现其中一个数组的值较小时,说明这个数组包括第 k/2 位数以及前面的数字都比第 k 位数小,直接剔除;此时 k 也要减去剔除数字的个数,递归执行这个步骤;
  5. 递归跳出的条件,当其中一个数组为空的时候,或者当 k1 的时候;
  6. k1 的时候,这时求第 1 小的数,只要比较两个数组首位数字较小即是所求的中位数;
  7. 当数组为空,且 k 不等于 1 时,这个时候,取其中不为空数组的第 k 小的数。

例子 1:

nums1: [1, 3, 5]
nums2: [1, 2, 3, 4, 5, 6, 7, 8]

这个例子中,假设要求的是第 6 小的数字。比较 k/2 也就是第 3 个数。

nums1 第 3 个数字为 5,nums2 第 3 个数字为 35 > 3,所以将下面数组的 3 个数字剔除掉,变成比较 [1, 3, 5][4, 5, 6, 7, 8],这个时候剔除 3 个数字,所以 k 需减去 3,此时 k=3。继续比较;

k/2 不能整除,向下取整,为 1,比较两个数组第一个数字,此时 1<4,剔除第一个数组的 1,剩下 [3, 5][4, 5, 6, 7, 8]k3-1=2,再次比较;

k/213<4,剔除第一个数组的 3,剩下 [5][4, 5, 6, 7, 8]k2-1=1

此时 k=1,也就是求第 1 小的数字,这个时候比较剩下两个数组的首位数字,去较小的数字即为第 1 小的数字,也就是 4

例子 2:

这里考虑数组长度小于 k/2 的情况:

nums1: [1, 2]
nums2: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

同样求第 6 小的数。

解题思路如 例子 1,先比较第 k/2 位数的大小,k/2=3。由于第一个数组长度小于 3,这时直接取最末尾的数字 2,而第二个数组取第 3 位数,也就是 3,这里 2<3,直接剔除第一个数组的两个数字,k 则变为 6-2=4

由于此时第一个数组为空,所以这个时候直接取另外一个数组第 k 小,也就是第 4 小数,也就是 4,即是本来要求的两个数组中第 6 小的数字。

因为每次循环都会减少 k/2 个元素,所以时间复杂度是O(log(k)),而 k 在本题中就是 (m+n)/2,所以最终复杂度就是 O(log(m+n))

代码实现


class Solution:
    def findMedianSortedArrays(self, nums1, nums2) -> float:
        m = len(nums1)
        n = len(nums2)
        # 这里默认为偶数情况,若为奇数,则计算两次相同的 k 值
        med_left = (m + n + 1) // 2
        med_right = (m + n + 2) // 2

        return (get_k_num(nums1, 0, m-1, nums2, 0, n-1, med_left) + get_k_num(nums1, 0, m-1, nums2, 0, n-1, med_right)) / 2

def get_k_num(nums1, head1, end1, nums2, head2, end2, k):
        len1 = end1 - head1 + 1
        len2 = end2 - head2 + 1
        # 递归跳出条件
        if (len1 == 0):
            return nums2[head2 + k - 1]
        if (len2 == 0):
            return nums1[head1 + k - 1]
        if (k == 1):
            return min(nums1[head1], nums2[head2])
        
        i = head1 + min(len1, k // 2) - 1
        j = head2 + min(len2, k // 2) - 1

        if (nums1[i] > nums2[j]):
            return get_k_num(nums1, head1, end1, nums2, j + 1, end2, k - (j - head2 + 1))
        
        else:
            return get_k_num(nums1, i + 1, end1, nums2, head2, end2, k - (i - head1 + 1))

实现效果


LeetCode 寻找两个有序数组的中位数


以上就是本篇的主要内容。

吐槽: 本篇幅解题思路写下来,回头看的时候,发现纯文字感觉逻辑会有点乱,下次争取结合图例完善这部分的内容。


欢迎关注微信公众号《书所集录》
点赞
收藏
评论区
推荐文章
22 22
4年前
【排序算法动画解】直接插入排序
本文为系列专题的第14篇文章。1.2.3.4.5.6.7.8.9.10.11.12.13.前面介绍了已经介绍了三种排序,暴力排序、冒泡排序和简单选择排序,一个共同点都是基于交换。我们可以用另一种视角来看待排序,即将一个待排序的数组看成两个部分:有序区和乱序区。在排序开始前,整个数组都是乱序区,而有序区则为空:排序开始后,有序区
Patrick Patrick
4年前
【分治法】解决中位数问题、格雷码问题以及分治法直接折半存在的问题讨论————武汉理工大学算法分析实验1
AlgorithmExperiment算法分析课实验分治法的核心思想是将问题分为若干子问题去,使规模一步步缩小,最终分到一步就能得出结果。要注意每个子问题需要性质相同而且相互不重复。采用分治法完成如下任务:i.中位数问题问题描述设X0:n1和Y0:n–1为两个数组,每个数组中含有n个已排好序的数。找出X和Y
Stella981 Stella981
3年前
LeetCode 1009. 十进制整数的反码
原文链接: LeetCode1009.十进制整数的反码(https://my.oschina.net/ahaoboy/blog/3118044)https://leetcodecn.com/problems/complementofbase10integer/(https://www.oschina.net/action/GoToL
Stella981 Stella981
3年前
LeetCode 58. 最后一个单词的长度 (java)
题目:https://leetcodecn.com/problems/lengthoflastword/submissions/(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fleetcodecn.com%2Fproblems%2Flengthoflastw
Stella981 Stella981
3年前
LeetCode 169. 求众数
原文链接: LeetCode169.求众数(https://my.oschina.net/ahaoboy/blog/3118038)https://leetcodecn.com/problems/majorityelement/(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F
Stella981 Stella981
3年前
LeetCode 338. 比特位计数
原文链接: LeetCode338.比特位计数(https://my.oschina.net/ahaoboy/blog/3117631)https://leetcodecn.com/problems/countingbits/(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%
Stella981 Stella981
3年前
LeetCode 227场周赛题解
【GiantPandaCV导语】这是LeetCode的第227场周赛的题解,本期考察的知识点有「暴力,字符串,二进制枚举」等等。比赛链接https://leetcodecn.com/contest/weeklycontest227/题目一:检查数组是否经排序和轮转得到
Stella981 Stella981
3年前
LeetCode 生命游戏,不用新数组的方式
原文链接: LeetCode生命游戏,不用新数组的方式(https://my.oschina.net/ahaoboy/blog/4958278)https://leetcodecn.com/problems/gameoflife/(https://www.oschina.net/action/GoToLink?urlhttps%3A%2
Stella981 Stella981
3年前
558. Quad Tree Intersection
https://leetcode.com/problems/quadtreeintersection/description/我觉得是用意挺好的一题目。求两个四叉树的逻辑union,可惜测试用例里面居然包含对题目外因素的检查(那个id)懒得弄了。思路其实挺简单,但是很容易忽略一个edgecase,就是当所有children的value都一致
Stella981 Stella981
3年前
Leetcode724:寻找数组的中心索引(java、python3)
寻找数组的中心索引给定一个整数类型的数组nums,请编写一个能够返回数组\\“中心索引”\\的方法。我们是这样定义数组中心索引的:数组中心索引的左侧所有元素相加的和等于右侧所有元素相加的和。如果数组不存在中心索引,那么我们应该返回1。如果数组有多个中心索引,那么我们应该返回最靠近左边的那一个。
菜园前端 菜园前端
2年前
什么是归并排序?
原文链接:什么是归并排序(mergeSort)?主要分成两部分实现,分、合操作:分:把数组分成两半,在递归地对子数组进行"分"操作,直到分成一个个单独的数合:把两个数组合并为有序数组,再对有序数组进行合并,直到全部子数组合并为一个完整数组归并排序就是采用了