NRF51822串口通信实战:从硬件连接到中断驱动框架设计
2026/6/7 16:14:16
本次围绕并查集的核心概念、实现方法、习题应用展开讨论,明确了并查集的实际使用场景与解题思路,以下是详细总结内容。
并查集的底层通常采用一维数组实现,利用数组下标与值的映射关系存储集合信息,具体规则如下:
-1(数组下标对应元素编号,如parent[0]对应元素 0)。parent[2] = -5表示元素 2 是根节点,对应集合有 5 个元素)。parent[3] = 2表示元素 3 的父节点是元素 2)。并查集的核心是查找(Find)和合并(Union),基于这两个操作可延伸出 “判断是否同集”“统计集合个数” 等功能:
会议讲解了两道并查集经典习题,核心是将实际问题转化为集合的合并与查询:
i行j列=1表示第 i 个城市和第 j 个城市直接相连),求省份的数量(省份是由直接 / 间接相连的城市组成的集合)。matrix[i][j]=1,则将城市 i 和城市 j 合并;遍历完成后,统计并查集数组中负数的个数,即为省份数量。a==b或a!=b),判断所有方程是否能同时成立。==方程,将等号连接的变量合并到同一集合;第二步遍历所有!=方程,检查不等号连接的变量是否在同一集合(若在则方程不成立,返回false;否则返回true)。parent[5] = 3表示元素 5 的上级是元素 3,parent[3] = -4表示元素 3 是族长,家族有 4 个人。-8(5+3),表示合并后的家族有 8 人。以下代码采用 Java 实现,包含并查集基础类、优化类(路径压缩 + 按秩合并),以及两道经典习题的解题代码。
java
运行
/** * 并查集基础实现类(线性查找,未优化合并) */ public class UnionFind { private int[] parent; /** * 构造方法:初始化并查集 * @param n 元素个数 */ public UnionFind(int n) { // 初始化并查集数组,初始值全为-1 parent = new int[n]; for (int i = 0; i < n; i++) { parent[i] = -1; } } /** * 查找元素x的根节点(基础版:线性查找) * @param x 目标元素 * @return 根节点下标 * @throws IllegalArgumentException 输入下标非法时抛出 */ public int find(int x) { if (x < 0 || x >= parent.length) { throw new IllegalArgumentException("元素下标超出范围"); } // 循环查找父节点,直到找到根节点(值为负数) while (parent[x] >= 0) { x = parent[x]; } return x; } /** * 判断元素x和y是否在同一集合 * @param x 元素x * @param y 元素y * @return true:同集;false:不同集 */ public boolean isSameSet(int x, int y) { return find(x) == find(y); } /** * 合并元素x和y所在的集合 * @param x 元素x * @param y 元素y */ public void union(int x, int y) { int rootX = find(x); int rootY = find(y); if (rootX == rootY) { // 已在同一集合,无需合并 return; } // 合并规则:将rootY的父节点设为rootX,更新rootX的集合大小 parent[rootX] += parent[rootY]; parent[rootY] = rootX; } /** * 统计集合的个数 * @return 集合数量 */ public int getSetCount() { int count = 0; for (int val : parent) { if (val < 0) { count++; } } return count; } }java
运行
/** * 并查集优化实现类(路径压缩 + 按秩合并) */ public class UnionFindOptimized { private int[] parent; private int[] rank; // 存储集合的高度(秩) /** * 构造方法:初始化并查集 * @param n 元素个数 */ public UnionFindOptimized(int n) { // parent数组:初始值为自身(根节点指向自己) parent = new int[n]; // rank数组:初始值为1(每个集合的高度初始为1) rank = new int[n]; for (int i = 0; i < n; i++) { parent[i] = i; rank[i] = 1; } } /** * 查找元素x的根节点(路径压缩:迭代版,避免递归栈溢出) * @param x 目标元素 * @return 根节点下标 * @throws IllegalArgumentException 输入下标非法时抛出 */ public int find(int x) { if (x < 0 || x >= parent.length) { throw new IllegalArgumentException("元素下标超出范围"); } // 路径压缩:将x的父节点直接设为根节点 if (parent[x] != x) { parent[x] = find(parent[x]); // 递归版路径压缩(简洁,小数据量推荐) // 迭代版路径压缩(大数据量推荐,避免栈溢出) /* int root = x; // 先找到根节点 while (parent[root] != root) { root = parent[root]; } // 路径压缩:将沿途节点的父节点都设为根节点 while (parent[x] != root) { int temp = parent[x]; parent[x] = root; x = temp; } return root; */ } return parent[x]; } /** * 判断元素x和y是否在同一集合 * @param x 元素x * @param y 元素y * @return true:同集;false:不同集 */ public boolean isSameSet(int x, int y) { return find(x) == find(y); } /** * 合并元素x和y所在的集合(按秩合并:小秩合并到大秩下) * @param x 元素x * @param y 元素y */ public void union(int x, int y) { int rootX = find(x); int rootY = find(y); if (rootX == rootY) { return; } // 按秩合并:高度小的集合合并到高度大的集合下,保持树的高度最小 if (rank[rootX] > rank[rootY]) { parent[rootY] = rootX; } else if (rank[rootX] < rank[rootY]) { parent[rootX] = rootY; } else { // 高度相同,合并后高度+1 parent[rootY] = rootX; rank[rootX]++; } } /** * 统计集合的个数 * @return 集合数量 */ public int getSetCount() { int count = 0; for (int i = 0; i < parent.length; i++) { // 根节点的特征是parent[i] == i if (parent[i] == i) { count++; } } return count; } }java
运行
/** * 省份数量问题求解 */ public class ProvinceCount { /** * 求解省份数量 * @param isConnected 城市的相连关系矩阵 * @return 省份数量 */ public static int findCircleNum(int[][] isConnected) { int n = isConnected.length; // 初始化并查集(这里用优化版,也可用基础版) UnionFindOptimized uf = new UnionFindOptimized(n); // 遍历矩阵(仅遍历上三角,避免重复合并) for (int i = 0; i < n; i++) { for (int j = i + 1; j < n; j++) { if (isConnected[i][j] == 1) { uf.union(i, j); } } } // 返回集合个数,即省份数量 return uf.getSetCount(); } // 测试用例 public static void main(String[] args) { int[][] testMatrix = {{1, 1, 0}, {1, 1, 0}, {0, 0, 1}}; System.out.println("省份数量:" + findCircleNum(testMatrix)); // 输出:2 } }java
运行
/** * 等式方程的可满足性问题求解 */ public class EquationPossible { /** * 判断等式方程是否可满足 * @param equations 等式/不等式数组 * @return true:可满足;false:不可满足 */ public static boolean equationsPossible(String[] equations) { // 变量是小写字母,共26个,映射为0-25的下标 UnionFindOptimized uf = new UnionFindOptimized(26); // 第一步:处理所有==方程,合并变量 for (String eq : equations) { if (eq.charAt(1) == '=') { // 字符转下标:'a'->0,'b'->1,...,'z'->25 int x = eq.charAt(0) - 'a'; int y = eq.charAt(3) - 'a'; uf.union(x, y); } } // 第二步:处理所有!=方程,检查是否同集 for (String eq : equations) { if (eq.charAt(1) == '!') { int x = eq.charAt(0) - 'a'; int y = eq.charAt(3) - 'a'; if (uf.isSameSet(x, y)) { return false; } } } // 所有检查通过,返回True return true; } // 测试用例 public static void main(String[] args) { String[] testEquations1 = {"a==b", "b!=a"}; System.out.println(equationsPossible(testEquations1)); // 输出:false String[] testEquations2 = {"a==b", "b==c", "a==c"}; System.out.println(equationsPossible(testEquations2)); // 输出:true } }