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

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

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

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

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

相关推荐
handler0116 分钟前
【算法】并查集(普通/扩展/带权)模板与例题
数据结构·c++·笔记·算法·c·图论·查并集
qq74223498432 分钟前
从“感知”到“决断”:测评百度伐谋产业决策智能体的端到端推理与行动机制
人工智能·算法·百度·大模型·运筹优化
中屹指纹浏览器37 分钟前
指纹浏览器环境克隆、批量派生的风控隐患剖析与标准化新建环境实操指南
经验分享·笔记
.千余1 小时前
【C++】C++手写Vector容器:从底层源码模拟实现
开发语言·c++·经验分享·笔记·学习
元直数字电路验证1 小时前
云计算实验笔记(四):容器编排(Container Orchestration)
运维·笔记·docker·云计算
huohaiyu1 小时前
深入解析Java垃圾回收机制
java·开发语言·算法·gc
浮芷.1 小时前
鸿蒙PC端 TTS 并发调用问题详解:资源竞争与队列管理
算法·华为·开源·harmonyos·鸿蒙·鸿蒙系统
装不满的克莱因瓶2 小时前
掌握感知器的学习原理
人工智能·python·神经网络·算法·ai·卷积神经网络
Lsk_Smion2 小时前
力扣实训 _ [994].腐烂的橘子/图论
算法·leetcode·图论
轻微的风格艾丝凡2 小时前
两电平三相VSC整流模式从不控整流平滑切换至有源整流调试记录
算法·dsp·c2000