zoukankan      html  css  js  c++  java
  • 第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 }

    结果如下:

    小结:

                         

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

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

  • 相关阅读:
    移动端前端布局的必看前提
    单词统计
    用户场景分析
    学习进度(九)
    团队项目
    学习进度(二)
    数据可视化
    大二下,学习进度(一)
    求最大子数组的和
    构建之法3
  • 原文地址:https://www.cnblogs.com/liuyueyue/p/13563761.html
Copyright © 2011-2022 走看看