上一篇博客里面提到了优先级搜索(priority first search),单独拿出来写一篇,因为优先级搜索可以适用大部分图搜算算法。所谓优先级搜索,是在图的遍历过程中,每次迭代都更新图中某些点或者是边的优先级,随着算法的不断推进调整,每次选取顶点或者边中优先级最高的那个元素进行访问。按照这种思想,深度优先搜索和广度优先搜索其实都可以纳入到这个框架中,因为他们的不同之处仅仅在于,每次选择点的时候,点的优先级策略是不同的。约定优先级数值越小,优先级越高。下面,给出优先级搜索的框架:
1 template<typename Tv, typename Te> template<typename PU> void Graph<Tv, Te>::pfs(int s,PU prioUpdater)//优先级搜索 2 { 3 reset(); int clock = 0; int v = s; 4 do 5 if (stasus(v) == UNDISCOVERED)//遇到未发现的顶点 6 PFS(v, clock);//执行一次pFS 7 while (s != (v = (++v%n)));//做到不重不漏 8 } 9 template<typename Tv, typename Te> template<typename PU>//优先级更新器 10 void Graph<Tv, Te>::PFS(int s, PU prioUpdater) 11 { 12 priority(s) = 0; status(s) = VISITED; parent(s) = -1; 13 while (1) 14 { 15 for (int w = firstNbr(s); w > -1; w = nextNbr(s, w))//更新所有的邻接顶点 16 prioUpdater(this, s, w); 17 for(int shortest = INT_MAX, w = 0; w < n; w++)//在所有顶点中寻找优先级最低的顶点 18 if(status(w) == UNDISCOVERED) 19 if (priority(w) < shortest) 20 { 21 shortest = priority(w); 22 s = w; 23 } 24 if (status(s) == VISITED) break;//优先级最高的顶点已经被访问 25 status(s) = VISITED; type(parent(s), s) = TREE; 26 } 27 }
优先级搜索的流程,可以总结如下:对于当前节点的所有邻居,按照某种更新策略,更新他们的优先级;然后,从所有的点中找出优先级最高的顶点进行访问;当取出的优先级最高的顶点已经访问完成,算法结束。这样我们同样得到了和之前相同的,图的一颗遍历树。算法每次迭代都要访问所有的n个节点,时间复杂度为O(n^2)。
下面,把BFS和DFS纳入这个框架中,它们的更新器如下:
1 template <typename Tv, typename Te> struct BfsPU 2 { 3 virtual void operator() ( Graph<Tv, Te>* g, int uk, int v ) 4 { 5 if ( g->status ( v ) == UNDISCOVERED ) 6 if ( g->priority ( v ) > g->priority ( uk ) + 1 ) 7 { 8 g->priority ( v ) = g->priority ( uk ) + 1; //更新优先级(数) 9 g->parent ( v ) = uk; //更新父节点 10 } 11 } 12 }
广度优先搜索,只需要每次将UNDISCOVERED的点,优先级设置为当前点优先级数+1,这样每次新发现的点都要比以前发现的点优先级低。
template <typename Tv, typename Te> struct DfsPU { virtual void operator() ( Graph<Tv, Te>* g, int uk, int v ) { if ( g->status ( v ) == UNDISCOVERED ) if ( g->priority ( v ) > g->priority ( uk ) - 1 ) { g->priority ( v ) = g->priority ( uk ) - 1; //更新优先级(数) g->parent ( v ) = uk; //更新父节点 return;//这里只要有一个节点更新就可以返回 } } }
深度优先搜索,每次新发现的顶点都比父亲节点优先级低。要注意的是,每当有一个节点的优先级发生了改变,就可以直接返回。