mirror of
https://github.com/krahets/hello-algo.git
synced 2026-07-14 08:06:06 +00:00
build
This commit is contained in:
@@ -12,7 +12,7 @@ So, can we use an array to represent a binary tree? The answer is yes.
|
||||
|
||||
Let's analyze a simple case first. Given a perfect binary tree, we store all nodes in an array according to the order of level-order traversal, where each node corresponds to a unique array index.
|
||||
|
||||
Based on the characteristics of level-order traversal, we can deduce a "mapping formula" between the index of a parent node and its children: **If a node's index is $i$, then the index of its left child is $2i + 1$ and the right child is $2i + 2$**. The Figure 7-12 shows the mapping relationship between the indices of various nodes.
|
||||
Based on the characteristics of level-order traversal, we can deduce a "mapping formula" between the index of a parent node and its children: **If a node's index is $i$, then the index of its left child is $2i + 1$ and the right child is $2i + 2$**. Figure 7-12 shows the mapping relationship between the indices of various nodes.
|
||||
|
||||
{ class="animation-figure" }
|
||||
|
||||
@@ -24,13 +24,13 @@ Based on the characteristics of level-order traversal, we can deduce a "mapping
|
||||
|
||||
Perfect binary trees are a special case; there are often many `None` values in the middle levels of a binary tree. Since the sequence of level-order traversal does not include these `None` values, we cannot solely rely on this sequence to deduce the number and distribution of `None` values. **This means that multiple binary tree structures can match the same level-order traversal sequence**.
|
||||
|
||||
As shown in the Figure 7-13 , given a non-perfect binary tree, the above method of array representation fails.
|
||||
As shown in Figure 7-13, given a non-perfect binary tree, the above method of array representation fails.
|
||||
|
||||
{ class="animation-figure" }
|
||||
|
||||
<p align="center"> Figure 7-13 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 the following figure, 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 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:
|
||||
|
||||
=== "Python"
|
||||
|
||||
@@ -146,7 +146,7 @@ To solve this problem, **we can consider explicitly writing out all `None` value
|
||||
|
||||
It's worth noting that **complete binary trees are very suitable for array representation**. Recalling the definition of a complete binary tree, `None` appears only at the bottom level and towards the right, **meaning all `None` values definitely appear at the end of the level-order traversal sequence**.
|
||||
|
||||
This means that when using an array to represent a complete binary tree, it's possible to omit storing all `None` values, which is very convenient. The Figure 7-15 gives an example.
|
||||
This means that when using an array to represent a complete binary tree, it's possible to omit storing all `None` values, which is very convenient. Figure 7-15 gives an example.
|
||||
|
||||
{ class="animation-figure" }
|
||||
|
||||
|
||||
@@ -6,13 +6,13 @@ comments: true
|
||||
|
||||
In the "Binary Search Tree" section, we mentioned that after multiple insertions and removals, a binary search tree might degrade to a linked list. In such cases, the time complexity of all operations degrades from $O(\log n)$ to $O(n)$.
|
||||
|
||||
As shown in the Figure 7-24 , after two node removal operations, this binary search tree will degrade into a linked list.
|
||||
As shown in Figure 7-24, after two node removal operations, this binary search tree will degrade into a linked list.
|
||||
|
||||
{ class="animation-figure" }
|
||||
|
||||
<p align="center"> Figure 7-24 Degradation of an AVL tree after removing nodes </p>
|
||||
|
||||
For example, in the perfect binary tree shown in the Figure 7-25 , after inserting two nodes, the tree will lean heavily to the left, and the time complexity of search operations will also degrade.
|
||||
For example, in the perfect binary tree shown in Figure 7-25, after inserting two nodes, the tree will lean heavily to the left, and the time complexity of search operations will also degrade.
|
||||
|
||||
{ class="animation-figure" }
|
||||
|
||||
@@ -682,7 +682,7 @@ We call nodes with an absolute balance factor $> 1$ "unbalanced nodes". Dependin
|
||||
|
||||
### 1. Right rotation
|
||||
|
||||
As shown in the Figure 7-26 , the first unbalanced node from the bottom up in the binary tree is "node 3". Focusing on the subtree with this unbalanced node as the root, denoted as `node`, and its left child as `child`, perform a "right rotation". After the right rotation, the subtree is balanced again while still maintaining the properties of a binary search tree.
|
||||
As shown in Figure 7-26, the first unbalanced node from the bottom up in the binary tree is "node 3". Focusing on the subtree with this unbalanced node as the root, denoted as `node`, and its left child as `child`, perform a "right rotation". After the right rotation, the subtree is balanced again while still maintaining the properties of a binary search tree.
|
||||
|
||||
=== "<1>"
|
||||
{ class="animation-figure" }
|
||||
@@ -698,7 +698,7 @@ As shown in the Figure 7-26 , the first unbalanced node from the bottom up in th
|
||||
|
||||
<p align="center"> Figure 7-26 Steps of right rotation </p>
|
||||
|
||||
As shown in the Figure 7-27 , when the `child` node has a right child (denoted as `grand_child`), a step needs to be added in the right rotation: set `grand_child` as the left child of `node`.
|
||||
As shown in Figure 7-27, when the `child` node has a right child (denoted as `grand_child`), a step needs to be added in the right rotation: set `grand_child` as the left child of `node`.
|
||||
|
||||
{ class="animation-figure" }
|
||||
|
||||
@@ -965,13 +965,13 @@ As shown in the Figure 7-27 , when the `child` node has a right child (denoted a
|
||||
|
||||
### 2. Left rotation
|
||||
|
||||
Correspondingly, if considering the "mirror" of the above unbalanced binary tree, the "left rotation" operation shown in the Figure 7-28 needs to be performed.
|
||||
Correspondingly, if considering the "mirror" of the above unbalanced binary tree, the "left rotation" operation shown in Figure 7-28 needs to be performed.
|
||||
|
||||
{ class="animation-figure" }
|
||||
|
||||
<p align="center"> Figure 7-28 Left rotation operation </p>
|
||||
|
||||
Similarly, as shown in the Figure 7-29 , when the `child` node has a left child (denoted as `grand_child`), a step needs to be added in the left rotation: set `grand_child` as the right child of `node`.
|
||||
Similarly, as shown in Figure 7-29, when the `child` node has a left child (denoted as `grand_child`), a step needs to be added in the left rotation: set `grand_child` as the right child of `node`.
|
||||
|
||||
{ class="animation-figure" }
|
||||
|
||||
@@ -1238,7 +1238,7 @@ It can be observed that **the right and left rotation operations are logically s
|
||||
|
||||
### 3. Right-left rotation
|
||||
|
||||
For the unbalanced node 3 shown in the Figure 7-30 , using either left or right rotation alone cannot restore balance to the subtree. In this case, a "left rotation" needs to be performed on `child` first, followed by a "right rotation" on `node`.
|
||||
For the unbalanced node 3 shown in Figure 7-30, using either left or right rotation alone cannot restore balance to the subtree. In this case, a "left rotation" needs to be performed on `child` first, followed by a "right rotation" on `node`.
|
||||
|
||||
{ class="animation-figure" }
|
||||
|
||||
@@ -1246,7 +1246,7 @@ For the unbalanced node 3 shown in the Figure 7-30 , using either left or right
|
||||
|
||||
### 4. Left-right rotation
|
||||
|
||||
As shown in the Figure 7-31 , for the mirror case of the above unbalanced binary tree, a "right rotation" needs to be performed on `child` first, followed by a "left rotation" on `node`.
|
||||
As shown in Figure 7-31, for the mirror case of the above unbalanced binary tree, a "right rotation" needs to be performed on `child` first, followed by a "left rotation" on `node`.
|
||||
|
||||
{ class="animation-figure" }
|
||||
|
||||
@@ -1254,13 +1254,13 @@ As shown in the Figure 7-31 , for the mirror case of the above unbalanced binary
|
||||
|
||||
### 5. Choice of rotation
|
||||
|
||||
The four kinds of imbalances shown in the Figure 7-32 correspond to the cases described above, respectively requiring right rotation, left-right rotation, right-left rotation, and left rotation.
|
||||
The four kinds of imbalances shown in Figure 7-32 correspond to the cases described above, respectively requiring right rotation, left-right rotation, right-left rotation, and left rotation.
|
||||
|
||||
{ class="animation-figure" }
|
||||
|
||||
<p align="center"> Figure 7-32 The four rotation cases of AVL tree </p>
|
||||
|
||||
As shown in the Table 7-3 , we determine which of the above cases an unbalanced node belongs to by judging the sign of the balance factor of the unbalanced node and its higher-side child's balance factor.
|
||||
As shown in Table 7-3, we determine which of the above cases an unbalanced node belongs to by judging the sign of the balance factor of the unbalanced node and its higher-side child's balance factor.
|
||||
|
||||
<p align="center"> Table 7-3 Conditions for Choosing Among the Four Rotation Cases </p>
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ comments: true
|
||||
|
||||
# 7.4 Binary search tree
|
||||
|
||||
As shown in the Figure 7-16 , a "binary search tree" satisfies the following conditions.
|
||||
As shown in Figure 7-16, 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.
|
||||
@@ -19,7 +19,7 @@ We encapsulate the binary search tree as a class `BinarySearchTree` and declare
|
||||
|
||||
### 1. Searching for a node
|
||||
|
||||
Given a target node value `num`, one can search according to the properties of the binary search tree. As shown in the Figure 7-17 , we declare a node `cur` and start from the binary tree's root node `root`, looping to compare the size relationship between the node value `cur.val` and `num`.
|
||||
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` and start from the binary tree's root node `root`, looping to compare the size relationship between the node value `cur.val` and `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`.
|
||||
@@ -369,7 +369,7 @@ The search operation in a binary search tree works on the same principle as the
|
||||
|
||||
### 2. Inserting a node
|
||||
|
||||
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 the Figure 7-18 .
|
||||
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.
|
||||
|
||||
1. **Finding the insertion position**: Similar to the search operation, start from the root node and loop downwards according to the size relationship between the current node value and `num` until passing through the leaf node (traversing to `None`) then exit the loop.
|
||||
2. **Insert the node at that position**: Initialize the node `num` and place it where `None` was.
|
||||
@@ -873,13 +873,13 @@ Similar to searching for a node, inserting a node uses $O(\log n)$ time.
|
||||
|
||||
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 0, 1, and 2 cases, performing the corresponding node removal operations.
|
||||
|
||||
As shown in the Figure 7-19 , when the degree of the node to be removed is $0$, it means the node is a leaf node, and it can be directly removed.
|
||||
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 it can be directly removed.
|
||||
|
||||
{ class="animation-figure" }
|
||||
|
||||
<p align="center"> Figure 7-19 Removing a node in a binary search tree (degree 0) </p>
|
||||
|
||||
As shown in the 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.
|
||||
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.
|
||||
|
||||
{ class="animation-figure" }
|
||||
|
||||
@@ -887,7 +887,7 @@ As shown in the Figure 7-20 , when the degree of the node to be removed is $1$,
|
||||
|
||||
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**.
|
||||
|
||||
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 the Figure 7-21 .
|
||||
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.
|
||||
|
||||
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.
|
||||
@@ -1705,7 +1705,7 @@ The operation of removing a node also uses $O(\log n)$ time, where finding the n
|
||||
|
||||
### 4. In-order traversal is ordered
|
||||
|
||||
As shown in the Figure 7-22 , the in-order traversal of a binary tree follows the "left $\rightarrow$ root $\rightarrow$ right" traversal order, and a binary search tree satisfies the size relationship "left child node < root node < right child node".
|
||||
As shown in Figure 7-22, the in-order traversal of a binary tree follows the "left $\rightarrow$ root $\rightarrow$ right" traversal order, and a binary search tree satisfies the size relationship "left child node < root node < right child node".
|
||||
|
||||
This means that in-order traversal in a binary search tree always traverses the next smallest node first, thus deriving an important property: **The in-order traversal sequence of a binary search tree is ascending**.
|
||||
|
||||
@@ -1717,7 +1717,7 @@ Using the ascending property of in-order traversal, obtaining ordered data in a
|
||||
|
||||
## 7.4.2 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 7-2 , the operations on a binary search tree all have logarithmic time complexity, which is stable and efficient. Only in scenarios of high-frequency addition and low-frequency search and removal, arrays are more efficient than binary search trees.
|
||||
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. Only in scenarios of high-frequency addition and low-frequency search and removal, arrays are more efficient than binary search trees.
|
||||
|
||||
<p align="center"> Table 7-2 Efficiency comparison between arrays and search trees </p>
|
||||
|
||||
@@ -1733,7 +1733,7 @@ Given a set of data, we consider using an array or a binary search tree for stor
|
||||
|
||||
In ideal conditions, the binary search tree is "balanced," thus any node can be found within $\log n$ loops.
|
||||
|
||||
However, continuously inserting and removing nodes in a binary search tree may lead to the binary tree degenerating into a chain list as shown in the Figure 7-23 , at which point the time complexity of various operations also degrades to $O(n)$.
|
||||
However, continuously inserting and removing nodes in a binary search tree may lead to the binary tree degenerating into a chain list as shown in Figure 7-23, at which point the time complexity of various operations also degrades to $O(n)$.
|
||||
|
||||
{ class="animation-figure" }
|
||||
|
||||
|
||||
@@ -204,7 +204,7 @@ A "binary tree" is a non-linear data structure that represents the ancestral and
|
||||
|
||||
Each node has two references (pointers), pointing to the "left-child node" and "right-child node," respectively. This node is called the "parent node" 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 under it the "left subtree" of this node. Similarly, the "right subtree" can be defined.
|
||||
|
||||
**In a binary tree, except for leaf nodes, all other nodes contain child nodes and non-empty subtrees.** As shown in the Figure 7-1 , if "Node 2" is considered as the parent node, then its left and right child nodes are "Node 4" and "Node 5," respectively. The left subtree is "the tree formed by Node 4 and all nodes under it," and the right subtree is "the tree formed by Node 5 and all nodes under it."
|
||||
**In a binary tree, except for leaf nodes, all other nodes contain child nodes and non-empty subtrees.** As shown in Figure 7-1, if "Node 2" is considered as the parent node, then its left and right child nodes are "Node 4" and "Node 5," respectively. The left subtree is "the tree formed by Node 4 and all nodes under it," and the right subtree is "the tree formed by Node 5 and all nodes under it."
|
||||
|
||||
{ class="animation-figure" }
|
||||
|
||||
@@ -212,7 +212,7 @@ Each node has two references (pointers), pointing to the "left-child node" and "
|
||||
|
||||
## 7.1.1 Common terminology of binary trees
|
||||
|
||||
The commonly used terminology of binary trees is shown in the following figure.
|
||||
The commonly used terminology of binary trees is shown in Figure 7-2.
|
||||
|
||||
- "Root node": The node at the top level of the binary tree, which has no parent node.
|
||||
- "Leaf node": A node with no children, both of its pointers point to `None`.
|
||||
@@ -455,7 +455,7 @@ Similar to a linked list, initialize nodes first, then construct references (poi
|
||||
|
||||
### 2. Inserting and removing nodes
|
||||
|
||||
Similar to a linked list, inserting and removing nodes in a binary tree can be achieved by modifying pointers. The Figure 7-3 provides an example.
|
||||
Similar to a linked list, inserting and removing nodes in a binary tree can be achieved by modifying pointers. Figure 7-3 provides an example.
|
||||
|
||||
{ class="animation-figure" }
|
||||
|
||||
@@ -625,7 +625,7 @@ Similar to a linked list, inserting and removing nodes in a binary tree can be a
|
||||
|
||||
### 1. Perfect binary tree
|
||||
|
||||
As shown in the Figure 7-4 , in a "perfect binary tree," all levels of nodes are fully filled. In a perfect binary tree, the degree of leaf nodes is $0$, and the degree of all other nodes is $2$; if the tree's height is $h$, then the total number of nodes is $2^{h+1} - 1$, showing a standard exponential relationship, reflecting the common phenomenon of cell division in nature.
|
||||
As shown in Figure 7-4, in a "perfect binary tree," all levels of nodes are fully filled. In a perfect binary tree, the degree of leaf nodes is $0$, and the degree of all other nodes is $2$; if the tree's height is $h$, then the total number of nodes is $2^{h+1} - 1$, showing a standard exponential relationship, reflecting the common phenomenon of cell division in nature.
|
||||
|
||||
!!! tip
|
||||
|
||||
@@ -637,7 +637,7 @@ As shown in the Figure 7-4 , in a "perfect binary tree," all levels of nodes are
|
||||
|
||||
### 2. Complete binary tree
|
||||
|
||||
As shown in the Figure 7-5 , a "complete binary tree" has only the bottom level nodes not fully filled, and the bottom level nodes are filled as far left as possible.
|
||||
As shown in Figure 7-5, a "complete binary tree" has only the bottom level nodes not fully filled, and the bottom level nodes are filled as far left as possible.
|
||||
|
||||
{ class="animation-figure" }
|
||||
|
||||
@@ -645,7 +645,7 @@ As shown in the Figure 7-5 , a "complete binary tree" has only the bottom level
|
||||
|
||||
### 3. Full binary tree
|
||||
|
||||
As shown in the Figure 7-6 , a "full binary tree" has all nodes except leaf nodes having two children.
|
||||
As shown in Figure 7-6, a "full binary tree" has all nodes except leaf nodes having two children.
|
||||
|
||||
{ class="animation-figure" }
|
||||
|
||||
@@ -653,7 +653,7 @@ As shown in the Figure 7-6 , a "full binary tree" has all nodes except leaf node
|
||||
|
||||
### 4. Balanced binary tree
|
||||
|
||||
As shown in the Figure 7-7 , in a "balanced binary tree," the absolute difference in height between the left and right subtrees of any node does not exceed 1.
|
||||
As shown in Figure 7-7, in a "balanced binary tree," the absolute difference in height between the left and right subtrees of any node does not exceed 1.
|
||||
|
||||
{ class="animation-figure" }
|
||||
|
||||
@@ -661,7 +661,7 @@ As shown in the Figure 7-7 , in a "balanced binary tree," the absolute differenc
|
||||
|
||||
## 7.1.4 Degeneration of binary trees
|
||||
|
||||
The 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"; when all nodes are biased towards one side, the binary tree degenerates into a "linked list".
|
||||
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"; when all nodes are biased towards one side, the binary tree degenerates into a "linked list".
|
||||
|
||||
- The perfect binary tree is the ideal situation, fully leveraging the "divide and conquer" advantage of binary trees.
|
||||
- A linked list is another extreme, where operations become linear, degrading the time complexity to $O(n)$.
|
||||
@@ -670,7 +670,7 @@ The Figure 7-8 shows the ideal and degenerate structures of binary trees. When
|
||||
|
||||
<p align="center"> Figure 7-8 The Best and Worst Structures of Binary Trees </p>
|
||||
|
||||
As shown in the Table 7-1 , in the best and worst structures, the number of leaf nodes, total number of nodes, and height of the binary tree reach their maximum or minimum values.
|
||||
As shown in Table 7-1, in the best and worst structures, the number of leaf nodes, total number of nodes, and height of the binary tree reach their maximum or minimum values.
|
||||
|
||||
<p align="center"> Table 7-1 The Best and Worst Structures of Binary Trees </p>
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ Common traversal methods for binary trees include level-order traversal, preorde
|
||||
|
||||
## 7.2.1 Level-order traversal
|
||||
|
||||
As shown in the Figure 7-9 , "level-order traversal" traverses the binary tree from top to bottom, layer by layer, and accesses nodes in each layer in a left-to-right order.
|
||||
As shown in Figure 7-9, "level-order traversal" traverses the binary tree from top to bottom, layer by layer, and accesses nodes in each layer in a left-to-right order.
|
||||
|
||||
Level-order traversal essentially belongs to "breadth-first traversal", also known as "breadth-first search (BFS)", which embodies a "circumferentially outward expanding" layer-by-layer traversal method.
|
||||
|
||||
@@ -380,7 +380,7 @@ Breadth-first traversal is usually implemented with the help of a "queue". The q
|
||||
|
||||
Correspondingly, preorder, inorder, and postorder traversal all belong to "depth-first traversal", also known as "depth-first search (DFS)", which embodies a "proceed to the end first, then backtrack and continue" traversal method.
|
||||
|
||||
The Figure 7-10 shows the working principle of performing a depth-first traversal 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 traversal, inorder traversal, and postorder traversal.
|
||||
Figure 7-10 shows the working principle of performing a depth-first traversal 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 traversal, inorder traversal, and postorder traversal.
|
||||
|
||||
{ class="animation-figure" }
|
||||
|
||||
@@ -875,7 +875,7 @@ Depth-first search is usually implemented based on recursion:
|
||||
|
||||
Depth-first search can also be implemented based on iteration, interested readers can study this on their own.
|
||||
|
||||
The 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 parts: "recursion" and "return".
|
||||
|
||||
1. "Recursion" means starting a new method, the program accesses the next node in this process.
|
||||
2. "Return" means the function returns, indicating the current node has been fully accessed.
|
||||
|
||||
Reference in New Issue
Block a user