深さ優先探索DFSと幅優先探索BFS


グラフや木構造であらわされたデータを探索するときに、主に2つの手法がある。深さ優先探索と幅優先探索。

主な違いは、直列に進むか並列に進むか、次の探索候補点をスタックに入れるか待ち行列(キュー)に入れるか。

それぞれ木であらわすと、こんな感じ。番号は呼ばれる順番。

# 深さ優先探索 (行きがけ順) 
     1
   2   5
 3  4 6  7

# 幅優先探索
      1
    2   3
  4  5 6  7

DFSは木を降りていくが、BFSは兄弟木をすべてたどってから子の階層を辿る。

実装

次のプログラムでは、両方とも隣接行列が与えられたとき、すべてのデータを探索し、その順番を表示する。DFSは再帰版とスタック版がある。

DFSスタック版とBFSは、スタックを使うか、キューを使うかの違いなの、が実装を見るとよくわかる。

スタックとキューは配列を使い実装。

#include <stdio.h>
#define N 8
const int mat[N][N]= {
                {0,1,0,0,0,0,0,0},
                {1,0,1,1,0,0,0,0},
                {0,1,0,0,0,0,1,0},
                {0,1,0,0,1,0,0,0},
                {0,0,0,1,0,1,0,0},
                {0,0,0,0,1,0,1,1},
                {0,0,1,0,0,1,0,1},
                {0,0,0,0,0,1,1,0}};
int visited[N];

 // 深さ優先探索 Depth First Search 再帰ver
int dfs_rec(int i){
    if(visited[i]) return 0;
    visited[i] = 1;
    printf("%d ",i);            

    for(int j=0;j<N;j++){
        if( visited[j]==0 && mat[i][j]==1){
            dfs_rec(j);
        }
    }
}

// 深さ優先探索 stack ver.
int dfs(){
    int stack[100];
    int sp=0; // スタックポインタ
    int i,j,k;

    stack[sp++]=0; // 0 ~ 7 どれでも
    while(sp){
        k = stack[--sp];
        if(visited[k]) continue;
        visited[k]=1;
        printf("%d ",k);

        for(j=N-1;0<=j;j--){ //逆からにすると番号順にスタックから取り出される。
            if( !visited[j] && mat[k][j])
                stack[sp++]=j;
        }
        // for(i=0;i<sp;i++) // スタックの状態を出力。
        //     printf("%d ",stack[i]);
        // printf("\n");
    }
}

// 幅優先探索 Breadth First Search
int bfs(){
    int queue[100];
    int head=0,tail=0,k,j;

    queue[tail++] = 0;
    while(head != tail){
        k = queue[head++];
        if(visited[k]) continue;
        visited[k] = 1;
        printf("%d ",k);

        for(j=0;j<N;j++){
            if(!visited[j] && mat[k][j])
                queue[tail++] = j;
        }
    }
}

int main(void){

    for(int i=0;i<N;i++)
        visited[i]=0;
    dfs_rec(0); printf("\n");

    for(int i=0;i<N;i++)
        visited[i]=0;
    dfs(); printf("\n");

    for(int i=0;i<N;i++)
        visited[i]=0;
    bfs(); printf("\n");
    
}

/* result

$ ./a.out
0 1 2 6 5 4 3 7
0 1 2 6 5 4 3 7
0 1 2 3 6 4 5 7

*/