hello-algo/en/docs/chapter_tree/binary_search_tree.md

665 lines
22 KiB
Markdown
Raw Normal View History

2024-04-02 19:00:01 +08:00
---
comments: true
---
# 7.4   Binary search tree
2024-05-02 01:46:14 +08:00
As shown in Figure 7-16, a <u>binary search tree</u> satisfies the following conditions.
2024-04-02 19:00:01 +08:00
2024-05-02 01:46:14 +08:00
1. For the root node, the value of all nodes in the left subtree $<$ the value of the root node $<$ the value of all nodes in the right subtree.
2024-04-02 19:00:01 +08:00
2. The left and right subtrees of any node are also binary search trees, i.e., they satisfy condition `1.` as well.
![Binary search tree](binary_search_tree.assets/binary_search_tree.png){ class="animation-figure" }
<p align="center"> Figure 7-16 &nbsp; Binary search tree </p>
## 7.4.1 &nbsp; Operations on a binary search tree
2024-09-28 09:26:54 +08:00
We encapsulate the binary search tree as a class `BinarySearchTree` and declare a member variable `root` pointing to the tree's root node.
2024-04-02 19:00:01 +08:00
### 1. &nbsp; Searching for a node
2024-09-28 09:26:54 +08:00
Given a target node value `num`, one can search according to the properties of the binary search tree. As shown in Figure 7-17, we declare a node `cur`, start from the binary tree's root node `root`, and loop to compare the size between the node value `cur.val` and `num`.
2024-04-02 19:00:01 +08:00
- If `cur.val < num`, it means the target node is in `cur`'s right subtree, thus execute `cur = cur.right`.
- If `cur.val > num`, it means the target node is in `cur`'s left subtree, thus execute `cur = cur.left`.
2024-09-28 09:26:54 +08:00
- If `cur.val = num`, it means the target node is found, exit the loop, and return the node.
2024-04-02 19:00:01 +08:00
=== "<1>"
![Example of searching for a node in a binary search tree](binary_search_tree.assets/bst_search_step1.png){ class="animation-figure" }
=== "<2>"
![bst_search_step2](binary_search_tree.assets/bst_search_step2.png){ class="animation-figure" }
=== "<3>"
![bst_search_step3](binary_search_tree.assets/bst_search_step3.png){ class="animation-figure" }
=== "<4>"
![bst_search_step4](binary_search_tree.assets/bst_search_step4.png){ class="animation-figure" }
<p align="center"> Figure 7-17 &nbsp; Example of searching for a node in a binary search tree </p>
2024-09-28 09:26:54 +08:00
The search operation in a binary search tree works on the same principle as the binary search algorithm, eliminating half of the cases in each round. The number of loops is at most the height of the binary tree. When the binary tree is balanced, it uses $O(\log n)$ time. The example code is as follows:
2024-04-02 19:00:01 +08:00
=== "Python"
```python title="binary_search_tree.py"
def search(self, num: int) -> TreeNode | None:
2024-05-06 05:27:10 +08:00
"""Search node"""
2024-04-02 19:00:01 +08:00
cur = self._root
2024-05-06 05:27:10 +08:00
# Loop find, break after passing leaf nodes
2024-04-02 19:00:01 +08:00
while cur is not None:
2024-05-06 05:27:10 +08:00
# Target node is in cur's right subtree
2024-04-02 19:00:01 +08:00
if cur.val < num:
cur = cur.right
2024-05-06 05:27:10 +08:00
# Target node is in cur's left subtree
2024-04-02 19:00:01 +08:00
elif cur.val > num:
cur = cur.left
2024-05-06 05:27:10 +08:00
# Found target node, break loop
2024-04-02 19:00:01 +08:00
else:
break
return cur
```
=== "C++"
```cpp title="binary_search_tree.cpp"
2024-05-06 14:40:36 +08:00
/* Search node */
TreeNode *search(int num) {
TreeNode *cur = root;
// Loop find, break after passing leaf nodes
while (cur != nullptr) {
// Target node is in cur's right subtree
if (cur->val < num)
cur = cur->right;
// Target node is in cur's left subtree
else if (cur->val > num)
cur = cur->left;
// Found target node, break loop
else
break;
}
// Return target node
return cur;
}
2024-04-02 19:00:01 +08:00
```
=== "Java"
```java title="binary_search_tree.java"
2024-05-06 05:27:10 +08:00
/* Search node */
2024-04-02 19:00:01 +08:00
TreeNode search(int num) {
TreeNode cur = root;
2024-05-06 05:27:10 +08:00
// Loop find, break after passing leaf nodes
2024-04-02 19:00:01 +08:00
while (cur != null) {
2024-05-06 05:27:10 +08:00
// Target node is in cur's right subtree
2024-04-02 19:00:01 +08:00
if (cur.val < num)
cur = cur.right;
2024-05-06 05:27:10 +08:00
// Target node is in cur's left subtree
2024-04-02 19:00:01 +08:00
else if (cur.val > num)
cur = cur.left;
2024-05-06 05:27:10 +08:00
// Found target node, break loop
2024-04-02 19:00:01 +08:00
else
break;
}
2024-05-06 05:27:10 +08:00
// Return target node
2024-04-02 19:00:01 +08:00
return cur;
}
```
=== "C#"
```csharp title="binary_search_tree.cs"
2024-05-06 05:27:10 +08:00
[class]{BinarySearchTree}-[func]{Search}
2024-04-02 19:00:01 +08:00
```
=== "Go"
```go title="binary_search_tree.go"
2024-05-06 05:27:10 +08:00
[class]{binarySearchTree}-[func]{search}
2024-04-02 19:00:01 +08:00
```
=== "Swift"
```swift title="binary_search_tree.swift"
2024-05-06 05:27:10 +08:00
[class]{BinarySearchTree}-[func]{search}
2024-04-02 19:00:01 +08:00
```
=== "JS"
```javascript title="binary_search_tree.js"
2024-05-06 05:27:10 +08:00
[class]{BinarySearchTree}-[func]{search}
2024-04-02 19:00:01 +08:00
```
=== "TS"
```typescript title="binary_search_tree.ts"
2024-05-06 05:27:10 +08:00
[class]{BinarySearchTree}-[func]{search}
2024-04-02 19:00:01 +08:00
```
=== "Dart"
```dart title="binary_search_tree.dart"
2024-05-06 05:27:10 +08:00
[class]{BinarySearchTree}-[func]{search}
2024-04-02 19:00:01 +08:00
```
=== "Rust"
```rust title="binary_search_tree.rs"
2024-05-06 05:27:10 +08:00
[class]{BinarySearchTree}-[func]{search}
2024-04-02 19:00:01 +08:00
```
=== "C"
```c title="binary_search_tree.c"
2024-05-06 05:27:10 +08:00
[class]{BinarySearchTree}-[func]{search}
2024-04-02 19:00:01 +08:00
```
=== "Kotlin"
```kotlin title="binary_search_tree.kt"
2024-05-06 05:27:10 +08:00
[class]{BinarySearchTree}-[func]{search}
2024-04-02 19:00:01 +08:00
```
=== "Ruby"
```ruby title="binary_search_tree.rb"
2024-05-06 05:27:10 +08:00
[class]{BinarySearchTree}-[func]{search}
2024-04-02 19:00:01 +08:00
```
=== "Zig"
```zig title="binary_search_tree.zig"
2024-05-06 05:27:10 +08:00
[class]{BinarySearchTree}-[func]{search}
2024-04-02 19:00:01 +08:00
```
### 2. &nbsp; Inserting a node
2024-05-01 07:30:10 +08:00
Given an element `num` to be inserted, to maintain the property of the binary search tree "left subtree < root node < right subtree," the insertion operation proceeds as shown in Figure 7-18.
2024-04-02 19:00:01 +08:00
2024-09-28 09:26:54 +08:00
1. **Finding insertion position**: Similar to the search operation, start from the root node, loop downwards according to the size relationship between the current node value and `num`, until the leaf node is passed (traversed to `None`), then exit the loop.
2. **Insert the node at this position**: Initialize the node `num` and place it where `None` was.
2024-04-02 19:00:01 +08:00
![Inserting a node into a binary search tree](binary_search_tree.assets/bst_insert.png){ class="animation-figure" }
<p align="center"> Figure 7-18 &nbsp; Inserting a node into a binary search tree </p>
In the code implementation, note the following two points.
2024-09-28 09:26:54 +08:00
- The binary search tree does not allow duplicate nodes to exist; otherwise, its definition would be violated. Therefore, if the node to be inserted already exists in the tree, the insertion is not performed, and the node returns directly.
- To perform the insertion operation, we need to use the node `pre` to save the node from the previous loop. This way, when traversing to `None`, we can get its parent node, thus completing the node insertion operation.
2024-04-02 19:00:01 +08:00
=== "Python"
```python title="binary_search_tree.py"
def insert(self, num: int):
2024-05-06 05:27:10 +08:00
"""Insert node"""
# If tree is empty, initialize root node
2024-04-02 19:00:01 +08:00
if self._root is None:
self._root = TreeNode(num)
return
2024-05-06 05:27:10 +08:00
# Loop find, break after passing leaf nodes
2024-04-02 19:00:01 +08:00
cur, pre = self._root, None
while cur is not None:
2024-05-06 05:27:10 +08:00
# Found duplicate node, thus return
2024-04-02 19:00:01 +08:00
if cur.val == num:
return
pre = cur
2024-05-06 05:27:10 +08:00
# Insertion position is in cur's right subtree
2024-04-02 19:00:01 +08:00
if cur.val < num:
cur = cur.right
2024-05-06 05:27:10 +08:00
# Insertion position is in cur's left subtree
2024-04-02 19:00:01 +08:00
else:
cur = cur.left
2024-05-06 05:27:10 +08:00
# Insert node
2024-04-02 19:00:01 +08:00
node = TreeNode(num)
if pre.val < num:
pre.right = node
else:
pre.left = node
```
=== "C++"
```cpp title="binary_search_tree.cpp"
2024-05-06 14:40:36 +08:00
/* Insert node */
void insert(int num) {
// If tree is empty, initialize root node
if (root == nullptr) {
root = new TreeNode(num);
return;
}
TreeNode *cur = root, *pre = nullptr;
// Loop find, break after passing leaf nodes
while (cur != nullptr) {
// Found duplicate node, thus return
if (cur->val == num)
return;
pre = cur;
// Insertion position is in cur's right subtree
if (cur->val < num)
cur = cur->right;
// Insertion position is in cur's left subtree
else
cur = cur->left;
}
// Insert node
TreeNode *node = new TreeNode(num);
if (pre->val < num)
pre->right = node;
else
pre->left = node;
}
2024-04-02 19:00:01 +08:00
```
=== "Java"
```java title="binary_search_tree.java"
2024-05-06 05:27:10 +08:00
/* Insert node */
2024-04-02 19:00:01 +08:00
void insert(int num) {
2024-05-06 05:27:10 +08:00
// If tree is empty, initialize root node
2024-04-02 19:00:01 +08:00
if (root == null) {
root = new TreeNode(num);
return;
}
TreeNode cur = root, pre = null;
2024-05-06 05:27:10 +08:00
// Loop find, break after passing leaf nodes
2024-04-02 19:00:01 +08:00
while (cur != null) {
2024-05-06 05:27:10 +08:00
// Found duplicate node, thus return
2024-04-02 19:00:01 +08:00
if (cur.val == num)
return;
pre = cur;
2024-05-06 05:27:10 +08:00
// Insertion position is in cur's right subtree
2024-04-02 19:00:01 +08:00
if (cur.val < num)
cur = cur.right;
2024-05-06 05:27:10 +08:00
// Insertion position is in cur's left subtree
2024-04-02 19:00:01 +08:00
else
cur = cur.left;
}
2024-05-06 05:27:10 +08:00
// Insert node
2024-04-02 19:00:01 +08:00
TreeNode node = new TreeNode(num);
if (pre.val < num)
pre.right = node;
else
pre.left = node;
}
```
=== "C#"
```csharp title="binary_search_tree.cs"
2024-05-06 05:27:10 +08:00
[class]{BinarySearchTree}-[func]{Insert}
2024-04-02 19:00:01 +08:00
```
=== "Go"
```go title="binary_search_tree.go"
2024-05-06 05:27:10 +08:00
[class]{binarySearchTree}-[func]{insert}
2024-04-02 19:00:01 +08:00
```
=== "Swift"
```swift title="binary_search_tree.swift"
2024-05-06 05:27:10 +08:00
[class]{BinarySearchTree}-[func]{insert}
2024-04-02 19:00:01 +08:00
```
=== "JS"
```javascript title="binary_search_tree.js"
2024-05-06 05:27:10 +08:00
[class]{BinarySearchTree}-[func]{insert}
2024-04-02 19:00:01 +08:00
```
=== "TS"
```typescript title="binary_search_tree.ts"
2024-05-06 05:27:10 +08:00
[class]{BinarySearchTree}-[func]{insert}
2024-04-02 19:00:01 +08:00
```
=== "Dart"
```dart title="binary_search_tree.dart"
2024-05-06 05:27:10 +08:00
[class]{BinarySearchTree}-[func]{insert}
2024-04-02 19:00:01 +08:00
```
=== "Rust"
```rust title="binary_search_tree.rs"
2024-05-06 05:27:10 +08:00
[class]{BinarySearchTree}-[func]{insert}
2024-04-02 19:00:01 +08:00
```
=== "C"
```c title="binary_search_tree.c"
2024-05-06 05:27:10 +08:00
[class]{BinarySearchTree}-[func]{insert}
2024-04-02 19:00:01 +08:00
```
=== "Kotlin"
```kotlin title="binary_search_tree.kt"
2024-05-06 05:27:10 +08:00
[class]{BinarySearchTree}-[func]{insert}
2024-04-02 19:00:01 +08:00
```
=== "Ruby"
```ruby title="binary_search_tree.rb"
2024-05-06 05:27:10 +08:00
[class]{BinarySearchTree}-[func]{insert}
2024-04-02 19:00:01 +08:00
```
=== "Zig"
```zig title="binary_search_tree.zig"
2024-05-06 05:27:10 +08:00
[class]{BinarySearchTree}-[func]{insert}
2024-04-02 19:00:01 +08:00
```
Similar to searching for a node, inserting a node uses $O(\log n)$ time.
### 3. &nbsp; Removing a node
2024-09-28 09:26:54 +08:00
First, find the target node in the binary tree, then remove it. Similar to inserting a node, we need to ensure that after the removal operation is completed, the property of the binary search tree "left subtree < root node < right subtree" is still satisfied. Therefore, based on the number of child nodes of the target node, we divide it into three cases: 0, 1, and 2, and perform the corresponding node removal operations.
2024-04-02 19:00:01 +08:00
2024-09-28 09:26:54 +08:00
As shown in Figure 7-19, when the degree of the node to be removed is $0$, it means the node is a leaf node and can be directly removed.
2024-04-02 19:00:01 +08:00
![Removing a node in a binary search tree (degree 0)](binary_search_tree.assets/bst_remove_case1.png){ class="animation-figure" }
<p align="center"> Figure 7-19 &nbsp; Removing a node in a binary search tree (degree 0) </p>
2024-05-01 07:30:10 +08:00
As shown in Figure 7-20, when the degree of the node to be removed is $1$, replacing the node to be removed with its child node is sufficient.
2024-04-02 19:00:01 +08:00
![Removing a node in a binary search tree (degree 1)](binary_search_tree.assets/bst_remove_case2.png){ class="animation-figure" }
<p align="center"> Figure 7-20 &nbsp; Removing a node in a binary search tree (degree 1) </p>
2024-05-02 01:46:14 +08:00
When the degree of the node to be removed is $2$, we cannot remove it directly, but need to use a node to replace it. To maintain the property of the binary search tree "left subtree $<$ root node $<$ right subtree," **this node can be either the smallest node of the right subtree or the largest node of the left subtree**.
2024-04-02 19:00:01 +08:00
2024-05-01 07:30:10 +08:00
Assuming we choose the smallest node of the right subtree (the next node in in-order traversal), then the removal operation proceeds as shown in Figure 7-21.
2024-04-02 19:00:01 +08:00
1. Find the next node in the "in-order traversal sequence" of the node to be removed, denoted as `tmp`.
2. Replace the value of the node to be removed with `tmp`'s value, and recursively remove the node `tmp` in the tree.
=== "<1>"
![Removing a node in a binary search tree (degree 2)](binary_search_tree.assets/bst_remove_case3_step1.png){ class="animation-figure" }
=== "<2>"
![bst_remove_case3_step2](binary_search_tree.assets/bst_remove_case3_step2.png){ class="animation-figure" }
=== "<3>"
![bst_remove_case3_step3](binary_search_tree.assets/bst_remove_case3_step3.png){ class="animation-figure" }
=== "<4>"
![bst_remove_case3_step4](binary_search_tree.assets/bst_remove_case3_step4.png){ class="animation-figure" }
<p align="center"> Figure 7-21 &nbsp; Removing a node in a binary search tree (degree 2) </p>
The operation of removing a node also uses $O(\log n)$ time, where finding the node to be removed requires $O(\log n)$ time, and obtaining the in-order traversal successor node requires $O(\log n)$ time. Example code is as follows:
=== "Python"
```python title="binary_search_tree.py"
def remove(self, num: int):
2024-05-06 05:27:10 +08:00
"""Remove node"""
# If tree is empty, return
2024-04-02 19:00:01 +08:00
if self._root is None:
return
2024-05-06 05:27:10 +08:00
# Loop find, break after passing leaf nodes
2024-04-02 19:00:01 +08:00
cur, pre = self._root, None
while cur is not None:
2024-05-06 05:27:10 +08:00
# Found node to be removed, break loop
2024-04-02 19:00:01 +08:00
if cur.val == num:
break
pre = cur
2024-05-06 05:27:10 +08:00
# Node to be removed is in cur's right subtree
2024-04-02 19:00:01 +08:00
if cur.val < num:
cur = cur.right
2024-05-06 05:27:10 +08:00
# Node to be removed is in cur's left subtree
2024-04-02 19:00:01 +08:00
else:
cur = cur.left
2024-05-06 05:27:10 +08:00
# If no node to be removed, return
2024-04-02 19:00:01 +08:00
if cur is None:
return
2024-05-06 05:27:10 +08:00
# Number of child nodes = 0 or 1
2024-04-02 19:00:01 +08:00
if cur.left is None or cur.right is None:
2024-05-06 05:27:10 +08:00
# When the number of child nodes = 0/1, child = null/that child node
2024-04-02 19:00:01 +08:00
child = cur.left or cur.right
2024-05-06 05:27:10 +08:00
# Remove node cur
2024-04-02 19:00:01 +08:00
if cur != self._root:
if pre.left == cur:
pre.left = child
else:
pre.right = child
else:
2024-05-06 05:27:10 +08:00
# If the removed node is the root, reassign the root
2024-04-02 19:00:01 +08:00
self._root = child
2024-05-06 05:27:10 +08:00
# Number of child nodes = 2
2024-04-02 19:00:01 +08:00
else:
2024-05-06 05:27:10 +08:00
# Get the next node in in-order traversal of cur
2024-04-02 19:00:01 +08:00
tmp: TreeNode = cur.right
while tmp.left is not None:
tmp = tmp.left
2024-05-06 05:27:10 +08:00
# Recursively remove node tmp
2024-04-02 19:00:01 +08:00
self.remove(tmp.val)
2024-05-06 05:27:10 +08:00
# Replace cur with tmp
2024-04-02 19:00:01 +08:00
cur.val = tmp.val
```
=== "C++"
```cpp title="binary_search_tree.cpp"
2024-05-06 14:40:36 +08:00
/* Remove node */
void remove(int num) {
// If tree is empty, return
if (root == nullptr)
return;
TreeNode *cur = root, *pre = nullptr;
// Loop find, break after passing leaf nodes
while (cur != nullptr) {
// Found node to be removed, break loop
if (cur->val == num)
break;
pre = cur;
// Node to be removed is in cur's right subtree
if (cur->val < num)
cur = cur->right;
// Node to be removed is in cur's left subtree
else
cur = cur->left;
}
// If no node to be removed, return
if (cur == nullptr)
return;
// Number of child nodes = 0 or 1
if (cur->left == nullptr || cur->right == nullptr) {
// When the number of child nodes = 0 / 1, child = nullptr / that child node
TreeNode *child = cur->left != nullptr ? cur->left : cur->right;
// Remove node cur
if (cur != root) {
if (pre->left == cur)
pre->left = child;
else
pre->right = child;
} else {
// If the removed node is the root, reassign the root
root = child;
}
// Free memory
delete cur;
}
// Number of child nodes = 2
else {
// Get the next node in in-order traversal of cur
TreeNode *tmp = cur->right;
while (tmp->left != nullptr) {
tmp = tmp->left;
}
int tmpVal = tmp->val;
// Recursively remove node tmp
remove(tmp->val);
// Replace cur with tmp
cur->val = tmpVal;
}
}
2024-04-02 19:00:01 +08:00
```
=== "Java"
```java title="binary_search_tree.java"
2024-05-06 05:27:10 +08:00
/* Remove node */
2024-04-02 19:00:01 +08:00
void remove(int num) {
2024-05-06 05:27:10 +08:00
// If tree is empty, return
2024-04-02 19:00:01 +08:00
if (root == null)
return;
TreeNode cur = root, pre = null;
2024-05-06 05:27:10 +08:00
// Loop find, break after passing leaf nodes
2024-04-02 19:00:01 +08:00
while (cur != null) {
2024-05-06 05:27:10 +08:00
// Found node to be removed, break loop
2024-04-02 19:00:01 +08:00
if (cur.val == num)
break;
pre = cur;
2024-05-06 05:27:10 +08:00
// Node to be removed is in cur's right subtree
2024-04-02 19:00:01 +08:00
if (cur.val < num)
cur = cur.right;
2024-05-06 05:27:10 +08:00
// Node to be removed is in cur's left subtree
2024-04-02 19:00:01 +08:00
else
cur = cur.left;
}
2024-05-06 05:27:10 +08:00
// If no node to be removed, return
2024-04-02 19:00:01 +08:00
if (cur == null)
return;
2024-05-06 05:27:10 +08:00
// Number of child nodes = 0 or 1
2024-04-02 19:00:01 +08:00
if (cur.left == null || cur.right == null) {
2024-05-06 05:27:10 +08:00
// When the number of child nodes = 0/1, child = null/that child node
2024-04-02 19:00:01 +08:00
TreeNode child = cur.left != null ? cur.left : cur.right;
2024-05-06 05:27:10 +08:00
// Remove node cur
2024-04-02 19:00:01 +08:00
if (cur != root) {
if (pre.left == cur)
pre.left = child;
else
pre.right = child;
} else {
2024-05-06 05:27:10 +08:00
// If the removed node is the root, reassign the root
2024-04-02 19:00:01 +08:00
root = child;
}
}
2024-05-06 05:27:10 +08:00
// Number of child nodes = 2
2024-04-02 19:00:01 +08:00
else {
2024-05-06 05:27:10 +08:00
// Get the next node in in-order traversal of cur
2024-04-02 19:00:01 +08:00
TreeNode tmp = cur.right;
while (tmp.left != null) {
tmp = tmp.left;
}
2024-05-06 05:27:10 +08:00
// Recursively remove node tmp
2024-04-02 19:00:01 +08:00
remove(tmp.val);
2024-05-06 05:27:10 +08:00
// Replace cur with tmp
2024-04-02 19:00:01 +08:00
cur.val = tmp.val;
}
}
```
=== "C#"
```csharp title="binary_search_tree.cs"
2024-05-06 05:27:10 +08:00
[class]{BinarySearchTree}-[func]{Remove}
2024-04-02 19:00:01 +08:00
```
=== "Go"
```go title="binary_search_tree.go"
2024-05-06 05:27:10 +08:00
[class]{binarySearchTree}-[func]{remove}
2024-04-02 19:00:01 +08:00
```
=== "Swift"
```swift title="binary_search_tree.swift"
2024-05-06 05:27:10 +08:00
[class]{BinarySearchTree}-[func]{remove}
2024-04-02 19:00:01 +08:00
```
=== "JS"
```javascript title="binary_search_tree.js"
2024-05-06 05:27:10 +08:00
[class]{BinarySearchTree}-[func]{remove}
2024-04-02 19:00:01 +08:00
```
=== "TS"
```typescript title="binary_search_tree.ts"
2024-05-06 05:27:10 +08:00
[class]{BinarySearchTree}-[func]{remove}
2024-04-02 19:00:01 +08:00
```
=== "Dart"
```dart title="binary_search_tree.dart"
2024-05-06 05:27:10 +08:00
[class]{BinarySearchTree}-[func]{remove}
2024-04-02 19:00:01 +08:00
```
=== "Rust"
```rust title="binary_search_tree.rs"
2024-05-06 05:27:10 +08:00
[class]{BinarySearchTree}-[func]{remove}
2024-04-02 19:00:01 +08:00
```
=== "C"
```c title="binary_search_tree.c"
2024-05-06 05:27:10 +08:00
[class]{BinarySearchTree}-[func]{removeItem}
2024-04-02 19:00:01 +08:00
```
=== "Kotlin"
```kotlin title="binary_search_tree.kt"
2024-05-06 05:27:10 +08:00
[class]{BinarySearchTree}-[func]{remove}
2024-04-02 19:00:01 +08:00
```
=== "Ruby"
```ruby title="binary_search_tree.rb"
2024-05-06 05:27:10 +08:00
[class]{BinarySearchTree}-[func]{remove}
2024-04-02 19:00:01 +08:00
```
=== "Zig"
```zig title="binary_search_tree.zig"
2024-05-06 05:27:10 +08:00
[class]{BinarySearchTree}-[func]{remove}
2024-04-02 19:00:01 +08:00
```
### 4. &nbsp; In-order traversal is ordered
2024-09-28 09:26:54 +08:00
As shown in Figure 7-22, the in-order traversal of a binary tree follows the traversal order of "left $\rightarrow$ root $\rightarrow$ right," and a binary search tree satisfies the size relationship of "left child node $<$ root node $<$ right child node."
2024-04-02 19:00:01 +08:00
2024-09-28 09:26:54 +08:00
This means that when performing in-order traversal in a binary search tree, the next smallest node will always be traversed first, thus leading to an important property: **The sequence of in-order traversal in a binary search tree is ascending**.
2024-04-02 19:00:01 +08:00
Using the ascending property of in-order traversal, obtaining ordered data in a binary search tree requires only $O(n)$ time, without the need for additional sorting operations, which is very efficient.
![In-order traversal sequence of a binary search tree](binary_search_tree.assets/bst_inorder_traversal.png){ class="animation-figure" }
<p align="center"> Figure 7-22 &nbsp; In-order traversal sequence of a binary search tree </p>
## 7.4.2 &nbsp; Efficiency of binary search trees
2024-09-28 09:26:54 +08:00
Given a set of data, we consider using an array or a binary search tree for storage. Observing Table 7-2, the operations on a binary search tree all have logarithmic time complexity, which is stable and efficient. Arrays are more efficient than binary search trees only in scenarios involving frequent additions and infrequent searches or removals.
2024-04-02 19:00:01 +08:00
<p align="center"> Table 7-2 &nbsp; Efficiency comparison between arrays and search trees </p>
<div class="center-table" markdown>
| | Unsorted array | Binary search tree |
| -------------- | -------------- | ------------------ |
| Search element | $O(n)$ | $O(\log n)$ |
| Insert element | $O(1)$ | $O(\log n)$ |
| Remove element | $O(n)$ | $O(\log n)$ |
</div>
2024-09-28 09:26:54 +08:00
Ideally, the binary search tree is "balanced," allowing any node can be found within $\log n$ loops.
2024-04-02 19:00:01 +08:00
2024-09-28 09:26:54 +08:00
However, if we continuously insert and remove nodes in a binary search tree, it may degenerate into a linked list as shown in Figure 7-23, where the time complexity of various operations also degrades to $O(n)$.
2024-04-02 19:00:01 +08:00
![Degradation of a binary search tree](binary_search_tree.assets/bst_degradation.png){ class="animation-figure" }
<p align="center"> Figure 7-23 &nbsp; Degradation of a binary search tree </p>
## 7.4.3 &nbsp; Common applications of binary search trees
- Used as multi-level indexes in systems to implement efficient search, insertion, and removal operations.
- Serves as the underlying data structure for certain search algorithms.
- Used to store data streams to maintain their ordered state.