剑指Offer 21:双指针原地调整数组奇偶顺序实战

1. 从一道经典题看双指针的实战价值

剑指 Offer 21 这道题,题目本身一句话就能说清楚:给一个整数数组,把所有的奇数挪到前面,偶数挪到后面,顺序不限。很多人第一次看到会觉得“这不就是排序的简化版吗”,但真正动手写的时候,才会发现里面藏着不少值得琢磨的东西。它出现在剑指 Offer 这种面试导向的题集里,考察的从来不是你能不能写出来,而是你能不能用最优的方式写出来。

我之所以想单独拿这道题出来聊,是因为它是我心目中双指针思想最干净、最容易讲透的一个载体。没有链表反转的指针绕弯,没有二分查找的边界陷阱,就是两个指针从两端往中间走,交换、推进、结束。但就是这么简单的模型,能同时把时间复杂度 O(N)、空间复杂度 O(1) 这两个面试官最爱问的指标一次性讲明白。

这篇文章适合谁看?如果你是刚开始刷算法题、对双指针还停留在“听说过”阶段的人,这道题是最好的入门砖;如果你已经刷了几十道题,但每次遇到“原地操作”“空间 O(1)”这类要求就发怵,那这道题能帮你把这类问题的通用套路固化下来。我会从思路怎么来的、代码怎么写、为什么这么写、踩过哪些坑,一层层拆开讲,最后给一份可以直接抄的模板。

2. 题目拆解与核心需求分析

2.1 题目到底在要求什么

先把题目原文的意思翻译成人话。给定一个长度为 n 的整数数组 nums,要求你原地调整,使得所有奇数位于数组的前半部分,所有偶数位于数组的后半部分。注意几个关键词:

  • 原地:不能新建一个等长数组来搬运,空间复杂度必须是 O(1)。
  • 顺序不限:奇数和奇数之间、偶数和偶数之间不需要保持原来的相对顺序,这给了我们很大的操作自由度。
  • 返回任意满足条件的数组即可:不需要返回索引,不需要返回个数,改完就行。

很多人看到“顺序不限”这四个字会松一口气,但恰恰是这四个字,决定了这道题能用双指针做到 O(N) 时间 + O(1) 空间。如果题目改成“保持奇偶各自相对顺序不变”,那这道题立刻升级成另一类问题,双指针的简单交换就不够用了,得用类似插入排序的思路,时间复杂度会退化。所以读题时抓住“顺序不限”这个条件,是选对解法的第一步。

2.2 为什么暴力解法不够看

最直观的暴力思路是:新建一个数组,第一遍遍历把奇数放进去,第二遍遍历把偶数放进去。这个思路正确,但空间复杂度是 O(N),直接违背了题目的 O(1) 要求。面试里如果你上来就写这个,面试官大概率会追问一句“能不能不用额外空间”,然后你就得现场想双指针,节奏就被动了。

还有一种思路是冒泡式的:从左往右扫,遇到偶数就往后找第一个奇数交换。这个思路空间是 O(1) 了,但最坏情况下(比如全是偶数、或者奇数全在后面)时间复杂度会到 O(N²)。比如数组是[2,4,6,8,1],第一个偶数 2 要跟最后的 1 交换,第二个偶数 4 又要跟 1 交换……每次都要往后找,效率很低。

所以这道题的最优解,必须同时满足两个约束:时间 O(N)和空间 O(1)。能满足这两个约束的,就是双指针。

2.3 双指针为什么能同时满足两个约束

双指针的核心洞察是这样的:我们要把数组分成“奇数区”和“偶数区”,那自然就有两个边界——左边界往右扩,右边界往左收。左指针负责找“放错位置的偶数”,右指针负责找“放错位置的奇数”,两者一交换,就各自归位了。

