图的深度优先遍历及广度优先遍历
声明: 代码中有大量的注释,所以此处就不再作出大量的解释了。
注 :本文是用有向图作为例子,其他三种图的形态可类比写出。
一 : 深度优先遍历
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 }
二 : 广度优先遍历
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 }
注:因为广度优先遍历中运用到了队列,代码中因此引用了自定义头文件。
三:自定义头文件(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
四 : 头文件之实现文件
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 }