[Data Structures] Binary Search Trees

Binary Search Tree

์šฉ์–ด ์„ค๋ช…

์ •๋ ฌ๋œ ์ด์ง„ํŠธ๋ฆฌ. ๋น ๋ฅธ ์กฐํšŒ, ์‚ฝ์ž… ๋ฐ ์ œ๊ฑฐ ์ž‘์—…์„ ์šฉ์ดํ•˜๊ฒŒ ํ•˜๋Š” ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ.

** binary search tree ์„ฑ๋ฆฝ ์กฐ๊ฑด**

left child์˜ ๊ฐ’์€ parent๋ณด๋‹ค ์ž‘์•„์•ผ ํ•œ๋‹ค. right child์˜ ๊ฐ’์€ parent๋ณด๋‹ค ์ปค์•ผ ํ•œ๋‹ค. subtree๋กœ ๋ดค์„ ๋•Œ๋„ ์œ„ ๋‘ ์กฐ๊ฑด์„ ๋งŒ์กฑํ•ด์•ผ ํ•œ๋‹ค.

array vs Binary Search Tree

์œ„์— ์ด๋ฏธ์ง€์— ์žˆ๋Š” binary search tree์™€ ๋ฐฐ์—ด [6, 8, 9, 10, 11, 12, 14] ์—์„œ ๊ฐ’์„ ์ฐพ๋Š”๋‹ค๊ณ  ๊ฐ€์ •ํ•ด๋ณด์ž.

  • array ์šฐ์„  ๋ฐฐ์—ด์—์„œ ๊ฐ’์„ ์ฐพ์„ ๋•Œ๋Š” contains(:) ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— O(n)์˜ ์‹œ๊ฐ„์ด ๊ฑธ๋ฆฐ๋‹ค. (ํ•˜๋‚˜์”ฉ ํ™•์ธํ•ด๋ด์•ผ ํ•˜๋‹ˆ๊นŒ)

  • binary search tree ํ•˜์ง€๋งŒ binary search tree๋ผ๋ฉด? ์œ„์— ์ ์–ด๋‘” ์„ฑ๋ฆฝ ์กฐ๊ฑด์„ ๋งŒ์กฑํ•œ๋‹ค๋Š” ๊ฐ€์ •ํ•˜์— ํŠธ๋ฆฌ๋ฅผ ํƒ์ƒ‰ํ•ด์„œ ๊ฐ’์„ ์ฐพ๊ธฐ ๋•Œ๋ฌธ์— ํŠธ๋ฆฌ์˜ ๋ถ„๊ธฐ์ ์—์„œ ์„ ํƒํ•˜๋Š” ์ˆœ๊ฐ„๋งˆ๋‹ค ๊ฒ€์ƒ‰ํ•ด์•ผ ํ•˜๋Š” ๊ณต๊ฐ„์„ ์ ˆ๋ฐ˜์œผ๋กœ ์ค„์ผ ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋ž˜์„œ O(log n)์ด๋ผ๋Š” ์‹œ๊ฐ„ ๋ณต์žก๋„๊ฐ€ ๋‚˜์˜จ๋‹ค.

Insertion

[6, 8, 9, 10, 11, 12, 14] ์— 1์„ ์ถ”๊ฐ€ํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•ด๋ณด์ž.

  • array ๋ฐฐ์—ด์˜ ๊ฐ€์žฅ ์•ž์— ๊ฐ’์ด ์ถ”๊ฐ€๋œ๋‹ค๋ฉด ๊ธฐ์กด ๋ฐฐ์—ด ์š”์†Œ๋“ค์ด ํ•œ์นธ์”ฉ ๋’ค๋กœ ๋ฐ€๋ฆฌ๊ฒŒ ๋˜๋ฏ€๋กœ ๋ฐฐ์—ด์˜ insertion ์‹œ๊ฐ„ ๋ณต์žก๋„๋Š” O(n)์ด ๋œ๋‹ค.

  • binary search tree binary search tree๋Š” search์™€ ๋™์ผํ•˜๊ฒŒ ํŠธ๋ฆฌ์˜ ๋ถ„๊ธฐ์ ์—์„œ ํ•œ ๋ฒˆ ์„ ํƒ ํ• ๋•Œ๋งˆ๋‹ค ๊ฒ€์ƒ‰ ๊ณต๊ฐ„์ด ์ ˆ๋ฐ˜์œผ๋กœ ์ค„์–ด๋“œ๋ฏ€๋กœ O(log n).