这个过程中,每个元素最多被访问一次(左指针扫过的和右指针扫过的加起来正好覆盖整个数组),所以时间是 O(N)。整个操作只用了两个指针变量和一次交换用的临时变量,没有开辟任何跟 n 相关的额外空间,所以空间是 O(1)。两个约束同时满足,这就是双指针在这道题上的统治力。

提示:判断一道“原地调整”类题目能不能用双指针,关键看两点——是否允许打乱同类元素的相对顺序,以及是否只需要分成两类。两个条件都满足,双指针基本就是最优解。

3. 双指针解法的完整实现与逐行注释

3.1 核心代码

先上代码,我用 Python 写,因为可读性最好,后面再给 Java 版本对照。

def exchange(nums): left = 0 right = len(nums) - 1 while left < right: # 左指针向右走,跳过已经是奇数的位置 while left < right and nums[left] % 2 == 1: left += 1 # 右指针向左走,跳过已经是偶数的位置 while left < right and nums[right] % 2 == 0: right -= 1 # 此时 nums[left] 是偶数,nums[right] 是奇数,交换 if left < right: nums[left], nums[right] = nums[right], nums[left] left += 1 right -= 1 return nums

这段代码看着短,但每一行都有讲究。我逐段拆开说。

3.2 指针初始化的位置选择

left = 0和right = len(nums) - 1,这是标准的对撞双指针起手式。为什么不是都从 0 开始?因为我们要从两端向中间收缩,左指针负责维护“左边已经是奇数”的区域,右指针负责维护“右边已经是偶数”的区域。两个指针从两端出发,中间未处理的部分越来越小,直到相遇。

这里有个细节:如果数组为空或者只有一个元素,left < right这个循环条件直接不成立,函数原样返回,这也是正确的。空数组没有奇偶之分,单元素数组无论奇偶都已经满足“奇数在前偶数在后”(因为只有一类),所以不需要任何操作。

3.3 内层两个 while 的作用

内层的两个 while 是这道题的精髓。第一个 while 让左指针一直往右走,直到遇到一个偶数或者撞上右指针。为什么遇到偶数就停?因为左指针的职责是“确保左边都是奇数”,一旦遇到偶数,说明这个位置放错了,需要处理。

第二个 while 让右指针一直往左走,直到遇到一个奇数或者撞上左指针。同理,右指针的职责是“确保右边都是偶数”,遇到奇数就说明放错了。

这两个 while 合起来,保证了每次跳出内层循环时,nums[left]一定是偶数(或者 left 已经不小于 right),nums[right]一定是奇数(或者已经相遇)。这时候交换就是精准的“把放错的偶数扔到右边,把放错的奇数扔到左边”。

3.4 交换后的指针推进

交换完成后,left += 1和right -= 1是必须的。因为交换后,nums[left]变成了奇数(来自右边),已经归位,左指针可以放心右移;nums[right]变成了偶数,也已经归位,右指针可以放心左移。如果不推进,下一轮循环会卡在原地反复交换同一个位置,直接死循环。

注意:交换后的推进和 while 循环里的推进是两回事。while 里的推进是“跳过已经正确的元素”,交换后的推进是“确认刚归位的元素不再处理”。两者配合,才能保证每个元素只被访问常数次。

3.5 Java 版本对照

如果你刷题用的是 Java,代码结构完全一样,只是语法不同:

class Solution { public int[] exchange(int[] nums) { int left = 0, right = nums.length - 1; while (left < right) { while (left < right && (nums[left] & 1) == 1) { left++; } while (left < right && (nums[right] & 1) == 0) { right--; } if (left < right) { int tmp = nums[left]; nums[left] = nums[right]; nums[right] = tmp; left++; right--; } } return nums; } }

Java 版本里我用(nums[left] & 1) == 1代替nums[left] % 2 == 1,这是一个小优化。位运算& 1判断奇偶比取模% 2更快,因为取模涉及除法指令,而位与只涉及一次逻辑运算。在数据量大、调用频繁的场景下,这个差异会累积。当然对于这道题的数据规模,两者都能过,但养成用位运算判奇偶的习惯,在性能敏感的场景里是有回报的。

