一.圖的存儲結構
1. 鄰接矩陣(順序存儲結構)
typedef struct
{
int no; //頂點編號
char info; //頂點其他信息
}VertexType; //頂點類型
typedef struct
{
int edges[maxSize][maxSize]; //鄰接矩陣定義
int n,e; //分別爲頂點數和邊數
VertexType vex[maxSize]; //存放結點信息
}MGraph; //圖的鄰接矩陣類型
2. 鄰接表(鏈式存儲結構)
typedef struct ArcNode
{ //邊結點表
int adjvex; //該邊所指向的結點的位置
struct ArcNode *nextarc; //指向下一條邊的指針
int info; //該邊的相關信息
}ArcNode;
typedef struct
{ //頂點表
char data; //頂點信息
ArcNode *firstarc; //指向第一條邊的指針
}VNode;
typedef struct
{
VNode adjlist[maxSize]; //鄰接表
int n,e; //頂點數和邊數
}AGraph; //圖的鄰接表類型
二.圖的遍歷算法
1. DFS深度優先搜索遍歷
算法執行過程:任取一個頂點,訪問之,然後檢查這個頂點的所有鄰接頂點,遞歸訪問其中未被訪問過的頂點。
int visit[maxSize];
/* v是起點編號,visit[]是一個全局數組,作爲頂點的訪問標記,初始時所有元素*/
/* 均爲0,表示所有頂點都未被訪問過。因圖中可能存在迴路,當前經過的頂點在將*/
/* 來還可能再次經過,所以要對每個頂點進行標記,以免重複訪問*/
void DFS(AGraph *G,int v)
{
ArcNode *p;
visit[v] = 1; //置已訪問標記
Visit(v);
p = G->adjlist[v].firstarc; //p指向頂點v的第一條邊
while(p != NULL)
{
if(visit[p->adjvex] == 0) //若頂點未訪問,則遞歸訪問它
DFS(G,p->adjvex);
p = p->nextarc; //p指向頂點v的下一條邊的終點
}
}
2. BFS廣度優先搜索遍歷
算法執行過程:
1). 任取圖中一個頂點訪問,入隊,並將這個頂點標記爲已訪問
2). 當隊列不空時循環執行:出隊,依次檢查出隊結點的所有鄰接頂點,訪問沒有被訪問過的鄰接頂點並將其入隊
3). 當隊列爲空時跳出循環,廣度優先搜索即完成
void BFS(AGraph *G,int v,int visit[maxSize])
{ //visit[]數組被初始化爲全0
ArcNode *p;
int que[maxSize];
int front = 0,rear = 0;
int j;
Visit(v);
visit[v] = 1;
rear = (rear + 1) % maxSize;
que[rear] = v;
while(front != rear) //隊空的時候說明遍歷完成
{
front = (front + 1) % maxSize; //頂點出隊
j = que[front];
p = G->adjlist[j].firstarc; //p指向出隊頂點j的第一條邊
while(p != NULL)
{
if(visit[p->adjvex] == 0) //當前鄰接頂點未被訪問,則入隊
{
Visit(p->adjvex);
visit[p->adjvex] = 1;
rear = (rear + 1) % maxSize; //該頂點入隊
que[rear] = p->adjvex;
}
p = p->nextarc; //p指向j的下一條邊
}
}
}
3.
int BFS(AGraph *G,int v)
{
/*本題用鄰接表作爲圖的存儲結構*/
ArcNode *p;
int que[maxSize],front = 0,rear = 0;
int visit[maxSize];
int i,j;
for(i=0;i<G->n;++i)
visit[i] = 0; //將visit數組全部初始化爲0
rear = (rear + 1) % maxSize;
que[rear] = v;
visit[v] = 1;
while(front != rear)
{
front = (front + 1) % maxSize;
j = que[front];
p = G->adjlist[j].firstarc;
while(p != NULL)
{
if(visit[p->adjvex] == 0)
{
visit[p->adjvex] = 1;
rear = (rear + 1) % maxSize;
que[rear] = p->adjvex;
}
p = p->nextarc;
}
}
return j; //隊空時,j保存了遍歷過程中的最後一個頂點
}
4.
void DFS(AGraph *G,int v,int &vn,int &en)
{
ArcNode *p;
visit[v] = 1;
++vn; //本題中對當前頂點的訪問即爲vn計數器自增1
p = G->adjlist[v].firstarc;
while(p != NULL)
{
++en; //邊數自增1
if(visit[p->adjvex] == 0)
DFS(G,p->adjvex,vn,en);
p = p->nextarc;
}
}
int GisTree(AGraph *G)
{
int vn = 0,en = 0;
for(i=0;i<G->n;++i)
visit[i] = 0;
DFS(G,1,vn,en);
if(vn == G->n && (G->n-1) == en/2) //在每次來到一個新頂點,en累加了當前訪問頂點的所有邊,也就是說每條邊訪問了兩次
/*如果遍歷過程中訪問過的頂點數和圖中的頂點數相等,且邊數等於頂點數減1,則證明是樹*/
return 1;
else
return 0;
}
5.
int DFSTrave(AGraph *G,int i,int j)
{
int k;
for(k=0;k<G->n;++k)
visit[k] = 0;
DFS(G,i);
if(visit[j] == 1) //visit[j]等於1則證明訪問過程中遇到了j
return 1;
else
return 0;
}
三.最小生成樹
1.普里姆算法
void Prim(MGraph g,int v0,int &sum)
{
int lowcost[maxSize],vset[maxSize],v;
int i,j,k,min;
v = v0;
for(i=0;i<g.n;++i)
{
lowcost[i] = g.edges[v0][i]; //頂點v0到其餘各定點的長度
vset[i] = 0; //vset全部置爲0
}
vset[v0] = 1; //將v0併入樹中
sum = 0; //sum清零用來累計樹的權值
for(i=0;i<g.n-1;++i) //循環n-1次
{
min = INF;
/*下面這個循環用於選出侯選邊中的最小值*/
for(j=0;j<g.n;++j)
{
if(vset[j] == 0 && lowcost[j] < min) //選出當前生成樹到其餘各頂點最短邊中的最短的一條
{
min = lowcost[j];
k = j;
}
}
vset[k] = 1;
v = k;
sum += min; //這裏用sum記錄了最小生成樹的權值
/*下面這個循環以剛併入的頂點v爲媒介更新候選邊*/
for(j=0;j<g.n;++j)
{
if(vset[j] == 0 && g.edges[v][j] < lowcost[j]) //併入生成樹的頂點無需更新
lowcost[j] = g.edges[v][j];
}
}
}
2.克魯斯卡爾算法
typedef struct
{
int a,b; //a,b爲一條邊所連的兩個頂點
int w; //邊的權值
}Road;
Road road[maxSize];
int v[maxSize]; //定義並查集數組
int getRoot(int a)
{
while(a != v[a])
a = v[a];
return a;
}
void Kruskal(MGraph g,int &sum,Road road[])
{
int i;
int N,E,a,b;
N = g.n; //頂點數
E = g.e; //邊數
sum = 0;
for(i=0;i<N;++i)
v[i] = i;
sort(road,E); //對road數組中的E條邊按其權值從小到大排序
for(i=0;i<E;++i)
{
a = getRoot(road[i].a);
b = getRoot(road[i].b);
if(a != b)
{
v[a] = b; //把b賦值給a,即將b作爲a的父結點
sum += road[i].w;
}
}
}