当前位置: 首页 > ai >正文

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

文章目录

  • 4. 寻找两个正序数组的中位数
    • 题目描述
    • 示例 1:
    • 示例 2:
    • 提示:
    • 解题思路
      • 算法分析
      • 问题本质分析
      • 二分查找分割算法详解
      • 分割策略可视化
      • 分割点计算过程
      • 边界情况处理
      • 算法流程图
      • 各种解法对比
      • 时间复杂度分析
      • 空间复杂度分析
      • 关键优化点
      • 实际应用场景
      • 测试用例设计
      • 代码实现要点
    • 完整题解代码

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

提示:

  • nums1.length == m
  • nums2.length == n
  • 0 <= m <= 1000
  • 0 <= n <= 1000
  • 1 <= m + n <= 2000
  • -10^6 <= nums1[i], nums2[i] <= 10^6

解题思路

这道题要求时间复杂度为 O(log(m+n)),这是一个经典的二分查找问题。

算法分析

这道题的核心思想是分割数组,主要解法包括:

  1. 二分查找分割法:使用二分查找找到正确的分割点
  2. 合并排序法:合并两个数组后找中位数(不满足时间复杂度要求)
  3. 双指针法:模拟合并过程(不满足时间复杂度要求)

问题本质分析

graph TDA[寻找两个正序数组中位数] --> B[分割策略]B --> C[二分查找分割点]B --> D[确保分割正确性]B --> E[计算中位数]C --> F[在较短数组中二分]D --> G[左半部分 ≤ 右半部分]E --> H[奇数取最大值]E --> I[偶数取平均值]F --> J[时间复杂度O_log_min_m_n]G --> K[边界条件处理]H --> L[返回结果]I --> L

二分查找分割算法详解

flowchart TDA[输入两个正序数组] --> B[确保nums1较短]B --> C[初始化二分查找范围]C --> D[计算分割点i和j]D --> E[检查分割是否有效]E --> F{分割有效?}F -->|是| G[计算中位数]F -->|否| H{调整方向}H -->|i太大| I[减小右边界]H -->|i太小| J[增大左边界]I --> DJ --> DG --> K[返回结果]D --> L[i = left+right/2]D --> M[j = m+n+1/2 - i]E --> N[检查四个边界值]N --> O[nums1[i-1] ≤ nums2[j]]N --> P[nums2[j-1] ≤ nums1[i]]

分割策略可视化

nums1: [1, 3, 5, 7]
nums2: [2, 4, 6, 8]
分割策略
左半部分: nums1[0..i-1] + nums2[0..j-1]
右半部分: nums1[i..m-1] + nums2[j..n-1]
要求: 左半部分所有元素 ≤ 右半部分所有元素
中位数 = 左半部分最大值 或 左右半部分边界值平均值

分割点计算过程

示例: nums1=[1,3], nums2=[2]
总长度: 3, 中位数位置: 2
尝试分割点i=1
左半部分: nums1[0..0] + nums2[0..0] = [1] + [2]
右半部分: nums1[1..1] + nums2[1..1] = [3] + []
检查分割有效性
max(左半部分) = max(1,2) = 2
min(右半部分) = min(3,∞) = 3
2 ≤ 3 ✓ 分割有效
中位数 = 2

边界情况处理

边界情况
i=0时
i=m时
j=0时
j=n时
nums1[i-1] = -∞
nums1[i] = +∞
nums2[j-1] = -∞
nums2[j] = +∞
确保比较逻辑正确

算法流程图

flowchart TDA[开始] --> B[确保nums1较短]B --> C[初始化left=0, right=m]C --> D[left ≤ right?]D -->|否| E[返回0.0]D -->|是| F[计算i和j]F --> G[获取四个边界值]G --> H[检查分割有效性]H --> I{分割有效?}I -->|是| J{总长度奇偶?}I -->|否| K{调整方向?}J -->|奇数| L[返回左半部分最大值]J -->|偶数| M[返回左右边界平均值]K -->|i太大| N[right = i-1]K -->|i太小| O[left = i+1]N --> DO --> DL --> P[结束]M --> P

