頂點(Vertex)
有向邊、無向邊
簡單圖形(Simple Graph):沒有從頂點連到自己的邊,或同一條邊不重複出現
無向完全圖:任意兩頂點都存在著一條邊,共n(n-1)/2條邊
有向完全圖:任意兩頂點都存在方向互為相反的兩條邊,共n(n-1)條邊
權 -> 加權的圖形通常稱為網
度(內分支度、外分支度)
連通:兩頂點之間存在路徑;若任意兩頂點皆為連通的 -> 連通圖;有向稱為強連通圖
展開樹:無向圖形連通且n個頂點n-1條邊;有向圖中一頂點內分支度為0,其餘頂點內分支度為1 -> 有向樹
儲存結構:
- 鄰接矩陣(Adjacency Matrix):一維陣列儲存圖形中的頂點;二維陣列儲存圖形中邊的資訊;無向圖是一個對稱矩陣
typedef char VertexType;
typedef int EdgeType;
#define MAXVEX 100
#define INFINITY 65535
typedef struct MGraph {
VertexType vexs[MAXVEX];
EdgeType arc[MAXVEX][MAXVEX];
int numVertexes, numEdges;
} MGraph;
對於邊數較少的圖形,浪費空間
- 鄰接串列(Adjacency List):一維陣列儲存圖形中的頂點,串列儲存頂點的第一個鄰接點(無向圖);有向圖為外邊集合和內邊集合
typedef char VertexType;
typedef int EdgeType;
typedef struct EdgeNode {
int adjvex;
EdgeType weight;
struct EdgeNode *next;
} EdgeNode;
typedef struct VertexNode {
VertexType data;
EdgeNode *firstedge;
} VertexNode, AdjList[MAXVEX]
typedef struct GraphAdjList {
AdjList adjList;
int numVertexes, numEdges;
} GraphAdjList;
對於有向圖,只能了解內分支度或外分支度
- 十字鏈結串列(有向圖應用中優秀的儲存結構):一維陣列儲存圖形中的頂點,其他如下
// |data|firstin|firstout|
// firstin: 內邊集合的首指標
// firstout: 外邊集合的首指標
// |tailvex|headvex|headlink|taillink|
// tailvex: 邊起點編號
// headvex: 邊終點編號
// headlink: 內邊集合,指向與終點相同的下一條邊
// taillink: 外邊集合,指向與起點相同的下一條邊
鄰接串列要刪除邊,要操作兩個邊集合節點
- 鄰接多重鏈結串列(無向圖應用中優秀的儲存結構):
// |ivex|ilink|jvex|jlink|
// ivex和jvex構成一條邊
// ilink指向依附頂點ivex的下一條邊、jlink指向依附頂點jvex的下一條邊
- 邊集陣列:一維陣列儲存圖形中的頂點,一維陣列儲存邊的資訊
適合對邊依次進行處理,不適合對頂點做操作
// |begin|end|weight|
// begin: 邊起點編號
// end: 邊終點編號
// weight: 權值
- 深度優先走訪(Depth-First Search)
typedef int Boolean;
Boolean visited[MAX];
// ********** 鄰接矩陣 **********
void DFS(MGraph G, int i) {
ing j;
visited[i] = True;
printf("%c ", G.vex[i]);
for (j = 0; j < G.numVertexes; j++)
if (G.arc[i][j] == 1 && !visited[j])
DFS(G, j);
}
void DFSTraverse(MGraph G) {
int i;
for (i = 0; i < G.numVertexes; i++)
visited[i] = False;
for (i = 0; i < G.numVertexes; i++)
if (!visited[i])
DFS(G, i);
}
// ******************************
// ********** 鄰接串列 **********
void DFS(GraphAdkList GL, int i) {
EdgeNode *p;
visited[i] = True;
printf("%c ", GL->adjList[i].data);
p = GL->adjList[i].firstedge;
while (p) {
if (!visited[p->adjvex])
DFS(GL, p->adjvex);
p = p->next;
}
}
void DFSTraverse(GraphAdkList GL) {
int i;
for (i = 0; i < GL->numVertexes; i++)
visited[i] = False;
for (i = 0; i < GL->numVertexes; i++)
if (!visited[i])
DFS(GL, i);
}
// ******************************
- 廣度優先走訪(Breadth-First Search)
// ********** 鄰接矩陣 **********
void BFSTraverse(MGraph G) {
int i, j;
Queue Q;
for (i = 0; i < G.numVertexes; i++)
visited[i] = False;
InitQueue(&Q);
for (i = 0; i < G.numVertexes; i++) {
if (!visited[i]) {
visited[i] = True;
printf("%c ", G.vexs[i]);
EnQueue(&Q, i);
while (!QueueEmpty(Q)) {
DeQueue(&Q, &i);
for (j = 0; j < G.numVertexes; j++) {
if (G.arc[i][j] && !visited[j]) {
visited[j] = True;
printf("%c ", G.vexs[j]);
EnQueue(&Q, j);
}
}
}
}
}
}
// ******************************
// ********** 鄰接串列 **********
void BFSTraverse(GraphAdkList GL) {
int i;
EdgeNode *p;
Queue Q;
for (i = 0; i < GL->numVertexes; i++)
visited[i] = False;
InitQueue(&Q);
for (i = 0; i < GL->numVertexes; i++) {
if (!visited[i]) {
visited[i] = True;
printf("%c ", GL->adjList[i].data);
EnQueue(&Q, i);
while (!QueueEmpty(Q)) {
DeQueue(&Q, &i);
p = GL->adjList[i].firstedge;
while (p) {
if (!visited[p->adjvex]) {
visited[p->adjvex] = True;
printf("%c ", GL->adjList[p->adjvex].data);
EnQueue(&Q, p->adjvex);
}
p = p->next;
}
}
}
}
}
// ******************************
- 最小展開樹(Minimum Cost Spanning Tree)
普裡姆(Prim)演算法
void MiniSpantree_Prim(MGraph G) {
int min, i, j, k;
int adjvex[MAXVEX];
int lowcost[MAXVEX];
for (i = 1; i < G.numVertexes; i++) {
lowcost[i] = G.arc[0][i];
adjvex[i] = 0;
}
for (i = 1; i < G.numVertexes; i++) {
min = INFINITY;
j = 1; k = 0;
while (j < G.numVertexes) {
if (lowcost[j] != 0 && lowcost[j] < min) {
min = lowcost[j];
k = j;
}
j++;
}
printf("(%d, %d)", adjvex[k], k);
lowcost[k] = 0;
for (j = 1; j < G.numVertexes; j++) {
if (lowcost[j] != 0 && G.arc[k][j] < lowcost[j]) {
lowcost[j] = G.arc[k][j];
adjvex[j] = k;
}
}
}
}
克魯斯卡爾(Kruskal)演算法
// 要有由小排到大的邊集合陣列
int Find(int *parent, int f) {
while (parent[f] > 0)
f = parent[f];
return f;
}
void MiniSpantree_Kruskal(MGraph G) {
int i, n, m;
Edge edges[MAXEDGE];
int parent[MAXVEX];
for (i = 0; i < G.numVertexes; i++)
parent[i] = 0;
for (i = 0; i < G.numEdge; i++) {
n = Find(parent, edges[i].begin);
m = Find(parent, edges[i].end);
if (n != m) {
parent[n] = m;
printf("(%d, %d) %d ", edges[i].begin, edges[i].end, edges[i].weight);
}
}
}
- 最短路徑
迪傑斯特拉(Dijkstra)演算法
void ShortestPath_Dijkstra(MGraph G, int v0, Pathmatrix *P, ShortPathTable *D) {
// D用來以目前找到的最短路徑對其他頂點進行計算 (最後為v0到各頂點的最短路徑)
// P紀錄前驅者
int v, w, k, min;
int final[MAXVEX];
for (v = 0; v < G.numVertexes; v++) {
final[v] = 0;
(*D)[v] = G.matrix[v0][v];
(*p)[v] = 0;
}
(*D)[v0] = 0;
final[v0] = 1;
for (v = 1; v < G.numVertexes; v++) {
min = INFINITY; // 尋找離v0最近的頂點
for (w = 0; w < G.numVertexes; w++) {
if (!final[w] && (*D)[w] < min) {
k = w;
min = (*D)[w];
}
}
final[k] = 1; // 修正當前最短路徑及距離
for (w = 0; w < G.numVertexes; w++) {
if (!final[w] && (min + G.matrix[k][w] < (*D)[w])) {
(*D)[w] = min + G.matrix[k][w];
(*P)[w] = k;
}
}
}
}
佛洛德(Floyd)演算法
// D^-1: 網圖的鄰接矩陣
// P^-1: 儲存路徑
void ShortestPath_Floyd(MGraph G, Pathmatrix *P, ShortPathTable) {
int v, w, k;
for (v = 0;v < G.numVertexes; ++v) {
for (w = 0; w < G.numVertexes; ++w) {
(*D)[v][w] = G.matrix[v][w];
(*P)[v][w] = w;
}
}
for (k = 0; k < G.numVertexes; ++k) {
for (v = 0; v < G.numVertexes; ++v) {
for (w = 0; w < G.numVertexes; ++w) {
if ((*D)[v][w] > (*D)[v][k] + (*D)[k][w]) {
(*D)[v][w] = (*D)[v][k] + (*D)[k][w];
(*P)[v][w] = (*P)[v][k];
}
}
}
}
}
// 最短路徑顯示
for (v = 0; v < G.numVertexes; ++v) {
for (w = v + 1; w < G.numVertexes; w++) {
printf("v%d-v%d weight: %d ", v, w, D[v][w]);
k = P[v][w];
printf(" path: %d", v);
while(k != w) {
printf(" -> %d", k);
k = P[k][w];
}
printf(" -> %d\n", w);
}
printf("\n");
}
- 拓樸排序(Topological Sorting):對一個有向圖架構拓樸序列過程
AOV網路(Activity On Vertex Network):圖中的邊表示活動之間存在某種制約關係
設G=(V, E)是一個具有n個頂點的有向圖,若Vi到Vj有一條路徑,則在頂點序列,頂點Vi必定在頂點Vj之前,這樣的頂點序列稱為拓樸序列;若頂點輸出數少了,則代表這個網圖有環路存在
typedef struct EdgeNode {
int adjvex;
int weight;
struct EdgeNode *next;
} EdgeNode;
// |in|data|firstedge|
typedef struct VertexNode {
int in;
int data;
EdgeNode *firstedge;
} VertexNode, AdjList[MAXVEX];
typedef struct {
AdjList adjList;
int numVertexes, numEdge;
} graphAdjList, *GraphAdjList;
Status TopologicalSort(GraphAdjList GL) {
EdgeNode *e;
int i = 0, gettop, k;
int top = 0;
int count = 0;
int *stack;
stack = (int *)malloc(GL->numVertexes * sizeof(int));
for (i = 0; i < GL->numVertexes; i++)
if (GL->adjList[i].in == 0)
stack[++top] = i;
while (top != 0) {
gettop = stack[top--];
printf("%d -> ", GL->adjList[gettop].data);
count++;
for (e = GL->adjList[gettop].firstedge; e; e = e->next) {
k = e->adjvex;
if (!(--GL->adjList[k].in))
stack[++top] = k;
}
}
if (count < GL->numVertexes)
return ERROR;
else
return OK;
}
- 關鍵路徑
AOE網路(Activity On Edge Network):沒有內邊的頂點稱為起點,沒有外邊的頂點稱為終點(只有一個起點一個終點)
從起點到終點具有最大長度的路徑稱為關鍵路徑
int *etv, *ltv; // 事件最早發生和最晚發生的時間陣列
int *stack2; // 拓樸序列堆疊
int top2; // stack2的指標
Status TopologicalSort(GraphAdjList GL) {
EdgeNode *e;
int i = 0, gettop, k;
int top = 0;
int count = 0;
int *stack;
stack = (int *)malloc(GL->numVertexes * sizeof(int));
for (i = 0; i < GL->numVertexes; i++)
if (GL->adjList[i].in == 0)
stack[++top] = i;
top2 = 0;
etv = (int *)malloc(GL->numVertexes * sizeof(int));
for (i = 0; i < GL->numVertexes; i++)
etv[i] = 0;
stack2 = (int *)malloc(GL->numVertexes * sizeof(int));
while (top != 0) {
gettop = stack[top--];
count++;
stack2[++top2] = gettop;
for (e = GL->adjList[gettop].firstedge; e; e = e->next) {
k = e->adjvex;
if (!(--GL->adjList[k].in))
stack[++top] = k;
if ((etv[gettop] + e->weight) > etv[k])
etv[k] = etv[gettop] + e->weight;
}
}
if (count < GL->numVertexes)
return ERROR;
else
return OK;
}
void CriticalPath(GraphAdjList GL) {
EdgeNode *e;
int i, gettop, k, j;
int ete, lte;
TopologicalSort(GL);
ltv = (int *)malloc(GL->numVertexes * sizeof(int));
for (i = 0; i < GL->numVertexes; i++)
ltv[i] = etv[GL->numVertexes - 1];
while (top2 != 0) {
gettop = stack2[top2--];
for (e = GL->adjList[gettop].firstedge; e; e = e->next) {
k = e->adjList;
if(ltv[k] - e->weight < ltv[gettop])
ltv[gettop] = ltv[k] - e->weight;
}
}
for (j = 0; j < GL->numVertexes; j++) {
for (e = GL->adjList[gettop].firstedge; e; e = e->next) {
k = e->adjvex;
ete = etv[j];
lte = ltv[k] - e->weight;
if (ete == lte)
printf("<v%d, v%d> length: %d, ",
GL->adjList[j].data,
GL->adjList[k].data,
e->weight);
}
}
}