Files
hello-algo/en/docs/chapter_tree/binary_search_tree.md
Yudong Jin 2778a6f9c7 Translate all code to English (#1836)
* Review the EN heading format.

* Fix pythontutor headings.

* Fix pythontutor headings.

* bug fixes

* Fix headings in **/summary.md

* Revisit the CN-to-EN translation for Python code using Claude-4.5

* Revisit the CN-to-EN translation for Java code using Claude-4.5

* Revisit the CN-to-EN translation for Cpp code using Claude-4.5.

* Fix the dictionary.

* Fix cpp code translation for the multipart strings.

* Translate Go code to English.

* Update workflows to test EN code.

* Add EN translation for C.

* Add EN translation for CSharp.

* Add EN translation for Swift.

* Trigger the CI check.

* Revert.

* Update en/hash_map.md

* Add the EN version of Dart code.

* Add the EN version of Kotlin code.

* Add missing code files.

* Add the EN version of JavaScript code.

* Add the EN version of TypeScript code.

* Fix the workflows.

* Add the EN version of Ruby code.

* Add the EN version of Rust code.

* Update the CI check for the English version  code.

* Update Python CI check.

* Fix cmakelists for en/C code.

* Fix Ruby comments
2025-12-31 07:44:52 +08:00

7.9 KiB
Executable File

Binary Search Tree

As shown in the figure below, a binary search tree satisfies the following conditions.

  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.
  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

Operations on a Binary Search Tree

We encapsulate the binary search tree as a class BinarySearchTree and declare a member variable root pointing to the tree's root node.

Searching for a Node

Given a target node value num, we can search according to the properties of the binary search tree. As shown in the figure below, we declare a node cur and start from the binary tree's root node root, looping to compare the node value cur.val with num.

  • 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.
  • If cur.val = num, it means the target node is found, exit the loop, and return the node.

=== "<1>" Example of searching for a node in a binary search tree

=== "<2>" bst_search_step2

=== "<3>" bst_search_step3

=== "<4>" bst_search_step4

The search operation in a binary search tree works on the same principle as the binary search algorithm, both eliminating half of the cases in each round. The number of loop iterations 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:

[file]{binary_search_tree}-[class]{binary_search_tree}-[func]{search}

Inserting a Node

Given an element num to be inserted, in order to maintain the property of the binary search tree "left subtree < root node < right subtree," the insertion process is as shown in the figure below.

  1. Finding the insertion position: Similar to the search operation, start from the root node and loop downward searching according to the size relationship between the current node value and num, until passing the leaf node (traversing to None) and then exit the loop.
  2. Insert the node at that position: Initialize node num and place it at the None position.

Inserting a node into a binary search tree

In the code implementation, note the following two points:

  • Binary search trees do not allow duplicate nodes; otherwise, it would violate its definition. Therefore, if the node to be inserted already exists in the tree, the insertion is not performed and it returns directly.
  • To implement the node insertion, we need to use node pre to save the node from the previous loop iteration. This way, when traversing to None, we can obtain its parent node, thereby completing the node insertion operation.
[file]{binary_search_tree}-[class]{binary_search_tree}-[func]{insert}

Similar to searching for a node, inserting a node uses O(\log n) time.

Removing a Node

First, find the target node in the binary tree, then remove it. Similar to node insertion, we need to ensure that after the removal operation is completed, the binary search tree's property of "left subtree < root node < right subtree" is still maintained. Therefore, depending on the number of child nodes the target node has, we divide it into 0, 1, and 2 three cases, and execute the corresponding node removal operations.

As shown in the figure below, when the degree of the node to be removed is 0, it means the node is a leaf node and can be directly removed.

Removing a node in a binary search tree (degree 0)

As shown in the figure below, when the degree of the node to be removed is 1, replacing the node to be removed with its child node is sufficient.

Removing a node in a binary search tree (degree 1)

When the degree of the node to be removed is 2, we cannot directly remove it; instead, we need to use a node to replace it. To maintain the binary search tree's property of "left subtree < root node < right subtree," this node can be either the smallest node in the right subtree or the largest node in the left subtree.

Assuming we choose the smallest node in the right subtree (the next node in the inorder traversal), the removal process is as shown in the figure below.

  1. Find the next node of the node to be removed in the "inorder traversal sequence," denoted as tmp.
  2. Replace the value of the node to be removed with the value of tmp, and recursively remove node tmp in the tree.

=== "<1>" Removing a node in a binary search tree (degree 2)

=== "<2>" bst_remove_case3_step2

=== "<3>" bst_remove_case3_step3

=== "<4>" bst_remove_case3_step4

The node removal operation also uses O(\log n) time, where finding the node to be removed requires O(\log n) time, and obtaining the inorder successor node requires O(\log n) time. Example code is as follows:

[file]{binary_search_tree}-[class]{binary_search_tree}-[func]{remove}

Inorder Traversal Is Ordered

As shown in the figure below, the inorder traversal of a binary tree follows the "left \rightarrow root \rightarrow right" traversal order, while the binary search tree satisfies the "left child node < root node < right child node" size relationship.

This means that when performing an inorder traversal in a binary search tree, the next smallest node is always traversed first, thus yielding an important property: The inorder traversal sequence of a binary search tree is ascending.

Using the property of inorder traversal being ascending, we can obtain ordered data in a binary search tree in only O(n) time, without the need for additional sorting operations, which is very efficient.

Inorder traversal sequence of a binary search tree

Efficiency of Binary Search Trees

Given a set of data, we consider using an array or a binary search tree for storage. Observing the table below, all operations in a binary search tree have logarithmic time complexity, providing stable and efficient performance. Arrays are more efficient than binary search trees only in scenarios with high-frequency additions and low-frequency searches and deletions.

Table   Efficiency comparison between arrays and search trees

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)

In the ideal case, a binary search tree is "balanced," such that any node can be found within \log n loop iterations.

However, if we continuously insert and remove nodes in a binary search tree, it may degenerate into a linked list as shown in the figure below, where the time complexity of various operations also degrades to O(n).

Degradation of a binary search tree

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.