顶点深度优先次序

45 篇文章 0 订阅

在调用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的主要步骤:

  1. G求解Reverse Post-Order,即上文中的”伪拓扑排序“
  2. G进行转置得到GR
  3. 按照第一步得到的集合中顶点出现的顺序,对GR调用DFS得到若干颗搜索树
  4. 每一颗搜索树就代表了一个强连通分量

#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,那么顶点sv是强连通的。

两个顶点如果是强连通的,那么彼此之间都有一条路径可达,因为DFS(s)能够达到顶点v,因此从sv的路径必然存在。现在关键就是需要证明在GR中从vs也是存在一条路径的,也就是要证明在G中存在sv的一条路径。

而之所以DFS(s)能够在DFS(v)之前被调用,是因为在对G获取ReversePost-Order序列时,s出现在v之前,这也就意味着,v是在s之前加入该序列的(因为该序列使用栈作为数据结构,先加入的反而会在序列的后面)。因此根据DFS调用的递归性质,DFS(v)应该在DFS(s)之前返回,而有两种情形满足该条件:

  1. DFS(v) START -> DFS(v) END -> DFS(s) START -> DFS(s) END
  1. DFS(s) START -> DFS(v) START -> DFS(v) END -> DFS(s) END

是因为而根据目前的已知条件,GR中存在一条sv的路径,即意味着G中存在一条vs的路径,而在第一种情形下,调用DFS(v)却没能在它返回前递归调用DFS(s),这是和G中存在vs的路径相矛盾的,因此不可取。故情形二为唯一符合逻辑的调用过程。而根据DFS(s) START -> DFS(v) START可以推导出从sv存在一条路径。

所以从sv以及vs都有路径可达,证明完毕。

 

复杂度分析:

根据上面总结的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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值