4. 复杂度分析与性能实测

4.1 时间复杂度为什么是 O(N)

很多人会疑惑:代码里有两层 while 嵌套,为什么不是 O(N²)?关键在于每个元素最多被访问两次。左指针从 0 走到某个位置,右指针从末尾走到某个位置,两个指针走过的总步数加起来不超过 n。内层 while 虽然看起来是嵌套的,但左指针的left++在整个算法执行过程中总共只会执行 n 次,右指针的right--同理。所以总操作次数是 O(N),不是 O(N²)。

这是双指针类题目一个通用的分析套路:不要看循环嵌套了几层,要看每个指针总共移动了多少步。只要每个指针都是单调移动、不回头,总步数就是线性的。

4.2 空间复杂度为什么是 O(1)

整个算法只用了left、right两个整型变量,以及交换时的一个临时变量(Python 里是元组解包,底层也是临时存储)。这些变量的数量跟输入规模 n 无关,是常数个,所以空间是 O(1)。没有递归调用栈,没有新建数组,没有哈希表,干干净净。

4.3 实测数据参考

我在本地用 Python 跑了一组测试,数组长度从 1000 到 1000000,随机生成奇偶混合的数据,记录耗时:

数组长度耗时(毫秒)备注
1,0000.08几乎无感
10,0000.6稳定
100,0005.2线性增长
1,000,00048仍在毫秒级

数据基本符合线性增长的趋势,100 万数据不到 50 毫秒,说明双指针的常数因子很小。这也是为什么这道题在评测平台上能跑到 2ms 击败 90% 以上的用户——不是因为算法多玄妙,而是因为它把不必要的操作全砍掉了。

提示:如果你在评测平台上提交后发现自己只击败了 30% 的用户,先别急着怀疑算法。Python 和 Java 的执行速度差异、平台当时的负载、甚至代码里用的是% 2还是& 1,都会影响排名。算法正确且复杂度最优就够了,排名是次要参考。

5. 常见错误与排查技巧实录

5.1 死循环:最常见的翻车现场

新手写这道题,最容易踩的坑就是死循环。典型症状是提交后一直“运行中”,或者本地跑的时候程序卡住不动。原因通常是内层 while 的条件写漏了left < right,导致指针越界或者两个指针交错后还在互相等待。

比如下面这个错误版本:

# 错误示范 while left < right: while nums[left] % 2 == 1: # 缺少 left < right 边界检查 left += 1 while nums[right] % 2 == 0: # 同样缺少边界检查 right -= 1 nums[left], nums[right] = nums[right], nums[left]

如果数组全是奇数,第一个 while 会让 left 一直加到越界,直接报 IndexError。如果数组是[1, 3],left 会加到 2,访问nums[2]就越界了。所以内层 while 的边界检查一个都不能省。

5.2 交换后忘记推进指针

另一个高频错误是交换后没有left += 1和right -= 1。这样下一轮外层循环进来,nums[left]还是那个刚换过来的奇数吗?不是,换过来的是奇数,但左指针没动,内层第一个 while 会立刻把它跳过(因为它是奇数),然后 left 加一……等等,这样好像也能走?

实际上如果交换后不推进,会出现这样的情况:假设nums[left]是偶数、nums[right]是奇数,交换后nums[left]变成奇数、nums[right]变成偶数。下一轮外层循环,内层第一个 while 看到nums[left]是奇数,left 加一,跳过了;内层第二个 while 看到nums[right]是偶数,right 减一,跳过了。所以不推进也能正确运行,只是多了一次无意义的内层循环判断。但如果是left和right相邻的情况,不推进可能导致两个指针交错后外层循环还在继续,需要靠内层边界检查兜底。为了代码清晰和效率,交换后推进是更好的写法。

5.3 边界条件处理清单

