算法與數據結構(c語言)——圖(Figure,三):遍歷操作

圖的遍歷

從圖的某個頂點出發訪問遍圖中所有頂點,且每個頂點僅被訪問一次,這一過程便叫做圖的遍歷。

深度優先遍歷

也有稱深度優先搜索(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;
}

 

運行結果:

對比兩種遍歷算法,它們在時間複雜度上是一樣的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章