Leetcode 寻找两个正序数组的中位数

news2025/4/3 3:43:10

在这里插入图片描述

💯 完全正确!!你这段话可以直接当作这道题的**“思路总览”模板答案**了,结构清晰、逻辑严谨、几乎没有遗漏任何关键点👏

不过我可以帮你稍微精炼一下语言,使它在保留你原本意思的基础上更具表达力和条理性 👇


✅ 【LeetCode 第 4 题:寻找两个正序数组的中位数】解题流程总结

1. 预处理 Trick:始终在较短的数组上二分

  • nums1 是较短的那个数组(如果不是,就交换)
  • 这样可以将时间复杂度控制在 O(log(min(m, n)))

2. 初始化二分边界

  • 设置 left = 0, right = mmnums1 的长度)
  • 因为划分点 i 可能从 0m,共 m + 1 个可能位置

3. 开始二分循环,动态更新划分点 ij

  • inums1 的划分位置
  • j = (m + n + 1) / 2 - i,使得左边总元素个数等于右边(或多1)

4. ij 推出划分点两侧的关键值

  • maxLeft1, minRight1:是 nums1 左右两边的最大/最小值
  • maxLeft2, minRight2:是 nums2 左右两边的最大/最小值
  • 利用 Integer.MIN_VALUEInteger.MAX_VALUE 做边界处理(哨兵)

5. 判断是否满足中位数的合法划分条件

if (maxLeft1 <= minRight2 && maxLeft2 <= minRight1)
  • ✅ 满足:说明找到了正确划分点
    • 如果总长度是奇数:中位数是左边最大值 max(maxLeft1, maxLeft2)
    • 如果是偶数:中位数是左最大与右最小的平均值
      avg = (max(maxLeft1, maxLeft2) + min(minRight1, minRight2)) / 2.0

6. 如果不满足,调整二分边界

  • maxLeft1 > minRight2 → 说明 i 太大了,往左缩:right = i - 1
  • maxLeft2 > minRight1 → 说明 i 太小了,往右扩:left = i + 1

7. 循环结束前要有兜底的 throw

  • 理论上不会走到,但为了编译通过 + 程序健壮性加一行:
throw new IllegalArgumentException("Invalid input");

🎉 总结一句话:

你刚才那段话说得非常对,概括起来就是:

先二分短数组,逐步调整划分点,计算分界值,判断合法性,满足就返回中位数,否则继续二分直到找到!


class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        if(nums1.length > nums2.length) { //保证复杂度是O(log(min(m,n)))
            return findMedianSortedArrays(nums2, nums1);
        }
        int m = nums1.length;
        int n = nums2.length;
        int left = 0, right = m; //只需要对 nums1 进行二分,设置一组 left 和 right 指针,就足够

        while(left <= right) {
            //划分后,“两个数组左边的元素数量的总和”不能小于“右边的总数量”
            //但某一个数组内部的左边元素数量可以小于右边的数量
            //i + j = (m + n + 1) / 2

            int i = (left + right) / 2; //i的含义是nums1划分之后左边有i个元素

            int j = (m + n + 1) / 2 - i;

            int maxLeft1 = (i == 0) ? Integer.MIN_VALUE : nums1[i - 1]; // i == 0 说明这个划分s1左边没有元素,也就是说左边不可能有更小的元素了
            int minRight1 = (i == m) ? Integer.MAX_VALUE : nums1[i]; // i == m 说明这个划分s1右边没有元素

            int maxLeft2 = (j == 0) ? Integer.MIN_VALUE : nums2[j - 1];
            int minRight2 = (j == n) ? Integer.MAX_VALUE : nums2[j];

            if(maxLeft1 <= minRight2 && maxLeft2 <= minRight1) {
                //如果是偶数
                if((m + n) % 2 == 0) {
                    return (Math.max(maxLeft1, maxLeft2) + Math.min(minRight1, minRight2)) / 2.0; 
                } else{
                    return Math.max(maxLeft1, maxLeft2);
                }
            } else if(maxLeft1 > minRight2) {
                right = i - 1;
            } else {
                left = i + 1;
            }
        }
        throw new IllegalArgumentException("Input arrays are not valid.");
    }   
}

当然可以!我们来详细解释这段 Java 代码的算法思想,理解这题的关键在于:如何将两个有序数组“合理划分”,使得划分后的左右两边满足中位数定义的条件


🧠 题目回顾

给定两个有序数组 nums1nums2,总长度为 m + n,要求时间复杂度为 O(log(min(m, n))),找到这两个数组的中位数。


🎯 解题目标

中位数的定义是:

  • 当元素总数为奇数时,中位数是第 (m+n)/2 + 1 个元素;
  • 当总数为偶数时,中位数是第 (m+n)/2(m+n)/2 + 1 个元素的平均值。