Removal

  • array [6, 8, 9, 10, 11, 12, 14]์—์„œ 9๋ฅผ ์ œ๊ฑฐํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•ด๋ณด์ž. ์ค‘๊ฐ„ ๊ฐ’์„ ์ง€์šฐ๋ฏ€๋กœ ๋’ค์— ์žˆ๋Š” ๊ฐ’๋“ค์ด ํ•œ์นธ์”ฉ ์•ž์œผ๋กœ ๋‹น๊ฒจ์ง„๋‹ค. (O(n)).

  • binary search tree Insertion, Search์™€ ๋™์ผํ•˜๊ฒŒ O(log n).

Binary Search Tree ๊ตฌํ˜„ํ•ด๋ณด๊ธฐ

์•ž์—์„œ ์ƒ์„ฑํ•ด๋‘” BinaryNode๋ฅผ ํ™œ์šฉํ•ด์„œ Binary Search Tree๋ฅผ ๊ตฌํ˜„ํ•œ๋‹ค.

struct BinarySearchTree<Element: Comparable> {
  private(set) var root: BinaryNode<Element>?
}

Insert

๊ฐ’์„ ์ถ”๊ฐ€ํ•  ๋•Œ ์œ ์˜ํ•ด์•ผ ํ•  ์ ์€ right child๋Š” ๋” ํฐ ๊ฐ’, left child๋Š” ๋” ์ž‘์€ ๊ฐ’ ์ด๋ผ๋Š” ๋ถ€๋ถ„์ด๋‹ค.

private func insert(from node: BinaryNode<Element>?, value: Element) -> BinaryNode<Element> {
        // terminating recursion
        guard let node = node else {
            return BinaryNode(value: value)
        }
        
        // ๋“ค์–ด๊ฐˆ ๊ฐ’์— ๋”ฐ๋ผ left๋กœ ๊ฐ€์•ผ ํ•˜๋Š”์ง€ right๋กœ ๊ฐ€์•ผ ํ•˜๋Š”์ง€ ์ฒดํฌ
        if value < node.value {
            node.leftChild = insert(from: node.leftChild, value: value)
        } else {
            node.rightChild = insert(from: node.rightChild, value: value)
        }
        
        return node
    }

Find

func contains(_ value: Element) -> Bool {
        guard let root = root else {
            return false
        }
        
        var found = false
        root.traverseInOrder {
            if $0 == value {
                found = true
            }
        }
        
        return found
    }

in-order traversal์ด O(n)์ด๋ฏ€๋กœ contains ๋˜ํ•œ O(n)

func contains(_ value: Element) -> Bool {
        // ์‹œ์ž‘ ๋…ธ๋“œ๋Š” root๋ถ€ํ„ฐ ์‹œ์ž‘
        var current = root
        
        // node๊ฐ€ nil์ด ์•„๋‹Œ ๊ฒฝ์šฐ ๊ณ„์† ๋ฐ˜๋ณต
        while let node = current {
            // ๋งŒ์•ฝ node ๊ฐ’์ด ์ฐพ๋Š” ๊ฐ’์ด๋ผ๋ฉด true
            if node.value == value {
                return true
            }
            // current๋ณด๋‹ค value๊ฐ€ ์ž‘์œผ๋ฉด left, ํฌ๋ฉด right๋กœ ์ด๋™ํ•ด์„œ ์ฐพ์Œ
            if value < node.value {
                current = node.leftChild
            } else {
                current = node.rightChild
            }
        }
        
        return false
    }

์ด๋ ‡๊ฒŒ ์ฐพ์œผ๋ฉด O(log n)

Remove

  • Leaf Node ์ž์‹์ด ์—†๋Š” ๊ฐ€์žฅ ๋์˜ ๋…ธ๋“œ์ด๋ฏ€๋กœ ๊ทธ๋ƒฅ ์ œ๊ฑฐ

  • Nodes with one child ์ง€์›Œ์•ผ ํ•˜๋Š” ๋…ธ๋“œ๊ฐ€ ์ž์‹์ด ํ•˜๋‚˜ ์žˆ๋‹ค๋ฉด ๋…ธ๋“œ๋ฅผ ์ง€์šฐ๊ณ  ์ž์‹ ๋…ธ๋“œ๋ฅผ ๊ทธ ์œ„์˜ ๋ถ€๋ชจ(์กฐ๋ถ€๋ชจโ€ฆ?)์— ์—ฐ๊ฒฐํ•ด์ค€๋‹ค.

  • Nodes with two children ์ง€์šฐ๊ณ  ์‹ถ์€ ๋…ธ๋“œ๊ฐ€ ์ž์‹์ด ์žˆ๋Š” ๊ฒฝ์šฐ๋Š” ์ง€์šฐ๊ธฐ๊ฐ€ ์‰ฝ์ง€ ์•Š๋‹ค.