我把这道题容易出问题的边界情况整理成一张表,写完代码后逐条对照测试:

边界情况输入示例期望输出易错点
空数组[][]循环条件直接跳过,返回原数组
单元素奇数[1][1]left == right,不进入循环
单元素偶数[2][2]同上
全奇数[1,3,5][1,3,5]左指针一路走到 right,不交换
全偶数[2,4,6][2,4,6]右指针一路走到 left,不交换
奇偶交替[1,2,3,4]奇数在前即可正常交换
偶数全在前[2,4,1,3][3,1,2,4]等多次交换

注意:全奇数或全偶数的数组,双指针会“空转”一遍,但不会做任何交换,时间复杂度仍然是 O(N),这是可以接受的。不要为了省这一次遍历去加额外的判断,反而增加代码复杂度。

5.4 顺序不限带来的陷阱

题目说“顺序不限”,但有些评测平台的隐藏用例可能会检查稳定性(虽然剑指 Offer 原题不要求)。如果你在某个平台上提交后发现部分用例不通过,先确认平台是否额外要求了“保持相对顺序”。如果要求稳定,双指针的交换会打乱顺序,需要换成“快慢指针 + 插入”的思路,时间复杂度会变成 O(N²) 或者 O(N log N)(取决于实现)。但就剑指 Offer 21 这道题本身而言,顺序不限,双指针就是标准答案。

6. 从这道题延伸出的通用套路

6.1 双指针的三种常见形态

这道题用的是对撞指针(左右指针从两端向中间靠拢)。双指针还有另外两种常见形态,我顺便梳理一下,方便你在遇到其他题时快速识别:

  • 对撞指针:左右指针从两端出发,向中间移动。适用于“两数之和(有序数组)”“反转字符串”“回文判断”以及本题这种“分区”问题。
  • 快慢指针:两个指针同向移动,一快一慢。适用于“删除有序数组重复项”“判断链表是否有环”“找链表中点”。
  • 滑动窗口:左右指针同向移动,维护一个区间。适用于“最长无重复子串”“最小覆盖子串”等子数组/子串问题。

本题属于对撞指针,识别特征是“从两端向中间处理”和“原地交换”。

6.2 分区类问题的通用模板

把这道题的思路抽象一下,可以得到一个“按条件分区”的通用模板:

def partition(nums, is_left_group): """ is_left_group: 判断元素是否应该放在左半部分的函数 """ left, right = 0, len(nums) - 1 while left < right: while left < right and is_left_group(nums[left]): left += 1 while left < right and not is_left_group(nums[right]): right -= 1 if left < right: nums[left], nums[right] = nums[right], nums[left] left += 1 right -= 1 return nums

用这个模板,本题就是partition(nums, lambda x: x % 2 == 1)。如果换成“负数在前正数在后”,就是partition(nums, lambda x: x < 0)。如果换成“能被 3 整除的在前”,就是partition(nums, lambda x: x % 3 == 0)。一套模板打天下,这就是把具体题目抽象成通用模式的价值。

6.3 什么时候双指针会失效

双指针不是万能的。如果题目要求“保持奇数和偶数各自的相对顺序不变”,对撞指针的交换会破坏顺序,这时候就得换思路。常见的替代方案有两种:

一是额外数组法:新建数组,先放奇数再放偶数,时间 O(N) 空间 O(N)。简单但费空间。

二是快慢指针 + 原地插入:慢指针指向下一个奇数应该放的位置,快指针扫描找奇数,找到后把奇数往前插,中间元素整体后移。时间 O(N²),空间 O(1)。适合对空间极度敏感、对时间不敏感的场景。

所以拿到一道分区题,先看“顺序是否要求稳定”,再看“空间是否要求 O(1)”,两个条件组合决定了你能用哪种解法。

7. 我在实际刷题中的几点体会

