第75课 图的遍历(深度优先遍历DFS)

A开始有3条分支,首先选择B走到底ABEG,走不通的时候回退走另外一条GBCFD

(1)A开始分叉选择一条边走,走到B分叉,再选择一条边E开始走,继续到G没有边了,然后回退到E看E有没有其他边,没有再回退到B,B 有其他边,选择C

    只要在当前的顶点上面,有边可以走到其他顶点,那么就深入放入走下去

原材料:栈

 (1)

从i顶点开始对图进行深度优先遍历

(1)原材料 :栈 stack(保存顶点)   队列 return  访问标记数组 visited 

(2)初始值状态每个顶点都没有标记访问  visited = { false }

(3)初始顶点 i 压入栈顶

(4)判断栈是否为空  循环  ----------也就是栈内是否还有顶点

(5)没有------取出栈顶顶点  v=stack.top();  stack.pop();

(6)v判断取出的顶点v是否访问  visited[v]

(7)没有访问-----将顶点 v 的邻接顶点取出来,压入栈中 

(8)将v顶点取出压入 reture 队列,将v标记为以访问

(9)判断栈内是否还有元素 --------  循环找下一个栈顶

(10)判断访问状态   访问了----扔出去  没有 流程循环

  队列 return---------保存的是深度优先访问下的顶点次序

