This commit is contained in:
krahets
2026-04-02 03:08:50 +08:00
parent 09a136c9fa
commit aaf9f58eb3
157 changed files with 3002 additions and 2994 deletions
@@ -6,7 +6,7 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="description" content="Data Structures and Algorithms Crash Course with Animated Illustrations and Off-the-Shelf Code">
<meta name="description" content="Data structures and algorithms tutorial with animated illustrations and ready-to-run code">
<meta name="author" content="krahets">
@@ -576,7 +576,7 @@
<span class="md-ellipsis">
Chapter 1. Encounter With Algorithms
Chapter 1. Encounter with Algorithms
@@ -598,7 +598,7 @@
<span class="md-nav__icon md-icon"></span>
Chapter 1. Encounter With Algorithms
Chapter 1. Encounter with Algorithms
</label>
@@ -1183,7 +1183,7 @@
<span class="md-ellipsis">
Chapter 4. Array and Linked List
Chapter 4. Arrays and Linked Lists
@@ -1205,7 +1205,7 @@
<span class="md-nav__icon md-icon"></span>
Chapter 4. Array and Linked List
Chapter 4. Arrays and Linked Lists
</label>
@@ -1311,7 +1311,7 @@
<span class="md-ellipsis">
4.4 Memory and Cache *
4.4 Random-Access Memory and Cache *
@@ -1402,7 +1402,7 @@
<span class="md-ellipsis">
Chapter 5. Stack and Queue
Chapter 5. Stacks and Queues
@@ -1424,7 +1424,7 @@
<span class="md-nav__icon md-icon"></span>
Chapter 5. Stack and Queue
Chapter 5. Stacks and Queues
</label>
@@ -1502,7 +1502,7 @@
<span class="md-ellipsis">
5.3 Double-Ended Queue
5.3 Deque
@@ -1593,7 +1593,7 @@
<span class="md-ellipsis">
Chapter 6. Hashing
Chapter 6. Hash Table
@@ -1615,7 +1615,7 @@
<span class="md-nav__icon md-icon"></span>
Chapter 6. Hashing
Chapter 6. Hash Table
</label>
@@ -1888,7 +1888,7 @@
<span class="md-ellipsis">
7.3 Array Representation of Tree
7.3 Array Representation of Binary Trees
@@ -2107,7 +2107,7 @@
<span class="md-ellipsis">
8.2 Building a Heap
8.2 Heap Construction Operation
@@ -2135,7 +2135,7 @@
<span class="md-ellipsis">
8.3 Top-K Problem
8.3 Top-k Problem
@@ -2493,7 +2493,7 @@
<span class="md-ellipsis">
10.2 Binary Search Insertion
10.2 Binary Search Insertion Point
@@ -2521,7 +2521,7 @@
<span class="md-ellipsis">
10.3 Binary Search Edge Cases
10.3 Binary Search Boundaries
@@ -2577,7 +2577,7 @@
<span class="md-ellipsis">
10.5 Search Algorithms Revisited
10.5 Searching Algorithms Revisited
@@ -2726,7 +2726,7 @@
<span class="md-ellipsis">
11.1 Sorting Algorithms
11.1 Sorting Algorithm
@@ -3199,7 +3199,7 @@
<span class="md-ellipsis">
12.4 Hanoi Tower Problem
12.4 Hanota Problem
@@ -4227,7 +4227,7 @@
<span class="md-ellipsis">
16.3 Terminology Table
16.3 Glossary
@@ -4653,13 +4653,13 @@
<h2 id="1311-attempt-and-backtrack">13.1.1 &nbsp; Attempt and Backtrack<a class="headerlink" href="#1311-attempt-and-backtrack" title="Permanent link">&para;</a></h2>
<p><strong>The reason it is called a backtracking algorithm is that it employs "attempt" and "backtrack" strategies when searching the solution space</strong>. When the algorithm encounters a state where it cannot continue forward or cannot find a solution that satisfies the constraints, it will undo the previous choice, return to a previous state, and try other possible choices.</p>
<p>For Example 1, visiting each node represents an "attempt", while skipping over a leaf node or a function <code>return</code> from the parent node represents a "backtrack".</p>
<p>For Example 1, visiting each node represents an "attempt", while skipping over a leaf node or the <code>return</code> that brings the traversal back to the parent node represents a "backtrack".</p>
<p>It is worth noting that <strong>backtracking is not limited to function returns alone</strong>. To illustrate this, let's extend Example 1 slightly.</p>
<div class="admonition question">
<p class="admonition-title">Example 2</p>
<p>In a binary tree, search all nodes with value <span class="arithmatex">\(7\)</span>, <strong>and return the paths from the root node to these nodes</strong>.</p>
</div>
<p>Based on the code from Example 1, we need to use a list <code>path</code> to record the visited node path. When we reach a node with value <span class="arithmatex">\(7\)</span>, we copy <code>path</code> and add it to the result list <code>res</code>. After traversal is complete, <code>res</code> contains all the solutions. The code is as follows:</p>
<p>Based on the code from Example 1, we need to use a list <code>path</code> to record the path of visited nodes. When we reach a node with value <span class="arithmatex">\(7\)</span>, we copy <code>path</code> and add it to the result list <code>res</code>. After traversal is complete, <code>res</code> contains all the solutions. The code is as follows:</p>
<div class="tabbed-set tabbed-alternate" data-tabs="2:13"><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" /><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></div>
<div class="tabbed-content">
<div class="tabbed-block">
@@ -5257,7 +5257,7 @@
<p align="center"> Figure 13-3 &nbsp; Pruning according to constraints </p>
<h2 id="1313-framework-code">13.1.3 &nbsp; Framework Code<a class="headerlink" href="#1313-framework-code" title="Permanent link">&para;</a></h2>
<p>Next, we attempt to extract the main framework of backtracking's "attempt, backtrack, and pruning", to improve code generality.</p>
<p>Next, we attempt to extract a general framework centered on backtracking's "attempt, backtrack, and pruning" to improve code generality.</p>
<p>In the following framework code, <code>state</code> represents the current state of the problem, and <code>choices</code> represents the choices available in the current state:</p>
<div class="tabbed-set tabbed-alternate" data-tabs="5:13"><input checked="checked" id="__tabbed_5_1" name="__tabbed_5" type="radio" /><input id="__tabbed_5_2" name="__tabbed_5" type="radio" /><input id="__tabbed_5_3" name="__tabbed_5" type="radio" /><input id="__tabbed_5_4" name="__tabbed_5" type="radio" /><input id="__tabbed_5_5" name="__tabbed_5" type="radio" /><input id="__tabbed_5_6" name="__tabbed_5" type="radio" /><input id="__tabbed_5_7" name="__tabbed_5" type="radio" /><input id="__tabbed_5_8" name="__tabbed_5" type="radio" /><input id="__tabbed_5_9" name="__tabbed_5" type="radio" /><input id="__tabbed_5_10" name="__tabbed_5" type="radio" /><input id="__tabbed_5_11" name="__tabbed_5" type="radio" /><input id="__tabbed_5_12" name="__tabbed_5" type="radio" /><input id="__tabbed_5_13" name="__tabbed_5" type="radio" /><div class="tabbed-labels"><label for="__tabbed_5_1">Python</label><label for="__tabbed_5_2">C++</label><label for="__tabbed_5_3">Java</label><label for="__tabbed_5_4">C#</label><label for="__tabbed_5_5">Go</label><label for="__tabbed_5_6">Swift</label><label for="__tabbed_5_7">JS</label><label for="__tabbed_5_8">TS</label><label for="__tabbed_5_9">Dart</label><label for="__tabbed_5_10">Rust</label><label for="__tabbed_5_11">C</label><label for="__tabbed_5_12">Kotlin</label><label for="__tabbed_5_13">Ruby</label></div>
<div class="tabbed-content">
@@ -6228,7 +6228,7 @@
<p><img alt="Comparison of search process with and without return statement" class="animation-figure" src="../backtracking_algorithm.assets/backtrack_remove_return_or_not.png" /></p>
<p align="center"> Figure 13-4 &nbsp; Comparison of search process with and without return statement </p>
<p>Compared to code based on preorder traversal, code based on the backtracking algorithm framework appears more verbose, but has better generality. In fact, <strong>many backtracking problems can be solved within this framework</strong>. We only need to define <code>state</code> and <code>choices</code> for the specific problem and implement each method in the framework.</p>
<p>Compared to code based on preorder traversal, code based on the backtracking algorithm framework appears more verbose, but is more general. In fact, <strong>many backtracking problems can be solved within this framework</strong>. We only need to define <code>state</code> and <code>choices</code> for the specific problem and implement each method in the framework.</p>
<h2 id="1314-common-terminology">13.1.4 &nbsp; Common Terminology<a class="headerlink" href="#1314-common-terminology" title="Permanent link">&para;</a></h2>
<p>To analyze algorithmic problems more clearly, we summarize the meanings of common terminology used in backtracking algorithms and provide corresponding examples from Example 3, as shown in the following table.</p>
<p align="center"> Table 13-1 &nbsp; Common Backtracking Algorithm Terminology </p>
@@ -6278,13 +6278,13 @@
</div>
<div class="admonition tip">
<p class="admonition-title">Tip</p>
<p>The concepts of problem, solution, state, etc. are universal and are involved in divide-and-conquer, backtracking, dynamic programming, greedy and other algorithms.</p>
<p>The concepts of problem, solution, state, etc. are universal and appear in divide-and-conquer, backtracking, dynamic programming, greedy algorithms, and others.</p>
</div>
<h2 id="1315-advantages-and-limitations">13.1.5 &nbsp; Advantages and Limitations<a class="headerlink" href="#1315-advantages-and-limitations" title="Permanent link">&para;</a></h2>
<p>The backtracking algorithm is essentially a depth-first search algorithm that tries all possible solutions until it finds one that satisfies the conditions. The advantage of this approach is that it can find all possible solutions, and with reasonable pruning operations, it achieves high efficiency.</p>
<p>However, when dealing with large-scale or complex problems, <strong>the running efficiency of the backtracking algorithm may be unacceptable</strong>.</p>
<ul>
<li><strong>Time</strong>: The backtracking algorithm usually needs to traverse all possibilities in the solution space, and the time complexity can reach exponential or factorial order.</li>
<li><strong>Time</strong>: The backtracking algorithm usually needs to traverse all possibilities in the state space, and the time complexity can reach exponential or factorial order.</li>
<li><strong>Space</strong>: During recursive calls, the current state needs to be saved (such as paths, auxiliary variables used for pruning, etc.), and when the depth is large, the space requirement can become very large.</li>
</ul>
<p>Nevertheless, <strong>the backtracking algorithm is still the best solution for certain search problems and constraint satisfaction problems</strong>. For these problems, since we cannot predict which choices will generate valid solutions, we must traverse all possible choices. In this case, <strong>the key is how to optimize efficiency</strong>. There are two common efficiency optimization methods.</p>
+20 -20
View File
@@ -6,7 +6,7 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="description" content="Data Structures and Algorithms Crash Course with Animated Illustrations and Off-the-Shelf Code">
<meta name="description" content="Data structures and algorithms tutorial with animated illustrations and ready-to-run code">
<meta name="author" content="krahets">
@@ -576,7 +576,7 @@
<span class="md-ellipsis">
Chapter 1. Encounter With Algorithms
Chapter 1. Encounter with Algorithms
@@ -598,7 +598,7 @@
<span class="md-nav__icon md-icon"></span>
Chapter 1. Encounter With Algorithms
Chapter 1. Encounter with Algorithms
</label>
@@ -1183,7 +1183,7 @@
<span class="md-ellipsis">
Chapter 4. Array and Linked List
Chapter 4. Arrays and Linked Lists
@@ -1205,7 +1205,7 @@
<span class="md-nav__icon md-icon"></span>
Chapter 4. Array and Linked List
Chapter 4. Arrays and Linked Lists
</label>
@@ -1311,7 +1311,7 @@
<span class="md-ellipsis">
4.4 Memory and Cache *
4.4 Random-Access Memory and Cache *
@@ -1402,7 +1402,7 @@
<span class="md-ellipsis">
Chapter 5. Stack and Queue
Chapter 5. Stacks and Queues
@@ -1424,7 +1424,7 @@
<span class="md-nav__icon md-icon"></span>
Chapter 5. Stack and Queue
Chapter 5. Stacks and Queues
</label>
@@ -1502,7 +1502,7 @@
<span class="md-ellipsis">
5.3 Double-Ended Queue
5.3 Deque
@@ -1593,7 +1593,7 @@
<span class="md-ellipsis">
Chapter 6. Hashing
Chapter 6. Hash Table
@@ -1615,7 +1615,7 @@
<span class="md-nav__icon md-icon"></span>
Chapter 6. Hashing
Chapter 6. Hash Table
</label>
@@ -1888,7 +1888,7 @@
<span class="md-ellipsis">
7.3 Array Representation of Tree
7.3 Array Representation of Binary Trees
@@ -2107,7 +2107,7 @@
<span class="md-ellipsis">
8.2 Building a Heap
8.2 Heap Construction Operation
@@ -2135,7 +2135,7 @@
<span class="md-ellipsis">
8.3 Top-K Problem
8.3 Top-k Problem
@@ -2493,7 +2493,7 @@
<span class="md-ellipsis">
10.2 Binary Search Insertion
10.2 Binary Search Insertion Point
@@ -2521,7 +2521,7 @@
<span class="md-ellipsis">
10.3 Binary Search Edge Cases
10.3 Binary Search Boundaries
@@ -2577,7 +2577,7 @@
<span class="md-ellipsis">
10.5 Search Algorithms Revisited
10.5 Searching Algorithms Revisited
@@ -2726,7 +2726,7 @@
<span class="md-ellipsis">
11.1 Sorting Algorithms
11.1 Sorting Algorithm
@@ -3199,7 +3199,7 @@
<span class="md-ellipsis">
12.4 Hanoi Tower Problem
12.4 Hanota Problem
@@ -4113,7 +4113,7 @@
<span class="md-ellipsis">
16.3 Terminology Table
16.3 Glossary
@@ -6,7 +6,7 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="description" content="Data Structures and Algorithms Crash Course with Animated Illustrations and Off-the-Shelf Code">
<meta name="description" content="Data structures and algorithms tutorial with animated illustrations and ready-to-run code">
<meta name="author" content="krahets">
@@ -576,7 +576,7 @@
<span class="md-ellipsis">
Chapter 1. Encounter With Algorithms
Chapter 1. Encounter with Algorithms
@@ -598,7 +598,7 @@
<span class="md-nav__icon md-icon"></span>
Chapter 1. Encounter With Algorithms
Chapter 1. Encounter with Algorithms
</label>
@@ -1183,7 +1183,7 @@
<span class="md-ellipsis">
Chapter 4. Array and Linked List
Chapter 4. Arrays and Linked Lists
@@ -1205,7 +1205,7 @@
<span class="md-nav__icon md-icon"></span>
Chapter 4. Array and Linked List
Chapter 4. Arrays and Linked Lists
</label>
@@ -1311,7 +1311,7 @@
<span class="md-ellipsis">
4.4 Memory and Cache *
4.4 Random-Access Memory and Cache *
@@ -1402,7 +1402,7 @@
<span class="md-ellipsis">
Chapter 5. Stack and Queue
Chapter 5. Stacks and Queues
@@ -1424,7 +1424,7 @@
<span class="md-nav__icon md-icon"></span>
Chapter 5. Stack and Queue
Chapter 5. Stacks and Queues
</label>
@@ -1502,7 +1502,7 @@
<span class="md-ellipsis">
5.3 Double-Ended Queue
5.3 Deque
@@ -1593,7 +1593,7 @@
<span class="md-ellipsis">
Chapter 6. Hashing
Chapter 6. Hash Table
@@ -1615,7 +1615,7 @@
<span class="md-nav__icon md-icon"></span>
Chapter 6. Hashing
Chapter 6. Hash Table
</label>
@@ -1888,7 +1888,7 @@
<span class="md-ellipsis">
7.3 Array Representation of Tree
7.3 Array Representation of Binary Trees
@@ -2107,7 +2107,7 @@
<span class="md-ellipsis">
8.2 Building a Heap
8.2 Heap Construction Operation
@@ -2135,7 +2135,7 @@
<span class="md-ellipsis">
8.3 Top-K Problem
8.3 Top-k Problem
@@ -2493,7 +2493,7 @@
<span class="md-ellipsis">
10.2 Binary Search Insertion
10.2 Binary Search Insertion Point
@@ -2521,7 +2521,7 @@
<span class="md-ellipsis">
10.3 Binary Search Edge Cases
10.3 Binary Search Boundaries
@@ -2577,7 +2577,7 @@
<span class="md-ellipsis">
10.5 Search Algorithms Revisited
10.5 Searching Algorithms Revisited
@@ -2726,7 +2726,7 @@
<span class="md-ellipsis">
11.1 Sorting Algorithms
11.1 Sorting Algorithm
@@ -3199,7 +3199,7 @@
<span class="md-ellipsis">
12.4 Hanoi Tower Problem
12.4 Hanota Problem
@@ -4194,7 +4194,7 @@
<span class="md-ellipsis">
16.3 Terminology Table
16.3 Glossary
@@ -4381,7 +4381,7 @@
<h1 id="134-n-queens-problem">13.4 &nbsp; N-Queens Problem<a class="headerlink" href="#134-n-queens-problem" title="Permanent link">&para;</a></h1>
<div class="admonition question">
<p class="admonition-title">Question</p>
<p>According to the rules of chess, a queen can attack pieces that share the same row, column, or diagonal line. Given <span class="arithmatex">\(n\)</span> queens and an <span class="arithmatex">\(n \times n\)</span> chessboard, find a placement scheme such that no two queens can attack each other.</p>
<p>According to the rules of chess, a queen can attack any piece in the same row, column, or diagonal. Given <span class="arithmatex">\(n\)</span> queens and an <span class="arithmatex">\(n \times n\)</span> chessboard, find an arrangement such that no two queens can attack each other.</p>
</div>
<p>As shown in Figure 13-15, when <span class="arithmatex">\(n = 4\)</span>, there are two solutions that can be found. From the perspective of the backtracking algorithm, an <span class="arithmatex">\(n \times n\)</span> chessboard has <span class="arithmatex">\(n^2\)</span> squares, which provide all the choices <code>choices</code>. During the process of placing queens one by one, the chessboard state changes continuously, and the chessboard at each moment represents the state <code>state</code>.</p>
<p><img alt="Solution to the 4-queens problem" class="animation-figure" src="../n_queens_problem.assets/solution_4_queens.png" /></p>
@@ -4392,9 +4392,9 @@
<p align="center"> Figure 13-16 &nbsp; Constraints of the n-queens problem </p>
<h3 id="1-row-by-row-placement-strategy">1. &nbsp; Row-By-Row Placement Strategy<a class="headerlink" href="#1-row-by-row-placement-strategy" title="Permanent link">&para;</a></h3>
<p>Since both the number of queens and the number of rows on the chessboard are <span class="arithmatex">\(n\)</span>, we can easily derive a conclusion: <strong>each row of the chessboard allows and only allows exactly one queen to be placed</strong>.</p>
<p>Since both the number of queens and the number of rows on the chessboard are <span class="arithmatex">\(n\)</span>, we can easily derive a conclusion: <strong>each row of the chessboard allows one and only one queen to be placed</strong>.</p>
<p>This means we can adopt a row-by-row placement strategy: starting from the first row, place one queen in each row until the last row is completed.</p>
<p>Figure 13-17 shows the row-by-row placement process for the 4-queens problem. Due to space limitations, the figure only expands one search branch of the first row, and all schemes that do not satisfy the column constraint and diagonal constraints are pruned.</p>
<p>Figure 13-17 shows the row-by-row placement process for the 4-queens problem. Due to space limitations, the figure only expands one search branch of the first row, and all schemes that violate the column or diagonal constraints are pruned.</p>
<p><img alt="Row-by-row placement strategy" class="animation-figure" src="../n_queens_problem.assets/n_queens_placing.png" /></p>
<p align="center"> Figure 13-17 &nbsp; Row-by-row placement strategy </p>
@@ -4412,7 +4412,7 @@
<p align="center"> Figure 13-18 &nbsp; Handling column and diagonal constraints </p>
<h3 id="3-code-implementation">3. &nbsp; Code Implementation<a class="headerlink" href="#3-code-implementation" title="Permanent link">&para;</a></h3>
<p>Please note that in an <span class="arithmatex">\(n\)</span>-dimensional square matrix, the range of <span class="arithmatex">\(row - col\)</span> is <span class="arithmatex">\([-n + 1, n - 1]\)</span>, and the range of <span class="arithmatex">\(row + col\)</span> is <span class="arithmatex">\([0, 2n - 2]\)</span>. Therefore, the number of both main diagonals and anti-diagonals is <span class="arithmatex">\(2n - 1\)</span>, meaning the length of both arrays <code>diags1</code> and <code>diags2</code> is <span class="arithmatex">\(2n - 1\)</span>.</p>
<p>Please note that in an <span class="arithmatex">\(n \times n\)</span> square matrix, the range of <span class="arithmatex">\(row - col\)</span> is <span class="arithmatex">\([-n + 1, n - 1]\)</span>, and the range of <span class="arithmatex">\(row + col\)</span> is <span class="arithmatex">\([0, 2n - 2]\)</span>. Therefore, the number of both main diagonals and anti-diagonals is <span class="arithmatex">\(2n - 1\)</span>, meaning the length of both arrays <code>diags1</code> and <code>diags2</code> is <span class="arithmatex">\(2n - 1\)</span>.</p>
<div class="tabbed-set tabbed-alternate" data-tabs="1:13"><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" /><input id="__tabbed_1_5" name="__tabbed_1" type="radio" /><input id="__tabbed_1_6" name="__tabbed_1" type="radio" /><input id="__tabbed_1_7" name="__tabbed_1" type="radio" /><input id="__tabbed_1_8" name="__tabbed_1" type="radio" /><input id="__tabbed_1_9" name="__tabbed_1" type="radio" /><input id="__tabbed_1_10" name="__tabbed_1" type="radio" /><input id="__tabbed_1_11" name="__tabbed_1" type="radio" /><input id="__tabbed_1_12" name="__tabbed_1" type="radio" /><input id="__tabbed_1_13" name="__tabbed_1" type="radio" /><div class="tabbed-labels"><label for="__tabbed_1_1">Python</label><label for="__tabbed_1_2">C++</label><label for="__tabbed_1_3">Java</label><label for="__tabbed_1_4">C#</label><label for="__tabbed_1_5">Go</label><label for="__tabbed_1_6">Swift</label><label for="__tabbed_1_7">JS</label><label for="__tabbed_1_8">TS</label><label for="__tabbed_1_9">Dart</label><label for="__tabbed_1_10">Rust</label><label for="__tabbed_1_11">C</label><label for="__tabbed_1_12">Kotlin</label><label for="__tabbed_1_13">Ruby</label></div>
<div class="tabbed-content">
<div class="tabbed-block">
@@ -6,7 +6,7 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="description" content="Data Structures and Algorithms Crash Course with Animated Illustrations and Off-the-Shelf Code">
<meta name="description" content="Data structures and algorithms tutorial with animated illustrations and ready-to-run code">
<meta name="author" content="krahets">
@@ -576,7 +576,7 @@
<span class="md-ellipsis">
Chapter 1. Encounter With Algorithms
Chapter 1. Encounter with Algorithms
@@ -598,7 +598,7 @@
<span class="md-nav__icon md-icon"></span>
Chapter 1. Encounter With Algorithms
Chapter 1. Encounter with Algorithms
</label>
@@ -1183,7 +1183,7 @@
<span class="md-ellipsis">
Chapter 4. Array and Linked List
Chapter 4. Arrays and Linked Lists
@@ -1205,7 +1205,7 @@
<span class="md-nav__icon md-icon"></span>
Chapter 4. Array and Linked List
Chapter 4. Arrays and Linked Lists
</label>
@@ -1311,7 +1311,7 @@
<span class="md-ellipsis">
4.4 Memory and Cache *
4.4 Random-Access Memory and Cache *
@@ -1402,7 +1402,7 @@
<span class="md-ellipsis">
Chapter 5. Stack and Queue
Chapter 5. Stacks and Queues
@@ -1424,7 +1424,7 @@
<span class="md-nav__icon md-icon"></span>
Chapter 5. Stack and Queue
Chapter 5. Stacks and Queues
</label>
@@ -1502,7 +1502,7 @@
<span class="md-ellipsis">
5.3 Double-Ended Queue
5.3 Deque
@@ -1593,7 +1593,7 @@
<span class="md-ellipsis">
Chapter 6. Hashing
Chapter 6. Hash Table
@@ -1615,7 +1615,7 @@
<span class="md-nav__icon md-icon"></span>
Chapter 6. Hashing
Chapter 6. Hash Table
</label>
@@ -1888,7 +1888,7 @@
<span class="md-ellipsis">
7.3 Array Representation of Tree
7.3 Array Representation of Binary Trees
@@ -2107,7 +2107,7 @@
<span class="md-ellipsis">
8.2 Building a Heap
8.2 Heap Construction Operation
@@ -2135,7 +2135,7 @@
<span class="md-ellipsis">
8.3 Top-K Problem
8.3 Top-k Problem
@@ -2493,7 +2493,7 @@
<span class="md-ellipsis">
10.2 Binary Search Insertion
10.2 Binary Search Insertion Point
@@ -2521,7 +2521,7 @@
<span class="md-ellipsis">
10.3 Binary Search Edge Cases
10.3 Binary Search Boundaries
@@ -2577,7 +2577,7 @@
<span class="md-ellipsis">
10.5 Search Algorithms Revisited
10.5 Searching Algorithms Revisited
@@ -2726,7 +2726,7 @@
<span class="md-ellipsis">
11.1 Sorting Algorithms
11.1 Sorting Algorithm
@@ -3199,7 +3199,7 @@
<span class="md-ellipsis">
12.4 Hanoi Tower Problem
12.4 Hanota Problem
@@ -3471,10 +3471,10 @@
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#1-pruning-duplicate-elements" class="md-nav__link">
<a href="#1-pruning-equal-elements" class="md-nav__link">
<span class="md-ellipsis">
1. &nbsp; Pruning Duplicate Elements
1. &nbsp; Pruning Equal Elements
</span>
</a>
@@ -4250,7 +4250,7 @@
<span class="md-ellipsis">
16.3 Terminology Table
16.3 Glossary
@@ -4416,10 +4416,10 @@
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#1-pruning-duplicate-elements" class="md-nav__link">
<a href="#1-pruning-equal-elements" class="md-nav__link">
<span class="md-ellipsis">
1. &nbsp; Pruning Duplicate Elements
1. &nbsp; Pruning Equal Elements
</span>
</a>
@@ -4533,7 +4533,7 @@
<h3 id="1-pruning-duplicate-choices">1. &nbsp; Pruning Duplicate Choices<a class="headerlink" href="#1-pruning-duplicate-choices" title="Permanent link">&para;</a></h3>
<p>To ensure that each element is chosen only once, we consider introducing a boolean array <code>selected</code>, where <code>selected[i]</code> indicates whether <code>choices[i]</code> has been chosen. We implement the following pruning operation based on it.</p>
<ul>
<li>After making a choice <code>choice[i]</code>, we set <code>selected[i]</code> to <span class="arithmatex">\(\text{True}\)</span>, indicating that it has been chosen.</li>
<li>After making a choice <code>choices[i]</code>, we set <code>selected[i]</code> to <span class="arithmatex">\(\text{True}\)</span>, indicating that it has been chosen.</li>
<li>When traversing the candidate list <code>choices</code>, we skip all nodes that have been chosen, which is pruning.</li>
</ul>
<p>As shown in Figure 13-6, suppose we choose <span class="arithmatex">\(1\)</span> in the first round, <span class="arithmatex">\(3\)</span> in the second round, and <span class="arithmatex">\(2\)</span> in the third round. Then we need to prune the branch of element <span class="arithmatex">\(1\)</span> in the second round and prune the branches of elements <span class="arithmatex">\(1\)</span> and <span class="arithmatex">\(3\)</span> in the third round.</p>
@@ -5010,12 +5010,12 @@
<p>Given an integer array that <strong>may contain duplicate elements</strong>, return all unique permutations.</p>
</div>
<p>Suppose the input array is <span class="arithmatex">\([1, 1, 2]\)</span>. To distinguish the two duplicate elements <span class="arithmatex">\(1\)</span>, we denote the second <span class="arithmatex">\(1\)</span> as <span class="arithmatex">\(\hat{1}\)</span>.</p>
<p>As shown in Figure 13-7, the method described above generates permutations where half are duplicates.</p>
<p>As shown in Figure 13-7, half of the permutations generated by the above method are duplicates.</p>
<p><img alt="Duplicate permutations" class="animation-figure" src="../permutations_problem.assets/permutations_ii.png" /></p>
<p align="center"> Figure 13-7 &nbsp; Duplicate permutations </p>
<p>So how do we remove duplicate permutations? The most direct approach is to use a hash set to directly deduplicate the permutation results. However, this is not elegant because <strong>the search branches that generate duplicate permutations are unnecessary and should be identified and pruned early</strong>, which can further improve algorithm efficiency.</p>
<h3 id="1-pruning-duplicate-elements">1. &nbsp; Pruning Duplicate Elements<a class="headerlink" href="#1-pruning-duplicate-elements" title="Permanent link">&para;</a></h3>
<h3 id="1-pruning-equal-elements">1. &nbsp; Pruning Equal Elements<a class="headerlink" href="#1-pruning-equal-elements" title="Permanent link">&para;</a></h3>
<p>Observe Figure 13-8. In the first round, choosing <span class="arithmatex">\(1\)</span> or choosing <span class="arithmatex">\(\hat{1}\)</span> is equivalent. All permutations generated under these two choices are duplicates. Therefore, we should prune <span class="arithmatex">\(\hat{1}\)</span>.</p>
<p>Similarly, after choosing <span class="arithmatex">\(2\)</span> in the first round, the <span class="arithmatex">\(1\)</span> and <span class="arithmatex">\(\hat{1}\)</span> in the second round also produce duplicate branches, so the second round's <span class="arithmatex">\(\hat{1}\)</span> should also be pruned.</p>
<p>Essentially, <strong>our goal is to ensure that multiple equal elements are chosen only once in a certain round of choices</strong>.</p>
@@ -5023,7 +5023,7 @@
<p align="center"> Figure 13-8 &nbsp; Pruning duplicate permutations </p>
<h3 id="2-code-implementation_1">2. &nbsp; Code Implementation<a class="headerlink" href="#2-code-implementation_1" title="Permanent link">&para;</a></h3>
<p>Building on the code from the previous problem, we consider opening a hash set <code>duplicated</code> in each round of choices to record which elements have been tried in this round, and prune duplicate elements:</p>
<p>Building on the code from the previous problem, we initialize a hash set <code>duplicated</code> in each round of choices to record which elements have already been tried in that round, and prune equal elements:</p>
<div class="tabbed-set tabbed-alternate" data-tabs="2:13"><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" /><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></div>
<div class="tabbed-content">
<div class="tabbed-block">
@@ -5518,7 +5518,7 @@
<p>Note that although both <code>selected</code> and <code>duplicated</code> are used for pruning, they have different objectives.</p>
<ul>
<li><strong>Pruning duplicate choices</strong>: There is only one <code>selected</code> throughout the entire search process. It records which elements are included in the current state, and its purpose is to prevent an element from appearing repeatedly in <code>state</code>.</li>
<li><strong>Pruning duplicate elements</strong>: Each round of choices (each <code>backtrack</code> function call) contains a <code>duplicated</code> set. It records which elements have been chosen in this round's iteration (the <code>for</code> loop), and its purpose is to ensure that equal elements are chosen only once.</li>
<li><strong>Pruning equal elements</strong>: Each round of choices (each <code>backtrack</code> function call) contains a <code>duplicated</code> set. It records which elements have been chosen in this round's iteration (the <code>for</code> loop), and its purpose is to ensure that equal elements are chosen only once.</li>
</ul>
<p>Figure 13-9 shows the effective scope of the two pruning conditions. Note that each node in the tree represents a choice, and the nodes on the path from the root to a leaf node form a permutation.</p>
<p><img alt="Effective scope of two pruning conditions" class="animation-figure" src="../permutations_problem.assets/permutations_ii_pruning_summary.png" /></p>
@@ -6,7 +6,7 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="description" content="Data Structures and Algorithms Crash Course with Animated Illustrations and Off-the-Shelf Code">
<meta name="description" content="Data structures and algorithms tutorial with animated illustrations and ready-to-run code">
<meta name="author" content="krahets">
@@ -576,7 +576,7 @@
<span class="md-ellipsis">
Chapter 1. Encounter With Algorithms
Chapter 1. Encounter with Algorithms
@@ -598,7 +598,7 @@
<span class="md-nav__icon md-icon"></span>
Chapter 1. Encounter With Algorithms
Chapter 1. Encounter with Algorithms
</label>
@@ -1183,7 +1183,7 @@
<span class="md-ellipsis">
Chapter 4. Array and Linked List
Chapter 4. Arrays and Linked Lists
@@ -1205,7 +1205,7 @@
<span class="md-nav__icon md-icon"></span>
Chapter 4. Array and Linked List
Chapter 4. Arrays and Linked Lists
</label>
@@ -1311,7 +1311,7 @@
<span class="md-ellipsis">
4.4 Memory and Cache *
4.4 Random-Access Memory and Cache *
@@ -1402,7 +1402,7 @@
<span class="md-ellipsis">
Chapter 5. Stack and Queue
Chapter 5. Stacks and Queues
@@ -1424,7 +1424,7 @@
<span class="md-nav__icon md-icon"></span>
Chapter 5. Stack and Queue
Chapter 5. Stacks and Queues
</label>
@@ -1502,7 +1502,7 @@
<span class="md-ellipsis">
5.3 Double-Ended Queue
5.3 Deque
@@ -1593,7 +1593,7 @@
<span class="md-ellipsis">
Chapter 6. Hashing
Chapter 6. Hash Table
@@ -1615,7 +1615,7 @@
<span class="md-nav__icon md-icon"></span>
Chapter 6. Hashing
Chapter 6. Hash Table
</label>
@@ -1888,7 +1888,7 @@
<span class="md-ellipsis">
7.3 Array Representation of Tree
7.3 Array Representation of Binary Trees
@@ -2107,7 +2107,7 @@
<span class="md-ellipsis">
8.2 Building a Heap
8.2 Heap Construction Operation
@@ -2135,7 +2135,7 @@
<span class="md-ellipsis">
8.3 Top-K Problem
8.3 Top-k Problem
@@ -2493,7 +2493,7 @@
<span class="md-ellipsis">
10.2 Binary Search Insertion
10.2 Binary Search Insertion Point
@@ -2521,7 +2521,7 @@
<span class="md-ellipsis">
10.3 Binary Search Edge Cases
10.3 Binary Search Boundaries
@@ -2577,7 +2577,7 @@
<span class="md-ellipsis">
10.5 Search Algorithms Revisited
10.5 Searching Algorithms Revisited
@@ -2726,7 +2726,7 @@
<span class="md-ellipsis">
11.1 Sorting Algorithms
11.1 Sorting Algorithm
@@ -3199,7 +3199,7 @@
<span class="md-ellipsis">
12.4 Hanoi Tower Problem
12.4 Hanota Problem
@@ -3460,10 +3460,10 @@
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#1-reference-to-full-permutation-solution" class="md-nav__link">
<a href="#1-using-the-permutation-solution-as-a-reference" class="md-nav__link">
<span class="md-ellipsis">
1. &nbsp; Reference to Full Permutation Solution
1. &nbsp; Using the Permutation Solution as a Reference
</span>
</a>
@@ -4250,7 +4250,7 @@
<span class="md-ellipsis">
16.3 Terminology Table
16.3 Glossary
@@ -4377,10 +4377,10 @@
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#1-reference-to-full-permutation-solution" class="md-nav__link">
<a href="#1-using-the-permutation-solution-as-a-reference" class="md-nav__link">
<span class="md-ellipsis">
1. &nbsp; Reference to Full Permutation Solution
1. &nbsp; Using the Permutation Solution as a Reference
</span>
</a>
@@ -4501,9 +4501,9 @@
<li>Elements in the input set can be selected repeatedly without limit.</li>
<li>Subsets do not distinguish element order; for example, <span class="arithmatex">\(\{4, 5\}\)</span> and <span class="arithmatex">\(\{5, 4\}\)</span> are the same subset.</li>
</ul>
<h3 id="1-reference-to-full-permutation-solution">1. &nbsp; Reference to Full Permutation Solution<a class="headerlink" href="#1-reference-to-full-permutation-solution" title="Permanent link">&para;</a></h3>
<p>Similar to the full permutation problem, we can imagine the process of generating subsets as a series of choices, and update the "sum of elements" in real-time during the selection process. When the sum equals <code>target</code>, we record the subset to the result list.</p>
<p>Unlike the full permutation problem, <strong>elements in this problem's set can be selected unlimited times</strong>, so we do not need to use a <code>selected</code> boolean list to track whether an element has been selected. We can make minor modifications to the full permutation code and initially obtain the solution:</p>
<h3 id="1-using-the-permutation-solution-as-a-reference">1. &nbsp; Using the Permutation Solution as a Reference<a class="headerlink" href="#1-using-the-permutation-solution-as-a-reference" title="Permanent link">&para;</a></h3>
<p>Similar to the permutation problem, we can view the process of generating subsets as the result of a series of choices and update the running sum during the selection process. When the sum equals <code>target</code>, we record the subset in the result list.</p>
<p>Unlike the permutation problem, <strong>elements in this problem can be selected any number of times</strong>, so we do not need to use a <code>selected</code> boolean list to track whether an element has already been selected. With a few small changes to the permutation code, we obtain an initial solution:</p>
<div class="tabbed-set tabbed-alternate" data-tabs="1:13"><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" /><input id="__tabbed_1_5" name="__tabbed_1" type="radio" /><input id="__tabbed_1_6" name="__tabbed_1" type="radio" /><input id="__tabbed_1_7" name="__tabbed_1" type="radio" /><input id="__tabbed_1_8" name="__tabbed_1" type="radio" /><input id="__tabbed_1_9" name="__tabbed_1" type="radio" /><input id="__tabbed_1_10" name="__tabbed_1" type="radio" /><input id="__tabbed_1_11" name="__tabbed_1" type="radio" /><input id="__tabbed_1_12" name="__tabbed_1" type="radio" /><input id="__tabbed_1_13" name="__tabbed_1" type="radio" /><div class="tabbed-labels"><label for="__tabbed_1_1">Python</label><label for="__tabbed_1_2">C++</label><label for="__tabbed_1_3">Java</label><label for="__tabbed_1_4">C#</label><label for="__tabbed_1_5">Go</label><label for="__tabbed_1_6">Swift</label><label for="__tabbed_1_7">JS</label><label for="__tabbed_1_8">TS</label><label for="__tabbed_1_9">Dart</label><label for="__tabbed_1_10">Rust</label><label for="__tabbed_1_11">C</label><label for="__tabbed_1_12">Kotlin</label><label for="__tabbed_1_13">Ruby</label></div>
<div class="tabbed-content">
<div class="tabbed-block">
@@ -4962,7 +4962,7 @@
</div>
</div>
</div>
<p>When we input array <span class="arithmatex">\([3, 4, 5]\)</span> and target element <span class="arithmatex">\(9\)</span> to the above code, the output is <span class="arithmatex">\([3, 3, 3], [4, 5], [5, 4]\)</span>. <strong>Although we successfully find all subsets that sum to <span class="arithmatex">\(9\)</span>, there are duplicate subsets <span class="arithmatex">\([4, 5]\)</span> and <span class="arithmatex">\([5, 4]\)</span></strong>.</p>
<p>Running the above code on array <span class="arithmatex">\([3, 4, 5]\)</span> with target value <span class="arithmatex">\(9\)</span> produces <span class="arithmatex">\([3, 3, 3], [4, 5], [5, 4]\)</span>. <strong>Although we successfully found all subsets that sum to <span class="arithmatex">\(9\)</span>, there are duplicate subsets <span class="arithmatex">\([4, 5]\)</span> and <span class="arithmatex">\([5, 4]\)</span></strong>.</p>
<p>This is because the search process distinguishes the order of selections, but subsets do not distinguish selection order. As shown in Figure 13-10, selecting 4 first and then 5 versus selecting 5 first and then 4 are different branches, but they correspond to the same subset.</p>
<p><img alt="Subset search and boundary pruning" class="animation-figure" src="../subset_sum_problem.assets/subset_sum_i_naive.png" /></p>
<p align="center"> Figure 13-10 &nbsp; Subset search and boundary pruning </p>
@@ -4976,13 +4976,13 @@
<p><strong>We consider deduplication through pruning during the search process</strong>. Observing Figure 13-11, duplicate subsets occur when array elements are selected in different orders, as in the following cases:</p>
<ol>
<li>When the first and second rounds select <span class="arithmatex">\(3\)</span> and <span class="arithmatex">\(4\)</span> respectively, all subsets containing these two elements are generated, denoted as <span class="arithmatex">\([3, 4, \dots]\)</span>.</li>
<li>Afterward, when the first round selects <span class="arithmatex">\(4\)</span>, <strong>the second round should skip <span class="arithmatex">\(3\)</span></strong>, because the subset <span class="arithmatex">\([4, 3, \dots]\)</span> generated by this choice is completely duplicate with the subset generated in step <code>1.</code></li>
<li>Afterward, when the first round selects <span class="arithmatex">\(4\)</span>, <strong>the second round should skip <span class="arithmatex">\(3\)</span></strong>, because the subset <span class="arithmatex">\([4, 3, \dots]\)</span> generated by this choice is an exact duplicate of the subset generated in step <code>1.</code></li>
</ol>
<p>In the search process, each level's choices are tried from left to right, so the rightmost branches are pruned more.</p>
<ol>
<li>The first two rounds select <span class="arithmatex">\(3\)</span> and <span class="arithmatex">\(5\)</span>, generating subset <span class="arithmatex">\([3, 5, \dots]\)</span>.</li>
<li>The first two rounds select <span class="arithmatex">\(4\)</span> and <span class="arithmatex">\(5\)</span>, generating subset <span class="arithmatex">\([4, 5, \dots]\)</span>.</li>
<li>If the first round selects <span class="arithmatex">\(5\)</span>, <strong>the second round should skip <span class="arithmatex">\(3\)</span> and <span class="arithmatex">\(4\)</span></strong>, because subsets <span class="arithmatex">\([5, 3, \dots]\)</span> and <span class="arithmatex">\([5, 4, \dots]\)</span> are completely duplicate with the subsets described in steps <code>1.</code> and <code>2.</code></li>
<li>If the first round selects <span class="arithmatex">\(5\)</span>, <strong>the second round should skip <span class="arithmatex">\(3\)</span> and <span class="arithmatex">\(4\)</span></strong>, because subsets <span class="arithmatex">\([5, 3, \dots]\)</span> and <span class="arithmatex">\([5, 4, \dots]\)</span> are exact duplicates of the subsets described in steps <code>1.</code> and <code>2.</code></li>
</ol>
<p><img alt="Different selection orders leading to duplicate subsets" class="animation-figure" src="../subset_sum_problem.assets/subset_sum_i_pruning.png" /></p>
<p align="center"> Figure 13-11 &nbsp; Different selection orders leading to duplicate subsets </p>
@@ -5488,7 +5488,7 @@
</div>
</div>
</div>
<p>Figure 13-12 shows the complete backtracking process when array <span class="arithmatex">\([3, 4, 5]\)</span> and target element <span class="arithmatex">\(9\)</span> are input to the above code.</p>
<p>Figure 13-12 shows the complete backtracking process produced by running the above code on array <span class="arithmatex">\([3, 4, 5]\)</span> with target value <span class="arithmatex">\(9\)</span>.</p>
<p><img alt="Subset-sum I backtracking process" class="animation-figure" src="../subset_sum_problem.assets/subset_sum_i.png" /></p>
<p align="center"> Figure 13-12 &nbsp; Subset-sum I backtracking process </p>
@@ -5497,13 +5497,13 @@
<p class="admonition-title">Question</p>
<p>Given a positive integer array <code>nums</code> and a target positive integer <code>target</code>, find all possible combinations where the sum of elements in the combination equals <code>target</code>. <strong>The given array may contain duplicate elements, and each element can be selected at most once</strong>. Return these combinations in list form, where the list should not contain duplicate combinations.</p>
</div>
<p>Compared to the previous problem, <strong>the input array in this problem may contain duplicate elements</strong>, which introduces new challenges. For example, given array <span class="arithmatex">\([4, \hat{4}, 5]\)</span> and target element <span class="arithmatex">\(9\)</span>, the output of the existing code is <span class="arithmatex">\([4, 5], [\hat{4}, 5]\)</span>, which contains duplicate subsets.</p>
<p>Compared to the previous problem, <strong>the input array in this problem may contain duplicate elements</strong>, which introduces a new issue. For example, given array <span class="arithmatex">\([4, \hat{4}, 5]\)</span> and target value <span class="arithmatex">\(9\)</span>, the output of the existing code is <span class="arithmatex">\([4, 5], [\hat{4}, 5]\)</span>, which contains duplicate subsets.</p>
<p><strong>The reason for this duplication is that equal elements are selected multiple times in a certain round</strong>. In Figure 13-13, the first round has three choices, two of which are <span class="arithmatex">\(4\)</span>, creating two duplicate search branches that output duplicate subsets. Similarly, the two <span class="arithmatex">\(4\)</span>'s in the second round also produce duplicate subsets.</p>
<p><img alt="Duplicate subsets caused by equal elements" class="animation-figure" src="../subset_sum_problem.assets/subset_sum_ii_repeat.png" /></p>
<p align="center"> Figure 13-13 &nbsp; Duplicate subsets caused by equal elements </p>
<h3 id="1-pruning-equal-elements">1. &nbsp; Pruning Equal Elements<a class="headerlink" href="#1-pruning-equal-elements" title="Permanent link">&para;</a></h3>
<p>To solve this problem, <strong>we need to limit equal elements to be selected only once in each round</strong>. The implementation is quite clever: since the array is already sorted, equal elements are adjacent. This means that in a certain round of selection, if the current element equals the element to its left, it means this element has already been selected, so we skip the current element directly.</p>
<p>To solve this problem, <strong>we need to limit equal elements to be selected only once in each round</strong>. The implementation is quite clever: since the array is already sorted, equal elements are adjacent. This means that in a given round of selection, if the current element equals the element to its left, then the same value has already been chosen in this round, so we skip the current element directly.</p>
<p>At the same time, <strong>this problem specifies that each array element can only be selected once</strong>. Fortunately, we can also use the variable <code>start</code> to satisfy this constraint: after making choice <span class="arithmatex">\(x_{i}\)</span>, set the next round to start traversal from index <span class="arithmatex">\(i + 1\)</span> onwards. This both eliminates duplicate subsets and avoids selecting elements multiple times.</p>
<h3 id="2-code-implementation">2. &nbsp; Code Implementation<a class="headerlink" href="#2-code-implementation" title="Permanent link">&para;</a></h3>
<div class="tabbed-set tabbed-alternate" data-tabs="3:13"><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" /><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></div>
@@ -6062,7 +6062,7 @@
</div>
</div>
</div>
<p>Figure 13-14 shows the backtracking process for array <span class="arithmatex">\([4, 4, 5]\)</span> and target element <span class="arithmatex">\(9\)</span>, which includes four types of pruning operations. Combine the illustration with the code comments to understand the entire search process and how each pruning operation works.</p>
<p>Figure 13-14 shows the backtracking process for array <span class="arithmatex">\([4, 4, 5]\)</span> with target value <span class="arithmatex">\(9\)</span>, which includes four types of pruning operations. Combine the illustration with the code comments to understand the entire search process and how each pruning operation works.</p>
<p><img alt="Subset-sum II backtracking process" class="animation-figure" src="../subset_sum_problem.assets/subset_sum_ii.png" /></p>
<p align="center"> Figure 13-14 &nbsp; Subset-sum II backtracking process </p>
+25 -25
View File
@@ -6,7 +6,7 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="description" content="Data Structures and Algorithms Crash Course with Animated Illustrations and Off-the-Shelf Code">
<meta name="description" content="Data structures and algorithms tutorial with animated illustrations and ready-to-run code">
<meta name="author" content="krahets">
@@ -576,7 +576,7 @@
<span class="md-ellipsis">
Chapter 1. Encounter With Algorithms
Chapter 1. Encounter with Algorithms
@@ -598,7 +598,7 @@
<span class="md-nav__icon md-icon"></span>
Chapter 1. Encounter With Algorithms
Chapter 1. Encounter with Algorithms
</label>
@@ -1183,7 +1183,7 @@
<span class="md-ellipsis">
Chapter 4. Array and Linked List
Chapter 4. Arrays and Linked Lists
@@ -1205,7 +1205,7 @@
<span class="md-nav__icon md-icon"></span>
Chapter 4. Array and Linked List
Chapter 4. Arrays and Linked Lists
</label>
@@ -1311,7 +1311,7 @@
<span class="md-ellipsis">
4.4 Memory and Cache *
4.4 Random-Access Memory and Cache *
@@ -1402,7 +1402,7 @@
<span class="md-ellipsis">
Chapter 5. Stack and Queue
Chapter 5. Stacks and Queues
@@ -1424,7 +1424,7 @@
<span class="md-nav__icon md-icon"></span>
Chapter 5. Stack and Queue
Chapter 5. Stacks and Queues
</label>
@@ -1502,7 +1502,7 @@
<span class="md-ellipsis">
5.3 Double-Ended Queue
5.3 Deque
@@ -1593,7 +1593,7 @@
<span class="md-ellipsis">
Chapter 6. Hashing
Chapter 6. Hash Table
@@ -1615,7 +1615,7 @@
<span class="md-nav__icon md-icon"></span>
Chapter 6. Hashing
Chapter 6. Hash Table
</label>
@@ -1888,7 +1888,7 @@
<span class="md-ellipsis">
7.3 Array Representation of Tree
7.3 Array Representation of Binary Trees
@@ -2107,7 +2107,7 @@
<span class="md-ellipsis">
8.2 Building a Heap
8.2 Heap Construction Operation
@@ -2135,7 +2135,7 @@
<span class="md-ellipsis">
8.3 Top-K Problem
8.3 Top-k Problem
@@ -2493,7 +2493,7 @@
<span class="md-ellipsis">
10.2 Binary Search Insertion
10.2 Binary Search Insertion Point
@@ -2521,7 +2521,7 @@
<span class="md-ellipsis">
10.3 Binary Search Edge Cases
10.3 Binary Search Boundaries
@@ -2577,7 +2577,7 @@
<span class="md-ellipsis">
10.5 Search Algorithms Revisited
10.5 Searching Algorithms Revisited
@@ -2726,7 +2726,7 @@
<span class="md-ellipsis">
11.1 Sorting Algorithms
11.1 Sorting Algorithm
@@ -3199,7 +3199,7 @@
<span class="md-ellipsis">
12.4 Hanoi Tower Problem
12.4 Hanota Problem
@@ -4183,7 +4183,7 @@
<span class="md-ellipsis">
16.3 Terminology Table
16.3 Glossary
@@ -4366,16 +4366,16 @@
<li>The permutation problem aims to find all possible permutations of elements in a given set. We use an array to record whether each element has been selected, thereby pruning search branches that attempt to select the same element repeatedly, ensuring each element is selected exactly once.</li>
<li>In the permutation problem, if the set contains duplicate elements, the final result will contain duplicate permutations. We need to impose a constraint so that equal elements can only be selected once per round, which is typically achieved using a hash set.</li>
<li>The subset-sum problem aims to find all subsets of a given set that sum to a target value. Since the set is unordered but the search process outputs results in all orders, duplicate subsets are generated. We sort the data before backtracking and use a variable to indicate the starting point of each round's traversal, thereby pruning search branches that generate duplicate subsets.</li>
<li>For the subset-sum problem, equal elements in the array produce duplicate sets. We leverage the precondition that the array is sorted by checking whether adjacent elements are equal to implement pruning, ensuring that equal elements can only be selected once per round.</li>
<li>For the subset-sum problem, equal elements in the array produce duplicate subsets. We leverage the precondition that the array is sorted by checking whether adjacent elements are equal to implement pruning, ensuring that equal elements can only be selected once per round.</li>
<li>The <span class="arithmatex">\(n\)</span> queens problem aims to find placements of <span class="arithmatex">\(n\)</span> queens on an <span class="arithmatex">\(n \times n\)</span> chessboard such that no two queens can attack each other. The constraints of this problem include row constraints, column constraints, and main and anti-diagonal constraints. To satisfy row constraints, we adopt a row-by-row placement strategy, ensuring exactly one queen is placed in each row.</li>
<li>The handling of column constraints and diagonal constraints is similar. For column constraints, we use an array to record whether each column has a queen, thereby indicating whether a selected cell is valid. For diagonal constraints, we use two arrays to separately record whether queens exist on each main or anti-diagonal. The challenge lies in finding the row-column index pattern that characterizes cells on the same main (anti-)diagonal.</li>
</ul>
<h3 id="2-q-a">2. &nbsp; Q &amp; A<a class="headerlink" href="#2-q-a" title="Permanent link">&para;</a></h3>
<p><strong>Q</strong>: How should we understand the relationship between backtracking and recursion?</p>
<p>Overall, backtracking is an "algorithm strategy", while recursion is more like a "tool".</p>
<p><strong>Q</strong>: How can we understand the relationship between backtracking and recursion?</p>
<p>Overall, backtracking is an algorithmic strategy, while recursion is better viewed as a tool.</p>
<ul>
<li>The backtracking algorithm is typically implemented based on recursion. However, backtracking is one application scenario of recursion and represents the application of recursion in search problems.</li>
<li>The structure of recursion embodies the "subproblem decomposition" problem-solving paradigm, commonly used to solve problems involving divide-and-conquer, backtracking, and dynamic programming (memoized recursion).</li>
<li>Backtracking is typically implemented with recursion. However, backtracking is only one application of recursion, specifically its use in search problems.</li>
<li>The structure of recursion reflects a problem-solving paradigm based on decomposing a problem into subproblems, and it is commonly used in divide-and-conquer, backtracking, and dynamic programming (memoized recursion).</li>
</ul>
<!-- Source file information -->