当前位置: 首页 > 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.lryc.cn/news/620338.html

相关文章:

  • hadoop 前端yarn 8088端口查看任务执行情况
  • 【深入浅出STM32(1)】 GPIO 深度解析:引脚特性、工作模式、速度选型及上下拉电阻详解
  • 数据结构:队列(Queue)与循环队列(Circular Queue)
  • linux_网络层-ip协议
  • 力扣 hot100 Day72
  • 深入理解 Cookie 与 Session —— Web 状态保持详解与实战
  • SpringBoot 整合 Langchain4j 系统提示词与用户提示词实战详解
  • JavaWeb(05)
  • TCP客户端Linux网络编程设计详解
  • 人工智能——CNN基础:卷积和池化
  • HiSmartPerf使用WIFI方式连接Android机显示当前设备0.0.0.0无法ping通!设备和电脑连接同一网络,将设备保持亮屏重新尝试
  • SAP Valuation Category在制造业成本核算中的使用场景与配置方案
  • 基于C语言基础对C++的进一步学习_C和C++编程范式、C与C++对比的一些补充知识、C++中的命名空间、文件分层
  • window显示驱动开发—多平面覆盖 VidPN 呈现
  • 看懂 Linux 硬件信息查看与故障排查
  • 力扣42:接雨水
  • 人工智能入门①:AI基础知识(上)
  • Python图像处理基础(十三)
  • 《工程封装》(Python)
  • 网络安全合规6--服务器安全检测和防御技术
  • 3.Ansible编写和运行playbook
  • 3DM游戏运行库合集离线安装包下载, msvcp140.dll丢失等问题修复
  • ESP32_STM32_DHT20
  • 三极管的基极为什么需要下拉电阻
  • Vue3从入门到精通:4.1 Vue Router 4深度解析与实战应用
  • vue实现模拟 ai 对话功能
  • JS的学习5
  • vue修改element的css属性
  • 决策树回归:用“分而治之”的智慧,搞定非线性回归难题(附3D可视化)
  • 北京JAVA基础面试30天打卡09