LeetCode热题100 刷题笔记(第四天)二分 「 寻找两个正序数组的中位数」

前言

这几天不是在写文章总结黑马点评吗,感觉敲代码的能力确实有点欠缺了,主要是敲项目代码的时候,接入了AI,大部分代码都是直接按Tab键自动生成的,代码能力确实有一点退步了🤪🤪🤪,这几天学学数据结构,刷刷题🏄🏄🏄,冲冲冲一天一道,(但是我感觉可能坚持不下去🤪)

题目

给定两个大小分别为 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

🧩 解题思路

这道题的本质是:在两个有序数组中,找到合并后第 k 小的元素 ,中位数就是特殊的第 k 小元素(总长度为奇数时取中间,偶数时取中间两个的平均)。

java 复制代码
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
  int m = nums1.length;
  int n = nums2.length;
  int total = m + m;
  //总长度为奇数: 找第 (total + 1)/2 小
  //总长度为偶数: 找第 total/2 小 和 第(total/2)+1 小,再取平均
  if(total % 2 == 1) {
     return getk(nums1, nums2, 0, m-1, 0, n-1, (total+1)/2);
  } else {
     int k1 = total / 2;
     int k2 = k1 + 1;
     return (getk(nums1, nums2, 0, m-1, 0, n-1, k1) + getk(nums1, nums2, 0, m-1, 0, n-1, k2)) / 2.0;  
  }
}


// 参数含义:
// nums1, nums2:两个有序数组
// l1, r1:nums1 当前考虑的区间[l1, r1]
// l2, r2:nums2 当前考虑的区间[l2, r2]


int getk(int[] nums1, int[] nums2, int l1, int r1, int l2, int r2, int k) {
  int len1 = r1 - l1 + 1; //num1 当前区间长度
  int len2 = r2 - r2 + 1; //nums2 当前区间长度
 
  //1. 让nums1 始终是更短的数组, 减少二分次数
  if(len1 > len2) {
    return getk(nums2, nums1, l2, r2, l1, r1, k);
  }
  //2. 边界情况: 如果一个数组已经空了, 直接返回另一个数组的第k小
  if(len1 == 0){
    return nums2[l2 + k - 1];
  }
  //3. 边界情况: k=1, 返回两个数组当前元素的较小值
  if (k == 1) {
    return Math.min(nums1[l1], nums2[l2]);
  }
  //4. 二分: 分别在两个数组中提取「前 k/2 个元素」的位置
  //避免数组越界, 取 min(len1, k/2)
  int idx1 = l1 + Math.min(len1, k/2) - 1;
  int idx2 = l2 + Math.min(len2, k/2) - 1;
  //5.比较两个数组中第k/2小的元素, 排除较小的一半
  if (nums1[] > nums2[]){
    //nums2 的前k/2 个元素一定都比第k小元素小, 排除他们
    //新的k =  k-被排除的元素个数(idx2 - l2 +1)
    return getk(nums1, nums2, l1, r1,idx2 + 1, r2, k - (idx2 - l2 +1));
  } else {
    // nums1 的前 k/2 个元素一定都比第k小元素 小, 排除他们
    return getk(nums1, nums2, ids1 + 1, r1, l2, r2, k - (idx1 - l1 + 1));
  }
 
}

💡 核心逻辑拆解
1. 保证短数组优先 :if (len1 > len2) 交换两个数组,让 nums1 始终更短,减少后续二分的计算量。
2. 空数组处理 :如果一个数组区间为空,直接从另一个数组取第 k 小元素。
3. k=1 处理 :直接取两个数组当前区间首元素的较小值,就是第 1 小。
4. 二分排除

  • 每次在两个数组中取 k/2 长度的片段,比较片段末尾的元素。
  • 较小的那个片段里的所有元素,一定都比第 k 小元素小,可以直接排除。
  • 更新 k 和对应数组的区间起点,递归继续找。

✅ 示例运行

假设 nums1 = [1,3],nums2 = [2],总长度 = 3,中位数是第 2 小:

  1. 调用 getK(..., k=2)
  • len1=2, len2=1 → 不交换
  • k≠1,取 k/2=1
  • idx1=0, idx2=0
  • nums1[0]=1 < nums2[0]=2 → 排除 nums1 前 1 个元素
  • 新调用:getK(nums1, nums2, 1, 1, 0, 0, 2-1=1)
  1. 新调用 k=1 → 返回 min(nums1[1]=3, nums2[0]=2) = 2
  2. 中位数就是 2,正确。
    ⚠️ 关键技巧
  • 二分思想:每次排除 k/2 个元素,将问题规模减半,时间复杂度 O (log k) → 最终 O (log (min (m,n)))。
  • 避免越界:用 Math.min(len, k/2) 保证取的位置不超过数组当前区间长度。
  • 递归边界:空数组和 k=1 是递归的终止条件,保证不会无限递归。

如果还不理解题目建议可以去学学二分法, 可以看看我这篇文章 数据结构09(Java)-- 二分查找模板

小白啊!!!写的不好轻喷啊🤯如果觉得写的不好,点个赞吧🤪(批评是我写作的动力)

...。。。。。。。。。。。...

...。。。。。。。。。。。...

相关推荐
lkforce21 小时前
MiniMind学习笔记(二)--model_minimind.py
笔记·python·学习·minimind·minimindconfig
逻辑驱动的ken1 天前
Java高频面试考点场景题09
java·开发语言·数据库·算法·oracle·哈希算法·散列表
帅小伙―苏1 天前
力扣42接雨水
前端·算法·leetcode
AI科技星1 天前
精细结构常数α的几何本源:从第一性原理的求导证明、量纲分析与全域验证
算法·机器学习·数学建模·数据挖掘·量子计算
三品吉他手会点灯1 天前
C语言学习笔记 - 1.C概述 - 本讲内容概述
c语言·笔记·学习
6Hzlia1 天前
【Hot 100 刷题计划】 LeetCode 287. 寻找重复数 | C++ 数组判环 (快慢指针终极解法)
c++·算法·leetcode
MegaDataFlowers1 天前
26.删除有序数组中的重复项
算法
故事和你911 天前
洛谷-数据结构1-4-图的基本应用2
开发语言·数据结构·算法·深度优先·动态规划·图论
是孑然呀1 天前
【笔记】激光定位-激光切割指针偏移设置
笔记
吴可可1231 天前
C#合并首尾相连多段线实战
算法·c#