题目
给定一个由表示变量之间关系的字符串方程组成的数组,每个字符串方程 equations[i]
的长度为 4,并采用两种不同的形式之一:a==b
或 a!=b
。在这里,a 和 b 是小写字母(不一定不同),表示单字母变量名。
只有当可以将整数分配给变量名,以便满足所有给定的方程时才返回 true
,否则返回 false
。
代码
private class UnionFind { private int[] parent; public UnionFind(int n) { parent = new int[n]; for (int i = 0; i < n; i++) { parent[i] = i; } } // 在这里用的是隔代路径压缩,较彻底路径压缩(递归实现)而言,虽压缩不完全但性能较高 public int find(int x) { while (x != parent[x]) { parent[x] = parent[parent[x]]; x = parent[x]; } return x; } public void union(int x, int y) { int rootX = find(x); int rootY = find(y); parent[rootX] = rootY; } public boolean isConnected(int x, int y) { return find(x) == find(y); } } public boolean equationsPossible(String[] equations) { UnionFind unionFind = new UnionFind(26); for (String equation : equations) { char[] charArr = equation.toCharArray(); if (charArr[1] == '=') { int index1 = charArr[0] - 'a'; int index2 = charArr[3] - 'a'; unionFind.union(index1, index2); } } for (String equation : equations) { char[] charArr = equation.toCharArray(); if (charArr[1] == '!') { int index1 = charArr[0] - 'a'; int index2 = charArr[3] - 'a'; if (unionFind.isConnected(index1, index2)) { return false; } } } return true; }
题外话
今天做到这个题学到了一个新知识点——并查集,就做个记录总结下。
CSDN博主的一篇文章将并查集讲述的生动形象,可以前往阅览。
相关概念
- 并查集 是一种树型的数据结构,用来处理一些不交集的 合并 及 查询 问题。
- 并查集森林 是一种将每一个集合以树表示的数据结构,其中每一个节点保存着到它父节点的引用。
代码实现
其中主要就是 find
和 union
两个方法。
find
方法用来寻找两个节点的根节点,如果两个节点的根节点一致,两个节点就是连通的。在寻找时可以进行路径压缩来优化查找效率。举个例子,假设原来的树内节点构成了一条线,则查找效率为 O(n)
,而如果在每次查找后将当前节点连接到父节点的父结点上(隔代路径压缩),或者直接将查找节点连接到根节点(彻底路径压缩)。那么经过多次的查找,当前树的深度就会变得很低,可以方便的找到当前节点所在树的根节点。
隔代路径压缩
public int find(int x) { while (x != parent[x]) { parent[x] = parent[parent[x]]; x = parent[x]; } return x; }
彻底路径压缩
public int find(int x) { if (parent[x]!=x) { parent[x] = find(parent[x]); } return parent[x]; }
union
方法是用来连结两个原来不相关的树,实现也较为简单,找到两个节点所在树的根节点,将其中一个根节点指向另外一个根节点即可。
并查集的具体代码如下:
private class UnionFind { private int[] parent; public UnionFind(int n) { parent = new int[n]; for (int i = 0; i < n; i++) { parent[i] = i; } } // 在这里用的是隔代路径压缩,较彻底路径压缩(递归实现)而言,虽压缩不完全但性能较高 public int find(int x) { while (x != parent[x]) { parent[x] = parent[parent[x]]; x = parent[x]; } return x; } public void union(int x, int y) { int rootX = find(x); int rootY = find(y); parent[rootX] = rootY; } public boolean isConnected(int x, int y) { return find(x) == find(y); } }