今日打卡,Leetcode第四题:寻找两个正序数组的中位数,博主表示就会sorted

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

博主只会第一个暴力解法,然后将官网上的源码上添加些注释,尝试理解,分下今日刷题记录

题目描述

给定两个大小分别为 mn 的正序(从小到大)数组 nums1nums2。请你找出并返回这两个正序数组的 中位数

算法的时间复杂度应该为 O(log (m+n))

解法一:暴力合并法(不符合题目要求的时间复杂度)

这种方法最为直观,但时间复杂度为 O((m+n)log(m+n)),不符合题目要求。

py 复制代码
class Solution:
    def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
        nums = sorted(nums1 + nums2)  # 合并并排序两个数组
        n = len(nums)
        result = 0
        for i in range(n):
            result += nums[i]
        return result / n  # 这里实际上计算的是平均值而非中位数,正确的中位数计算应为:
        # 如果n为奇数,返回nums[n//2]
        # 如果n为偶数,返回(nums[n//2-1] + nums[n//2])/2

注意:上述代码中计算的是平均值而非中位数。正确的中位数计算应该是:

python 复制代码
if n % 2 == 1:
    return nums[n//2]
else:
    return (nums[n//2-1] + nums[n//2]) / 2

解法二:二分查找法(符合题目要求的时间复杂度)

这种方法的核心思想是将"寻找中位数"转化为"寻找第k小的元素"的问题。

python 复制代码
class Solution:
    def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
        def getKthElement(k):
            """
            - 主要思路:要找到第 k (k>1) 小的元素,那么就取 pivot1 = nums1[k/2-1] 和 pivot2 = nums2[k/2-1] 进行比较
            - 这里的 "/" 表示整除
            - nums1 中小于等于 pivot1 的元素有 nums1[0 .. k/2-2] 共计 k/2-1 个
            - nums2 中小于等于 pivot2 的元素有 nums2[0 .. k/2-2] 共计 k/2-1 个
            - 取 pivot = min(pivot1, pivot2),两个数组中小于等于 pivot 的元素共计不会超过 (k/2-1) + (k/2-1) <= k-2 个
            - 这样 pivot 本身最大也只能是第 k-1 小的元素
            - 如果 pivot = pivot1,那么 nums1[0 .. k/2-1] 都不可能是第 k 小的元素。把这些元素全部 "删除",剩下的作为新的 nums1 数组
            - 如果 pivot = pivot2,那么 nums2[0 .. k/2-1] 都不可能是第 k 小的元素。把这些元素全部 "删除",剩下的作为新的 nums2 数组
            - 由于我们 "删除" 了一些元素(这些元素都比第 k 小的元素要小),因此需要修改 k 的值,减去删除的数的个数
            """
            
            index1, index2 = 0, 0  # 初始化两个数组的起始索引
            while True:
                # 特殊情况处理
                if index1 == m:  # m是数组num1的长度,如果nums1已经全部被排除
                    return nums2[index2 + k - 1]  # 直接返回nums2中的第k个元素
                if index2 == n:  #n是数组num2的长度 如果nums2已经全部被排除
                    return nums1[index1 + k - 1]  # 直接返回nums1中的第k个元素
                if k == 1:  # 如果要找第1小的元素
                    return min(nums1[index1], nums2[index2])  # 返回两个数组当前位置的最小值

                # 正常情况处理
                newIndex1 = min(index1 + k // 2 - 1, m - 1)  # 计算nums1中的比较位置,防止越界
                newIndex2 = min(index2 + k // 2 - 1, n - 1)  # 计算nums2中的比较位置,防止越界
                pivot1, pivot2 = nums1[newIndex1], nums2[newIndex2]  # 取出比较值
                
                # 比较两个数组的元素,排除较小元素所在的部分
                if pivot1 <= pivot2:  # 如果nums1的元素较小
                    k -= newIndex1 - index1 + 1  # 更新k值,减去排除的元素个数
                    index1 = newIndex1 + 1  # 更新nums1的起始位置
                else:  # 如果nums2的元素较小
                    k -= newIndex2 - index2 + 1  # 更新k值,减去排除的元素个数
                    index2 = newIndex2 + 1  # 更新nums2的起始位置
        
        m, n = len(nums1), len(nums2)  # 获取两个数组的长度
        totalLength = m + n  # 计算总长度
        
        # 根据总长度的奇偶性,计算中位数
        if totalLength % 2 == 1:  # 如果总长度为奇数
            return getKthElement((totalLength + 1) // 2)  # 返回中间的元素
        else:  # 如果总长度为偶数
            return (getKthElement(totalLength // 2) + getKthElement(totalLength // 2 + 1)) / 2  # 返回中间两个元素的平均值

详细解释

二分查找法的核心思想

  1. 问题转化:中位数可以转化为找第k小的元素

    • 如果总长度为奇数,中位数是第(m+n+1)/2小的元素
    • 如果总长度为偶数,中位数是第(m+n)/2小和第(m+n)/2+1小的元素的平均值
  2. 二分排除法:每次比较两个数组中第k/2个元素,排除较小值所在数组的前k/2个元素

算法流程图解

假设有两个数组:

  • nums1 = 1, 3, 5, 7, 9
  • nums2 = 2, 4, 6, 8, 10

要找这两个数组合并后的中位数:

  1. 总长度为10,是偶数,需要找第5小和第6小的元素的平均值
  2. 寻找第5小的元素:
    • 比较nums15/2-1=nums11=3和nums25/2-1=nums21=4
    • 3<4,排除nums1的1,3,k=3,nums1现在从索引2开始
    • 比较nums13/2-1+2=nums13=7和nums23/2-1=nums20=2
    • 7>2,排除nums2的2,k=2,nums2现在从索引1开始
    • 比较nums12/2-1+2=nums12=5和nums22/2-1+1=nums21=4
    • 5>4,排除nums2的4,k=1,nums2现在从索引2开始
    • k=1,返回min(nums12, nums22)=min(5,6)=5
  3. 寻找第6小的元素(类似过程):结果为6
  4. 中位数=(5+6)/2=5.5

时间复杂度分析

  • 每次操作会排除k/2个元素
  • 总共有m+n个元素,最多需要log(m+n)次操作
  • 因此时间复杂度为O(log(m+n)),符合题目要求

空间复杂度分析

  • 只使用了常数级别的额外空间
  • 空间复杂度为O(1)

总结

  1. 暴力合并法:简单直观但不符合题目要求
  2. 二分查找法:通过每次排除一部分元素,达到O(log(m+n))的时间复杂度
  3. 关键在于将"寻找中位数"转化为"寻找第k小元素"的问题
  4. 通过比较两个数组中第k/2个元素,每次可以排除至少k/2个元素
相关推荐
江畔柳前堤5 小时前
大语言模型分布式训练:从并行策略到万卡工程的系统梳理
人工智能·分布式·深度学习·算法·目标检测·机器学习·语言模型
Forever Nore6 小时前
LeetCode 4 寻找两个正序数组的中位数 - 二分
算法·leetcode
罗西的思考8 小时前
【OpenClaw具身硬件】MiniClaw 阅读笔记---(1)基础
人工智能·算法·机器学习
蛋先生DX9 小时前
大模型参数存储格式揭秘:BF不是男朋友
深度学习·算法·llm
爱跳舞的烤冷面9 小时前
自学嵌入式第22天(数据结构——哈希)
数据结构·算法·哈希算法
猎嘤一号9 小时前
博弈论(Game Theory)的理论、算法与工程
人工智能·算法·安全·博弈论
月光船幽幽10 小时前
影子模式下保护 logits 不被修改
人工智能·python·算法
马拉AI10 小时前
腾讯开源 Agent 记忆系统,AI“换对话就忘”的问题有了新解法(附安装使用教程)
人工智能·算法·开源·科研
地平线开发者11 小时前
征程6工具链模型X86推理方式说明
算法
地平线开发者11 小时前
【征程6】校准量化中HistogramObserver解析
算法