圖的遍歷
從圖的某個頂點出發訪問遍圖中所有頂點,且每個頂點僅被訪問一次,這一過程便叫做圖的遍歷。
深度優先遍歷
也有稱深度優先搜索(Depth First Search),簡稱DFS。有點像是樹的先序遍歷。
無向圖的鄰接矩陣存儲方式深度優先遍歷算法的代碼實現:
// 訪問標誌數組
bool visited[MAXVN];
// 鄰接矩陣深度優先遞歸算法
void DFS(MGraph G, int i) {
// 訪問這個頂點,此處只是簡單打印
printf("%c ", G.vexs[i]);
// 訪問過後將標記設置爲true
visited[i] = true;
// 開始操作結點i的鄰接點
for(int j = 0; j < G.vertexNum; j++) {
// 當頂點j未訪問過,同時頂點i和頂點j是相鄰接的,才進行遞歸調用。
if(G.arc[i][j] == 1 && !visited[j]) {
DFS(G, j);
}
}
}
// 鄰接矩陣的深度遍歷操作
void DFSTraverse(MGraph G) {
int i;
// 初始化所有頂點的初始化狀態爲未訪問
for(i = 0; i < G.vertexNum; i++) {
visited[i] = false;
}
for(i = 0; i < G.vertexNum; i++) {
// 對所有未訪問過的頂點調用DFS,若是連通圖,則只會調用一次
if(!visited[i]) {
DFS(G,i);
}
}
}
鄰接表結構存儲的形式,
// 鄰接表的深度優先遞歸算法
void DFS(MLGraph MLG, int i) {
EdgeNode *p = MLG.adjList[i].firstedge;
// 訪問這個頂點,此處只是簡單打印
printf("%c ", MLG.adjList[i].data);
// 訪問過後將標記設置爲true
visited[i] = true;
// 頂點i存在邊(有鄰接點),進行操作
while(p) {
// 當頂點i的鄰接點未被訪問過,才進行遞歸調用。
if(!visited[p->adjvex]) {
DFS(MLG, p->adjvex);
}
// 指向結點i的鄰接點
p = p->next;
}
}
對於有向圖而言,它只是對通道存在可行或不可行,算法上沒有變化。
廣度優先遍歷
又稱廣度優先搜索算法(Breadth-First-Search),BFS,有點像是圖的層序遍歷。
無向圖的鄰接表存儲方式廣度優先遍歷算法實現
// 鄰接表的廣度遍歷操作
void BFSTraverse(MLGraph G) {
Queue q;
EdgeNode *p;
InitQueue(&q);
for(int i = 0; i < G.vertexNum; i++) {
visited[i] = false;
}
for(int i = 0; i < G.vertexNum; i++) {
if(!visited[i]) {
// 訪問頂點,這裏只是簡單打印
printf("%c ",G.adjList[i].data);
// 設置訪問過後的標記
visited[i] = true;
// 將頂點的下標入隊列
EnQueue(&q,i);
// 隊列不爲空
while(!QueueEmpty(q)) {
// 出隊列
DeQueue(&q, &i);
// 將i結點的第一條邊賦值給指針p
p = G.adjList[i].firstedge;
// 循環判斷邊結點是否存在
while(p) {
// 判斷鄰接點是否被訪問過
if(!visited[p->adjvex]) {
printf("%c ",G.adjList[p->adjvex].data);
// 修改鄰接頂點的訪問標誌
visited[p->adjvex] = true;
// 鄰接點的下標入隊列
EnQueue(&q, p->adjvex);
}
// 指向下一個邊表結點
p = p->next;
}
}
}
}
}
鄰接矩陣存儲方式 :
/* 鄰接矩陣的廣度遍歷算法 */
void BFSTraverse(MGraph G) {
int i, j;
Queue Q;
for(i = 0; i < G.vertexNum; i++) {
visited[i] = false;
}
/* 初始化一輔助用的隊列 */
InitQueue(&Q);
/* 對每一個頂點做循環 */
for(i = 0; i < G.vertexNum; i++) {
/* 若是未訪問過就處理 */
if (!visited[i]) {
/* 設置當前頂點訪問過 */
visited[i]=TRUE;
/* 操作頂點,此處只是簡單打印 */
printf("%c ", G.vexs[i]);
/* 將此頂點下標入隊列 */
EnQueue(&Q,i);
/* 若當前隊列不爲空 */
while(!QueueEmpty(Q)) {
/* 將隊對元素出隊列,賦值給i */
DeQueue(&Q,&i);
for(j = 0; j < G.vertexNum; j++) {
/* 判斷其它頂點若與當前頂點存在邊且未訪問過 */
if(G.arc[i][j] == 1 && !visited[j]) {
/* 將找到的此頂點標記爲已訪問 */
visited[j] = true;
/* 打印頂點 */
printf("%c ", G.vexs[j]);
/* 將找到的此頂點入隊列 */
EnQueue(&Q,j);
}
}
}
}
}
}
用到的輔助隊列:
Status InitQueue(Queue *Q) {
Q->front = 0;
Q->rear = 0;
return OK;
}
/* 若隊列Q爲空隊列,則返回TRUE,否則返回FALSE */
bool QueueEmpty(Queue Q) {
if(Q.front == Q.rear) {
return true;
} else {
return false;
}
}
/* 若隊列未滿,則插入元素e爲Q新的隊尾元素 */
Status EnQueue(Queue *Q, int e) {
/* 隊列滿的判斷 */
if ((Q->rear+1)%MAXSIZE == Q->front) {
return ERROR;
}
/* 將元素e賦值給隊尾 */
Q->data[Q->rear]=e;
/* rear指針向後移一位置,若到最後則轉到數組頭部 */
Q->rear=(Q->rear+1)%MAXSIZE;
return OK;
}
/* 若隊列不空,則刪除Q中隊頭元素,用e返回其值 */
Status DeQueue(Queue *Q,int *e) {
/* 隊列空的判斷 */
if (Q->front == Q->rear) {
return ERROR;
}
/* 將隊頭元素賦值給e */
*e=Q->data[Q->front];
/* front指針向後移一位置, 若到最後則轉到數組頭部*/
Q->front=(Q->front+1)%MAXSIZE;
return OK;
}
運行結果:
對比兩種遍歷算法,它們在時間複雜度上是一樣的。