본문 바로가기
TIL/ZTM Data Structures and Algorithms

ZTM Data Structures & Algorithms - Tree

by Dev_Dank 2021. 8. 28.

udemy 에서 Master the Coding interview: Data Structures & Algorithms 강의를 수강한 내용을 정리하는 포스팅입니다.

https://en.wikipedia.org/wiki/Tree_(data_structure) 

 

Tree (data structure) - Wikipedia

From Wikipedia, the free encyclopedia Jump to navigation Jump to search Not to be confused with trie, a specific type of tree data structure. Abstract data type simulating a hierarchical tree structure and represented as a set of linked nodes A generic, an

en.wikipedia.org

- 트리 자료구조는 데이터가 노드의 계층으로 이루어진 형태의 자료구조이다. 

이진탐색트리 (Binary Search Tree)

https://en.wikipedia.org/wiki/Binary_search_tree

 

Binary search tree - Wikipedia

From Wikipedia, the free encyclopedia Jump to navigation Jump to search Data structure in tree form sorted for fast lookup Binary search treeTypetreeInvented1960Invented byP.F. Windley, A.D. Booth, A.J.T. Colin, and T.N. HibbardAlgorithm Average Worst case

en.wikipedia.org

- 하나의 부모에 단 2개의 자식만이 존재할수 있으며 ("이진") 노드의 왼쪽에는 부모보다 작은 값이, 노드의 오른쪽에는 부모보다 큰 값이 와야한다. 
- 트리가 균형잡히게 구성된다면 탐색, 삽입, 삭제의 수행시간은 O(logn) 이지만 트리가 균형잡히지 않을 경우 O(n)의 시간이 걸린다. 
- 균형잡힌 트리를 만들고 싶다면 AVL 트리나 Red Black 트리를 사용할 수 있다. 

코드로 구현한 이진 탐색트리의 모습은 아래와 같다. (remove 메서드의 경우 상당히 이해하는데 시간이 걸렸다.)

class Node {
  constructor(value){
    this.left = null;
    this.right = null;
    this.value = value;
  }
}

class BinarySearchTree {
  constructor(){
    this.root = null;
  }
  insert(value){
    const newNode = new Node(value);
    if (this.root === null) {
      this.root = newNode;
    } else {
      let currentNode = this.root;
      while(true){
        if(value < currentNode.value){
          //Left
          if(!currentNode.left){
            currentNode.left = newNode;
            return this;
          }
          currentNode = currentNode.left;
        } else {
          //Right
          if(!currentNode.right){
            currentNode.right = newNode;
            return this;
          } 
          currentNode = currentNode.right;
        }
      }
    }
  }
  lookup(value){
    if (!this.root) {
      return false;
    }
    let currentNode = this.root;
    while(currentNode){
      if(value < currentNode.value){
        currentNode = currentNode.left;
      } else if(value > currentNode.value){
        currentNode = currentNode.right;
      } else if (currentNode.value === value) {
        return currentNode;
      }
    }
    return null
  }
  remove(value) {
    if (!this.root) {
      return false;
    }
    let currentNode = this.root;
    let parentNode = null;
    while(currentNode){
      if(value < currentNode.value){
        parentNode = currentNode;
        currentNode = currentNode.left;
      } else if(value > currentNode.value){
        parentNode = currentNode;
        currentNode = currentNode.right;
      } else if (currentNode.value === value) {
        //We have a match, get to work!
        
        //Option 1: No right child: 
        if (currentNode.right === null) {
          if (parentNode === null) {
            this.root = currentNode.left;
          } else {
            
            //if parent > current value, make current left child a child of parent
            if(currentNode.value < parentNode.value) {
              parentNode.left = currentNode.left;
            
            //if parent < current value, make left child a right child of parent
            } else if(currentNode.value > parentNode.value) {
              parentNode.right = currentNode.left;
            }
          }
        
        //Option 2: Right child which doesnt have a left child
        } else if (currentNode.right.left === null) {
          currentNode.right.left = currentNode.left;
          if(parentNode === null) {
            this.root = currentNode.right;
          } else {
            
            //if parent > current, make right child of the left the parent
            if(currentNode.value < parentNode.value) {
              parentNode.left = currentNode.right;
            
            //if parent < current, make right child a right child of the parent
            } else if (currentNode.value > parentNode.value) {
              parentNode.right = currentNode.right;
            }
          }
        
        //Option 3: Right child that has a left child
        } else {

          //find the Right child's left most child
          let leftmost = currentNode.right.left;
          let leftmostParent = currentNode.right;
          while(leftmost.left !== null) {
            leftmostParent = leftmost;
            leftmost = leftmost.left;
          }
          
          //Parent's left subtree is now leftmost's right subtree
          leftmostParent.left = leftmost.right;
          leftmost.left = currentNode.left;
          leftmost.right = currentNode.right;

          if(parentNode === null) {
            this.root = leftmost;
          } else {
            if(currentNode.value < parentNode.value) {
              parentNode.left = leftmost;
            } else if(currentNode.value > parentNode.value) {
              parentNode.right = leftmost;
            }
          }
        }
      return true;
      }
    }
  }
}

const tree = new BinarySearchTree();
tree.insert(9)
tree.insert(4)
tree.insert(6)
tree.insert(20)
tree.insert(170)
tree.insert(15)
tree.insert(1)
tree.remove(170)
JSON.stringify(traverse(tree.root))

//     9
//  4     20
//1  6  15  170

function traverse(node) {
  const tree = { value: node.value };
  tree.left = node.left === null ? null : traverse(node.left);
  tree.right = node.right === null ? null : traverse(node.right);
  return tree;
}

 

댓글