问题
I know the common way to do a topological sort is using DFS with recursion. But how would you do it using stack<int>
instead of recursion? I need to obtain the reversed post-order but I'm kinda stuck:
The graph is a vector<vector<int> >
adjacency list
The following is the DFS which I want to use for topological sort
bool visited[MAX]={0};
stack<int> dfs, postOrder;
vector<int> newVec;
vector<int>::iterator it;
for(int i=0;i<MAX;i++){
if(visited[i]==false){
dfs.push(i);
}
while(!dfs.empty()){
int node=dfs.top();
dfs.pop();
visited[node]=true;
newVec=graph[node]; //vector of neighboors
for(it=newVec.begin();it!=newVec.end();it++){
int son=*it;
if(visited[son]==false){
dfs.push(son);
}
}
}
}
回答1:
In order to construct the postOrder
list you need to know the time when your algorithm has finished processing the last child of node k
.
One way to figure out when you have popped the last child off the stack is to put special marks on the stack to indicate spots where the children of a particular node are starting. You could change the type of your dfs
stack to vector<pair<bool,int> >
. When the bool
is set to true
, it indicates a parent; false
indicates a child.
When you pop a "child pair" (i.e. one with the first member of the pair set to false
) off the stack, you run the code that you currently have, i.e. push all their children onto the stack with your for
loop. Before entering the for
loop, however, you should push make_pair(true, node)
onto the stack to mark the beginning of all children of this node
.
When you pop a "parent pair" off the stack, you push the parent index onto the postOrder
, and move on:
vector<bool> visited(MAX);
stack<pair<bool,int> > dfs;
stack<int> postOrder;
vector<int> newVec;
vector<int>::iterator it;
vector<vector<int> > graph;
for(int i=0;i<MAX;i++){
if(visited[i]==false){
dfs.push(make_pair(false,i));
}
while(!dfs.empty()){
pair<bool,int> node=dfs.top();
dfs.pop();
if (node.first) {
postOrder.push(node.second);
continue;
}
visited[node.second]=true;
dfs.push(make_pair(true, node.second));
newVec=graph[node.second]; //vector of neighboors
for(it=newVec.begin();it!=newVec.end();it++){
int son=*it;
if(visited[son]==false){
dfs.push(make_pair(false, son));
}
}
}
}
Demo on ideone.
回答2:
I think your code is a good non-recursive DFS . The key point of topological sort is that:
When you pick a node to push into the stack. This node must have no precessor( has a in-degree of 0). This means you are doing a DFS base on in-degree in stead of push all the adjacent nodes into the stack, you always pick the one with 0 in-degree
So you push every node that have no precessor in the stack. Pop one, print it and remove it from all its adjacent nodes' precessor list ( or decrease its adjacent nodes' in-degree by 1). This need you to edit your adjacent list. Than push all its adjacent nodes that has in-degree of 0 now in to the stack( this phase may fail but no problem, you still got some nodes in the stack). Then pop the next one. Repeat until the stack is empty. The node sequence that you printed is the topological sort result of the graph.
回答3:
Below is my iterative code to topological sorting of DAG.
#include <iostream>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include <stack>
using namespace std;
unordered_map<int, unordered_set<int>> g; // this is the simplest graph representation I was able to implement. Map the vertices to their set of children
void addEdge(int x, int y) { // Add edges to the graph
g[x].insert(y);
}
void topSort() {
unordered_set<int> visited; // Keep track of visited nodes
stack<int> mainStack; // The stack that will have the resulting vertices in topologically sorted order
for(auto it = g.begin(); it != g.end(); it++) {
if(visited.count(it->first) == 0) { // If the vertex was not already visited do the following
visited.insert(it->first); // visit the vertex
stack<int> locStack;
locStack.push(it->first); // push it to local stack
while(!locStack.empty()) { // This part is similar to basic DFS algorithm
int curr = locStack.top();
bool unVisCh = false; // Keep a boolean to check whether there is any unvisited child
for(auto it2 = g[curr].begin(); it2 != g[curr].end(); it2++) {
int child = *it2;
if(visited.count(child) == 0) {
unVisCh = true;
visited.insert(child);
locStack.push(child);
}
}
if(!unVisCh) { // if there is no unvisited child then we can push the vertex to the resulting stack
locStack.pop();
mainStack.push(curr);
}
}
}
}
while(!mainStack.empty()) {
cout<<mainStack.top()<<" ";
mainStack.pop(); // print them in order
}
cout<<endl;
}
int main() {
addEdge(1,2);
addEdge(4,5);
addEdge(5,6);
addEdge(3,2);
addEdge(2,6);
addEdge(1,3);
addEdge(4,3); // add adges to the graph
topSort();
return 0;
}
For testing: ideone
Hope that helps!
回答4:
The node is 1st visited and is still in process, it's added to stack as false. These nodes are then processed from stack as LIFO, and changed to true (processed means children visited). After all children processed, while tracing path back, this node dropped from stack.
For those trying to implement this code, visited[node.second]=true;
should be moved to the 2 places where node is 1st added to stack as false. This, so that back edges leading to already traced vertices not revisited.
回答5:
Here we go again. :-) I am submitting an answer, because I do not have enough points to make comments. :-(
Well let me say that I like this algorithm a lot. If the graph is defined in the right way, then there is no error. But take this graph:
vector<vector<int>> graph
{
{ 2, 1 }
,{ 2 }
,{ }
};
This will display: 2 1 2 0
To protect yourself from graphs defined in that way or where the edges that come in are random you can do this:
#include <iostream>
#include <stack>
#include <algorithm>
#include <vector>
using namespace std;
int main()
{
stack<pair<bool, int>> dfs;
stack<int> postorder;
vector<int> newvector;
vector<int>::iterator it;
vector<vector<int>> graph
{
{ 2, 1 }
,{ 2 }
,{ }
};
vector<bool> visited(graph.size());
vector<bool> finallyvisited(graph.size());
for (int i = 0;i < graph.size();i++)
{
if (!visited[i])
{
dfs.push(make_pair(false, i));
}
while (!dfs.empty())
{
pair<bool, int> node = dfs.top();
dfs.pop();
if (node.first)
{
if (!finallyvisited[node.second])
{
finallyvisited[node.second] = true;
postorder.push(node.second);
cout << node.second << endl;
}
continue;
}
visited[node.second] = true;
dfs.push(make_pair(true, node.second));
newvector = graph[node.second];
for (it = newvector.begin();it != newvector.end();it++)
{
int son = *it;
if (!visited[son])
{
dfs.push(make_pair(false, son));
}
}
}
}
return 0;
}
Or you could preorder the graph, maybe someone could show that solution. How to preorder randomly given edges that there is no need for second check. :-)
And I did though over the Atif Hussain comment and it is erroneous. That would never work. You always want to push down the stack a node as late as possible so it pops as first as possible.
来源:https://stackoverflow.com/questions/20153488/topological-sort-using-dfs-without-recursion