【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)),这是一个经典的二分查找问题。
算法分析
这道题的核心思想是分割数组,主要解法包括:
- 二分查找分割法:使用二分查找找到正确的分割点
- 合并排序法:合并两个数组后找中位数(不满足时间复杂度要求)
- 双指针法:模拟合并过程(不满足时间复杂度要求)
问题本质分析
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]]
分割策略可视化
分割点计算过程
边界情况处理
算法流程图
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
各种解法对比
时间复杂度分析
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[最优解法]
空间复杂度分析
关键优化点
实际应用场景
测试用例设计
代码实现要点
-
数组交换优化:
- 确保nums1是较短的数组
- 减少二分查找的范围
-
分割点计算:
- i = (left + right) / 2
- j = (m + n + 1) / 2 - i
- 确保左右两部分元素数量平衡
-
边界条件处理:
- 使用math.MinInt32和math.MaxInt32
- 处理数组边界情况
-
分割有效性检查:
- nums1[i-1] ≤ nums2[j]
- nums2[j-1] ≤ nums1[i]
-
中位数计算:
- 奇数情况: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")
}