圖和圖的遍曆

來源:互聯網
上載者:User

由於本人沒學過離散數學,在這裡只能班門弄斧一下了:

一幅圖(graph)G = (V,E)是由頂點集V(vertex)和邊集E(egge)組成的。每一條邊就是一個點對(v,w),其中v,w∈V。如果點對是有序的,那麼圖就是有向的,否則成為無向圖。如果兩個頂點(v1,v2)∈E,那麼它們就是相關聯的,它們互為鄰接點。一個點的度數,指的是與這個點相關聯的頂點的個數。對於有向圖,還分為入度和出度。如果有一點序列(v1,v2,……vn),如果其中相鄰的兩個(vi,vi+1)∈E,則它們構成路徑。如果v1=vn,則稱為環或者迴路。如果能從點v1沿著路徑到v2,那就稱這兩點是聯通的。如果圖中的任意兩點都是聯通的,那麼就稱為連通圖。如果有令一幅圖G'=(V',E'),且V'∈V,E'∈E,則稱G'是G的子圖。特別的如果兩幅圖的頂點完全相同,只是第二幅圖的邊集是第一幅的子集,則第二幅圖稱為第一幅圖的產生子圖。

圖的基本概念先說這麼多吧,其實畫出來以後這些概念都很明了的。

那麼這種複雜的資料結構,即有點,又有邊,該如何表示呢?人們想出了有很多種標記法:鄰接矩陣,鄰接表等等。這裡只介紹最簡單的鄰接矩陣:就是把圖中的n個頂點寫成一個n*n的矩陣,如果兩個頂點是關聯的,那麼矩陣的值為1,否者為0。

#include <stdio.h>#include <malloc.h>//深度優先搜尋需要使用棧#include"stack.h"//廣度優先遍曆需要使用隊列#include "queue.h"#define MAX 8//鄰接矩陣typedef int** adjacentMatrix;//圖的資料結構typedef struct graph{//鄰接矩陣adjacentMatrix matrix;//頂點向量char* vextex;//頂點向量個數int vextexNum;//邊的個數int arcNum;}Graph;//圖的基本操作//初始化圖Graph initGraph(int );//銷毀圖void destroyGraph(Graph* );//增加一條邊bool addArc(Graph* ,char ,char );//刪除一條邊bool deleteArc(Graph* ,char ,char );//顯示圖void showGraph(Graph* );//圖的遍曆://深度優先搜尋的遞迴實現void do_DFS(Graph* ,int );void DFS(Graph* );//深度優先搜尋的棧實現int* findAdjacentVertex(Graph* ,int ,int *);void DFS_byStack(Graph*,char);//廣度優先搜尋的隊列實現void BFS_byQueue(Graph*,char);

 一種資料結構,最重要的操作就是對他遍曆。對於圖的遍曆,有兩種常用的方法:深度優先遍曆和廣度優先遍曆。深度優先遍曆類似於對二叉樹的先序遍曆;廣度優先遍曆類似按層遍曆樹。

廣度優先遍曆的基本步驟如下:

1.將初始節點入隊

2.當隊列不為空白時,出隊;出隊的節點放在vx中。

3.找到與vx相鄰且沒有被訪問過的節點,將它們入隊。

4.轉2.

注意在每次出隊以後,就可以進行相應的操作了,比如列印。

深度優先遍曆的基本步驟如下:

1.將起始節點壓棧

2.如果棧不為空白,彈棧;彈出的節點放在vx中。

3.找到與vx相鄰且沒被訪問過的節點,將它們壓棧。

4.轉2.

 

下面給出相應的代碼:

