顶点深度优先次序
来源:互联网 发布:网络赌钱平台破解方法 编辑:程序博客网 时间:2024/05/21 23:50
在调用dfs的过程中,几种添加顶点到集合的顺序。一共有四种顺序:
- Pre-Order,在递归调用dfs之前将当前顶点添加到queue中
- Reverse Pre-Order,在递归调用dfs之前将当前顶点添加到stack中
- Post-Order,在递归调用dfs之后将当前顶点添加到queue中
- Reverse Post-Order,在递归调用dfs之后将当前顶点添加到stack中
最后一种的用途最广,至少目前看来是这样,比如步骤2-a以及拓扑排序中,都是利用的Reverse Post-Order来获取顶点集合。
Kosaraju的主要步骤:
- 对G求解Reverse Post-Order,即上文中的”伪拓扑排序“
- 对G进行转置得到GR
- 按照第一步得到的集合中顶点出现的顺序,对GR调用DFS得到若干颗搜索树
- 每一颗搜索树就代表了一个强连通分量
#include <stdlib.h>#include <iostream>#include <stack>#include <queue>using namespace std;struct node /* 图顶点结构定义 */{ int vertex; /* 顶点数据信息 */ node *nextnode; /* 指下一顶点的指标 */};const int vertexnum = 4;/*顶点的数目*/const int instance = 4;typedef struct node *graph; /* 图形的结构新型态 */node head[vertexnum+1]; /* 图形顶点数组 */node reversehead[vertexnum+1]; /* 图形顶点数组 */int visited[vertexnum+1]; /* 遍历标记数组 */int id[vertexnum+1]; /*标记连通分量的编号*///int edgeto[vertexnum+1];/*标记节点前继*///int onstack[vertexnum+1];/*标记栈中节点*/int count = 0; /*连通分量编号*/int edgenum = 0;/*边的数目*/queue<int> pre;queue<int> post;stack<int> reversepost;/********************根据已有的信息建立邻接表********************/void creategraph(int node[instance][2],int num)/*num指的是图的边数*/{ graph newnode; /*指向新节点的指针定义*/ graph ptr; int from; /* 边的起点 */ int to; /* 边的终点 */ int i; for ( i = 0; i < num; i++ ) /* 读取边线信息,插入邻接表*/ { from = node[i][0]; /* 边线的起点 */ to = node[i][1]; /* 边线的终点 */ /* 建立新顶点 */ newnode = ( graph ) malloc(sizeof(struct node)); newnode->vertex = to; /* 建立顶点内容 */ newnode->nextnode = NULL; /* 设定指标初值 */ ptr = &(head[from]); /* 顶点位置 */ while ( ptr->nextnode != NULL ) /* 遍历至链表尾 */ ptr = ptr->nextnode; /* 下一个顶点 */ ptr->nextnode = newnode; /* 插入节点 */ }}void createreversegraph(int node[instance][2],int num)/*num指的是图的边数*/{ graph newnode; /*指向新节点的指针定义*/ graph ptr; int from; /* 边的起点 */ int to; /* 边的终点 */ int i; for ( i = 0; i < num; i++ ) /* 读取边线信息,插入邻接表*/ { to = node[i][0]; /* 边线的起点 */ from = node[i][1]; /* 边线的终点 */ /* 建立新顶点 */ newnode = ( graph ) malloc(sizeof(struct node)); newnode->vertex = to; /* 建立顶点内容 */ newnode->nextnode = NULL; /* 设定指标初值 */ ptr = &(reversehead[from]); /* 顶点位置 */ while ( ptr->nextnode != NULL ) /* 遍历至链表尾 */ ptr = ptr->nextnode; /* 下一个顶点 */ ptr->nextnode = newnode; /* 插入节点 */ }}void dfs(int current){visited[current] = 1;pre.push(current);cout<<current<<endl;node *p;p = head[current].nextnode;while(p != NULL){if(visited[p->vertex] == 0){visited[p->vertex] = 1;dfs(p->vertex);}p = p->nextnode;}post.push(current);reversepost.push(current);}void kosarajuSCCdfs(int current){visited[current] = 1;id[current] = count;node *p;p = reversehead[current].nextnode;cout<<current<<" ";while(p != NULL){if(visited[p->vertex] == 0){visited[p->vertex] = 1;kosarajuSCCdfs(p->vertex);}p = p->nextnode;}}void kosarajuSCC(){int current;for(int i=1;i <= vertexnum;i++){visited[i] = 0;}count = 0;while(!reversepost.empty()){current = reversepost.top();reversepost.pop();if(visited[current] == 0){kosarajuSCCdfs(current);cout<<endl;count++;}}}/****************************** 主程序******************************/int main(){ graph ptr; /* int node[instance][2] = { {1, 2}, {2, 1}, {1, 3}, {3, 1}, {1, 4}, {4, 1}, {2, 5}, {5, 2}, {2, 6}, {6, 2}, {3, 7}, {7, 3}, {4, 7}, {4, 4}, {5, 8}, {8, 5}, {6, 7}, {7, 6}, {7, 8}, {8, 7} }; */ int node[instance][2] = { {1, 2}, {2, 3},{3,4},{4,2} }; int i; edgenum = 4; //clrscr(); for ( i = 1; i <= vertexnum; i++ ) /* 顶点数组初始化 */ { head[i].vertex = i; /* 设定顶点值 */ head[i].nextnode = NULL; /* 指针为空 */ visited[i] = 0; /* 设定遍历初始标志 */ //onstack[i] = 0; } creategraph(node,instance); /* 建立邻接表 */ createreversegraph(node,instance); /* 建立邻接表 */ cout<<"Content of the gragh's ADlist is:\n"; for ( i = 1; i <= vertexnum; i++ ) { cout<<"vertex"<<head[i].vertex<<" ->";/* 顶点值 */ ptr = head[i].nextnode; /* 顶点位置 */ while ( ptr != NULL ) /* 遍历至链表尾 */ { cout<<" "<<ptr->vertex<<" "; /* 印出顶点内容 */ ptr = ptr->nextnode; /* 下一个顶点 */ } cout<<endl; /* 换行 */ } cout<<"\nThe end of the dfs are:\n"; for(i=1;i<=vertexnum;i++){ if(visited[i] == 0){ dfs(i); /* 打印输出遍历过程 */ count++; } } cout<<endl; kosarajuSCC();}
对kosaraju的证明,这个版本比较清晰,来自http://blog.csdn.net/dm_vincent/article/details/8554244
证明的目标,就是最后一步 --- 每一颗搜索树代表的就是一个强连通分量
证明:设在图GR中,调用DFS(s)能够到达顶点v,那么顶点s和v是强连通的。
两个顶点如果是强连通的,那么彼此之间都有一条路径可达,因为DFS(s)能够达到顶点v,因此从s到v的路径必然存在。现在关键就是需要证明在GR中从v到s也是存在一条路径的,也就是要证明在G中存在s到v的一条路径。
而之所以DFS(s)能够在DFS(v)之前被调用,是因为在对G获取ReversePost-Order序列时,s出现在v之前,这也就意味着,v是在s之前加入该序列的(因为该序列使用栈作为数据结构,先加入的反而会在序列的后面)。因此根据DFS调用的递归性质,DFS(v)应该在DFS(s)之前返回,而有两种情形满足该条件:
- DFS(v) START -> DFS(v) END -> DFS(s) START -> DFS(s) END
- DFS(s) START -> DFS(v) START -> DFS(v) END -> DFS(s) END
是因为而根据目前的已知条件,GR中存在一条s到v的路径,即意味着G中存在一条v到s的路径,而在第一种情形下,调用DFS(v)却没能在它返回前递归调用DFS(s),这是和G中存在v到s的路径相矛盾的,因此不可取。故情形二为唯一符合逻辑的调用过程。而根据DFS(s) START -> DFS(v) START可以推导出从s到v存在一条路径。
所以从s到v以及v到s都有路径可达,证明完毕。
复杂度分析:
根据上面总结的Kosaraju算法关键步骤,不难得出,该算法需要对图进行两次DFS,以及一次图的转置。所以复杂度为O(V+E)。
参考:
http://blog.csdn.net/dm_vincent/article/details/8554244
http://blog.csdn.net/michealtx/article/details/8233814
http://www.cnblogs.com/luweiseu/archive/2012/07/14/2591370.html
http://www.cnblogs.com/suoloveyou/archive/2012/05/06/2486589.html
http://edward-mj.com/?p=455
- 顶点深度优先次序
- 深度优先搜索算法和广度优先搜索算法的搜索次序(二叉树)
- 深度优先搜索/广度优先搜索顶点之间的路径(邻接表)
- 事情优先次序的原则
- java运算符优先次序
- 深度优先
- C 运算符的优先次序
- Turbo C运算符的优先次序
- Turbo C运算符的优先次序
- 安排不同险种的优先次序
- 对系统需求进行优先次序排序
- css的引入和优先次序
- Python中元类的执行优先次序
- 深度优先与广度优先
- 深度优先还是广度优先
- 深度优先和广度优先
- 深度优先和广度优先
- 广度优先和深度优先
- java 构造方法
- thinkphp M和D
- 基于邻接表建图的几种方法
- jsp中文编码 xhtml中文编码
- V-Chip 分类
- 顶点深度优先次序
- java学习入门
- android menu学习小记
- 我想找个地方安家,整理和记录自己在学习路上的点点滴滴(自我介绍和评价)
- 通过小练习掌握MFC知识点之起步篇-MFC与数据库的交互
- wpa_supplicant无线网络配置
- standard system viewcontroller
- 详细解说STL hash_map系列
- 动态规划之最长公共子序列