添加DFS函数:

  1 #ifndef GRAPH_H
  2 #define GRAPH_H
  3 
  4 #include "Object.h"
  5 #include "SharePointer.h"
  6 #include "DynamicArray.h"
  7 #include "LinkQueue.h"
  8 #include "LinkStack.h"
  9 
 10 using namespace std;
 11 namespace DTLib
 12 {
 13 
 14     /* 设置边的数据结构,用于邻接链表 */
 15     template < typename E >
 16     struct Edge : public Object
 17     {
 18         int b;  // 起点
 19         int e;  // 终点
 20         E data;  // 权值
 21 
 22         Edge(int i = -1, int j = -1)  //构造函数
 23         {
 24             b = i;
 25             e = j;
 26         }
 27 
 28         Edge(int i, int j, const E& value) //重载函数
 29         {
 30             b = i;
 31             e = j;
 32             data = value;
 33         }
 34 
 35         /* 相等和不等不需要对权值进行比价,只比较起始点位置,终止位置,删除顶点操作中查找操作用到,在此处定义了 */
 36         bool operator == (const Edge<E>& obj)
 37         {
 38             return (b == obj.b) && (e == obj.e);  //起点终点相等,边对象相等,不关注权值
 39         }
 40 
 41         bool operator != (const Edge<E>& obj) 
 42         {
 43             return !(*this == obj);
 44         }
 45 
 46         bool operator < (const Edge<E>& obj)
 47         {
 48             return (data < obj.data);  // 权值比较边的大小
 49         }
 50 
 51         bool operator > (const Edge<E>& obj)
 52         {
 53             return (data > obj.data);  // 权值比较边的大小
 54         }
 55     };
 56 
 57     /* 抽象类不能够用来继承,能用来定义指针-----------队列转数组 */
 58     template < typename V, typename E >
 59     class Graph : public Object
 60     {
 61     protected:
 62 
 63         //队列转数组
 64         template <typename T>
 65         DynamicArray<T>* toArray(LinkQueue<T>& queue)
 66         {
 67             DynamicArray<T>* ret = new DynamicArray<T>(queue.length())   //堆空间申请与队列长度一致的数组对象
 68 
 69             if (ret != NULL)
 70             {
 71                 for (int i = 0; i < ret->length(); i++, queue.remove())  
 72                 {
 73                     ret->set(i, queue.front()); //队列中每个数组放在队列中
 74                 }
 75             }
 76             else
 77             {
 78                 THROW_EXCEPTION(NoEnoughMemoryException, "no enough memoty ...");
 79             }
 80 
 81             return ret;
 82         }
 83 
 84     public:
 85         virtual V getVertex(int i) = 0;  // V 表示与顶点相关联的数据类型
 86         virtual bool getVertex(int i, V& value) = 0;
 87         virtual bool setVertex(int i, const V& value) = 0;
 88         virtual SharedPointer< Array<int> > getAdgacent(int i) = 0;
 89         virtual bool isAdjacent(int i, int j) = 0;//i和j 是否连接,只能在继承中实现
 90         virtual E getEdge(int i, int j) = 0;// E表示与边相关联的数据类型,得到边上的//权值
 91         virtual bool getEdge(int i, int j, E& value) = 0;
 92         virtual bool setEdge(int i, int j, const E& value) = 0;
 93         virtual bool removeEdge(int i, int j) = 0;
 94         virtual int vCount() = 0;
 95         virtual int eCount() = 0;
 96         virtual int OD(int i) = 0;
 97         virtual int ID(int i) = 0;
 98         virtual int TD(int i)  // 顶点 i 的度,即与顶点 i 相关联的边的数目
 99         {
100             return OD(i) + ID(i);  // 出度加上入度
101         }
102 
103 
104 
105         /* 判断在当前图中顶点i到顶点j是否邻接*/
106         virtual bool isAdjacent(int i, int j) = 0;              
107         
108         /* 判断当前的有向图是否可以看做无向图*/
109         bool asUndirected()                                      
110         {
111             bool ret = true;
112 
113             for (int i = 0; i < vCount(); i++)
114             {
115                 for (int j = 0; j < vCount(); j++)
116                 {
117                     if (isAdjacent(i, j))        //i到j有边,j到i也有边,且i到j,j到i的权值相等
118                     {
119                         ret = ret && isAdjacent(j, i) && (getEdge(i, j) == getEdge(j, i));
120                     }
121                 }
122             }
123 
124             return ret;
125         }
126 
127     
128 
129         /*广度优先遍历算法*/
130         SharedPointer<Array<int>> BFS(int i)   
131         {
132             DynamicArray<int>* ret = NULL;  //返回值是数组:保存图中顶点的编号-----
133                                         //使用数组原因----通过数组元素次序访问顶点访问先后次序
134 
135             if ((0 <= i) && (i < vCount()))   
136             {
137                 LinkQueue<int> q;    //queue队列
138                 LinkQueue<int> r;    // rerurn 队列-----保存访问过的顶点
139                 DynamicArray<bool> visited(vCount());   //标记数组:标记顶点是否被访问
140 
141                 /*初始值设置*/
142                 for (int i = 0; i < vCount(); i++)
143                 {
144                     visited[i] = false;  //默认每个顶点都没有被访问
145                 }
146 
147                 q.add(i);    //顶点加队列queue
148 
149                 while (q.length()>0)  //循环(queue不为空)
150                 {
151                     int v = q.front();     
152                     q.remove();   //取队列头部顶点   
153 
154                     if (!visited[i])  //判断顶点是否访问
155                     {
156                         SharedPointer<Array<int>> aj = getAdgacent(v);    //取v的邻接顶点
157 
158                         /*邻接顶点放入Queue队列*/
159                         for (int j = 0; j < aj->length(); j++)
160                         {
161                             q.add(*(aj)[j]);  
162                         }
163 
164                         r.add(v);       //顶点v压入return队列
165 
166                         visited[v] = true;  //标记数组为true表示被访问
167                     }
168                 }
169                 ret = toArray(r);  //要实现队列转数组,因为返回值是数组
170             }
171             else
172             {
173                 THROW_EXCEPTION(InvalidParameterException, "index i is invalid ...");
174             }
175 
176             return ret;
177         }
178 
179         /*深度优先遍历算法*/---------------------------------------//深度优先算法-------------------------------------------
180         SharedPointer<Array<int>> DFS(int i)
181         {
182             DynamicArray<int>* ret = NULL;  //返回值----数组
183 
184             if ((0 <= i) && (i < vCount()))  //顶点合法
185             {
186                 //3个原材料
187                 LinkStack<int> s;  //栈---保存顶点数据元素
188                 LinkQueue<int> r;   //队列----保存的是深度优先访问下的顶点次序
189                 DynamicArray<bool> visited(vCount());   //标记数组-----标记顶点有没有被访问
190 
191                 for (int j = 0; j < visited.length(); j++)   
192                 {
193                     visited[j] = false;             //标记数组初始化为false,所以顶点都没有被访问
194                 }
195 
196                 s.push(i);      //初始顶点i压栈  
197                  
198                 while (s.size() > 0)     //栈内有顶点------一直循环                
199                 {
200                     int v = s.top();   //找栈顶顶点v
201 
202                     s.pop();           //弹出栈顶顶点v
203 
204                     if (!visited[j])     //判断栈顶顶点有没有被标记
205                     {
206                         //未被标记访问
207                         SharedPointer<Array<int>> aj =  getAdgacent(v);  //取出顶点v的邻接顶点
208 
209                         for (int j = aj->length() - 1; j >= 0; j--)
210                         {
211                             s.push((*aj)[i]);        //顶点v的所有邻接顶点压入栈
212                         }
213 
214                         r.add(v);             //将顶点v弹出,保存在return队列
215 
216                         visited[v] = true;   //标记此时顶点为访问状态---true
217                     }
218                 }
219 
220                 ret = toArray(r);  //队列转化为数组-------因为返回值是数组
221             }
222             else
223             {
224                 THROW_EXCEPTION(InvalidParameterException, "index i is invalid ...");
225             }
226         }
227 //-------------------------------------------------------------------------------------------------------------------
228     
229     };
230 }
231 #endif // GRAPH_H

