[CLRS Chapter22]基本的图算法Ⅱ深度优先搜索原理角度实现&性质证明(括号化定理、白色路径定理)
广度优先树首先给定图GGG和目标搜索点sss的情况下,定义前驱子图Gπ=(Vπ,Eπ)G_\pi=(V_\pi,E_\pi)Gπ=(Vπ,Eπ),其中点集Vπ={v∈V∣v.π≠NIL}⋃{s}V_\pi=\{v\in V|v.\pi\ne NIL\}\bigcup\{s\}Vπ={v∈V∣v.π=NIL}⋃{s},Eπ={(v.π,v)∣v∈V−{s}}E_\pi=\{(v.\pi,
广度优先树
首先给定图GGG和目标搜索点sss的情况下,定义前驱子图Gπ=(Vπ,Eπ)G_\pi=(V_\pi,E_\pi)Gπ=(Vπ,Eπ),其中点集Vπ={v∈V∣v.π≠NIL}⋃{s}V_\pi=\{v\in V|v.\pi\ne NIL\}\bigcup\{s\}Vπ={v∈V∣v.π=NIL}⋃{s},Eπ={(v.π,v)∣v∈V−{s}}E_\pi=\{(v.\pi,v)|v\in V-\{s\}\}Eπ={(v.π,v)∣v∈V−{s}}。
可以看出前驱子图表达的是由目标点出发进行搜索,过程中组成的从中心向外的辐射结构,因为边是否在前驱子图内都由点的前驱所决定,如果前驱子图是一棵树而不是森林(不含孤立点,任意点都有最短路包含在前驱子图中),那么前驱子图是一棵广度优先树。
引理6. 有/无向图G=(V,E)G=(V,E)G=(V,E)中,BFSBFSBFS在每个节点所构建的π\piπ属性使得前驱子图成为一棵广度优先树。
v.π=uv.\pi=uv.π=u存在当且仅当该前驱到该点的边在图中存在,且该点定义距离目标点不是无穷,所以如果vvv可以由目标点到达,那么这些点vvv就共同构成了VπV_\piVπ。通过递归运用上节的广搜准确性定理获得了路径是图中的最短路径。
广度优先树的计算也是递归利用上述定理所完成的:
void Graph::PrintPath(char s, char v)
{
if (v == s)
cout << s << ' ';
else if (G[index[v]]._pi == nullptr)
cout << "NO Path" << endl;
else
{
PrintPath(s, G[index[v]]._pi->_name);
cout << v << ' ';
}
}
利用前驱性质最终输出目标点到输入点的节点路径。
DFS(深度优先搜索)
深度优先搜索中定义了另一种前驱子图,其中改动是深搜的点集不包含搜索目标点,这与广搜宽搜的应用场景有很大关系:
- 广搜的应用更多的是查找距离某点最近的距离;
- 而宽搜的目的是作为子程序找到一个答案使其最优;
所以前者搜索从一个目标点出发搜索到数据就结束,而后者更多的是全图搜索,图很可能不是连续的,这也意味着有多个搜索目标点。
搜索算法原理实现
搜索中还需要再每个节点添加“时间戳”表示什么时候开始当前节点、什么时候回溯到当前节点,具体的需要做:
- 当前所有节点颜色定义为白色;
- 从任意一个点开始搜索,开始时将当前节点状态记录开始时间戳并赋为灰色;
- 在灰色节点的白色相邻节点开始继续调用搜索函数;
- 当程序回溯到某个灰色节点时赋为黑色并记录结束时间戳;
可以发现每个节点只有一次机会被搜索开始和搜索回溯,所以时间戳最大值为2∣V∣2|V|2∣V∣且开始时间戳u.du.du.d和结束时间戳u.fu.fu.f满足u.d<u.fu.d<u.fu.d<u.f。
并且可以得到在时间轴上:
- t<u.dt<u.dt<u.d时节点未被搜索为白色
- t<u.dt<u.dt<u.d时节点的临近节点正在被搜索为灰色
- t>u.dt>u.dt>u.d时节点已被搜索为黑色
程序实现为:
void Graph::DFS()
{
for (auto i : G)
{
i._color = WHITE;
i._pi = nullptr;
}
time = 0;
for (auto i : G)
if (i._color == WHITE)
DFSVisit(i._name);
}
void Graph::DFSVisit(char u)
{
G[index[u]]._d = ++time;
G[index[u]]._color = GRAY;
for (auto v = G[index[u]]._first; v; v = v->NEXT)
if (G[index[v->adjvex]]._color == WHITE)
{
G[index[v->adjvex]]._pi = &G[index[u]];
DFSVisit(v->adjvex);
}
G[index[u]]._color = BLACK;
G[index[u]]._f = ++time;
}
上半部分DFS()
方法主要任务是将节点初始化且搜索完整个图,图中存在不连通的子图造成单点搜索不能覆盖全图;下半部分DFSVisit()
方法阐述了上面搜索步骤。
相关性质
由下图的搜索(先进入sss进行搜索在进入ttt)可以发现在时间轴上搜索过程描述了一种括号化的结构。
[定理]括号化定理:在有向或无向图GGG中进行深搜,对于搜索过程中的任意两个节点
u
和v
满足三种情况之一:
- [u.d,u.f][u.d,u.f][u.d,u.f]与[v.d,v.f][v.d,v.f][v.d,v.f]区间不重合,两个节点不存在父子关系;
- [u.d,u.f][u.d,u.f][u.d,u.f]在[v.d,v.f][v.d,v.f][v.d,v.f]区间内部,uuu是vvv的前驱子图某棵树(也有可能是只有一棵树)中的后代;
- 第二种情况的镜像;
当u.d<v.du.d<v.du.d<v.d情况下有两种情况:
- 当v.d<u,fv.d<u,fv.d<u,f的时候
v
在搜索中的时候u
仍为灰色,说明v
是其后代且u
的时间戳结束时v
的状态是已经被搜索完毕,所以v.f<u.fv.f<u.fv.f<u.f证明其是子区间且为后代; - 当v.d>u.fv.d>u.fv.d>u.f时,根据不等式有u.d<u.f<v.d<v.fu.d<u.f<v.d<v.fu.d<u.f<v.d<v.f,得到两区间完全分离;
镜像情况类似。
[推论]相应的可以类推到
v
是u
的后代当且仅当u.d<v.d<v.f<u.fu.d<v.d<v.f<u.fu.d<v.d<v.f<u.f不等式存在时成立。
[定理]白色路径定理:在有向或无向图GGG中进行深搜,
u
是v
的后代当且仅当在时刻u.du.du.d存在一条白色节点组成的路径从u
到v
。
⇒\Rightarrow⇒:u=vu=vu=v时刻,算法设计中先定义时间再覆盖灰色所以自身还是白色成立;vvv为任意后代都满足上述类推u.d<v.du.d<v.du.d<v.d那么后续节点都为白色。
⇐\Leftarrow⇐:假定vvv不为uuu的后代且是搜索之后第一个不是uuu后代的节点,但存在一条白色路径,这样的话设w=v.πw=v.\piw=v.π,那么有w.f≤u.fw.f\le u.fw.f≤u.f(推论成立,等号存在当且仅当u=wu=wu=w),因为w=v.πw=v.\piw=v.π所以作为前驱要满足v.f<w.fv.f<w.fv.f<w.f同理推得v
的时间戳区间被限制在u
的区间内则满足定理的条件。

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐
所有评论(0)