#include "graph.h"//圖的基本操作//初始化圖Graph initGraph(int n){Graph g;g.vextexNum = n;g.arcNum = 0;g.matrix = (int**)malloc(sizeof(int*) * n);for(int i = 0; i < n;++i)g.matrix[i] = (int*)malloc(sizeof(int) * n);for(int i = 0;i < n;++i)for(int j = 0; j < n;++j)g.matrix[i][j] = 0;g.vextex = (char*)malloc(sizeof(char) * n);char a = 'A';for(int i = 0; i < n; ++i)g.vextex[i] = a+i;return g;}//銷毀圖void destroyGraph(Graph* g){free(g->vextex);g->vextex = NULL;for(int i = 0; i < g->vextexNum;++i)free(g->matrix[i]);free(g->matrix);g->matrix = NULL;g->arcNum = -1;g->vextexNum = -1;}//增加一條邊bool addArc(Graph* g,char vex1,char vex2){int m = vex1-'A';int n = vex2-'A';if(m < 0 || m > g->vextexNum || n < 0 || n > g->vextexNum ){printf("2 vertexes must in the graph\n");return false;}else{g->matrix[m][n] = 1;g->matrix[n][m] = 1;++g->arcNum;return true;}}//刪除一條邊bool deleteArc(Graph* g,char vex1,char vex2){int m = vex1-'A';int n = vex2-'A';if(0 == g->matrix[m][n]){printf("this arc does not exsit!\n");return false;}else{g->matrix[m][n] = 0;g->matrix[n][m] = 0;g->arcNum--;return true;}}//顯示圖void showGraph(Graph* g){printf("  ");for(int i = 0; i < g->vextexNum;++i)printf("%c ",g->vextex[i]);for(int i = 0; i < g->vextexNum;++i){printf("\n");printf("%c ",g->vextex[i]);for(int j = 0;j < g->vextexNum;++j)printf("%d ",g->matrix[i][j]);}printf("\n");}//圖的遍曆://深度優先搜尋的遞迴實現//需要使用全域變數記錄某個頂點是否被訪問過bool visited_DFS[MAX];void DFS(Graph* g){printf("使用遞迴深度優先遍曆:\n依次訪問節點:");//訪問開始前,所有節點均未被訪問過for(int i = 0;i < g->vextexNum;++i)visited_DFS[i] = false;//對於每個節點,如果它未被訪問,則調用深度優先搜尋for(int i = 0; i < g->vextexNum;++i){if(false == visited_DFS[i])do_DFS(g,i);}printf("\n");}void do_DFS(Graph* g,int i){visited_DFS[i] = true;printf("%c ",g->vextex[i]);//依次搜尋i的鄰接點for(int j = 0; j < g->vextexNum;++j){//如果j是i的鄰接點,且未被訪問過,則對j調用深度優先搜尋if(visited_DFS[j] == false && 1 == g->matrix[i][j])do_DFS(g,j);}}void DFS_byStack(Graph* g,char Start){int start = Start-'A';printf("使用隊列進行深度優先遍曆:\n依次訪問節點:");//建立數組來標記節點是否被訪問過bool *is_visited = (bool*)malloc(sizeof(bool) * g->vextexNum);for(int i = 0;i < g->vextexNum;++i)is_visited[i] = false;//建立一個棧Stack s;s = initStack(g->vextexNum);push(&s,start);is_visited[start] = true;while(!is_empty(&s)){int vertex;pop(&s,&vertex);int adjacentNumber;int* adjacentVertex = findAdjacentVertex(g,vertex,&adjacentNumber);for(int i = 0;i < adjacentNumber;++i){if(false == is_visited[adjacentVertex[i]]){push(&s,adjacentVertex[i]);is_visited[adjacentVertex[i]] = true;}}printf("%c ",g->vextex[vertex]);}printf("\n");destroyStack(&s);free(is_visited);}//尋找與vex相鄰的節點,通過n帶出結點個數,返回這些節點群組成的數組int* findAdjacentVertex(Graph* g,int vex,int *n){//計數器int cnt = 0;int* adj = (int*)malloc(sizeof(int) * g->vextexNum);for(int i = 0;i < g->vextexNum;++i){if(1 == g->matrix[vex][i])adj[cnt++] = i;}*n = cnt;return adj;}void BFS_byQueue(Graph *g,char Start){int start = Start-'A';printf("使用棧進行廣度優先遍曆:\n依次訪問節點:");//建立數組來標記節點是否被訪問過bool *is_visited = (bool*)malloc(sizeof(bool) * g->vextexNum);for(int i = 0;i < g->vextexNum;++i)is_visited[i] = false;Queue q;q = initQueue(g->vextexNum);enqueue(&q,&start);is_visited[start] = true;while(!is_empty(&q)){int vertex;dequeue(&q,&vertex);int adjacentNumber;int* adjacentVertex = findAdjacentVertex(g,vertex,&adjacentNumber);for(int i = 0; i < adjacentNumber;++i){if(false == is_visited[adjacentVertex[i]] ){enqueue(&q,&adjacentVertex[i]);is_visited[adjacentVertex[i]] = true;}}printf("%c ",g->vextex[vertex]);}free(is_visited);destroyQueue(&q);}

注意到,圖的深度優先遍曆既可以使用棧來實現,也可以使用對歸來實現。這與我們之前的認識是相一致的。

程式中使用的棧和隊列的代碼就不往出貼了,之前的部落格都介紹過。主要區別在於這次是通過函數的傳回值來獲得一個棧或者隊列,以前是把指向它們的指標傳到函數裡。但是二者的思想是大同小異的。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.