[JAVA] 이진 탐색 트리


이진 탐색 트리란?

이진 탐색 트리(Binary Search Tree)란 부모노드의 값을 기준으로하여 더 작은 값은 왼쪽 자식노드에, 더 큰 값은 오른쪽 자식노드에 이어지는 트리의 형태를 말한다. 탐색하고자 하는 값이 있을 때에는 현재 노드를 중심으로 더 가까운 값들만 골라서 탐색하게 되므로 시간복잡도에서 유리한 시간을 갖는다.

또한 루트노드를 기준으로 자식노드가 연결되므로, 노드가 입력되는 순서에 따라 트리의 모양이 달라진다는 특징을 가진다.



트리 구현

먼저 처음 입력값으로 루트노들르 만들고 그 뒤부터는 노드와의 값을 비교해 탐색하는 노드보다 값이크면 오른쪽 자식노드를, 값이 작다면 왼쪽 자식 노드를 탐색한다. 해당 자식노드가 비어있다면 새로운 노드가 그 위치에 들어가게 되고, 그 노드가 존재한다면, 그 자식노드를 중심으로 함수를 재귀호출하는 방법이다. 코드는 아래와 같다.



소스코드

static class TreeNode{
   int value;
   TreeNode left;
   TreeNode right;
   public TreeNode(int v) {value = v;}
}

// 자식노드와 부모노드 연결 함수
static void link_node(TreeNode parent, int value){
   // 현재 값이 탐색하고 있는 노드(parent)보다 크다면
   // 오른쪽 자식 노드를 탐색
   if(value > parent.value){
       // 오른쪽 자식 노드가 null 이라면 해당 노드로 연결
       if(parent.right == null){
           TreeNode newNode = new TreeNode(value);
           parent.right = newNode;
      } // 오른쪽 자식 노드가 있다면 해당 노드를 탐색노드로 하여 재귀
       else{
           link_node(parent.right, value);
      }
  }else{ // 값이 더 작다면 왼쪽 자식 노드 탐색
       if(parent.left == null){
           TreeNode newNode = new TreeNode(value);
           parent.left = newNode;
      }else{
           link_node(parent.left, value);
      }
  }
}

트리의 순회

1) 전위순회

  • 루트노드 - 왼쪽자식노드 - 오른쪽 자식노드 순으로 탐색하는 방법이다.

  • 루트노드를 탐색하고 왼쪽자식노드를 탐색하면 그 단계에서는 왼쪽 자식노드가 루트노드(부모노드)가 되고 그 밑에 있는 자식노드들이 왼쪽자식노드, 오른쪽자식노드가 된다.

// 전위순회 (루트 - 왼 - 오)
static void preorder(TreeNode node){
   System.out.print(node.value);
   if(node.left != null)
       preorder(node.left);
   if(node.right != null)
       preorder(node.right);
}

2) 중위순회

  • 왼쪽자식노드 - 루트노드 - 오른쪽자식노드 순으로 탐색하는 방법이다.

// 중위순회 (왼 - 루트 - 오)
static void inorder(TreeNode node){
   if(node.left != null)
       inorder(node.left);
   System.out.print(node.value);
   if(node.right != null)
       inorder(node.right);
}

3) 후위순회

  • 왼쪽자식노드 - 오른쪽자식노드 - 루트노드 순으로 탐색하는 방법이다.

// 후위순회 (왼 - 오 - 루트)
static void postorder(TreeNode node){
   if(node.left != null)
       postorder(node.left);
   if(node.right != null)
       postorder(node.right);
   System.out.print(node.value);
}


입력을 받아 트리를 만들고 트리를 순회하는 메인함수는 아래와 같다.

public static void main(String[] args) {
   Scanner sc = new Scanner(System.in);
   int N = sc.nextInt();

   TreeNode root = new TreeNode(sc.nextInt());
   for(int i=0; i<N-1; i++)
       link_node(root, sc.nextInt());


   preorder(root);
   System.out.println();

   inorder(root);
   System.out.println();

   preorder(root);
   System.out.println();
}




'JAVA' 카테고리의 다른 글

[JAVA] 조합(Combination) 구현  (0) 2018.11.23
[JAVA] ArrayList 배열로 바꾸기  (0) 2018.11.21
[JAVA] 입력 클래스  (0) 2018.10.10
[JAVA] Scanner함수 next(), nextLine() 차이  (0) 2018.09.20