各种解法对比

解法对比
二分查找分割法
合并排序法
双指针模拟法
时间O_log_min_m_n
空间O_1
满足题目要求
时间O_m+n
空间O_m+n
不满足要求
时间O_m+n
空间O_1
不满足要求
推荐解法
不推荐
不推荐

时间复杂度分析

graph TDA[时间复杂度分析] --> B[二分查找]B --> C[查找范围: min(m,n)]C --> D[每次查找: O_1]D --> E[总时间: O_log_min_m_n]E --> F[满足题目要求O_log_m+n]F --> G[最优解法]

空间复杂度分析

空间复杂度分析
额外空间使用
几个局部变量
常数空间O_1
空间效率最优
原地算法

关键优化点

优化策略
数组交换
边界处理
提前返回
确保nums1较短
使用正负无穷
找到分割点立即返回
减少二分查找范围
避免边界判断错误
提高执行效率

实际应用场景

应用场景
数据库查询
统计分析
机器学习
金融分析
分位数计算
数据分布分析
特征工程
风险评估
核心算法组件

测试用例设计

测试用例
基础功能
边界情况
性能测试
简单数组
复杂数组
不同长度
空数组
单元素数组
最大最小值
最大长度数组
大量重复元素
验证正确性
验证性能

代码实现要点

  1. 数组交换优化

    • 确保nums1是较短的数组
    • 减少二分查找的范围
  2. 分割点计算

    • i = (left + right) / 2
    • j = (m + n + 1) / 2 - i
    • 确保左右两部分元素数量平衡
  3. 边界条件处理

    • 使用math.MinInt32和math.MaxInt32
    • 处理数组边界情况
  4. 分割有效性检查

    • nums1[i-1] ≤ nums2[j]
    • nums2[j-1] ≤ nums1[i]
  5. 中位数计算

    • 奇数情况:max(nums1[i-1], nums2[j-1])
    • 偶数情况:(max + min) / 2

这个问题的关键在于理解分割策略掌握二分查找技巧,通过巧妙的分割将两个数组的问题转化为单个数组的二分查找问题,实现高效的中位数计算。

完整题解代码

package mainimport ("fmt""math"
)// findMedianSortedArrays 寻找两个正序数组的中位数
// 时间复杂度: O(log(min(m,n)))
// 空间复杂度: O(1)
func findMedianSortedArrays(nums1 []int, nums2 []int) float64 {// 确保nums1是较短的数组,这样可以减少二分查找的范围if len(nums1) > len(nums2) {nums1, nums2 = nums2, nums1}m, n := len(nums1), len(nums2)left, right := 0, m// 二分查找for left <= right {// 前一部分包含 nums1[0 .. i-1] 和 nums2[0 .. j-1]// 后一部分包含 nums1[i .. m-1] 和 nums2[j .. n-1]i := (left + right) / 2j := (m+n+1)/2 - i// nums1[i-1], nums1[i], nums2[j-1], nums2[j] 分别表示// nums1 和 nums2 中前一部分的最大值和后一部分的最小值// 当 i = 0 时,nums1[i-1] 不存在,我们将其设为负无穷// 当 i = m 时,nums1[i] 不存在,我们将其设为正无穷nums1LeftMax := math.MinInt32if i > 0 {nums1LeftMax = nums1[i-1]}nums1RightMin := math.MaxInt32if i < m {nums1RightMin = nums1[i]}// 当 j = 0 时,nums2[j-1] 不存在,我们将其设为负无穷// 当 j = n 时,nums2[j] 不存在,我们将其设为正无穷nums2LeftMax := math.MinInt32if j > 0 {nums2LeftMax = nums2[j-1]}nums2RightMin := math.MaxInt32if j < n {nums2RightMin = nums2[j]}// 前一部分的最大值应该小于等于后一部分的最小值if nums1LeftMax <= nums2RightMin && nums2LeftMax <= nums1RightMin {// 找到了正确的分割点if (m+n)%2 == 1 {// 奇数个元素,中位数是前一部分的最大值return float64(max(nums1LeftMax, nums2LeftMax))} else {// 偶数个元素,中位数是前一部分的最大值和后一部分的最小值的平均值return float64(max(nums1LeftMax, nums2LeftMax)+min(nums1RightMin, nums2RightMin)) / 2.0}} else if nums1LeftMax > nums2RightMin {// nums1 的前一部分太大,需要减小right = i - 1} else {// nums1 的前一部分太小,需要增大left = i + 1}}// 正常情况下不会到达这里return 0.0
}// 辅助函数
func max(a, b int) int {if a > b {return a}return b
}func min(a, b int) int {if a < b {return a}return b
}func main() {// 测试用例1nums1 := []int{1, 3}nums2 := []int{2}result1 := findMedianSortedArrays(nums1, nums2)fmt.Printf("示例1: nums1 = %v, nums2 = %v\n", nums1, nums2)fmt.Printf("输出: %.5f\n", result1)fmt.Println("期望: 2.00000")fmt.Println()// 测试用例2nums3 := []int{1, 2}nums4 := []int{3, 4}result2 := findMedianSortedArrays(nums3, nums4)fmt.Printf("示例2: nums1 = %v, nums2 = %v\n", nums3, nums4)fmt.Printf("输出: %.5f\n", result2)fmt.Println("期望: 2.50000")fmt.Println()// 额外测试用例nums5 := []int{0, 0}nums6 := []int{0, 0}result3 := findMedianSortedArrays(nums5, nums6)fmt.Printf("额外测试: nums1 = %v, nums2 = %v\n", nums5, nums6)fmt.Printf("输出: %.5f\n", result3)fmt.Println("期望: 0.00000")fmt.Println()nums7 := []int{}nums8 := []int{1}result4 := findMedianSortedArrays(nums7, nums8)fmt.Printf("额外测试: nums1 = %v, nums2 = %v\n", nums7, nums8)fmt.Printf("输出: %.5f\n", result4)fmt.Println("期望: 1.00000")
}
http://www.xdnf.cn/news/17664.html

