先描述思路,再列出我自己的算法实现,最后列出geeksforgeeks上的代码,进行比较分析
思路
在做并查集时,每次对节点i和节点j做合并时,分别会在parent数组中找到他们的根节点
- 如果根节点不同,则做合并
- 而如果指向同一个根节点,则意味着,存在环
算法实现
我的版本
int[] unionParent = new int[V];
int find(int node) {
int result = node;
while (unionParent[result] != -1) {
result = unionParent[result];
}
// 压缩路径
int tmp = node;
while (unionParent[tmp] != -1) {
tmp = unionParent[tmp];
unionParent[tmp] = result;
}
return result;
}
boolean isCycle() {
// init unionParent array
for (int i = 0; i < V; i++) {
unionParent[i] = -1;
}
// traversal edge
for (int i = 0; i < V; i++) {
Iterator<Integer> j = adj[i].iterator();
while (j.hasNext()) {
int subSet1 = find(i);
int subSet2 = find(j.next());
if (subSet1 != subSet2) {
unionParent[subSet2] = subSet1;
}else {
// 存在一个环
return true;
}
}
}
//做完并查集
return false;
}
geeksforgeeks版本
// A utility function to find the subset of an element i
int find(int parent[], int i)
{
if (parent[i] == -1)
return i;
return find(parent, parent[i]);
}
// A utility function to do union of two subsets
void Union(int parent[], int x, int y)
{
int xset = find(parent, x);
int yset = find(parent, y);
parent[xset] = yset;
}
// The main function to check whether a given graph
// contains cycle or not
int isCycle( Graph graph)
{
// Allocate memory for creating V subsets
int parent[] = new int[graph.V];
// Initialize all subsets as single element sets
for (int i=0; i<graph.V; ++i)
parent[i]=-1;
// Iterate through all edges of graph, find subset of both
// vertices of every edge, if both subsets are same, then
// there is cycle in graph.
for (int i = 0; i < graph.E; ++i)
{
int x = graph.find(parent, graph.edge[i].src);
int y = graph.find(parent, graph.edge[i].dest);
if (x == y)
return 1;
graph.Union(parent, x, y);
}
return 0;
}
- find 用了递归实现,没有对路径进行压缩
- union中没有对获得的xset,yset判断是否相等,把比较过程放在isCycle中
- find,union函数参数包含了parent数组
- isCycle的参数为Graph对象,在函数体中创建的parent数组
总结
我认为geeksforgeeks版本中,参数设置更为合理,更具抽象性
- 我不该把parent 数组声明在Graph中
- isCycle应该设有Graph对象作为参数,我的版本为每次需要相应Graph对象调用自己的isCycle方法
find函数应该进行垃圾压缩优化