圖的表示有很多,形式不固定,我暫時先記錄我已經懂了的,能寫的兩種即大多數人應該都知道的鄰接矩陣和鄰接表。
鄰接矩陣:
這里的鄰接矩陣和離散數學說的有一點不同,至少有向圖的鄰接矩陣不同(離散書上的有向圖的鄰接矩陣求法到是有點像求任意兩點的最短路徑的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); } }