ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 트리 정리
    Algorithm 2020. 9. 7. 16:58

    트리 (tree)

    • 자료구조의 일종
    • 사이클이 없는 연결 그래프
    • 정점의 개수 : V
    • 간선의 개수 : V-1
      • 정점 V, 간선 V 개 라면 사이클이 1개 있다.
    • 정점 V, 간선 V-1 개라면 트리라 할 수 있을까 ? 아니다.
    • 왜냐면 연결이 되어있지 않다.
    • 정점 V, 간선 V-1 개 라는것은 트리의 성질이다.
    • 트리가 맞을려면 연결되어있다는 조건이 추가되어야 한다.

     

    트리를 구성하는 요소

    - node 

    - edge

     

    root

    - 트리의 가장 윗부분에 위치하는 노드

    - 하나의 트리에는 하나의 루트가 존재

     

    reaf (terminal node, external node)

    - 트리의 가장 아랫부분에 위치하는 노드

    - 더 이상 뻗어나갈 수 없는 마지막 노드

     

    Parent

    - Parent가 없는 V를 Root라고 할 수 있다.

     

    Chileren

     

    Sibiling

    - 같은 부모를 가지면 형제

     

    Ancestor, Descendent

    - 조상의 정의에는 자신이 포함된다.

    - 자손의 정의에는 자신이 포함된다.

     

    level

    - 루트로부터 얼마나 떨어져 있는지에 대한 값

    - 루트의 레벨은 0이고 가지가 하나씩 뻗어나갈 때마다 레벨이 1씩 늘어난다.

     

    depth

    - 루트에서 현재 노드 사이의 간선 개수 

     

    height

    - 루트로부터 가장 멀리 떨어진 리프까지의 거리 (리프 레벨의 최댓값)

     

    degree 

    - 노드가 갖는 자식의 수

    - 모든 노드의 차수가 n 이하인 트리를 n진 트리라고 한다.

    - 모든 노드의 자식 수가 2개 이하인 경우에는 특별히 이진트리라고 한다.

     

    Rooted Tree

    - 루트가 있는 트리

    - 루트는 정하기 나름, 있을수도 없을수도 있다.

     

    ordered tree, unorered tree

    - 형제 노드의 순서가 있는지 없는지에 따라 트리를 두 종류로 분류한다.

    - 형제 노드의 순서를 따지면 ordered tree, 따지지 않으면 unorered tree

     

    -> 순서 트리로 보면 다른 트리지만 무순서 트리로 보면 같은 트리라고 할 수 있다.

     

     

    이진트리 (Binary Tree)

     

    binary tree (이진트리)

    - 노드가 왼쪽 자식과 오른쪽 자식을 갖는 트리를 이진트리 (binary tree) 라고 한다.

    - 각 노드의 자식은 2명 이하만 유지해야 한다.

     

    Perfect Binary Tree

    - Leaf Node 를 제외한 노드의 자식의 수는 2개

    - Leaf Node 의 자식의 수 0개

    - 모든 Leaf Node 의 깊이가 같아야한다.

    - 높이가 h인 트리의 노드 개수 = 2^h -1 

     

    complete binary tree (완전이진트리)

    - Perfect Binary Tree 의 마지막 레벨에서 오른쪽 일부가 사라진 트리

    - 루트부터 노드가 채워져 있으면서 같은 레벨에서는 왼쪽에서 오른쪽으로 노드가 채워져 있는 이진트리를 완전이진트리라 한다.

    - 높이가 k인 완전이진트리가 가질 수 있는 노드의 최댓값은 2^k+1 -1 개이다.

    - 따라서 n개의 노드를 저장할 수 있는 완전이진트리의 높이는 log n이다.

     

    binary search tree (이진검색트리)

    - 어떤 노드 N을 기준으로 왼쪽 서브 트리 노드의 모든 키 값은 노드 N의 키 값보다 작아야한다.

    - 오른쪽 서브 트리 노드의 키 값은 노드 N의 키 값보다 커야 한다.

    - 같은 키 값을 갖는 노드는 없다.

     

     

     

    - 이진검색트리는 중위 순회를 하면 키 값의 오름차순으로 노드를 얻을 수 있다는 점, 구조가 단순하다는 점,

      이진검색과 비슷한 방식으로 검색이 가능하다는 점, 노드의 삽입이 쉽다는 점등의 특징이 있어 폭넓게 사용된다.

     


    트리의 표현

    - 그래프의 표현과 같은 방식으로 저장할 수 있다.

     

    1. 트리의 부모만 저장하는 방식 

    - 각 노드의 부모만 저장한다. 

    - 루트는 부모가 없다.

    # Union-Find

    부모를 한 번에 찾을 수 있지만 자식을 찾는데는 오래 걸린다.

     

    2. 완전 이진트리의 경우에는 배열로 표현할 수 있다. 

    - 부모의 노드가 x 인 경우 자식의 노드는 2*x, 2*x + 1로 나타내면 된다.

    - 구조체나 클래스를 이용할 수도 있다

    Heap, Segment Tree

    완전이진트리만 사용할 수 있다.

     


    트리의 순회

    • 어떤 의미를 가지고 있는지가 중요하다.
    • DFS, BFS 모두 가능 
    • DFS 는 3가지 출력 순서가 있다.
    • 세 방문의 차이는 노드 방문 처리를 언제 할 것인가 이다.
    • PRE-ORDER (전위순회)
      • 그래프의 DFS 순서와 같다. 
        • 노드 방문
        • 왼쪽 자식 노드를 루트로 하는 서브 트리 프리오더
        • 오른쪽 자식 노드를 루트로 하는 서브 트리 프리오더
    • IN-ORDER (중위순회)
      • 별로 쓸 일 없고 BST 에서 삭제를 구현할때 이용한다.
      • 이진트리가 아니면 정의되지 않는다.
        • 왼쪽 자식 노드를 루트로 하는 서브 트리 인오더
        • 노드 방문
        • 오른쪽 자식 노드를 루트로 하는 서브 트리 인오더
    • POST-ORDER (후위순회)
      • 제일 많이 사용하는 방식
        • 왼쪽 자식 노드를 루트로 하는 서브 트리 후위순회
        • 오른쪽 자식 노드를 루트로 하는 서브 트리 후위순회
        • 노드 방문

     


    트리의 탐색

    • 트리의 탐색은 BFS/DFS 를 이용해서 할 수 있다.
    • 트리는 사이클이 없는 그래프이기 때문에 임의의 두 정점 사이의 경로는 1개이다.
    • 경로가 1개 보다 많다면 그것은 트리가 아니다.
    • 경로가 1개이기에 찾으면 그 경로가 최단경로이다.

     


    트리의 지름

    • 트리에 존재하는 모든 경로 중에서 가장 긴 것을 트리의 지름이라고 한다.
    • 트리의 지름은 탐색 2번으로 구할 수 있다.
      • 1. 한 정점 s에서 모든 정점까지의 거리를 구한다. 이때 가장 먼 거리의 정점을 u 라고 한다.
      • 2. u에서 모든 정점까지의 거리를 구한다. 이때 가장 먼 거리의 정점 v를 구한다.
    • 이때 u와 v 사이의 거리를 트리의 지름이다.

     


    import java.util.Comparator;
    
    public class BinTree<K,V> {
        // 노드
        static class Node<K,V> {
            private K key;
            private V data;
            private Node<K,V> left;
            private Node<K,V> right;
    
            Node(K key, V data, Node<K,V> left, Node<K,V> right) {
                this.key   = key;
                this.data  = data;
                this.left  = left;
                this.right = right;
            }
    
            K getKey() {
                return key;
            }
    
            V getValue() {
                return data;
            }
    
            void print() {
                System.out.println(data);
            }
        }
    
        private Node<K,V> root;
        private Comparator<? super K> comparator = null;
    
        public BinTree() {
            root = null;
        }
    
        public BinTree(Comparator<? super K> c) {
            this();
            comparator = c;
        }
    
        // 두 키 값을 비교
        private int comp(K key1, K key2) {
            return (comparator == null) ? ((Comparable<K>)key1).compareTo(key2)
                    : comparator.compare(key1, key2);
        }
    
        // 키에 의한 검색
        public V search(K key)	{
            Node<K,V> p = root;
    
            while (true) {
                if (p == null)
                    return null;
                int cond = comp(key, p.getKey());
                if (cond == 0)
                    return p.getValue();
                else if (cond < 0)
                    p = p.left;
                else
                    p = p.right;
            }
        }
    
        // node를 루트로 하는 서브 트리에 노드<K,V>를 삽입
        private void addNode(Node<K,V> node, K key, V data) {
            int cond = comp(key, node.getKey());
            if (cond == 0) return;
            else if (cond < 0) {
                if (node.left == null)
                    node.left = new Node<K,V>(key, data, null, null);
                else
                    addNode(node.left, key, data);
            } else {
            if (node.right == null)
                node.right = new Node<K,V>(key, data, null, null);
            else
                addNode(node.right, key, data);
            }
        }
    
        // 노드를 삽입
        public void add(K key, V data) {
            if (root == null)
                root = new Node<K,V>(key, data, null, null);
            else
                addNode(root, key, data);
        }
    
        // 키 값이 key인 노드를 삭제
        public boolean remove(K key) {
            Node<K,V> p = root;
            Node<K,V> parent = null;
            boolean isLeftChild = true;
    
            // 검색
            while (true) {
                if (p == null)
                    return false;
                int cond = comp(key, p.getKey());
                if (cond == 0)
                    break;
                else {
                    parent = p;
                    if (cond < 0) {
                        isLeftChild = true;
                        p = p.left;
                    } else {
                        isLeftChild = false;
                        p = p.right;
                    }
                }
            }
    
            // 자식 노드가 없거나 1개인 경우
            if (p.left == null) {
                if (p == root)
                    root = p.right;
                else if (isLeftChild)
                    parent.left  = p.right;
                else
                    parent.right = p.right;
            } else if (p.right == null) {
                if (p == root)
                    root = p.left;
                else if (isLeftChild)
                    parent.left  = p.left;
                else
                    parent.right = p.left;
            } else {
                // 자식 노드가 2개인 경우
                parent = p;
                Node<K,V> left = p.left;
                isLeftChild = true;
                while (left.right != null) {
                    parent = left;
                    left = left.right;
                    isLeftChild = false;
                }
                p.key  = left.key;
                p.data = left.data;
                if (isLeftChild)
                    parent.left  = left.left;
                else
                    parent.right = left.left;
            }
            return true;
        }
    
        // node를 루트로 하는 서브 트리의 노드를 키 값의 오름차순으로 출력
        private void printSubTree(Node node) {
            if (node != null) {
                printSubTree(node.left);
                System.out.println(node.key + " " + node.data);
                printSubTree(node.right);
            }
        }
    
        // 모든 노드를 키 값의 오름차순으로 출력
        public void print() {
            printSubTree(root);
        }
    }

     



    - 20.12.12 추가

    'Algorithm' 카테고리의 다른 글

    비트마스크 (BitMask)  (0) 2020.12.09
    다이나믹 프로그래밍 정리  (0) 2020.09.09
    그리디&구현, DFS&BFS  (0) 2020.09.01
    LinkedList  (0) 2020.08.31
    EOF (End of File)  (0) 2020.08.29
킹수빈닷컴