[JAVA] 백준 3055 : 탈출


문제

시간이 지날 수록 물이 차오르는 상황에서 고슴도치 집에서 출발한 고슴도치가 친구인 비버 집에 얼마만에 도착할 수 있는가를 구하는 문제. R행 C열로 이뤄진 지도에는 빈칸(.), 물(*), 돌(X), 비버집(D), 고슴도치집(S)이 주어져있다. 매 분마다 고슴도치는 현재 있는 칸과 인접한 네 칸 중 하나로 이동할 수 있고, 물도 매 분마다 비어있는 칸으로 확장한다. 물이 있는 칸과 인접해있는 비어있는 칸은 물이 차게 된다. 물과 고슴도치는 돌을 통과할 수 없다. 또, 고슴도치는 물로 차있는 구역으로 이동할 수 없고, 물도 비버의 소굴로 이동할 수 없다. 이러한 지도가 주어졌을 때, 고슴도치가 안전하게 비버의 굴로 이동하기 위해 필요한 최소 시간을 구하는 문제.



접근방법

  1. 맵의 입력을 숫자로 변환하여 받는다.

    • 빈칸(-2), 물(-1), 돌(2) 고슴도치집(0), 비버집(-100)으로 설정하였다. → 처음 큐에 들어가면서 1씩 증가해야 하므로 고슴도치 집을 0으로 설정하였고, 이와 구분해주기 위해 다른 값들은 위와 같이 설정하였다.

  2. 입력을 받으면서 물을 큐에 넣고, 그 뒤에 고슴도치의 위치를 큐에 넣으면서 BFS를 수행한다. 먼저 물과 인접한 빈칸이 물로 바뀌고, 고슴도치는 나머지 빈칸을 이동해 비버의 집까지의 이동거리를 체크한다.



소스코드

import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;

public class Main{

   static int[] dr = {1, -1, 0, 0};
   static int[] dc = {0, 0, 1, -1};

   static class Pos{
       int r;
       int c;
       public Pos(int r, int c) {this.r = r; this.c = c;}
  }

   public static void main(String[] args) {
       Scanner sc = new Scanner(System.in);
       int N = sc.nextInt();
       int M = sc.nextInt();

       Queue<Pos> queue = new LinkedList<>();
       int map[][] = new int[N+2][M+2];
       boolean check_doch[][] = new boolean[N+2][M+2];
       boolean check_rain[][] = new boolean[N+2][M+2];

       // 돌(테두리) 세우기
       for(int i=0; i<N+2; i++)
           for(int j=0; j<M+2; j++)
               map[i][j] = 2;  // 1-벽(돌)

       Pos dochi = new Pos(0,0);
       Pos bibber = new Pos(0,0);
       for(int i=1; i<=N; i++){
           String line = sc.next();
           for(int j=1; j<=M; j++){
               char input = line.charAt(j-1);
               switch (input){
                   case '*':   // 물
                       map[i][j] = -1;
                       queue.offer(new Pos(i, j));
                       check_rain[i][j] = true;
                       break;
                   case 'S':   // 고슴도치 집
                       map[i][j] = 0;
                       dochi = new Pos(i, j);
                       break;
                   case '.':   // 빈칸
                       map[i][j] = -2;
                       break;
                   case 'D':   // 비버 집
                       map[i][j] = -100;
                       bibber = new Pos(i, j);
                       break;
                   case 'X':   // 돌
                       map[i][j] = 2;
                       break;
              }
          }
      }
       queue.offer(dochi);
       check_doch[dochi.r][dochi.c] = true;

       First_Loop:
       while(!queue.isEmpty()) {
           Pos cur_pos = queue.poll();
           for (int i = 0; i < 4; i++) {
               Pos next_pos = new Pos(cur_pos.r + dr[i], cur_pos.c + dc[i]);

               // 현재 칸이 빗물일 경우 -> 빗물 증가 (방문여부 확인할 필요x)
               if (map[cur_pos.r][cur_pos.c] == -1) {
                   // 주변 칸이 빈칸(-2)일 경우 큐에 넣는다.
                   if (map[next_pos.r][next_pos.c] == -2) {
                       map[next_pos.r][next_pos.c] = -1;
                       check_rain[next_pos.r][next_pos.c] = true;
                       queue.offer(next_pos);
                  }
              } // 현재 칸이 빈칸일 경우 -> 고슴도치 이동거리 증가
               else {
                   // 주변 칸이 방문하지 않았으면서
                   if (!check_doch[next_pos.r][next_pos.c]) {
                       // 비버의 집(-100)일 경우 while문 탈출
                       if (map[next_pos.r][next_pos.c] == -100){
                           map[next_pos.r][next_pos.c] = map[cur_pos.r][cur_pos.c] + 1;
                           break First_Loop;
                      }// 빈칸(-2)일 경우 계속해서 이동
                       else if(map[next_pos.r][next_pos.c] == -2){
                           map[next_pos.r][next_pos.c] = map[cur_pos.r][cur_pos.c] + 1;
                           check_doch[next_pos.r][next_pos.c] = true;
                           queue.offer(next_pos);
                      }
                  }
              }
          }
      }

       if(map[bibber.r][bibber.c] == -100)
           System.out.println("KAKTUS");
       else
           System.out.println(map[bibber.r][bibber.c]);
  }
}


