This commit is contained in:
krahets
2026-04-03 18:46:15 +08:00
parent 377736b1bd
commit 9d21ca86b0
352 changed files with 46563 additions and 11262 deletions
@@ -4,7 +4,7 @@ comments: true
# 7.3   Array Representation of Binary Trees
Under the linked list representation, the storage unit of a binary tree is a node `TreeNode`, and nodes are connected by pointers. The previous section introduced the basic operations of binary trees under the linked list representation.
In the linked-list representation, the storage unit of a binary tree is a node `TreeNode`, and nodes are connected by pointers. The previous section introduced the basic operations of binary trees in this representation.
So, can we use an array to represent a binary tree? The answer is yes.
@@ -30,7 +30,7 @@ As shown in Figure 7-13, given a non-perfect binary tree, the above method of ar
<p align="center"> Figure 7-13 &nbsp; Level-order traversal sequence corresponds to multiple binary tree possibilities </p>
To solve this problem, **we can consider explicitly writing out all `None` values in the level-order traversal sequence**. As shown in Figure 7-14, after this treatment, the level-order traversal sequence can uniquely represent a binary tree. Example code is as follows:
To solve this problem, **we can explicitly write out all `None` values in the level-order traversal sequence**. As shown in Figure 7-14, once we do this, the level-order traversal sequence can uniquely represent a binary tree. Example code is as follows:
=== "Python"
@@ -136,9 +136,9 @@ To solve this problem, **we can consider explicitly writing out all `None` value
tree = [1, 2, 3, 4, nil, 6, 7, 8, 9, nil, nil, 12, nil, nil, 15]
```
![Array representation of any type of binary tree](array_representation_of_tree.assets/array_representation_with_empty.png){ class="animation-figure" }
![Array representation of an arbitrary binary tree](array_representation_of_tree.assets/array_representation_with_empty.png){ class="animation-figure" }
<p align="center"> Figure 7-14 &nbsp; Array representation of any type of binary tree </p>
<p align="center"> Figure 7-14 &nbsp; Array representation of an arbitrary binary tree </p>
It's worth noting that **complete binary trees are very well-suited for array representation**. Recalling the definition of a complete binary tree, `None` only appears at the bottom level and towards the right, **meaning all `None` values must appear at the end of the level-order traversal sequence**.
@@ -148,9 +148,9 @@ This means that when using an array to represent a complete binary tree, it's po
<p align="center"> Figure 7-15 &nbsp; Array representation of a complete binary tree </p>
The following code implements a binary tree based on array representation, including the following operations:
The following code implements a binary tree using an array representation, including the following operations:
- Given a certain node, obtain its value, left (right) child node, and parent node.
- Given a node, obtain its value, left (right) child node, and parent node.
- Obtain the preorder, inorder, postorder, and level-order traversal sequences.
=== "Python"
+8 -8
View File
@@ -2,7 +2,7 @@
comments: true
---
# 7.5 &nbsp; Avl Tree *
# 7.5 &nbsp; AVL Tree *
In the "Binary Search Tree" section, we mentioned that after multiple insertion and removal operations, a binary search tree may degenerate into a linked list. In this case, the time complexity of all operations degrades from $O(\log n)$ to $O(n)$.
@@ -18,9 +18,9 @@ For example, in the perfect binary tree shown in Figure 7-25, after inserting tw
<p align="center"> Figure 7-25 &nbsp; Degradation of an AVL tree after inserting nodes </p>
In 1962, G. M. Adelson-Velsky and E. M. Landis proposed the <u>AVL tree</u> in their paper "An algorithm for the organization of information". The paper described in detail a series of operations ensuring that after continuously adding and removing nodes, the AVL tree does not degenerate, thus keeping the time complexity of various operations at the $O(\log n)$ level. In other words, in scenarios requiring frequent insertions, deletions, searches, and modifications, the AVL tree can always maintain efficient data operation performance, making it very valuable in applications.
In 1962, G. M. Adelson-Velsky and E. M. Landis proposed the <u>AVL tree</u> in their paper "An algorithm for the organization of information". The paper describes a series of operations that prevent an AVL tree from degenerating as nodes are inserted and removed, thereby keeping the time complexity of various operations at $O(\log n)$. In other words, in scenarios that require frequent insertion, deletion, lookup, and update operations, AVL trees can maintain consistently efficient performance and therefore have strong practical value.
## 7.5.1 &nbsp; Common Terminology in Avl Trees
## 7.5.1 &nbsp; Common Terminology in AVL Trees
An AVL tree is both a binary search tree and a balanced binary tree, simultaneously satisfying all the properties of these two types of binary trees, hence it is a <u>balanced binary search tree</u>.
@@ -236,7 +236,7 @@ Since the operations related to AVL trees require obtaining node heights, we nee
end
```
The "node height" refers to the distance from that node to its farthest leaf node, i.e., the number of "edges" passed. It is important to note that the height of a leaf node is $0$, and the height of a null node is $-1$. We will create two utility functions for getting and updating the height of a node:
The "node height" refers to the distance from that node to its farthest leaf node, i.e., the number of edges on the path. It is important to note that the height of a leaf node is $0$, and the height of a null node is $-1$. We will create two utility functions for getting and updating the height of a node:
=== "Python"
@@ -650,11 +650,11 @@ The <u>balance factor</u> of a node is defined as the height of the node's left
Let the balance factor be $f$, then the balance factor of any node in an AVL tree satisfies $-1 \le f \le 1$.
## 7.5.2 &nbsp; Rotations in Avl Trees
## 7.5.2 &nbsp; Rotations in AVL Trees
The characteristic of AVL trees lies in the "rotation" operation, which can restore balance to unbalanced nodes without affecting the inorder traversal sequence of the binary tree. In other words, **rotation operations can both maintain the property of a "binary search tree" and make the tree return to a "balanced binary tree"**.
We call nodes with a balance factor absolute value $> 1$ "unbalanced nodes". Depending on the imbalance situation, rotation operations are divided into four types: right rotation, left rotation, left rotation then right rotation, and right rotation then left rotation. Below we describe these rotation operations in detail.
We call nodes with a balance factor absolute value $> 1$ "unbalanced nodes". Depending on the imbalance situation, rotation operations are divided into four types: right rotation, left rotation, right rotation then left rotation, and left rotation then right rotation. Below we describe these rotation operations in detail.
### 1. &nbsp; Right Rotation
@@ -1659,7 +1659,7 @@ For ease of use, we encapsulate the rotation operations into a function. **With
end
```
## 7.5.3 &nbsp; Common Operations in Avl Trees
## 7.5.3 &nbsp; Common Operations in AVL Trees
### 1. &nbsp; Node Insertion
@@ -2642,7 +2642,7 @@ Similarly, on the basis of the binary search tree's node removal method, rotatio
The node search operation in AVL trees is consistent with that in binary search trees, and will not be elaborated here.
## 7.5.4 &nbsp; Typical Applications of Avl Trees
## 7.5.4 &nbsp; Typical Applications of AVL Trees
- Organizing and storing large-scale data, suitable for scenarios with high-frequency searches and low-frequency insertions and deletions.
- Used to build index systems in databases.
+7 -7
View File
@@ -19,7 +19,7 @@ We encapsulate the binary search tree as a class `BinarySearchTree` and declare
### 1. &nbsp; 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 Figure 7-17, 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`.
Given a target node value `num`, we can search according to the properties of the binary search tree. As shown in Figure 7-17, we declare a node `cur` and start from the binary search tree's root node `root`, looping to compare `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`.
@@ -39,7 +39,7 @@ Given a target node value `num`, we can search according to the properties of th
<p align="center"> Figure 7-17 &nbsp; Example of searching for a node in a binary search tree </p>
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:
The search operation in a binary search tree follows the same principle as binary search: each round rules out half of the remaining cases. The number of loop iterations is at most the height of the tree. When the tree is balanced, the search takes $O(\log n)$ time. The example code is as follows:
=== "Python"
@@ -343,7 +343,7 @@ The search operation in a binary search tree works on the same principle as the
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 Figure 7-18.
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.
2. **Insert the node at that position**: Create a node for `num` and place it at the `None` position.
![Inserting a node into a binary search tree](binary_search_tree.assets/bst_insert.png){ class="animation-figure" }
@@ -351,7 +351,7 @@ Given an element `num` to be inserted, in order to maintain the property of the
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.
- Binary search trees do not allow duplicate nodes; otherwise, the tree would no longer satisfy its definition. Therefore, if the node to be inserted already exists in the tree, the insertion is skipped and the function 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.
=== "Python"
@@ -801,7 +801,7 @@ Similar to searching for a node, inserting a node uses $O(\log n)$ time.
### 3. &nbsp; 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.
First, find the target node in the binary search 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 consider three cases: degree $0$, degree $1$, and degree $2$, and perform the corresponding removal operation.
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.
@@ -817,7 +817,7 @@ As shown in Figure 7-20, when the degree of the node to be removed is $1$, repla
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 Figure 7-21.
Assuming we choose the smallest node in the right subtree, that is, the inorder successor, the removal process is as shown in Figure 7-21.
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.
@@ -1606,7 +1606,7 @@ Given a set of data, we consider using an array or a binary search tree for stor
</div>
In the ideal case, a binary search tree is "balanced," such that any node can be found within $\log n$ loop iterations.
In the ideal case, a binary search tree is balanced, so any node can be found within $O(\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 Figure 7-23, where the time complexity of various operations also degrades to $O(n)$.
+7 -7
View File
@@ -4,7 +4,7 @@ comments: true
# 7.1 &nbsp; Binary Tree
A <u>binary tree</u> is a non-linear data structure that represents the derivation relationship between "ancestors" and "descendants" and embodies the divide-and-conquer logic of "one divides into two". Similar to a linked list, the basic unit of a binary tree is a node, and each node contains a value, a reference to its left child node, and a reference to its right child node.
A <u>binary tree</u> is a non-linear data structure that models the hierarchical relationship between "ancestors" and "descendants" and embodies a divide-and-conquer pattern in which each split branches into two. Similar to a linked list, the basic unit of a binary tree is a node, and each node contains a value, a reference to its left child node, and a reference to its right child node.
=== "Python"
@@ -207,7 +207,7 @@ A <u>binary tree</u> is a non-linear data structure that represents the derivati
Each node has two references (pointers), pointing respectively to the <u>left-child node</u> and <u>right-child node</u>. This node is called the <u>parent node</u> of these two child nodes. When given a node of a binary tree, we call the tree formed by this node's left child and all nodes below it the <u>left subtree</u> of this node. Similarly, the <u>right subtree</u> can be defined.
**In a binary tree, except leaf nodes, all other nodes contain child nodes and non-empty subtrees.** As shown in Figure 7-1, if "Node 2" is regarded as a parent node, its left and right child nodes are "Node 4" and "Node 5" respectively. The left subtree is formed by "Node 4" and all nodes beneath it, while the right subtree is formed by "Node 5" and all nodes beneath it.
**In a binary tree, every non-leaf node has child nodes and therefore non-empty subtrees.** As shown in Figure 7-1, if "Node 2" is regarded as a parent node, its left and right child nodes are "Node 4" and "Node 5" respectively. The left subtree is formed by "Node 4" and all nodes beneath it, while the right subtree is formed by "Node 5" and all nodes beneath it.
![Parent Node, child Node, subtree](binary_tree.assets/binary_tree_definition.png){ class="animation-figure" }
@@ -232,7 +232,7 @@ The commonly used terminology of binary trees is shown in Figure 7-2.
!!! tip
Please note that we usually define "height" and "depth" as "the number of edges traversed", but some questions or textbooks may define them as "the number of nodes traversed". In this case, both height and depth need to be incremented by 1.
We usually define "height" and "depth" as the number of edges traversed, but some textbooks and problem statements define them as the number of nodes on the path. In that case, both values are larger by 1.
## 7.1.2 &nbsp; Basic Operations of Binary Trees
@@ -629,13 +629,13 @@ Similar to a linked list, inserting and removing nodes in a binary tree can be a
!!! tip
It should be noted that inserting nodes may change the original logical structure of the binary tree, while removing nodes typically involves removing the node and all its subtrees. Therefore, in a binary tree, insertion and removal are usually performed through a set of operations to achieve meaningful outcomes.
Keep in mind that inserting a node can alter the original logical structure of a binary tree, while deleting a node usually entails removing that node together with its entire subtree. In practice, insertion and deletion in binary trees are therefore typically implemented as coordinated sequences of operations to achieve a meaningful result.
## 7.1.3 &nbsp; Common Types of Binary Trees
### 1. &nbsp; Perfect Binary Tree
As shown in Figure 7-4, a <u>perfect binary tree</u> has all levels completely filled with nodes. In a perfect binary tree, leaf nodes have a degree of $0$, while all other nodes have a degree of $2$. If the tree height is $h$, the total number of nodes is $2^{h+1} - 1$, exhibiting a standard exponential relationship that reflects the common phenomenon of cell division in nature.
As shown in Figure 7-4, a <u>perfect binary tree</u> has every level completely filled. In a perfect binary tree, leaf nodes have a degree of $0$, while all other nodes have a degree of $2$. If the tree height is $h$, the total number of nodes is $2^{h+1} - 1$, following a standard exponential pattern that mirrors the common phenomenon of cell division in nature.
!!! tip
@@ -671,9 +671,9 @@ As shown in Figure 7-7, in a <u>balanced binary tree</u>, the absolute differenc
## 7.1.4 &nbsp; Degeneration of Binary Trees
Figure 7-8 shows the ideal and degenerate structures of binary trees. When every level of a binary tree is filled, it reaches the "perfect binary tree" state; when all nodes are biased toward one side, the binary tree degenerates into a "linked list".
Figure 7-8 contrasts the ideal and degenerate structures of binary trees. When every level is filled, the tree becomes a "perfect binary tree"; when all nodes skew to one side, the binary tree degenerates into a "linked list".
- A perfect binary tree is the ideal case, fully leveraging the "divide and conquer" advantage of binary trees.
- A perfect binary tree is the ideal case, fully leveraging the divide-and-conquer advantages of binary trees.
- A linked list represents the other extreme, where all operations become linear operations with time complexity degrading to $O(n)$.
![The Best and Worst Structures of Binary Trees](binary_tree.assets/binary_tree_best_worst_cases.png){ class="animation-figure" }
@@ -12,7 +12,7 @@ The common traversal methods for binary trees include level-order traversal, pre
As shown in Figure 7-9, <u>level-order traversal</u> traverses the binary tree from top to bottom, layer by layer. Within each level, it visits nodes from left to right.
Level-order traversal is essentially <u>breadth-first traversal</u>, also known as <u>breadth-first search (BFS)</u>, which embodies a "expanding outward circle by circle" layer-by-layer traversal method.
Level-order traversal is essentially <u>breadth-first traversal</u>, also known as <u>breadth-first search (BFS)</u>, which proceeds outward level by level.
![Level-order traversal of a binary tree](binary_tree_traversal.assets/binary_tree_bfs.png){ class="animation-figure" }
@@ -341,7 +341,7 @@ Breadth-first traversal is typically implemented with the help of a "queue". The
## 7.2.2 &nbsp; Preorder, Inorder, and Postorder Traversal
Correspondingly, preorder, inorder, and postorder traversals all belong to <u>depth-first traversal</u>, also known as <u>depth-first search (DFS)</u>, which embodies a "first go to the end, then backtrack and continue" traversal method.
Correspondingly, preorder, inorder, and postorder traversals all belong to <u>depth-first traversal</u>, also known as <u>depth-first search (DFS)</u>, which goes as deep as possible before backtracking.
Figure 7-10 shows how depth-first traversal works on a binary tree. **Depth-first traversal is like "walking" around the perimeter of the entire binary tree**, encountering three positions at each node, corresponding to preorder, inorder, and postorder traversal.
@@ -816,12 +816,12 @@ Depth-first search is usually implemented based on recursion:
!!! tip
Depth-first search can also be implemented based on iteration, interested readers can study this on their own.
Depth-first search can also be implemented iteratively, and interested readers can explore this on their own.
Figure 7-11 shows the recursive process of preorder traversal of a binary tree, which can be divided into two opposite parts: "recursion" and "return".
Figure 7-11 shows the recursive process of preorder traversal of a binary tree, which can be divided into two opposite phases: "descending" and "returning".
1. "Recursion" means opening a new method, where the program accesses the next node in this process.
2. "Return" means the function returns, indicating that the current node has been fully visited.
1. "Descending" means making a new recursive call, during which the program visits the next node.
2. "Returning" means the function call returns, indicating that the current node has been fully processed.
=== "<1>"
![The recursive process of preorder traversal](binary_tree_traversal.assets/preorder_step1.png){ class="animation-figure" }
+4 -3
View File
@@ -9,14 +9,15 @@ icon: material/graph-outline
!!! abstract
Towering trees are full of vitality, with deep roots and lush leaves, spreading branches and flourishing.
Towering trees are full of vitality, with deep roots, lush foliage, and sprawling branches.
They offer a vivid illustration of divide-and-conquer in data structures.
They show us the vivid form of divide and conquer in data.
## Chapter contents
- [7.1 &nbsp; Binary Tree](binary_tree.md)
- [7.2 &nbsp; Binary Tree Traversal](binary_tree_traversal.md)
- [7.3 &nbsp; Array Representation of Tree](array_representation_of_tree.md)
- [7.3 &nbsp; Array Representation of Binary Trees](array_representation_of_tree.md)
- [7.4 &nbsp; Binary Search Tree](binary_search_tree.md)
- [7.5 &nbsp; AVL Tree *](avl_tree.md)
- [7.6 &nbsp; Summary](summary.md)
+6 -6
View File
@@ -6,23 +6,23 @@ comments: true
### 1. &nbsp; Key Review
- A binary tree is a non-linear data structure that embodies the divide-and-conquer logic of "one divides into two". Each binary tree node contains a value and two pointers, which respectively point to its left and right child nodes.
- A binary tree is a non-linear data structure that embodies the divide-and-conquer logic of splitting into two. Each binary tree node contains a value and two pointers, which point to its left and right child nodes.
- For a certain node in a binary tree, the tree formed by its left (right) child node and all nodes below is called the left (right) subtree of that node.
- Related terminology of binary trees includes root node, leaf node, level, degree, edge, height, and depth.
- The initialization, node insertion, and node removal operations of binary trees are similar to those of linked lists.
- Common types of binary trees include perfect binary trees, complete binary trees, full binary trees, and balanced binary trees. The perfect binary tree is the ideal state, while the linked list is the worst state after degradation.
- Common types of binary trees include perfect binary trees, complete binary trees, full binary trees, and balanced binary trees. A perfect binary tree is the ideal form, while a linked list represents the worst degenerate case.
- A binary tree can be represented using an array by arranging node values and empty slots in level-order traversal sequence, and implementing pointers based on the index mapping relationship between parent and child nodes.
- Level-order traversal of a binary tree is a breadth-first search method, embodying a layer-by-layer traversal approach of "expanding outward circle by circle", typically implemented using a queue.
- Preorder, inorder, and postorder traversals all belong to depth-first search, embodying a traversal approach of "first go to the end, then backtrack and continue", typically implemented using recursion.
- Level-order traversal of a binary tree is a breadth-first search method that proceeds level by level, typically implemented using a queue.
- Preorder, inorder, and postorder traversals all belong to depth-first search, which proceeds by going as deep as possible before backtracking, typically using recursion.
- A binary search tree is an efficient data structure for element searching, with search, insertion, and removal operations all having time complexity of $O(\log n)$. When a binary search tree degenerates into a linked list, all time complexities degrade to $O(n)$.
- An AVL tree, also known as a balanced binary search tree, ensures the tree remains balanced after continuous node insertions and removals through rotation operations.
- Rotation operations in AVL trees include right rotation, left rotation, left rotation then right rotation, and right rotation then left rotation. After inserting or removing nodes, AVL trees perform rotation operations from bottom to top to restore the tree to balance.
- Rotation operations in AVL trees include right rotation, left rotation, right rotation followed by left rotation, and left rotation followed by right rotation. After inserting or removing nodes, AVL trees perform rotations from bottom to top to restore balance.
### 2. &nbsp; Q & A
**Q**: For a binary tree with only one node, are both the height of the tree and the depth of the root node $0$?
Yes, because height and depth are typically defined as "the number of edges passed."
Yes, because height and depth are typically defined as the number of edges on the path.
**Q**: The insertion and removal in a binary tree are generally accomplished by a set of operations. What does "a set of operations" refer to here? Does it imply releasing the resources of the child nodes?