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

【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.dtcms.com/a/330230.html

相关文章:

  • 芯伯乐300kHz降压DC/DC转换器XBL4005:4.5V~40V宽电压范围,5A大电流高效输出
  • 三伍微电子GSR2406 IoT FEM 2.4G PA 射频前端模组芯片
  • 深入解析C语言嵌套结构体的内存管理与操作实践
  • linux_网络层-ip协议
  • [系统架构设计师]信息安全技术基础知识(三)
  • SpringBoot3+ Elasticsearch8 Spring-data-Elasticsearch使用
  • 多模态数据集分级方案设计与实现
  • 容器基础镜像制作
  • ETLCloud批流一体化体现在哪
  • 【Python】Python 函数基本介绍(详细版)​
  • 版图设计学习2_掌握PDK中的层定义(工艺文档精读)
  • DAY39打卡
  • 【运维进阶】管理变量和事实
  • 哥斯拉--安装、使用
  • graf示教界面技术累积
  • 数据结构摘星题库800题笔记 第2章线性表
  • [TG开发]简单的回声机器人
  • Linux信号量和信号
  • 淘汰人工巡检!企业配电室无线测温实战:0布线+240点位同步监控
  • @进程管理工具 - Glances工具详细指南
  • 20250813测试开发岗(凉)面
  • 《探索C++ set与multiset容器:深入有序唯一性集合的实现与应用》
  • 网络存储技术:数据存储架构的演进与全景解析
  • 计算机网络——协议
  • 基于SpringBoot+Vue的智能消费记账系统(AI问答、WebSocket即时通讯、Echarts图形化分析)
  • Python 类元编程(元类基础知识)
  • 推荐系统论文分享之多任务模型--PLE(二)
  • python与JavaScript的区别
  • MoviiGen1.1模型脚本调用
  • C语言队列的实现