图的两种存储(邻接矩阵和邻接表)和两种遍历(DFS和BFS)
图的表示有很多,形式不固定,我暂时先记录我已经懂了的,能写的两种即大多数人应该都知道的邻接矩阵和邻接表。
邻接矩阵:
这里的邻接矩阵和离散数学说的有一点不同,至少有向图的邻接矩阵不同(离散书上的有向图的邻接矩阵求法到是有点像求任意两点的最短路径的Floyd算法)
以上都是(我现有知识认为的)废话;
重点 :
G : 表示图;
Nv:表示图的点数;
Ne:表示图的边数;
邻接矩阵 即是一个 Nv * Nv 的矩阵,矩阵是用来储存 权值的(如果是带权图且有边的话),如果是无权图的的话,如果两顶点有边就用 1表示 ;如果两个点之间无边则用 -1或无穷表示
这里的无穷表示的是 权值所用类型的最大边界。
图的遍历:
图有两种遍历,深度优先搜索(DFS)和广度优先搜索(BFS);
DFS:类似树的先序遍历,就是每次都先打印当前要遍历的点,然后递归遍历它的邻接点,(就像二叉树中我们每次都先输出根,再遍历左右子树一样,不过图中变成了邻接点而已)
BFS:类似树的层次遍历,就是先将要遍历的起点先入队,之后就重复3件事,就是 先出队 ,再打印出队点,最后没入队的,与出队点有边的 入队,如此重复 直到遍历完;
代码如下:
头文件:
//Graph.h
#include <stdbool.h> #define MaxVertexNum 100 #define INFINITY 65535 #define ERROR -1 typedef int Vertex; typedef int WeightType; typedef char DataType; //图 typedef struct ENode *PtrToENode; struct ENode{ Vertex V1, V2; WeightType weight; }; typedef PtrToENode Edge; typedef struct GNode *PtrToGNode; struct GNode{ int Nv; int Ne; WeightType G[MaxVertexNum][MaxVertexNum]; DataType Data[MaxVertexNum]; }; typedef PtrToGNode MGraph; MGraph CreateGraph(int); void InsertEdge(MGraph, Edge); MGraph BuildGraph(); void DFS(MGraph, Vertex); void BFS(MGraph ,Vertex); //队列 struct QNode{ int *Data; int front, rear; int MaxSize; }; typedef struct QNode * Queue; Queue CreateQ(int MaxSize); bool IsFull(Queue Q); bool AddQ(Queue Q, int data); bool IsEmpty(Queue Q); Vertex DeleteQ(Queue Q); bool DestoryQ(Queue Q);
实现:
#include <stdio.h> #include <stdlib.h> #include "Graph.h" //队列的实现 Queue CreateQ(int MaxSize) { Queue Q = (Queue)malloc(sizeof (struct QNode)); Q->Data = (int*)malloc(sizeof (int)*MaxSize); Q->front = Q->rear = 0; Q->MaxSize = MaxSize; return Q; } bool IsEmpty(Queue Q) { return (Q->rear == Q->front); } bool IsFull(Queue Q) {//告非 return (Q->rear + 1) % Q->MaxSize == Q->front; } bool AddQ(Queue Q, Vertex data) { if (IsFull(Q)){ printf("满\n"); exit(0); } else{ Q->rear = (Q->rear + 1) % Q->MaxSize; Q->Data[Q->rear] = data; return true; } } Vertex DeleteQ(Queue Q) { if (IsEmpty(Q)){ printf("空\n"); exit(0); } else{ Q->front = (Q->front + 1) % Q->MaxSize; return Q->Data[Q->front]; } } bool DestoryQ(Queue Q) { free(Q->Data); free(Q); return true; } bool Visited[MaxVertexNum]; //遍历时用来标记是否已经访问过 MGraph CreateGraph(int VertexNum) //先建一个无边图 { Vertex V, W; MGraph Graph; Graph = (MGraph)malloc(sizeof(struct GNode)); Graph->Nv = VertexNum; Graph->Ne = 0; for (V = 0; V < Graph->Nv; ++V){ for (W = 0; W < Graph->Nv; ++W) Graph->G[V][W] = INFINITY; } for (int i = 0; i < Graph->Nv; ++i) Visited[i] = false; return Graph; } void InsertEdge(MGraph Graph, Edge E) //向图中加入一条边 { Graph->G[E->V1][E->V2] = E->weight; Graph->G[E->V2][E->V1] = E->weight; } MGraph BuildGraph() //建立完整的图 { MGraph Graph; Edge E; Vertex V; int Nv, i; printf("输入顶点数: \n"); scanf_s("%d", &Nv); Graph = CreateGraph(Nv); printf("输入边数\n"); scanf_s("%d", &(Graph->Ne)); if (Graph->Ne != 0){ E = (Edge)malloc(sizeof(struct ENode)); printf("输入%d条边\n",Graph->Ne); for (i = 0; i < Graph->Ne; ++i){ scanf_s("%d %d %d", &E->V1, &E->V2, &E->weight); InsertEdge(Graph, E); } } printf("输入顶点值\n"); for (V = 0; V < Graph->Nv; V++) scanf_s("%c", &Graph->Data[V]); return Graph; } void DFS(MGraph G, Vertex V) //深度优先搜索 { printf("正在访问的是%d\n", V); Visited[V] = true; Vertex W; for (W = 0; W < G->Nv; ++W){ if (!Visited[W] && G->G[V][W] < INFINITY) DFS(G, W); } } void BFS(MGraph Graph,Vertex S) //广度优先搜索 { Queue Q = CreateQ(MaxVertexNum); Vertex V, W; printf("正在访问的点是:%d\n", S); Visited[S] = true; AddQ(Q, S); while (!IsEmpty(Q)){ V = DeleteQ(Q); for (W = 0; W < Graph->Nv; ++W){ if (!Visited[W] && Graph->G[V][W] < INFINITY){ printf("正在访问的点是:%d\n", W); Visited[W] = true; AddQ(Q, W); } } } } int main() { printf("创建邻接矩阵表示图\n"); MGraph Graph = BuildGraph(); printf("打印矩阵\n"); for (int i = 0; i < Graph->Nv; ++i){ for (int j = 0; j < Graph->Nv; ++j) printf("%d\t", Graph->G[i][j]); putchar('\n'); } printf("\n\n"); //printf("DFS遍历\n"); //DFS(Graph, 0); printf("BFS遍历\n"); BFS(Graph, 0); system("pause"); return 0; }
邻接表:是用结构数组加链表形式表示,先创建一个大小为Nv的结构数组表示Nv个点,以每一个结构为头结点,再在它们后面接它们各自的邻接点;
代码如下:
头文件
//Graph.h
#include <stdbool.h> #define MaxVertexNum 100 #define INFINITY 65535 #define ERROR -1 typedef int Vertex; typedef int WeightType; typedef char DataType; //图 typedef struct ENode *PtrToENode; struct ENode{ Vertex V1, V2; WeightType weight; }; typedef PtrToENode Edge; typedef struct GNode *PtrToGNode; struct GNode{ int Nv; int Ne; WeightType G[MaxVertexNum][MaxVertexNum]; DataType Data[MaxVertexNum]; }; typedef PtrToGNode MGraph; MGraph CreateGraph(int); void InsertEdge(MGraph, Edge); MGraph BuildGraph(); void DFS(MGraph, Vertex); void BFS(MGraph ,Vertex); //队列 struct QNode{ int *Data; int front, rear; int MaxSize; }; typedef struct QNode * Queue; Queue CreateQ(int MaxSize); bool IsFull(Queue Q); bool AddQ(Queue Q, int data); bool IsEmpty(Queue Q); Vertex DeleteQ(Queue Q); bool DestoryQ(Queue Q);
实现
#include <stdio.h> #include <stdlib.h> #include "Graph.h" //队列实现 Queue CreateQ() { Queue Q; Q = (Queue)malloc(sizeof (struct QNode)); Q->front = (item)malloc(sizeof (struct INode)); Q->front->next = NULL; Q->rear = Q->front; return Q; } bool IsEmptyQ(Queue Q) { return Q->front->next == NULL; } bool AddQ(Queue Q, Vertex X) { item tmp = (item)malloc(sizeof(struct INode)); tmp->V = X; tmp->next = NULL; Q->rear->next = tmp; Q->rear = tmp; return true; } Vertex DeleteQ(Queue Q) { if (IsEmptyQ(Q)){ printf("队空\n"); exit(0); } Vertex X; item tmp = Q->front->next; if (tmp == Q->rear) Q->rear = Q->front; Q->front->next = tmp->next; X = tmp->V; return X; } bool Destory(Queue Q) { while (!IsEmptyQ(Q)) DeleteQ(Q); free(Q->front); free(Q); return true; } //邻接表实现 bool Visited[MaxVertexNum]; LGraph CreateGraph(int VertexNum) { Vertex V; LGraph Graph; Graph = (LGraph)malloc(sizeof(struct GNode)); Graph->Nv = VertexNum; Graph->Ne = 0; for (V = 0; V < Graph->Nv; ++V) Graph->G[V].FirstEdge = NULL; for (int i = 0; i < MaxVertexNum; ++i) Visited[i] = false; return Graph; } void InsertEdge(LGraph Graph, Edge E) { PtrToAdjVNode NewNode; NewNode = (PtrToAdjVNode)malloc(sizeof(struct AdjVNode)); NewNode->AdjV = E->V2; NewNode->Weight = E->weight; NewNode->Next = Graph->G[E->V1].FirstEdge; Graph->G[E->V1].FirstEdge = NewNode; NewNode = (PtrToAdjVNode)malloc(sizeof(struct AdjVNode)); NewNode->AdjV = E->V1; NewNode->Weight = E->weight; NewNode->Next = Graph->G[E->V2].FirstEdge; Graph->G[E->V2].FirstEdge = NewNode; } LGraph BuildGraph() { LGraph Graph; Edge E; Vertex V; int Nv, i; printf("创建图(邻接表):\n"); printf("输入顶点数\n"); scanf_s("%d", &Nv); Graph = CreateGraph(Nv); printf("输入边数\n"); scanf_s("%d", &(Graph->Ne)); if (Graph->Ne != 0){ E = (Edge)malloc(sizeof(struct ENode)); printf("输入%d边\n", Graph->Ne); for (i = 0; i < Graph->Ne; ++i){ scanf_s("%d %d %d", &E->V1, &E->V2, &E->weight); InsertEdge(Graph, E); } } printf("输入顶点值\n"); for (V = 0; V < Graph->Nv; V++) scanf_s("%D", &(Graph->G[V].Data)); return Graph; } void DFS(LGraph Graph, Vertex S) { PtrToAdjVNode W; printf("正在访问的顶点是%d\n", S); Visited[S] = true; for (W = Graph->G[S].FirstEdge; W;W=W->Next) if (!Visited[W->AdjV]) DFS(Graph, W->AdjV); } void BFS(LGraph Graph, Vertex S) { Queue Q = CreateQ(); Vertex V; PtrToAdjVNode W; printf("正在访问的点是:%d\n", S); Visited[S] = true; AddQ(Q, S); while (!IsEmptyQ(Q)){ V = DeleteQ(Q); for (W = Graph->G[V].FirstEdge; W; W = W->Next) if (!Visited[W->AdjV]){ printf("正在访问的点是:%d\n", W->AdjV); Visited[W->AdjV] = true; AddQ(Q, W->AdjV); } } } void PrintGraph(LGraph Graph) { PtrToAdjVNode tmp; for (int i = 0; i < Graph->Nv; ++i){ tmp = Graph->G[i].FirstEdge; while (tmp){ printf("%d\t", tmp->AdjV); tmp = tmp->Next; } putchar('\n'); } } int main() { LGraph Graph; Graph = BuildGraph(); printf("打印邻接表\n"); PrintGraph(Graph); printf("DFS遍历:\n"); //DFS(Graph, 1); printf("\n\n"); printf("BFS遍历:\n"); BFS(Graph,1); system("pause"); return 0; }
注意这里的代码实现默认都是无向图;若是有向图,只要改动 InsertEdge(Graph,E) 函数即可;即按方向只添加一条边就行了。
补充:当图不连通,即有一个或多个连通分量时可用以下函数实现;
按顺序检查没有被访问的,然后调用DFS()或BFS()
void ListComponents(LGraph Graph) { Vertex V; for (V = 0; V < Graph->Nv; ++V){ if (!Visited[V]) DFS(Graph, V); //BFS(Graph, V); } }