无旋Treap详解:基于split与merge的平衡树核心操作
无旋 Treap也叫无旋平衡树这个名字这几年在算法圈子里已经快变成默认平衡树的代名词了。它本质上还是一棵 Treap——键值满足二叉搜索树BST性质、优先级满足堆性质的二叉树但特殊之处在于完全不需要旋转操作所有增删查改都能用 split分裂和 merge合并两个基础原语组合出来。这篇是 Treap 系列的第二篇目标是把无旋 Treap 的基本操作讲透从节点定义到插入、删除、排名、第 k 小、前驱后继再到踩坑排查覆盖新手从入门到能独立实现的全过程。适合已经了解二叉搜索树基本概念、想换一款好用平衡树的读者。如果你已经会旋转版 Treap那看起来会更快——无旋版本的分裂合并思路会让很多原本麻烦的区间操作变得异常直觉。1. 整体设计为什么是无旋 Treap1.1 Treap 的本质BST 的平衡靠运气Treap 就是 Tree Heap 的组合词。每个节点同时满足两组约束键值 val 满足 BST 性质左子树所有 val 小于当前节点右子树所有 val 大于当前节点。优先级 prio 满足堆性质父节点的 prio 小于或大于取决于实现约定两个子节点的 prio。BST 性质保证我们可以像在有序数组里一样二分查找堆性质则保证树高是受控的。这里有一个很多人没想透的关键点如果每个节点的 prio 是独立均匀随机生成的那么整棵树的形态等价于按 prio 从小到大依次插入键值的随机二叉搜索树期望高度是 O(log n)。换句话说平衡不是靠旋转规则硬调出来的而是靠概率包出来的。你只需要保证 prio 的随机性足够好树的形态就大概率是均匀的。这也就是为什么 Treap 不需要 AVL 那样的严格平衡条件也不会像裸 BST 那样插入有序序列就退化成一条链。随机化让人安心也让代码简单。1.2 无旋 到底解决了什么问题旋转版 Treap 在插入、删除时靠左旋、右旋来恢复堆性质。无旋版本把调整彻底换成分裂合并我实际用了很久体会到三个核心优势。第一思维负担小。所有操作都是同一个套路split 切两刀得到若干棵树再按需 merge 回去。你不需要记四种旋转组合不需要处理祖父节点、父节点、当前节点之间的复杂关系递归的每一层只关心当前节点该归哪边。第二可组合性强。split 和 merge 是通用原语可以随意组合出删除一段区间翻转区间在任意位置插入这类高级操作。这是后续做区间操作最重要的跳板也是无旋 Treap 相比旋转版最大的扩展空间。第三可持久化友好。split 和 merge 每次只修改递归路径上的一串节点只要把原地修改改成复制节点再修改就能直接做成可持久化平衡树。旋转版要持久化难度直接上一个档次。1.3 先问自己你需要平衡树干什么学习任何数据结构之前最好先想清楚使用场景。如果只是维护一个有序集合做插入、删除、查前驱后继那么 C 的 std::set 完全够用没必要自己造轮子。但如果你需要查询第 k 小元素某个值的排名这类按序统计或者以后要写区间翻转、区间最值、可持久化这些操作std::set 就无能为力了无旋 Treap 几乎是性价比最高的选择。这也是为什么很多搞竞赛和做算法底层工作的同学最后都把无旋 Treap 当作默认平衡树——它是一棵可以被组装的平衡树核心代码短扩展空间大值得花时间彻底吃透。2. 地基操作split 与 merge2.1 节点定义和两个辅助函数无旋 Treap 的节点定义非常朴素struct Node { int l, r; // 左右孩子下标0 表示空 int val; // 键值满足 BST 性质 uint32_t prio; // 随机优先级满足堆性质 int sz; // 子树大小 };相比普通二叉树只多了 prio 和 sz。sz 用于支持排名、第 k 小查询也是 split 按大小拆时必需的信息。所有操作基于下标完成root 为 0 表示空树这样可以用静态数组存储避免频繁 new/delete在性能敏感场景下更稳。辅助函数只有两个int tot 0; int new_node(int val) { tr[tot] {0, 0, val, next_rand(), 1}; return tot; } void push_up(int u) { tr[u].sz tr[tr[u].l].sz tr[tr[u].r].sz 1; }push_up 在任何子树结构变更后都要调用用来重算 sz。这个函数看着不起眼漏调一次就是大面积错误后面排查部分我会专门强调。2.2 split 的两种口径按值和按大小split 是把一棵树沿某个分界线拆成两棵树。无旋 Treap 里有两种常见口径按值拆和按大小拆。按值拆给定 val拆成 a 和 ba 里所有节点键值 valb 里所有节点键值 val。// 把树 u 按值拆成 a val和 b val void split_val(int u, int val, int a, int b) { if (u 0) { a b 0; return; } if (tr[u].val val) { a u; // u 和它的左子树都属于 a split_val(tr[u].r, val, tr[u].r, b); // 右子树里可能还有 val 的 push_up(a); } else { b u; // u 和它的右子树都属于 b split_val(tr[u].l, val, a, tr[u].l); // 左子树里可能还有 val 的 push_up(b); } }这里最需要理解递归里的方向选择。当前节点 val 时它应该留在左边这棵树 a 里而且它的左子树一定也都 valBST 性质保证所以只需要递归拆它的右子树把右子树拆成 val 和 val 两半前者接回 u 的右儿子位置后者作为 b 传递出去。当前节点 val 的情况完全对称只是方向反转。按大小拆给定 k拆成 a 和 ba 包含原树中序遍历的前 k 个节点也就是最小的 k 个b 是剩余部分。// 把树 u 按大小拆成 a前 k 个和 b剩余 void split_k(int u, int k, int a, int b) { if (u 0) { a b 0; return; } int left_sz tr[tr[u].l].sz; if (left_sz 1 k) { a u; split_k(tr[u].r, k - left_sz - 1, tr[u].r, b); push_up(a); } else { b u; split_k(tr[u].l, k, a, tr[u].l); push_up(b); } }按值拆用于所有按值操作插入、删除、排名、前驱后继按大小拆用于按位序操作查第 k 小、区间操作。两者一个看 val、一个看 sz其余逻辑完全一致学会一个另一个就是照猫画虎。2.3 merge 的铁律全序前提merge 是把两棵树拼回一棵它有一个必须满足的前提a 中所有节点的键值都严格小于 b 中所有节点的键值。在这个前提下合并只看优先级// 合并 a、b返回新根前提是 a 中所有 val b 中所有 val int merge(int a, int b) { if (a 0 || b 0) return a b; if (tr[a].prio tr[b].prio) { tr[a].r merge(tr[a].r, b); push_up(a); return a; } else { tr[b].l merge(a, tr[b].l); push_up(b); return b; } }为什么必须有全序前提因为 BST 性质要求任意节点的左子树全部小于自己、右子树全部大于自己。如果 a、b 之间存在键值交错merge 之后中序遍历就不再有序后面的查询全部失效。代码里 merge 只管 prio但使用者必须心里清楚这两棵树到底是不是 a b。每次调用 merge都要能说清它的两棵子树分别代表哪一段值域否则 bug 会埋得非常深。merge 的细节也值得讲当 prio[a] prio[b] 时a 成为新根。因为 a 里所有键都小于 b所以 b 只能出现在 a 的右子树方向于是递归 merge(a.r, b)。反之b 成为根时a 只能出现在 b 的左子树方向递归 merge(a, b.l)。这个方向写反是新手最容易犯的错误。2.4 复杂度直觉为什么是期望 O(log n)split 和 merge 的每次操作都只沿着一条根到叶子的路径走代价是 O(树高)。树的期望高度是 O(log n)源自随机优先级带来的随机 BST 等价性因此所有基于 split/merge 的操作都是期望 O(log n)。需要强调一下期望两个字。随机化数据结构不保证每次都是 O(log n)而是长期运行下平均表现稳定。实际工程中这个概率模型非常可靠配合好的随机数发生器几乎不会出问题。用大 O 描述时多数资料直接写 O(log n)但心里要明白这是期望意义下的。3. 基本操作的完整实现3.1 插入和删除拆两刀焊回去插入 val 是最好理解的void insert(int val) { int a, b; split_val(root, val, a, b); root merge(merge(a, new_node(val)), b); }把原树按 val 拆成 val 和 val 两棵新建节点先和 a 合并再和 b 合并。整个过程没有任何旋转思路就是切一刀、放进去、焊起来。删除一个等于 val 的节点要稍微绕一点void erase_one(int val) { int a, b, c; split_val(root, val, a, b); // a: val, b: val split_val(a, val - 1, a, c); // 再拆: a: val-1, c: val c merge(tr[c].l, tr[c].r); // 拿走 c 的根把剩余子树合并回 root merge(merge(a, c), b); }这里有个关键细节如果允许重复值c 里可能不是单个节点而是一棵子树。所以不能直接把 c 丢掉而是只拿走 c 的根节点把 c 的左右子树合并后接回原位。这样每次删除恰好移除一个 val符合多重集的操作语义。如果要求删除所有等于 val 的节点直接把 c 丢弃即可但我个人不推荐在静态数组实现里这么做原因后面讲内存时会提到。上面这种删除一个节点但保留其余重复值的方法操作次数多但语义干净是我在竞赛和工程里最常用的做法。如果你确定元素不重复也可以简化成直接丢弃 c代码更短。3.2 kth 和 rank一个走树一个拆树第 k 小查询不需要 split直接沿树走下去int kth(int k) { int u root; while (u) { int left_sz tr[tr[u].l].sz; if (left_sz 1 k) return tr[u].val; if (left_sz 1 k) { u tr[u].l; } else { k - left_sz 1; u tr[u].r; } } return -1; // 找不到 }核心是 left_sz 1 这个表达式它表示当前节点在中序遍历里的排名。比 k 大就往左比 k 小就往右并把 k 减掉当前节点和左子树的贡献非常像在数组上做二分查找。求排名有多少个数严格小于 val再加 1用拆树更直观int rank_of(int val) { int a, b; split_val(root, val - 1, a, b); // a 里全是严格小于 val 的 int ans tr[a].sz 1; root merge(a, b); return ans; }注意这里拆的是 val - 1不是 val。如果拆 vala 里会包含等于 val 的节点排名就变成小于等于了语义不准确。为什么 val - 1 可行因为键值是整数 val-1 等价于 val。用 val - 1 这个细节充分体现了对 split 边界的精确掌控后面排错部分还会遇到。3.3 前驱和后继拆出一半来找边界前驱是严格小于 val 的最大键后继是严格大于 val 的最小键。实现思路是拆出一半再在边界上找最值int get_pre(int val) { int a, b; split_val(root, val - 1, a, b); // a 里全是 val-1 int res -1; int u a; while (u) { res tr[u].val; // 一路向右记录经过的值 u tr[u].r; } root merge(a, b); return res; // 右走到尽头就是最大前驱 } int get_nxt(int val) { int a, b; split_val(root, val, a, b); // b 里全是 val int res -1; int u b; while (u) { res tr[u].val; u tr[u].l; } root merge(a, b); return res; }前驱就是 a 树中序遍历的最后一个节点一路往右到底后继就是 b 树中序遍历的第一个节点一路往左到底。这个实现比用 kth 和 rank 组合拼前驱后继要快也更能体现拆树思维的价值。3.4 中序遍历和完整示例中序遍历可以验证整棵树的正确性——无旋 Treap 的中序遍历一定是升序序列void inorder(int u) { if (u 0) return; inorder(tr[u].l); cout tr[u].val ; inorder(tr[u].r); }把前面所有操作拼成一个最小可运行示例int main() { int n; cin n; for (int i 0; i n; i) { int x; cin x; insert(x); } inorder(root); cout \n; cout 第3小: kth(3) \n; cout 5的排名: rank_of(5) \n; }我建议你拿到代码后先打印中序遍历确认有序性再做查询操作。这一步很便宜却能帮你排除掉大量低级错误。4. 常见问题与排查技巧实录4.1 等号问题split 边界一毫米都不能差split 按值拆时到底用 val 还是 val这不只是风格问题而是语义问题。我推荐统一约定为left 树取 valright 树取 val然后所有操作都严格按这个约定写。最常见的 bug 出现在删除操作里。第一次 split(root, val) 拆走所有 val 的节点第二次 split(a, val - 1) 才能把等于 val 的节点单独拎出来。如果有人把第二次写成 split(a, val)结果 c 永远是空树等于 val 的节点根本删不掉。这一类问题很难靠肉眼发现因为插入、查询可能都正常只有重复删除时才露馅。注意每次调用 split 前都要想清楚这条分界线到底要卡在哪尤其是涉及 val-1 这种偏移时宁可多花十秒脑内模拟也不要写完就提交。4.2 push_up 的黄金法则sz 维护错误是新手第二个重灾区。原则只有一条凡是修改了子树结构的递归函数在返回前必须调用 push_up。split 两个分支里各调一次merge 两个分支里各调一次少一次就是灾难。为什么后果严重sz 不准会直接污染 kth、rank、split_k 这些依赖子树大小的操作。而且这个错误有隐蔽性——插入删除次数少时可能碰巧正常次数多了排名结果就开始飘。排查时如果发现 kth 和 rank 结果不一致、且中序遍历正常优先怀疑 sz 是否在某个分支上忘了更新。4.3 rand 的坑远比你想的多实现里那个 next_rand() 千万别直接用 C 标准库的 rand()。问题在于 rand() 在部分平台上 RAND_MAX 只有 32767一旦节点数量超过这个范围随机优先级就严重不够用等价于在较小的值域里重复采样堆性质容易退化。我踩过这个坑之后的所有实现都统一用 mt19937 加时间种子static mt19937 rng(chrono::steady_clock::now().time_since_epoch().count()); uint32_t next_rand() { return rng(); }mt19937 的周期和质量对于平衡树场景绰绰有余生成一次 32 位随机数也就几纳秒完全不是性能瓶颈。另外有人用 prio rng() ^ 某个递增计数器来进一步打散这属于微优化实际表现差别不大看个人习惯。4.4 递归深度和数组内存的隐患无旋 Treap 的期望树高 O(log n)但随机数发生器质量差或者种子碰巧坏掉时单次操作偶尔也会递归很深。在 Windows 默认 1MB 栈空间下几十万层递归会直接爆栈。我在自己的代码里一般会加一句编译指令开大栈或者把递归改成显式栈迭代前者在竞赛场景更省事。数组内存是另一个容易被忽略的点。如果采用删除时直接丢弃节点的策略每次删除都会产生一个垃圾节点虽然逻辑上不可达但物理上仍占用数组空间。操作量大时数组会被快速耗尽。解决方案是维护一个回收栈int bin[N], bin_top 0; // 回收栈 int new_node(int val) { int u; if (bin_top) u bin[bin_top--]; else u tot; tr[u] {0, 0, val, next_rand(), 1}; return u; } void recycle(int u) { if (u 0) return; recycle(tr[u].l); recycle(tr[u].r); bin[bin_top] u; }删除整个子树时先回收再拼接内存就能循环使用。这个技巧在持久化场景下反而不能用但在普通静态数组实现里非常实用。4.5 调试神器和 std::multiset 对拍我调试平衡树类数据结构时最依赖的方法就是对拍。思路很简单用 std::multiset 作为标准答案随机生成操作序列同时喂给两套实现每次操作后比对结果。不需要什么复杂框架几十行代码就能跑。// 伪代码示意 multisetint st; for (int i 0; i 100000; i) { int op rng() % 4; int val rng() % 1000; if (op 0) { st.insert(val); insert(val); } else if (op 1) { auto it st.find(val); if (it ! st.end()) st.erase(it); erase_one(val); } else if (op 2) { assert(kth(3) *next(st.begin(), 2)); } else if (op 3) { int r 1; for (int x : st) if (x val) break; else r; assert(rank_of(val) r); } }这个对拍脚本帮我抓出过至少三种隐蔽 bugsplit 等号边界错误、erase 后 sz 未更新、merge 顺序写反。如果你自己写了一遍无旋 Treap 却总感觉哪里不对别硬看代码写个对拍跑一万次随机操作问题自动现形。最后再分享一个小技巧无旋 Treap 的代码结构非常规整建议把 split_val、split_k、merge 三个函数背下来剩下所有操作都是它们的组合。所谓基本操作本质就是拆得好、合得对这六个字。后面做区间翻转、持久化、可持久化文艺平衡树你会发现用的还是这三个函数只是分界线从值变成了位置仅此而已。

相关新闻

从MathCAD迁移到Maple Flow的完整实战复盘

从MathCAD迁移到Maple Flow的完整实战复盘

这次内部技术分享会,我把团队从 MathCAD 迁移到 Maple Flow 的完整过程做了逐段复盘——录像和配套的迁移手册、模板文件都已经打包放到资料区了,需要的同事直接下载。这篇文章是文字版精简稿,把为什么要迁、怎么迁、迁完之后踩过哪些坑&…

2026/10/12 9:09:39 阅读更多 →
C#上位机如何用OpenCvSharp实现USB摄像头H264录像

C#上位机如何用OpenCvSharp实现USB摄像头H264录像

简介:面向需要在C#中调用OpenCVSharp完成USB摄像头录像并以H264编码压缩的开发者,这份资源提供一套可运行的工程实现与配套项目文件。包内通过OpenCVSharp接入视频捕获设备,涵盖枚举摄像头、设置分辨率与帧率、指定H264编码器、逐帧转换写入以…

2026/10/12 9:08:38 阅读更多 →
开单软件二维码收款怎么用?从动态二维码到自动对账全解析

开单软件二维码收款怎么用?从动态二维码到自动对账全解析

管家婆易指开单支持二维码收款,这个更新对天天用开单软件跑业务的人来说,算是解决了一个老生常谈的痛点。以前最麻烦的不是开单本身,而是开完单之后那笔钱怎么收、什么时候收、收完怎么跟单子对上。我自己的使用场景是门店加外勤混着来&#…

2026/10/12 9:08:38 阅读更多 →

最新新闻

Matlab检验函数实战:统计检验与代码验证全解析

Matlab检验函数实战:统计检验与代码验证全解析

1. 从“Matlab检验函数”说起:为什么它值得单独拎出来聊“Matlab检验函数”这个说法,乍一听有点笼统——检验什么?怎么检验?是统计上的假设检验,还是代码层面的单元测试?其实这两层含义在Matlab的日常使用中…

2026/10/12 9:59:49 阅读更多 →
隐变量能量基础模型(EBM)推理与训练全解析:从椭圆流形到自监督学习(NYU-DLSP20 第十五周)

隐变量能量基础模型(EBM)推理与训练全解析:从椭圆流形到自监督学习(NYU-DLSP20 第十五周)

示例工程 【免费下载链接】NYU-DLSP20 NYU Deep Learning Spring 2020 项目地址: https://gitcode.com/gh_mirrors/pyt/pytorch-Deep-Learning 点击查看 免费下载 本文基于 NYU-DLSP20 课程第十五周讲义(docs/zh/week15/15.md)及其实践课讲义…

2026/10/12 9:59:48 阅读更多 →
AnyPS5深入解析:跨平台PS5远程串流协议统一封装与优化实战

AnyPS5深入解析:跨平台PS5远程串流协议统一封装与优化实战

1. 项目概览与目标拆解1.1 AnyPS5 是什么先聊清楚一件事:AnyPS5 并不是一个“把某平台游戏搬到另一平台”的移植工具,也不是模拟器前端。准确说,它是一个跨平台远程游玩控制协议的统一封装层。它做的事情可以这样理解:你的 PS5 主…

2026/10/12 9:59:48 阅读更多 →
蓝桥杯压缩变换:基于访问历史的序列距离编码算法

蓝桥杯压缩变换:基于访问历史的序列距离编码算法

1. 项目概述:一道被低估的算法思维训练题“蓝桥杯 压缩变换”——这六个字在历年蓝桥杯省赛真题中反复出现,尤其在C/C组和Java组的填空题与编程题中高频现身。它不是考你调用哪个现成库函数,也不是比谁写的代码行数少,而是直击算法…

2026/10/12 9:59:48 阅读更多 →
MATLAB图像计数实战:从连通域分析到分水岭分割的完整流程

MATLAB图像计数实战:从连通域分析到分水岭分割的完整流程

做了几年机器视觉相关的项目,我经常被问到同一个问题:用MATLAB怎么数清楚一张图里有多少个物体?这个问题听起来简单,实际上坑很深。图里可能有几十个上百个零件、细胞、菌落,人工肉眼数费时费力还容易数错,…

2026/10/12 9:59:48 阅读更多 →
联邦学习基础介绍:用 TaoToken 统一 Key 跑通本地模拟环境

联邦学习基础介绍:用 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/12 9:58:48 阅读更多 →

日新闻

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

在数码相机、高清显示屏与现代矢量图形技术高度发达的今天,画面可以做到绝对的锐利、平滑与无瑕。然而,当一张秋日手账插画或拍立得照片过于“平整无瑕”时,往往会散发出一种冰冷生硬的“数码塑料感(Digital Plasticity&#xff0…

2026/10/12 0:00:59 阅读更多 →
活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

在现代网页与移动端设计中,横排(Horizontal Layout)早已经成为了绝对的主流。然而,当我们翻开泛黄的线装古籍、宋版木刻诗集,或是欣赏一张茶道雅集的手写便签时,那种**自上而下纵向书写、自右向左逐列铺展&…

2026/10/12 0:00:59 阅读更多 →
周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

每到周日的晚上八点到十点,很多人心里都会悄悄亮起一盏警示灯。 在心理学上,这种现象有一个专门的称谓——“周日夜晚焦虑症(Sunday Scaries)”。明天又是周一,闹钟又要重新在七点响彻卧房;脑海里仿佛有一个…

2026/10/12 0:00:59 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/12 0:16:30 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/12 0:16:38 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/12 0:16:43 阅读更多 →

月新闻

我发现了一个新思路:用 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/11 10:45:37 阅读更多 →
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/11 14:36:53 阅读更多 →
黑夜航拍船只数据集训练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/11 14:36:54 阅读更多 →