图的拓扑排序的非递归和递归的c++实现
2015-08-05 15:21
603 查看
最近研究了一下图的深度优先遍历,尝试写了一下递归和非递归的算法,从而得到一个有向无环图的拓扑序列, 在教材书中,很多都是利用的递归方法来完成图的深度优先遍历,这里可以给大家介绍一下非递归的遍历实现。
对于有向图的深度优先遍历和拓扑序,需要明确下面几点:
1)只有有向无环图才可以生成拓扑序,如果有环,无法生成拓扑序
2)深度优先遍历可以检查出是否有环
3)深度优先遍历的非递归实现应该使用栈来实现。
4)拓扑序如果存在,可能不止一个,这里只要求出一个就可以了。
下面就简单利用C++来实现递归和非递归的基于深度优先的拓扑序。
前置条件: 不失一般性,我们可以用0…n-1 来表示n个顶点,其使用vector<int> verticalState来表示顶点的访问状态 (0, 表示未访问,1 表示正在访问,2 表示访问完成,实际的项目中我们更应该用枚举来实现)。可以利用map<int, vector> adjList 的map数据结构来表示邻接表,map的 key值代表的是顶点的id, value代表的是顶点的邻接表,这样我们就可以表示一张有向图了。
递归实现, 递归实现相对来说简单一点,代码如下:
非递归实现: 和通常的树的访问一样,深度优先遍历的非递归实现需要用栈来实现。
上面的map可以换成hashmap以提高访问的效率。
对于有向图的深度优先遍历和拓扑序,需要明确下面几点:
1)只有有向无环图才可以生成拓扑序,如果有环,无法生成拓扑序
2)深度优先遍历可以检查出是否有环
3)深度优先遍历的非递归实现应该使用栈来实现。
4)拓扑序如果存在,可能不止一个,这里只要求出一个就可以了。
下面就简单利用C++来实现递归和非递归的基于深度优先的拓扑序。
前置条件: 不失一般性,我们可以用0…n-1 来表示n个顶点,其使用vector<int> verticalState来表示顶点的访问状态 (0, 表示未访问,1 表示正在访问,2 表示访问完成,实际的项目中我们更应该用枚举来实现)。可以利用map<int, vector> adjList 的map数据结构来表示邻接表,map的 key值代表的是顶点的id, value代表的是顶点的邻接表,这样我们就可以表示一张有向图了。
递归实现, 递归实现相对来说简单一点,代码如下:
// 生成拓扑序,如果没有,就生成空的Vector // n 表示顶点个数,adjList 表示图的邻接表 vector<int> generateOrder (int n, map<int, vector<int>> &adjList) { //初始化顶点的状态 vector<VerticalState> verticalState (n, NoneVisit); vector<int> empty; // 返回值当图为有环图的时候 vector<int> ans; // 一个一个的顶点进行访问 for ( int i = 0; i < n; i++ ){ bool success = dfs (adjList, i, verticalState, ans); if ( !success ){ return empty; } // 有环就直接返回空 } // 处理哪些无边的顶点 for ( int i = 0; i < n; i++ ){ if ( std::find (ans.begin (), ans.end (), i) == ans.end () ){ ans.push_back (i); } } return ans; } // 递归函数用于访问当前节点的深度优先遍历图 // 返回false如果有环,有环的唯一情况是:深度优先遍历时候,访问到的邻接点是正在访问状态 // 无环的访问true // adjList 表示图的邻接表 // verticalId 表示当前的顶点id // verticalState 表示顶点的访问状态 // order 表示顶点访问完成的顺序,用于生成拓扑序 bool dfs (map<int, vector<int>> &adjList, int verticalId, vector<VerticalState> &verticalState, vector<int>&order) { if ( verticalState[verticalId] == NoneVisit ){ bool success = true; verticalState[verticalId] = Visiting; // is visiting vector<int> afterVec = adjList[verticalId]; for ( int index = 0; index < afterVec.size (); index++ ){ if ( verticalState[afterVec[index]] == Visiting ) { return false; } else if ( verticalState[afterVec[index]] == NoneVisit) { success = success && dfs (adjList, afterVec[index], verticalState, order); if ( !success ) return false; } } // 顶点访问完成,可以将其放入完成队列中 verticalState[verticalId] = Visited; order.push_back (verticalId); } return true; }
非递归实现: 和通常的树的访问一样,深度优先遍历的非递归实现需要用栈来实现。
enum VerticalState { NoneVisit, Visiting, Visited, }; // 生成拓扑序,如果没有,就生成空的Vector // n 表示顶点个数,adjList 表示图的邻接表 vector<int> generateOrder (int n, map<int, vector<int>> &adjList) { //初始化顶点的状态 vector<VerticalState> verticalState (n, NoneVisit); vector<int> empty; // 返回值当图为有环图的时候 vector<int> ans; // 一个一个的顶点进行访问 for ( int i = 0; i < n; i++ ){ ***if ( verticalState[i] != NoneVisit ) { continue; } // 顶点已经访问过了,无需访问 stack<int> verticalStack; verticalStack.push (i); while ( !verticalStack.empty () ){ int verticalId = verticalStack.top (); if (verticalState[verticalId] == Visiting ) //这是顶点弹出,应该是访问已经完成 { verticalState[verticalId] = Visited; // 节点访问完成 verticalStack.pop (); ans.push_back (verticalId); continue; } verticalState[verticalId] = Visiting; // 设置顶点访问标志 bool finished = true; // 是否有未访问到的临接点,如果没有,表示该节点已经访问完成。 vector<int> afterVec = adjList[verticalId]; for ( int index = 0; index< afterVec.size (); index++ ){ // 有环,直接返回 if (verticalState[afterVec[index]] == Visited) { return empty; } // 代表有临接点,压栈 else if (verticalState[afterVec[index]] == NoneVisit ) { verticalStack.push (afterVec[index]); finished = false; } } if (finished ){ verticalState[verticalId] = Visited; // 顶点返回完成 verticalStack.pop (); ans.push_back (verticalId); } }*** } // 处理哪些无边的顶点 for ( int i = 0; i < n; i++ ){ if ( std::find (ans.begin (), ans.end (), i) == ans.end () ){ ans.push_back (i); } } return ans; }
上面的map可以换成hashmap以提高访问的效率。
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- 关于指针的一些事情
- c++ primer 第五版 笔记前言
- share_ptr的几个注意点
- 渗透技术一瞥(图)
- 图片引发的溢出危机(图)
- Lua中调用C++函数示例
- Lua教程(一):在C++中嵌入Lua脚本
- Lua教程(二):C++和Lua相互传递数据示例
- C++联合体转换成C#结构的实现方法
- C++编写简单的打靶游戏
- C++ 自定义控件的移植问题
- C++变位词问题分析
- C/C++数据对齐详细解析
- C++基于栈实现铁轨问题
- C++中引用的使用总结
- 使用Lua来扩展C++程序的方法
- C++中调用Lua函数实例
- Lua和C++的通信流程代码实例
- C与C++之间相互调用实例方法讲解