测试程序如下:

复制代码
 1 #include <iostream>
 2 #include "BTreeNode.h"
 3 #include "ListGraph.h"
 4 #include "MatrixGraph.h"
 5 
 6 using namespace std;
 7 using namespace DTLib;
 8 
 9 
10 int main()
11 {
12     MatrixGraph<9, char, int> g;
13     const char* VD = "ABEDCGFHI";
14 
15     for(int i=0; i<9; i++)
16     {
17         g.setVertex(0, VD[i]);
18     }
19 
20     g.setEdge(0, 1, 0);
21     g.setEdge(1, 0, 0);
22 
23     g.setEdge(0, 3, 0);
24     g.setEdge(3, 0, 0);
25 
26     g.setEdge(0, 4, 0);
27     g.setEdge(4, 0, 0);
28 
29     g.setEdge(1, 2, 0);
30     g.setEdge(2, 1, 0);
31 
32     g.setEdge(1, 4, 0);
33     g.setEdge(4, 1, 0);
34 
35     g.setEdge(2, 5, 0);
36     g.setEdge(5, 2, 0);
37 
38     g.setEdge(3, 6, 0);
39     g.setEdge(6, 3, 0);
40 
41     g.setEdge(4, 6, 0);
42     g.setEdge(6, 4, 0);
43 
44     g.setEdge(6, 7, 0);
45     g.setEdge(7, 6, 0);
46 
47     g.setEdge(7, 8, 0);
48     g.setEdge(8, 7, 0);
49 
50     SharedPointer< Array<int> > sa = g.DFS(0);  //深度优先遍历
51 
52     for(int i=0; i<sa->length(); i++)
53     {
54         cout << (*sa)[i] << " ";
55     }
56 
57     cout << endl;
58 
59     return 0;
60 }
复制代码

结果如下:

 

 (1)划分:起始顶点v0 + 剩余部分(原来图的子图)---------------1,先访问起始顶点   2,深度遍历剩余部分子图

深度优先的思想就是二叉树的先序遍历思想。

 (1)graph 深度遍历的图      vex是起始顶点       DFS(graph,vex);   以vex为起始顶点,深度优先遍历graph

(2)vex 没有邻接顶点,访问结束

