이진 검색 트리(내 개인 Google 인터뷰 연구 노트)

Note: This is not a "professionally written" post. This is a post sharing personal notes I wrote down while preparing for FAANG interviews.



See all of my Google, Amazon, & Facebook interview study notes


이진 트리 연습




  • Is This a Binary Search Tree? | HackerRank
  • Binary Search Tree : Lowest Common Ancestor | HackerRank
  • Binary Search Tree : Insertion | HackerRank

  • 이진 검색 트리




  • 이진 트리
  • 각 노드에는 키와 선택적 관련 값이 포함되어 있습니다
  • .
  • 특히 빠른 항목 조회, 추가 및 제거를 허용합니다
  • .

    이진 검색 트리 ~ 노드 배열


  • 특정 노드의 왼쪽 하위 트리
  • 해당 노드의 키보다 작은 키를 가진 노드를 포함합니다
  • .

  • 특정 노드의 오른쪽 하위 트리
  • 해당 노드의 키보다 큰 키가 있는 노드를 포함합니다
  • .

  • 오른쪽 및 왼쪽 하위 트리도 차례로 이진 키가 됩니다
  • .

    이진 검색 트리 시간 복잡도


  • 평균적인 경우 O(log n), 여기서 _n은 트리의 노드 수입니다.
  • 삽입 작업
  • 검색 작업
  • 삭제 작업

  • 최악의 경우 트리가 균형이 맞지 않아 O(n)
  • 삽입 작업
  • 검색 작업
  • 삭제 작업


  • 이진 검색 트리 공간 복잡성


  • 이진 트리의 공간 복잡도는 평균 및 최악의 시나리오에서 O(n)입니다
  • .

    순회 유형

    순회


  • 선주문 순회
  • 부모-LeftChild-RightChild 순서로 노드를 방문합니다
  • .

  • 순서 순회
  • LeftChild-Parent-RightChild 순서로 노드를 방문합니다.
  • 이러한 방식으로 트리는 다음의 오름차순으로 탐색됩니다.
    열쇠

  • 사후 순회
  • LeftChild-RightChild-Parent의 노드를 방문합니다
  • .


    Check out my FAANG interview study notes (70+ documents worth of resources)

    Clean Code
    Clean Code Studio

    Github Data Structures & Algorithms 101 (Using JS)

    Binary Tree Node

    /*------------------------------------------------------------------------------------
     |   Binary Tree Node
     *------------------------------------------------------------------------------------
     |
     |   . Api
     |     -> leftHeight
     |        @return {number}
     |     -> rightHeight
     |        @return {number}
     |     -> height
     |        @return {number}
     |     -> balanceFactor
     |        @return {number}
     |     -> uncle
     |        @return {BinaryTreeNode}
     |     -> setValue
     |        @param {*} value
     |        @return {BinaryTreeNode}
     |     -> setLeft
     |        @param {BinaryTreeNode} node
     |        @return {BinaryTreeNode}
     |     -> setRight
     |        @param {BinaryTreeNode} node
     |        @return {BinaryTreeNode}
     |     -> removeChild
     |        @param {BinaryTreeNode} nodeToRemove
     |        @return {boolean}
     |     -> replaceChild
     |        @param {BinaryTreeNode} nodeToReplace
     |        @param {BinaryTreeNode} replacementNode
     |        @return {boolean}
     |     -> copyNode (static)
     |        @param {BinaryTreeNode} sourceNode
     |        @param {BinaryTreeNode} targetNode
     |     -> traverseInOrder
     |        @return {*[]}
     |     -> toString
     |        @return {string}
     |
     */
    const HashTable = require('@DataStructure/HashTable.js')
    const Comparator = require('@Helper/Comparator')
    
    
    class BinaryTreeNode 
    {
      static make(...parameters)
      {
        return new this(...parameters)
      }
    
      constructor(data = null) 
      {
        this.data = data
        this.left = null
        this.right = null
        this.parent = null
    
        // Any node related meta information may be stored here.
        this.meta = HashTable.make()
    
        // This comparator is used to compare binary tree nodes with each other.
        this.nodeComparator = Comparator.make()
      }
    
    
      get leftHeight() 
      {
        if (!this.left) return 0
    
        return this.left.height + 1
      }
    
      get rightHeight() 
      {
        if (!this.right) return 0
    
        return this.right.height + 1
      }
    
      get height() 
      {
        return Math.max(this.leftHeight, this.rightHeight)
      }
    
      get balanceFactor() 
      {
        return this.leftHeight - this.rightHeight
      }
    
      get uncle() 
      {
        if (!this.parent) return undefined
    
        // Check if current node has grand-parent.
        if (!this.parent.parent) return undefined
    
        // Check if grand-parent has two children.
        if (!this.parent.parent.left || !this.parent.parent.right) return undefined
    
        // So for now we know that current node has grand-parent and this
        // grand-parent has two children. Let's find out who is the uncle.
        if (this.nodeComparator.equal(this.parent, this.parent.parent.left)) return this.parent.parent.right
    
        // Left one is an uncle.
        return this.parent.parent.left
      }
    
      setValue(value) 
      {
        this.value = value
    
        return this
      }
    
    
      setLeft(node) 
      {
        // Reset parent for left node since it is going to be detached.
        if (this.left) this.left.parent = null
    
        // Attach new node to the left.
        this.left = node
    
        // Make current node to be a parent for new left one.
        if (this.left) this.left.parent = this
    
        return this
      }
    
      setRight(node) 
      {
        // Reset parent for right node since it is going to be detached.
        if (this.right) this.right.parent = null
    
        // Attach new node to the right.
        this.right = node
    
        // Make current node to be a parent for new right one.
        if (node) this.right.parent = this
    
        return this
      }
    
      removeChild(nodeToRemove) 
      {
        if (this.left && this.nodeComparator.equal(this.left, nodeToRemove)) {
          this.left = null
          return true
        }
    
        if (this.right && this.nodeComparator.equal(this.right, nodeToRemove)) {
          this.right = null
          return true
        }
    
        return false
      }
    
      replaceChild(nodeToReplace, replacementNode) 
      {
        if (!nodeToReplace || !replacementNode) return false
    
        if (this.left && this.nodeComparator.equal(this.left, nodeToReplace)) {
          this.left = replacementNode
          return true
        }
    
        if (this.right && this.nodeComparator.equal(this.right, nodeToReplace)) {
          this.right = replacementNode
          return true
        }
    
        return false
      }
    
      static copyNode(sourceNode, targetNode) 
      {
        targetNode.setValue(sourceNode.value)
        targetNode.setLeft(sourceNode.left)
        targetNode.setRight(sourceNode.right)
      }
    
      traverseInOrder() 
      {
        let traverse = []
    
        // Add left node.
        if (this.left) {
          traverse = traverse.concat(this.left.traverseInOrder())
        }
    
        // Add root.
        traverse.push(this.value)
    
        // Add right node.
        if (this.right) {
          traverse = traverse.concat(this.right.traverseInOrder())
        }
    
        return traverse
      }
    
      toString() 
      {
        return this.traverseInOrder().toString()
      }
    }
    
    module.exports = BinaryTreeNode
    


    Binary Search Tree

    // https://opendatastructures.org/versions/edition-0.1c/ods-java/node36.html
    
    
    // lookup
    // insert
    // delete
    // access 
    
    // depth
    // count
    // isEmpty
    // isNotEmpty
    
    
    /** 
     | has(value)
       insert(value)
       lookup(value)
     | delete(value) // TODO: fix this function
     | depthFirstLog (cllback)
    */
    
    class BinarySearchTree 
    {
        constructor (value)
        {
          this.value = value 
          this.left = undefined
          this.right = undefined
    
          return this
        }
    
        insert (value)
        {
          let node = new BinarySearchTree(value)
    
          let recurse = bst => {
            if (bst.value > value && bst.left === undefined) {
              bst.left = node
            } else if (bst.value > value) {
              recurse(bst.left)
            } else if (bst.value < value && bst.right === undefined) {
              bst.right = node
            } else if (bst.value < value) {
              recurse(bst.right)
            }
          }
    
          recurse(this)
    
        }
    
        lookup (value)
        {
          let resolved = -1
          let recurse = bst => {
            if (bst.value === value) {
              resolved = bst
            } else if (bst.left !== undefined && value < bst.value) {
              recurse(bst.left)
            } else if (bst.right !== undefined && value > bst.value) {
              recurse(bst.right)
            }
          }
    
          recurse(this)
    
          return resolved
        }
    
        has (value)
        {
          let hasValue = false;
          //accepts a value and returns a boolean reflecting whether or not the value is contained in the tree.
          let recurse = bst => {
            if (bst.value === value) {
              hasValue = true
            } else if (bst.left !== undefined && value < bst.value) {
              recurse(bst.left)
            } else if (bst.right !== undefined && value > bst.value) {
              recurse(bst.right)
            }
          }
    
          recurse(this)
    
          return hasValue
        }
    
        delete (start) 
        {
          let data = []
          let queue = []
    
          let node = start ? this.lookup(start) : this.root
    
          queue.push(node)
          while (queue.length) {
            node = queue.shift()
            data.push(node.value)
    
            if (node.left) queue.push(node.left)
            if (node.right) queue.push(node.right)
          }
        }
    
        depthFirstLog (callback = console.log)
        {
          let recurse = bst => {
            callback.call(bst, bst.value)
    
            if (bst.left !== undefined) recurse(bst.left)
            if (bst.right !== undefined) recurse(bst.right)
          }
    
          recurse(this)
        }
    }
    
    
    
    module.exports = BinarySearchTree
    



    FAANG Study Resource: Cracking the Coding Interview
    (Google Recommended)






    My FAANG interview study notes

    Clean Code
    Clean Code Studio
    Clean Code Clean Life ~ Simplify

    Clean Code Studio 뉴스레터를 구독하세요!

    좋은 웹페이지 즐겨찾기