目錄
1 前奏(鄰接表)
圖作爲種比較繁瑣的數據結構,在進行圖的操作之前,首先應該用合適的數據類型來存儲圖的信息。
我們使用鄰接表來存儲,它是一種鏈式的存儲結構。所謂鄰接表就是對途中的每個頂點建立一個單鏈表。看一下下面的定義就明白了。
鄰接表的定義如下:
//邊
typedef struct ArcNode
{
int adjvex;//該邊所指向的節點的位置
struct ArcNode* nextarc;//指向下一條邊的指針
int info;//存儲邊的相關信息,比如權重
}ArcNode;
//頂點
typedef struct VNode
{
char data;//頂點信息
ArcNode* firstarc;//指向第一條邊的指針
}VNode;
//鄰接表的定義
typedef struct AGraph
{
VNode adjlist[MAXSIZE];//鄰接表,裏面存儲着圖種的所有頂點
int n;//頂點的個數
int e;//邊的個數
}AGraph;
2 深度遍歷
圖的深度優先搜索遍歷(DFS)類似於二叉樹的先序遍歷。它的基本思想是,從訪問節點v出發,並將其標爲已訪問過,然後選取與v鄰接的未被訪問過的任何一個頂點w,並訪問它,再選取與w鄰接的未被訪問的任一頂點並訪問,以重複進行。當一個頂點所有的鄰接頂點都被訪問過時,則依次退回到最近被訪問過的頂點,若該頂點還有其他鄰接頂點未被訪問,則從這些未被訪問的頂點中取一個頂點並重覆上述訪問過程,直至所有的頂點都被訪問過爲止。
我們假設從頂點A出發開始訪問,那麼可能的訪問序列有:
- A,D,C,E,B
- A,D,E,C,V
- A,C,D,B,E
不限於以上三種,這與圖的鄰接表有關。
接下來是代碼的實現:
首先我們看一個連通圖的深度優先搜索遍歷:
int vist[MAXSIZE];//用於記錄訪問過的節點,0代表未訪問,1代表已訪問
//G爲鄰接表,v爲起始節點
void DFS(AGraph* G, int v)
{
ArcNode* p;
vist[v] = 1;
cout << v << endl;
p = G->adjlist[v].firstarc;//p指向v節點的第一條邊。
while (p != NULL)
{
if (visit[p->adjvex] == 0)
DFS(G, p->adjvex);//遞歸的進行訪問
p = p->nextarc;//指向下一條邊
}
}
上述的代碼只適合連通圖,如果一個圖中有孤立的節點,即當這個圖不是連通圖的時,就應該從所有的節點開始進行遍歷,如下:
void dfs(AGraph* g)
{
for (int i = 0; i < g->n; ++i)
if(visit[i]==0)
DFS(g, i);
}
3 廣度遍歷
圖的廣度優先搜索遍歷類似於樹的層次遍歷。它的基本思想是:首先訪問起始頂點v,然後選取與v鄰接的全部頂點進行訪問,再依次訪問鄰接頂點的鄰接頂點,以此類推,直至所有的頂點都被訪問。
上述圖從A出發,我們假設D,C,B分別是A指向的第一個、第二個和第三個節點,那麼廣度遍歷的序列爲:
A,D,C,B,E
連通圖的廣度遍歷的代碼如下所示:
void BFS(AGraph* G, int v, int visti[MAXSIZE])
{
ArcNode* p;
queue<int> que;
cout << v << endl;
visit[v] = 1;
que.push(v);
while (!que.empty())
{
int j = que.top();//得到隊頭元素
que.pop();//出隊
p = G->adjlist[j].firstaec;
while (p != NULL)
{
if (visit[] p->adjvex == 0)
{
cout << p->adjvex << endl;
visit[p->adjvex] = 1;
que.push(p->adjvex);
}
p = p->nextarc;//指向下一條邊
}
}
}
上述代碼是針對連通圖的,那麼對於非連通圖,需要從不同的節點開始進行訪問,充分考慮到孤立節點:
void bfs(AGraph* g)
{
for (int i = 0; i < g->n; ++i)
if(visit[i]==0)
BFS(g, i);
}