DFS(Depth - First - Search) : 깊이 우선 탐색

DFS(Depth - First - Search) : 깊이 우선 탐색




dfs

DFS 방법 1 : 재귀 함수 이용

#include <stdio.h>
#include <stdlib.h>

#define VERTEX_TOTAL_NUM 7 
#define TRUE 1
#define FALSE 0

struct Node{
  
  int vertex_num;
  struct Node *next;
};


struct Node *graph[VERTEX_TOTAL_NUM]; // init with null

/*---------- vertex 방문여부 표시 배열----------*/
// if visited[i] == 1, visited
//                  0, otherwise
int visited[VERTEX_TOTAL_NUM]={0,};

/*---------- graph related ----------*/
void add_next(int v1, int v2){

  struct Node *new = (struct Node*)malloc(sizeof(struct Node));
  new -> vertex_num = v2;
  new -> next = NULL;

  struct Node *cur = graph[v1]; 
  if(cur == NULL){
    graph[v1] = new;
  } else {
   
    while(1){
      if(cur -> next == NULL){
      cur -> next = new;
      break;
      }
      cur = cur -> next; 
    }
  }
}

void addEdge(int v1, int v2, int reverse){

  add_next(v1,v2);
  if(reverse == TRUE){
    add_next(v2,v1);
  }
}

void printLists(){

  int i;
  for(i=0; i<VERTEX_TOTAL_NUM;i++){
    printf("vertex %d: ",i);
    struct Node *cur = graph[i];
    while(cur){
      int num = cur -> vertex_num;
      printf("%d ", num);
      cur = cur -> next;
    }
    printf("\n");
  }
  printf("\n");
}

void showAdjacentVertex(int vertex_num){

     
  printf("vertex %d: ",vertex_num);
    struct Node *cur = graph[vertex_num];
    while(cur){
      int num = cur -> vertex_num;
      printf("%d ", num);
      cur = cur -> next;
    }
  printf("\n");
}


int findNextVertex(int vertex_num){
 
  struct Node *cur = graph[vertex_num];
  
  while(cur){
    int num = cur -> vertex_num;

    if(visited[num] == TRUE){
      cur = cur -> next;
      continue;		
    }

  return num;
		
  }
    
  return -1;
}

void aDFS(int v){

  visited[v] = TRUE;
  printf("visited vertex: %d \n", v);
  struct Node *cur = graph[v];

  while(cur){
    int vertex_num = cur -> vertex_num; 
    if(visited[vertex_num] == FALSE){
      aDFS(vertex_num);
    }
    cur = cur -> next;
  }
}

void doDFS(){
  int i;
  for(i=0; i<VERTEX_TOTAL_NUM;i++){
    if(visited[i] == FALSE){
      aDFS(i);
    }
  }
}



int main(){

  addEdge(0,4,TRUE);
  addEd**ge(0,5,TRUE);
  addEdge(0,6,TRUE);
  addEdge(4,1,TRUE);
  addEdge(5,2,TRUE);
  addEdge(6,3,TRUE);

  printLists();
  showAdjacentVertex(0);
  
  doDFS();  
  return 0;
}
// 실행 결과
// vertex 0: 4 5 6 
// vertex 1: 4 
// vertex 2: 5 
// vertex 3: 6 
// vertex 4: 0 1 
// vertex 5: 0 2 
// vertex 6: 0 3 

// vertex 0: 4 5 6 

// visited vertex: 0 
// visited vertex: 4 
// visited vertex: 1 
// visited vertex: 5 
// visited vertex: 2 
// visited vertex: 6 
// visited vertex: 3

=> (0) -> (4) -> (1) -> (5) -> (2) -> (6) -> (3)

=> doDFS()와 aDFS()에 초점을 맞춰서 얘기해보자. doBFS()는 for문을 이용해 노드번호를 오름차순으로 dfs방식으로 탐색한다. 만약 탐색할 노드가 방문하지 않았다면(visited[i] == FALSE ) aDFS(탐색할 노드 번호 i)을 호출 한다.

=> aDFS()는 재귀함수로 우선 매개값의 노드를 방문 기록한다.(visited[v] = TRUE) 이후 매개값의 노드와 연결된 노드를 대상으로 방문 기록을 체크하고 방문하지 않았다면 다시 aDFS(연결된 노드 번호,j)를 호출한다. 이후 노드 j를 방문 기록하고, 또 j와 연결된 노드를 대상으로 aDFS()를 호출하는 식으로 계속 함수를 호출한다. 이렇게 연결된 노드들 중에 방문하지 않은 노드를 기준으로 계속 재귀적으로 함수가 호출되며 깊이 우선(DFS)으로 노드를 방문됨을 알 수 있다.





DFS 방법 2 : 자료구조인 스택 사용

#include <stdio.h>
#include <stdlib.h>

#define VERTEX_TOTAL_NUM 7 
#define TRUE 1
#define FALSE 0

struct Node{
  
  int vertex_num;
  struct Node *next;
};


