zoukankan      html  css  js  c++  java
  • 图的深度优先遍历及广度优先遍历

     声明: 代码中有大量的注释,所以此处就不再作出大量的解释了。

        注 :本文是用有向图作为例子,其他三种图的形态可类比写出。

    一 : 深度优先遍历

      1 #include <iostream>
      2 #include <string>
      3 using namespace std;
      4 using std :: string;
      5 typedef string InforType;
      6 //顶点类型
      7 typedef char VertexType;
      8 typedef int Status;
      9 typedef enum {UDG = 1, DG, UDN, DN} GraphKind;
     10 //最大顶点个数
     11 #define MAX_VERTEX_NUM 20
     12 #define ERROR -1
     13 #define OK 1
     14 //表节点的定义
     15 typedef struct ArcNode
     16 {
     17     //该弧所指向的顶点的位置(相对地址)
     18     int adjvex;
     19     //指向下一条弧的指针
     20     struct ArcNode* nextarc;
     21     //该弧相关信息的指针(例如权值)
     22     InforType info;
     23 }ArcNode;
     24 //头节点的定义
     25 typedef struct VNode
     26 {
     27     //顶点信息
     28     VertexType data;
     29     //指向第一条依附该顶点的弧的指针
     30     ArcNode* firstarc;
     31 }VNode, AdjList[MAX_VERTEX_NUM];
     32 //图的定义
     33 typedef struct
     34 {
     35     //存放头节点的一维数组
     36     AdjList vertices;
     37     //图的当前顶点数和弧数
     38     int vexnum, arcnum;
     39     //图的种类标志
     40     GraphKind kind;
     41 }ALGraph;
     42 //标志数组,用来查看顶点是否被访问过
     43 bool visited[MAX_VERTEX_NUM];
     44 Status Visit(VNode node);
     45 Status DFS(const ALGraph& G, int index, Status (*visit)(VNode));
     46 void DFSTraverse(const ALGraph& G, Status (*visit)(VNode));
     47 //定位顶点在一维数组中的位置
     48 int LocateVex(VNode vertices[], int vexnum, VertexType e)
     49 {
     50     int i;
     51     for(i = 0; i < vexnum; i++)
     52     {
     53         if(vertices[i].data == e)
     54             break;
     55     }
     56     return i;
     57 }
     58 //创建有向图
     59 Status CreateDG(ALGraph& G)
     60 {
     61     VertexType e;
     62     int num;
     63     cout << "该图共有多少个顶点、多少条弧?" << endl;
     64     cin >> G.vexnum >> G.arcnum;
     65     cout << "请输入各顶点:" << endl;
     66     for(int i = 0; i < G.vexnum; i++)
     67     {
     68         //注意存储结构
     69         cin >> G.vertices[i].data;
     70         //先将头节点的指针域设为空
     71         G.vertices[i].firstarc = NULL;
     72     }
     73     for(int i = 0; i < G.vexnum; i++)
     74     {
     75         //(强调引用)
     76         //注意ptr是节点的指针(是节点类型的一部分)而不是‘指向’节点的指针
     77         //所以接连各节点不想以前那样简单了
     78         ArcNode* &ptr = G.vertices[i].firstarc;
     79         cout << "请问以顶点" << G.vertices[i].data << "为起点的弧一共有多少条?" << endl;
     80         cin >> num;
     81         if(num > 0)
     82         {
     83             int Index;
     84             ArcNode* p = NULL;
     85             cout << "请将这些顶点依次输入:" << endl;
     86             //先处理一个节点(为了方便后面的操作)
     87             ptr = new ArcNode;
     88             if(ptr)
     89             {
     90                 cin >> e;
     91                 Index = LocateVex(G.vertices, G.vexnum, e);
     92                 p = ptr;
     93                 p->adjvex = Index;
     94                 p->nextarc = NULL;
     95             }
     96             else
     97                 return ERROR;
     98             for(int j = 1; j < num; j++)
     99             {
    100                 //注意各变量的类型,不要搞混了
    101                 cin >> e;
    102                 Index = LocateVex(G.vertices, G.vexnum, e);
    103                 ArcNode* ptr2 = new ArcNode;
    104                 if(ptr2)
    105                 {
    106                     ptr2->adjvex = Index;
    107                     ptr2->nextarc = NULL;
    108                     //注意此处的连接节点与以前写的有点不同(ptr‘一直’为空)
    109                     p->nextarc = ptr2;
    110                     p = p->nextarc;
    111                 }
    112                 else
    113                     return ERROR;
    114             }
    115         }
    116     }
    117     return OK;
    118 }
    119 int main(void)
    120 {
    121     ALGraph G;
    122     CreateDG(G);
    123     DFSTraverse(G, Visit);
    124     return 0;
    125 }
    126 void DFSTraverse(const ALGraph& G, Status (*visit)(VNode))
    127 {
    128     for(int i = 0; i < G.vexnum; i++)
    129     {
    130         //首先将每个顶点都设置为未被访问的状态
    131         visited[i] = false;
    132     }
    133     //对每一个顶点都查看一下,防止有遗漏(特别是针对非连通图)
    134     for(int i = 0; i < G.vexnum; i++)
    135     {
    136         if(!visited[i])
    137             DFS(G, i, visit);
    138     }
    139 }
    140 Status DFS(const ALGraph& G, int index, Status (*visit)(VNode))
    141 {
    142     //注意不同的存储结构会导致遍历的算法不同
    143     if(visit(G.vertices[index]))
    144     {
    145         //改变即将要被访问的节点的访问状态
    146         visited[index] = true;
    147         ArcNode* ptr = G.vertices[index].firstarc;
    148         while(ptr)
    149         {
    150             if(!visited[ptr->adjvex])
    151                 DFS(G, ptr->adjvex, visit);
    152             ptr = ptr->nextarc;
    153         }
    154         return OK;
    155     }
    156     else
    157         return ERROR;
    158 }
    159 //访问节点
    160 Status Visit(VNode node)
    161 {
    162     cout << node.data << '	';
    163     return OK;
    164 }
    View Code

    二 : 广度优先遍历

      1 #include <iostream>
      2 #include "Queue.h"
      3 using namespace std;
      4 //无穷大
      5 #define INFINITY INT_MAX
      6 //最大顶点个数
      7 #define MAX_VERTEX_NUM 20
      8 //标志数组, 表示顶点访问状态
      9 bool visited[MAX_VERTEX_NUM];
     10 //顶点类型
     11 typedef char VertexType;
     12 typedef int VRType;
     13 typedef char infoType;
     14 //图的四种类型
     15 typedef enum {DG = 1, DN, UDG, UDN} GraphKind;
     16 //弧的结构体定义
     17 typedef struct ArcCell
     18 {
     19     // 对于网来说是权值;对于图来说就是0或1
     20     VRType adj;
     21     //该弧相关信息的指针(现在暂且可以理解为字符指针)
     22     infoType* info;
     23 }ArcCell, AdjMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM];
     24 //图的结构体定义
     25 typedef struct
     26 {
     27     VertexType vexs[MAX_VERTEX_NUM];
     28      //arcs可以先简单地理解为一个数组名
     29     AdjMatrix arcs;
     30     //当前图中的顶点数和弧数
     31     int vexnum, arcnum;
     32     //图的种类标志
     33     GraphKind kind;
     34 }MGraph;
     35 void CreateGraph(MGraph &G);
     36 int LocateVex(MGraph G , int v1);
     37 void CreateDG(MGraph &G);
     38 Status Visit(VertexType e);
     39 Status BFSTraverse(const MGraph& G, Status (*visit)(VertexType));
     40 int main(void)
     41 {
     42     MGraph G;
     43     CreateDG(G);
     44     BFSTraverse(G, Visit);
     45     return 0;
     46 }
     47 //返回该顶点在一维数组中的下标(当然每一个人创建的一维数组可以是不同的)
     48 int LocateVex(MGraph G , int v1)
     49 {
     50     int i;
     51     for(i = 0; i < G.vexnum; i++)
     52     {
     53         if(G.vexs[i] == v1)
     54             break;
     55     }
     56     return i;
     57 }
     58 //创建有向图
     59 void CreateDG(MGraph &G)
     60 {
     61     cout << "该图有多少顶点以及多少条边?" << endl;
     62     cin >> G.vexnum  >> G.arcnum;
     63     cout << "请输入顶点:" << endl;
     64     for(int i = 0; i < G.vexnum; i++)
     65     {
     66         cin >> G.vexs[i];
     67     }
     68     for(int i = 0; i < G.vexnum; i++)
     69     {
     70         for(int j = 0; j < G.vexnum; j++)
     71         {
     72             //先假定图中没有弧
     73             G.arcs[i][j].adj = 0;
     74         }
     75     }
     76     cout << "请输入各弧的两个端点" << endl;
     77     VertexType v1, v2;
     78     //用于暂时存储每条弧的'权值'(存在即为1,否则为0)
     79     //因为temp的取值只能为1或0,所以不需要再输入
     80     VRType temp = 1;
     81     int i, j;
     82     //有向图不具备对称性
     83     for(int k = 0; k < G.arcnum; k++)
     84     {
     85         cin >> v1 >> v2;
     86         i = LocateVex(G, v1), j = LocateVex(G, v2),
     87         G.arcs[i][j].adj = temp;
     88     }
     89 }
     90 Status BFSTraverse(const MGraph& G, Status (*visit)(VertexType))
     91 {
     92     for(int i = 0; i < G.vexnum; i++)
     93     {
     94         //先将所有的顶点的访问状态设置为false
     95         visited[i] = false;
     96     }
     97     //注意调整好各文件之间的关系
     98     LinkQueue Q;
     99     InitQueue(Q);
    100     //对每一个顶点都进行查看一次,以防止遗漏(特别是非连通图)
    101     for(int i = 0; i < G.vexnum; i++)
    102     {
    103         if(!visited[i])
    104         {
    105             EnQueue(Q, G.vexs[i]);
    106             //保证每一个节点只进入栈中一次
    107             visited[i] = true;
    108             while(!QueueEmpty(Q))
    109             {
    110                 QueuePtr p = NULL;
    111                 GetHead(Q, p);
    112                 //返回队首元素在一维数组中的下标
    113                 //千万不要将i当作k来使用
    114                 int k = LocateVex(G, p->Qdata);
    115                 for(int j = 0; j < G.vexnum; j++)
    116                 {
    117                     //将所有与顶点G.vexs[i]相邻的顶点压入栈中(0或1, 权值也一样)
    118                     if(G.arcs[k][j].adj)
    119                     {
    120                         if(!visited[j])
    121                         {
    122                             EnQueue(Q, G.vexs[j]);
    123                             visited[j] = true;
    124                         }
    125                     }
    126                 }
    127                 //等到与顶点G.vexs[i]相邻的顶点全部都被压入栈中后, 立刻对顶点G.vexs[i]进行访问
    128                 //只不过我此时的顶点结构未进行封装,所有在这里其实visit函数可有可无
    129                 VertexType temp;
    130                 DeQueue(Q, temp);
    131                 visit(temp);
    132             }
    133         }
    134     }
    135     return OK;
    136 }
    137 Status Visit(VertexType e)
    138 {
    139     cout << e << '	';
    140     return OK;
    141 }
    View Code

    注:因为广度优先遍历中运用到了队列,代码中因此引用了自定义头文件。

    三:自定义头文件(copy时记得让头文件的名字一模一样哦!否则.....哈哈)

     1 #ifndef QUEUE_H_INCLUDED
     2 #define QUEUE_H_INCLUDED
     3 
     4 
     5 #include <iostream>
     6 using namespace std;
     7 #define TRUE 1
     8 #define FALSE 0
     9 #define OK 1
    10 #define ERROR -1
    11 #define OVERFLOW -2
    12 typedef char VertexType;
    13 typedef VertexType QElemType;
    14 typedef int Status;
    15 typedef struct QNode
    16 {
    17     QElemType Qdata;
    18     struct QNode* next;
    19 }QNode, *QueuePtr;
    20 typedef struct
    21 {
    22     QueuePtr front;         //队首指针
    23     QueuePtr rear;          //队尾指针
    24 }LinkQueue;
    25 Status InitQueue(LinkQueue&);               //初始化队列
    26 Status DestroyQueue(LinkQueue&);            //销毁队列
    27 Status ClearQueue(LinkQueue&);              //清空队列
    28 Status QueueEmpty(const LinkQueue&);              //判断队列是否为空
    29 int QueueLength(const LinkQueue&);                //返回队列长度
    30 Status GetHead(const LinkQueue&, QueuePtr&);      //返回队首元素
    31 Status EnQueue(LinkQueue&, QElemType);          //插入一个元素
    32 Status DeQueue(LinkQueue&, QElemType&);          //删除一个元素
    33 Status QueueTraverse(const LinkQueue&, Status (*visit)(const QueuePtr ));  //遍历整个队列
    34 Status Visit(const QueuePtr);
    35 
    36 
    37 
    38 #endif // QUEUE_H_INCLUDED
    View Code


    四 : 头文件之实现文件

      1 #include "Queue.h"
      2 Status InitQueue(LinkQueue& Q)
      3 {
      4     Q.front = Q.rear = new QNode;       //带头节点的链式队列
      5     if(Q.front)
      6     {
      7         Q.front->next = NULL;
      8         return OK;
      9     }
     10     else
     11         return OVERFLOW;
     12 }
     13 Status DestroyQueue(LinkQueue& Q)
     14 {
     15     while(Q.front)
     16     {
     17         Q.rear = Q.front->next;
     18         delete (Q.front);       //空间虽已销毁,但原本指向该空间的指针还在,只不过变为野指针了而已。
     19         Q.front = Q.rear;
     20     }
     21     return OK;
     22 }
     23 Status ClearQueue(LinkQueue& Q)
     24 {
     25     QueuePtr ptr = Q.front->next;
     26     Q.rear = Q.front;
     27     while(ptr)
     28     {
     29         QueuePtr p = ptr;
     30         ptr = ptr->next;
     31         delete p;
     32     }
     33     return OK;
     34 }
     35 Status QueueEmpty(const LinkQueue& Q)
     36 {
     37     if(Q.front == Q.rear)
     38         return TRUE;
     39     else
     40         return FALSE;
     41 }
     42 int QueueLength(const LinkQueue& Q)
     43 {
     44     if(Q.front == Q.rear)
     45         return 0;
     46     else
     47     {
     48         int count = 0;
     49         QueuePtr ptr = Q.front->next;
     50         while(ptr)
     51         {
     52             count++;
     53             ptr = ptr->next;
     54         }
     55         return count;
     56     }
     57 }
     58 Status GetHead(const LinkQueue& Q, QueuePtr& ptr)
     59 {
     60     QueuePtr p = Q.front->next;
     61     if(p)
     62     {
     63         ptr = p;
     64         return OK;
     65     }
     66     else
     67     {
     68         ptr = NULL;
     69         return ERROR;
     70     }
     71 }
     72 Status EnQueue(LinkQueue& Q, QElemType e)
     73 {
     74     QueuePtr ptr = new QNode;
     75     if(ptr)
     76     {
     77         ptr->next = NULL;
     78         ptr->Qdata = e;
     79         Q.rear->next = ptr;
     80         Q.rear = ptr;
     81         return OK;
     82     }
     83     else
     84         return OVERFLOW;
     85 }
     86 Status DeQueue(LinkQueue& Q, QElemType& e)
     87 {
     88     if(Q.front == Q.rear)
     89         return ERROR;
     90     else                                //注意这是队列,只能从队首删除元素,从队尾插入元素
     91     {
     92         QueuePtr ptr = Q.front->next;
     93         Q.front->next = ptr->next;
     94         e = ptr->Qdata;
     95         if(ptr == Q.rear)       //如果原本队列里只有一个元素
     96         {
     97             Q.rear = Q.front;
     98         }
     99         delete ptr;
    100         return OK;
    101     }
    102 }
    103 Status Visit(const QueuePtr ptr)
    104 {
    105     if(!ptr)
    106     {
    107         return ERROR;
    108     }
    109     else
    110     {
    111         cout << ptr->Qdata << '	';
    112         return OK;
    113     }
    114 }
    115 Status QueueTraverse(const LinkQueue& Q, Status (*visit)(const QueuePtr ))
    116 {
    117     QueuePtr ptr = NULL;
    118     if(Q.front == Q.rear)
    119         return ERROR;
    120     else
    121     {
    122         ptr = Q.front->next;        //从第一个节点开始遍历(带头节点的链表)
    123         while(ptr)
    124         {
    125             (*visit)(ptr);
    126             ptr = ptr->next;
    127         }
    128         cout << endl;
    129         return OK;
    130     }
    131 }
    View Code
  • 相关阅读:
    特殊字符大全
    ASP執行文件下載
    在 Access 里使用查询建立 存储过程/视图, 并使用 ASP 执行
    通過windows的排定工作來執行存儲過程
    前台页面中用js取得eWebEditor的值
    IE7外觀優化
    电话订票
    在BIOS设置“扩展或板载”显卡的方法
    子窗口中操作父窗口对像(javascript)(转)
    mailto用法
  • 原文地址:https://www.cnblogs.com/ReturnOfTheKing/p/11298949.html
Copyright © 2011-2022 走看看