mirror of
https://github.com/krahets/hello-algo.git
synced 2026-07-23 03:46:05 +00:00
deploy
This commit is contained in:
@@ -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>
|
||||
@@ -1185,7 +1185,7 @@
|
||||
<span class="md-ellipsis">
|
||||
|
||||
|
||||
Chapter 4. Array and Linked List
|
||||
Chapter 4. Arrays and Linked Lists
|
||||
|
||||
|
||||
|
||||
@@ -1207,7 +1207,7 @@
|
||||
<span class="md-nav__icon md-icon"></span>
|
||||
|
||||
|
||||
Chapter 4. Array and Linked List
|
||||
Chapter 4. Arrays and Linked Lists
|
||||
|
||||
|
||||
</label>
|
||||
@@ -1313,7 +1313,7 @@
|
||||
<span class="md-ellipsis">
|
||||
|
||||
|
||||
4.4 Memory and Cache *
|
||||
4.4 Random-Access Memory and Cache *
|
||||
|
||||
|
||||
|
||||
@@ -1404,7 +1404,7 @@
|
||||
<span class="md-ellipsis">
|
||||
|
||||
|
||||
Chapter 5. Stack and Queue
|
||||
Chapter 5. Stacks and Queues
|
||||
|
||||
|
||||
|
||||
@@ -1426,7 +1426,7 @@
|
||||
<span class="md-nav__icon md-icon"></span>
|
||||
|
||||
|
||||
Chapter 5. Stack and Queue
|
||||
Chapter 5. Stacks and Queues
|
||||
|
||||
|
||||
</label>
|
||||
@@ -1504,7 +1504,7 @@
|
||||
<span class="md-ellipsis">
|
||||
|
||||
|
||||
5.3 Double-Ended Queue
|
||||
5.3 Deque
|
||||
|
||||
|
||||
|
||||
@@ -1595,7 +1595,7 @@
|
||||
<span class="md-ellipsis">
|
||||
|
||||
|
||||
Chapter 6. Hashing
|
||||
Chapter 6. Hash Table
|
||||
|
||||
|
||||
|
||||
@@ -1617,7 +1617,7 @@
|
||||
<span class="md-nav__icon md-icon"></span>
|
||||
|
||||
|
||||
Chapter 6. Hashing
|
||||
Chapter 6. Hash Table
|
||||
|
||||
|
||||
</label>
|
||||
@@ -1890,7 +1890,7 @@
|
||||
<span class="md-ellipsis">
|
||||
|
||||
|
||||
7.3 Array Representation of Tree
|
||||
7.3 Array Representation of Binary Trees
|
||||
|
||||
|
||||
|
||||
@@ -2109,7 +2109,7 @@
|
||||
<span class="md-ellipsis">
|
||||
|
||||
|
||||
8.2 Building a Heap
|
||||
8.2 Heap Construction Operation
|
||||
|
||||
|
||||
|
||||
@@ -2137,7 +2137,7 @@
|
||||
<span class="md-ellipsis">
|
||||
|
||||
|
||||
8.3 Top-K Problem
|
||||
8.3 Top-k Problem
|
||||
|
||||
|
||||
|
||||
@@ -2495,7 +2495,7 @@
|
||||
<span class="md-ellipsis">
|
||||
|
||||
|
||||
10.2 Binary Search Insertion
|
||||
10.2 Binary Search Insertion Point
|
||||
|
||||
|
||||
|
||||
@@ -2523,7 +2523,7 @@
|
||||
<span class="md-ellipsis">
|
||||
|
||||
|
||||
10.3 Binary Search Edge Cases
|
||||
10.3 Binary Search Boundaries
|
||||
|
||||
|
||||
|
||||
@@ -2579,7 +2579,7 @@
|
||||
<span class="md-ellipsis">
|
||||
|
||||
|
||||
10.5 Search Algorithms Revisited
|
||||
10.5 Searching Algorithms Revisited
|
||||
|
||||
|
||||
|
||||
@@ -2728,7 +2728,7 @@
|
||||
<span class="md-ellipsis">
|
||||
|
||||
|
||||
11.1 Sorting Algorithms
|
||||
11.1 Sorting Algorithm
|
||||
|
||||
|
||||
|
||||
@@ -3201,7 +3201,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>
|
||||
@@ -1344,7 +1344,7 @@
|
||||
<span class="md-ellipsis">
|
||||
|
||||
|
||||
Chapter 4. Array and Linked List
|
||||
Chapter 4. Arrays and Linked Lists
|
||||
|
||||
|
||||
|
||||
@@ -1366,7 +1366,7 @@
|
||||
<span class="md-nav__icon md-icon"></span>
|
||||
|
||||
|
||||
Chapter 4. Array and Linked List
|
||||
Chapter 4. Arrays and Linked Lists
|
||||
|
||||
|
||||
</label>
|
||||
@@ -1472,7 +1472,7 @@
|
||||
<span class="md-ellipsis">
|
||||
|
||||
|
||||
4.4 Memory and Cache *
|
||||
4.4 Random-Access Memory and Cache *
|
||||
|
||||
|
||||
|
||||
@@ -1563,7 +1563,7 @@
|
||||
<span class="md-ellipsis">
|
||||
|
||||
|
||||
Chapter 5. Stack and Queue
|
||||
Chapter 5. Stacks and Queues
|
||||
|
||||
|
||||
|
||||
@@ -1585,7 +1585,7 @@
|
||||
<span class="md-nav__icon md-icon"></span>
|
||||
|
||||
|
||||
Chapter 5. Stack and Queue
|
||||
Chapter 5. Stacks and Queues
|
||||
|
||||
|
||||
</label>
|
||||
@@ -1663,7 +1663,7 @@
|
||||
<span class="md-ellipsis">
|
||||
|
||||
|
||||
5.3 Double-Ended Queue
|
||||
5.3 Deque
|
||||
|
||||
|
||||
|
||||
@@ -1754,7 +1754,7 @@
|
||||
<span class="md-ellipsis">
|
||||
|
||||
|
||||
Chapter 6. Hashing
|
||||
Chapter 6. Hash Table
|
||||
|
||||
|
||||
|
||||
@@ -1776,7 +1776,7 @@
|
||||
<span class="md-nav__icon md-icon"></span>
|
||||
|
||||
|
||||
Chapter 6. Hashing
|
||||
Chapter 6. Hash Table
|
||||
|
||||
|
||||
</label>
|
||||
@@ -2049,7 +2049,7 @@
|
||||
<span class="md-ellipsis">
|
||||
|
||||
|
||||
7.3 Array Representation of Tree
|
||||
7.3 Array Representation of Binary Trees
|
||||
|
||||
|
||||
|
||||
@@ -2268,7 +2268,7 @@
|
||||
<span class="md-ellipsis">
|
||||
|
||||
|
||||
8.2 Building a Heap
|
||||
8.2 Heap Construction Operation
|
||||
|
||||
|
||||
|
||||
@@ -2296,7 +2296,7 @@
|
||||
<span class="md-ellipsis">
|
||||
|
||||
|
||||
8.3 Top-K Problem
|
||||
8.3 Top-k Problem
|
||||
|
||||
|
||||
|
||||
@@ -2654,7 +2654,7 @@
|
||||
<span class="md-ellipsis">
|
||||
|
||||
|
||||
10.2 Binary Search Insertion
|
||||
10.2 Binary Search Insertion Point
|
||||
|
||||
|
||||
|
||||
@@ -2682,7 +2682,7 @@
|
||||
<span class="md-ellipsis">
|
||||
|
||||
|
||||
10.3 Binary Search Edge Cases
|
||||
10.3 Binary Search Boundaries
|
||||
|
||||
|
||||
|
||||
@@ -2738,7 +2738,7 @@
|
||||
<span class="md-ellipsis">
|
||||
|
||||
|
||||
10.5 Search Algorithms Revisited
|
||||
10.5 Searching Algorithms Revisited
|
||||
|
||||
|
||||
|
||||
@@ -2887,7 +2887,7 @@
|
||||
<span class="md-ellipsis">
|
||||
|
||||
|
||||
11.1 Sorting Algorithms
|
||||
11.1 Sorting Algorithm
|
||||
|
||||
|
||||
|
||||
@@ -3360,7 +3360,7 @@
|
||||
<span class="md-ellipsis">
|
||||
|
||||
|
||||
12.4 Hanoi Tower Problem
|
||||
12.4 Hanota Problem
|
||||
|
||||
|
||||
|
||||
@@ -4272,7 +4272,7 @@
|
||||
<span class="md-ellipsis">
|
||||
|
||||
|
||||
16.3 Terminology Table
|
||||
16.3 Glossary
|
||||
|
||||
|
||||
|
||||
@@ -4540,7 +4540,7 @@
|
||||
<p><u>Iteration</u> is a control structure for repeatedly executing a task. In iteration, a program repeatedly executes a segment of code under certain conditions until those conditions are no longer satisfied.</p>
|
||||
<h3 id="1-for-loop">1. For Loop<a class="headerlink" href="#1-for-loop" title="Permanent link">¶</a></h3>
|
||||
<p>The <code>for</code> loop is one of the most common forms of iteration, <strong>suitable for use when the number of iterations is known in advance</strong>.</p>
|
||||
<p>The following function implements the summation <span class="arithmatex">\(1 + 2 + \dots + n\)</span> based on a <code>for</code> loop, with the sum result recorded using the variable <code>res</code>. Note that in Python, <code>range(a, b)</code> corresponds to a "left-closed, right-open" interval, with the traversal range being <span class="arithmatex">\(a, a + 1, \dots, b-1\)</span>:</p>
|
||||
<p>The following function implements the summation <span class="arithmatex">\(1 + 2 + \dots + n\)</span> using a <code>for</code> loop, with the result stored in the variable <code>res</code>. Note that in Python, <code>range(a, b)</code> corresponds to a "left-closed, right-open" interval, with the traversal range being <span class="arithmatex">\(a, a + 1, \dots, b-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">
|
||||
@@ -5324,7 +5324,7 @@
|
||||
<p align="center"> Figure 2-2 Flowchart of nested loops </p>
|
||||
|
||||
<p>In this case, the number of operations of the function is proportional to <span class="arithmatex">\(n^2\)</span>, or the algorithm's running time has a "quadratic relationship" with the input data size <span class="arithmatex">\(n\)</span>.</p>
|
||||
<p>We can continue adding nested loops, where each nesting is a "dimension increase", raising the time complexity to "cubic relationship", "quartic relationship", and so on.</p>
|
||||
<p>We can continue adding nested loops, where each additional level of nesting can be viewed as an increase in dimensionality, raising the time complexity to a "cubic relationship", a "quartic relationship", and so on.</p>
|
||||
<h2 id="222-recursion">2.2.2 Recursion<a class="headerlink" href="#222-recursion" title="Permanent link">¶</a></h2>
|
||||
<p><u>Recursion</u> is an algorithmic strategy that solves problems by having a function call itself. It mainly consists of two phases.</p>
|
||||
<ol>
|
||||
@@ -5524,7 +5524,7 @@
|
||||
<li><strong>Recursion</strong>: Decomposes the problem into the subproblem <span class="arithmatex">\(f(n) = n + f(n-1)\)</span>, continuously decomposing (recursively) until terminating at the base case <span class="arithmatex">\(f(1) = 1\)</span>.</li>
|
||||
</ul>
|
||||
<h3 id="1-call-stack">1. Call Stack<a class="headerlink" href="#1-call-stack" title="Permanent link">¶</a></h3>
|
||||
<p>Each time a recursive function calls itself, the system allocates memory for the newly opened function to store local variables, call addresses, and other information. This leads to two consequences.</p>
|
||||
<p>Each time a recursive function calls itself, the system allocates memory for the newly invoked function to store local variables, call addresses, and other information. This leads to two consequences.</p>
|
||||
<ul>
|
||||
<li>The function's context data is stored in a memory area called "stack frame space", which is not released until the function returns. Therefore, <strong>recursion usually consumes more memory space than iteration</strong>.</li>
|
||||
<li>Recursive function calls incur additional overhead. <strong>Therefore, recursion is usually less time-efficient than loops</strong>.</li>
|
||||
@@ -5535,7 +5535,7 @@
|
||||
|
||||
<p>In practice, the recursion depth allowed by programming languages is usually limited, and excessively deep recursion may lead to stack overflow errors.</p>
|
||||
<h3 id="2-tail-recursion">2. Tail Recursion<a class="headerlink" href="#2-tail-recursion" title="Permanent link">¶</a></h3>
|
||||
<p>Interestingly, <strong>if a function makes the recursive call as the very last step before returning</strong>, the function can be optimized by the compiler or interpreter to have space efficiency comparable to iteration. This case is called <u>tail recursion</u>.</p>
|
||||
<p>Interestingly, <strong>if a function makes the recursive call as the very last step before returning</strong>, the compiler or interpreter may optimize it so that its space efficiency is comparable to iteration. This case is called <u>tail recursion</u>.</p>
|
||||
<ul>
|
||||
<li><strong>Regular recursion</strong>: When a function returns to the previous level, it needs to continue executing code, so the system needs to save the context of the previous layer's call.</li>
|
||||
<li><strong>Tail recursion</strong>: The recursive call is the last operation before the function returns, meaning that after returning to the previous level, there is no need to continue executing other operations, so the system does not need to save the context of the previous layer's function.</li>
|
||||
@@ -5687,7 +5687,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p>The execution process of tail recursion is shown in Figure 2-5. Comparing regular recursion and tail recursion, the execution point of the summation operation is different.</p>
|
||||
<p>The execution process of tail recursion is shown in Figure 2-5. Comparing regular recursion and tail recursion, the summation operation is performed at different points.</p>
|
||||
<ul>
|
||||
<li><strong>Regular recursion</strong>: The summation operation is performed during the "ascending" process, requiring an additional summation operation after each layer returns.</li>
|
||||
<li><strong>Tail recursion</strong>: The summation operation is performed during the "descending" process; the "ascending" process only needs to return layer by layer.</li>
|
||||
@@ -5882,7 +5882,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p>Observing the above code, we recursively call two functions within the function, <strong>meaning that one call produces two call branches</strong>. As shown in Figure 2-6, such continuous recursive calling will eventually produce a <u>recursion tree</u> with <span class="arithmatex">\(n\)</span> levels.</p>
|
||||
<p>Observing the above code, we make two recursive calls within the function, <strong>meaning that one call produces two call branches</strong>. As shown in Figure 2-6, this repeated recursive calling eventually produces a <u>recursion tree</u> with <span class="arithmatex">\(n\)</span> levels.</p>
|
||||
<p><img alt="Recursion tree of the Fibonacci sequence" class="animation-figure" src="../iteration_and_recursion.assets/recursion_tree.png" /></p>
|
||||
<p align="center"> Figure 2-6 Recursion tree of the Fibonacci sequence </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>
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -4370,12 +4370,12 @@
|
||||
<p>In short, <strong>our goal is to design data structures and algorithms that are "both fast and memory-efficient"</strong>. Effectively evaluating algorithm efficiency is crucial, because only in this way can we compare various algorithms and guide the algorithm design and optimization process.</p>
|
||||
<p>Efficiency evaluation methods are mainly divided into two types: actual testing and theoretical estimation.</p>
|
||||
<h2 id="211-actual-testing">2.1.1 Actual Testing<a class="headerlink" href="#211-actual-testing" title="Permanent link">¶</a></h2>
|
||||
<p>Suppose we now have algorithm <code>A</code> and algorithm <code>B</code>, both of which can solve the same problem, and we need to compare the efficiency of these two algorithms. The most direct method is to find a computer, run these two algorithms, and monitor and record their running time and memory usage. This evaluation approach can reflect the real situation, but it also has considerable limitations.</p>
|
||||
<p>On one hand, <strong>it is difficult to eliminate interference factors from the testing environment</strong>. Hardware configuration affects the performance of algorithms. For example, if an algorithm has a high degree of parallelism, it is more suitable for running on multi-core CPUs; if an algorithm has intensive memory operations, it will perform better on high-performance memory. In other words, the test results of an algorithm on different machines may be inconsistent. This means we need to test on various machines and calculate average efficiency, which is impractical.</p>
|
||||
<p>Suppose we now have algorithm <code>A</code> and algorithm <code>B</code>, both of which can solve the same problem, and we need to compare their efficiency. The most direct method is to run them on a computer and measure their running time and memory usage. This evaluation approach can reflect real-world behavior, but it also has considerable limitations.</p>
|
||||
<p>On one hand, <strong>it is difficult to eliminate interference factors from the testing environment</strong>. Hardware configuration affects algorithmic performance. For example, if an algorithm has a high degree of parallelism, it is more suitable for running on multi-core CPUs; if an algorithm performs memory-intensive operations, it will benefit more from high-performance memory. In other words, the test results of an algorithm on different machines may be inconsistent. This means we need to test on various machines and calculate average efficiency, which is impractical.</p>
|
||||
<p>On the other hand, <strong>conducting complete testing is very resource-intensive</strong>. As the input data volume changes, the algorithm will exhibit different efficiencies. For example, when the input data volume is small, the running time of algorithm <code>A</code> is shorter than algorithm <code>B</code>; but when the input data volume is large, the test results may be exactly the opposite. Therefore, to obtain convincing conclusions, we need to test input data of various scales, which requires a large amount of computational resources.</p>
|
||||
<h2 id="212-theoretical-estimation">2.1.2 Theoretical Estimation<a class="headerlink" href="#212-theoretical-estimation" title="Permanent link">¶</a></h2>
|
||||
<p>Since actual testing has considerable limitations, we can consider evaluating algorithm efficiency through calculations alone. This estimation method is called <u>asymptotic complexity analysis</u>, or <u>complexity analysis</u> for short.</p>
|
||||
<p>Complexity analysis can reflect the relationship between the time and space resources required for algorithm execution and the input data scale. <strong>It describes the growth trend of the time and space required for algorithm execution as the input data scale increases</strong>. This definition is somewhat convoluted, so we can break it down into three key points to understand.</p>
|
||||
<p>Since actual testing has considerable limitations, we can consider evaluating algorithm efficiency through theoretical calculation. This estimation method is called <u>asymptotic complexity analysis</u>, or <u>complexity analysis</u> for short.</p>
|
||||
<p>Complexity analysis can reflect the relationship between the time and space resources required for algorithm execution and the input data scale. <strong>It describes the growth trend of the time and space required for algorithm execution as the input data scale increases</strong>. This definition is a bit cumbersome, so we can break it down into three key points to understand.</p>
|
||||
<ul>
|
||||
<li>"Time and space resources" correspond to <u>time complexity</u> and <u>space complexity</u>, respectively.</li>
|
||||
<li>"As the input data scale increases" means that complexity reflects the relationship between algorithm running efficiency and input data scale.</li>
|
||||
@@ -4392,8 +4392,8 @@
|
||||
<p>If you are still confused about the concept of complexity, don't worry—we will introduce it in detail in subsequent chapters.</p>
|
||||
</div>
|
||||
<p>Complexity analysis provides us with a "ruler" for evaluating algorithm efficiency, allowing us to measure the time and space resources required to execute a certain algorithm and compare the efficiency between different algorithms.</p>
|
||||
<p>Complexity is a mathematical concept that may be relatively abstract for beginners, with a relatively high learning difficulty. From this perspective, complexity analysis may not be very suitable as the first content to be introduced. However, when we discuss the characteristics of a certain data structure or algorithm, it is difficult to avoid analyzing its running speed and space usage.</p>
|
||||
<p>In summary, it is recommended that before diving deep into data structures and algorithms, <strong>you first establish a preliminary understanding of complexity analysis so that you can complete complexity analysis of simple algorithms</strong>.</p>
|
||||
<p>Complexity is a mathematical concept that may feel abstract and challenging for beginners. From this perspective, complexity analysis may not be the most suitable topic to introduce first. However, when we discuss the characteristics of a certain data structure or algorithm, it is difficult to avoid analyzing its running speed and space usage.</p>
|
||||
<p>In summary, it is recommended that before diving deep into data structures and algorithms, <strong>you first establish a preliminary understanding of complexity analysis so that you can analyze the complexity of simple algorithms</strong>.</p>
|
||||
|
||||
<!-- Source file information -->
|
||||
|
||||
|
||||
@@ -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>
|
||||
@@ -1338,7 +1338,7 @@
|
||||
<span class="md-ellipsis">
|
||||
|
||||
|
||||
Chapter 4. Array and Linked List
|
||||
Chapter 4. Arrays and Linked Lists
|
||||
|
||||
|
||||
|
||||
@@ -1360,7 +1360,7 @@
|
||||
<span class="md-nav__icon md-icon"></span>
|
||||
|
||||
|
||||
Chapter 4. Array and Linked List
|
||||
Chapter 4. Arrays and Linked Lists
|
||||
|
||||
|
||||
</label>
|
||||
@@ -1466,7 +1466,7 @@
|
||||
<span class="md-ellipsis">
|
||||
|
||||
|
||||
4.4 Memory and Cache *
|
||||
4.4 Random-Access Memory and Cache *
|
||||
|
||||
|
||||
|
||||
@@ -1557,7 +1557,7 @@
|
||||
<span class="md-ellipsis">
|
||||
|
||||
|
||||
Chapter 5. Stack and Queue
|
||||
Chapter 5. Stacks and Queues
|
||||
|
||||
|
||||
|
||||
@@ -1579,7 +1579,7 @@
|
||||
<span class="md-nav__icon md-icon"></span>
|
||||
|
||||
|
||||
Chapter 5. Stack and Queue
|
||||
Chapter 5. Stacks and Queues
|
||||
|
||||
|
||||
</label>
|
||||
@@ -1657,7 +1657,7 @@
|
||||
<span class="md-ellipsis">
|
||||
|
||||
|
||||
5.3 Double-Ended Queue
|
||||
5.3 Deque
|
||||
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
<span class="md-ellipsis">
|
||||
|
||||
|
||||
Chapter 6. Hashing
|
||||
Chapter 6. Hash Table
|
||||
|
||||
|
||||
|
||||
@@ -1770,7 +1770,7 @@
|
||||
<span class="md-nav__icon md-icon"></span>
|
||||
|
||||
|
||||
Chapter 6. Hashing
|
||||
Chapter 6. Hash Table
|
||||
|
||||
|
||||
</label>
|
||||
@@ -2043,7 +2043,7 @@
|
||||
<span class="md-ellipsis">
|
||||
|
||||
|
||||
7.3 Array Representation of Tree
|
||||
7.3 Array Representation of Binary Trees
|
||||
|
||||
|
||||
|
||||
@@ -2262,7 +2262,7 @@
|
||||
<span class="md-ellipsis">
|
||||
|
||||
|
||||
8.2 Building a Heap
|
||||
8.2 Heap Construction Operation
|
||||
|
||||
|
||||
|
||||
@@ -2290,7 +2290,7 @@
|
||||
<span class="md-ellipsis">
|
||||
|
||||
|
||||
8.3 Top-K Problem
|
||||
8.3 Top-k Problem
|
||||
|
||||
|
||||
|
||||
@@ -2648,7 +2648,7 @@
|
||||
<span class="md-ellipsis">
|
||||
|
||||
|
||||
10.2 Binary Search Insertion
|
||||
10.2 Binary Search Insertion Point
|
||||
|
||||
|
||||
|
||||
@@ -2676,7 +2676,7 @@
|
||||
<span class="md-ellipsis">
|
||||
|
||||
|
||||
10.3 Binary Search Edge Cases
|
||||
10.3 Binary Search Boundaries
|
||||
|
||||
|
||||
|
||||
@@ -2732,7 +2732,7 @@
|
||||
<span class="md-ellipsis">
|
||||
|
||||
|
||||
10.5 Search Algorithms Revisited
|
||||
10.5 Searching Algorithms Revisited
|
||||
|
||||
|
||||
|
||||
@@ -2881,7 +2881,7 @@
|
||||
<span class="md-ellipsis">
|
||||
|
||||
|
||||
11.1 Sorting Algorithms
|
||||
11.1 Sorting Algorithm
|
||||
|
||||
|
||||
|
||||
@@ -3354,7 +3354,7 @@
|
||||
<span class="md-ellipsis">
|
||||
|
||||
|
||||
12.4 Hanoi Tower Problem
|
||||
12.4 Hanota Problem
|
||||
|
||||
|
||||
|
||||
@@ -4266,7 +4266,7 @@
|
||||
<span class="md-ellipsis">
|
||||
|
||||
|
||||
16.3 Terminology Table
|
||||
16.3 Glossary
|
||||
|
||||
|
||||
|
||||
@@ -4538,7 +4538,7 @@
|
||||
<li><strong>Stack frame space</strong>: Used to save the context data of called functions. The system creates a stack frame at the top of the stack each time a function is called, and the stack frame space is released after the function returns.</li>
|
||||
<li><strong>Instruction space</strong>: Used to save compiled program instructions, which are usually ignored in actual statistics.</li>
|
||||
</ul>
|
||||
<p>When analyzing the space complexity of a program, <strong>we usually count three parts: temporary data, stack frame space, and output data</strong>, as shown in the following figure.</p>
|
||||
<p>When analyzing the space complexity of a program, <strong>we usually consider three parts: temporary data, stack frame space, and output data</strong>, as shown in the following figure.</p>
|
||||
<p><img alt="Algorithm-related space" class="animation-figure" src="../space_complexity.assets/space_types.png" /></p>
|
||||
<p align="center"> Figure 2-15 Algorithm-related space </p>
|
||||
|
||||
@@ -4859,9 +4859,9 @@
|
||||
</div>
|
||||
</div>
|
||||
<h2 id="242-calculation-method">2.4.2 Calculation Method<a class="headerlink" href="#242-calculation-method" title="Permanent link">¶</a></h2>
|
||||
<p>The calculation method for space complexity is roughly the same as for time complexity, except that the statistical object is changed from "number of operations" to "size of space used".</p>
|
||||
<p>The calculation method for space complexity is roughly the same as for time complexity, except that what we measure changes from the "number of operations" to the "amount of space used".</p>
|
||||
<p>Unlike time complexity, <strong>we usually only focus on the worst-case space complexity</strong>. This is because memory space is a hard requirement, and we must ensure that sufficient memory space is reserved for all input data.</p>
|
||||
<p>Observe the following code. The "worst case" in worst-case space complexity has two meanings.</p>
|
||||
<p>Observe the following code. Here, "worst case" in worst-case space complexity has two meanings.</p>
|
||||
<ol>
|
||||
<li><strong>Based on the worst input data</strong>: When <span class="arithmatex">\(n < 10\)</span>, the space complexity is <span class="arithmatex">\(O(1)\)</span>; but when <span class="arithmatex">\(n > 10\)</span>, the initialized array <code>nums</code> occupies <span class="arithmatex">\(O(n)\)</span> space, so the worst-case space complexity is <span class="arithmatex">\(O(n)\)</span>.</li>
|
||||
<li><strong>Based on the peak memory during algorithm execution</strong>: For example, before executing the last line, the program occupies <span class="arithmatex">\(O(1)\)</span> space; when initializing the array <code>nums</code>, the program occupies <span class="arithmatex">\(O(n)\)</span> space, so the worst-case space complexity is <span class="arithmatex">\(O(n)\)</span>.</li>
|
||||
@@ -5261,7 +5261,7 @@ O(1) < O(\log n) < O(n) < O(n^2) < O(2^n) \newline
|
||||
<p align="center"> Figure 2-16 Common types of space complexity </p>
|
||||
|
||||
<h3 id="1-constant-order-o1">1. Constant Order <span class="arithmatex">\(O(1)\)</span><a class="headerlink" href="#1-constant-order-o1" title="Permanent link">¶</a></h3>
|
||||
<p>Constant order is common in constants, variables, and objects whose quantity is independent of the input data size <span class="arithmatex">\(n\)</span>.</p>
|
||||
<p>Constant order is common for constants, variables, and objects whose number is independent of the input data size <span class="arithmatex">\(n\)</span>.</p>
|
||||
<p>It should be noted that memory occupied by initializing variables or calling functions in a loop is released when entering the next iteration, so it does not accumulate space, and the space complexity remains <span class="arithmatex">\(O(1)\)</span>:</p>
|
||||
<div class="tabbed-set tabbed-alternate" data-tabs="4:13"><input checked="checked" id="__tabbed_4_1" name="__tabbed_4" type="radio" /><input id="__tabbed_4_2" name="__tabbed_4" type="radio" /><input id="__tabbed_4_3" name="__tabbed_4" type="radio" /><input id="__tabbed_4_4" name="__tabbed_4" type="radio" /><input id="__tabbed_4_5" name="__tabbed_4" type="radio" /><input id="__tabbed_4_6" name="__tabbed_4" type="radio" /><input id="__tabbed_4_7" name="__tabbed_4" type="radio" /><input id="__tabbed_4_8" name="__tabbed_4" type="radio" /><input id="__tabbed_4_9" name="__tabbed_4" type="radio" /><input id="__tabbed_4_10" name="__tabbed_4" type="radio" /><input id="__tabbed_4_11" name="__tabbed_4" type="radio" /><input id="__tabbed_4_12" name="__tabbed_4" type="radio" /><input id="__tabbed_4_13" name="__tabbed_4" type="radio" /><div class="tabbed-labels"><label for="__tabbed_4_1">Python</label><label for="__tabbed_4_2">C++</label><label for="__tabbed_4_3">Java</label><label for="__tabbed_4_4">C#</label><label for="__tabbed_4_5">Go</label><label for="__tabbed_4_6">Swift</label><label for="__tabbed_4_7">JS</label><label for="__tabbed_4_8">TS</label><label for="__tabbed_4_9">Dart</label><label for="__tabbed_4_10">Rust</label><label for="__tabbed_4_11">C</label><label for="__tabbed_4_12">Kotlin</label><label for="__tabbed_4_13">Ruby</label></div>
|
||||
<div class="tabbed-content">
|
||||
@@ -6500,7 +6500,7 @@ O(1) < O(\log n) < O(n) < O(n^2) < O(2^n) \newline
|
||||
<p>Another example is converting a number to a string. Given a positive integer <span class="arithmatex">\(n\)</span>, it has <span class="arithmatex">\(\lfloor \log_{10} n \rfloor + 1\)</span> digits, i.e., the corresponding string length is <span class="arithmatex">\(\lfloor \log_{10} n \rfloor + 1\)</span>, so the space complexity is <span class="arithmatex">\(O(\log_{10} n + 1) = O(\log n)\)</span>.</p>
|
||||
<h2 id="244-trading-time-for-space">2.4.4 Trading Time for Space<a class="headerlink" href="#244-trading-time-for-space" title="Permanent link">¶</a></h2>
|
||||
<p>Ideally, we hope that both the time complexity and space complexity of an algorithm can reach optimal. However, in practice, optimizing both time complexity and space complexity simultaneously is usually very difficult.</p>
|
||||
<p><strong>Reducing time complexity usually comes at the cost of increasing space complexity, and vice versa</strong>. The approach of sacrificing memory space to improve algorithm execution speed is called "trading space for time"; conversely, it is called "trading time for space".</p>
|
||||
<p><strong>Reducing time complexity usually comes at the cost of increasing space complexity, and vice versa</strong>. Sacrificing memory space to improve execution speed is called "trading space for time"; the reverse is called "trading time for space".</p>
|
||||
<p>The choice of which approach depends on which aspect we value more. In most cases, time is more precious than space, so "trading space for time" is usually the more common strategy. Of course, when the data volume is very large, controlling space complexity is also very important.</p>
|
||||
|
||||
<!-- Source file information -->
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
@@ -1399,7 +1399,7 @@
|
||||
<span class="md-ellipsis">
|
||||
|
||||
|
||||
Chapter 4. Array and Linked List
|
||||
Chapter 4. Arrays and Linked Lists
|
||||
|
||||
|
||||
|
||||
@@ -1421,7 +1421,7 @@
|
||||
<span class="md-nav__icon md-icon"></span>
|
||||
|
||||
|
||||
Chapter 4. Array and Linked List
|
||||
Chapter 4. Arrays and Linked Lists
|
||||
|
||||
|
||||
</label>
|
||||
@@ -1527,7 +1527,7 @@
|
||||
<span class="md-ellipsis">
|
||||
|
||||
|
||||
4.4 Memory and Cache *
|
||||
4.4 Random-Access Memory and Cache *
|
||||
|
||||
|
||||
|
||||
@@ -1618,7 +1618,7 @@
|
||||
<span class="md-ellipsis">
|
||||
|
||||
|
||||
Chapter 5. Stack and Queue
|
||||
Chapter 5. Stacks and Queues
|
||||
|
||||
|
||||
|
||||
@@ -1640,7 +1640,7 @@
|
||||
<span class="md-nav__icon md-icon"></span>
|
||||
|
||||
|
||||
Chapter 5. Stack and Queue
|
||||
Chapter 5. Stacks and Queues
|
||||
|
||||
|
||||
</label>
|
||||
@@ -1718,7 +1718,7 @@
|
||||
<span class="md-ellipsis">
|
||||
|
||||
|
||||
5.3 Double-Ended Queue
|
||||
5.3 Deque
|
||||
|
||||
|
||||
|
||||
@@ -1809,7 +1809,7 @@
|
||||
<span class="md-ellipsis">
|
||||
|
||||
|
||||
Chapter 6. Hashing
|
||||
Chapter 6. Hash Table
|
||||
|
||||
|
||||
|
||||
@@ -1831,7 +1831,7 @@
|
||||
<span class="md-nav__icon md-icon"></span>
|
||||
|
||||
|
||||
Chapter 6. Hashing
|
||||
Chapter 6. Hash Table
|
||||
|
||||
|
||||
</label>
|
||||
@@ -2104,7 +2104,7 @@
|
||||
<span class="md-ellipsis">
|
||||
|
||||
|
||||
7.3 Array Representation of Tree
|
||||
7.3 Array Representation of Binary Trees
|
||||
|
||||
|
||||
|
||||
@@ -2323,7 +2323,7 @@
|
||||
<span class="md-ellipsis">
|
||||
|
||||
|
||||
8.2 Building a Heap
|
||||
8.2 Heap Construction Operation
|
||||
|
||||
|
||||
|
||||
@@ -2351,7 +2351,7 @@
|
||||
<span class="md-ellipsis">
|
||||
|
||||
|
||||
8.3 Top-K Problem
|
||||
8.3 Top-k Problem
|
||||
|
||||
|
||||
|
||||
@@ -2709,7 +2709,7 @@
|
||||
<span class="md-ellipsis">
|
||||
|
||||
|
||||
10.2 Binary Search Insertion
|
||||
10.2 Binary Search Insertion Point
|
||||
|
||||
|
||||
|
||||
@@ -2737,7 +2737,7 @@
|
||||
<span class="md-ellipsis">
|
||||
|
||||
|
||||
10.3 Binary Search Edge Cases
|
||||
10.3 Binary Search Boundaries
|
||||
|
||||
|
||||
|
||||
@@ -2793,7 +2793,7 @@
|
||||
<span class="md-ellipsis">
|
||||
|
||||
|
||||
10.5 Search Algorithms Revisited
|
||||
10.5 Searching Algorithms Revisited
|
||||
|
||||
|
||||
|
||||
@@ -2942,7 +2942,7 @@
|
||||
<span class="md-ellipsis">
|
||||
|
||||
|
||||
11.1 Sorting Algorithms
|
||||
11.1 Sorting Algorithm
|
||||
|
||||
|
||||
|
||||
@@ -3415,7 +3415,7 @@
|
||||
<span class="md-ellipsis">
|
||||
|
||||
|
||||
12.4 Hanoi Tower Problem
|
||||
12.4 Hanota Problem
|
||||
|
||||
|
||||
|
||||
@@ -4327,7 +4327,7 @@
|
||||
<span class="md-ellipsis">
|
||||
|
||||
|
||||
16.3 Terminology Table
|
||||
16.3 Glossary
|
||||
|
||||
|
||||
|
||||
@@ -4827,7 +4827,7 @@
|
||||
<div class="arithmatex">\[
|
||||
1 + 1 + 10 + (1 + 5) \times n = 6n + 12
|
||||
\]</div>
|
||||
<p>In reality, however, <strong>counting an algorithm's runtime is neither reasonable nor realistic</strong>. First, we do not want to tie the estimated time to the running platform, because algorithms need to run on various different platforms. Second, it is difficult to know the runtime of each type of operation, which brings great difficulty to the estimation process.</p>
|
||||
<p>In reality, however, <strong>trying to count an algorithm's exact runtime is neither practical nor realistic</strong>. First, we do not want to tie the estimated time to the running platform, because algorithms need to run on many different platforms. Second, it is difficult to know the runtime of each type of operation, which makes the estimation process extremely difficult.</p>
|
||||
<h2 id="231-counting-time-growth-trends">2.3.1 Counting Time Growth Trends<a class="headerlink" href="#231-counting-time-growth-trends" title="Permanent link">¶</a></h2>
|
||||
<p>Time complexity analysis does not count the algorithm's runtime, <strong>but rather counts the growth trend of the algorithm's runtime as the data volume increases</strong>.</p>
|
||||
<p>The concept of "time growth trend" is rather abstract; let us understand it through an example. Suppose the input data size is <span class="arithmatex">\(n\)</span>, and given three algorithms <code>A</code>, <code>B</code>, and <code>C</code>:</p>
|
||||
@@ -5089,7 +5089,7 @@
|
||||
<p>Compared to directly counting the algorithm's runtime, what are the characteristics of time complexity analysis?</p>
|
||||
<ul>
|
||||
<li><strong>Time complexity can effectively evaluate algorithm efficiency</strong>. For example, the runtime of algorithm <code>B</code> grows linearly; when <span class="arithmatex">\(n > 1\)</span> it is slower than algorithm <code>A</code>, and when <span class="arithmatex">\(n > 1000000\)</span> it is slower than algorithm <code>C</code>. In fact, as long as the input data size <span class="arithmatex">\(n\)</span> is sufficiently large, an algorithm with "constant order" complexity will always be superior to one with "linear order" complexity, which is precisely the meaning of time growth trend.</li>
|
||||
<li><strong>The derivation method for time complexity is simpler</strong>. Obviously, the running platform and the types of computational operations are both unrelated to the growth trend of the algorithm's runtime. Therefore, in time complexity analysis, we can simply treat the execution time of all computational operations as the same "unit time", thus simplifying "counting computational operation runtime" to "counting the number of computational operations", which greatly reduces the difficulty of estimation.</li>
|
||||
<li><strong>The derivation method for time complexity is simpler</strong>. Obviously, the running platform and the types of computational operations are both unrelated to the growth trend of the algorithm's runtime. Therefore, in time complexity analysis, we can simply treat the execution time of all computational operations as the same "unit time", reducing "tracking the runtime of each operation" to "counting the number of operations", which greatly reduces the difficulty of estimation.</li>
|
||||
<li><strong>Time complexity also has certain limitations</strong>. For example, although algorithms <code>A</code> and <code>C</code> have the same time complexity, their actual runtimes differ significantly. Similarly, although algorithm <code>B</code> has a higher time complexity than <code>C</code>, when the input data size <span class="arithmatex">\(n\)</span> is small, algorithm <code>B</code> is clearly superior to algorithm <code>C</code>. In such cases, it is often difficult to judge the efficiency of algorithms based solely on time complexity. Of course, despite the above issues, complexity analysis remains the most effective and commonly used method for evaluating algorithm efficiency.</li>
|
||||
</ul>
|
||||
<h2 id="232-asymptotic-upper-bound-of-functions">2.3.2 Asymptotic Upper Bound of Functions<a class="headerlink" href="#232-asymptotic-upper-bound-of-functions" title="Permanent link">¶</a></h2>
|
||||
@@ -5269,7 +5269,7 @@ T(n) = 3 + 2n
|
||||
<p align="center"> Figure 2-8 Asymptotic upper bound of a function </p>
|
||||
|
||||
<h2 id="233-derivation-method">2.3.3 Derivation Method<a class="headerlink" href="#233-derivation-method" title="Permanent link">¶</a></h2>
|
||||
<p>The asymptotic upper bound has a bit of mathematical flavor. If you feel you haven't fully understood it, don't worry. We can first master the derivation method, and gradually grasp its mathematical meaning through continuous practice.</p>
|
||||
<p>The idea of an asymptotic upper bound is somewhat mathematical. If you feel you haven't fully understood it, don't worry. We can first master the derivation method, and gradually grasp its mathematical meaning through continuous practice.</p>
|
||||
<p>According to the definition, after determining <span class="arithmatex">\(f(n)\)</span>, we can obtain the time complexity <span class="arithmatex">\(O(f(n))\)</span>. So how do we determine the asymptotic upper bound <span class="arithmatex">\(f(n)\)</span>? Overall, it is divided into two steps: first count the number of operations, then determine the asymptotic upper bound.</p>
|
||||
<h3 id="1-step-1-count-the-number-of-operations">1. Step 1: Count the Number of Operations<a class="headerlink" href="#1-step-1-count-the-number-of-operations" title="Permanent link">¶</a></h3>
|
||||
<p>For code, count from top to bottom line by line. However, since the constant coefficient <span class="arithmatex">\(c\)</span> in <span class="arithmatex">\(c \cdot f(n)\)</span> above can be of any size, <strong>coefficients and constant terms in the number of operations <span class="arithmatex">\(T(n)\)</span> can all be ignored</strong>. According to this principle, the following counting simplification techniques can be summarized.</p>
|
||||
@@ -5556,7 +5556,7 @@ O(1) < O(\log n) < O(n) < O(n \log n) < O(n^2) < O(2^n) < O(n!
|
||||
|
||||
<h3 id="1-constant-order-o1">1. Constant Order <span class="arithmatex">\(O(1)\)</span><a class="headerlink" href="#1-constant-order-o1" title="Permanent link">¶</a></h3>
|
||||
<p>The number of operations in constant order is independent of the input data size <span class="arithmatex">\(n\)</span>, meaning it does not change as <span class="arithmatex">\(n\)</span> changes.</p>
|
||||
<p>In the following function, although the number of operations <code>size</code> may be large, since it is independent of the input data size <span class="arithmatex">\(n\)</span>, the time complexity remains <span class="arithmatex">\(O(1)\)</span>:</p>
|
||||
<p>In the following function, although the value of <code>size</code> may be large, it is independent of the input data size <span class="arithmatex">\(n\)</span>, so the time complexity remains <span class="arithmatex">\(O(1)\)</span>:</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">
|
||||
<div class="tabbed-block">
|
||||
@@ -7104,7 +7104,7 @@ O(1) < O(\log n) < O(n) < O(n \log n) < O(n^2) < O(2^n) < O(n!
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p>Logarithmic order commonly appears in algorithms based on the divide-and-conquer strategy, embodying the algorithmic thinking of "dividing into many" and "simplifying complexity". It grows slowly and is the ideal time complexity second only to constant order.</p>
|
||||
<p>Logarithmic order commonly appears in algorithms based on the divide-and-conquer strategy, reflecting the idea of repeatedly splitting a problem and simplifying it. It grows slowly and is the ideal time complexity second only to constant order.</p>
|
||||
<div class="admonition tip">
|
||||
<p class="admonition-title">What is the base of <span class="arithmatex">\(O(\log n)\)</span>?</p>
|
||||
<p>To be precise, "dividing into <span class="arithmatex">\(m\)</span>" corresponds to a time complexity of <span class="arithmatex">\(O(\log_m n)\)</span>. And through the logarithmic base change formula, we can obtain time complexities with different bases that are equal:</p>
|
||||
@@ -7842,7 +7842,7 @@ n! = n \times (n - 1) \times (n - 2) \times \dots \times 2 \times 1
|
||||
</div>
|
||||
</div>
|
||||
<p>It is worth noting that we rarely use best-case time complexity in practice, because it can usually only be achieved with a very small probability and may be somewhat misleading. <strong>The worst-case time complexity is more practical because it gives a safety value for efficiency</strong>, allowing us to use the algorithm with confidence.</p>
|
||||
<p>From the above example, we can see that both worst-case and best-case time complexities only occur under "special data distributions", which may have a very small probability of occurrence and may not truly reflect the algorithm's running efficiency. In contrast, <strong>average time complexity can reflect the algorithm's running efficiency under random input data</strong>, denoted using the <span class="arithmatex">\(\Theta\)</span> notation.</p>
|
||||
<p>From the above example, we can see that both worst-case and best-case time complexities arise only under particular input distributions, which may occur with very low probability and may not truly reflect the algorithm's running efficiency. In contrast, <strong>average time complexity can reflect the algorithm's running efficiency under random input data</strong>, denoted using the <span class="arithmatex">\(\Theta\)</span> notation.</p>
|
||||
<p>For some algorithms, we can simply derive the average case under random data distribution. For example, in the above example, since the input array is shuffled, the probability of element <span class="arithmatex">\(1\)</span> appearing at any index is equal, so the algorithm's average number of loops is half the array length <span class="arithmatex">\(n / 2\)</span>, giving an average time complexity of <span class="arithmatex">\(\Theta(n / 2) = \Theta(n)\)</span>.</p>
|
||||
<p>But for more complex algorithms, calculating average time complexity is often quite difficult, because it is hard to analyze the overall mathematical expectation under data distribution. In this case, we usually use worst-case time complexity as the criterion for judging algorithm efficiency.</p>
|
||||
<div class="admonition question">
|
||||
|
||||
Reference in New Issue
Block a user