众所周知,在有些省份(比如山东、河南),省选时使用 Windows 垃圾系统评测,而 Windows 下默认的系统栈非常小(只有 1M),这造成了有些 DFS 相关算法无法通过极端数据,而是发生『栈溢出』的错误。一种解决方法是使用非递归的 DFS。
框架
我们通常这样实现递归 DFS:
void dfs(Iterator x) {
x->visited = true;
x->initialization();
for (Iterator i = x->subIterators.begin(); i != x->subIterators.end(); i++) {
if (!i->visited) {
x->pushDown(i);
dfs(i);
x->pushUpBack();
} else x->pushUp(i);
}
x->exit();
}
第一次访问每个元素时,标记它为访问过,并对其进行初始化操作;枚举所有子元素,分为『未访问过』和『已访问过』分别进行处理。
为了将这个过程转化为非递归,我们使用一个栈来存储 DFS 搜索树上的一条链。
void dfs(Iterator start) {
std::stack<Iterator> s;
s.push(start);
start->pushed = true;
while (!s.empty()) {
Iterator x = s.top();
if (!x->visited) {
x->visited = true;
x->initialization();
x->currentSubIterator = x->subIterators.begin();
}
if (x->currentSubIterator != x->subIterators.end()) {
Iterator &i = x->currentSubIterator;
if (!i->pushed) {
x->pushDown(i);
i->predecessor = x;
i->pushed = true;
s.push(i);
} else {
x->pushUp(i);
}
i++;
} else {
x->exit();
x->predecessor->pushUpBack(i);
s.pop();
}
}
}
Tarjan 强连通分量模板
struct Node {
Edge *firstEdge, *currentEdge, *inEdge;
Connected *connected;
int dfn, low;
bool inStack, visited, pushed;
} nodes[MAXN];
struct Edge {
Node *from, *to;
Edge *next;
Edge(Node *from, Node *to) : from(from), to(to), next(from->firstEdge) {}
};
struct Connected {
int size;
} connecteds[MAXN];
int n;
inline int tarjan() {
int timeStamp = 0, count = 0;
for (int i = 0; i < n; i++) {
if (nodes[i].visited) continue;
std::stack<Node *> s, t;
s.push(&nodes[i]);
nodes[i].pushed = true;
while (!s.empty()) {
Node *v = s.top();
if (!v->visited) {
v->visited = true;
v->currentEdge = v->firstEdge;
v->dfn = v->low = timeStamp++;
v->inStack = true;
t.push(v);
}
if (v->currentEdge) {
Edge *&e = v->currentEdge;
if (!e->to->pushed) s.push(e->to), e->to->pushed = true, e->to->inEdge = e;
else if (e->to->inStack) v->low = std::min(v->low, e->to->dfn);
e = e->next;
} else {
if (v->dfn == v->low) {
v->connected = &connecteds[count++];
Node *u;
do {
u = t.top();
t.pop();
u->inStack = false;
u->connected = v->connected;
u->connected->size++;
} while (u != v);
}
if (v->inEdge) v->inEdge->from->low = std::min(v->inEdge->from->low, v->low);
s.pop();
}
}
}
return count;
}
树链剖分模板
树链剖分的 DFS 过程比较特殊,我们可以每次将一个节点的所有子节点压入栈中,所有子树全部遍历完后回溯回来上传信息。
struct Node {
Edge *e;
Node *c, *p;
int size, pos, posEnd;
bool visited;
Path *path;
} N[MAXN];
struct Edge {
Node *s, *t;
Edge *next;
Edge(Node *s, Node *t) : s(s), t(t), next(s->e) {}
};
struct Path {
Node *top;
Path(Node *top) : top(top) {}
};
inline void cut() {
std::stack<Node *> s;
s.push(&N[0]);
while (!s.empty()) {
Node *v = s.top();
if (!v->visited) {
v->visited = true;
for (Edge *e = v->e; e; e = e->next) if (e->t->p == NULL && e->t != v->p) e->t->p = v, s.push(e->t);
} else {
v->size = 1;
for (Edge *e = v->e; e; e = e->next) if (e->t->p == v) {
v->size += e->t->size;
if (v->c == NULL || v->c->size < e->t->size) v->c = e->t;
}
s.pop();
}
}
for (int i = 0; i < n; i++) N[i].visited = false;
s.push(&N[0]);
int time = -1;
while (!s.empty()) {
Node *v = s.top();
if (!v->visited) {
v->visited = true;
v->pos = ++time;
if (!v->p || v != v->p->c) v->path = new Path(v);
else v->path = v->p->path;
for (Edge *e = v->e; e; e = e->next) if (e->t->p == v && e->t != v->c) s.push(e->t);
if (v->c) s.push(v->c);
} else v->posEnd = time, s.pop();
}
}