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

LeetCode 4:寻找两个正序数组的中位数

LeetCode 4:寻找两个正序数组的中位数

在这里插入图片描述

问题定义与核心挑战

给定两个有序(升序)数组 nums1nums2,要求找到它们的中位数,且算法时间复杂度为 O(log(m+n))mn 分别是两个数组的长度)。

中位数定义

  • 若合并后数组长度为奇数,中位数是中间位置的元素(如 [1,2,3] 的中位数是 2)。
  • 若合并后数组长度为偶数,中位数是中间两个元素的平均值(如 [1,2,3,4] 的中位数是 (2+3)/2=2.5)。

常规方法的不足

若直接合并两个数组(时间复杂度 O(m+n)),虽能解决问题,但无法满足题目对时间复杂度的严格要求(O(log(m+n)))。因此,必须利用数组有序的特性,通过 二分查找 避免完全合并。

核心思路:二分法定位分割点

中位数的本质是将合并后的数组划分为左右两部分,使得:

  • 左边所有元素 ≤ 右边所有元素;
  • 左边元素个数 ≥ 右边(奇数长度时左边多一个)。

对于两个有序数组,我们可以通过二分查找分割点,将 nums1nums2 分别划分为左右两部分,使得:

  • nums1 的左半部分 + nums2 的左半部分 nums1 的右半部分 + nums2 的右半部分;
  • 左半部分总元素数为 (m+n+1)//2(向上取整,保证奇数长度时左边多一个)。

算法步骤详解

步骤 1:统一数组长度(优化二分范围)

为了减少二分查找的范围,确保 nums1 是较短的数组(若 nums1 更长,则交换 nums1nums2)。这样,二分查找仅需在较短数组上进行,降低时间复杂度。

int m = nums1.length, n = nums2.length;
if (m > n) {// 交换数组,确保 nums1 是较短的int[] temp = nums1;nums1 = nums2;nums2 = temp;m = nums1.length;n = nums2.length;
}
步骤 2:二分查找分割点

nums1 中二分查找分割点 i,并计算 nums2 的对应分割点 j

  • i 表示 nums1 左半部分的元素个数(范围:0 ≤ i ≤ m);
  • j = (m + n + 1) // 2 - i,保证左半部分总元素数为 (m+n+1)//2(向上取整)。
int left = 0, right = m;
while (left < right) {// 向上取整,避免死循环(如 left=1, right=2 时,mid=2)int mid = (left + right + 1) / 2; int j = (m + n + 1) / 2 - mid;if (nums1[mid-1] > nums2[j]) {// i 太大,左移右边界right = mid - 1;} else {// i 太小,右移左边界left = mid;}
}
int i = left;
int j = (m + n + 1) / 2 - i;
步骤 3:处理边界值(分割点越界)

分割点 ij 可能为 0(左半部分为空)或等于数组长度(右半部分为空),需用极值(-∞+∞)处理:

// nums1 左半部分的最大值(i=0 时,左半部分为空,设为 -∞)
double nums1_left = (i == 0) ? Integer.MIN_VALUE : nums1[i-1];
// nums1 右半部分的最小值(i=m 时,右半部分为空,设为 +∞)
double nums1_right = (i == m) ? Integer.MAX_VALUE : nums1[i];
// nums2 左半部分的最大值(j=0 时,左半部分为空,设为 -∞)
double nums2_left = (j == 0) ? Integer.MIN_VALUE : nums2[j-1];
// nums2 右半部分的最小值(j=n 时,右半部分为空,设为 +∞)
double nums2_right = (j == n) ? Integer.MAX_VALUE : nums2[j];
步骤 4:计算中位数

根据合并后数组的长度(m+n)的奇偶性,计算中位数:

  • 奇数:左半部分最大值(max(nums1_left, nums2_left));
  • 偶数:左半部分最大值与右半部分最小值的平均值((max(...) + min(...)) / 2)。
if ((m + n) % 2 == 1) {// 奇数长度,中位数是左半部分最大值return Math.max(nums1_left, nums2_left);
} else {// 偶数长度,中位数是左右部分的中间值平均return (Math.max(nums1_left, nums2_left) + Math.min(nums1_right, nums2_right)) / 2.0;
}

完整代码(Java)

