并查集(Union-Find / 不相交集)主要解决?
考点:并查集解决的问题(A1)。
解析:并查集维护动态连通性——合并集合、查询"两元素是否同组"。✅ 正确
排除法:无(判断题)。混淆点:与区间信息(线段树系)完全不同的问题域。
关联 · 与其他结构的对比(A6):问题域划分。
并查集的两个基本操作是?
考点:并查集的基本操作(A2)。
解析:两大操作 = find(x)(找代表元)与 union(x, y)(合并集合)。✅ 正确
排除法:无(判断题)。混淆点:名字叫"并查"——合并 + 查询。
关联 · find 的作用(B1):操作一。
并查集的常见实现是?
考点:森林表示法(A3)。
解析:数组 fa[x] 存父节点——每集合一棵树、根为代表元。✅ 正确
排除法:无(判断题)。混淆点:不是邻接矩阵/哈希——就是父指针数组。
关联 · find 的递归细节(B6):数组操作。
带路径压缩与按秩合并的并查集,单次操作复杂度是?
考点:并查集的时间复杂度(A4)。
解析:双优化后近似 ——反阿克曼函数 现实取值为常数。✅ 正确
排除法:无(判断题)。混淆点:不是严格 ——比对数还快。
关联 · 双优化后的复杂度(C6):复杂度来源。
并查集的典型应用是?
考点:应用场景(A5)。
解析:连通性、连通块、判环、Kruskal、亲戚关系。✅ 正确
排除法:无(判断题)。混淆点:区间求和/字符串查找/最短路不是并查集主场。
关联 · 并查集应用(D 组):应用展开。
判断题:并查集与线段树/树状数组解决的问题完全不同——并查集管"集合关系"、后两者管"区间信息"。
考点:与其他结构的对比(A6)。
解析:并查集管"集合关系"、线段树系管"区间信息"——问题域正交。✅ 正确
排除法:无(判断题)。混淆点:按问题选结构(G1)。
关联 · 并查集与其他结构(G1):分类。
find(x) 的作用是?
考点:find 的作用(B1)。
解析:find(x) = 找 x 所在集合的代表元(根)——根相同 ⇔ 同集合。✅ 正确
排除法:无(判断题)。混淆点:不是找"直接父节点"(那是 fa[x])。
关联 · 连通性判定(D1):find 的直接应用。
判断题:朴素 find 沿父链一路向上——若树退化成链(1→2→3→…→n),find(1) 要 步。
考点:朴素 find 的退化(B2)。
解析:链状退化时 find 要 步——无压缩的代价。✅ 正确
排除法:无(判断题)。混淆点:退化 = 每次 union 都挂成链。
关联 · 路径压缩(B3):解决方案。
路径压缩的递归版 find 是?
考点:路径压缩递归版(B3)。
解析:fa[x] == x ? x : fa[x] = find(fa[x])——返回时沿途挂根。✅ 正确
排除法:无(判断题)。混淆点:赋值是压缩的关键(B6)。
关联 · 路径压缩 find 输出(I2):代码版。
判断题:路径压缩也可写成迭代——先找到根、再让沿途节点全部指向根(两次遍历)。
考点:路径压缩迭代版(B4)。
解析:先找根、再让沿途全指向根——两次遍历。✅ 正确
排除法:无(判断题)。混淆点:递归版简洁、迭代版防爆栈。
关联 · 路径压缩递归版(B3):两种写法。
判断题:路径压缩把树压扁——find 一次后,路径上所有节点的父都直接是根,后续 find 接近 。
考点:路径压缩的效果(B5)。
解析:find 一次后沿途全指向根——后续 find 近 。✅ 正确
排除法:无(判断题)。混淆点:压缩是"顺手"的——不影响正确性。
关联 · 压缩效果(I5):代码版。
判断题:路径压缩递归版的关键细节是赋值——fa[x] = find(fa[x]) 把返回的根写回 fa[x]。
考点:find 的递归细节(B6)。
解析:fa[x] = find(fa[x]) 的赋值把根写回——漏赋值则只查不压。✅ 正确
排除法:无(判断题)。混淆点:赋值是路径压缩的灵魂。
关联 · find 填空(I4):填空版。
判断题:find 的返回值恒是"代表元"——同一集合内所有元素 find 结果相同。
考点:find 综合(B7)。
解析:同集合所有元素 find 结果相同——代表元的唯一性。✅ 正确
排除法:无(判断题)。混淆点:这是"同组判定"的依据。
关联 · find 综合(I6):代码版。
union(x, y) 的作用是?
考点:union 的作用(C1)。
解析:union 把两集合合并成一个。✅ 正确
排除法:无(判断题)。混淆点:不是删除/查询大小/排序。
关联 · union 的写法(C2):实现。
union 的标准写法是?
考点:union 的写法(C2)。
解析:fa[find(x)] = find(y)——x 的根挂到 y 的根。✅ 正确
排除法:无(判断题)。混淆点:必须挂根(find 后),不是挂 x 本身。
关联 · union 填空(J4):填空版。
按秩合并(union by rank)是?
考点:按秩合并(C3)。
解析:矮树挂高树——树高至多加 1,防退化。✅ 正确
排除法:无(判断题)。混淆点:秩 = 树高上界,不等同实际高度。
关联 · 按秩合并输出(J2):代码版。
按大小合并(union by size)是?
考点:按大小合并(C4)。
解析:小集合挂大集合——同样防退化且能顺手维护集合大小。✅ 正确
排除法:无(判断题)。混淆点:按大小与按秩效果同级。
关联 · 按大小合并输出(J3):代码版。
判断题:按秩/按大小合并保证树高 ——每次挂接后树高至多加 1,且小树挂大树不增加大树的秩。
考点:启发式合并的效果(C5)。
解析:按秩/按大小保证树高 。✅ 正确
排除法:无(判断题)。混淆点:单用启发式合并已是 单次。
关联 · 按秩合并(C3):效果分析。
判断题:路径压缩 + 按秩合并双优化后, 次操作总复杂度 ——反阿克曼函数增长极慢,实际视为常数。
考点:双优化后的复杂度(C6)。
解析:压缩 + 按秩 → ——反阿克曼近常数。✅ 正确
排除法:无(判断题)。混淆点:单优化各有软肋,双优化是标配。
关联 · 并查集的时间复杂度(A4):总表。
判断题:union 前先 find 两元素——已在同一集合则无需合并(也防止自环)。
考点:union 综合(C7)。
解析:union 前先 find 判同根——同集合则跳过(防自环)。✅ 正确
排除法:无(判断题)。混淆点:跳过是标准实现的一部分。
关联 · union 自环(P3):边界情形。
用并查集判定两节点是否连通,做法是?
考点:连通性判定(D1)。
解析:find(x) == find(y)——根相同即连通。✅ 正确
排除法:无(判断题)。混淆点:不需要遍历边。
关联 · 连通性判定(K1):代码版。
判断题:连通块数 = 合并完成后"根是自身"的节点数——每次有效 union 使块数减 1。
考点:连通块计数(D2)。
解析:块数 = "根是自身"的节点数;有效 union 使块数 -1。✅ 正确
排除法:无(判断题)。混淆点:无效 union(同集合)不减块数。
关联 · 连通块计数(K2):代码版。
无向图判环的并查集做法是?
考点:判环(D3)。
解析:逐边处理——两端已连通则成环。✅ 正确
排除法:无(判断题)。混淆点:无向图判环的经典 做法。
关联 · 判环(K3):代码版。
Kruskal 最小生成树中并查集的作用是?
考点:Kruskal 中的作用(D4)。
解析:判连通避免成环——已连通则跳过该边。✅ 正确
排除法:无(判断题)。混淆点:并查集是 Kruskal 的两个组件之一(另一个是排序)。
关联 · Kruskal 片段(K5):代码版。
判断题:Kruskal = 边排序 + 并查集判环——并查集是 Kruskal 正确性的关键组件。
考点:并查集与最小生成树(D5)。
解析:Kruskal = 边排序 + 并查集判环。✅ 正确
排除法:无(判断题)。混淆点:判环的正确性直接来自并查集。
关联 · Kruskal 中的作用(D4):组件定位。
判断题:并查集还用于——亲戚关系、等价类合并、区间染色(反向并查集)、食物链(带权并查集)。
考点:其他应用(D6)。
解析:亲戚关系、等价类合并、区间染色、带权并查集(食物链)。✅ 正确
排除法:无(判断题)。混淆点:带权并查集存"与根的关系"是进阶形态。
关联 · 亲戚关系(K4):代码版。
判断题:并查集的核心价值 = 高效维护"同一组"关系——一切"合并+查同组"的问题都适用。
考点:应用综合(D7)。
解析:一切"合并 + 查同组"问题都用并查集。✅ 正确
排除法:无(判断题)。混淆点:识别"同组关系"是关键。
关联 · 应用场景(A5):识别准则。
字典树(Trie)的定义是?
考点:Trie 的定义(E1)。
解析:多叉树——每条边一个字符、根到节点路径 = 字符串前缀。✅ 正确
排除法:无(判断题)。混淆点:不是 BST(按字符分叉而非值)。
关联 · Trie 的节点结构(E3):结构细节。
Trie 主要解决?
考点:Trie 解决的问题(E2)。
解析:字符串前缀查询——插入/查找/前缀统计,。✅ 正确
排除法:无(判断题)。混淆点:排序/最短路/区间和不是 Trie 主场。
关联 · 应用场景(E6):用途。
Trie 的节点通常包含?
考点:Trie 的节点结构(E3)。
解析:子节点指针数组 + 结束标记 + 计数。✅ 正确
排除法:无(判断题)。混淆点:字符集决定数组大小(26 字母/01 位)。
关联 · Trie 下标 0 混淆(H3):下标约定。
Trie 插入/查询长度为 的字符串的复杂度是?
考点:Trie 的复杂度(E4)。
解析:插入/查询 (L 为串长)——与单词总数无关。✅ 正确
排除法:无(判断题)。混淆点:哈希平均 但前缀查询做不到。
关联 · 复杂度总表(G4):总表。
Trie 与哈希表的正确对比是?
考点:与哈希的对比(E5)。
解析:Trie 支持前缀与字典序遍历(哈希不支持);哈希单点更快。✅ 正确
排除法:无(判断题)。混淆点:按需选择(G2)。
关联 · Trie 与哈希(G2):选择。
Trie 的典型应用是?
考点:应用场景(E6)。
解析:单词查找、前缀匹配、自动补全、最大异或对。✅ 正确
排除法:无(判断题)。混淆点:最大异或对用 01-Trie(二进制位作边)。
关联 · 最大异或对思想(M3):应用代码。
Trie 插入字符串的过程是?
考点:插入(F1)。
解析:逐字符走边、缺边新建节点、最后标记结束。✅ 正确
排除法:无(判断题)。混淆点:整串存根是错的——逐字符分摊。
关联 · Trie 插入填空(O5):填空版。
判断题:查询完整单词 = 逐字符走边,全部走通且末节点有结束标记才算存在。
考点:查询完整单词(F2)。
解析:走通 + 末节点有结束标记才算存在。✅ 正确
排除法:无(判断题)。混淆点:无结束标记则只是"某单词的前缀"。
关联 · 查询输出(L3):代码版。
判断题:前缀查询 = 逐字符走边,全部走通即"存在以该前缀开头的单词"(不需要结束标记)。
考点:前缀查询(F3)。
解析:走通即"存在以该前缀开头的单词"——无需结束标记。✅ 正确
排除法:无(判断题)。混淆点:与完整查询的差别就在结束标记。
关联 · 前缀统计(L4):代码版。
判断题:Trie 删除单词 = 找到末节点去掉结束标记;若整条路径无其他单词可回收节点。
考点:删除思想(F4)。
解析:去掉结束标记;路径无其他单词可回收节点。✅ 正确
排除法:无(判断题)。混淆点:回收是可选优化。
关联 · 插入(F1):对称操作。
判断题:Trie 节点存"经过次数"计数——插入时路径上所有节点计数 +1,可统计"以某前缀开头的单词数"。
考点:节点计数(F5)。
解析:路径节点计数 +1 → 统计"以某前缀开头的单词数"。✅ 正确
排除法:无(判断题)。混淆点:计数是前缀统计的实现基础。
关联 · 忘计数(P5):错误示范。
判断题:Trie 三大操作 = 插入、查询、前缀统计——都沿"逐字符走边"的同一框架。
考点:操作综合(F6)。
解析:插入、查询、前缀统计共享"逐字符走边"框架。✅ 正确
排除法:无(判断题)。混淆点:一个框架三操作是 Trie 的简洁性。
关联 · Trie 综合(L6):代码版。
判断题:并查集与线段树/树状数组/Trie 解决的问题正交——按"集合关系/区间信息/字符串前缀"三分类选结构。
考点:并查集与其他结构(G1)。
解析:集合关系/区间信息/字符串前缀三分类——问题域正交。✅ 正确
排除法:无(判断题)。混淆点:先分类再选结构。
关联 · 选择矩阵(G3):分类选型。
需要"统计以 'ab' 为前缀的单词数",选?
考点:Trie 与哈希(G2)。
解析:前缀统计 → Trie(哈希做不到)。✅ 正确
排除法:无(判断题)。混淆点:前缀查询是 Trie 的独门。
关联 · 与哈希的对比(E5):选择依据。
判断题:选择矩阵——同组关系用并查集、前缀查询用 Trie、精确查找用哈希、区间信息用线段树系。
考点:选择矩阵(G3)。
解析:同组→并查集、前缀→Trie、精确→哈希、区间→线段树系。✅ 正确
排除法:无(判断题)。混淆点:四类问题的选型总表。
关联 · 选择综合(G6):应用版。
判断题:复杂度总表——并查集近 、Trie (L 为串长)、哈希平均 ——各擅胜场。
考点:复杂度总表(G4)。
解析:并查集近 、Trie 、哈希平均 。✅ 正确
排除法:无(判断题)。混淆点:复杂度与能力是两维度。
关联 · 复杂度总表(G4 与 A4/E4):汇总。
判断题:并查集空间 、Trie 空间 ——Trie 空间消耗大是其主要缺点。
考点:综合对比(G5)。
解析:Trie 空间 ——空间是其主要缺点。✅ 正确
排除法:无(判断题)。混淆点:26 字母数组大部分空置。
关联 · Trie 的节点结构(E3):空间来源。
需要"动态合并集合 + 查询同组",选?
考点:选择综合(G6)。
解析:动态合并 + 查同组 → 并查集。✅ 正确
排除法:无(判断题)。混淆点:Trie/哈希/线段树都不管"同组"。
关联 · 选择矩阵(G3):矩阵落点。
判断题:只写 return fa[x] 的 find(无压缩)在链状退化时单次 ——大量操作会超时。
考点:find 忘路径压缩(H1)。
解析:无压缩的 find 链状退化 ——大量操作超时。✅ 正确
排除法:无(判断题)。混淆点:正确性对、性能错。
关联 · find 无压缩(P1):错误示范。
判断题:union 不做按秩合并,最坏树高可达 ——即使有路径压缩也不如双优化稳定。
考点:union 未按秩(H2)。
解析:不做按秩合并最坏树高 。✅ 正确
排除法:无(判断题)。混淆点:单压缩也可能退化(构造性输入)。
关联 · 按秩合并(C3):优化纪律。
判断题:Trie 用"子节点下标 0 表示空"时,节点编号从 1 开始——0 号留作空标志,混用会误判。
考点:Trie 下标 0 混淆(H3)。
解析:0 号留作空标志、节点从 1 编号——混用误判。✅ 正确
排除法:无(判断题)。混淆点:根也是 0 号,但"空"与"根"语义不同。
关联 · Trie 下标 0 混淆(P4):错误示范。
判断题:并查集使用前必须初始化 fa[i] = i——忘初始化导致所有 find 结果错乱。
考点:并查集忘初始化(H4)。
解析:必须 fa[i] = i——忘初始化全错乱。✅ 正确
排除法:无(判断题)。混淆点:初始化是并查集第一步(P2)。
关联 · 未初始化(P2):错误示范。
判断题:以下结论全部正确——"find 带路径压缩;union 按秩合并防退化;并查集初始化 fa[i]=i;Trie 逐字符走边、0 下标作空标志;前缀查询不需结束标记"。
考点:综合判断(H5)。
解析:五结论全对——压缩 find、按秩 union、初始化、Trie 走边与 0 下标、前缀无需结束标记。✅ 正确
排除法:无(判断题)。混淆点:本章核心模板要点收官自查。
关联 · 本章全部核心结论:收官综合判断题。
01#include <bits/stdc++.h> 02using namespace std; 03int fa[6] = {0, 2, 3, 4, 5, 5}; // 链:1→2→3→4→5 04int find(int x) { 05 while (fa[x] != x) x = fa[x]; // 朴素 find(无压缩) 06 return x; 07} 08int main() { 09 cout << find(1); 10 return 0; 11}
单选题:程序输出是?(元素 1 所在集合的根)
考点:朴素 find 输出(I1)。
解析:链 1→2→3→4→5,find(1) 沿链到根 5。正确答案 A。
实现要点:朴素 find = while 沿 fa 上爬。手算:1→2→3→4→5。
排除法:B 是起点;C/D 无依据。
关联 · 朴素 find 的退化(B2):概念题代码化。
01#include <bits/stdc++.h> 02using namespace std; 03int fa[6] = {0, 2, 3, 4, 5, 5}; 04int find(int x) { 05 return fa[x] == x ? x : fa[x] = find(fa[x]); // 路径压缩 06} 07int main() { 08 find(1); 09 cout << fa[1] << " " << fa[2] << " " << fa[3] << " " << fa[4]; 10 return 0; 11}
单选题:程序输出是?(find(1) 后沿途节点的父节点——全部被压到根)
考点:路径压缩 find 输出(I2)。
解析:find(1) 后沿途全挂根 → 5 5 5 5。正确答案 A。
实现要点:递归版 fa[x] = find(fa[x])。手算:回溯逐层写回。
排除法:B 是压缩前;C/D 无依据。
关联 · 路径压缩递归版(B3):概念题代码化。
01#include <bits/stdc++.h> 02using namespace std; 03int fa[6] = {0, 2, 3, 4, 5, 5}; 04int find(int x) { 05 int steps = 0; 06 while (fa[x] != x) { x = fa[x]; steps++; } 07 return steps; 08} 09int main() { 10 cout << find(1); 11 return 0; 12}
单选题:程序输出是?(链 1→2→3→4→5 中 find(1) 的步数)
考点:find 链长(I3)。
解析:1 到根走 4 步。正确答案 A。
实现要点:链长 = 深度。手算:1→2→3→4→5。
排除法:B 是节点数;C/D 无依据。
关联 · 朴素 find 的退化(B2):退化实证。
01#include <bits/stdc++.h> 02using namespace std; 03int fa[6] = {0, 2, 3, 4, 5, 5}; 04int find(int x) { 05 return ______; // 路径压缩递归版 06} 07int main() { 08 find(1); 09 cout << fa[1]; 10 return 0; 11}
单选题:横线处应填入?(使输出为 5)
考点:find 填空(I4)。
解析:fa[x] == x ? x : fa[x] = find(fa[x])。正确答案 A。
实现要点:递归压缩三目式。手算:fa[1] = 5。
排除法:B 无压缩;C 无赋值;D 只压一层。
关联 · find 的递归细节(B6):填空版。
01#include <bits/stdc++.h> 02using namespace std; 03int fa[6] = {0, 2, 3, 4, 5, 5}; 04int find(int x) { 05 return fa[x] == x ? x : fa[x] = find(fa[x]); 06} 07int main() { 08 find(1); 09 int steps = 0, x = 1; 10 while (fa[x] != x) { x = fa[x]; steps++; } // 压缩后再次 find(1) 的步数 11 cout << steps; 12 return 0; 13}
单选题:程序输出是?(路径压缩后 find(1) 只需 1 步)
考点:压缩效果(I5)。
解析:压缩后 find(1) 只需 1 步。正确答案 A。
实现要点:压缩后树高约 1。手算:fa[1] 直接是根。
排除法:B 是压缩前;C/D 无依据。
关联 · 路径压缩的效果(B5):概念题代码化。
01#include <bits/stdc++.h> 02using namespace std; 03int fa[7] = {0, 1, 1, 2, 3, 4, 6}; // 1←2←3←4←5、6 独立 04int find(int x) { 05 return fa[x] == x ? x : fa[x] = find(fa[x]); 06} 07int main() { 08 find(5); 09 cout << find(1) << " " << find(5) << " " << find(6); 10 return 0; 11}
单选题:程序输出是?(三个集合的代表元)
考点:find 综合(I6)。
解析:集合 {1,2,3,4,5} 根 1、{6} 根 6 → 1 1 6。正确答案 A。
实现要点:同集合代表元一致。手算:两集合各找根。
排除法:B 是未压缩结果;C/D 无依据。
关联 · find 综合(B7):概念题代码化。
01#include <bits/stdc++.h> 02using namespace std; 03int fa[6]; 04int find(int x) { 05 return fa[x] == x ? x : fa[x] = find(fa[x]); 06} 07int main() { 08 for (int i = 1; i <= 5; i++) fa[i] = i; 09 fa[find(1)] = find(2); // union(1,2) 10 fa[find(2)] = find(3); // union(2,3) 11 cout << find(1) << " " << find(3); 12 return 0; 13}
单选题:程序输出是?(合并后 1 与 3 是否同集合)
考点:union 输出(J1)。
解析:1→2→3 链,find(1) = find(3) = 3 → 3 3。正确答案 A。
实现要点:union 挂根。手算:fa[1]=2、fa[2]=3。
排除法:B/C/D 无依据。
关联 · union 的写法(C2):概念题代码化。
01#include <bits/stdc++.h> 02using namespace std; 03int fa[6], rk[6]; 04int find(int x) { 05 return fa[x] == x ? x : fa[x] = find(fa[x]); 06} 07void uni(int x, int y) { 08 int rx = find(x), ry = find(y); 09 if (rx == ry) return; 10 if (rk[rx] < rk[ry]) fa[rx] = ry; // 矮挂高 11 else if (rk[rx] > rk[ry]) fa[ry] = rx; 12 else { fa[rx] = ry; rk[ry]++; } // 等高时挂后秩 +1 13} 14int main() { 15 for (int i = 1; i <= 4; i++) { fa[i] = i; rk[i] = 0; } 16 uni(1, 2); uni(3, 4); uni(1, 3); 17 cout << find(1) << " " << find(4); 18 return 0; 19}
单选题:程序输出是?(按秩合并:uni(1,2) 后 1 挂 2;uni(3,4) 后 3 挂 4;uni(1,3) 时两树等高 → 2 挂 4)
考点:按秩合并输出(J2)。
解析:三组合并后代表元 4 → 4 4。正确答案 A。
实现要点:矮挂高、等高秩 +1。手算:1→2、3→4、2→4。
排除法:B 是中间状态;C/D 无依据。
关联 · 按秩合并(C3):概念题代码化。
01#include <bits/stdc++.h> 02using namespace std; 03int fa[6], sz[6]; 04int find(int x) { 05 return fa[x] == x ? x : fa[x] = find(fa[x]); 06} 07void uni(int x, int y) { 08 int rx = find(x), ry = find(y); 09 if (rx == ry) return; 10 if (sz[rx] < sz[ry]) swap(rx, ry); // 小挂大 11 fa[ry] = rx; sz[rx] += sz[ry]; 12} 13int main() { 14 for (int i = 1; i <= 5; i++) { fa[i] = i; sz[i] = 1; } 15 uni(1, 2); uni(3, 4); uni(1, 3); 16 cout << sz[find(1)]; 17 return 0; 18}
单选题:程序输出是?(合并后集合 {1,2,3,4} 的大小)
考点:按大小合并输出(J3)。
解析:合并 {1,2,3,4} 大小为 4。正确答案 A。
实现要点:小挂大、大小累加。手算:1+1+2 = 4。
排除法:B/C/D 无依据。
关联 · 按大小合并(C4):概念题代码化。
01#include <bits/stdc++.h> 02using namespace std; 03int fa[6]; 04int find(int x) { 05 return fa[x] == x ? x : fa[x] = find(fa[x]); 06} 07int main() { 08 for (int i = 1; i <= 5; i++) fa[i] = i; 09 ______; // union(1, 3):把 1 的根挂到 3 的根 10 cout << find(1) << " " << find(3); 11 return 0; 12}
单选题:横线处应填入?(使输出为 3 3)
考点:union 填空(J4)。
解析:fa[find(1)] = find(3)。正确答案 A。
实现要点:挂根不挂节点。手算:find(1)=3 后同集合。
排除法:B 直接挂 fa[1](路径压缩后可能不同);C 语法错;D 方向反。
关联 · union 的写法(C2):填空版。
01#include <bits/stdc++.h> 02using namespace std; 03int fa[6]; 04int find(int x) { 05 return fa[x] == x ? x : fa[x] = find(fa[x]); 06} 07int main() { 08 for (int i = 1; i <= 5; i++) fa[i] = i; 09 fa[find(2)] = find(1); // 2 挂到 1 10 fa[find(3)] = find(1); // 3 挂到 1 11 fa[find(4)] = find(2); // 4 挂到 2 12 cout << fa[2] << " " << fa[3] << " " << fa[4]; 13 return 0; 14}
单选题:程序输出是?(2、3 挂 1;4 挂到 find(2)——此时 2 已挂 1,故 4 也挂 1)
考点:合并后结构(J5)。
解析:2、3 挂 1;4 挂 find(2) = 1 → 1 1 1。正确答案 A。
实现要点:union 挂的是当时的根。手算:find(2) 已因 2 挂 1 而返回 1。
排除法:B 是 4 挂 2 的误读;C/D 无依据。
关联 · union 综合(C7):根挂接细节。
01#include <bits/stdc++.h> 02using namespace std; 03int fa[6]; 04int find(int x) { 05 return fa[x] == x ? x : fa[x] = find(fa[x]); 06} 07int main() { 08 for (int i = 1; i <= 5; i++) fa[i] = i; 09 fa[find(1)] = find(2); 10 fa[find(2)] = find(3); 11 fa[find(4)] = find(5); 12 fa[find(5)] = find(3); // 合并两个集合 13 int cnt = 0; 14 for (int i = 1; i <= 5; i++) if (find(i) == i) cnt++; 15 cout << cnt; 16 return 0; 17}
单选题:程序输出是?(最终连通块数)
考点:union 综合(J6)。
解析:全部合并成一块 → 1。正确答案 A。
实现要点:连通块 = 根自指计数。手算:4 次有效 union 减 4 块。
排除法:B/C/D 无依据。
关联 · 连通块计数(D2):概念题代码化。
01#include <bits/stdc++.h> 02using namespace std; 03int fa[6]; 04int find(int x) { 05 return fa[x] == x ? x : fa[x] = find(fa[x]); 06} 07int main() { 08 for (int i = 1; i <= 5; i++) fa[i] = i; 09 fa[find(1)] = find(2); 10 fa[find(3)] = find(4); 11 cout << (find(1) == find(3) ? "CONNECTED" : "NOT-CONNECTED"); 12 return 0; 13}
单选题:程序输出是?
考点:连通性判定(K1)。
解析:1 与 3 不同集合 → NOT-CONNECTED。正确答案 A。
实现要点:find 比根。手算:{1,2} 与 {3,4}。
排除法:B 是连通;C/D 无依据。
关联 · 连通性判定(D1):概念题代码化。
01#include <bits/stdc++.h> 02using namespace std; 03int fa[7]; 04int find(int x) { 05 return fa[x] == x ? x : fa[x] = find(fa[x]); 06} 07int main() { 08 for (int i = 1; i <= 6; i++) fa[i] = i; 09 fa[find(1)] = find(2); // {1,2} 10 fa[find(3)] = find(4); // {3,4} 11 fa[find(5)] = find(6); // {5,6} 12 int cnt = 0; 13 for (int i = 1; i <= 6; i++) if (find(i) == i) cnt++; 14 cout << cnt; 15 return 0; 16}
单选题:程序输出是?(6 个元素分成 3 组后的连通块数)
考点:连通块计数(K2)。
解析:三组 → 3 块。正确答案 A。
实现要点:根自指计数。手算:2、4、6 是根。
排除法:B/C/D 无依据。
关联 · 连通块计数(D2):概念题代码化。
01#include <bits/stdc++.h> 02using namespace std; 03int fa[4]; 04int find(int x) { 05 return fa[x] == x ? x : fa[x] = find(fa[x]); 06} 07int main() { 08 for (int i = 1; i <= 3; i++) fa[i] = i; 09 bool cycle = false; 10 int e[3][2] = {{1, 2}, {2, 3}, {1, 3}}; // 三条边 11 for (int i = 0; i < 3; i++) { 12 int x = e[i][0], y = e[i][1]; 13 if (find(x) == find(y)) cycle = true; 14 else fa[find(x)] = find(y); 15 } 16 cout << (cycle ? "CYCLE" : "NO-CYCLE"); 17 return 0; 18}
单选题:程序输出是?(三角形三边成环)
考点:判环(K3)。
解析:第三条边 (1,3) 两端已连通 → CYCLE。正确答案 A。
实现要点:逐边查连通。手算:1-2、2-3 后 1 与 3 同集合。
排除法:B 是树;C/D 无依据。
关联 · 判环(D3):概念题代码化。
01#include <bits/stdc++.h> 02using namespace std; 03int fa[6]; 04int find(int x) { 05 return fa[x] == x ? x : fa[x] = find(fa[x]); 06} 07int main() { 08 for (int i = 1; i <= 5; i++) fa[i] = i; 09 // 亲戚关系:1 与 2、2 与 3、4 与 5 是亲戚 10 fa[find(1)] = find(2); 11 fa[find(2)] = find(3); 12 fa[find(4)] = find(5); 13 cout << (find(1) == find(3) ? "YES" : "NO") << " " 14 << (find(3) == find(5) ? "YES" : "NO"); 15 return 0; 16}
单选题:程序输出是?(1 与 3 是亲戚、3 与 5 不是)
考点:亲戚关系(K4)。
解析:1-3 亲戚、3-5 不是 → YES NO。正确答案 A。
实现要点:传递闭包 = 并查集。手算:{1,2,3} 与 {4,5}。
排除法:B/C/D 无依据。
关联 · 其他应用(D6):概念题代码化。
01#include <bits/stdc++.h> 02using namespace std; 03int fa[5]; 04int find(int x) { 05 return fa[x] == x ? x : fa[x] = find(fa[x]); 06} 07int main() { 08 for (int i = 1; i <= 4; i++) fa[i] = i; 09 // 边按权升序:(1,2,1) (3,4,1) (2,3,2) (1,3,5) 10 int edges[4][3] = {{1, 2, 1}, {3, 4, 1}, {2, 3, 2}, {1, 3, 5}}; 11 int cost = 0, taken = 0; 12 for (int i = 0; i < 4; i++) { 13 int x = edges[i][0], y = edges[i][1], w = edges[i][2]; 14 if (find(x) != find(y)) { 15 fa[find(x)] = find(y); 16 cost += w; taken++; 17 } 18 } 19 cout << cost << " " << taken; 20 return 0; 21}
单选题:程序输出是?(Kruskal 最小生成树的总权与边数)
考点:Kruskal 片段(K5)。
解析:取 (1,2,1)、(3,4,1)、(2,3,2),跳过 (1,3,5) → 4 3。正确答案 A。
实现要点:边排序 + 判连通。手算:权 1+1+2 = 4、3 条边。
排除法:B 是全边权;C/D 无依据。
关联 · Kruskal 中的作用(D4):概念题代码化。
01#include <bits/stdc++.h> 02using namespace std; 03int fa[6]; 04int find(int x) { 05 return fa[x] == x ? x : fa[x] = find(fa[x]); 06} 07int main() { 08 for (int i = 1; i <= 5; i++) fa[i] = i; 09 fa[find(1)] = find(2); 10 cout << (______ ? "SAME" : "DIFF"); // 判 1、2 同集合 11 return 0; 12}
单选题:横线处应填入?(使输出为 SAME)
考点:应用填空(K6)。
解析:find(1) == find(2)。正确答案 A。
实现要点:同组判定 = 根相等。手算:合并后同根。
排除法:B 反向;C 比父指针(可能不同);D 无依据。
关联 · 连通性判定(D1):填空版。
01#include <bits/stdc++.h> 02using namespace std; 03int fa[7]; 04int find(int x) { 05 return fa[x] == x ? x : fa[x] = find(fa[x]); 06} 07int main() { 08 for (int i = 1; i <= 6; i++) fa[i] = i; 09 // 边:(1,2) (2,3) (4,5) —— 3 个连通块 {1,2,3}、{4,5}、{6} 10 fa[find(1)] = find(2); 11 fa[find(2)] = find(3); 12 fa[find(4)] = find(5); 13 int blocks = 0; 14 for (int i = 1; i <= 6; i++) if (find(i) == i) blocks++; 15 cout << blocks; 16 return 0; 17}
单选题:程序输出是?
考点:应用综合(K7)。
解析:{1,2,3}、{4,5}、{6} → 3 块。正确答案 A。
实现要点:混合操作 + 计数。手算:三个根。
排除法:B/C/D 无依据。
关联 · 应用综合(D7):概念题代码化。
01#include <bits/stdc++.h> 02using namespace std; 03int ch[100][26]; // 子节点(0 = 空) 04int cnt[100]; // 经过次数 05int tot = 0; 06void ins(string s) { 07 int p = 0; 08 for (char c : s) { 09 int t = c - 'a'; 10 if (!ch[p][t]) ch[p][t] = ++tot; // 新建节点 11 p = ch[p][t]; 12 cnt[p]++; 13 } 14} 15int main() { 16 ins("cat"); ins("car"); ins("dog"); 17 cout << tot; 18 return 0; 19}
单选题:程序输出是?(cat 新建 3 节点、car 新建 1 节点(r)、dog 新建 3 节点)
考点:建树输出(L1)。
解析:cat 建 3、car 建 1(r)、dog 建 3 → 7 节点。正确答案 A。
实现要点:共享前缀只建一次。手算:c-a 共享。
排除法:B 多数 1;C/D 无依据。
关联 · Trie 插入(F1):概念题代码化。
01#include <bits/stdc++.h> 02using namespace std; 03int ch[100][26]; 04int cnt[100]; 05int tot = 0; 06void ins(string s) { 07 int p = 0; 08 for (char c : s) { 09 int t = c - 'a'; 10 if (!ch[p][t]) ch[p][t] = ++tot; 11 p = ch[p][t]; 12 cnt[p]++; 13 } 14} 15int main() { 16 ins("cat"); ins("car"); 17 cout << cnt[ch[0]['c' - 'a']]; // 根下 'c' 节点的经过次数 18 return 0; 19}
单选题:程序输出是?(以 'c' 开头的单词数)
考点:插入过程(L2)。
解析:'c' 节点经过 2 次(cat、car)。正确答案 A。
实现要点:路径计数 +1。手算:两个 c 开头单词。
排除法:B/C/D 无依据。
关联 · 节点计数(F5):概念题代码化。
01#include <bits/stdc++.h> 02using namespace std; 03int ch[100][26]; 04bool isEnd[100]; 05int tot = 0; 06void ins(string s) { 07 int p = 0; 08 for (char c : s) { 09 int t = c - 'a'; 10 if (!ch[p][t]) ch[p][t] = ++tot; 11 p = ch[p][t]; 12 } 13 isEnd[p] = true; 14} 15bool query(string s) { 16 int p = 0; 17 for (char c : s) { 18 int t = c - 'a'; 19 if (!ch[p][t]) return false; 20 p = ch[p][t]; 21 } 22 return isEnd[p]; 23} 24int main() { 25 ins("cat"); ins("car"); ins("dog"); 26 cout << (query("cat") ? "Y" : "N") << (query("ca") ? "Y" : "N") 27 << (query("cow") ? "Y" : "N"); 28 return 0; 29}
单选题:程序输出是?(cat 存在、ca 不是完整单词、cow 不存在)
考点:查询输出(L3)。
解析:cat 完整存在 Y、ca 无结束标记 N、cow 走不通 N → YNN。正确答案 A。
实现要点:完整查询需结束标记。手算:逐个走边。
排除法:B 把 ca 当前缀;C/D 无依据。
关联 · 查询完整单词(F2):概念题代码化。
01#include <bits/stdc++.h> 02using namespace std; 03int ch[100][26]; 04int cnt[100]; 05int tot = 0; 06void ins(string s) { 07 int p = 0; 08 for (char c : s) { 09 int t = c - 'a'; 10 if (!ch[p][t]) ch[p][t] = ++tot; 11 p = ch[p][t]; 12 cnt[p]++; 13 } 14} 15int pref(string s) { 16 int p = 0; 17 for (char c : s) { 18 int t = c - 'a'; 19 if (!ch[p][t]) return 0; 20 p = ch[p][t]; 21 } 22 return cnt[p]; 23} 24int main() { 25 ins("cat"); ins("car"); ins("dog"); ins("do"); 26 cout << pref("do"); 27 return 0; 28}
单选题:程序输出是?(以 "do" 为前缀的单词数:dog、do 共 2 个)
考点:前缀统计(L4)。
解析:以 "do" 开头:dog、do → 2。正确答案 A。
实现要点:前缀查询 = 走通后返回 cnt。手算:两单词经过 do。
排除法:B/C/D 无依据。
关联 · 前缀查询(F3):概念题代码化。
01#include <bits/stdc++.h> 02using namespace std; 03int ch[100][26]; 04int cnt[100]; 05int tot = 0; 06void ins(string s) { 07 int p = 0; 08 for (char c : s) { 09 int t = c - 'a'; 10 if (!ch[p][t]) ch[p][t] = ++tot; 11 ______; // 移动到子节点 12 cnt[p]++; 13 } 14} 15int main() { 16 ins("ab"); 17 cout << tot; 18 return 0; 19}
单选题:横线处应填入?(使输出为 2)
考点:Trie 填空(L5)。
解析:p = ch[p][t]——移动到子节点。正确答案 A。
实现要点:走边 = 指针下移。手算:ab 建 2 节点。
排除法:B 丢路径;C 错对象;D 无依据。
关联 · Trie 插入(F1):填空版。
01#include <bits/stdc++.h> 02using namespace std; 03int ch[100][26]; 04bool isEnd[100]; 05int tot = 0; 06void ins(string s) { 07 int p = 0; 08 for (char c : s) { 09 int t = c - 'a'; 10 if (!ch[p][t]) ch[p][t] = ++tot; 11 p = ch[p][t]; 12 } 13 isEnd[p] = true; 14} 15bool query(string s) { 16 int p = 0; 17 for (char c : s) { 18 int t = c - 'a'; 19 if (!ch[p][t]) return false; 20 p = ch[p][t]; 21 } 22 return isEnd[p]; 23} 24int main() { 25 ins("a"); ins("ab"); ins("abc"); 26 cout << (query("a") ? "Y" : "N") << (query("ab") ? "Y" : "N") 27 << (query("abc") ? "Y" : "N") << (query("abcd") ? "Y" : "N"); 28 return 0; 29}
单选题:程序输出是?
考点:Trie 综合(L6)。
解析:a、ab、abc 都完整存在、abcd 走不通 → YYYN。正确答案 A。
实现要点:完整查询 + 结束标记。手算:逐串验证。
排除法:B/C/D 无依据。
关联 · 操作综合(F6):概念题代码化。
01#include <bits/stdc++.h> 02using namespace std; 03int ch[100][26]; 04bool isEnd[100]; 05int tot = 0; 06void ins(string s) { 07 int p = 0; 08 for (char c : s) { 09 int t = c - 'a'; 10 if (!ch[p][t]) ch[p][t] = ++tot; 11 p = ch[p][t]; 12 } 13 isEnd[p] = true; 14} 15bool query(string s) { 16 int p = 0; 17 for (char c : s) { 18 int t = c - 'a'; 19 if (!ch[p][t]) return false; 20 p = ch[p][t]; 21 } 22 return isEnd[p]; 23} 24int main() { 25 string words[4] = {"the", "a", "there", "answer"}; 26 for (string w : words) ins(w); 27 cout << (query("there") ? "FOUND" : "NOT-FOUND"); 28 return 0; 29}
单选题:程序输出是?
考点:单词查找(M1)。
解析:there 已插入 → FOUND。正确答案 A。
实现要点:Trie 单词查找框架。手算:逐字符走边。
排除法:B 是未找到;C/D 无依据。
关联 · 单词查找(E6):应用代码化。
01#include <bits/stdc++.h> 02using namespace std; 03int ch[100][26]; 04int cnt[100]; 05int tot = 0; 06void ins(string s) { 07 int p = 0; 08 for (char c : s) { 09 int t = c - 'a'; 10 if (!ch[p][t]) ch[p][t] = ++tot; 11 p = ch[p][t]; 12 cnt[p]++; 13 } 14} 15int pref(string s) { 16 int p = 0; 17 for (char c : s) { 18 int t = c - 'a'; 19 if (!ch[p][t]) return 0; 20 p = ch[p][t]; 21 } 22 return cnt[p]; 23} 24int main() { 25 string words[5] = {"app", "apple", "apply", "banana", "apex"}; 26 for (string w : words) ins(w); 27 cout << pref("app"); 28 return 0; 29}
单选题:程序输出是?(以 "app" 为前缀的单词数:app、apple、apply 共 3 个)
考点:前缀计数(M2)。
解析:以 "app" 开头:app、apple、apply → 3。正确答案 A。
实现要点:路径计数。手算:三单词共享 app 前缀。
排除法:B 漏 app 本身;C/D 无依据。
关联 · 前缀计数(F5):概念题代码化。
01#include <bits/stdc++.h> 02using namespace std; 03int main() { 04 // 最大异或对:对每个数在 01-Trie 上贪心走"相反位"路径 05 int a[3] = {3, 5, 6}; 06 int mx = 0; 07 for (int i = 0; i < 3; i++) 08 for (int j = i + 1; j < 3; j++) 09 mx = max(mx, a[i] ^ a[j]); 10 cout << mx; 11 return 0; 12}
单选题:程序输出是?(3^5=6、3^6=5、5^6=3,最大 6)
考点:最大异或对思想(M3)。
解析:3^5=6、3^6=5、5^6=3 → 最大 6。正确答案 A。
实现要点:01-Trie 贪心走相反位(朴素版两两异或)。手算:逐对异或。
排除法:B/C/D 无依据。
关联 · 最大异或对(E6):应用代码化。
01#include <bits/stdc++.h> 02using namespace std; 03int main() { 04 string words[3] = {"banana", "apple", "apricot"}; 05 sort(words, words + 3); 06 for (string w : words) cout << w << " "; 07 return 0; 08}
单选题:程序输出是?(字典序——Trie 深度优先遍历天然得到此序)
考点:字典序输出(M4)。
解析:排序后 apple apricot banana。正确答案 A。
实现要点:Trie 深度优先遍历天然字典序。手算:sort 对照。
排除法:B/C/D 无依据。
关联 · 与哈希的对比(E5):字典序能力。
01#include <bits/stdc++.h> 02using namespace std; 03int ch[100][26]; 04bool isEnd[100]; 05int tot = 0; 06void ins(string s) { 07 int p = 0; 08 for (char c : s) { 09 int t = c - 'a'; 10 if (!ch[p][t]) ch[p][t] = ++tot; 11 p = ch[p][t]; 12 } 13 ______; // 标记单词结束 14} 15int main() { 16 ins("hi"); 17 cout << isEnd[2]; 18 return 0; 19}
单选题:横线处应填入?(使输出为 1)
考点:应用填空(M5)。
解析:isEnd[p] = true——标记结束。正确答案 A。
实现要点:插入最后一步。手算:isEnd[2] = 1。
排除法:B 反向;C 标记错节点;D 无依据。
关联 · Trie 插入(F1):填空版。
01#include <bits/stdc++.h> 02using namespace std; 03int ch[100][26]; 04int cnt[100]; 05int tot = 0; 06void ins(string s) { 07 int p = 0; 08 for (char c : s) { 09 int t = c - 'a'; 10 if (!ch[p][t]) ch[p][t] = ++tot; 11 p = ch[p][t]; 12 cnt[p]++; 13 } 14} 15int main() { 16 string words[4] = {"ab", "abc", "abd", "ac"}; 17 for (string w : words) ins(w); 18 // 统计以 'a' 开头的单词数 = 根下 'a' 节点的 cnt 19 cout << cnt[ch[0]['a' - 'a']]; 20 return 0; 21}
单选题:程序输出是?
考点:应用综合(M6)。
解析:'a' 节点经过 4 次。正确答案 A。
实现要点:根下 'a' 的 cnt = 全部单词数。手算:四个 a 开头。
排除法:B/C/D 无依据。
关联 · 节点计数(F5):应用版。
01#include <bits/stdc++.h> 02using namespace std; 03int fa[5]; 04int find(int x) { 05 return fa[x] == x ? x : fa[x] = find(fa[x]); 06} 07int main() { 08 for (int i = 1; i <= 4; i++) fa[i] = i; 09 // 四边形加一条对角线:(1,2) (2,3) (3,4) (4,1) —— 第 4 条边成环 10 int e[4][2] = {{1, 2}, {2, 3}, {3, 4}, {4, 1}}; 11 int cycleAt = 0; 12 for (int i = 0; i < 4; i++) { 13 int x = e[i][0], y = e[i][1]; 14 if (find(x) == find(y)) { cycleAt = i + 1; break; } 15 fa[find(x)] = find(y); 16 } 17 cout << cycleAt; 18 return 0; 19}
单选题:程序输出是?(第几条边发现成环)
考点:并查集判环(N1)。
解析:第 4 条边 (4,1) 两端已连通 → 4。正确答案 A。
实现要点:逐边判连通。手算:前 3 条连成 1-2-3-4。
排除法:B/C/D 无依据。
关联 · 判环(D3):应用版。
01#include <bits/stdc++.h> 02using namespace std; 03int ch[100][26]; 04int cnt[100]; 05int tot = 0; 06void ins(string s) { 07 int p = 0; 08 for (char c : s) { 09 int t = c - 'a'; 10 if (!ch[p][t]) ch[p][t] = ++tot; 11 p = ch[p][t]; 12 cnt[p]++; 13 } 14} 15int main() { 16 ins("abc"); ins("abc"); ins("abd"); 17 cout << cnt[ch[ch[ch[0]['a' - 'a']]['b' - 'a']]['c' - 'a']]; 18 return 0; 19}
单选题:程序输出是?("abc" 路径末端节点的经过次数)
考点:Trie 统计(N2)。
解析:abc 插入两次 → 'c' 末端 cnt = 2。正确答案 A。
实现要点:同单词多次插入计数累加。手算:路径走两次。
排除法:B 是总插入数;C/D 无依据。
关联 · 节点计数(F5):计数应用。
01#include <bits/stdc++.h> 02using namespace std; 03int fa[9]; 04int find(int x) { 05 return fa[x] == x ? x : fa[x] = find(fa[x]); 06} 07int main() { 08 for (int i = 1; i <= 8; i++) fa[i] = i; 09 // 4 条边连成两个块:{1,2,3,4} 与 {5,6,7,8} 10 int e[4][2] = {{1, 2}, {2, 3}, {3, 4}, {5, 6}}; 11 for (int i = 0; i < 4; i++) fa[find(e[i][0])] = find(e[i][1]); 12 int blocks = 0; 13 for (int i = 1; i <= 8; i++) if (find(i) == i) blocks++; 14 cout << blocks; 15 return 0; 16}
单选题:程序输出是?({1,2,3,4}、{5,6}、{7}、{8} 共 4 块)
考点:连通块计数(N3)。
解析:{1,2,3,4}、{5,6}、{7}、{8} → 4 块。正确答案 A。
实现要点:根自指计数。手算:4 个根。
排除法:B/C/D 无依据。
关联 · 连通块计数(D2):应用版。
01#include <bits/stdc++.h> 02using namespace std; 03int fa[6]; 04int find(int x) { 05 return fa[x] == x ? x : fa[x] = find(fa[x]); 06} 07int main() { 08 for (int i = 1; i <= 5; i++) fa[i] = i; 09 // 朋友关系:1-2、3-4、4-5 10 fa[find(1)] = find(2); 11 fa[find(3)] = find(4); 12 fa[find(4)] = find(5); 13 cout << (find(2) == find(5) ? "CONNECTED" : "NOT-CONNECTED"); 14 return 0; 15}
单选题:程序输出是?
考点:综合应用(N4)。
解析:{1,2} 与 {3,4,5} 不连通 → NOT-CONNECTED。正确答案 A。
实现要点:关系合并后判定。手算:两组根不同。
排除法:B 是连通;C/D 无依据。
关联 · 连通性判定(D1):应用版。
01#include <bits/stdc++.h> 02using namespace std; 03int fa[6]; 04int find(int x) { 05 return fa[x] == x ? x : fa[x] = find(fa[x]); 06} 07int main() { 08 for (int i = 1; i <= 5; i++) ______; // 初始化 09 cout << find(5); 10 return 0; 11}
单选题:横线处应填入?(使输出为 5)
考点:填空(N5)。
解析:fa[i] = i——初始化。正确答案 A。
实现要点:并查集第一步。手算:find(5) = 5。
排除法:B 全 0 错;C/D 无依据。
关联 · 并查集忘初始化(H4):填空版。
01#include <bits/stdc++.h> 02using namespace std; 03int fa[7], rk[7]; 04int find(int x) { 05 return fa[x] == x ? x : fa[x] = find(fa[x]); 06} 07void uni(int x, int y) { 08 int rx = find(x), ry = find(y); 09 if (rx == ry) return; 10 if (rk[rx] < rk[ry]) fa[rx] = ry; 11 else if (rk[rx] > rk[ry]) fa[ry] = rx; 12 else { fa[rx] = ry; rk[ry]++; } 13} 14int main() { 15 for (int i = 1; i <= 6; i++) { fa[i] = i; rk[i] = 0; } 16 uni(1, 2); uni(3, 4); uni(5, 6); // 3 块 17 uni(2, 4); // 合并两块 → 2 块 18 cout << (find(1) == find(3) ? "S" : "D") << " "; 19 int blocks = 0; 20 for (int i = 1; i <= 6; i++) if (find(i) == i) blocks++; 21 cout << blocks; 22 return 0; 23}
单选题:程序输出是?
考点:大综合(N6)。
解析:合并后 {1,2,3,4} 与 {5,6} → 1 与 3 同组、共 2 块 → S 2。正确答案 A。
实现要点:按秩合并 + 判定 + 计数全链路。手算:逐操作。
排除法:B/C/D 无依据。
关联 · 应用综合(D7):大综合版。
01#include <bits/stdc++.h> 02using namespace std; 03int fa[6] = {0, 2, 3, 4, 5, 5}; 04int find(int x) { 05 if (______) return x; // x 是根 06 return fa[x] = find(fa[x]); 07} 08int main() { 09 find(1); 10 cout << fa[1]; 11 return 0; 12}
单选题:横线处应填入?(使输出为 5)
考点:find 填空(O1)。
解析:fa[x] == x——根判定。正确答案 A。
实现要点:递归出口。手算:fa[1] = 5。
排除法:B 反向;C/D 无依据。
关联 · 路径压缩递归版(B3):填空版。
01#include <bits/stdc++.h> 02using namespace std; 03int fa[6]; 04int find(int x) { 05 return fa[x] == x ? x : fa[x] = find(fa[x]); 06} 07int main() { 08 for (int i = 1; i <= 5; i++) fa[i] = i; 09 ______; // union(2, 5) 10 cout << find(2) << " " << find(5); 11 return 0; 12}
单选题:横线处应填入?(使输出为 5 5)
考点:union 填空(O2)。
解析:fa[find(2)] = find(5)。正确答案 A。
实现要点:挂根。手算:输出 5 5。
排除法:B 直接挂;C 语法错;D 方向反。
关联 · union 的写法(C2):填空版。
01#include <bits/stdc++.h> 02using namespace std; 03int fa[6] = {0, 2, 3, 4, 5, 5}; 04int find(int x) { 05 if (fa[x] == x) return x; 06 return fa[x] = ______; // 递归找根并压缩 07} 08int main() { 09 find(1); 10 cout << fa[1] << " " << fa[2]; 11 return 0; 12}
单选题:横线处应填入?(使输出为 5 5)
考点:路径压缩填空(O3)。
解析:find(fa[x])——递归找根。正确答案 A。
实现要点:赋值 + 递归。手算:fa[1]=fa[2]=5。
排除法:B 只压一层;C 错误返回;D 无压缩。
关联 · find 的递归细节(B6):填空版。
01#include <bits/stdc++.h> 02using namespace std; 03int fa[6], rk[6]; 04int find(int x) { 05 return fa[x] == x ? x : fa[x] = find(fa[x]); 06} 07void uni(int x, int y) { 08 int rx = find(x), ry = find(y); 09 if (rx == ry) return; 10 if (rk[rx] < rk[ry]) fa[rx] = ry; 11 else { 12 fa[ry] = rx; 13 if (______) rk[rx]++; // 秩相等时根秩 +1 14 } 15} 16int main() { 17 for (int i = 1; i <= 4; i++) { fa[i] = i; rk[i] = 0; } 18 uni(1, 2); uni(3, 4); uni(1, 3); 19 cout << find(1); 20 return 0; 21}
单选题:横线处应填入?(使输出为 4——两树等高时合并、根的秩 +1)
考点:按秩合并填空(O4)。
解析:rk[rx] == rk[ry]——秩相等时根秩 +1。正确答案 A。
实现要点:按秩三情况。手算:输出 4。
排除法:B 是高挂矮条件;C/D 无依据。
关联 · 按秩合并(C3):填空版。
01#include <bits/stdc++.h> 02using namespace std; 03int ch[100][26]; 04bool isEnd[100]; 05int tot = 0; 06void ins(string s) { 07 int p = 0; 08 for (char c : s) { 09 int t = c - 'a'; 10 if (______) ch[p][t] = ++tot; // 边不存在则新建 11 p = ch[p][t]; 12 } 13 isEnd[p] = true; 14} 15int main() { 16 ins("ab"); ins("ac"); 17 cout << tot; 18 return 0; 19}
单选题:横线处应填入?(使输出为 3——a、b、c 三个节点)
考点:Trie 插入填空(O5)。
解析:!ch[p][t]——边不存在则新建。正确答案 A。
实现要点:0 表示空边。手算:3 节点。
排除法:B 反向;C/D 无依据。
关联 · Trie 插入(F1):填空版。
01#include <bits/stdc++.h> 02using namespace std; 03int ch[100][26]; 04bool isEnd[100]; 05int tot = 0; 06void ins(string s) { 07 int p = 0; 08 for (char c : s) { 09 int t = c - 'a'; 10 if (!ch[p][t]) ch[p][t] = ++tot; 11 p = ch[p][t]; 12 } 13 isEnd[p] = true; 14} 15bool query(string s) { 16 int p = 0; 17 for (char c : s) { 18 int t = c - 'a'; 19 if (!ch[p][t]) return false; 20 p = ch[p][t]; 21 } 22 return ______; // 完整单词需结束标记 23} 24int main() { 25 ins("ab"); 26 cout << (query("ab") ? "Y" : "N") << (query("a") ? "Y" : "N"); 27 return 0; 28}
单选题:横线处应填入?(使输出为 YN)
考点:Trie 查询填空(O6)。
解析:isEnd[p]——完整单词需结束标记。正确答案 A。
实现要点:完整 vs 前缀的分界。手算:YN。
排除法:B 恒真(前缀误判完整);C/D 无依据。
关联 · 查询完整单词(F2):填空版。
01#include <bits/stdc++.h> 02using namespace std; 03int fa[6]; 04int find(int x) { 05 return fa[x] == x ? x : fa[x] = find(fa[x]); 06} 07int main() { 08 for (int i = 1; i <= 5; i++) fa[i] = i; 09 fa[find(1)] = find(2); 10 fa[find(3)] = find(4); 11 cout << (find(1) == find(4) ? "S" : "D") << " "; 12 fa[find(2)] = find(3); // 合并两集合 13 cout << (find(1) == find(4) ? ______ : "D"); 14 return 0; 15}
单选题:横线处应填入?(使输出为 D S)
考点:综合填空(O7)。
解析:"S"——合并后同组。正确答案 A。
实现要点:判定输出。手算:D S。
排除法:B 反向;C/D 无依据。
关联 · 连通性判定(D1):填空版。
01#include <bits/stdc++.h> 02using namespace std; 03int fa[6] = {0, 2, 3, 4, 5, 5}; 04int find(int x) { 05 while (fa[x] != x) x = fa[x]; // 无路径压缩 06 return x; 07} 08int main() { 09 int total = 0; 10 for (int i = 1; i <= 5; i++) { 11 int x = i, steps = 0; 12 while (fa[x] != x) { x = fa[x]; steps++; } 13 total += steps; 14 } 15 cout << total; 16 return 0; 17}
单选题:程序输出是?(链 1→2→3→4→5 上 5 次 find 的总步数 = 4+3+2+1+0)
考点:find 无压缩(P1)。
解析:链上 5 次 find 总步数 = 4+3+2+1+0 = 10。正确答案 A。
实现要点:无压缩的退化代价。手算:逐元素数步。
排除法:B 是单次;C/D 无依据。
关联 · find 忘路径压缩(H1):错误示范。
01#include <bits/stdc++.h> 02using namespace std; 03int fa[6]; // 未初始化! 04int find(int x) { 05 return fa[x] == x ? x : fa[x] = find(fa[x]); 06} 07int main() { 08 // 未初始化 fa[i] = i 直接 find——fa 全是 0,find(1) 会走 fa[1]=0 → find(0) 越界 09 cout << "danger"; 10 return 0; 11}
判断题:并查集未初始化 fa[i] = i 就 find,会沿 0 下标越界递归——初始化是并查集的第一步。
考点:未初始化(P2)。
解析:未初始化 fa 全 0 → find 沿 0 越界递归。✅ 正确
实现要点:初始化是第一步。手算:fa[1]=0 → find(0)。
排除法:无(判断题)。混淆点:越界是静默错误。
关联 · 并查集忘初始化(H4):错误示范。
01#include <bits/stdc++.h> 02using namespace std; 03int fa[6]; 04int find(int x) { 05 return fa[x] == x ? x : fa[x] = find(fa[x]); 06} 07int main() { 08 for (int i = 1; i <= 5; i++) fa[i] = i; 09 fa[find(2)] = find(2); // union(2, 2):自己挂自己 10 cout << find(2) << " "; 11 // 正确写法:if (find(x) == find(y)) return; 提前返回 12 cout << "ok"; 13 return 0; 14}
单选题:程序输出是?(自环 union 后 find(2) 的结果)
考点:union 自环(P3)。
解析:fa[find(2)] = find(2) 自挂无害但应避免——标准写法先判同根。正确答案 A。
实现要点:union 先判 find(x) == find(y)。手算:自挂后 find(2) 仍 2。
排除法:B/C/D 无依据。
关联 · union 综合(C7):边界纪律。
01#include <bits/stdc++.h> 02using namespace std; 03int ch[100][26]; // 0 表示空 04int tot = 0; 05int main() { 06 // 错误示范:把节点 0(根)当作"空"判断——根也是 0,与新节点混淆 07 // 正确:tot 从 0 开始,新建节点 ++tot(从 1 开始编号),0 永远是根 08 cout << "root is 0"; 09 return 0; 10}
判断题:Trie 中根节点固定为 0、新节点从 1 开始编号——若新建节点也从 0 编号会与根冲突。
考点:Trie 下标 0 混淆(P4)。
解析:根固定 0、新节点从 1 编号——混淆会冲突。✅ 正确
实现要点:tot 从 0 起、新建 ++tot。手算:编号约定。
排除法:无(判断题)。混淆点:0 的双重身份(根 vs 空)。
关联 · Trie 下标 0 混淆(H3):错误示范。
01#include <bits/stdc++.h> 02using namespace std; 03int ch[100][26]; 04int cnt[100]; 05int tot = 0; 06void ins(string s) { 07 int p = 0; 08 for (char c : s) { 09 int t = c - 'a'; 10 if (!ch[p][t]) ch[p][t] = ++tot; 11 p = ch[p][t]; 12 // 错误:忘了 cnt[p]++ —— 前缀统计全为 0 13 } 14} 15int main() { 16 ins("ab"); ins("ac"); 17 cout << cnt[ch[0]['a' - 'a']]; 18 return 0; 19}
单选题:程序输出是?(忘计数后前缀统计失效)
考点:忘计数(P5)。
解析:忘 cnt[p]++ → 前缀统计全 0。正确答案 A。
实现要点:计数是前缀统计的基础。手算:cnt 恒 0。
排除法:B 是正确值;C/D 无依据。
关联 · 节点计数(F5):错误示范。
判断题:以下结论全部正确——"并查集初始化 fa[i]=i;find 带路径压缩;union 按秩防退化;Trie 根为 0、新节点从 1 编号;前缀查询无需结束标记"。
考点:综合判断(P6)。
解析:五结论全对——初始化、压缩 find、按秩 union、Trie 编号约定、前缀查询。✅ 正确
实现要点:本章两大结构模板要点收官自查。手算:逐条对照本章代码。
排除法:无(判断题)。混淆点:并查集与 Trie 是数据结构板块的"关系型 + 字符串型"两翼。
关联 · 本章全部核心结论:收官综合判断题。