跳至主要内容

[DS] Binary Search Tree

Binary Search Tree 基本概念是每一個節點最多有左右各一個子節點, 左子節點的值小於自身節點的值, 右子節點則大於本身.

A Binary Search Tree (BST) is a binary tree in which each vertex has only up to 2 children that satisfies BST property: All vertices in the left subtree of a vertex must hold a value smaller than its own and all vertices in the right subtree of a vertex must hold a value larger than its own.

Binary Search Tree, AVL Tree - VisuAlgo

只要符合上述定義, 無論樹長得如何, 都符合 BST 的規範.
BST 在搜尋資料上有 O(log N) 複雜度優勢, 是很常使用的基礎資料結構.

Data Struct

一般用資料結構來表示二元樹節點有兩種方式:

w/ parent

struct node {
int value;
node *parent;
node *l_child;
node *r_child;
}

w/o parent

struct node {
int value;
node *l_child;
node *r_child;
}

兩者的差別至在於節點定義中是否包含指向父節點的屬性, 節點間的連結是單向還是雙向關係.

包含父節點的資料結構雙向連結的屬性, 從二元樹中任一節點巡訪, 皆可完整還原完整二元樹的資料. 若有需要, 可以從任何一個節點開始尋訪, 無須每一次都必須從 Root 開始巡訪. 但當修改二元樹中的資料時, 需要注意維護節點中的連結關係, 尤其是父節點的連結.

而不包含父節點的結構中, API 呼叫基本上都必須從 Root 開始巡訪. 但相對修改資料時, 只需要處理子節點的單向連結關係, 程式結構都比較簡單.

兩者各有優缺點, 依實務需求決定. 本篇選用不包括父連結的結構.

type IBSTNode interface {
search(int) bool
insert(int)
remove(int) IBSTNode
findMin() int
findMax() int
findPredecessor(int) int
findSuccessor(int) int
inorder(*[]int)
}

type BST struct {
root IBSTNode
}

type BSTNode struct {
value int
left *BSTNode
right *BSTNode
}

ADT Basic Operate

BST (and especially balanced BST like AVL Tree) is an efficient data structure to implement a certain kind of Table (or Map) Abstract Data Type (ADT).

A Table ADT must support at least the following three operations as efficient as possible:

  • Search(v) — determine if v exists in the ADT or not,
  • Insert(v) — insert v into the ADT,
  • Remove(v) — remove v from the ADT.

Search(v)

func (n *BSTNode) search(val int) bool {
if n == nil { return false }
if n.value > val {
return n.left.search(val)
} else if n.value < val {
return n.right.search(val)
} else {
return true
}
}

Insert(v)

func (bst *BST) Insert(val int) {
if bst.root == nil { return }
bst.root = bst.root.insert(val)
}

func (n *BSTNode) insert(val int) IBSTNode {
return n.insertHelper(val)
}

func (n *BSTNode) insertHelper(val int) *BSTNode {
if n == nil { return newBSTNode(val) }

if val < n.value { n.left = n.left.insertHelper(val) }
else { n.right = n.right.insertHelper(val) }
return n
}

Remove(v)

func (bst *BST) Remove(val int) {
if bst.root == nil { return }
bst.root = bst.root.remove(val)
}

func (n *BSTNode) remove(val int) IBSTNode {
return n.removeHelper(val)
}

func (n *BSTNode) removeHelper(val int) *BSTNode {
if n == nil { return nil }

if n.value > val {
n.left = n.left.removeHelper(val)
} else if n.value < val {
n.right = n.right.removeHelper(val)
} else {
if n.left != nil && n.right != nil {
successor := n.right.findMin()
n.value = successor
n.right = n.right.removeHelper(successor)
} else if n.left != nil {
n = n.left
} else if n.right != nil {
n = n.right
} else {
return nil
}
}
return n
}

Find & Travsal

Min / Max

func (n *BSTNode) findMin() int {
if n.left == nil { return n.value }
else { return n.left.findMin() }
}

func (n *BSTNode) findMax() int {
if n.right == nil { return n.value }
else { return n.right.findMax() }
}

Predecessor

func (n *BSTNode) findPredecessor(val int) int {
predecessor := NOTFOUND
node := n
for node != nil && node.value != val {
if node.value < val {
predecessor = node.value
node = node.right
} else { node = node.left }
}
if node == nil { return NOTFOUND }
if node.left != nil { return node.left.findMax() }
else { return predecessor }
}

Successor

func (n *BSTNode) findSuccessor(val int) int {
successor := NOTFOUND
node := n
for node != nil && node.value != val {
if node.value > val {
successor = node.value
node = node.left
} else { node = node.right }
}
if node == nil { return NOTFOUND }
if node.right != nil { return node.right.findMin() }
else { return successor }
}

Traversal

Deep First Traversal

... TBD...

Inorder

An Inorder Traversal of this BST to obtain a list of sorted integers inside this BST.

Inorder Traversal is a recursive method whereby we visit the left subtree first, exhausts all items in the left subtree, visit the current root, before exploring the right subtree and all items in the right subtree.

func (bst *BST) Inorder() []int {
if bst.root == nil { return nil }
result := make([]int, 0)
bst.root.inorder(&result)
return result
}

func (n *BSTNode) inorder(buf *[]int) {
if n == nil { return }
n.left.inorder(buf)
*buf = append(*buf, n.value)
n.right.inorder(buf)
}

小結

Binary Heap 相比, BST 中程式遞迴可能會修改到物件本身. 呼叫和回傳的物件處理上需要比較注意.

See Also