《算法设计与分析》第04章
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
【程序4-3】图的深度优先遍历
void Graph::DFS(int u, int* parent, ColorType* color) { color[u]=Gray; cout<<" "<<u; d[u]=time++; //记录第1个 时间 for (ENode* w=a[u]; w; w=w>nextArc){ int v=w->adjVex; if (color[v]==White) { parent[v]=u; DFS(v, parent, color); } } color[u]=Black; f[u]=time++; //记录第2 个时间 }
性质4-1:一个有向图无回路当且仅当在深度优 先搜索中不包含反向边。
性质4-2:一个无向图的深度优先森林中仅包含 树边和反向边。
4.Байду номын сангаас 双连通分量
4.3.1 基本概念(对无向图) 在无向连通图G=(V,E)中,可能存在某个(或多个) 结点a,使得一旦删除a及其相关联的边,图G不再是连通图, 则结点a称为图G的关节点。若删除图G的某条边b,该图分离 成两个非空子图,则称边b是图G的桥。 若无向连通图G中不包含关节点,则称图G 为双连通图 (biconnected graph)。一个无向连通图G的双连通分量 (biconnected component)是图G的极大双连通子图。
4.3.2 发现关节点
无向连通图不是双连通图充分必要条件:图中存在关节点。 识别关节点的简单方法:从图G中删除一个结点a和该结点的 关联边,再检查图G的连通性。 性质4-3:给定无向连通图G=(V,E),S=(V,T)是图G的一颗 深度优先树,图中结点a是一个关节点,当且仅当 (1)a是根,且至少有两个孩子; (2)或者a不是根,且a的某棵子树上没有指向a的祖先的反 向边。
活结点(life node):未检测结点。 死结点(dead node):已检测结点。 活结点表(life node list):需要有一个数据结 构保存活结点 深度优先搜索用堆栈,广度优先搜索用队列 D-搜索( depth search ):若一个搜索算法既按 广度优先搜索方式选择E-结点,又使用堆栈保存 活结点。 标志位(mark bit):图中结点已访问和未访问
推理4-1 后裔区间嵌入
在有向图G的深度优先森林中,结点v是结点u的后
裔当且仅当d[u]<d[v]<f[v]<f[u]。
定理4-3 白色路径定理
在有向图G的深度优先森林中,结点v是结点u的后 裔,当且仅当在时刻d[u],图G中存在一条从u到v的路径, 路径上除u以外的结点都是白色的 。
5.边的分类
void Graph::BFS(int u, int* parent, ColorType* color) {//广度优先搜索算法 Queue<int> q(QSize); color[u]=Gray; cout<<" "<<u;//标记起始结点u 为活结点 q.Append(u); //将起始结点u加 入队列q while (!q.IsEmpty()){ u=q.Front(); q.Serve();//选择一个活结点 为E-结点 for (ENode *w=a[u]; w; w=w->nextArc) { //检测E-结点u的全部 邻接点 int v=w->adjVex; if (color[v]==White){ color[v]=Gray; cout<<" "<<v;
深度优先数:d[u] 最低深度优先数(lowest depth fiest number):low[u]表示从 结点u出发,经过某条路径可以达到深度优先树其他结点的最 低深度优先数。
定义4-1 low[u]定义如下: low[u]=min{ d[u], min{low[w]|w是u的孩子}, min{d[x]|(u,x)是一条反向边} }
protected: void DFS(int u, int* parent, ColorType* color); //递归DFS函数访问 从u可达结点 void BFS(int u, int* parent, ColorType* color); //BFS函数访问从u可 达结点 ENode** a; //生成指向ENode类对 象的指针数组 int n; //图中结点数目 };
第2部分 算法设计策略
第4章 基本搜索和遍历方法
4.1 4.2 4.3 4.4 基本概念 图的搜索和遍历 双连通分量 与或图
4.1 基本概念
搜索(search)
一种通过系统地检查给定数据对象的每个结点,寻找一 条从开始结点到答案结点的路径,最终输出问题解的求解方法。
遍历方法(traversal method)
无 知 搜 索 ( uninformed serach ) ( 盲 目 搜 索 (blind
search) 穷举搜索(brute search) ):最简单的搜索状态
空间树(图)的方法。 有知搜索(informed search):运用已有的知识,克 服盲目性,有效地指导搜索过程,使之尽快到达答案状 态。 启发式搜索(heuristic search):采用经验法则的搜 索方法。 深度优先搜索(depth first search) 广度优先搜索(bread first search) D-搜索( depth search )
4.2 图的遍历和搜索
4.2.1 搜索方法
在树中,一个结点的直接后继结点(direct successor) 是其孩子结点。在图中,一个结点的后继结点是邻接于 该结点的所有邻接点(adjacent node)。 一个结点x若尚未访问,则它处于未访问(unvisitsd) 状态;若x自身已访问,但x的后继结点尚未全部访问, 则称x处于未检测(unexplored)状态; 当算法访问了x 的所有后继结点时,则称x处于已检测(unexplored)状 态; 检测一个结点x指算法从正从x出发,访问x的某个后 继结点y,x被称为扩展结点(being expanded),简称为 E-结点。
i
0
1
2
3
4
5
6
7
d[i]
low[i] 求low值顺序
0
0 8
2
1 5
1
0 7
7
0 6
3
1 4
5
4 2
4
4 3
6
4 1
判断关节点1、2、6: (1) low[6]=4>d[1]=2
(2) low[1]=1≥d[2]=1
(3) low[5]=4≥d[6]=4
(4) 对于0结点:low[2]=0≥d[0]=0 但其只有一个孩子, 所以不是
(1)树边(tree edge)( 从灰到白的边):深度优先森 林的边。 (2)反向边(back edge)( 从灰到灰的边):深度优 先树中从后裔到祖先的边,环也被认为是反向边。 (3)正向边(forward edge)( 从灰到黑的边):深度 优先树中从祖先到后裔的非树边。 (4)交叉边(cross edge)( 从灰到黑的边):其余的 边。它们可以连接同一棵深度优先树中的两个结点, 只要一个结点不是另一个结点的祖先,也可以连接分 属两棵深度优先树的结点。
2.广度优先树 概念 双亲(parent) 祖先(ancestor) 后裔(descendent) 广度优先森林
3.时间分析—用邻接表O(n+e),用邻接矩阵O(n2) 4.BFS算法证明—归纳法
4.2.4 深度优先搜索
1.深度优先遍历算法 时间戳(time stamp) d[u](第1个时间):白色u着灰色时 f [u](第2个时间) :灰色u着黑色时 初始时,所有u,d[u]=f[u]=0
【程序4-5】求双连通分量 void Graph::BiCom(int u, int p) { Low[u]=d[u]=time++; eNode e; for (ENode* w=a[u]; w; w=w->nextArc){ int v=w->adjVex; e.u=u; e.v=v; if(v!=p && d[v]<d[u]) s.Push(e); //边进栈 if (d[v]==-1) { BiCom(v, u); if(Low[v]>=d[u]){ cout<<endl<<"New bicommponent\n"; do {e=s.Top(); s.Pop(); if(u<v && e.u>e.v)Swap(e.u, e.v); else if(u>v && e.u<e.v)Swap(e.u,e.v);
【程序4-4】计算d和Low
void Graph::DFS(int u, int p) {//u是起始结点,p是u的双亲结点 Low[u]=d[u]=time++; //Low[u]=d[u] for (ENode* w=a[u]; w; w=w->nextArc){ int v=w->adjVex; if (d[v]==-1) { //表示v尚未访问 DFS(v, u); if (Low[u]>Low[v]) Low[u]=Low[v]; //<u, v>是树边 } else if (v!=p && Low[u]>d[v]) Low[u]=d[v] //<u, v>是反向边 }
2.深度优先树
3.时间分析—用邻接表O(n+e),用邻接矩阵O(n2)
4.深度优先搜索的性质 定理4-2 括号定理
在对有向图或无向图G=(V,E)的任何深度优先搜索 中,对于图中任意两结点u和v,下述3个条件中有且仅有
1条成立: (1)区间[d[u],f[u]]和[d[v],f[v]]是完全分离的,且 在深度优先森林中, u和v互不为后裔 ; (2)区间[d[u],f[u]]完全包含区间[d[v],f[v]],且在 深度优先树中v是 u的后裔; (3)区间[d[v],f[v]]完全包含区间[d[u],f[u]],且在 深度优先树中 u是 v的后裔。
系统地检查给定数据对象的每个结点。根据被遍历的数据 对象的结构不同,可分成树遍历和图遍历。
遍历(traversal )
遵循某种次序,系统地访问一个数据结构的全部元素,并 且每个元素仅访问一次,这种运算称为遍历。
状态空间(state space):描述所求问题的各种 可能的情况,每种情况对应于状态空间中的一个状 态。常用一颗树或一个图来表示。 初始状态(start state):一种特殊情况,代表搜 索开始。 目 标 状 态 ( goal state ) 或 答 案 状 态 ( answer state):一个或多个状态代表已经求得问题解的情 况。
4.2.3 广度优先搜索
1.广度优先遍历算法
【程序4-2】图的广度优先遍历
void Graph::BFS_Traversal(int* parent) {//遍历算法将在parent数组中返回以双亲表示法 表示的BFS生成森林 ColorType* color=new ColorType[n]; // 颜色数组 cout<<endl<<"BFS:"; for(int u=0; u<n; u++){ color[u]=White; parent[u]=-1; } for (u=0; u<n; u++) if (color[u]==White) BFS(u, parent, color); //从未标记的结点出发 进行BFS delete[] color; cout<<endl;
广度优先搜索(bread first search):若对于一个 未检测结点,一个搜索算法必定在访问它的全部后继 结点后(使得该E-结点称为已检测结点后),才另选 一个未检测结点(作为扩展结点),检测它。 深度优先搜索(depth first search):若一个算法 一旦访问某个结点,该结点成为未检测结点后,便立 即被算法检测,成为E-结点,而此时,原E-结点尚未 检测完毕,仍处于未检测状态,需在以后适当时候才 得以继续检测。
4.2.2 邻接表类
【程序4-1】 边结点 ENode类 enum ColorType{White, Gray, Black}; struct ENode { int adjVex; ENode* nextArc; };
class Graph \\邻接表类 { public: Graph(int mSize) {//构造仅有n个结点的图的邻接表 n=mSize; a=new ENode* [n]; for (int i=0; i<n; i++) a[i]=NULL; } void DFS_Traversal(int* parent); //一维数组parent保存DFS生 成森林 void BFS_Traversal(int* prarent); //一维数组parent保存BFS生 成森林