티스토리 뷰

728x90
SMALL

이진트리

function BinaryTree() {
    this._root = null;
}

선순위 순회 : 루트, 왼쪽, 오른쪽순

BinaryTree.prototype.traversePreOrder = function () {
  traverseInOrderHelper(this._root);

  function traverseInOrderHelper(node) {
    if (!node) return;
    console.log(node.value);
    traverseInOrderHelper(node.left);
    traverseInOrderHelper(node.right);
  }
};

중순위 순회 : 왼쪽, 루트, 오른쪽 순

BinaryTree.prototype.traverseInOrder = function() {
    traverseInOrderHelper(this._root);

    function traverseInOrderHelper(node) {
        if (!node)
            return;
        traverseInOrderHelper(node.left);
        console.log(node.value);
        traverseInOrderHelper(node.right);
    }
}

후순위 순회 : 왼쪽, 오른쪽, 루트 순

BinaryTree.prototype.traversePostOrder = function() {
    traversePostOrderHelper(this._root);

    function traversePostOrderHelper(node) {
        if (node.left)
            traversePostOrderHelper(node.left);
        if (node.right)
            traversePostOrderHelper(node.right);
        console.log(node.value);
    }
}

단계순위 순회(너비 우선 검색 BFS:breadth first search) : 각 노드 단계를 방문(깊게x 차례대로)

BinaryTree.prototype.traverseLevelOrder = function() {
    // Breath first search
    var root = this._root,
        queue = [];

    if (!root)
        return;
    queue.push(root);

    while (queue.length) {
        var temp = queue.shift();
        console.log(temp.value);
        if (temp.left)
            queue.push(temp.left);
        if (temp.right)
            queue.push(temp.right);
    }
}

 

요약

잎 노드(자식 노드가 없는 노드)를 방문하기 전에 루트를 조사할 필요가 있는 경우 선순위 순회를 선택

부모 노드를 방문하기 전에 잎 노드를 먼저 조사해야 하는 경우 후순위 순회를 선택

트리의 노드 자체에 순서가 있어서 트리를 원래 순서대로 방문하고 싶은 경우 중순위 순회를 선택

후순위 순회는 트리가 생성된 순서와 다른 순서로 트리를 방문

시간 복잡도 : O(n)

 

이진 검색 트리 BST, binary search tree

왼쪽 자식이 부모보다 작고 오른쪽 자식이 부모보다 크다.

검색과 삽입, 특정 값을 지닌 노드 제거의 시간 복잡도가 O(log2(n))

 

function BinarySearchTree() {
    this._root = null;
}

삽입

루트가 빈 경우 루트가 신규 노드가 된다. 루트가 비어 있지 않다면 while 루프를 사용해 조건이 만족될 때까지 이진 검색 트리를 순회한다. 각 루프 반복 시 신규 노드가 현재 루트보다 크거나 작은지 확인한다.

BinarySearchTree.prototype.insert = function(value) {
    var thisNode = {
        left: null,
        right: null,
        value: value
    };
    if (!this._root) {
        //if there is no root value yet
        this._root = thisNode;
    } else {
        //loop traverse until
        var currentRoot = this._root;
        while (true) {
            if (currentRoot.value > value) {
                //let's increment if it's not a null and insert if it is a null
                if (currentRoot.left != null) {
                    currentRoot = currentRoot.left;
                } else {
                    currentRoot.left = thisNode;
                    break;
                }
            } else if (currentRoot.value < value) {
                //if bigger than current, put it on the right
                //let's increment if it's not a null and insert if it is a null
                if (currentRoot.right != null) {
                    currentRoot = currentRoot.right;
                } else {
                    currentRoot.right = thisNode;
                    break;
                }
            } else {
                //case that both are the same
                break;
            }
        }
    }
}

시간 복잡도(균형 트리) : O(log2(n))

시간 복잡도(불균형 트리) : O(n)

 

삭제

삭제 알고리즘은 우선 삭제하고자 하는 값을 지닌 노드를 찾기 위해 트리를 순회한다. 해당 노드를 찾은 경우 다음 세 가지 경우가 있다.

1. 노드에 자식이 없다 - null을 반환

2. 노드에 자식이 하나 있다. - 현재 자식을 반환. 해당 자식이 위 단계로 올라가서 부모를 대체

3. 노드에 자식이 두 개 있다. 왼쪽 하위 트리의 최대치를 찾거나 오른쪽 하위 트리의 최소치를 찾아서 해당 노드를 대체

BinarySearchTree.prototype.remove = function(value) {

    return deleteRecursively(this._root, value);

    function deleteRecursively(root, value) {
        if (!root) {
            return null;
        } else if (value < root.value) {
            root.left = deleteRecursively(root.left, value);
        } else if (value > root.value) {
            root.right = deleteRecursively(root.right, value);
        } else {
            //no child
            if (!root.left && !root.right) {
                return null; // case 1
            } else if (!root.left) { // case 2
                root = root.right;
                return root;
            } else if (!root.right) { // case 2
                root = root.left;
                return root;
            } else {
                var temp = findMin(root.right); // case 3
                root.value = temp.value;
                root.right = deleteRecursively(root.right, temp.value);
                return root;
            }
        }
        return root;
    }

    function findMin(root) {
        while (root.left) {
            root = root.left;
        }
        return root;
    }
}

시간 복잡도(균형 트리): O(log2(n))

시간 복잡도(불균형 트리): O(n)

 

검색

BinarySearchTree.prototype.findNode = function(value) {
    var currentRoot = this._root,
        found = false;
    while (currentRoot) {
        if (currentRoot.value > value) {
            currentRoot = currentRoot.left;
        } else if (currentRoot.value < value) {
            currentRoot = currentRoot.right;
        } else {
            //we've found the node
            found = true;
            break;
        }
    }
}