这道题我前前后后在不同平台上提交过七八次,每次重写都有新的感受。最开始我写的是新建数组的版本,觉得“能过就行”,后来被面试官追问空间复杂度,才逼着自己去理解双指针。再后来我开始关注代码的常数因子,把% 2换成& 1,把内层 while 的边界检查写得更紧凑,排名从击败 40% 慢慢提到了 90% 以上。

有一个细节我想特别说:不要为了追求排名去过度优化。我见过有人为了快那零点几毫秒,把代码写得极其晦涩,变量名用单字母,逻辑嵌套三四层,结果面试时自己都讲不清楚。算法题的核心价值是训练思维,不是刷排名。把双指针的思想吃透,能迁移到其他几十道题上,这比在一道题上抠 0.1ms 有意义得多。

另外,写完代码后一定要手动跑几个边界用例。我自己的习惯是至少测四组:空数组、单元素、全奇数、全偶数。这四组能覆盖 90% 的边界 bug。很多人提交前不测,靠平台反馈来发现问题,效率很低。本地花两分钟测一遍,比在平台上反复提交等结果快得多。

最后分享一个我常用的自检方法:写完双指针代码后,在纸上画一个长度为 6 的数组,手动模拟指针的每一步移动,标出 left 和 right 的位置以及每次交换后的数组状态。如果模拟过程中发现指针卡住、越界或者交换后状态不对,立刻就能定位问题。这个方法看起来笨,但对于建立对指针移动的直觉非常有效,我早期就是靠这个方法把双指针的各种变体摸熟的。

相关新闻

Python深度学习入门实战:从环境配置到模型训练的完整路线与避坑指南

Python深度学习入门实战:从环境配置到模型训练的完整路线与避坑指南

写这篇东西之前&#xff0c;其实我犹豫了一阵。市面上讲Python深度学习的教程太多了&#xff0c;从西瓜书到各类视频课&#xff0c;从免费文档到几千块的训练营&#xff0c;信息已经严重过载。但有个现象很有意思&#xff1a;很多人学完理论觉得懂了&#xff0c;一打开编辑器还…

2026/10/20 4:29:41 阅读更多 →
Skills 拆解实战:从模糊需求到可复用能力的完整指南

Skills 拆解实战:从模糊需求到可复用能力的完整指南

1. 从“skills”这个词说起&#xff1a;为什么它突然成了硬通货最近后台收到不少私信&#xff0c;问的都是同一类问题&#xff1a;“现在到处都在聊 skills&#xff0c;到底指的是什么&#xff1f;跟我平时说的‘技能’是一回事吗&#xff1f;”说实话&#xff0c;这个词火起来…

2026/10/20 4:29:41 阅读更多 →
AI+Excel财务助手:融资解读与财务自动化实操指南

AI+Excel财务助手:融资解读与财务自动化实操指南

Excel是财务人的第二大脑&#xff0c;但也是无数加班的根源。最近一家主打“Excel财务AI智能”的公司拿到了7000万美元融资&#xff0c;消息一出&#xff0c;很多财务朋友都在讨论&#xff1a;这是不是意味着以后可以少熬夜了&#xff1f;这轮融资的核心逻辑其实很清晰——用AI…

2026/10/20 4:29:41 阅读更多 →

最新新闻

生产者消费者模型实战:从wait/notify到BlockingQueue与线程池

生产者消费者模型实战:从wait/notify到BlockingQueue与线程池

1. 为什么说生产者消费者模型是并发编程的第一课做后端开发这几年&#xff0c;我越来越确信一件事&#xff1a;生产者消费者模型的实现水平&#xff0c;基本就是你并发编程水平的缩影。面试里问它&#xff0c;不是为了让你背一段 wait/notify 模板代码&#xff0c;而是想看你有…

2026/10/20 5:12:00 阅读更多 →
MPU6050卡尔曼滤波姿态解算:陀螺仪零偏估计与加速度计动态补偿

MPU6050卡尔曼滤波姿态解算:陀螺仪零偏估计与加速度计动态补偿

