#include "Stack.hpp" #include "Queue.hpp" typedef enum { UNDISCOVERED, DISCOVERED, VISITED }VStatus; //顶点状态 typedef enum { UNDETERMINED, TREE, CROSS, FORWARD, BACKWARD }EType; //边在遍历树中所属的类型 template <typename Tv,typename Te> //顶点类型,边类型 class Graph{ private: //所有顶点、边的辅助信息复位 void reset(){ for (int i = 0; i < n; i++) { status(i) = UNDISCOVERED; dTime(i) = fTime(i) = -1; parent(i) = -1; priority(i) = INT_MAX; for (int j = 0; j < n; j++) { if (exists(i, j)) { type(i, j) = UNDETERMINED; } } } } void BFS(int, int&); //(连通域)广度优先搜索算法 void DFS(int, int&); //(连通域)深度优先搜索算法 void BCC(int, int&, Stack<int>&); //(连通域)基于DFS的双连通分量分解算法 bool TSort(int, int&,Stack<Tv>*); //(连通域)基于DFS的拓扑排序算法 template <typename PU> void PFS(int, PU); //(连通域)优先级搜索框架 public: //顶点的操作 int n; //顶点总数 virtual int insert(Tv const&) = 0; //插入顶点,返回编号 virtual Tv remove(int) = 0; //删除顶点及其关联边,返回该顶点信息 virtual Tv& vertex(int) = 0; //顶点v的数据(该顶点的确存在) virtual int inDegree(int) = 0; //顶点v的入度 virtual int outDegree(int) = 0; //顶点v的出度 virtual int firstNbr(int) = 0; //顶点v的首个邻接顶点 virtual int nextNbr(int, int) = 0;//顶点v的(相对于顶点j的)下一邻接顶点 virtual VStatus& status(int) = 0; //顶点v的状态 virtual int& dTime(int) = 0; //顶点v的时间标签dTime virtual int& fTime(int) = 0; //顶点v的时间标签fTime virtual int& parent(int) = 0; //顶点v在遍历树中的父亲 virtual int& priority(int) = 0; //顶点v在遍历树中的优先级数 //边的操作 //这里约定,无向边均统一转化成方向互逆的一对有向边,从而将无向边视作有向边的特例 int e; //边总数 virtual bool exists(int,int) = 0; //边(v, u)是否存在 virtual void insert(Te const&,int,int,int) = 0;//在顶点v和u之间插入权重为w的边e virtual Te remove(int, int) = 0; //删除顶点v和u之间的边e,返回该边信息 virtual EType& type(int,int) = 0; //边(v,u)的类型 virtual Te& edge(int,int) = 0; //边(v,u)的数据(该边的确存在) virtual int& weight(int, int) = 0; //边(v, u)的权重 //算法 void bfs(int); //广度优先搜索算法 void dfs(int); //深度优先搜索算法 void bcc(int); //基于DFS的双连通分量分解算法 Stack<Tv> *tSort(int); //基于DFS的拓扑排序算法 void prim(int); //最小支撑树prim算法 void dijkstra(int); //最短路径dijkstra算法 template <typename PU> void pfs(int, PU); //优先级搜索框架 }; //广度优先搜索算法(breadth-first-serach, BFS) //越早被访问到的顶点,其邻居越优先被选用 template <typename Tv,typename Te> void Graph<Tv,Te>::bfs(int s) { //assert: 0 <= s < n reset(); int clock = 0; int v = s; do { //逐一检查所有顶点,一旦遇到尚未发现的顶点,即从该顶点出发启动一次BFS if (UNDISCOVERED == status(v)) { BFS(v, clock); } } while ( s != (v = (++v % n))); //按序号检查,故不漏不重 } //单个连通域 template <typename Tv,typename Te> void Graph<Tv, Te>::BFS(int v, int &clock) { //assert: 0 <= v < n Queue<int> Q; status(v) = DISCOVERED; Q.enqueue(v); while (!Q.empty()) { int v = Q.dequeue(); dTime(v) = ++clock; for (int u = firstNbr(v); -1 < u; u = nextNbr(v, u)) { //枚举v的所有邻居u if (UNDISCOVERED == status(u)) { //若u尚未被发现,则发现该顶点,引入树边拓展支撑树 status(u) = DISCOVERED; Q.enqueue(u); type(v, u) = TREE; parent(u) = v; }else{ //若u已被发现,或者甚至已访问完毕,则将(v,u)归类于跨边 type(v, u) = CROSS; } } status(v) = VISITED; } } //深度优先搜索(depth-first-search, DFS) //优先选取最后一个被访问到的顶点的邻居 template <typename Tv, typename Te> void Graph<Tv, Te>::dfs(int s) { reset(); int clock = 0; int v = s; do { //逐一检查所有顶点,一旦遇到尚未发现的顶点,即从该顶点出发启动一次DFS if (UNDISCOVERED == status(v)) { DFS(v, clock); } } while ( s != (v = (++v % n))); //按序号检查,故不漏不重 } template <typename Tv, typename Te> void Graph<Tv, Te>::DFS(int v,int& clock) { //assert: 0 <= v < n dTime(v) = ++clock; status(v) = DISCOVERED; for (int u = firstNbr(v); -1 < u; u = nextNbr(v, u)) { //枚举v的所有邻居 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方告访问完毕 } //基于DFS的拓扑排序算法 template <typename Tv,typename Te> Stack<Tv>* Graph<Tv, Te>::tSort(int s) { reset(); int clock = 0; int v = s; Stack<Tv> *S = new Stack<Tv>; //用栈记录排序顶点 do{ if (UNDISCOVERED == status(v)) { //clock并非必须 if (!TSort(v, clock, S)) { //任一连通域(亦即整图)非DAG,则不必继续计算,故直接返回 while (!S->empty()) { S -> pop(); break; } } } }while( s != (v = (++v % n))); //若输入为DAG(有向无环图),则S内各顶点自顶向底排序;否则(不存在拓扑排序),S空 return S; } //基于DFS的拓扑排序算法(单趟) template <typename Tv,typename Te> bool Graph<Tv, Te>::TSort(int v, int &clock, Stack<Tv> *S) { //assert: 0 <= v < n dTime(v) = ++clock; status(v) = DISCOVERED; for (int u = firstNbr(v); -1 < u; u = nextNbr(v, u)) { //枚举v的所有邻居 switch (status(u)) { case UNDISCOVERED: parent(u) = v; type(v, u) = TREE; //从顶点u出出发深入搜索,若u及其后代不能拓扑排序(则全图亦必如此),返回并报告 if (! TSort(u, clock, S)) { return false; } break; case DISCOVERED: //一旦发现后向边,则不必深入,故返回并报告 type(v, u) = BACKWARD; return false; break; default: type(v, u) = ( dTime(v) < dTime(u) ? FORWARD : CROSS); break; } } status(v) = VISITED; S -> push(vertex(v)); //顶点被标记为VISITED时,随即入栈 return true; //v及其后代可以拓扑排序 } //基于DFS的BCC分解算法 template <typename Tv,typename Te> void Graph<Tv, Te>::bcc(int s) { reset(); int clock = 0; int v = s; Stack<int> S; do{ if (UNDISCOVERED == status(v)) { BCC(v, clock, S); S.pop(); } }while( s != (v = (++v % n)) ); } #define hca(x) (fTime(x)) //利用此处闲置的fTime[]充当hca[] template <typename Tv,typename Te> void Graph<Tv, Te>::BCC(int v, int &clock, Stack<int> &S) { hca(v) = dTime(v) = ++clock; status(v) = DISCOVERED; S.push(v); for (int u = firstNbr(v); -1 < u; u = nextNbr(v, u)) { switch (status(u)) { case UNDISCOVERED: parent(u) = v; type(v, u) = TREE; BCC(u, clock, S); //从顶点u出深入 //遍历返回后,若发现u(通过后向边)可指向v的真祖先,则v亦必如此 if (hca(u) < dTime(v)) { hca(v) = std::min(hca(v), hca(u)); }else{ //否砸,以v为关节点(u以下即是一个BCC,且其中顶点此时正集中于栈S的顶部) while ( v != S.pop()) { //依次弹出当前BCC中的节点,亦可根据实际需求转存至其他结构 S.push(v); //最后一个顶点(关节点)重新入栈----分摊不足一次 } } break; case DISCOVERED: type(v, u) = BACKWARD; if (u != parent(v)) { hca(v) = std::min(hca(v), hca(u)); } break; default: type(v, u) = (dTime(v) < dTime(u) ? FORWARD : CROSS); break; } } status(v) = VISITED; } #undef hca //优先级搜索 template <typename Tv,typename Te> template <typename PU> void Graph<Tv, Te>::pfs(int s, PU prioUpdater) { reset(); int v = s; do{ if (UNDISCOVERED == status(v)) { PFS(v, prioUpdater); } }while( s != (v = (++v % n))); } template <typename Tv,typename Te> template <typename PU> void Graph<Tv, Te>::PFS(int s, PU prioUpdater){ priority(s) = 0; status(s) = VISITED; parent(s) = -1; while (1) { //将下一顶点和边加至PFS(priority-first-search)树中 for (int w = firstNbr(s); -1 < w; w = nextNbr(s, w)) { prioUpdater(this, s, w); } for (int shortest = INT_MAX, w = 0; w < n; w++) { //从尚未加入遍历树的顶点中选出下一个 if (UNDISCOVERED == status(w)) { if (shortest > priority(w)) { shortest = priority(w); s = w; } } } //直至所有顶点均已加入 if (VISITED == status(s)) { break; } status(s) = VISITED; type(parent(s), s) = TREE; //将s及与其父的联边加入遍历树 } } //针对Prim算法的顶点优先级更新器 template <typename Tv, typename Te> struct PrimPU { virtual void operator()(Graph<Tv, Te>* g,int uk,int v){ if (UNDISCOVERED == g -> status(v)) { if (g->priority(v) > g -> weight(uk, v)) { //按Prim策略做松弛 g -> priority(v) = g -> weight(uk, v); g -> parent(v) = uk; } } } }; //针对Dijkstra算法的顶点优先级更新器 template <typename Tv, typename Te> struct DijkstraPU { virtual void operator()(Graph<Tv, Te>* g,int uk,int v){ if (UNDISCOVERED == g -> status(v)) { if (g->priority(v) > g->priority(uk) + g -> weight(uk, v)) { //做松弛 g -> priority(v) = g->priority(uk) + g -> weight(uk, v); g -> parent(v) = uk; } } } }; //针对BFS算法的顶点优先级更新器 template <typename Tv, typename Te> struct BfsPU { virtual void operator()(Graph<Tv, Te>* g,int uk,int v){ if (UNDISCOVERED == g -> status(v)) { if (g->priority(v) > g->priority(uk) + 1) { g -> priority(v) = g->priority(uk) + 1; g -> parent(v) = uk; } } } }; //针对DFS算法的顶点优先级更新器 template <typename Tv, typename Te> struct DfsPU { virtual void operator()(Graph<Tv, Te>* g,int uk,int v){ if (UNDISCOVERED == g -> status(v)) { if (g->priority(v) > g->priority(uk) - 1) { g -> priority(v) = g->priority(uk) - 1; g -> parent(v) = uk; return; } } } };
浙公网安备 33010602011771号