图上的迭代 DFS 具有 post-visit 排序
Iterative DFS on graph with post-visit ordering
我目前正在尝试在有向图上实施 Kosaraju 算法以查找所有强连通分量。
我很清楚这个算法是如何工作的,但是我在获取 DFS 结果的 post-visit 顺序时遇到了一些问题。
目前,我的 DFS 实现的伪代码如下:
[编辑] :我终于得到了一个 DFS 版本,其中 post 访问顺序作为输出。我考虑在每次该节点将邻居推送到 DFS 堆栈时递增一个代表该节点 "lock" 的数字。然后,当一个节点的访问结束时(没有更多的邻居可以推送),我注意到推送它的那个 child 的 DFS 结束了(我减少了它的锁)。如果这个通知也结束了前面的访问,我就到上一个前面的节点去通知,等等...
dfs(G, s):
for all v in vertices :
mark[v] = false
blocked[v] = 0
preceding[v] = -1
mark[s] = true
create a stack P
P.push(s)
while P not empty:
v = P.pop
for all t neighbours of v:
if mark[t] = false:
mark[t] = true
blocked[v]++
preceding[t] = v
P.push(t)
if no neighbours added to the stack:
while(blocked[v] == 0):
result.push(v);
v = (preceding[v] == -1) ? preceding[v] : v
blocked[v]--
但我不确定这种实现是否会为 Kosaraju 算法输出正确的 post 顺序。
我的DFS对不对?
我的 DFS 示例:
(我顺时针列出了顶点,所以a = 0, b = 1, c = 2, d = 3, h = 4, g = 5, f = 6, e = 7)
图作为邻接表:
{0} --> {1}
{1} --> {2, 6, 7}
{2} --> {3, 5}
{3} --> {2, 4}
{4} --> {3, 5}
{5} --> {6}
{6} --> {5}
{7} --> {0, 6}
我的 DFS 的另一个问题是我的算法以降序计算邻居。我认为这并不重要,因为邻居实际上是一组顶点,但在我的例子中我遇到了奇怪的问题。
邻居计算顺序递增的解释:
输入0,开1
我弹出 DFS 堆栈(是 {0}),它 returns 0。他有 1 作为邻居,所以我将 1 压入 DFS 堆栈(现在是 {1} )
进入1,开场2 6 7
我从DFS栈中弹出1,他有2,6,7作为邻居,所以我将它们压入栈中(现在是{7,6,2})
进入2,开场3
我从栈中弹出2(现在是{7, 6}),邻居是3和5,我压入他们,栈是{7, 6, 5, 3}
进入3,开场4
*我从栈中弹出3(现在是{7,6,5}),4是唯一的邻居,我压栈,栈是{7,6,5,4}
正在输入 4
我从堆栈中弹出 4(是 {7, 6, 5}),5 是邻居但已经 "marked" 因为是 2[= 的邻居13=]
输入 5
我从栈中弹出5,现在是{7, 6},所有的邻居(6)都已经“标记”
输入 6
我从栈中弹出6,{7},5已经是"marked"邻居
正在输入 7
我从堆栈中弹出 7,{empty},邻居 (1 & 6) 已经是 "marked"
我的结果数组是 0 1 2 3 4 5 6 7 因为我的 "treated" 节点是这个顺序(或者 7 6 5 4 3 2 1 0 我的 post-visit实施)
随着邻居的计算顺序递减(我的实际 DFS),我得到:
输入0,开1
堆栈 {} -> {1}
进入1,开场7 6 2
堆栈 {} -> {2, 6, 7}
正在输入 7
堆栈 {2, 6} -> {2, 6}
进入6,开场5
堆栈 {2} -> {2, 5}
输入 5
堆栈 {2} -> {2}
进入2,开场3
堆栈 {} -> {3}
进入3,开场4
堆栈 {} -> {4}
正在输入 4
堆栈 {} -> {}
我得到了以下结果 0 1 7 6 5 2 3 4(或 4 3 2 5 6 7 1 0 对于 post 访问)
问题是,当我反转图形计算 Kosaraju 算法的最后部分时,两个结果不等价:
第一个结果(最终获得正确数量的 SCC)(堆栈 {7, 6, 5, 4, 3, 2, 1, 0}):
Opening 0,dfs returns 1 and 7 -> 0, 1, 7在同一个SCC中,从工作栈中删除它们(现在是{6, 5, 4, 3, 2} ) 和图表。
Opening 2, dfs returns 3 and 4 -> 2, 3, 4 在同一个SCC中,从working stack(现在是{6, 5})和graph中删除它们。
Opening 5, dfs returns 6 -> 5, 6在同一个SCC
2n 结果,堆栈 {4, 3, 2, 5, 6, 7, 1, 0}:
Opening 0,dfs returns 1 and 7 -> 0, 1, 7在同一个SCC中,从工作栈中删除它们(现在是{4, 3, 2, 5, 6} ) 和图表。
打开 6,dfs returns 5、4、3 和 2 -> 2、3、4、5、6 在同一个 SCC (但它们不在)(因为在反转图中,从 6 开始到 5 然后到 4 或 2...)
可以一个有人向我解释为什么在 DFS 中按升序或降序计算顶点与 Kosaraju 算法的结果不同吗?
编辑:我提供了一些关于如何获得结果以及如何手动 运行 算法的额外信息。希望现在更清楚了。
这是我的 DFS 获取 post 访问顺序的 C 实现,希望这对某人有所帮助:
typedef struct _graph_list {
int nb_nodes;
int* tab_nodes;
int* succ_list;
} graph_list;
typedef struct _stack {
unsigned int max_capacity;
int* array;
int top_position;
} stack;
int* getSuccFromList(graph_list* graph, int i){
if (graph->tab_nodes[i] == -1) return NULL;
int case_succ = graph->tab_nodes[i];
int next_case_succ = graph->tab_nodes[++i];
while (next_case_succ == -1){
next_case_succ = graph->tab_nodes[++i];
}
int nb_succ = next_case_succ - case_succ;
int j;
int* res = malloc(sizeof(int)*(nb_succ + 1));
for(j = 0; j < nb_succ; j++){
res[j] = graph->succ_list[case_succ + j];
}
res[nb_succ] = -1;
return res;
}
int* dfsPostVisit(graph_list* graph, int vertex){
int i, j, nb_nodes, tmp_v;
int* succ;
int eof_visit;
nb_nodes = graph->nb_nodes;
int* res = malloc(sizeof(int)*(nb_nodes + 1));
int* mark = malloc(sizeof(int)*nb_nodes);
int* blocked = malloc(sizeof(int)*nb_nodes);
int* prec = malloc(sizeof(int)*nb_nodes);
for(i = 0; i < nb_nodes; i++){
res[i] = -1;
mark[i] = 0;
blocked[i] = 0;
prec[i] = -1;
}
res[nb_nodes] = -1;
stack* s = createStack(nb_nodes);
push(s, vertex);
mark[vertex] = 1;
i = 0;
while(!isEmpty(s)){
vertex = pop(s);
succ = getSuccFromList(graph, vertex);
j = 0;
eof_visit = 0;
if(succ!=NULL){
while(succ[j] != -1){
tmp_v = succ[j++];
if(!mark[tmp_v]){
mark[tmp_v] = 1;
blocked[vertex]++;
prec[tmp_v] = vertex;
push(s, tmp_v);
eof_visit++;
}
}
free(succ);
}
if (eof_visit == 0){
while (blocked[vertex] == 0){
res[i++] = vertex;
vertex = (prec[vertex] >= 0)? prec[vertex] : vertex;
blocked[vertex]--;
}
}
}
i = 0;
freeStack(s);
free(mark);
free(blocked);
free(prec);
return res;
}
有些东西可能需要增强,例如使用位数组作为布尔数组而不是 int 数组,但这是我的实际代码,它似乎可以正常工作。
感谢那些试图帮助我的人。
我目前正在尝试在有向图上实施 Kosaraju 算法以查找所有强连通分量。
我很清楚这个算法是如何工作的,但是我在获取 DFS 结果的 post-visit 顺序时遇到了一些问题。
目前,我的 DFS 实现的伪代码如下:
[编辑] :我终于得到了一个 DFS 版本,其中 post 访问顺序作为输出。我考虑在每次该节点将邻居推送到 DFS 堆栈时递增一个代表该节点 "lock" 的数字。然后,当一个节点的访问结束时(没有更多的邻居可以推送),我注意到推送它的那个 child 的 DFS 结束了(我减少了它的锁)。如果这个通知也结束了前面的访问,我就到上一个前面的节点去通知,等等...
dfs(G, s):
for all v in vertices :
mark[v] = false
blocked[v] = 0
preceding[v] = -1
mark[s] = true
create a stack P
P.push(s)
while P not empty:
v = P.pop
for all t neighbours of v:
if mark[t] = false:
mark[t] = true
blocked[v]++
preceding[t] = v
P.push(t)
if no neighbours added to the stack:
while(blocked[v] == 0):
result.push(v);
v = (preceding[v] == -1) ? preceding[v] : v
blocked[v]--
但我不确定这种实现是否会为 Kosaraju 算法输出正确的 post 顺序。
我的DFS对不对?
我的 DFS 示例:
(我顺时针列出了顶点,所以a = 0, b = 1, c = 2, d = 3, h = 4, g = 5, f = 6, e = 7)
图作为邻接表:
{0} --> {1}
{1} --> {2, 6, 7}
{2} --> {3, 5}
{3} --> {2, 4}
{4} --> {3, 5}
{5} --> {6}
{6} --> {5}
{7} --> {0, 6}
我的 DFS 的另一个问题是我的算法以降序计算邻居。我认为这并不重要,因为邻居实际上是一组顶点,但在我的例子中我遇到了奇怪的问题。
邻居计算顺序递增的解释:
输入0,开1
我弹出 DFS 堆栈(是 {0}),它 returns 0。他有 1 作为邻居,所以我将 1 压入 DFS 堆栈(现在是 {1} )
进入1,开场2 6 7
我从DFS栈中弹出1,他有2,6,7作为邻居,所以我将它们压入栈中(现在是{7,6,2})
进入2,开场3
我从栈中弹出2(现在是{7, 6}),邻居是3和5,我压入他们,栈是{7, 6, 5, 3}
进入3,开场4
*我从栈中弹出3(现在是{7,6,5}),4是唯一的邻居,我压栈,栈是{7,6,5,4}
正在输入 4
我从堆栈中弹出 4(是 {7, 6, 5}),5 是邻居但已经 "marked" 因为是 2[= 的邻居13=]
输入 5
我从栈中弹出5,现在是{7, 6},所有的邻居(6)都已经“标记”
输入 6
我从栈中弹出6,{7},5已经是"marked"邻居
正在输入 7
我从堆栈中弹出 7,{empty},邻居 (1 & 6) 已经是 "marked"
我的结果数组是 0 1 2 3 4 5 6 7 因为我的 "treated" 节点是这个顺序(或者 7 6 5 4 3 2 1 0 我的 post-visit实施)
随着邻居的计算顺序递减(我的实际 DFS),我得到:
输入0,开1
堆栈 {} -> {1}
进入1,开场7 6 2
堆栈 {} -> {2, 6, 7}
正在输入 7
堆栈 {2, 6} -> {2, 6}
进入6,开场5
堆栈 {2} -> {2, 5}
输入 5
堆栈 {2} -> {2}
进入2,开场3
堆栈 {} -> {3}
进入3,开场4
堆栈 {} -> {4}
正在输入 4
堆栈 {} -> {}
我得到了以下结果 0 1 7 6 5 2 3 4(或 4 3 2 5 6 7 1 0 对于 post 访问)
问题是,当我反转图形计算 Kosaraju 算法的最后部分时,两个结果不等价:
第一个结果(最终获得正确数量的 SCC)(堆栈 {7, 6, 5, 4, 3, 2, 1, 0}):
Opening 0,dfs returns 1 and 7 -> 0, 1, 7在同一个SCC中,从工作栈中删除它们(现在是{6, 5, 4, 3, 2} ) 和图表。
Opening 2, dfs returns 3 and 4 -> 2, 3, 4 在同一个SCC中,从working stack(现在是{6, 5})和graph中删除它们。
Opening 5, dfs returns 6 -> 5, 6在同一个SCC
2n 结果,堆栈 {4, 3, 2, 5, 6, 7, 1, 0}:
Opening 0,dfs returns 1 and 7 -> 0, 1, 7在同一个SCC中,从工作栈中删除它们(现在是{4, 3, 2, 5, 6} ) 和图表。
打开 6,dfs returns 5、4、3 和 2 -> 2、3、4、5、6 在同一个 SCC (但它们不在)(因为在反转图中,从 6 开始到 5 然后到 4 或 2...)
可以一个有人向我解释为什么在 DFS 中按升序或降序计算顶点与 Kosaraju 算法的结果不同吗?
编辑:我提供了一些关于如何获得结果以及如何手动 运行 算法的额外信息。希望现在更清楚了。
这是我的 DFS 获取 post 访问顺序的 C 实现,希望这对某人有所帮助:
typedef struct _graph_list {
int nb_nodes;
int* tab_nodes;
int* succ_list;
} graph_list;
typedef struct _stack {
unsigned int max_capacity;
int* array;
int top_position;
} stack;
int* getSuccFromList(graph_list* graph, int i){
if (graph->tab_nodes[i] == -1) return NULL;
int case_succ = graph->tab_nodes[i];
int next_case_succ = graph->tab_nodes[++i];
while (next_case_succ == -1){
next_case_succ = graph->tab_nodes[++i];
}
int nb_succ = next_case_succ - case_succ;
int j;
int* res = malloc(sizeof(int)*(nb_succ + 1));
for(j = 0; j < nb_succ; j++){
res[j] = graph->succ_list[case_succ + j];
}
res[nb_succ] = -1;
return res;
}
int* dfsPostVisit(graph_list* graph, int vertex){
int i, j, nb_nodes, tmp_v;
int* succ;
int eof_visit;
nb_nodes = graph->nb_nodes;
int* res = malloc(sizeof(int)*(nb_nodes + 1));
int* mark = malloc(sizeof(int)*nb_nodes);
int* blocked = malloc(sizeof(int)*nb_nodes);
int* prec = malloc(sizeof(int)*nb_nodes);
for(i = 0; i < nb_nodes; i++){
res[i] = -1;
mark[i] = 0;
blocked[i] = 0;
prec[i] = -1;
}
res[nb_nodes] = -1;
stack* s = createStack(nb_nodes);
push(s, vertex);
mark[vertex] = 1;
i = 0;
while(!isEmpty(s)){
vertex = pop(s);
succ = getSuccFromList(graph, vertex);
j = 0;
eof_visit = 0;
if(succ!=NULL){
while(succ[j] != -1){
tmp_v = succ[j++];
if(!mark[tmp_v]){
mark[tmp_v] = 1;
blocked[vertex]++;
prec[tmp_v] = vertex;
push(s, tmp_v);
eof_visit++;
}
}
free(succ);
}
if (eof_visit == 0){
while (blocked[vertex] == 0){
res[i++] = vertex;
vertex = (prec[vertex] >= 0)? prec[vertex] : vertex;
blocked[vertex]--;
}
}
}
i = 0;
freeStack(s);
free(mark);
free(blocked);
free(prec);
return res;
}
有些东西可能需要增强,例如使用位数组作为布尔数组而不是 int 数组,但这是我的实际代码,它似乎可以正常工作。
感谢那些试图帮助我的人。