树链剖分算法详解:从LCA查询到树上路径操作
1. 项目概述从暴力到优雅的树上路径查询刚接触图论和树结构算法时遇到“求树上两个节点的最近公共祖先”这种问题很多人的第一反应可能是朴素的向上跳转法。这个方法简单直接先把两个节点跳到同一深度然后一起向上跳直到相遇。在树比较小的时候这方法完全够用。但一旦树节点数上了十万、百万级别查询次数也动辄几十万次这种每次查询都可能要跳O(n)次的方法时间开销就成了灾难。这时候你就需要一个更强大的武器来处理树上路径问题而树链剖分正是为此而生。简单来说树链剖分是一种对树形结构进行“重组”的算法。它通过一次复杂度为O(n)的预处理把一棵树“拍平”成线性序列并同时维护树上的链式结构。经过剖分后原本复杂的树上路径查询比如求LCA、路径权值和、路径最大值等就可以转化为对若干个连续区间的操作从而能够借助线段树、树状数组等成熟的数据结构在O(log² n)甚至O(log n)的时间内高效完成。对于LCA问题经过树链剖分预处理后单次查询的复杂度可以稳定在O(log n)。这个项目我们就从最经典的应用——求解最近公共祖先入手彻底搞懂树链剖分的原理、实现细节和那些容易踩坑的地方。无论你是正在备战算法竞赛还是在学习高级数据结构掌握树链剖分都能让你在面对复杂的树上问题时多一份从容和底气。我会结合代码一步步拆解并分享我在实现过程中总结出的、那些教科书上不会写的调试技巧和优化心得。2. 核心思路如何把一棵树“拆解”成链树链剖分的核心思想可以用一个生活化的比喻来理解想象一棵枝繁叶茂的大树我们的目标是以树干为主干将所有的树枝都归类到几条主要的“大链条”上。这样当你想从树梢A去到树梢B时你不用在细枝末节间迂回而是先沿着各自所在的“链条”爬到主干再在主干上相遇。这个“归类”的过程就是剖分。为了实现这个目标树链剖分主要定义了两个关键概念和一次DFS预处理我们通常称之为“重链剖分”。2.1 关键概念重儿子与重链首先我们需要选定一个根节点将无根树变为有根树。接下来为每个节点定义以下几个属性siz[u]: 以节点u为根的子树所包含的节点总数。son[u](重儿子): 节点u的所有子节点中siz值最大的那一个。如果存在多个子节点size相同通常任意选一个即可。重儿子意味着从这个节点往下走子树规模最大“势力范围”最广。top[u](所在重链的顶端节点): 节点u所在的那条“主要链条”的起点最顶端的节点。一条重链就是由一系列重儿子连接而成的路径链顶节点的top指向它自己。那么重链就是由某个节点开始不断走向其重儿子直到叶子节点所形成的一条路径。不在重链上的边即连接非重儿子的边称为轻边。整棵树就这样被轻重边划分成了若干条重链。为什么这么定义这样定义的优势在于从任意节点到根节点的路径可以被分解为不超过O(log n)条重链片段。因为每经过一条轻边当前子树的大小至少会减半轻儿子所在的子树大小不超过父节点子树大小的一半。这就保证了路径查询的效率。2.2 两次DFS预处理的核心流程预处理通常通过两次深度优先搜索完成。第一次DFS (dfs1)计算每个节点的深度(dep)、父节点(fa)和子树大小(siz)。通过比较子树的size确定每个节点的重儿子(son)。void dfs1(int u, int father) { fa[u] father; // 记录父节点 dep[u] dep[father] 1; // 计算深度 siz[u] 1; // 至少包含自己 int maxSize -1; for (int v : graph[u]) { if (v father) continue; dfs1(v, u); siz[u] siz[v]; // 累加子树大小 if (siz[v] maxSize) { maxSize siz[v]; son[u] v; // 更新重儿子 } } }第二次DFS (dfs2)优先遍历重儿子并让重儿子继承当前节点所在的重链即top值相同。这个过程就是在“拉链”把重儿子们串到同一条链上。然后遍历轻儿子。每个轻儿子都会开启一条新的重链其top值就是它自己。通常在这次DFS中我们还会为每个节点分配一个独一无二的id时间戳并将节点的权值或其他信息映射到这个线性序列上为后续线段树操作做准备。虽然求LCA本身不需要这个序列但它是树链剖分完整功能的一部分。void dfs2(int u, int topf) { top[u] topf; // 记录链顶 id[u] cnt; // 分配线性序列编号 // 如果有权值在这里进行映射newVal[cnt] oldVal[u]; if (!son[u]) return; // 叶子节点没有重儿子 // 优先走重儿子继承当前链顶 dfs2(son[u], topf); // 再走轻儿子每个轻儿子自成一链 for (int v : graph[u]) { if (v fa[u] || v son[u]) continue; dfs2(v, v); // 轻儿子作为新链的链顶 } }经过这两次DFS我们就完成了树的剖分。每个节点都知道自己的深度、父节点、所在重链的顶端。接下来求LCA就变成了一个“跳链”游戏。3. 基于树链剖分求LCA的算法流程有了剖分后的信息求两个节点u和v的LCA就变得非常高效。其核心逻辑是让两个节点不断跳到所在重链的顶端直到它们位于同一条重链上此时深度较小的那个节点就是LCA。具体步骤如下如果u和v不在同一条重链上即top[u] ! top[v]就比较它们所在重链顶端节点的深度dep[top[u]]和dep[top[v]]。将链顶深度更深的那个节点比如dep[top[u]] dep[top[v]]向上跳到其链顶节点的父节点。即u fa[top[u]]。这一步相当于让这个节点直接“跨过”整条重链来到了上一条轻边的起点。重复步骤1和2直到u和v位于同一条重链上top[u] top[v]。此时u和v已经在同一条链上它们的LCA就是两者中深度较小的那个节点。int queryLCA(int u, int v) { while (top[u] ! top[v]) { // 步骤12跳链 if (dep[top[u]] dep[top[v]]) swap(u, v); u fa[top[u]]; // 深度大的节点向上跳一整条链 } // 步骤4在同一条链上深度小的是LCA return dep[u] dep[v] ? u : v; }为什么这样跳是正确的因为重链剖分保证了树结构的“轻重”特性。每次跳链都是从一条重链的顶端跳到其父节点这相当于走过了一条轻边。而轻边连接的两个子树其大小关系决定了路径分解的条数是O(log n)级别。因此while循环最多执行O(log n)次单次查询复杂度就是O(log n)。注意这里有一个非常关键的细节也是新手容易写错的地方。在while循环的判断条件里我们比较的是dep[top[u]]和dep[top[v]]而不是dep[u]和dep[v]。我们必须让链顶更深的节点向上跳才能保证两个节点最终汇聚到同一点。如果错误地比较u和v的深度可能会导致无限循环或者跳到错误的节点。4. 完整实现与代码剖析下面我们结合一个具体的题目场景给出树链剖分求LCA的完整代码实现。假设题目要求给定一棵包含N个节点的树以及M次询问每次询问两个节点的LCA。4.1 数据结构定义与存储首先我们需要用邻接表来存储这棵树并定义剖分所需的所有数组。#include iostream #include vector #include cstring using namespace std; const int MAXN 100005; // 根据题目最大节点数调整 vectorint graph[MAXN]; // 邻接表存树 int fa[MAXN]; // 父节点 int dep[MAXN]; // 深度 int siz[MAXN]; // 子树大小 int son[MAXN]; // 重儿子 int top[MAXN]; // 所在重链的顶端 // id[MAXN] 和 newVal[MAXN] 在求LCA时非必需为完整性保留 int id[MAXN]; int cnt; // 时间戳计数器 int N, M, root; // 节点数查询数根节点通常为14.2 第一次DFS计算基本信息void dfs1(int u, int father) { fa[u] father; dep[u] dep[father] 1; siz[u] 1; // 初始化大小为1自己 int maxSize -1; for (int i 0; i graph[u].size(); i) { int v graph[u][i]; if (v father) continue; dfs1(v, u); siz[u] siz[v]; // 回溯时累加子树大小 if (siz[v] maxSize) { maxSize siz[v]; son[u] v; // 更新重儿子 } } }实操心得在递归调用dfs1(v, u)之后才累加siz[u]这是典型的树形DP后序遍历思想。确保子节点的信息先被正确计算父节点才能汇总。4.3 第二次DFS划分重链void dfs2(int u, int topf) { top[u] topf; id[u] cnt; // 分配序号求LCA本身用不到但保留以体现完整性 if (!son[u]) return; // 如果没有重儿子叶子节点直接返回 // 优先递归重儿子并继承当前链顶 dfs2(son[u], topf); // 然后递归轻儿子每个轻儿子都是一条新链的起点 for (int i 0; i graph[u].size(); i) { int v graph[u][i]; if (v fa[u] || v son[u]) continue; dfs2(v, v); // 轻儿子作为新链的链顶 } }关键点解析dfs2的调用顺序是先重儿子后轻儿子这保证了同一条重链上的节点获得的id是连续的。这个性质对于后续的路径区间查询结合线段树至关重要虽然LCA查询不直接使用id的连续性但理解这一点有助于掌握树链剖分的全貌。4.4 LCA查询函数int queryLCA(int u, int v) { while (top[u] ! top[v]) { // 谁所在的链顶更深谁就先往上跳 if (dep[top[u]] dep[top[v]]) swap(u, v); u fa[top[u]]; // 跳到链顶的父节点 } // 此时u和v在同一条重链上 return dep[u] dep[v] ? u : v; }4.5 主函数与调用流程int main() { ios::sync_with_stdio(false); cin.tie(0); cin N M root; for (int i 1; i N; i) { int u, v; cin u v; graph[u].push_back(v); graph[v].push_back(u); } // 初始化 cnt 0; // 通常dep[0]可以设为0或-1作为根节点的“虚拟父节点” dep[0] 0; // 第一次DFS从根开始父节点设为0 dfs1(root, 0); // 第二次DFS从根开始根自身作为第一条重链的链顶 dfs2(root, root); for (int i 0; i M; i) { int u, v; cin u v; cout queryLCA(u, v) \n; } return 0; }5. 对比、优化与深度解析5.1 树链剖分 vs. 倍增法求LCA很多人学习树上LCA第一个遇到的是倍增算法。这里简单对比一下倍增法预处理复杂度O(n log n)查询复杂度O(log n)。思路是预处理每个节点向上跳2^k步到达的祖先。查询时先将两点调至同深再一起向上跳。代码相对简单易于理解。树链剖分预处理复杂度O(n)查询复杂度O(log n)。虽然查询的常数可能略大但预处理更快且其威力远不止于求LCA。它真正的价值在于将树上路径问题转化为序列问题从而能处理路径修改、路径查询等更复杂的操作。选择建议如果题目只要求LCA倍增法足矣代码更短。但如果题目涉及树上路径的区间操作比如“将u到v路径上所有点权值加x”树链剖分是更优、更通用的选择。从学习角度理解树链剖分能让你对树的结构有更深刻的认知。5.2 复杂度证明与常数优化时间复杂度两次DFS每个节点和每条边都被访问常数次O(n)。单次LCA查询每次while循环都会让某个节点跳到其链顶的父节点。由于从任一节点到根最多经过O(log n)条轻边每跳一次轻边子树大小至少减半而重链跳跃是连续的所以总跳跃次数为O(log n)。空间复杂度存储树和各个数组O(n)。常数优化技巧使用链式前向星代替vector在节点数极大1e5时链式前向星的缓存友好性可能带来小幅提升但vector的代码可读性更好。对于大部分竞赛场景vector足够。递归改迭代DFS可以用栈手动模拟避免递归过深导致栈溢出。这对极端深度如链状树的题目很重要。dfs2中的循环优化在dfs2中遍历轻儿子时可以避免重复判断v fa[u]因为邻接表是无向的。我们的写法是清晰的但确保fa数组已在dfs1中正确赋值。5.3 从LCA扩展到路径操作树链剖分的魅力在于其扩展性。求LCA只是其应用的一个特例。一旦我们有了id数组将树节点映射到连续区间和top数组我们就可以处理任意两点间路径上的修改和查询。核心操作模板void updatePath(int u, int v, int val) { while (top[u] ! top[v]) { if (dep[top[u]] dep[top[v]]) swap(u, v); // 此时[id[top[u]], id[u]] 构成了一个连续区间 // 用线段树对这个区间进行更新: update(1, id[top[u]], id[u], val); u fa[top[u]]; } // 最后处理同一条链上的部分 if (dep[u] dep[v]) swap(u, v); // 更新区间 [id[u], id[v]] // update(1, id[u], id[v], val); }查询操作(queryPath)的框架与此完全一致只是将线段树的更新操作换成查询操作。这揭示了树链剖分的通用模式通过跳链将树上路径分解为O(log n)个线性区间然后利用序列数据结构批量处理。6. 常见问题、调试技巧与避坑指南在实际实现和解题过程中会遇到各种各样的问题。下面是我总结的一些典型坑点和解决技巧。6.1 初始化与边界条件根节点的父节点设置在dfs1(root, father)中father参数通常设为0。我们需要保证dep[0]有一个合理的值比如0或-1使得dep[root] dep[0] 1成立。同时在后续跳转判断中要确保不会访问到0号节点。数组大小务必根据题目最大数据范围开够数组大小通常MAXN设为n5。邻接表graph也要开够。多组数据清空如果有多组测试数据必须在每组开始前清空graph、son等数组。son数组需要重置为0因为重儿子可能不存在。6.2 递归深度与栈溢出树的深度可能很大例如一条链递归DFS可能导致栈溢出。解决方案使用手动栈模拟递归较复杂。在C中可以在编译命令或代码开头加入栈空间扩容指令如#pragma comment(linker, “/STACK:102400000,102400000”)但这并非标准做法依赖环境。最通用的方法是将递归函数改为迭代实现。虽然代码稍长但一劳永逸。6.3 求LCA时陷入死循环这是新手最容易出错的地方。错误示例while (top[u] ! top[v]) { if (dep[u] dep[v]) swap(u, v); // 错误比较的是节点深度不是链顶深度 u fa[top[u]]; }如果u和v深度相同但不在同一条链且top[u]和top[v]深度也相同上述代码可能会让u和v在两个链顶之间来回跳无法收敛。正确做法必须比较dep[top[u]]和dep[top[v]]保证每次都是链顶更深的节点向上跳一整条链。6.4 对“重儿子”的理解偏差son[u]存储的是u的子节点中子树大小最大的那个。如果u是叶子节点则没有重儿子son[u]应为0初始化时就要置0。在dfs2中需要用if (!son[u]) return;来判断避免访问非法内存。6.5 调试方法当程序输出错误答案或运行时错误时可以按以下步骤排查小数据画图模拟用3-5个节点的树手工模拟整个剖分过程和LCA查询与程序输出对比。打印关键数组在两次DFS后打印出fa[],dep[],siz[],son[],top[]数组。检查是否符合预期。siz[root]应该等于N。每条重链上的节点其top值应该相同。从根到任意叶子轻边数量应该较少。单步调试LCA查询在queryLCA函数中打印每次跳转前的u, v, top[u], top[v], dep[top[u]], dep[top[v]]观察跳转逻辑是否正确。检查输入和建图确认是无向图边是否添加了两次。确认根节点是否正确。6.6 性能问题分析查询超时首先检查queryLCA的while循环是否可能死循环。其次虽然O(log n)很快但如果查询次数M极大如1e6常数过大也可能导致超时。此时可以检查是否使用了endl会导致刷新缓冲区应用\n或考虑使用更快的输入输出如scanf/printf或关闭cin同步。内存超限检查数组是否开得过大或者vector没有清空导致多组数据内存累积。掌握树链剖分尤其是从求LCA这个切入点入手是打开高级树上算法大门的一把钥匙。它背后的“轻重链”思想非常巧妙将树的非线性结构赋予了某种线性秩序。实现时耐心处理好两次DFS的细节深刻理解跳链的过程就能顺利掌握。当你再遇到需要维护树上路径信息的题目时你会庆幸自己掌握了这个强大的工具。

相关新闻

5分钟掌握VBScript浏览器自动化:SeleniumBasic让Excel和脚本开发更高效

5分钟掌握VBScript浏览器自动化:SeleniumBasic让Excel和脚本开发更高效

5分钟掌握VBScript浏览器自动化:SeleniumBasic让Excel和脚本开发更高效 【免费下载链接】SeleniumBasic A Selenium based browser automation framework for VB.Net, VBA and VBScript 项目地址: https://gitcode.com/gh_mirrors/se/SeleniumBasic 还在为复…

2026/8/2 23:20:16 阅读更多 →
复杂网络前沿:从图神经网络到高阶交互,第二十二届会议核心议题与参会指南

复杂网络前沿:从图神经网络到高阶交互,第二十二届会议核心议题与参会指南

1. 会议背景与复杂网络领域现状如果你对“复杂网络”这个词感到既熟悉又陌生,那太正常了。它听起来像是一个高深莫测的学术黑话,但实际上,它的触角早已渗透到我们生活的方方面面。从你每天刷的社交媒体好友关系图,到城市交通的拥堵…

2026/8/2 23:20:16 阅读更多 →
C# Chart控件深度解析:从数据可视化原理到实时监控实战

C# Chart控件深度解析:从数据可视化原理到实时监控实战

1. 项目概述:为什么Chart控件是C#开发者的必备技能如果你正在用C#开发WinForms或WPF桌面应用,并且需要展示数据趋势、对比分析或者任何形式的统计图表,那么Chart控件几乎是你绕不开的一个工具。它不是简单的绘图组件,而是一个功能…

2026/8/2 23:19:15 阅读更多 →

最新新闻

AI合同要素提取不是NLP任务,而是法律知识图谱工程(附237类条款本体映射表·2024Q2更新版)

AI合同要素提取不是NLP任务,而是法律知识图谱工程(附237类条款本体映射表·2024Q2更新版)

更多请点击: https://kaifayun.com 第一章:AI合同要素提取不是NLP任务,而是法律知识图谱工程(附237类条款本体映射表2024Q2更新版) 合同要素提取的本质挑战,不在于文本分词或序列标注的精度,而…

2026/8/2 23:47:40 阅读更多 →
GPT-5.6技术前瞻:双向理解、长上下文与代码生成革命

GPT-5.6技术前瞻:双向理解、长上下文与代码生成革命

1. 项目概述:GPT-5.6传闻的深度拆解最近几天,AI圈子里关于GPT-5.6的讨论热度突然飙升,各种“实测截图”、“内部消息”和“本周四发布”的传闻满天飞。作为一名长期关注大模型动态的从业者,我第一反应是保持审慎。OpenAI的发布节奏…

2026/8/2 23:47:40 阅读更多 →
单片机计算机毕设之基于蓝牙模块与 S8550 驱动的多路输出控制硬件系统开发 基于单片机蓝牙通信的小型电气设备无线分路控制器设计(020801)

单片机计算机毕设之基于蓝牙模块与 S8550 驱动的多路输出控制硬件系统开发 基于单片机蓝牙通信的小型电气设备无线分路控制器设计(020801)

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于嵌入式单片机,Java、小程序技术领域和毕业项目实战 ✌️…

2026/8/2 23:47:40 阅读更多 →
【单片机毕业设计】基于单片机传感器的水体温浊实时检测装置开发 基于 STC89C52 的水质阈值可调报警控制系统设计(018101)

【单片机毕业设计】基于单片机传感器的水体温浊实时检测装置开发 基于 STC89C52 的水质阈值可调报警控制系统设计(018101)

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于嵌入式单片机,Java、小程序技术领域和毕业项目实战 ✌️…

2026/8/2 23:47:40 阅读更多 →
Obsidian CSS定制化架构:构建企业级知识管理系统界面

Obsidian CSS定制化架构:构建企业级知识管理系统界面

Obsidian CSS定制化架构:构建企业级知识管理系统界面 【免费下载链接】awesome-obsidian 🕶️ Awesome stuff for Obsidian 项目地址: https://gitcode.com/gh_mirrors/aw/awesome-obsidian Obsidian作为现代知识管理工具的核心优势在于其高度可扩…

2026/8/2 23:47:40 阅读更多 →
Python PDF处理终极指南:pypdf库高效配置与实战应用

Python PDF处理终极指南:pypdf库高效配置与实战应用

Python PDF处理终极指南:pypdf库高效配置与实战应用 【免费下载链接】pypdf A pure-python PDF library capable of splitting, merging, cropping, and transforming the pages of PDF files 项目地址: https://gitcode.com/GitHub_Trending/py/pypdf 如果你…

2026/8/2 23:46:40 阅读更多 →

日新闻

最大流算法详解:从水管网络到Ford-Fulkerson与Dinic实战

最大流算法详解:从水管网络到Ford-Fulkerson与Dinic实战

1. 从水管网络到最大流:一个核心问题的诞生想象一下,你是一个城市供水系统的总工程师。你的城市有多个水源(水库),需要通过一个复杂的地下管道网络,将水输送到各个居民区。每条管道都有其最大通水能力&…

2026/8/2 0:00:38 阅读更多 →
基于Springboot的企业门户网站(源码+LW+调试文档+讲解)

基于Springboot的企业门户网站(源码+LW+调试文档+讲解)

温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台…

2026/8/2 0:00:38 阅读更多 →
MATLAB xcorr函数详解:从互相关原理到四大实战应用

MATLAB xcorr函数详解:从互相关原理到四大实战应用

1. 从一次信号“找茬”说起:为什么我们需要互相关几年前,我在处理一组声学传感器数据时遇到了一个棘手的问题。我有两个麦克风记录了一段相同的音频信号,理论上它们接收到的声音波形应该非常相似,只是由于麦克风位置不同&#xff…

2026/8/2 0:00:38 阅读更多 →

周新闻

最大流算法详解:从水管网络到Ford-Fulkerson与Dinic实战

最大流算法详解:从水管网络到Ford-Fulkerson与Dinic实战

1. 从水管网络到最大流:一个核心问题的诞生想象一下,你是一个城市供水系统的总工程师。你的城市有多个水源(水库),需要通过一个复杂的地下管道网络,将水输送到各个居民区。每条管道都有其最大通水能力&…

2026/8/2 0:00:38 阅读更多 →
基于Springboot的企业门户网站(源码+LW+调试文档+讲解)

基于Springboot的企业门户网站(源码+LW+调试文档+讲解)

温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台…

2026/8/2 0:00:38 阅读更多 →
MATLAB xcorr函数详解:从互相关原理到四大实战应用

MATLAB xcorr函数详解:从互相关原理到四大实战应用

1. 从一次信号“找茬”说起:为什么我们需要互相关几年前,我在处理一组声学传感器数据时遇到了一个棘手的问题。我有两个麦克风记录了一段相同的音频信号,理论上它们接收到的声音波形应该非常相似,只是由于麦克风位置不同&#xff…

2026/8/2 0:00:38 阅读更多 →

月新闻

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南 【免费下载链接】BaiduNetdiskPlugin-macOS For macOS.百度网盘 破解SVIP、下载速度限制~ 项目地址: https://gitcode.com/gh_mirrors/ba/BaiduNetdiskPlugin-macOS 还在为百度网盘macOS版的龟速下…

2026/8/2 6:34:16 阅读更多 →
终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 还在为网易云音乐下载的NCM格式文件无法在其他播放器播放而烦恼吗?ncmdump解密工具帮你轻松解决这个困…

2026/8/2 2:47:48 阅读更多 →
HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

AgentCard 智能体卡片:为英语学习 App 打造桌面级学习助手适用平台:HarmonyOS 7.0 (API 26 Beta)一、引言 HarmonyOS 7.0(API 26 Beta)新增了 AgentCard 智能体卡片能力,这是继 HMAF(鸿蒙智能体框架&#x…

2026/8/2 0:23:22 阅读更多 →