图搜索
1. 介绍
本文介绍了比较初级的图搜索算法,包括深度优先遍历,广度优先遍历和双向广度优先遍历。
2. 深度优先遍历DFS
2.1 算法思想
从图中某个顶点v开始,访问此节点,然后依次从v中未被访问的邻接点出发深度优先遍历图,直到图中上所有和v有路径相通的顶点都被访问;若此时图中尚有顶点未被访问,则另选图中一个未被访问顶点做起点,重复以上过程,直到图中所有顶点都被访问为止。
深度优先搜索遍历类似于树的先序遍历。假定给定图G的初态是所有顶点均未被访问过,在G中任选一个顶点i作为遍历的初始点,则深度优先搜索遍历可定义如下:
(1) 首先访问顶点i,并将其访问标记置为访问过,即visited[i]=1;
(2) 然后搜索与顶点i有边相连的下一个顶点j,若j未被访问过,则访问它,并将j的访问标记置为访问过,visited[j]=1,然后从j开始重复此过程,若j已访问,再看与i有边相连的其它顶点;
(3) 若与i有边相连的顶点都被访问过,则退回到前一个访问顶点并重复刚才过程,直到图中所有顶点都被访问完止。
2.2 算法实现
邻接矩阵的算法描述为下面形式:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void dfs1 (graph & g, int i, int n) // 从顶点i 出发遍历
{
cout<<g.v[i]; //输出访问顶点
visited[i]=1; //访问标记置1表示已经访问
for(j=1; j<=n; j++)
if ((g.arcs[i ][j]= =1)&&(!visited[j]))
dfs(g,j,n);
}
邻接表算法描述为下面形式:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void dfs2(adjlist GL,int i, int n)
{
cout<<i<<‘’ ; //输出访问顶点
visted[i]=1; //访问标记置为1表示已访问
edgenode * p=GL[i];
while (p!=NULL)
{
if (!visited[p->adjvex])
dfs2(p->adjvex);
p=p->next;
}
}
2.3 适用范围
需要有顺序遍历图,且找到一个解后输出即可。如:trie树排序
多用于只要求解,并且解答树中的重复节点较多并且重复较难判断时使用,但往往可以用A*或回溯算法代替。
2.4 举例
数独求解。给出一个不完整的数独,让填充其中空白的数字。
更多题目:
POJ 1321 棋盘问题:http://www.cublog.cn/u3/105033/showart_2212140.html
类迷宫问题:http://www.jguoer.com/post/2010/02/17/DFS-Code.aspx
数独问题:http://acm.hdu.edu.cn/showproblem.php?pid=1426
3. 广度优先遍历BFS
3.1 算法思想
广度优先搜索遍历类似于树的按层次遍历。设图G的初态是所有顶点均未访问,在G 任选一顶点i作为初始点,则广度优先搜索的基本思想是:
(1)首先访问顶点i,并将其访问标志置为已被访问,即visited[i]=1;
(2)接着依次访问与顶点i有边相连的所有顶点W1,W2,…,Wt;
(3)然后再按顺序访问与W1,W2,…,Wt有边相连又未曾访问过的顶点;
依此类推,直到图中所有顶点都被访问完为止。
3.2 算法实现
用邻接矩阵的算法描述如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
void bfs1( graph g, int i) //从顶点i出发遍历
{
Queue Q ; //Q为队列
InitQueue(Q)
cout<<g.v[i] ; // 输出访问顶点
visited[i]=1 ; //标记置1表示已经访问
Qinsert(Q,i) ; //入队列
while (!Queueempty(Q))
{
int k=Qdelete(Q);
for (j=0; j<n; j++)
{
if ((g.a[i][j]==1)&&(!visited[j]))
{
cout<<g.v[j];
visited[j]=1 ;
Qinsert(Q,i) ;
}
}
}
}
用邻接矩阵的算法描述如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
void bfs2(adjlist GL, int i, int n)
{
Queue Q ;
InitQueue(Q); //定义队列
cout<<i<<‘’;
visited[i]=1;
Qinsert(Q,i) //进队
while (!QueueEmpty(Q))
{
int k=Qdelete(Q) ; //出队
edgenode* p=GL[k];
while (p!=NULL)
{
if (!visited[p->adjvex])
{
cout<<j;
visited[p->data]=1;
Qinsert(Q);
}
p=p->next;
}
}
}
3.3 适用范围
求问题的最优解
3.4 举例
给定一个8*8的格子地图,再给定初始状态和终止状态,输出从初始点到达终止点的最少步数。
更多题目:
http://www.cppblog.com/firstnode/archive/2009/03/07/75839.html
http://blog.sina.com.cn/s/blog_6635898a0100hwe3.html
http://blog.csdn.net/super_chris/archive/2009/12/26/5082666.aspx
http://www.chenyajun.com/2010/05/08/4540
4. 双向广度优先遍历
4.1 算法思想
有些问题按照广度优先搜索法则扩展结点的规则,既适合顺序,也适合逆序,于是我们考虑在寻找目标结点或路径的搜索过程中,初始结点向目标结点和目标结点向初始结点同时进行扩展,直至在两个扩展方向上出现同一个子结点,搜索结束,这就是双向搜索过程。
出现的这个同一子结点,我们称为相交点,如果确实存在一条从初始结点到目标结点的最佳路径,那么按双向搜索进行搜索必然会在某层出现“相交”,即有相交点,初始结点一相交点一目标结点所形成的一条路径即是所求路径。
4.2 算法实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
now = 0;
Q.push(st); RQ.push(ed);
mark[st] = true; Rmark[ed] = true;
while(!Q.empty() && !RQ.empty())
{ // 两边的扩展方式须为按层扩展
while(Q.front().step == now)
{ //step表示节点的层数
nextState = extend(Q.front); t
if(mark[nextState]) continuel
if(Rmark[nextState]) return true;
}
while(RQ.front().step == now)
{
nextState = extend(RQ.front);
if(Rmark[nextState]) continuel
if(mark[nextState]) return true;
}
now++;
}
4.3 适用范围
最优化问题中,知道问题的起始状态和最终状态,且两个状态可相互到达。
4.4 举例
棋盘上有四个棋子,给你两个状态,问可否8步内将一个状态转移到另一个状态?
5. DFS与BFS比较
数据结构:DFS采用栈,而BFS采用队列
算法思想:深度搜索与广度搜索的控制结构和产生系统很相似,唯一的区别在于对扩展节点选取上。由于其保留了所有的前继节点,所以在产生后继节点时可以去掉一部分重复的节点,从而提高了搜索效率。这两种算法每次都扩展一个节点的所有子节点,而不同的是,深度搜索下一次扩展的是本次扩展出来的子节点中的一个,而广度搜索扩展的则是本次扩展的节点的兄弟节点
使用范围:DFS可以迅速的找到一个解,然后利用这个解进行剪枝,而BFS可找到最优解。
原创文章,转载请注明: 转载自董的博客
本文链接地址: http://dongxicheng.org/structure/basic-graph-search/
本文介绍了比较初级的图搜索算法,包括深度优先遍历,广度优先遍历和双向广度优先遍历。
2. 深度优先遍历DFS
2.1 算法思想
从图中某个顶点v开始,访问此节点,然后依次从v中未被访问的邻接点出发深度优先遍历图,直到图中上所有和v有路径相通的顶点都被访问;若此时图中尚有顶点未被访问,则另选图中一个未被访问顶点做起点,重复以上过程,直到图中所有顶点都被访问为止。
深度优先搜索遍历类似于树的先序遍历。假定给定图G的初态是所有顶点均未被访问过,在G中任选一个顶点i作为遍历的初始点,则深度优先搜索遍历可定义如下:
(1) 首先访问顶点i,并将其访问标记置为访问过,即visited[i]=1;
(2) 然后搜索与顶点i有边相连的下一个顶点j,若j未被访问过,则访问它,并将j的访问标记置为访问过,visited[j]=1,然后从j开始重复此过程,若j已访问,再看与i有边相连的其它顶点;
(3) 若与i有边相连的顶点都被访问过,则退回到前一个访问顶点并重复刚才过程,直到图中所有顶点都被访问完止。
2.2 算法实现
邻接矩阵的算法描述为下面形式:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void dfs1 (graph & g, int i, int n) // 从顶点i 出发遍历
{
cout<<g.v[i]; //输出访问顶点
visited[i]=1; //访问标记置1表示已经访问
for(j=1; j<=n; j++)
if ((g.arcs[i ][j]= =1)&&(!visited[j]))
dfs(g,j,n);
}
邻接表算法描述为下面形式:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void dfs2(adjlist GL,int i, int n)
{
cout<<i<<‘’ ; //输出访问顶点
visted[i]=1; //访问标记置为1表示已访问
edgenode * p=GL[i];
while (p!=NULL)
{
if (!visited[p->adjvex])
dfs2(p->adjvex);
p=p->next;
}
}
2.3 适用范围
需要有顺序遍历图,且找到一个解后输出即可。如:trie树排序
多用于只要求解,并且解答树中的重复节点较多并且重复较难判断时使用,但往往可以用A*或回溯算法代替。
2.4 举例
数独求解。给出一个不完整的数独,让填充其中空白的数字。
更多题目:
POJ 1321 棋盘问题:http://www.cublog.cn/u3/105033/showart_2212140.html
类迷宫问题:http://www.jguoer.com/post/2010/02/17/DFS-Code.aspx
数独问题:http://acm.hdu.edu.cn/showproblem.php?pid=1426
3. 广度优先遍历BFS
3.1 算法思想
广度优先搜索遍历类似于树的按层次遍历。设图G的初态是所有顶点均未访问,在G 任选一顶点i作为初始点,则广度优先搜索的基本思想是:
(1)首先访问顶点i,并将其访问标志置为已被访问,即visited[i]=1;
(2)接着依次访问与顶点i有边相连的所有顶点W1,W2,…,Wt;
(3)然后再按顺序访问与W1,W2,…,Wt有边相连又未曾访问过的顶点;
依此类推,直到图中所有顶点都被访问完为止。
3.2 算法实现
用邻接矩阵的算法描述如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
void bfs1( graph g, int i) //从顶点i出发遍历
{
Queue Q ; //Q为队列
InitQueue(Q)
cout<<g.v[i] ; // 输出访问顶点
visited[i]=1 ; //标记置1表示已经访问
Qinsert(Q,i) ; //入队列
while (!Queueempty(Q))
{
int k=Qdelete(Q);
for (j=0; j<n; j++)
{
if ((g.a[i][j]==1)&&(!visited[j]))
{
cout<<g.v[j];
visited[j]=1 ;
Qinsert(Q,i) ;
}
}
}
}
用邻接矩阵的算法描述如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
void bfs2(adjlist GL, int i, int n)
{
Queue Q ;
InitQueue(Q); //定义队列
cout<<i<<‘’;
visited[i]=1;
Qinsert(Q,i) //进队
while (!QueueEmpty(Q))
{
int k=Qdelete(Q) ; //出队
edgenode* p=GL[k];
while (p!=NULL)
{
if (!visited[p->adjvex])
{
cout<<j;
visited[p->data]=1;
Qinsert(Q);
}
p=p->next;
}
}
}
3.3 适用范围
求问题的最优解
3.4 举例
给定一个8*8的格子地图,再给定初始状态和终止状态,输出从初始点到达终止点的最少步数。
更多题目:
http://www.cppblog.com/firstnode/archive/2009/03/07/75839.html
http://blog.sina.com.cn/s/blog_6635898a0100hwe3.html
http://blog.csdn.net/super_chris/archive/2009/12/26/5082666.aspx
http://www.chenyajun.com/2010/05/08/4540
4. 双向广度优先遍历
4.1 算法思想
有些问题按照广度优先搜索法则扩展结点的规则,既适合顺序,也适合逆序,于是我们考虑在寻找目标结点或路径的搜索过程中,初始结点向目标结点和目标结点向初始结点同时进行扩展,直至在两个扩展方向上出现同一个子结点,搜索结束,这就是双向搜索过程。
出现的这个同一子结点,我们称为相交点,如果确实存在一条从初始结点到目标结点的最佳路径,那么按双向搜索进行搜索必然会在某层出现“相交”,即有相交点,初始结点一相交点一目标结点所形成的一条路径即是所求路径。
4.2 算法实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
now = 0;
Q.push(st); RQ.push(ed);
mark[st] = true; Rmark[ed] = true;
while(!Q.empty() && !RQ.empty())
{ // 两边的扩展方式须为按层扩展
while(Q.front().step == now)
{ //step表示节点的层数
nextState = extend(Q.front); t
if(mark[nextState]) continuel
if(Rmark[nextState]) return true;
}
while(RQ.front().step == now)
{
nextState = extend(RQ.front);
if(Rmark[nextState]) continuel
if(mark[nextState]) return true;
}
now++;
}
4.3 适用范围
最优化问题中,知道问题的起始状态和最终状态,且两个状态可相互到达。
4.4 举例
棋盘上有四个棋子,给你两个状态,问可否8步内将一个状态转移到另一个状态?
5. DFS与BFS比较
数据结构:DFS采用栈,而BFS采用队列
算法思想:深度搜索与广度搜索的控制结构和产生系统很相似,唯一的区别在于对扩展节点选取上。由于其保留了所有的前继节点,所以在产生后继节点时可以去掉一部分重复的节点,从而提高了搜索效率。这两种算法每次都扩展一个节点的所有子节点,而不同的是,深度搜索下一次扩展的是本次扩展出来的子节点中的一个,而广度搜索扩展的则是本次扩展的节点的兄弟节点
使用范围:DFS可以迅速的找到一个解,然后利用这个解进行剪枝,而BFS可找到最优解。
原创文章,转载请注明: 转载自董的博客
本文链接地址: http://dongxicheng.org/structure/basic-graph-search/