var bst1 = new BinarySearchTree();
bst1.insert(1);
bst1.insert(3);
bst1.insert(2);
bst1.findNode(3); // true
bst1.findNode(5); // false
console.log(bst1);

AVL 트리

AVL은 스스로 균형을 잡는 이진 검색 트리다.

function AVLTree(value) {
  this.left = null;
  this.right = null;
  this.value = value;
  this.depth = 1;
}

AVL 트리의 높이

AVLTree.prototype.setDepthBasedOnChildren = function () {
  if (this.node == null) {
    this.depth = 1;
  }

  if (this.left != null) {
    this.depth = this.left.depth + 1;
  }
  if (this.right != null && this.depth <= this.right.depth) {
    this.depth = this.right.depth + 1;
  }
};

단일 회전 ㅆ바련ㄴ이 좇같이 해놧ㄴ[

왼쪽 회전

AVLTree.prototype.rotateLL = function () {
  var valueBefore = this.value;
  var rightBefore = this.right;
  this.value = this.left.value;

  this.right = this.left;
  this.left = this.left.left;
  this.right.left = this.right.right;
  this.right.right = rightBefore;
  this.right.value = valueBefore;

  this.right.setDepthBasedOnChildren();
  this.setDepthBasedOnChildren();
};

오른쪽 회전

AVLTree.prototype.rotateRR = function () {
  // the right side is too long => rotate from the right (_not_ rightwards)
  var valueBefore = this.value;
  var leftBefore = this.left;
  this.value = this.right.value;

  this.left = this.right;
  this.right = this.right.right;
  this.left.right = this.left.left;
  this.left.left = leftBefore;
  this.left.value = valueBefore;

  this.left.setDepthBasedOnChildren();
  this.setDepthBasedOnChildren();
};

이중 회전

한번의 회전을 한 이후에도 AVL 트리가 여전히 불균형이라면 완전한 균형을 위해 두번 회전해야 한다.

오른쪽 왼쪽 회전(오른쪽 이후에 왼쪽)

왼쪽 오른쪽 회전(왼쪽 이후에 오른쪽)

 

트리 균형 잡기

AVLTree.prototype.balance = function () {
  var ldepth = this.left == null ? 0 : this.left.depth;
  var rdepth = this.right == null ? 0 : this.right.depth;

  if (ldepth > rdepth + 1) {
    // LR or LL rotation
    var lldepth = this.left.left == null ? 0 : this.left.left.depth;
    var lrdepth = this.left.right == null ? 0 : this.left.right.depth;

    if (lldepth < lrdepth) {
      // LR rotation consists of a RR rotation of the left child
      this.left.rotateRR();
      // plus a LL rotation of this node, which happens anyway
    }
    this.rotateLL();
  } else if (ldepth + 1 < rdepth) {
    // RR or RL rorarion
    var rrdepth = this.right.right == null ? 0 : this.right.right.depth;
    var rldepth = this.right.left == null ? 0 : this.right.left.depth;

    if (rldepth > rrdepth) {
      // RR rotation consists of a LL rotation of the right child
      this.right.rotateLL();
      // plus a RR rotation of this node, which happens anyway
    }
    this.rotateRR();
  }
};

삽입

AVLTree.prototype.insert = function (value) {
  var childInserted = false;
  if (value == this.value) {
    return false; // should be all unique
  } else if (value < this.value) {
    if (this.left == null) {
      this.left = new AVLTree(value);
      childInserted = true;
    } else {
      childInserted = this.left.insert(value);
      if (childInserted == true) this.balance();
    }
  } else if (value > this.value) {
    if (this.right == null) {
      this.right = new AVLTree(value);
      childInserted = true;
    } else {
      childInserted = this.right.insert(value);

      if (childInserted == true) this.balance();
    }
  }
  if (childInserted == true) this.setDepthBasedOnChildren();
  return childInserted;
};

시간 복잡도:O(nlog2(n))

공간 복잡도:O(nlog2(n))

 

삭제

AVLTree.prototype.remove = function (value) {
  return deleteRecursively(this, value);

  function deleteRecursively(root, value) {
    if (!root) {
      return null;
    } else if (value < root.value) {
      root.left = deleteRecursively(root.left, value);
    } else if (value > root.value) {
      root.right = deleteRecursively(root.right, value);
    } else {
      //no child
      if (!root.left && !root.right) {
        return null; // case 1
      } else if (!root.left) {
        root = root.right;
        return root;
      } else if (!root.right) {
        root = root.left;
        return root;
      } else {
        var temp = findMin(root.right);
        root.value = temp.value;
        root.right = deleteRecursively(root.right, temp.value);
        return root;
      }
    }
    root.setDepthBasedOnChildren(); // ONLY DIFFERENCE from the BST one
    return root;
  }

  function findMin(root) {
    while (root.left) root = root.left;
    return root;
  }
};

dd

var avlTest = new AVLTree(1, "");
avlTest.insert(2);
avlTest.insert(3);
avlTest.insert(4);
avlTest.insert(5);
avlTest.insert(123);
avlTest.insert(203);
avlTest.insert(2222);
console.log(avlTest);
728x90
LIST

' > 자바스크립트로 하는 자료 구조와 알고리즘' 카테고리의 다른 글

그래프  (0) 2020.12.22
  (0) 2020.12.21
캐싱  (0) 2020.12.21
연결 리스트  (0) 2020.12.20
스택과 큐  (0) 2020.12.19
댓글
공지사항