相关文章:

  • 教育元宇宙:一场重构教育生态的数字革命
  • 软件架构重构:从混沌到有序的系统性演进
  • Pycharm选好的env有包,但是IDE环境显示无包
  • Avalon-MM协议
  • MySQL 到 ClickHouse 明细分析链路改造:数据校验、补偿与延迟治理
  • React常见的Hooks
  • 华为认证的HCIE是永久的吗?
  • 使用TexLive与VScode排版论文
  • Verilog功能模块--SPI主机和从机(02)--SPI主机设计思路与代码解析
  • PyTorch基础(使用TensorFlow架构)
  • Deep Agents:用于复杂任务自动化的 AI 代理框架
  • Debian 网络服务管理的深度解析:传统与现代工具的碰撞
  • 肖臻《区块链技术与应用》第十二讲:比特币是匿名的吗?—— 深入解析匿名性、隐私风险与增强技术
  • VBS 时间函数
  • Redis命令大全
  • 调整UOS在VMware中的分辨率
  • 肖臻《区块链技术与应用》第九讲:比特币交易的“智能”核心:深入解析脚本语言Script
  • Windows已经安装了一个MySQL8,通过修改配置文件的端口号跑2个或多个Mysql服务方法,并注册为系统服务
  • 08--深入解析C++ list:高效操作与实现原理
  • DeepSeek-R1-0528 推理模型完整指南:领先开源推理模型的运行平台与选择建议
  • Android性能优化:架构层面的性能考量
  • Web 服务详解:HTTP 与 HTTPS 配置
  • 超详细!VMware12 安装win7操作系统
  • Linux下命名管道和共享内存
  • 邦纳BANNER相机视觉加镜头PresencePLUSP4 RICOH FL-CC2514-2M工业相机
  • 腾讯codebuddy.ai 安装实测【从零开始开发在线五子棋游戏:完整开发记录】
  • iceberg FlinkSQL 特性
  • QT(概述、基础函数、界面类、信号和槽)
  • 【SpringBoot】08 容器功能 - SpringBoot底层注解汇总大全
  • 《汇编语言:基于X86处理器》第13章 高级语言接口(2)