This commit is contained in:
krahets
2024-09-28 09:26:59 +08:00
parent 55b91eb967
commit 8ffc9616e9
34 changed files with 548 additions and 591 deletions
+15 -15
View File
@@ -3702,13 +3702,13 @@
<p align="center"> Figure 7-16 &nbsp; Binary search tree </p>
<h2 id="741-operations-on-a-binary-search-tree">7.4.1 &nbsp; Operations on a binary search tree<a class="headerlink" href="#741-operations-on-a-binary-search-tree" title="Permanent link">&para;</a></h2>
<p>We encapsulate the binary search tree as a class <code>BinarySearchTree</code> and declare a member variable <code>root</code>, pointing to the tree's root node.</p>
<p>We encapsulate the binary search tree as a class <code>BinarySearchTree</code> and declare a member variable <code>root</code> pointing to the tree's root node.</p>
<h3 id="1-searching-for-a-node">1. &nbsp; Searching for a node<a class="headerlink" href="#1-searching-for-a-node" title="Permanent link">&para;</a></h3>
<p>Given a target node value <code>num</code>, one can search according to the properties of the binary search tree. As shown in Figure 7-17, we declare a node <code>cur</code> and start from the binary tree's root node <code>root</code>, looping to compare the size relationship between the node value <code>cur.val</code> and <code>num</code>.</p>
<p>Given a target node value <code>num</code>, one can search according to the properties of the binary search tree. As shown in Figure 7-17, we declare a node <code>cur</code>, start from the binary tree's root node <code>root</code>, and loop to compare the size between the node value <code>cur.val</code> and <code>num</code>.</p>
<ul>
<li>If <code>cur.val &lt; num</code>, it means the target node is in <code>cur</code>'s right subtree, thus execute <code>cur = cur.right</code>.</li>
<li>If <code>cur.val &gt; num</code>, it means the target node is in <code>cur</code>'s left subtree, thus execute <code>cur = cur.left</code>.</li>
<li>If <code>cur.val = num</code>, it means the target node is found, exit the loop and return the node.</li>
<li>If <code>cur.val = num</code>, it means the target node is found, exit the loop, and return the node.</li>
</ul>
<div class="tabbed-set tabbed-alternate" data-tabs="1:4"><input checked="checked" id="__tabbed_1_1" name="__tabbed_1" type="radio" /><input id="__tabbed_1_2" name="__tabbed_1" type="radio" /><input id="__tabbed_1_3" name="__tabbed_1" type="radio" /><input id="__tabbed_1_4" name="__tabbed_1" type="radio" /><div class="tabbed-labels"><label for="__tabbed_1_1">&lt;1&gt;</label><label for="__tabbed_1_2">&lt;2&gt;</label><label for="__tabbed_1_3">&lt;3&gt;</label><label for="__tabbed_1_4">&lt;4&gt;</label></div>
<div class="tabbed-content">
@@ -3728,7 +3728,7 @@
</div>
<p align="center"> Figure 7-17 &nbsp; Example of searching for a node in a binary search tree </p>
<p>The search operation in a binary search tree works on the same principle as the binary search algorithm, eliminating half of the possibilities in each round. The number of loops is at most the height of the binary tree. When the binary tree is balanced, it uses <span class="arithmatex">\(O(\log n)\)</span> time. Example code is as follows:</p>
<p>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 <span class="arithmatex">\(O(\log n)\)</span> time. The example code is as follows:</p>
<div class="tabbed-set tabbed-alternate" data-tabs="2:14"><input checked="checked" id="__tabbed_2_1" name="__tabbed_2" type="radio" /><input id="__tabbed_2_2" name="__tabbed_2" type="radio" /><input id="__tabbed_2_3" name="__tabbed_2" type="radio" /><input id="__tabbed_2_4" name="__tabbed_2" type="radio" /><input id="__tabbed_2_5" name="__tabbed_2" type="radio" /><input id="__tabbed_2_6" name="__tabbed_2" type="radio" /><input id="__tabbed_2_7" name="__tabbed_2" type="radio" /><input id="__tabbed_2_8" name="__tabbed_2" type="radio" /><input id="__tabbed_2_9" name="__tabbed_2" type="radio" /><input id="__tabbed_2_10" name="__tabbed_2" type="radio" /><input id="__tabbed_2_11" name="__tabbed_2" type="radio" /><input id="__tabbed_2_12" name="__tabbed_2" type="radio" /><input id="__tabbed_2_13" name="__tabbed_2" type="radio" /><input id="__tabbed_2_14" name="__tabbed_2" type="radio" /><div class="tabbed-labels"><label for="__tabbed_2_1">Python</label><label for="__tabbed_2_2">C++</label><label for="__tabbed_2_3">Java</label><label for="__tabbed_2_4">C#</label><label for="__tabbed_2_5">Go</label><label for="__tabbed_2_6">Swift</label><label for="__tabbed_2_7">JS</label><label for="__tabbed_2_8">TS</label><label for="__tabbed_2_9">Dart</label><label for="__tabbed_2_10">Rust</label><label for="__tabbed_2_11">C</label><label for="__tabbed_2_12">Kotlin</label><label for="__tabbed_2_13">Ruby</label><label for="__tabbed_2_14">Zig</label></div>
<div class="tabbed-content">
<div class="tabbed-block">
@@ -3840,16 +3840,16 @@
<h3 id="2-inserting-a-node">2. &nbsp; Inserting a node<a class="headerlink" href="#2-inserting-a-node" title="Permanent link">&para;</a></h3>
<p>Given an element <code>num</code> to be inserted, to maintain the property of the binary search tree "left subtree &lt; root node &lt; right subtree," the insertion operation proceeds as shown in Figure 7-18.</p>
<ol>
<li><strong>Finding the insertion position</strong>: Similar to the search operation, start from the root node and loop downwards according to the size relationship between the current node value and <code>num</code> until passing through the leaf node (traversing to <code>None</code>) then exit the loop.</li>
<li><strong>Insert the node at that position</strong>: Initialize the node <code>num</code> and place it where <code>None</code> was.</li>
<li><strong>Finding insertion position</strong>: Similar to the search operation, start from the root node, loop downwards according to the size relationship between the current node value and <code>num</code>, until the leaf node is passed (traversed to <code>None</code>), then exit the loop.</li>
<li><strong>Insert the node at this position</strong>: Initialize the node <code>num</code> and place it where <code>None</code> was.</li>
</ol>
<p><a class="glightbox" href="../binary_search_tree.assets/bst_insert.png" data-type="image" data-width="100%" data-height="auto" data-desc-position="bottom"><img alt="Inserting a node into a binary search tree" class="animation-figure" src="../binary_search_tree.assets/bst_insert.png" /></a></p>
<p align="center"> Figure 7-18 &nbsp; Inserting a node into a binary search tree </p>
<p>In the code implementation, note the following two points.</p>
<ul>
<li>The binary search tree does not allow duplicate nodes; otherwise, it will violate its definition. Therefore, if the node to be inserted already exists in the tree, the insertion is not performed, and it directly returns.</li>
<li>To perform the insertion operation, we need to use the node <code>pre</code> to save the node from the last loop. This way, when traversing to <code>None</code>, we can get its parent node, thus completing the node insertion operation.</li>
<li>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.</li>
<li>To perform the insertion operation, we need to use the node <code>pre</code> to save the node from the previous loop. This way, when traversing to <code>None</code>, we can get its parent node, thus completing the node insertion operation.</li>
</ul>
<div class="tabbed-set tabbed-alternate" data-tabs="3:14"><input checked="checked" id="__tabbed_3_1" name="__tabbed_3" type="radio" /><input id="__tabbed_3_2" name="__tabbed_3" type="radio" /><input id="__tabbed_3_3" name="__tabbed_3" type="radio" /><input id="__tabbed_3_4" name="__tabbed_3" type="radio" /><input id="__tabbed_3_5" name="__tabbed_3" type="radio" /><input id="__tabbed_3_6" name="__tabbed_3" type="radio" /><input id="__tabbed_3_7" name="__tabbed_3" type="radio" /><input id="__tabbed_3_8" name="__tabbed_3" type="radio" /><input id="__tabbed_3_9" name="__tabbed_3" type="radio" /><input id="__tabbed_3_10" name="__tabbed_3" type="radio" /><input id="__tabbed_3_11" name="__tabbed_3" type="radio" /><input id="__tabbed_3_12" name="__tabbed_3" type="radio" /><input id="__tabbed_3_13" name="__tabbed_3" type="radio" /><input id="__tabbed_3_14" name="__tabbed_3" type="radio" /><div class="tabbed-labels"><label for="__tabbed_3_1">Python</label><label for="__tabbed_3_2">C++</label><label for="__tabbed_3_3">Java</label><label for="__tabbed_3_4">C#</label><label for="__tabbed_3_5">Go</label><label for="__tabbed_3_6">Swift</label><label for="__tabbed_3_7">JS</label><label for="__tabbed_3_8">TS</label><label for="__tabbed_3_9">Dart</label><label for="__tabbed_3_10">Rust</label><label for="__tabbed_3_11">C</label><label for="__tabbed_3_12">Kotlin</label><label for="__tabbed_3_13">Ruby</label><label for="__tabbed_3_14">Zig</label></div>
<div class="tabbed-content">
@@ -3991,8 +3991,8 @@
</div>
<p>Similar to searching for a node, inserting a node uses <span class="arithmatex">\(O(\log n)\)</span> time.</p>
<h3 id="3-removing-a-node">3. &nbsp; Removing a node<a class="headerlink" href="#3-removing-a-node" title="Permanent link">&para;</a></h3>
<p>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 &lt; root node &lt; 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.</p>
<p>As shown in Figure 7-19, when the degree of the node to be removed is <span class="arithmatex">\(0\)</span>, it means the node is a leaf node, and it can be directly removed.</p>
<p>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 &lt; root node &lt; 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.</p>
<p>As shown in Figure 7-19, when the degree of the node to be removed is <span class="arithmatex">\(0\)</span>, it means the node is a leaf node and can be directly removed.</p>
<p><a class="glightbox" href="../binary_search_tree.assets/bst_remove_case1.png" data-type="image" data-width="100%" data-height="auto" data-desc-position="bottom"><img alt="Removing a node in a binary search tree (degree 0)" class="animation-figure" src="../binary_search_tree.assets/bst_remove_case1.png" /></a></p>
<p align="center"> Figure 7-19 &nbsp; Removing a node in a binary search tree (degree 0) </p>
@@ -4231,14 +4231,14 @@
</div>
</div>
<h3 id="4-in-order-traversal-is-ordered">4. &nbsp; In-order traversal is ordered<a class="headerlink" href="#4-in-order-traversal-is-ordered" title="Permanent link">&para;</a></h3>
<p>As shown in Figure 7-22, the in-order traversal of a binary tree follows the "left <span class="arithmatex">\(\rightarrow\)</span> root <span class="arithmatex">\(\rightarrow\)</span> right" traversal order, and a binary search tree satisfies the size relationship "left child node <span class="arithmatex">\(&lt;\)</span> root node <span class="arithmatex">\(&lt;\)</span> right child node".</p>
<p>This means that in-order traversal in a binary search tree always traverses the next smallest node first, thus deriving an important property: <strong>The in-order traversal sequence of a binary search tree is ascending</strong>.</p>
<p>As shown in Figure 7-22, the in-order traversal of a binary tree follows the traversal order of "left <span class="arithmatex">\(\rightarrow\)</span> root <span class="arithmatex">\(\rightarrow\)</span> right," and a binary search tree satisfies the size relationship of "left child node <span class="arithmatex">\(&lt;\)</span> root node <span class="arithmatex">\(&lt;\)</span> right child node."</p>
<p>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: <strong>The sequence of in-order traversal in a binary search tree is ascending</strong>.</p>
<p>Using the ascending property of in-order traversal, obtaining ordered data in a binary search tree requires only <span class="arithmatex">\(O(n)\)</span> time, without the need for additional sorting operations, which is very efficient.</p>
<p><a class="glightbox" href="../binary_search_tree.assets/bst_inorder_traversal.png" data-type="image" data-width="100%" data-height="auto" data-desc-position="bottom"><img alt="In-order traversal sequence of a binary search tree" class="animation-figure" src="../binary_search_tree.assets/bst_inorder_traversal.png" /></a></p>
<p align="center"> Figure 7-22 &nbsp; In-order traversal sequence of a binary search tree </p>
<h2 id="742-efficiency-of-binary-search-trees">7.4.2 &nbsp; Efficiency of binary search trees<a class="headerlink" href="#742-efficiency-of-binary-search-trees" title="Permanent link">&para;</a></h2>
<p>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>
<p>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.</p>
<p align="center"> Table 7-2 &nbsp; Efficiency comparison between arrays and search trees </p>
<div class="center-table">
@@ -4269,8 +4269,8 @@
</tbody>
</table>
</div>
<p>In ideal conditions, the binary search tree is "balanced," thus any node can be found within <span class="arithmatex">\(\log n\)</span> loops.</p>
<p>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 <span class="arithmatex">\(O(n)\)</span>.</p>
<p>Ideally, the binary search tree is "balanced," allowing any node can be found within <span class="arithmatex">\(\log n\)</span> loops.</p>
<p>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 <span class="arithmatex">\(O(n)\)</span>.</p>
<p><a class="glightbox" href="../binary_search_tree.assets/bst_degradation.png" data-type="image" data-width="100%" data-height="auto" data-desc-position="bottom"><img alt="Degradation of a binary search tree" class="animation-figure" src="../binary_search_tree.assets/bst_degradation.png" /></a></p>
<p align="center"> Figure 7-23 &nbsp; Degradation of a binary search tree </p>