Chapter 14: Binary Search Trees
前言
二元搜尋樹是一個相當厲害的資料結構,在尋找,插入,刪除的平均時間複雜度為O(log n),相對其他線性的資料結構都快上很多。
大綱
Case study: array vs. BST
Lookup
Insertion
Removal
Implementation
Inserting elements
Finding elements
Optimizing contains
Removing elements
Case 1: Leaf node
Case 2: Nodes with one child
Case 3: Nodes with two children
Implementation
Case study: array vs. BST
先來討論一下array和BST的差異,以下面case為例

Lookup
對於一個unsorted array只能一一尋找所需要的元素
That’s why array.contains(:) is an O(n) operation.
對於一顆平衡的BST, 每次的尋找都可以排除一半其他不可能的元素,其複雜度為O(log n)
Insertion
每次插入元素都array中,都會造成被插入位置的後方元素進行位移的動作
In the above example, zero is inserted in front of the array, causing all other elements to shift backwards by one position. Inserting into an array has a time complexity of O(n).
對於BST找到對應插入位置只需要O(log n)
Removing an element from a BST is still an O(log n) operation.
Inserting elements
對於一顆BST的定義
leftChild.vale < root.value
rightChild.value >= root.value
extension BinarySearchTree {
public mutating func insert(_ value: Element) {
// 根據插入值決定新的root位置
root = insert(from: root, value: value)
}
private func insert(from node: BinaryNode<Element>?, value: Element) -> BinaryNode<Element> {
// 如果目前沒有root, 則當前的插入值則為新root
guard let node = node else {
return BinaryNode(value: value)
}
// 利用遞迴方式繼續往下尋找
if value < node.value {
node.leftChild = insert(from: node.leftChild, value: value)
} else {
node.rightChild = insert(from: node.rightChild, value: value)
}
return node
}
}
Finding elements
透過traverseInOrder來找element,並沒有好好活用到BST的特性。
public func contains(_ value: Element) -> Bool {
guard let root = root else {
return false
}
var found = false
// In-order traversal has a time complexity of O(n)
root.traverseInOrder {
if $0 == value {
found = true
}
}
return found
}
Optimizing contains
活用BST特性,不用每個element都要走過。
public func opt_contains(_ value: Element) -> Bool {
var current = root
while let node = current {
if node.value == value {
return true
}
// 利用BST特點來尋找
// This implementation of contains is an O(log n) operation in blanced binary search tree
if value < node.value {
current = node.leftChild
} else {
current = node.rightChild
}
}
return false
}
Removing elements
刪除元素,對BST是比較複雜的,有下列種case要討論
Case 1: Leaf node
被刪除的節點是葉子,這個好處理就直接刪除
Case 2: Nodes with one child
被刪除的節點有一個child, 那需要處理這個child,但也很簡單,就是把這個child連接到阿公節點。
Case 3: Nodes with two children
被刪除的節點有兩個child, 處理方式就是拿右子樹的最小值來當root,當然也可以拿左子樹的最大值來當root。
extension BinarySearchTree {
public mutating func remove(_ value: Element) {
root = remove(node: root, value: value)
}
private func remove(node: BinaryNode<Element>?, value: Element) -> BinaryNode<Element>? {
guard let node = node else {
// 連root都沒有,也沒啥好刪
return nil
}
if value == node.value {
// case1: 在leaf
if node.leftChild == nil && node.rightChild == nil {
// 直接刪除root
return nil
}
// case2: 此node只有一個child
if node.leftChild == nil {
return node.rightChild
}
if node.rightChild == nil {
return node.leftChild
}
// case3: 此node有兩個children
node.value = node.rightChild!.min.value
node.rightChild = remove(node: node.rightChild, value: node.value)
} else if value < node.value {
// 遞迴處理
node.leftChild = remove(node: node.leftChild, value: value)
} else {
// 遞迴處理
node.rightChild = remove(node: node.rightChild, value: value)
}
return node
}
}
Last updated
Was this helpful?