图的深度优先遍历及广度优先遍历

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

 

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

 

一 : 深度优先遍历

  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 << '\t';
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 << '\t';
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 << '\t';
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

 

posted @ 2019-08-04 17:40  先娶国王后取经  阅读(577)  评论(0编辑  收藏  举报