'알고리즘' 카테고리의 다른 글

백준15684 : 사다리 조작  (0) 2018.11.21
백준14889 : 스타트와 링크  (0) 2018.11.16
[JAVA] 백준 11559 : 뿌요뿌요  (0) 2018.09.20
[JAVA] 백준 14503 : 로봇 청소기  (0) 2018.09.18
[JAVA] 백준 3190 : 뱀  (1) 2018.09.18

[JAVA] 백준 11559 : 뿌요뿌요

문제

뿌요뿌요 룰대로 필드가 주어졌을 때 콤보가 몇 번인지 구하는 문제

- 필드에 여러 가지 색깔의 뿌요를 놓는다. 뿌요는 중력의 영향을 받아 아래에 바닥이나 다른 뿌요가 나올 때까지 아래로 떨어진다.

- 뿌요를 놓고 난 후, 같은 색 뿌요가 4개 이상 상하좌우로 연결되어 있으면 연결된 같은 색 뿌요들이 한꺼번에 없어진다.

- 뿌요들이 없어지고 나서 위에 다른 뿌요들이 있다면, 역시 중력의 영향을 받아 차례대로 아래로 떨어지게 된다.

- 아래로 떨어지고 나서 다시 같은 색의 뿌요들이 4개 이상 모이게 되면 또 터지게 되는데, 터진 후 뿌요들이 내려오고 다시 터짐을 반복할 때마다 1연쇄씩 늘어난다.

- 터질 수 있는 뿌요가 여러 그룹이 있다면 동시에 터져야 하고 여러 그룹이 터지더라도 한번의 연쇄가 추가된다.


접근방법

  1. 4개 이상 붙어있는 것들을 체크한다.

  2. 1에 해당하는 뿌요들을 한 번에 없애고 위에 있는 뿌요들을 내려준다.

  3. 위 단계를 4개 이상 붙어있는 것이 없을 때까지 반복한다.




    1. "4개 이상 붙어있는 것들을 체크한다."

    • BFS를 사용해 붙어있는 것들을 체크한다.

    • 이 때, 큐를 하나만 사용하면 큐에 있는 것들이 없어지면서(poll) 인접한 배열 원소들을 큐에 넣는 방식이기 때문에, 인접한 원소를 모두 가지고 있을 큐(count_queue)를 하나 더 만든다.

    • 즉, 큐에서 poll() 한 원소를 새로운 큐에 offer() 한다.

    • 새로운 큐의 개수가 4개 이상인지 체크한다.

    private static int count(char alpha){
       Pos cur_pos;
       while(!queue.isEmpty()){
           cur_pos = queue.poll();
           count_queue.offer(cur_pos);

           for(int i=0; i<4; i++){
               Pos next_pos = new Pos(cur_pos.r + dr[i], cur_pos.c + dc[i]);
               // 동일한 색이면서 아직 방문하지 않았으면 큐에 삽입
               if(map[next_pos.r][next_pos.c] == alpha && !check[next_pos.r][next_pos.c]){
                   check[next_pos.r][next_pos.c] = true;
                   queue.offer(next_pos);
              }
          }
      }

       // count_queue : 붙어있는 뿌요들의 모임
       return count_queue.size();
    }

