个人网站 不用备案,二建注册信息查询系统官网,做国外网站做外贸,做网站需要那些软件深度优先遍历过程 1、图的遍历 和树的遍历类似#xff0c;图的遍历也是从某个顶点出发#xff0c;沿着某条搜索路径对图中每个顶点各做一次且仅做一次访问。它是许多图的算法的基础。 深度优先遍历和广度优先遍历是最为重要的两种遍历图的方法。它们对无向图和有… 深度优先遍历过程 1、图的遍历 和树的遍历类似图的遍历也是从某个顶点出发沿着某条搜索路径对图中每个顶点各做一次且仅做一次访问。它是许多图的算法的基础。 深度优先遍历和广度优先遍历是最为重要的两种遍历图的方法。它们对无向图和有向图均适用。 注意 以下假定遍历过程中访问顶点的操作是简单地输出顶点。 2、布尔向量visited[0n-1]的设置 图中任一顶点都可能和其它顶点相邻接。在访问了某顶点之后又可能顺着某条回路又回到了该顶点。为了避免重复访问同一个顶点必须记住每个已访问的顶点。为此可设一布尔向量visited[0n-1]其初值为假一旦访问了顶点Vi之后便将visited[i]置为真。 深度优先遍历(Depth-First Traversal) 1图的深度优先遍历的递归定义 假设给定图G的初态是所有顶点均未曾访问过。在G中任选一顶点v为初始出发点(源点)则深度优先遍历可定义如下首先访问出发点v并将其标记为已访问过然后依次从v出发搜索v的每个邻接点w。若w未曾访问过则以w为新的出发点继续进行深度优先遍历直至图中所有和源点v有路径相通的顶点(亦称为从源点可达的顶点)均已被访问为止。若此时图中仍有未访问的顶点则另选一个尚未访问的顶点作为新的源点重复上述过程直至图中所有顶点均已被访问为止。 图的深度优先遍历类似于树的前序遍历。采用的搜索方法的特点是尽可能先对纵深方向进行搜索。这种搜索方法称为深度优先搜索(Depth-First Search)。相应地用此方法遍历图就很自然地称之为图的深度优先遍历。 2、深度优先搜索的过程 设x是当前被访问顶点在对x做过访问标记后选择一条从x出发的未检测过的边(xy)。若发现顶点y已访问过则重新选择另一条从x出发的未检测过的边否则沿边(xy)到达未曾访问过的y对y访问并将其标记为已访问过然后从y开始搜索直到搜索完从y出发的所有路径即访问完所有从y出发可达的顶点之后才回溯到顶点x并且再选择一条从x出发的未检测过的边。上述过程直至从x出发的所有边都已检测过为止。此时若x不是源点则回溯到在x之前被访问过的顶点否则图中所有和源点有路径相通的顶点(即从源点可达的所有顶点)都已被访问过若图G是连通图则遍历过程结束否则继续选择一个尚未被访问的顶点作为新源点进行新的搜索过程。 3、深度优先遍历的递归算法 1深度优先遍历算法 typedef enum{FALSETRUE}Boolean//FALSE为0TRUE为1 Boolean visited[MaxVertexNum] //访问标志向量是全局量 void DFSTraverse(ALGraph *G) { //深度优先遍历以邻接表表示的图G而以邻接矩阵表示G时算法完全与此相同 int i for(i0;iG-n;i) visited[i]FALSE //标志向量初始化 for(i0;iG-ni) if(!visited[i]) //vi未访问过 DFS(Gi) //以vi为源点开始DFS搜索 }//DFSTraverse 2邻接表表示的深度优先搜索算法 void DFS(ALGraph *Gint i){ //以vi为出发点对邻接表表示的图G进行深度优先搜索 EdgeNode *p printf(visit vertexcG-adjlist[i].vertex)//访问顶点vi visited[i]TRUE //标记vi已访问 pG-adjlist[i].firstedge //取vi边表的头指针 while(p){//依次搜索vi的邻接点vj这里jp-adjvex if (!visited[p-adjvex])//若vi尚未被访问 DFS(Gp-adjvex);//则以Vj为出发点向纵深搜索 pp-next //找vi的下一邻接点 } }//DFS 3邻接矩阵表示的深度优先搜索算法 void DFSM(MGraph *Gint i) { //以vi为出发点对邻接矩阵表示的图G进行DFS搜索设邻接矩阵是0,l矩阵 int j printf(visit vertexcG-vexs[i])//访问顶点vi visited[i]TRUE for(j0jG-nj) //依次搜索vi的邻接点 if(G-edges[i][j]1!visited[j]) DFSM(Gj)//(vivj)∈E且vj未访问过故vj为新出发点 }//DFSM 注意 遍历操作不会修改图G的内容故上述算法中可将G定义为ALGraph或MGraph类型的参数而不一定要定义为ALGraph和MGraph的指针类型。但基于效率上的考虑选择指针类型的参数为宜。 4、深度优先遍历序列 对图进行深度优先遍历时按访问顶点的先后次序得到的顶点序列称为该图的深度优先遍历序列或简称为DFS序列。 1一个图的DFS序列不一定惟一 当从某顶点x出发搜索时若x的邻接点有多个尚未访问过则我们可任选一个访问之。 2源点和存储结构的内容均已确定的图的DFS序列惟一 ① 邻接矩阵表示的图确定源点后DFS序列惟一 DFSM算法中当从vi出发搜索时是在邻接矩阵的第i行上从左至右选择下一个未曾访问过的邻接点作为新的出发点若这样的邻接点多于一个则选中的总是序号较小的那一个。 ②只有给出了邻接表的内容及初始出发点才能惟一确定其DFS序列 邻接表作为给定图的存储结构时其表示不惟一。因为邻接表上边表里的邻接点域的内容与建表时的输入次序相关。 因此只有给出了邻接表的内容及初始出发点才能惟一确定其DFS序列。 3栈在深度优先遍历算法中的作用 深度优先遍历过程中后访问的顶点其邻接点被先访问故在递归调用过程中使用栈系统运行时刻栈来保存已访问的顶点。 5、算法分析 对于具有n个顶点和e条边的无向图或有向图遍历算法DFSTraverse对图中每顶点至多调用一次DFS或DFSM。从DFSTraverse中调用DFS(或DFSM)及DFS(或DFSM)内部递归调用自己的总次数为n。 当访问某顶点vi时DFS(或DFSM)的时间主要耗费在从该顶点出发搜索它的所有邻接点上。用邻接矩阵表示图时其搜索时间为O(n)用邻接表表示图时需搜索第i个边表上的所有结点。因此对所有n个顶点访问在邻接矩阵上共需检查n2个矩阵元素在邻接表上需将边表中所有O(e)个结点检查一遍。 所以DFSTraverse的时间复杂度为O(n2) 调用DFSM或0(ne)调用DFS。 广度优先遍历过程 1、广度优先遍历的递归定义 设图G的初态是所有顶点均未访问过。在G中任选一顶点v为源点则广度优先遍历可以定义为首先访问出发点v接着依次访问v的所有邻接点w1w2…wt然后再依次访问与wlw2…wt邻接的所有未曾访问过的顶点。依此类推直至图中所有和源点v有路径相通的顶点都已访问到为止。此时从v开始的搜索过程结束。 若G是连通图则遍历完成否则在图C中另选一个尚未访问的顶点作为新源点继续上述的搜索过程直至G中所有顶点均已被访问为止。 广度优先遍历类似于树的按层次遍历。采用的搜索方法的特点是尽可能先对横向进行搜索故称其为广度优先搜索(Breadth-FirstSearch)。相应的遍历也就自然地称为广度优先遍历。2、广度优先搜索过程 在广度优先搜索过程中设x和y是两个相继要被访问的未访问过的顶点。它们的邻接点分别记为x1x2…xs和y1y2…yt。 为确保先访问的顶点其邻接点亦先被访问在搜索过程中使用FIFO队列来保存已访问过的顶点。当访问x和y时这两个顶点相继入队。此后当x和y相继出队时我们分别从x和y出发搜索其邻接点x1x2…xs和y1y2…yt对其中未访者进行访问并将其人队。这种方法是将每个已访问的顶点人队故保证了每个顶点至多只有一次人队。 3、广度优先搜索算法 (1)邻接表表示图的广度优先搜索算法 void BFS(ALGraph*Gint k) {// 以vk为源点对用邻接表表示的图G进行广度优先搜索 int i CirQueue Q //须将队列定义中DataType改为int EdgeNode *p InitQueue(Q)//队列初始化 //访问源点vk printf(visit vertexeG-adjlist[k].vertex) visited[k]TRUE EnQueue(Qk)//vk已访问将其人队。实际上是将其序号人队 while(!QueueEmpty(Q)){//队非空则执行 iDeQueue(Q) //相当于vi出队 pG-adjlist[i].firstedge //取vi的边表头指针 while(p){//依次搜索vi的邻接点vj(令p-adjvexj) if(!visited[p-adivex]){ //若vj未访问过 printf(visitvertexcC-adjlistlp-adjvex].vertex) //访问vj visited[p-adjvex]TRUE EnQueue(Qp-adjvex)//访问过的vj人队 }//endif pp-next//找vi的下一邻接点 }//endwhile }//endwhile }//end of BFS 2邻接矩阵表示的图的广度优先搜索算法 void BFSM(MGraph *Gint k) {以vk为源点对用邻接矩阵表示的图G进行广度优先搜索 int ij CirQueue Q InitQueue(Q) printf(visit vertexcG-vexs[k]) //访问源点vk visited[k]TRUE EnQueue(Qk) while(!QueueEmpty(Q)){ iDeQueue(Q) //vi出队 for(j0;jG-n;j)//依次搜索vi的邻接点vj if(G-edges[i][j]1!visited[j]){//vi未访问 printf(visit vertexcG-vexs[j])//访问vi visited[j]TRUE EnQueue(Qj)//访问过的vi人队 } }//endwhile }//BFSM 3广度优先遍历算法 类似于DFSTraverse。【参见DFSTraverse算法】 4、图的广度优先遍历序列 广度优先遍历图所得的顶点序列定义为图的广度优先遍历序列简称BFS序列。 1一个图的BFS序列不是惟一的 2给定了源点及图的存储结构时算法BFS和BFSM所给出BFS序列就是惟一的。 5、算法分析 对于具有n个顶点和e条边的无向图或有向图每个顶点均入队一次。广度优先遍历(BFSTraverse)图的时间复杂度和DFSTraverse算法相同。 当图是连通图时BFSTraverse算法只需调用一次BFS或BFSM即可完成遍历操作此时BFS和BFSM的时间复杂度分别为O(ne)和0(n2)。 图的深度优先和广度优先遍历算法 #include stdio.h #include stdlib.h #define MaxN 30 /*图中定点数的最大值*/ typedef struct ArcNode /*邻接链表的表节点*/ { int adjvvex; /*邻接顶点的顶点序号*/ double weight; /*边上的权值*/ struct ArcNode *nextarc; /*下一个邻接定点*/ }EdgeNode; typedef struct VNode /*邻接链表的头节点*/ { char data; /*定点表示的数据以一个字符表示*/ struct ArcNode *firstarc; /*指向第一个依附于该定点的边的指针*/ }AdjList[MaxN]; typedef struct { int Vnum; /*图中定点的数目*/ AdjList Vertices; }Graph; int visited[MaxN]{0}; /*调用遍历算法前所有的定都没有被访问过*/ void Dfs(Graph G,int i) { EdgeNode *t; int j; printf(%d,i); /*访问序号为i的顶点*/ visited[i]1; /*序号为i的定点已访问过*/ tG.Vertices[i].firstarc; /*取定点i的第一个邻接定点*/ while (t!NULL) /*检查所有与顶点i相邻接的顶点*/ { jt-adjvex; /*顶点j为顶点i的一个邻接顶点*/ if(visited[j]0) /*若顶点j未被访问过*/ Dfs(g,j); /*从顶点j出发进行深度优先搜索*/ tt-nextarc; /*取顶点i的下一个邻接顶点*/ } } void Bfs(Graph G) /*广度优先遍历图G*/ { EdgeNode *t; int i,j,k; int visted[G.Vnum]{0}; /*调用遍历算法前所有的定都没有被访问过*/ InitQueue(Q); /*创建一个空队列*/ for (i0;iG.Vnum ;i ) { if (!visit[i]) { EnQueue(Q,i); /*访问顶点i*/ printf(%d,j); visited[i]1; while (!Empty(Q)) { Dequeue(Q,k); for (tG.Vertices[k].firstarc ; t ;tt-nextarc ) { jt-adjvex; if (visted[j]0) { EnQueue(Q,j); printf(%d,j); visited[j]1; } } } } } } 深度优先搜索 现在我们用堆栈解决一个有意思的问题定义一个二维数组 int maze[5][5] { 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, }; 它表示一个迷宫其中的1表示墙壁0表示可以走的路只能横着走或竖着走不能斜着走要求编程序找出从左上角到右下角的路线。程序如下 例 12.3. 用深度优先搜索解迷宫问题 #include stdio.h #define MAX_ROW 5 #define MAX_COL 5 struct point { int row, col; } stack[512]; int top 0; void push(struct point p) { stack[top] p; top; } struct point pop(void) { top--; return stack[top]; } int is_empty(void) { return top 0; } int maze[MAX_ROW][MAX_COL] { 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, }; void print_maze(void) { int i, j; for (i 0; i MAX_ROW; i) { for (j 0; j MAX_COL; j) printf(%d , maze[i][j]); putchar(/n); } printf(*********/n); } struct point predecessor[MAX_ROW][MAX_COL] { {{-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}}, {{-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}}, {{-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}}, {{-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}}, {{-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}}, }; void visit(int row, int col, struct point pre) { struct point visit_point { row, col }; maze[row][col] 2; predecessor[row][col] pre; push(visit_point); } int main(void) { struct point p { 0, 0 }; maze[p.row][p.col] 2; push(p); while (!is_empty()) { p pop(); if (p.row MAX_ROW - 1 /* goal */ p.col MAX_COL - 1) break; if (p.col1 MAX_COL /* right */ maze[p.row][p.col1] 0) visit(p.row, p.col1, p); if (p.row1 MAX_ROW /* down */ maze[p.row1][p.col] 0) visit(p.row1, p.col, p); if (p.col-1 0 /* left */ maze[p.row][p.col-1] 0) visit(p.row, p.col-1, p); if (p.row-1 0 /* up */ maze[p.row-1][p.col] 0) visit(p.row-1, p.col, p); print_maze(); } if (p.row MAX_ROW - 1 p.col MAX_COL - 1) { printf((%d, %d)/n, p.row, p.col); while (predecessor[p.row][p.col].row ! -1) { p predecessor[p.row][p.col]; printf((%d, %d)/n, p.row, p.col); } } else printf(No path!/n); return 0; } 运行结果如下 2 1 0 0 0 2 1 0 1 0 0 0 0 0 0 0 1 1 1 0 0 0 0 1 0 ********* 2 1 0 0 0 2 1 0 1 0 2 0 0 0 0 0 1 1 1 0 0 0 0 1 0 ********* 2 1 0 0 0 2 1 0 1 0 2 2 0 0 0 2 1 1 1 0 0 0 0 1 0 ********* 2 1 0 0 0 2 1 0 1 0 2 2 0 0 0 2 1 1 1 0 2 0 0 1 0 ********* 2 1 0 0 0 2 1 0 1 0 2 2 0 0 0 2 1 1 1 0 2 2 0 1 0 ********* 2 1 0 0 0 2 1 0 1 0 2 2 0 0 0 2 1 1 1 0 2 2 2 1 0 ********* 2 1 0 0 0 2 1 0 1 0 2 2 0 0 0 2 1 1 1 0 2 2 2 1 0 ********* 2 1 0 0 0 2 1 0 1 0 2 2 2 0 0 2 1 1 1 0 2 2 2 1 0 ********* 2 1 0 0 0 2 1 2 1 0 2 2 2 2 0 2 1 1 1 0 2 2 2 1 0 ********* 2 1 2 0 0 2 1 2 1 0 2 2 2 2 0 2 1 1 1 0 2 2 2 1 0 ********* 2 1 2 2 0 2 1 2 1 0 2 2 2 2 0 2 1 1 1 0 2 2 2 1 0 ********* 2 1 2 2 2 2 1 2 1 0 2 2 2 2 0 2 1 1 1 0 2 2 2 1 0 ********* 2 1 2 2 2 2 1 2 1 2 2 2 2 2 0 2 1 1 1 0 2 2 2 1 0 ********* 2 1 2 2 2 2 1 2 1 2 2 2 2 2 2 2 1 1 1 0 2 2 2 1 0 ********* 2 1 2 2 2 2 1 2 1 2 2 2 2 2 2 2 1 1 1 2 2 2 2 1 0 ********* 2 1 2 2 2 2 1 2 1 2 2 2 2 2 2 2 1 1 1 2 2 2 2 1 2 ********* (4, 4) (3, 4) (2, 4) (1, 4) (0, 4) (0, 3) (0, 2) (1, 2) (2, 2) (2, 1) (2, 0) (1, 0) (0, 0) 这次堆栈里的元素是结构体类型的用来表示迷宫中一个点的x和y座标。我们用一个新的数据结构保存走迷宫的路线每个走过的点都有一个前趋Predecessor的点表示是从哪儿走到当前点的比如predecessor[4][4]是座标为(3, 4)的点就表示从(3, 4)走到了(4, 4)一开始predecessor的各元素初始化为无效座标(-1, -1)。在迷宫中探索路线的同时就把路线保存在predecessor数组中已经走过的点在maze数组中记为2防止重复走最后找到终点时就根据predecessor数组保存的路线从终点打印到起点。为了帮助理解我把这个算法改写成伪代码如下 将起点标记为已走过并压栈; while (栈非空) { 从栈顶弹出一个点p; if (p这个点是终点) break; 否则沿右、下、左、上四个方向探索相邻的点if (和p相邻的点有路可走并且还没走过) 将相邻的点标记为已走过并压栈它的前趋就是p点; } if (p点是终点) { 打印p点的座标; while (p点有前趋) { p点p点的前趋; 打印p点的座标; } } else 没有路线可以到达终点; 我在while循环的末尾插了打印语句每探索一步都打印出当前标记了哪些点从打印结果可看出这种搜索算法的特点每次取一个相邻的点走下去一直走到无路可走了再退回来取另一个相邻的点再走下去。这称为深度优先搜索DFSDepth First Search。探索迷宫和堆栈变化的过程如下图所示。 图 12.2. 深度优先搜索 图中各点的编号反映出探索的顺序堆栈中的数字就是图中点的编号可见正是因为堆栈后进先出的性质使这个算法具有了深度优先的特点。如果在探索问题的解时走进了死胡同则需要退回来从另一条路继续探索这种思想称为回溯Backtrack一个典型的例子是很多编程书上都会讲的八皇后问题。 最后我们打印终点的座标并通过predecessor数据结构找到它的前趋这样顺藤摸瓜一直打印到起点。那么能不能从起点到终点正向打印路线呢在上一节我们看到如果是在一个循环里打印数组既可以正向打印也可以反向打印因为数组这种数据结构是支持随机访问的当然也支持顺序访问并且既可以是正向的也可以是反向的。但现在predecessor这种数据结构的每个元素只知道它的前趋是谁而不知道它的后继Successor是谁所以在循环里只能反向打印。由此可见有什么样的数据结构就决定了可以用什么样的算法。那么为什么不再建一个successor数组来保存每个点的后继呢虽然每个点的前趋只有一个后继却不止一个从DFS算法的过程可以看出如果每次在保存前趋的同时也保存后继后继不一定会指向正确的路线请读者想一想为什么。由此可见有什么样的算法就决定了可以用什么样的数据结构。设计算法和设计数据结构这两件工作是紧密联系的。 习题 1、修改本节的程序最后从起点到终点正向打印路线。你能想出几种办法 2、本节程序中predecessor这个数据结构占用的存储空间太多了可以改变它的存储方式以节省空间想一想该怎么做。 3、上一节我们实现了一个基于堆栈的程序然后用递归改写了它用函数调用的栈帧实现同样的功能。本节的DSF算法是基于堆栈的请把它改写成递归的程序。改写成递归程序是可以避免使用predecessor数据结构的想想该怎么做。 转载声明 本文转自 http://www.yayu.org/book/Linux_c_study_html/ch12s03.html 队列与广度优先搜索 队列也是一组元素的集合也提供两种基本操作Enqueue入队将元素添加到队尾Dequeue出队从队头取出元素并返回。就像排队买票一样先来先服务先入队的人也是先出队的这种方式称为FIFOFirst In First Out先进先出有时候队列本身也被称为FIFO。 下面我们用队列解决迷宫问题。程序如下 例 12.4. 用广度优先搜索解迷宫问题 #include stdio.h #define MAX_ROW 5 #define MAX_COL 5 struct point { int row, col, predecessor; } queue[512]; int head 0, tail 0; void enqueue(struct point p) { queue[tail] p; tail; } struct point dequeue(void) { head; return queue[head-1]; } int is_empty(void) { return head tail; } int maze[MAX_ROW][MAX_COL] { 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, }; void print_maze(void) { int i, j; for (i 0; i MAX_ROW; i) { for (j 0; j MAX_COL; j) printf(%d , maze[i][j]); putchar(/n); } printf(*********/n); } void visit(int row, int col) { struct point visit_point { row, col, head-1 }; maze[row][col] 2; enqueue(visit_point); } int main(void) { struct point p { 0, 0, -1 }; maze[p.row][p.col] 2; enqueue(p); while (!is_empty()) { p dequeue(); if (p.row MAX_ROW - 1 /* goal */ p.col MAX_COL - 1) break; if (p.col1 MAX_COL /* right */ maze[p.row][p.col1] 0) visit(p.row, p.col1); if (p.row1 MAX_ROW /* down */ maze[p.row1][p.col] 0) visit(p.row1, p.col); if (p.col-1 0 /* left */ maze[p.row][p.col-1] 0) visit(p.row, p.col-1); if (p.row-1 0 /* up */ maze[p.row-1][p.col] 0) visit(p.row-1, p.col); print_maze(); } if (p.row MAX_ROW - 1 p.col MAX_COL - 1) { printf((%d, %d)/n, p.row, p.col); while (p.predecessor ! -1) { p queue[p.predecessor]; printf((%d, %d)/n, p.row, p.col); } } else printf(No path!/n); return 0; } 运行结果如下 2 1 0 0 0 2 1 0 1 0 0 0 0 0 0 0 1 1 1 0 0 0 0 1 0 ********* 2 1 0 0 0 2 1 0 1 0 2 0 0 0 0 0 1 1 1 0 0 0 0 1 0 ********* 2 1 0 0 0 2 1 0 1 0 2 2 0 0 0 2 1 1 1 0 0 0 0 1 0 ********* 2 1 0 0 0 2 1 0 1 0 2 2 2 0 0 2 1 1 1 0 0 0 0 1 0 ********* 2 1 0 0 0 2 1 0 1 0 2 2 2 0 0 2 1 1 1 0 2 0 0 1 0 ********* 2 1 0 0 0 2 1 2 1 0 2 2 2 2 0 2 1 1 1 0 2 0 0 1 0 ********* 2 1 0 0 0 2 1 2 1 0 2 2 2 2 0 2 1 1 1 0 2 2 0 1 0 ********* 2 1 0 0 0 2 1 2 1 0 2 2 2 2 2 2 1 1 1 0 2 2 0 1 0 ********* 2 1 2 0 0 2 1 2 1 0 2 2 2 2 2 2 1 1 1 0 2 2 0 1 0 ********* 2 1 2 0 0 2 1 2 1 0 2 2 2 2 2 2 1 1 1 0 2 2 2 1 0 ********* 2 1 2 0 0 2 1 2 1 2 2 2 2 2 2 2 1 1 1 2 2 2 2 1 0 ********* 2 1 2 2 0 2 1 2 1 2 2 2 2 2 2 2 1 1 1 2 2 2 2 1 0 ********* 2 1 2 2 0 2 1 2 1 2 2 2 2 2 2 2 1 1 1 2 2 2 2 1 0 ********* 2 1 2 2 0 2 1 2 1 2 2 2 2 2 2 2 1 1 1 2 2 2 2 1 2 ********* 2 1 2 2 2 2 1 2 1 2 2 2 2 2 2 2 1 1 1 2 2 2 2 1 2 ********* 2 1 2 2 2 2 1 2 1 2 2 2 2 2 2 2 1 1 1 2 2 2 2 1 2 ********* (4, 4) (3, 4) (2, 4) (2, 3) (2, 2) (2, 1) (2, 0) (1, 0) (0, 0) 其实仍然可以像例 12.3 “用深度优先搜索解迷宫问题”一样用predecessor数组表示每个点的前趋但是我想换一种更方便的数据结构直接在每个点的结构体中加一个成员表示前趋 struct point { int row, col, predecessor; } queue[512]; int head 0, tail 0; 变量head、tail就像前两节用来表示栈顶的top一样是queue数组的索引或者叫指针分别指向队头和队尾。每个点的predecessor成员也是一个指针指向它的前趋在queue数组中的位置。如下图所示 图 12.3. 广度优先搜索的队列数据结构 为了帮助理解我把这个算法改写成伪代码如下 将起点标记为已走过并入队; while (队列非空) { 出队一个点p; if (p这个点是终点) break; 否则沿右、下、左、上四个方向探索相邻的点if (和p相邻的点有路可走并且还没走过) 将相邻的点标记为已走过并入队它的前趋就是刚出队的p点; } if (p点是终点) { 打印p点的座标; while (p点有前趋) { p点p点的前趋; 打印p点的座标; } } else 没有路线可以到达终点; 从打印的搜索过程可以看出这个算法的特点是沿各个方向同时展开搜索每个可以走通的方向轮流往前走一步这称为广度优先搜索BFSBreadth First Search。探索迷宫和队列变化的过程如下图所示。 图 12.4. 广度优先搜索 广度优先是一种步步为营的策略每次都从各个方向探索一步将前线推进一步图中的虚线就表示这个前线队列中的元素总是由前线的点组成的可见正是因为队列先进先出的性质使这个算法具有了广度优先的特点。广度优先搜索还有一个特点是可以找到从起点到终点的最短路径而深度优先搜索找到的不一定是最短路径比较本节和上一节程序的运行结果可以看出这一点想一想为什么。 转载声 明 本文转自 http://www.yayu.org/book/Linux_c_study_html/ch12s04.html转载于:https://www.cnblogs.com/springside4/archive/2010/05/31/2481824.html