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>
@@ -1255,7 +1255,7 @@
<span class="md-ellipsis">
Chapter 4. Array and Linked List
Chapter 4. Arrays and Linked Lists
@@ -1277,7 +1277,7 @@
<span class="md-nav__icon md-icon"></span>
Chapter 4. Array and Linked List
Chapter 4. Arrays and Linked Lists
</label>
@@ -1383,7 +1383,7 @@
<span class="md-ellipsis">
4.4 Memory and Cache *
4.4 Random-Access Memory and Cache *
@@ -1474,7 +1474,7 @@
<span class="md-ellipsis">
Chapter 5. Stack and Queue
Chapter 5. Stacks and Queues
@@ -1496,7 +1496,7 @@
<span class="md-nav__icon md-icon"></span>
Chapter 5. Stack and Queue
Chapter 5. Stacks and Queues
</label>
@@ -1574,7 +1574,7 @@
<span class="md-ellipsis">
5.3 Double-Ended Queue
5.3 Deque
@@ -1665,7 +1665,7 @@
<span class="md-ellipsis">
Chapter 6. Hashing
Chapter 6. Hash Table
@@ -1687,7 +1687,7 @@
<span class="md-nav__icon md-icon"></span>
Chapter 6. Hashing
Chapter 6. Hash Table
</label>
@@ -1960,7 +1960,7 @@
<span class="md-ellipsis">
7.3 Array Representation of Tree
7.3 Array Representation of Binary Trees
@@ -2179,7 +2179,7 @@
<span class="md-ellipsis">
8.2 Building a Heap
8.2 Heap Construction Operation
@@ -2207,7 +2207,7 @@
<span class="md-ellipsis">
8.3 Top-K Problem
8.3 Top-k Problem
@@ -2565,7 +2565,7 @@
<span class="md-ellipsis">
10.2 Binary Search Insertion
10.2 Binary Search Insertion Point
@@ -2593,7 +2593,7 @@
<span class="md-ellipsis">
10.3 Binary Search Edge Cases
10.3 Binary Search Boundaries
@@ -2649,7 +2649,7 @@
<span class="md-ellipsis">
10.5 Search Algorithms Revisited
10.5 Searching Algorithms Revisited
@@ -2798,7 +2798,7 @@
<span class="md-ellipsis">
11.1 Sorting Algorithms
11.1 Sorting Algorithm
@@ -3271,7 +3271,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
@@ -4362,16 +4362,16 @@
<ul>
<li>Time efficiency and space efficiency are the two primary evaluation metrics for measuring algorithm performance.</li>
<li>We can evaluate algorithm efficiency through actual testing, but it is difficult to eliminate the influence of the testing environment, and it consumes substantial computational resources.</li>
<li>Complexity analysis can eliminate the drawbacks of actual testing, with results applicable to all running platforms, and it can reveal algorithm efficiency under different data scales.</li>
<li>Complexity analysis can overcome the limitations of actual testing. Its results apply across running platforms, and it can reveal algorithm efficiency under different data scales.</li>
</ul>
<p><strong>Time Complexity</strong></p>
<ul>
<li>Time complexity is used to measure the trend of algorithm runtime as data volume increases. It can effectively evaluate algorithm efficiency, but may fail in certain situations, such as when the input data volume is small or when time complexities are identical, making it impossible to precisely compare algorithm efficiency.</li>
<li>Time complexity is used to measure the trend of algorithm runtime as data volume increases. It can effectively evaluate algorithm efficiency, but it may be less informative in certain situations, such as when the input data volume is small or when time complexities are identical, making it impossible to precisely compare algorithm efficiency.</li>
<li>Worst-case time complexity is represented using Big <span class="arithmatex">\(O\)</span> notation, corresponding to the asymptotic upper bound of a function, reflecting the growth level of the number of operations <span class="arithmatex">\(T(n)\)</span> as <span class="arithmatex">\(n\)</span> approaches positive infinity.</li>
<li>Deriving time complexity involves two steps: first, counting the number of operations, then determining the asymptotic upper bound.</li>
<li>Common time complexities arranged from low to high include <span class="arithmatex">\(O(1)\)</span>, <span class="arithmatex">\(O(\log n)\)</span>, <span class="arithmatex">\(O(n)\)</span>, <span class="arithmatex">\(O(n \log n)\)</span>, <span class="arithmatex">\(O(n^2)\)</span>, <span class="arithmatex">\(O(2^n)\)</span>, and <span class="arithmatex">\(O(n!)\)</span>.</li>
<li>The time complexity of some algorithms is not fixed, but rather depends on the distribution of input data. Time complexity is divided into worst-case, best-case, and average-case time complexity. Best-case time complexity is rarely used because input data generally needs to satisfy strict conditions to achieve the best case.</li>
<li>Average time complexity reflects the algorithm's runtime efficiency under random data input, and is closest to the algorithm's performance in practical applications. Calculating average time complexity requires statistical analysis of input data distribution and the combined mathematical expectation.</li>
<li>Average time complexity reflects the algorithm's runtime efficiency under random data input, and is closest to the algorithm's performance in practical applications. Calculating average time complexity requires analyzing the input data distribution and the resulting mathematical expectation.</li>
</ul>
<p><strong>Space Complexity</strong></p>
<ul>
@@ -4384,7 +4384,7 @@
<p><strong>Q</strong>: Is the space complexity of tail recursion <span class="arithmatex">\(O(1)\)</span>?</p>
<p>Theoretically, the space complexity of tail recursive functions can be optimized to <span class="arithmatex">\(O(1)\)</span>. However, most programming languages (such as Java, Python, C++, Go, C#, etc.) do not support automatic tail recursion optimization, so the space complexity is generally considered to be <span class="arithmatex">\(O(n)\)</span>.</p>
<p><strong>Q</strong>: What is the difference between the terms function and method?</p>
<p>A <u>function</u> can be executed independently, with all parameters passed explicitly. A <u>method</u> is associated with an object, is implicitly passed to the object that invokes it, and can operate on data contained in class instances.</p>
<p>A <u>function</u> can be executed independently, with all parameters passed explicitly. A <u>method</u> is associated with an object, is implicitly bound to the object that invokes it, and can operate on data contained in class instances.</p>
<p>The following examples use several common programming languages for illustration.</p>
<ul>
<li>C is a procedural programming language without object-oriented concepts, so it only has functions. However, we can simulate object-oriented programming by creating structures (struct), and functions associated with structures are equivalent to methods in other programming languages.</li>
@@ -4394,7 +4394,7 @@
<p><strong>Q</strong>: Does the diagram for "common space complexity types" reflect the absolute size of occupied space?</p>
<p>No, the diagram shows space complexity, which reflects growth trends rather than the absolute size of occupied space.</p>
<p>Assuming <span class="arithmatex">\(n = 8\)</span>, you might find that the values of each curve do not correspond to the functions. This is because each curve contains a constant term used to compress the value range into a visually comfortable range.</p>
<p>In practice, because we generally do not know what the "constant term" complexity of each method is, we usually cannot select the optimal solution for <span class="arithmatex">\(n = 8\)</span> based on complexity alone. But for <span class="arithmatex">\(n = 8^5\)</span>, the choice is straightforward, as the growth trend already dominates.</p>
<p>In practice, because we generally do not know the "constant-term" cost of each method, we usually cannot choose the optimal solution for cases like <span class="arithmatex">\(n = 8\)</span> based on complexity alone. But for <span class="arithmatex">\(n = 8^5\)</span>, the choice is straightforward, because the growth trend already dominates.</p>
<p><strong>Q</strong>: Are there situations where algorithms are designed to sacrifice time (or space) based on actual use cases?</p>
<p>In practical applications, most situations choose to sacrifice space for time. For example, with database indexes, we typically choose to build B+ trees or hash indexes, occupying substantial memory space in exchange for efficient queries of <span class="arithmatex">\(O(\log n)\)</span> or even <span class="arithmatex">\(O(1)\)</span>.</p>
<p>In scenarios where space resources are precious, time may be sacrificed for space. For example, in embedded development, device memory is precious, and engineers may forgo using hash tables and choose to use array sequential search to save memory usage, at the cost of slower searches.</p>