○ 새로운 큐의 개수가 4개 이상이면 해당 배열 원소를 'd'로 바꿔준다.


  1. 1에 해당하는 뿌요들을 한 번에 없애고 위에 있는 뿌요들을 내려준다.

    • 없어질 뿌요 위에 있는 원소들을 하나하나 밑으로 내려준다.

    • 처음에 배열을 (N+2) x (N+2) 로 설정해놓고, 테두리에 '.'로 설정해주었으므로 배열 범위 체크를 따로 해주지 않아도 된다.

    private static void deletePuyo(Pos pos){
       for(int i=pos.r; i>=1; i--)
           map[i][pos.c] = map[i-1][pos.c];
    }

  1. 위 단계를 4개 이상 붙어있는 것이 없을 때까지 반복한다.

    • 1에서 4개 이상의 뿌요가 붙어있다면 isQuit 변수를 true로 바꿔주고, combo를 증가시키기 전 해당 변수를 확인해 false라면 combo를 증가시키지 않고 while문을 빠져나간다.



소스코드

import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;

public class Main{

   static int dr[] = {1, -1, 0, 0};
   static int dc[] = {0, 0, 1, -1};

   static int N = 12;
   static int M = 6;
   static char map[][];
   static boolean check[][];
   static Queue<Pos> queue;
   static Queue<Pos> count_queue;

   static class Pos{
       int r;
       int c;
       public Pos(int r, int c) {this.r = r; this.c = c;}
  }

   private static int count(char alpha){
       Pos cur_pos;
       while(!queue.isEmpty()){
           cur_pos = queue.poll();
           count_queue.offer(cur_pos);

           for(int i=0; i<4; i++){
               Pos next_pos = new Pos(cur_pos.r + dr[i], cur_pos.c + dc[i]);
               // 동일한 색이면서 아직 방문하지 않았으면 큐에 삽입
               if(map[next_pos.r][next_pos.c] == alpha && !check[next_pos.r][next_pos.c]){
                   check[next_pos.r][next_pos.c] = true;
                   queue.offer(next_pos);
              }
          }
      }

       // count_queue : 붙어있는 뿌요들의 모임
       return count_queue.size();
  }

   private static void deletePuyo(Pos pos){
       for(int i=pos.r; i>=1; i--)
           map[i][pos.c] = map[i-1][pos.c];
  }


   public static void main(String[] args) {
       Scanner sc = new Scanner(System.in);
       int combo = 0;
       N = 12;
       M = 6;

       map = new char[N+2][M+2];
       check = new boolean[N+2][M+2];
       queue = new LinkedList<>();
       count_queue = new LinkedList<>();

       // 초기화 (테두리 설정)
       for(int i=0; i<N+2; i++)
           for(int j=0; j<M+2; j++)
               map[i][j] = '.';

       // 맵 설정
       for(int i=1; i<=N; i++){
           String line = sc.nextLine();
           for(int j=1; j<=M; j++)
               map[i][j] = line.charAt(j-1);
      }

       boolean isQuit = false;
       while(true){
           isQuit = true;
           for(int i=1; i<=N; i++){
               for(int j=1; j<=M; j++){
                   // 배열이 값이 알파벳이면서 아직 체크하지 않았다면 큐에 넣는다.
                   if(Character.isAlphabetic(map[i][j]) && !check[i][j]){
                       check[i][j] = true;
                       queue.offer(new Pos(i, j));

                       // 붙어있는 동일한 색깔의 뿌요의 개수를 구한다.
                       int num = count(map[i][j]);

                       if(num >= 4){
                           for(int k=0; k<num; k++){
                               Pos delete = count_queue.poll();
                               map[delete.r][delete.c] = 'd';
                          }
                           // 하나라도 삭제할 게 있으면 while문이 끝나지 않는다.
                           isQuit = false;
                      }
                       count_queue.clear();
                  }
              }
          }

           for(int i=1; i<=N; i++)
               for(int j=1; j<=M; j++){
                   if(map[i][j] == 'd')
                       deletePuyo(new Pos(i, j));
                   check[i][j] = false;
              }
               
           if(isQuit)
               break;

           ++combo;
      }
       System.out.println(combo);
  }
}


'알고리즘' 카테고리의 다른 글

백준15684 : 사다리 조작  (0) 2018.11.21
백준14889 : 스타트와 링크  (0) 2018.11.16
[JAVA] 백준 3055 : 탈출  (0) 2018.09.27
[JAVA] 백준 14503 : 로봇 청소기  (0) 2018.09.18
[JAVA] 백준 3190 : 뱀  (1) 2018.09.18

+ Recent posts