一、基本概念
圖是由頂點的有窮非空集合和頂點之間邊的集合組成,通常表示爲:G(V,E),其中G表示一個圖,V表示圖中邊的集合。
連通圖:圖中任意兩點都能連通。
帶權值的圖稱之爲網。
樹:沒有環路的連通圖。
生成樹:由n-1條邊將n個頂點連接成的連通圖。
最小生成樹:最小權值的生成樹稱爲最小生成樹。
二、圖的存儲結構
順序存儲:
- 鄰接矩陣
鏈式存儲:
- 鄰接表
- 十字鏈表
漫畫圖結構
三、算法
遍歷:從圖中某一頂點出發,依次訪問圖中其餘頂點,且每個頂點僅被訪問一次。
深度優先遍歷(DFS)
DFS原理:從圖中某個頂點出發,先訪問此頂點,再依次訪問它的每個未被訪問過的鄰接點,重複上述操作,直到所有頂點都被訪問。
DFS本質上就是樹的先序遍歷,需要增加一個訪問數組標記當前結點是否被訪問過,核心算法就是遞歸。
- 鄰接矩陣深度遍歷算法
void DFS(MGraph* G, int i){//深度遞歸遍歷
visited[i] = true;
printf("%c\n", G->vexter[i]);
for(int j = 0; j < G-->num_vertex; j++){
if(G->arc[i][j] == 1 && !visit[j]){
DFS(G, j);
}
}
}
void DFSTraverse(MGraph* G){//深度優先遍歷
int i = 0;
for(i = 0; i < G->num_vertex; i++){
visited[i] = false; //初始化訪問數組
}
for( i = 0; i < G->num_vertex; i++){
if(!visited[i]){
DFS(G, i); //非連通圖設計,連通圖只執行一次
}
}
}
時間複雜度爲O(n2)
- 鄰接表深度遍歷算法
void DFS(MGraph* G, int i){
EdgeNode* p;
visted[i] = true;
printf("%d\n", G->adjl[i].data);
p = G->firstedge;
while(p){
if(!visited[p->adjvex]){
DFS(G, p-adjvex);
}
p = p->next;
}
}
void DFSTraversal(MGraph* G){
for(int i = 0; i < G->num_vertex; i++){
vidited[i] = false;
}
for(int i = 0; i < G->num_vertex; i++){
if(!visited[i]){
DFS(G, i);
}
}
}
時間複雜度爲O(n + e)
廣度優先遍歷(BFS)
時間複雜度和DFS相同
- 鄰接矩陣廣度優先遍歷
void BFSTraverse(GL* G) {
int i = 0, j = 0;
Queue Q;
InitQueue(Q);
for (i = 0; i < G->num_vertex; i++) {
visited[i] = false;
}
for (i = 0; i < G->num_vertex; i++) {
if (!visited[i]) {
visited[i] = true;
printf("%c\n", G->vexs[i]);
Enqueue(Q, i);
while (!Empty(Q)) {
Dequeue(Q, &i);
for (int j = 0; j < G->num_vertexs; j++) {
if ((G->arc[i][j] == 1) && (!visited[j])) {
visited[j] = true;
printf("%c\n", G->vexs[j]);
Enqueue(Q, j);
}
}
}
}
}
}
- 鄰接表廣度優先遍歷
int BFSTraverse(GL* G) {
EdgeNode* p = NULL;
int i = 0;
Queue Q;
InitQueue(Q);
for (i = 0; i < G->num_vertex; i++) { //訪問數組初始化
visited[i] = false;
}
for (i = 0; i < G->num_vertex; i++) { //訪問所有頂點
if (!visited[i]) {
visited[i] = true;
printf("%c\n", G->vertex.data); //訪問頂點
EnQueue(Q, i);
while (!QueueEmpty(Q)) {
Dequeue(Q, &i);
p = G->adjList[i].firstedge; //訪問鄰接點
while (p) {
if (!visited[p->adjvex]) {
visited[p->adjvex] = true;
printf("%c\n", G->adjList[p->adjvex].data);
EnQueue(Q, p->adjvex);
}
p = p->next;
}
}
}
}
}
最小生成樹算法
- Prim算法是走一步看一步的思維方式。從任意一個頂點出發,每次添加最短邊,逐步生成最小生成樹。
時間複雜度爲O(n2),適合稠密圖算法。 - Kruskal算法是全局思維方式。直接從最短邊開始,每次添加最短邊。
時間複雜度爲O(elbe),適合稀疏圖算法
最短路徑算法
- 迪傑斯特拉算法
- 弗洛伊德算法