๊ทธ๋ƒฅ ๋ฌด์ž‘์ • ์ง€์šฐ๊ณ  ์•„๋ž˜ ์ž์‹๋“ค์„ ์—ฐ๊ฒฐํ•˜๊ณ  ์‹ถ์–ด๋„ binary search tree์ด๋ฏ€๋กœ ๋…ธ๋“œ์˜ children์ด ์„ธ๊ฐœ๊ฐ€ ๋  ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

๊ทธ๋Ÿผ ์–ด๋–ป๊ฒŒ ์‚ญ์ œ๋ฅผ ํ•ด์•ผํ• ๊นŒ?

๋‘ ์ž์‹์œผ๋กœ ๋…ธ๋“œ๋ฅผ ์ œ๊ฑฐํ•  ๋•Œ, ์ œ๊ฑฐํ•œ ๋…ธ๋“œ๋ฅผ ์˜ค๋ฅธ์ชฝ ์„œ๋ธŒ ํŠธ๋ฆฌ์—์„œ ๊ฐ€์žฅ ์ž‘์€ ๋…ธ๋“œ๋กœ ๋ฐ”๊พผ๋‹ค. BST์˜ ๊ทœ์น™์— ๋”ฐ๋ผ, ์ž‘์€ ๋…ธ๋“œ๋Š” ์˜ค๋ฅธ์ชฝ ์„œ๋ธŒํŠธ๋ฆฌ์˜ ๊ฐ€์žฅ ์™ผ์ชฝ ๋…ธ๋“œ์ด๋‹ค.

์ด๊ฒŒ ๋ฌด์Šจ๋ง์ด๋ƒ ํ•˜๋ฉด, ์œ„ ํŠธ๋ฆฌ์—์„œ ๋ดค์„ ๋•Œ ์ œ๊ฑฐํ•ด์•ผํ•˜๋Š” ๋…ธ๋“œ์ธ 25์˜ ์˜ค๋ฅธ์ชฝ ์„œ๋ธŒํŠธ๋ฆฌ์—์„œ ๊ฐ€์žฅ ์ž‘์€ ๊ฐ’์„ ๊ฐ€์ง„ ๋…ธ๋“œ, 27์„ 25 ์ž๋ฆฌ์— ๊ฐ–๋‹ค ๋„ฃ์œผ๋ผ๋Š” ์†Œ๋ฆฌ๋‹ค.

์š”๋ ‡๊ฒŒ ํ•˜๋ฉด ์˜ค๋ฅธ์ชฝ ์„œ๋ธŒํŠธ๋ฆฌ์—์„œ ๊ฐ€์žฅ ์ž‘์€ ๊ฐ’์„ ๊ฐ€์ ธ์™”๊ธฐ ๋•Œ๋ฌธ์— ์˜ค๋ฅธ์ชฝ ์„œ๋ธŒํŠธ๋ฆฌ์˜ ๋ชจ๋“  ๋…ธ๋“œ๊ฐ€ 27๋ณด๋‹ค ํด ๊ฒƒ์ด๊ณ , ์˜ค๋ฅธ์ชฝ ์„œ๋ธŒํŠธ๋ฆฌ์—์„œ ๊ฐ€์ ธ์™”๊ธฐ ๋•Œ๋ฌธ์— ๋‹น์—ฐํžˆ ์™ผ์ชฝ ์„œ๋ธŒํŠธ๋ฆฌ์˜ ๋ชจ๋“  ๋…ธ๋“œ๋Š” 27๋ณด๋‹ค ์ž‘์€ ๊ฐ’์ž„์„ ๋ณด์žฅํ•  ์ˆ˜ ์žˆ๋‹ค. (BST์˜ ์ƒ์„ฑ ์กฐ๊ฑด์ด ๊ทธ๋Ÿฌ๋ฏ€๋กœ..!)

์š”๊ฑธ ์ฝ”๋“œ๋กœ ํ‘œํ˜„ํ•˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

private func remove(node: BinaryNode<Element>?, value: Element) -> BinaryNode<Element>? {
        guard let node = node else {
            return nil
        }
        
        // leaf ๋…ธ๋“œ๋ฅผ ์ œ๊ฑฐํ•˜๋Š” ๊ฒฝ์šฐ
        if node.leftChild == nil && node.rightChild == nil {
          return nil
        }
        
        // left child๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ
        if node.leftChild == nil {
          return node.rightChild
        }
        
        // right child๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ
        if node.rightChild == nil {
          return node.leftChild
        }
        
        // ์–‘์ชฝ ๋‹ค ์ž์‹์ด ์žˆ๋Š” ๊ฒฝ์šฐ
        node.value = node.rightChild!.min.value
        node.rightChild = remove(node: node.rightChild, value: node.value)
        
        return node
    }

Leave a comment