См. Мой ответ на предыдущий вопрос о сильно связанных компонентах.
Ваши dfs также очень неэффективны, как написано, потому что вы начинаете повторное сканирование при i = 0; ваш стек должен помнить, где вы остановились и продолжать оттуда. Рекурсия более естественна, но если у вас ограниченный размер стека вызовов, лучше использовать явный стек (только для огромных деревьев).
Вот рекурсивные DFS. Если вы не заинтересованы в сохранении дерева dfs, вы можете просто сохранить 1 в предшественнике [] вместо узла, с которого вы его достигли):
const unsigned MAXNODES=100;
/* predecessor must be 0-initialized by the caller; nodes graph[n] that are
reached from start will have predecessor[n]=p+1, where graph[pred] is the
predecessor via which n was reached from graph[start].
predecessor[start]=MAXNODES+1 (this is the root of the tree; there is NO
predecessor, but instead of 0, I put a positive value to show that it's
reached).
graph[a][b] is true iff there is a directed arc from a->b
*/
void dfs(bool graph[MAXNODES][MAXNODES],unsigned predecessor[]
,unsigned start,unsigned pred=MAXNODES)
{
if (predecessor[start]) return;
predecessor[start]=pred+1;
for (unsigned i=0;i<MAXNODES;++i)
if (graph[start][i])
dfs(graph,predecessor,i,start);
}
Вот нерекурсивный dfs, созданный по шаблону выше, но использующий одну и ту же переменную стека для pred
и i
(в общем случае у вас будет переменная стека для каждой локальной переменной и параметра, которые могут измениться в вашей рекурсии ):
void dfs_iter(bool graph[MAXNODES][MAXNODES],unsigned predecessor[]
,unsigned start)
{
unsigned stack[MAXNODES]; // node indices
unsigned n,pred;
int top=0;
stack[top]=start;
for(;;) {
recurse:
// invariant: stack[top] is the next (maybe reached) node
n=stack[top];
if (!predecessor[n]) { // not started yet
pred=top>0?stack[top-1]:MAXNODES;
//show(n,pred);
predecessor[n]=pred+1;
// the first thing we can reach from n
for (unsigned i=0;i<MAXNODES;++i)
if (graph[n][i] && !predecessor[i]) {
stack[++top]=i; goto recurse; // push
}
}
if (top>0) {
pred=stack[top-1];
// the next thing we can reach from pred after n
for (unsigned i=n+1;i<MAXNODES;++i)
if (graph[pred][i]) {
stack[top]=i; goto recurse; // replace top
}
--top;
} else
return;
}
}
Это может быть структурировано без перехода (это просто именованное продолжение в самый дальний цикл), но без какой-либо дополнительной ясности, на мой взгляд.
В любом случае, рекурсивные вызовы намного проще. Существует рекурсивный псевдокод для Алгоритма сильно связанных компонентов Тарьяна , который вы можете транскрибировать довольно напрямую. Если вам нужна помощь, чтобы сделать его нерекурсивным (явный стек), спросите.