Tree

2024. 3. 7. 11:51
728x90

- 원소들 간에 1:N 관계를 가지는 비선형 자료구조

- 상위 원소와 하위 원소의 관계가 있는 계층적 자료구조

 

1. 정의

: 한 개 이상의 노드로 이루어진 유한 집합.

- 노드/정점 : 각각 데이터를 담고 있는 원소

- 루트 노드 : 노드 중 최상위 노드

 

- 각 노드는 0개 이상의 자식 노드를 가질 수 있음. 

- 하나의 부모에 여러 자식이 연결

- 하나의 자식은 둘 이상의 부모 X

- 노드의 갯수가 N개일 때, N-1개의 간선을 가지고 있음.  -> 그래서 순환 구조가 생기지 않음.

(cf. Graph는 순환 구조가 있음.)

- 형제 노드 : 같은 부모 노드를 가진 자식 노드들

- 조상 노드 : 간선을 따라 루트 노드까지 가는 경로의 모든 노드들

 

- 차수 (Degree) : 노드에 연결된 자식 노드의 수 

ex) A의 차수 : 3, B의 차수: 2

- 트리의 차수 : 트리 노드들의 차수 중 제일 큰 값

- 높이 (Level) : 루트에서 노드에 이르는 간선의 수

ex)

- 트리의 높이 : 노드 중 높이가 가장 큰 값

 

2. 이진 트리 (Binary Tree)

: 트리 중에서 모든 부모 노드가 최대 2개의 자식 노드를 가진 트리 

- 높이가 H인 이진 트리에서

> 레벨이 i인 노드의 갯수는 최 2^i

> 트리 전체의 노드의 최소 갯수는 H+1

> 최대 갯수는 2^(H+1) -1

 

1) 포화 이진 트리 (Perfect Binary Tree)

: 모든 레벨에 노드가 최대로 차 있는 이진 트리

- 높이가 H인 트리에서 최대 노드 갯수 : 2^(H+1) -1

 

2) 완전 이진 트리 (Complete Binary Tree)

: 제일 깊은 레벨을 제외한 레벨에 노드의 갯수가 최대로 차 있으며, 마지막 레벨에 노드가 존재할 경우 왼쪽부터 차례대로 채워 넣어진 이진 트리

 

3) 편행 이진 트리 (Skewed Binary Tree)

: 높이가 H일 때 이진 트리가 가질 수 있는 최소의 노드 갯수를 가지며, 한쪽 방향의 자식 노드만 가진 이진 트리

 

4) 이진 트리 배열로 표현하기 

 

- 왼쪽 자식 노드 : i x 2

- 오른쪽 자식 노드 : i x 2 + 1

- 부모 노드 : i / 2 (소수점 버림) 

 

** 편향 이진 트리의 경우 사용하지 않는 경우가 많다.

 

 

3. 이진 트리 순회

: 이진 트리의 각 노드를 한 번씩만 방문하는 체계적인 방법

- 루트 노드(V), 왼쪽 서브트리 (L), 오른쪽 서브트리 (R)을 정해진 순서대로

1) 전위 순회 (VLR)

: 루트 노드 -> 왼쪽 서브트리 -> 오른쪽 서브트리

ex) 1 2 4 8 9 5 10 11 3 6 12 7

 

2) 중위 순회 (LVR)

: 왼쪽 서브트리 -> 루트 노드 -> 오른쪽 서브트리

ex) 8 4 9 2 10 5 11 1 12 6 3 7

 

3) 후위 순회 (LRV)

: 왼쪽 서브트리 -> 오른쪽 서브트리 -> 루트 노드

ex) 8 9 4 10 11 5 2 12 6 7 3 1

 

public class TreeArray {
    // 이진 트리는 배열로 표현이 가능하다.

    // 총 노드의 개수
    private int nodes;
    //실제 트리를 담고있는 배열
    private int[] arr;
    //배열을 담는다.
    public void setArr(int[] arr) {
        this.arr = arr;
        this.nodes = arr.length;
    }

    // 전위순회 : VLR
    // node : 현재 트리의 루트 노드 index
    public void traversePreorder(int node) {
        // node가 배열을 벗어나지 않고,
        // 데이터가 저장되어 있다 (0이 아니다)
        if (node < nodes && arr[node] != 0) {
            // V방문
            System.out.print(arr[node] + ", ");
            // L방문
            this.traversePreorder(node * 2);
            // R방문
            this.traversePreorder(node * 2 + 1);
        }
    }

    // 중위순회 : LVR
    public void traverseInorder(int node) {
        // node가 배열을 벗어나지 않고, 데이터가 저장되어 있을 때
        if (node < nodes && arr[node] != 0) {
            // L방문
            this.traverseInorder(node * 2);
            // V방문
            System.out.print(arr[node] + ", ");
            // R방문
            this.traverseInorder(node * 2 + 1);
        }
    }

