void Graph<Tv, Te>::DFS(int v, int &clock){ //深度优先搜索DFS算法(单个连通域) dTime(v) = ++clock; status(v) = DISCOVERED; //发现当前顶点v for( int u = firstNbr(v); -1 < u; u = nextNbr(v, u) ){ //枚举v的所有邻居u switch(status(u)){ case UNDISCOVERED://u尚未发现,意味着支撑树可在此拓展 type(v, u) = TREE; parent(u) = v; DFS(u, clock); break; case DISCOVERED: //u已被发现但未访问完毕, 应属被后代指向的祖先 type(v, u) = BACKWARD; break; default: //u已访问完毕(VISITED, 有向图), 则视承袭关系分为前向边或跨边 type(v, u) = (dTime(v) < dTime(u) ) ? FORWARD : CROSS; break; } } status(v) = VISITED; fTime(v) = ++clock; //至此,当前顶点v方告访问完毕 }
int outDegree(int i) {return V[i].outDegree};//出度 VStatus & status(int i) {return V[i].status;}//状态 int & dTime(int i) {return V[i].dTime;}//时间标签dTime int & fTime(int i) {return V[i].fTime;}//时间标签fTime int & parent(int i) {return V[i].parent;}//在遍历树中的父亲 int & priority (int i) {return V[i].priority;}//优先函数 int nextNbr( int i, int j ){//若已枚举至邻居j,则转向下一邻居 while ( (-1 < j) && !exists(i, --j) );//逆向顺序查找,O(n) return j; }//改用邻接表可提高至O(1+outDegree(i) ) int firstNbr( int i ){ return nextNbr( i, n );//假想哨兵 }//首个邻居 //边操作 bool exists (int j, int j){//判断边(i, j)是否存在 return (0 <= i) && (i < n) && (0 <= j) && (j < n) && E[i][j] != NULL; } Te & edge (int i, int j)//边(i, j)的数据 {return E[i][j]->data;}//O(1) EType & type(int i; int j)//边(i, j)的类型 {return E[i][j]->type;}//O(1) int & weight (int j; int j)//边(i, j)的权重 {return E[i][j]->weight;}//O(1) //边插入 void insert (Te const & edge, int w, int i, int j){//插入(i, j, w) if (exists(i, j) ) return;//忽略已有边 E[i][j] = new Edge<Te> (edge, w);//创建新边 e++;//更新边计数 V[i].outDegree++;//更新关联点i的出度 V[j].inDegree++;//更新关联点j的入度 } //边删除 Te remove(int i, int j){//删除顶点i和j之间的边(exists(i, j)) Te eBak = edge(i, j);//备份边(i, j)的信息 delete E[i][j]; E[i][j] = NULL; e--;//更新计数 V[i].outDegree--; V[j].inDegree--; return eBak;//返回删除边的信息 }
void Graph<Tv, Te>::PFS(int s, PU prioUpdater){ //优先级搜索(单个连通域) priority(s) = 0; status(s) = VISITED; parent(s) = -1; //初始化,起点s加至PFS树中 while(1){ //将下一顶点和边加至PFS树中 for(int w = firstNbr(s); -1 < w; w = nextNbr(s, w)) //枚举s的所有邻居w prioUpdater(this, s, w); //更新顶点w的优先级及其父顶点 for(int shortest = INT_MAX, w = 0; w < n; w++) if(UNDISCOVERED == status(w)) //从尚未加入遍历树的顶点中 if(shortest > priority(w)){ //选出下一个 shortest = priority(w); s = w; //优先级最高的顶点s } if(VISITED == status(s) ) break; //直至所有顶点均已加入 status(s) = VISITED; type( parent(s), s ) = TREE; //将s及与其父的联边加入遍历树 } }//通过定义具体的优先级更新策略prioUpdater,即可实现不同的算法功能
void Graph<Tv, Te>::BFS(int v, int &clock){ MyQueue<int> Q; //引入辅助队列 status(v) = DISCOVERED; Q.enqueue(v); //初始化起点 while(!Q.empty()){ //在Q变空之前,不断 int v = Q.dequeue(); dTime(v) = ++clock; //取出队首顶点v for(int u = firstNbr(v); -1 < u; u = nextNbr(v, u)) //枚举v的所有邻居 if(UNDISCOVERED == status(u)){ //尚未被发现,则 status(u) = DISCOVERED; //设置为已发现 Q.enqueue(u); //并且将该顶点入队 type(v, u) = TREE; parent(u) = v; //引入树边拓展支撑树 }else{ type(v, u) = CROSS; //将(v,u)归类于跨边 } status(v) = VISITED; //至此,当前节点访问完毕 } }
bool Graph<Tv, Te>::TSort(int v, int &clock, MyStack<Tv> *S){ //assert: 0 <= v < n dTime(v) = ++clock; status(v) = DISCOVERED; //发现顶点v for(int u = firstNbr(v); -1 < u; u = nextNbr(v, u)) //枚举v的所有邻居u switch(status(v)){ case UNDISCOVERED: parent(u) = v; type(v, u) = TREE; if( !TSort(u, clock, S)) //从顶点u处出发深入搜索 return false; //若u及其后代不能拓扑程序(则全图亦必如此), 故返回并报告 break; case DISCOVERED: type(v, u) = BACKWARD; //一旦发现后向边(非DAG), 则 return false; //不必深入,故返回并报告 default: //VISITED(digraphs only) type(v, u) = (dTime(v) < dTime(u) ) ? FORWARD : CROSS; break; } status(v) = VISITED; S->push(vertex(v) ); //顶点被标记为VISITED时, 随即入栈 return true; //v及其后代可以拓扑排序 }
void Graph<Tv, Te>::BCC(int v, int &clock, MyStack<int> &S){ //assert: 0 <= v < n hca(v) = dTime(v) = ++clock; status(v) = DISCOVERED; S.push(v); //v被发现并入栈 for(int u = firstNbr(v); -1 < u; u = nextNbr(v, u)) //枚举v的所有邻居u switch(status(u)){ //并视u的状态分别处理 case UNDISCOVERED: parent(u) = v; type(v,u) = TREE; BCC(u, clock, S); //从顶点u处深入 if(hca(u) < dTime(v)) //遍历返回后, 若发现u(通过回边)可指向v的真祖先 hca(v) = min( hca(v), hca(u) ); //则v亦必如此 else{ //否则,以v为关节点(u以下即是一个BCC, 且其中顶点此时正集中于栈s的顶部) while( v != S.pop() ); //依次弹出当前BCC中的节点,亦可根据实际需求转存至其他结构 S.push(v); //最后一个顶点(关节点)重新入栈---总计至多两次 } break; case DISCOVERED: type(v, u) = BACKWARD; //标记(v,u), 并按照"越小越高"的准则 if( u != parent(v) ) hca(v) = min(hca(v), dTime(u)); //更新hca(v) break; default: //VISITED(digraphs only) type(v, u) = (dTime(V) < dTime(u) ? FORWARD : CROSS); break; } status(v) = VISITED; //对v的访问结束 }