圖
六度空間理論
圖中兩個頂點若要聯系,最多通過6個結點便可以完成 。
基本概念
-
圖用於表示“多對多”的關系。
-
包含
-
一組頂點:通常用V (Vertex) 表示頂點集合
-
一組邊:通常用E (Edge) 表示邊的集合
-
邊是頂點對:(v, w) E ,其中v, w V
-
有向邊< v, w> 表示從v指向w的邊(單行線)
-
不考慮重邊和自回路
- 重邊:兩個頂點之間有兩條邊。
- 自回路:一個頂點的邊指向自己。
抽象數據類型定義
- 類型名稱:圖(Graph)
- 數據對象集:G(V,E)由一個非空的有限頂點集合V和一個有限邊集合E組成。
- 操作集:對於任意圖G Graph,以及v V, e E
- Graph Create():建立並返回空圖;
- Graph InsertVertex(Graph G, Vertex v):將v插入G;
- Graph InsertEdge(Graph G, Edge e):將e插入G;
- void DFS(Graph G, Vertex v):從頂點v出發深度優先遍歷圖G;
- void BFS(Graph G, Vertex v):從頂點v出發寬度優先遍歷圖G;
- void ShortestPath(Graph G, Vertex v, int Dist[]):計
算圖G中頂點v到任意其他頂點的最短距離; - void MST(Graph G):計算圖G的最小生成樹;
怎么在程序中表示一個圖
- 對頂點進行編號。
- 頂點序號所對應在鄰接矩陣中的值為1。
- 因為沒有自回路,所以對角線全為0。
- 無向圖為對稱矩陣。
問題:對於無向圖的存儲,怎樣可以省一半空間?
- 用一個長度為N(N+1)/2的1維數組A存儲{G00,G10,G11,……,Gn-1 0,…,Gn-1 n-1},則Gij在A中對應的下標是:(i*(i+1)/2+j)。
-對於網絡,只要把G[i][j]的值定義為邊<vi,vj>的權重即可。
鄰接矩陣—— 有什么好處?
- 直觀、簡單、好理解
- 方便檢查任意一對頂點間是否存在邊
- 方便找任一頂點的所有“鄰接點”(有邊直接相連的頂點)
- 方便計算任一頂點的“度”(從該點發出的邊數為“出
度”,指向該點的邊數為“入度”) - 無向圖:對應行(或列)非0元素的個數
- 有向圖:對應行非0元素的個數是“出度”;對應列非0元素的
個數是“入度”
鄰接矩陣—— 有什么不好?
- 浪費空間—— 存稀疏圖(點很多而邊很少)有大量無效元素
- 對稠密圖(特別是完全圖)還是很合算的
- 浪費時間—— 統計稀疏圖中一共有多少條邊
鄰接表
- G[N]為指針數組,對應矩陣每行一個鏈表,只存非0元素
鄰接矩陣—— 有什么好處?
- 方便找任一頂點的所有“鄰接點”
- 節約稀疏圖的空間:需要N個頭指針+2E(每個節點至少兩個域)
- 方便計算任一頂點的“度”?:對無向圖:是的,對又想吐只能計算出度,需要構造“逆鄰接表”計算入度。
鄰接矩陣—— 有什么不好?
- 不方便檢查任意一對頂點間是否存在邊。
代碼實現
- 設定初始參數,最大值和權值頂點數
- 設定邊結點,用於連接頂點。
- 設定圖結點,建立頂點數,邊數,表示關系的鄰接矩陣。
- 設定插入函數,把邊插入到圖當中。
設定參數
- 設定INFINITY為最大值,便於權值的比較。
#define MaxVertexNum 100 /* 最大頂點數設為100 */
#define INFINITY 65535 /* ∞設為雙字節無符號整數的最大值65535*/
typedef int Vertex; /* 用頂點下標表示頂點,為整型 */
typedef int WeightType; /* 邊的權值設為整型 */
typedef char DataType; /* 頂點存儲的數據類型設為字符型 */
設定邊結點
- 包括邊結點、邊結點的指針、邊連接的兩個頂點和邊的權重。
/* 邊的定義 */
typedef struct ENode *PtrToENode;
struct ENode{
Vertex V1, V2; /* 有向邊<V1, V2> */
WeightType Weight; /* 權重 */
};
typedef PtrToENode Edge;
設定圖結點
- 包括圖結點、圖結點的指針、包含的頂點數、邊數、圖的鄰接矩陣、頂點存儲的數據。
/* 圖結點的定義 */
typedef struct GNode *PtrToGNode;
struct GNode{
int Nv; /* 頂點數 */
int Ne; /* 邊數 */
WeightType G[MaxVertexNum][MaxVertexNum]; /* 鄰接矩陣 */
DataType Data[MaxVertexNum]; /* 存頂點的數據 */
/* 注意:很多情況下,頂點無數據,此時Data[]可以不用出現 */
};
typedef PtrToGNode MGraph; /* 以鄰接矩陣存儲的圖類型 */
創建一個圖
- 創建一個圖結點並將頂點初始化,返回指針。
MGraph CreateGraph( int VertexNum )
{ /* 初始化一個有VertexNum個頂點但沒有邊的圖 */
Vertex V, W;
MGraph Graph;
Graph = (MGraph)malloc(sizeof(struct GNode)); /* 建立圖 */
Graph->Nv = VertexNum;
Graph->Ne = 0;
/* 初始化鄰接矩陣 */
/* 注意:這里默認頂點編號從0開始,到(Graph->Nv - 1) */
for (V=0; V<Graph->Nv; V++)
for (W=0; W<Graph->Nv; W++)
Graph->G[V][W] = INFINITY;
return Graph;
}
將邊插入到頂點當中
- 接受一個邊結點,把對應值的關系儲存。
- 若是無向圖要儲存兩次。
void InsertEdge( MGraph Graph, Edge E )
{
/* 插入邊 <V1, V2> */
Graph->G[E->V1][E->V2] = E->Weight;
/* 若是無向圖,還要插入邊<V2, V1> */
Graph->G[E->V2][E->V1] = E->Weight;
}
建立圖
MGraph BuildGraph()
{
MGraph Graph;
Edge E;
Vertex V;
int Nv, i;
scanf("%d", &Nv); /* 讀入頂點個數 */
Graph = CreateGraph(Nv); /* 初始化有Nv個頂點但沒有邊的圖 */
scanf("%d", &(Graph->Ne)); /* 讀入邊數 */
if ( Graph->Ne != 0 ) { /* 如果有邊 */
E = (Edge)malloc(sizeof(struct ENode)); /* 建立邊結點 */
/* 讀入邊,格式為"起點 終點 權重",插入鄰接矩陣 */
for (i=0; i<Graph->Ne; i++) {
scanf("%d %d %d", &E->V1, &E->V2, &E->Weight);
/* 注意:如果權重不是整型,Weight的讀入格式要改 */
InsertEdge( Graph, E );
}
}
/* 如果頂點有數據的話,讀入數據 */
for (V=0; V<Graph->Nv; V++)
scanf(" %c", &(Graph->Data[V]));
return Graph;
}