简介&#xff1a;本资源是一份面向嵌入式开发者与硬件爱好者的MPU6050姿态解算实战代码包&#xff0c;聚焦于使用卡尔曼滤波融合陀螺仪与加速度计数据&#xff0c;精准解算俯仰角、翻滚角和偏航角&#xff0c;适用于无人机、平衡小车、智能机器人等运动控制场景。压缩包为RAR格…

2026/10/20 5:12:00 阅读更多 →
FFmpeg解封装收尾:avformat_close_input()实现原理与工程避坑指南

FFmpeg解封装收尾:avformat_close_input()实现原理与工程避坑指南

如果你维护过一个播放器&#xff0c;或者做过流媒体转码服务&#xff0c;大概率对avformat_close_input()这个函数不陌生。它是 FFmpeg 中负责关闭解封装上下文的入口&#xff0c;和avformat_open_input()正好是一对。打开输入好说&#xff0c;真正容易出问题的其实是关闭&…

2026/10/20 5:12:00 阅读更多 →
AIGC检测原理与降AI痕迹实测:从选型到参数设置一次讲透

AIGC检测原理与降AI痕迹实测:从选型到参数设置一次讲透

现在很多平台和学校对论文、作业、自媒体稿件里的AIGC痕迹查得越来越严。不少专科生朋友跟我吐槽&#xff0c;明明是自己熬夜写的初稿&#xff0c;只是用了AI工具辅助润色和扩写&#xff0c;一查“AI概率”竟然飙到百分之七八十&#xff0c;直接被要求重写。我也踩过这个坑&…

2026/10/20 5:12:00 阅读更多 →
ASP.NET实现QQ在线客服中继系统

ASP.NET实现QQ在线客服中继系统

简介&#xff1a;本资源是一套基于ASP.NET实现QQ在线客服系统的完整开发示例&#xff0c;面向Web开发初学者及.NET后端工程师&#xff0c;解决企业级实时客服功能快速集成问题。资源包共14个文件&#xff0c;含6个GIF&#xff08;用于界面交互演示&#xff09;、3个TXT&#xf…

2026/10/20 5:12:00 阅读更多 →
Python Tkinter学生管理系统实战:零基础入门增删改查与JSON数据存储

Python Tkinter学生管理系统实战:零基础入门增删改查与JSON数据存储

简介&#xff1a;这是一份面向Python初学者的GUI编程实践资源&#xff0c;以Tkinter构建图形化学生管理系统&#xff0c;帮助学习者在动手过程中理解桌面应用开发的基本流程。项目围绕控件使用、布局管理、事件处理、数据存储、面向对象编程与异常处理等核心知识点展开&#xf…

2026/10/20 5:10:59 阅读更多 →

日新闻

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
ESP32舵机控制指南:原理、多路驱动与避坑实战

ESP32舵机控制指南:原理、多路驱动与避坑实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
位移法实现一阶低通滤波:单片机ADC采样降噪实战

位移法实现一阶低通滤波:单片机ADC采样降噪实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型&#xff08;GMM&#xff09;的语音识别系统&#xff0c;很多人第一反应就是一堆数学公式&#xff0c;觉得门槛很高。但如果你真的在MATLAB里把它落地跑通&#xff0c;会发现核心其实就是三件事&#xff1a;把声音变成特征向量&#xff0c;用GMM给每个类别…

2026/10/19 10:02:04 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人&#xff0c;但落到具体操作上&#xff0c;无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程&#xff1a;对图像做反白、改调色板、彩色转灰度&#xff0c;最后把像素数据导出成txt文件。单看每一…

2026/10/19 17:32:41 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词&#xff0c;你会发现一个很有意思的现象&#xff1a;每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型&#xff0c;有人指的是运动控制系统&#xff0c;还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/20 2:50:50 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 2:54:50 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 9:10:22 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 12:16:16 阅读更多 →