class Solution {public double findMedianSortedArrays(int[] nums1, int[] nums2) {int m = nums1.length, n = nums2.length;// 确保 nums1 是较短的数组,优化二分范围if (m > n) {int[] temp = nums1;nums1 = nums2;nums2 = temp;m = nums1.length;n = nums2.length;}int left = 0, right = m;while (left < right) {// 向上取整,避免死循环int mid = (left + right + 1) / 2; int j = (m + n + 1) / 2 - mid;if (nums1[mid - 1] > nums2[j]) {// i 太大,左移右边界right = mid - 1;} else {// i 太小,右移左边界left = mid;}}int i = left;int j = (m + n + 1) / 2 - i;// 处理边界值:左半部分或右半部分为空的情况double nums1_left = (i == 0) ? Integer.MIN_VALUE : nums1[i - 1];double nums1_right = (i == m) ? Integer.MAX_VALUE : nums1[i];double nums2_left = (j == 0) ? Integer.MIN_VALUE : nums2[j - 1];double nums2_right = (j == n) ? Integer.MAX_VALUE : nums2[j];// 计算中位数if ((m + n) % 2 == 1) {return Math.max(nums1_left, nums2_left);} else {return (Math.max(nums1_left, nums2_left) + Math.min(nums1_right, nums2_right)) / 2.0;}}
}

关键逻辑解析

1. 数组交换的意义

将较长数组与较短数组交换,确保二分查找在较短数组上进行,减少二分次数(如 m=3, n=5 时,二分范围是 0~3 而非 0~5)。

2. 分割点的数学意义

j = (m + n + 1) // 2 - i 保证 左半部分总元素数为 (m+n+1)//2

  • m+n 为奇数,左半部分比右半部分多 1 个元素,中位数是左半部分的最大值;
  • m+n 为偶数,左半部分与右半部分元素数相等,中位数是两部分的中间值平均。
3. 边界值处理

通过 Integer.MIN_VALUEInteger.MAX_VALUE 处理“左半部分为空”或“右半部分为空”的情况,确保比较逻辑的一致性。

4. 二分循环的细节

使用 (left + right + 1) / 2 向上取整,避免 leftright 相邻时的死循环(如 left=1, right=2 时,mid=2 而非 1,确保范围缩小)。

该方法通过 二分查找分割点 避免了数组的完全合并,将时间复杂度优化到 O(log(min(m,n)))(等价于 O(log(m+n))),是处理“两个有序数组中位数”问题的经典方案。

http://www.xdnf.cn/news/16782.html

相关文章:

  • DISTILLM:迈向大型语言模型的简化蒸馏方法
  • 基于React+Express的前后端分离的个人相册管理系统
  • OpenBayes 一周速览丨Self Forcing 实现亚秒级延迟实时流视频生成;边缘AI新秀,LFM2-1.2B采用创新性架构超越传统模型
  • 爱车生活汽车GPS定位器:智能监控与安全驾驶的守护者
  • 云原生环境里的显示变革:Docker虚拟浏览器与cpolar穿透技术实战
  • 新零售“实—虚—合”逻辑下的技术赋能与模式革新:基于开源AI大模型、AI智能名片与S2B2C商城小程序源码的研究
  • RAG:检索增强生成的范式演进、技术突破与前沿挑战
  • pytorch入门2:利用pytorch进行概率预测
  • 智慧城市SaaS平台|市政公用管理系统
  • LeetCode Hot 100 搜索旋转排序数组
  • Java项目:基于SSM框架实现的济南旅游网站管理系统【ssm+B/S架构+源码+数据库+毕业论文+远程部署】
  • Linux系统指令之 —— passwd
  • 【maven】仓库配置
  • 基于 Hadoop 生态圈的数据仓库实践 —— OLAP 与数据可视化(二)
  • 15.10 单机8卡到千卡集群!DeepSpeed实战调参手册:A100训练效率翻倍,百万成本优化实录
  • 【C++详解】深入解析多态 虚函数、虚函数重写、纯虚函数和抽象类、多态原理、重载/重写/隐藏的对⽐
  • composer 常用命令
  • Unity_XR控制手部动画
  • NVIDIA Isaac平台推动医疗AI机器人发展研究
  • C++:STL中list的使用和模拟实现
  • 常见的cms框架的webshell方法
  • JavaScript和小程序写水印的方法示例
  • 谈谈毕业工作一年后的变化
  • 【C语言】指针深度剖析(一)
  • 集成电路学习:什么是Wi-Fi无线保真度
  • Java优雅使用Spring Boot+MQTT推送与订阅
  • 使用LangChain构建法庭预定智能体:结合vLLM部署的Qwen3-32B模型
  • Accessibility Insights for Windows 使用教程
  • dubbo应用之3.0新特性(响应式编程)(2)
  • JVM 崩溃(Fatal Error)解决方法