    // 후위순회 : LRV
    public void traversePostorder(int node) {
    	// 조건은 동일
        if (node < nodes && arr[node] != 0) {
            // L방문
            this.traversePostorder(node * 2);
            // R방문
            this.traversePostorder(node * 2 + 1);
            // V방문
            System.out.print(arr[node]+", ");
        }
    }

    public static void main(String[] args) {
        TreeArray tree = new TreeArray();
        tree.setArr(new int[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12});
        tree.traversePreorder(1);
        System.out.println(" ");
        tree.traverseInorder(1);
        System.out.println(" ");
        tree.traversePostorder(1);
    }
}

 

 

 

4. 이진 탐색 트리 (Binary Search Tree)

: 탐색 작업을 효율적으로 하기 위한 자료구조. 

- 모든 노드의 데이터가 서로 다른 이진 트리. + 왼쪽 자식의 데이터는 모두 부모보다 작고, 오른쪽 자식의 데이터는 모두 부모보다 크다.

 

: 루트 노드 기준으로 

- 왼쪽 서브트리의 모든 데이터는 루트 노드보다 작다. 

- 오른쪽 서브트리의 모든 데이터는 루트 노드보다 크다.

- 중위 순회할 경우 오름차순 정렬된 데이터를 얻을 수 있다. 

 

1) 탐색

- 루트 노드와 탐색 데이터 비교

- 데이터가 더 작을 경우 왼쪽 서브트리로

- 데이터가 더 클 경우 오른쪽 서브트리로

- 시간복잡도 : O(h) ~ O(logN)

- 편향 이진 트리의 경우 : O(N)

 

 

 

 

2) 삽입

- 탐색 과정을 따라간다.

- 탐색에 성공한 경우 삽입 불가

- 탐색에 실패하는 시점에 새로운 노드로 데이터 추가

 

 

 

3) 삭제

- 삭제할 데이터를 가진 노드를 탐색

- 삭제할 노드가 단말 노드면 삭제 (링크 해제)

- 삭제할 노드에 자식이 하나일 경우 - 부모와 대신 연결

- 삭제할 노드에 자식이 두개일 경우, 왼쪽 서브트리의 값 중 제일 큰 노드/ 오른쪽 서브트리의 값 중 제인 작은 노드 중 하나와 교체

위나 아래의 결과를 갖게 된다.

 

 

구현

더보기
public class BinarySearchTree {
    // 트리의 노드를 나타내는 클래스
    private static class Node {
        // 담고 있는 데이터
        int key;
        // 왼쪽 자식
        Node left;
        //오른쪽 자식
        Node right;

        public Node(int key) {
            this.key = key;
            left = null;
            right = null;
        }
    }

    // 루트 노드
    private Node root;

    // 데이터 추가
    // 데이터를 탐색해서 실패위치에서 새로운 노드를 만들어서 데이터 추가
    public void insert(int key) {
        root = insertNode(root, key);
    }

    private Node insertNode(Node node, int key) {
        // 탐색 실패하는 경우 새 노드를 만든다.
        if (node == null) {
            node = new Node(key);
            return node;
        }
        // 현재 노드보다 데이터가 작을 경우 왼쪽 노드를 확인
        if (key < node.key) {
            node.left = insertNode(node.left, key);
        } //현재 노드보다 데이터가 클 경우 오른쪽 노드를 확인
        else if (key > node.key) {
            node.right = insertNode(node.right, key);
        } return node;
    }

    // 탐색
    public boolean search(int key) {
        return searchNode(root, key);
    }

    private boolean searchNode(Node node, int key) {
        // 현재 노드가 없을 경우 탐색 실패
        if (node == null) {
            return false;
        }

        // 현재 노드의 데이터와 일치하는 경우 탐색 성공
        if (node.key == key) {
            return true;
        }
        // 현재 노드보다 데이터가 작을 경우 왼쪽으로
        // 현재 노드보다 데이터가 클 경우 오른쪽으로
        if (key < node.key) {
            return searchNode(node.left, key);
        } else {
            return searchNode(node.right, key);
        }
    }

    public void traverseInorder() {
        inorder(root);

    }

    private void inorder(Node node) {
        if (node != null) {
            inorder(node.left);
            System.out.print(node.key + " ");
            inorder(node.right);
        }
    }

    public static void main(String[] args) {
        BinarySearchTree bst = new BinarySearchTree();
        bst.insert(50);
        bst.insert(30);
        bst.insert(70);
        bst.insert(20);
        bst.insert(40);
        bst.insert(60);
        bst.insert(80);

        bst.traverseInorder();
        System.out.println();
        System.out.println(bst.search(40));
        System.out.println(bst.search(85));
        System.out.println(bst.search(30));
    }
}

 

728x90

'Coding test > Algorithm' 카테고리의 다른 글

Divide & Conquer 분할 정복  (0) 2024.03.12
Heap  (0) 2024.03.07
이항 계수  (0) 2023.12.29
DP(Dynamic Programming)  (0) 2023.12.28
Brute Force / 순열 / 조합 / 멱집합  (1) 2023.12.17

BELATED ARTICLES

more