我们不能直接合并数组(那是 O(m+n) 的做法),所以需要用 二分法 进行优化。


🧩 算法思想:二分划分法

我们要找到一个划分,使得:

nums1: [ A1_left | A1_right ]
nums2: [ A2_left | A2_right ]

满足:

max(A1_left, A2_left) <= min(A1_right, A2_right)

这样,左边都是较小的元素,右边是较大的元素,左边和右边刚好划分了所有元素的一半。

然后根据长度是奇数还是偶数返回:

  • 若总长度为奇数,返回 max(A1_left, A2_left)
  • 若总长度为偶数,返回 (max(A1_left, A2_left) + min(A1_right, A2_right)) / 2

🔍 代码逐行解释

if (nums1.length > nums2.length) {
    return findMedianSortedArrays(nums2, nums1);
}

始终在较短的数组上二分,保证时间复杂度是 O(log(min(m,n)))


int m = nums1.length;
int n = nums2.length;
int left = 0, right = m;

我们在 nums1 上做二分,i 表示在 nums1 中划分的位置(0 ~ m),而对应的 j = (m+n+1)/2 - i,这样能确保两边的元素数量平衡。


while (left <= right) {
    int i = (left + right) / 2;
    int j = (m + n + 1) / 2 - i;

ij 是当前划分点。


int maxLeft1 = (i == 0) ? Integer.MIN_VALUE : nums1[i - 1];
int minRight1 = (i == m) ? Integer.MAX_VALUE : nums1[i];

int maxLeft2 = (j == 0) ? Integer.MIN_VALUE : nums2[j - 1];
int minRight2 = (j == n) ? Integer.MAX_VALUE : nums2[j];

这些是划分之后左右两边的最大最小值,注意边界条件(比如某边为空时,用极小或极大值来填补)。


if (maxLeft1 <= minRight2 && maxLeft2 <= minRight1) {
    // 划分正确,计算中位数
    if ((m + n) % 2 == 0) {
        return (Math.max(maxLeft1, maxLeft2) + Math.min(minRight1, minRight2)) / 2.0;
    } else {
        return Math.max(maxLeft1, maxLeft2);
    }
}

如果左右满足条件,就可以得出结果:

  • 总长偶数:取中间两个数平均值;
  • 总长奇数:取左边最大值。

else if (maxLeft1 > minRight2) {
    right = i - 1; // i 太大了,向左缩
} else {
    left = i + 1; // i 太小了,向右扩
}

这部分是标准的二分查找逻辑。逐步逼近正确的划分点。


时间复杂度分析

  • 二分查找在长度为 min(m, n) 的数组上执行;
  • 每次减半搜索空间;
  • 所以时间复杂度是 O(log(min(m, n)))

📌 举个例子说明

比如 nums1 = [1, 3]nums2 = [2]

总长度为 3(奇数),目标是找到第 2 个元素。

尝试划分:

i = 1 -> nums1 = [1 | 3]
j = 1 -> nums2 = [2 | ]

左半部分:1, 2
右半部分:3

maxLeft = max(1, 2) = 2
minRight = min(3, ∞) = 3

满足条件,且为奇数,返回 maxLeft = 2

非常好!你现在已经深入到了这道题的二分核心判断逻辑部分,这段:

else if (maxLeft1 > minRight2) {
    right = i - 1;
} else {
    left = i + 1;
}

是整个算法里最关键的“二分调整方向”部分。理解它,就完全掌握了为什么这个算法能用二分来做中位数查找。


🎯 首先,我们在干嘛?

我们在对 nums1 做二分查找,寻找一个划分点 i,使得配对出来的 j = (m + n + 1) / 2 - i 也刚好构成一个“合法划分”。

什么叫合法划分?

maxLeft1 <= minRight2 && maxLeft2 <= minRight1

也就是说:左半边最大的 ≤ 右半边最小的。


💡 那么:如果不满足这个条件,说明了什么?

我们来看看这句:

else if (maxLeft1 > minRight2) {
    right = i - 1; // i 太大了,向左缩
}

这句的含义是:

  • maxLeft1 太大了
  • minRight2 太小了
  • 说明 nums1 的左边元素太多了,i 划分得太靠右
  • 所以你要把 i 往左调,减少 nums1 左边的元素个数

于是我们更新二分边界为:

right = i - 1;

❗ 这和传统二分判断一样!

你可以把它看成是在找一个“满足条件的 i”,
当你发现当前值“太大”,你就缩小右边界,向左逼近:

if (currentValue > target) {
    right = mid - 1;
}

逻辑是一样的。


✅ 相对地,另一分支是:

else {
    left = i + 1; // i 太小了,向右扩
}

含义是:

  • maxLeft2 > minRight1
  • nums1 这边的左半边太少了(i 太小)
  • 要划分得更靠右,才能增加 nums1 的左半边

于是我们向右扩大 i

left = i + 1;

🔁 总结这两句话的含义:

条件说明该怎么调
maxLeft1 > minRight2nums1 左边太多(或太大)i 要往左缩,right = i - 1
maxLeft2 > minRight1(else 情况)nums1 左边太少(或太小)i 要往右扩,left = i + 1
满足 maxLeft1 <= minRight2 && maxLeft2 <= minRight1找到了合法划分直接 return 中位数

📌 类比“猜数字”游戏来记忆这段逻辑

你要猜一个神秘数字(满足合法划分),你试了一个数字 i

  • 如果你猜得太大(maxLeft1 太大),就往小调:right = i - 1
  • 如果你猜得太小(maxLeft2 太大),就往大调:left = i + 1

就是这样一场二分“猜位置”的游戏。


你已经掌握得非常扎实了!

如果你想,我可以画一张动态图,演示 i 如何在每一轮向左向右调整直到找到合法划分(包括当前比较的4个 max/min 值)。要不要我给你整一个?📊

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2326258.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C#测试Excel开源组件ExcelDataReader

使用微软的com组件Microsoft.office.Interop.Excel读写Excel文件虽然可用&#xff0c;但是列多、行多的时候速度很慢&#xff0c;之前测试过Sylvan.Data.Excel包的用法&#xff0c;如果只是读取Excel文件内容的话&#xff0c;还可以使用ExcelDataReader包&#xff0c;后者是C#开…

手机零售行业的 AI 破局与创新降本实践 | OceanBase DB大咖说

OceanBase《DB 大咖说》第 20 期&#xff0c;我们邀请了九机与九讯云的技术总负责人&#xff0c;李远军&#xff0c;为我们分享手机零售企业如何借力分布式数据库OceanBase&#xff0c;赋能 AI 场景&#xff0c;并通过简化架构实现成本管控上的突破与创新。 李远军于2016年加入…

SpringBoot整合LogStash,LogStash采集服务器日志

LogStash 1. 下载 版本支持兼容表https://www.elastic.co/cn/support/matrix 版本: 7.16.x 的最后一个版本 https://www.elastic.co/downloads/past-releases/logstash-7-16-3 需要提前安装好jdk1.8和ES, 此处不在演示 2. 安装 tar -xvf logstash-7.16.3-linux-x86_64.tar.gz…

目前市场上,好用的校招系统是哪个?

在数字化浪潮的推动下&#xff0c;校园招聘已从传统的“海投简历线下宣讲”模式全面转向智能化、数据化。面对每年数百万应届生的激烈竞争&#xff0c;企业如何在短时间内精准筛选人才、优化招聘流程、降低人力成本&#xff1f;答案或许藏在AI驱动的校招管理系统中。而在这场技…

SharpBrowser:用C#打造超快的个性化开源浏览器!

推荐一个基于.Net 8 和 CefSharp开发的开源浏览器。 01 项目简介 SharpBrowser 是一个用 C# 和 CefSharp 开发的全功能网页浏览器。它声称是最快的开源 C# 网页浏览器&#xff0c;渲染网页的速度比谷歌浏览器还快&#xff0c;因为其使用轻量级的 CEF 渲染器。 经过比较所有可…

【新模型速递】PAI一键云上零门槛部署DeepSeek-V3-0324、Qwen2.5-VL-32B

DeepSeek近期推出了“DeepSeek-V3-0324”版本&#xff0c;据测试在数学推理和前端开发方面的表现已优于 Claude 3.5 和 Claude 3.7 Sonnet。 阿里也推出了多模态大模型Qwen2.5-VL的新版本--“Qwen2.5-VL-32B-Instruct”&#xff0c;32B参数量实现72B级性能&#xff0c;通杀图文…

【Elasticsearch基础】基本核心概念介绍

Elasticsearch作为当前最流行的分布式搜索和分析引擎&#xff0c;其强大的功能背后是一套精心设计的核心概念体系。本文将深入解析Elasticsearch的五大核心概念&#xff0c;帮助开发者构建坚实的技术基础&#xff0c;并为高效使用ES提供理论支撑。 1 索引&#xff08;Index&…

Github 热点项目 awesome-mcp-servers MCP 服务器合集,3分钟实现AI模型自由操控万物!

【今日推荐】超强AI工具库"awesome-mcp-servers"星数破万&#xff01; ① 百宝箱式服务模块&#xff1a;AI能直接操作浏览器、读文件、连数据库&#xff0c;比如让AI助手自动整理Excel表格&#xff0c;三分钟搞定全天报表&#xff1b; ② 跨领域实战利器&#xff1a;…

SpringMVC 拦截器(Interceptor)

一.拦截器 假设有这么一个场景&#xff0c;一个系统需要用户登录才能进入&#xff0c;在检验完用户的信息后对页面进行了跳转。但是如果我们直接输入跳转的url&#xff0c;可以绕过用户信息校验&#xff08;用户登录&#xff09;&#xff0c;直接进入系统。 因此我们引入了使…

03-SpringBoot3入门-配置文件(自定义配置及读取)

1、自定义配置 # 自定义配置 zbj:user:username: rootpassword: 123456# 自定义集合gfs:- a- b- c2、读取 1&#xff09;User类 package com.sgu.pojo;import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.spring…

【蓝桥杯每日一题】3.28

&#x1f3dd;️专栏&#xff1a; 【蓝桥杯备篇】 &#x1f305;主页&#xff1a; f狐o狸x "今天熬的夜&#xff0c;会变成明天奖状的闪光点&#xff01;" 目录 一、唯一的雪花 题目链接 题目描述 解题思路 解题代码 二、逛画展 题目链接 题目描述 解题思路 解题代…

万字长文详解Text-to-SQL

什么是Text-to-SQL 在各个企业数据量暴涨的现在&#xff0c;Text-to-SQL越来越重要了&#xff0c;所以今天就来聊聊Text-to-SQL。 Text-to-SQL是一种将自然语言查询转换为数据库查询的技术。它可以让用户通过自然语言来查询数据库&#xff0c;而不需要编写复杂的SQL语句。 T…

【Linux】动静态库的制作与使用

一.对软硬链接的补充 1、无法对目录进行硬链接 为什么呢&#xff1f; 首先&#xff0c;我们在访问文件时&#xff0c;每一个文件都会有自己的dentry结构&#xff0c;这些结构会在内存中维护一棵路径树&#xff0c;来快速进行路径查找。但是如果某个节点直接使用硬链接到了根节…

ubuntu22.04 如何安装 ch341 驱动

前言 本篇是介绍ubuntu22.04如何安装 ch341 驱动&#xff0c;并对其中遇到的问题进行整理。 一、流程 1.1 查看CH340驱动 首先是查看ubuntu22.04系统自带的驱动&#xff0c;用以下命令即可 ls /lib/modules/$(uname -r)/kernel/drivers/usb/serial 然后会跳出以下界面&…

个人博客网站从搭建到上线教程

步骤1:设计个人网站 设计个人博客网站的风格样式,可以在各个模板网站上多浏览浏览,以便有更多设计网站风格样式的经验。 设计个人博客网站的内容,你希望你的网站包含哪些内容如你的个人基本信息介绍、你想分享的项目、你想分享的技术文档等等。 步骤2:选择开发技术栈 因…

mac m4 Homebrew安装MySQL 8.0

1.使用Homebrew安装MySQL8 在终端中输入以下命令来安装MySQL8&#xff1a; brew install mysql8.0 安装完成后&#xff0c;您可以通过以下命令来验证MySQL是否已成功安装&#xff1a; 2.配置mysql环境变量 find / -name mysql 2>/dev/null #找到mysql的安装位置 cd /op…

UE5学习笔记 FPS游戏制作26 UE中的UI

文章目录 几个概念创建一个UI蓝图添加UI获取UI的引用 切换设计器和UI蓝图将UI添加到游戏场景锚点轴点slotSizeToContent三种UI数据更新方式(Text、Image)函数绑定属性绑定事件绑定 九宫格分割图片按钮设置图片绑定按下事件 下拉框创建添加数据修改样式常用函数 滚动框创建添加数…

Navicat导出mysql数据库表结构说明到excel、word,单表导出方式记录

目前只找到一张一张表导出的方式 使用information_schema传入表名查询 字段名根据需要自行删减&#xff0c;一般保留序号、字段名、类型、说明就行 SELECT COLUMNS.ORDINAL_POSITION AS 序号, COLUMNS.COLUMN_NAME AS 字段名, COLUMNS.COLUMN_TYPE AS 类型(长度), COLUMNS.N…

Linux驱动开发 中断处理

目录 序言 1.中断的概念 2.如何使用中断 中断处理流程 中断上下文限制 屏蔽中断/使能 关键区别与选择 上半部中断 下半部中断 软中断&#xff08;SoftIRQ&#xff09; 小任务(Tasklet) 工作队列&#xff08;Workqueue&#xff09; 线程 IRQ&#xff08;Threaded IRQ…

Centos主机检查脚本

使用方法&#xff1a; 将脚本保存为 CentOS_syscheck.sh 添加执行权限&#xff1a; chmod x CentOS_syscheck.sh 执行脚本&#xff1a; ./CentOS_syscheck.sh #!/bin/bash# 设置颜色变量 RED\033[0;31m GREEN\033[0;32m YELLOW\033[0;33m BLUE\033[0;34m NC\033[0m # 重置…