使用 Kruskal 算法检测图中的周期
Detect cycle in a graph using Kruskal's algorithm
我正在实现克鲁斯卡尔的算法,这是找到加权图的最小跨越树的众所周知的方法。但是,我正在调整它以在图中找到周期。这是Kruskal算法的伪代码:
KRUSKAL(G):
1 A = ∅
2 foreach v ∈ G.V:
3 MAKE-SET(v)
4 foreach (u, v) ordered by weight(u, v), increasing:
5 if FIND-SET(u) ≠ FIND-SET(v):
6 A = A ∪ {(u, v)}
7 UNION(u, v)
8 return A
我很难掌握FIND-SET()
和MAKE-SET()
函数,或者它们使用不连接集合数据结构的实现。
我当前的代码看起来像这样:
class edge {
public: //for quick access (temp)
char leftV;
char rightV;
int weight;
};
std::vector<edge> kruskalMST(std::vector<edge> edgeList){
std::vector<char> set;
std::vector<edge> result;
sortList(edgeList); //sorts according to weight ( passed by reference)
do{
if(set.empty()){
set.push_pack(edgeList[i].leftV); //also only push them when
set.push_pack(edgeList[i].rightV); //they aren't there , will fix
result.push_back(edgeList[i]);
++i;
}
else {
if((setContains(set , edgeList[i].leftV)) && (setContains(set , edgeList[i].rightV)))
++i; //skip node
else {
set.push_pack(edgeList[i].leftV); //also only push them when
set.push_pack(edgeList[i].rightV); //they aren't there , will fix
result.push_back(edgeList[i]);
++i;
}
} while(i<edgeList.size());
return result;
}
当set vector
中已经存在的两个顶点再次出现时,我的代码在图中检测到一个周期。在大多数情况下,直到我遇到这样的情况之前,这似乎都起作用:
[a] [c]
| |
| |
| |
[b] [d]
当这些边缘以排序顺序出现时,这会发生,因为a
,b
,c
,d
已经将其推入set vector
。将[a]
加入[c]
不会在图内产生周期,但由于当前实现而被检测为周期。
在我的情况下,是否有可行的替代方法可以检测周期?或者,如果某人可以解释MAKE-SET
,FIND-SET
和UNION
在Kruskal的算法中工作,这将很有帮助。
MAKE-SET(v)
表示您是您初始化一个初始化一个设置仅由顶点v
组成。最初,每个顶点都在一个集合中。
FIND-SET(u)
是一个函数,告诉您设置哪个顶点属于。它必须返回指针或唯一标识该集合的ID号。
UNION(u, v)
表示您将包含u
的集合与包含v
的集合合并。换句话说,如果u
和v
在不同的集合中,则UNION
操作将形成一个包含集合FIND-SET(u)
和FIND-SET(v)
的所有成员的新集合。
当我们使用不相交的数据结构实施这些操作时,关键想法是每个集合都由领导者表示。每个顶点都有一个指向其集合中一些顶点的指针。集合的领导者是指向自身的顶点。所有其他顶点都指向父母,指针形成了树结构,其领导者是其根。
要实现FIND-SET(u)
,您从u
开始关注指针,直到您到达设定的领导者,这是集合指向自身的唯一顶点。
要实现UNION(u, v)
,您将一个固定点的领导者与另一组领导者成为领导者。
可以通过按等级和路径压缩的联合思想进行优化这些操作。
按等级的联合意味着您可以跟踪一组到领导者的任何顶点的最大指针数量。这与指针形成的树的高度相同。您可以通过为每个UNION
操作执行恒定数量的步骤来更新排名,这是集合排名更改的唯一一次。假设我们正在合并集合A和B,以使A的排名大于B。我们将B的领导者指向A的领导者。结果组的等级与A相同。,我们使BOIND与B的领导者成为领导者,并且由此产生的集合的等级与B相同。如果A和B的排名相同,我们选择哪个领导者都没关系。无论我们是使B的领导者成为领导者还是反之亦然,结果组的排名将大于A。
路径压缩意味着,当我们执行FIND
操作时,该操作需要按照一系列指针到集合的领导者时,我们将使我们沿着沿途遇到的所有顶点直接指向领导者。这将当前FIND
操作的工作量增加了一个恒定因素,并且减少了FIND
的未来调用的工作量。
如果您按等级和路径压缩实施联盟,则将实现巨大的联合信息实现。 n 操作的时间复杂性是 o(n&alpha;(n)),其中&alpha; 是逆ackermann函数。此函数变得如此慢,以至于 n 是宇宙中原子的数量,&alpha;(n)是5脱节集数据结构实际上是Union-Find的线性时间实现。
我不会重复联合/查找算法的设置理论描述(Kruskal只是一种特殊情况),但使用更简单的方法(您可以在其上应用该方法按等级和路径压缩的联合。)
为简单起见,我认为每个顶点都有一个唯一的整数ID,范围从0到顺序-1(例如,顶点ID可以用作数组的索引。)
天真的算法是如此简单,以至于代码本身说话:
int find(int v, int cc[]) {
while (cc[v] >= 0)
v = cc[v];
return v;
}
bool edge_union(int v0, int v1, int cc[]) {
int r0 = find(v0, cc);
int r1 = find(v1, cc);
if (r0 != r1) {
cc[r1] = r0;
return true;
}
return false;
}
CC阵列以-1的到来初始化(当然它的大小反映了图顺序。)
路径压缩可以通过在查找函数的同时循环中遇到的顶点进行堆叠来完成,然后将相同的代表设置为所有这些代表。
int find2(int v, int cc[]) {
std::deque<int> stack;
while (cc[v] >= 0) {
stack.push_back(v);
v = cc[v];
}
for (auto i : stack) {
cc[i] = v;
}
return v;
}
对于联合等级,我们只是使用数组的负值,值越小,等级就越大。这是代码:
bool edge_union2(int v0, int v1, int cc[]) {
int r0 = find(v0, cc);
int r1 = find(v1, cc);
if (r0 == r1)
return false;
if (cc[r0] < cc[r1])
cc[r1] = r0;
else {
if (cc[r1] < cc[r0])
cc[r0] = r1;
else {
cc[r1] = r0;
cc[r0] -= 1;
}
}
return true;
}
- 使用CMake检测支持的C++标准
- 当套接字连接断开时检测C/C++Unix
- C/C++预处理器是否可以检测一些编译器选项
- WMI检测进程创建事件-c++
- 基于树莓pi的tensorflow lite量化ssd目标检测
- 下面是我为检测链接列表中的循环而制作的代码
- 落砂模拟碰撞检测C++和SFML
- 我可以检测和更改 gcc/g++ 中结构的当前数据对齐设置吗?
- 使用贝尔曼福特检测产品超过阈值的周期
- 具有周期检测的呼叫研磨性能分析
- 使用非递归 DFS 检测有向图中的周期
- 使用查找和联合检测图形中的周期
- 图形:如何使用 DFS 检测无向图中的周期
- 使用 Kruskal 算法检测图中的周期
- 如何以尽可能低的时间复杂度检测网格中的周期
- 如何检测C++随机引擎的整个周期已被消耗
- 无法使用 cin.get 来检测周期来中断循环
- 检测周期并使求和速度更快
- 是否有调试机制、解决方法、包装器或工具来检测shared_ptr周期
- 无向图的检测和打印周期