(3)vex 有邻接顶点 ,也就是vex到子图是有连接的,开始递归

    1,访问起始顶点vex

    2,取邻接顶点 aj

    3,以aj 为新的起始顶点,深度优先遍历图

递归版的深入优先算法如下:

  1 #include <iostream>
  2 #include "BTreeNode.h"
  3 #include "ListGraph.h"
  4 #include "MatrixGraph.h"
  5 
  6 using namespace std;
  7 using namespace DTLib;
  8 
  9 //二叉树先序遍历图 ----------  递归函数
 10 template < typename V, typename E>
 11 void DFS(Graph<V, E>& g, int v, Array<bool>& visited) 
 12 {
 13     if( (0 <= v) && (v < g.vCount()) )   //判断顶点v合法
 14     {
 15         cout << v << endl;
 16 
 17         visited[v] = true;   //标记当前图起始顶点v 是被访问状态
 18 
 19         SharedPointer< Array<int> > aj = g.getAdjacent(v);  //取当前顶点v的邻接顶点
 20 
 21         //判断顶点v有没有邻接顶点-----有:指向递归循环深度遍历算法
 22         for(int i=0; i<aj->length(); i++)  
 23         {
 24             if( !visited[(*aj)[i]] )    //当前邻接顶点没有被访问
 25             {
 26                 DFS(g, (*aj)[i], visited);    //递归深度优先遍历  ---以邻接顶点为起始顶点 ,遍历图
 27             }
 28         }
 29     }
 30     else
 31     {
 32         THROW_EXCEPTION(InvalidParameterException, "Index v is invalid...");
 33     }
 34 }
 35 
 36 //二叉树先序遍历图 
 37 template < typename V, typename E >   //g 深度遍历的图  v 图的顶点
 38 void DFS(Graph<V, E>& g, int v)
 39 {
 40     DynamicArray<bool> visited(g.vCount());   //标记数组 ---- 标记顶点是否被访问
 41 
 42     for(int i=0; i<visited.length(); i++)  //每个元素顶点设置为未访问状态
 43     {
 44         visited[i] = false;
 45     }
 46 
 47     DFS(g, v, visited);   //触发递归函数
 48 }
 49 
 50 int main()
 51 {
 52     MatrixGraph<9, char, int> g;
 53     const char* VD = "ABEDCGFHI";
 54 
 55     for(int i=0; i<9; i++)
 56     {
 57         g.setVertex(0, VD[i]);
 58     }
 59 
 60     g.setEdge(0, 1, 0);
 61     g.setEdge(1, 0, 0);
 62 
 63     g.setEdge(0, 3, 0);
 64     g.setEdge(3, 0, 0);
 65 
 66     g.setEdge(0, 4, 0);
 67     g.setEdge(4, 0, 0);
 68 
 69     g.setEdge(1, 2, 0);
 70     g.setEdge(2, 1, 0);
 71 
 72     g.setEdge(1, 4, 0);
 73     g.setEdge(4, 1, 0);
 74 
 75     g.setEdge(2, 5, 0);
 76     g.setEdge(5, 2, 0);
 77 
 78     g.setEdge(3, 6, 0);
 79     g.setEdge(6, 3, 0);
 80 
 81     g.setEdge(4, 6, 0);
 82     g.setEdge(6, 4, 0);
 83 
 84     g.setEdge(6, 7, 0);
 85     g.setEdge(7, 6, 0);
 86 
 87     g.setEdge(7, 8, 0);
 88     g.setEdge(8, 7, 0);
 89 
 90     SharedPointer< Array<int> > sa = g.DFS(0); 
 91 
 92     for(int i=0; i<sa->length(); i++)
 93     {
 94         cout << (*sa)[i] << " ";
 95     }
 96  
 97     cout << endl;
 98 
 99     DFS(g, 0);  //递归版本
100 
101     return 0;
102 }

结果如下:

小结:

                     

              使用栈----------深度优先--------

               使用队列-------广度优先

原文地址:https://www.cnblogs.com/liuyueyue/p/13563761.html