struct Node *graph[VERTEX_TOTAL_NUM]; // init with null

/*---------- vertex 방문여부 표시 배열----------*/
// if visited[i] == 1, visited
//                  0, otherwise
int visited[VERTEX_TOTAL_NUM]={0,};

/*---------- stack related ----------*/
int stack[VERTEX_TOTAL_NUM]= {-1,};
int top = -1;

void push(int vertex_num){
  
  if(top == VERTEX_TOTAL_NUM -1){
    printf("-------stack is full------ \n");
    return;
  }
  stack[++top] = vertex_num;
}

int pop(){
  if(top == -1){
    printf("------stack is empty------ \n");
    return -1;
  }
  int vertex_num = stack[top];
  stack[top--] = -1;

  return vertex_num;
}

int peek(){
  if(top == -1){
    printf("stack is empty \n");
    return -1;
  }
  int vertex_num = stack[top];
  return vertex_num;
}

int isStackEmpty(){
  // top 덕분에 for문을 쓰지 않아도 된다.  
  return top == -1? TRUE : FALSE;

}

/*---------- graph related ----------*/
void add_next(int v1, int v2){

  struct Node *new = (struct Node*)malloc(sizeof(struct Node));
  new -> vertex_num = v2;
  new -> next = NULL;

  struct Node *cur = graph[v1]; 
  if(cur == NULL){
     graph[v1] = new;
  } else {

    while(1){
      if(cur -> next == NULL){
        cur -> next = new;
        break;
      }
      cur = cur -> next;
    }
  }
}

void addEdge(int v1, int v2, int reverse){

  add_next(v1,v2);
  if(reverse == TRUE){
    add_next(v2,v1);
  }
}

void printLists(){

  int i;
  for(i=0; i<VERTEX_TOTAL_NUM;i++){
    printf("vertex %d: ",i);
    struct Node *cur = graph[i];
    while(cur){
      int num = cur -> vertex_num;
      printf("%d ", num);
      cur = cur -> next;
    }
    printf("\n");
  }
  printf("\n");
}

void showAdjacentVertex(int vertex_num){

  printf("vertex %d: ",vertex_num);
  struct Node *cur = graph[vertex_num];
  while(cur){
    int num = cur -> vertex_num;
    printf("%d ", num);
    cur = cur -> next;
  }
  printf("\n");
}


int findNextVertex(int vertex_num){
 
  struct Node *cur = graph[vertex_num];
  
  while(cur){
    int num = cur -> vertex_num;

    if(visited[num] == TRUE){
      cur = cur -> next;
      continue;		
    }
    return num;		
  }
    
  return -1;
}
void doDFS(int vertex_num){
  
  visited[vertex_num] = TRUE;
  printf("visited vertex : %d \n",vertex_num);
  push(vertex_num); 

  while(isStackEmpty() == FALSE){
    int next_vertex = -1;
    next_vertex = findNextVertex(peek());
    
    if(next_vertex == -1){
      pop();
    } else {
      visited[next_vertex] = TRUE;
      printf("visited vertex : %d \n",next_vertex);
      push(next_vertex);
    }
  }
}



int main(){

  addEdge(0,4,TRUE);
  addEdge(0,5,TRUE);
  addEdge(0,6,TRUE);
  addEdge(4,1,TRUE);
  addEdge(5,2,TRUE);
  addEdge(6,3,TRUE);

  printLists();
  showAdjacentVertex(0);
  
  doDFS(0);
  
  return 0;
}
// 실행 결과 
// vertex 0: 4 5 6 
// vertex 1: 4 
// vertex 2: 5 
// vertex 3: 6 
// vertex 4: 0 1 
// vertex 5: 0 2 
// vertex 6: 0 3 

// vertex 0: 4 5 6 

// visited vertex : 0 
// visited vertex : 4 
// visited vertex : 1 
// visited vertex : 5 
// visited vertex : 2 
// visited vertex : 6 
// visited vertex : 3 

=> (0) -> (4) -> (1) -> (5) -> (2) -> (6) -> (3)

=> doDFS()와 findNextVertex()에 초점을 맞춰서 얘기해보자. 우선 맨 처음 방문할 노드를 방문 기록하고(visited[vertex_num] = TRUE;), push하여 스택에 넣는다. 이후 findNextVertex()함수를 호출하여 이 노드의 연결된 노드 중 방문하지 않은 노드가 있는 지 확인한다. 있다면 다시 doDFS()에서 그 노드를 push하여 스택에 집어넣고 또 연결된 노드가 있는 지 반복 확인한다. 만약 없다면 pop하여 노드를 제거한다. 이러한 방식으로 스택이 빌때 까지 수행하면 깊이우선탐색(DFS)이 이루어진다.




체크해야 될 점!

  • DFS : 자료구조로 스택을 사용. (last - in 된 노드를 필요로 하므로)

  • BFS : 자료구조로 큐를 사용. (first - in 된 노드를 필요로 하므로)