mirror of
https://github.com/krahets/hello-algo.git
synced 2026-08-18 22:40:59 +00:00
Fix multilingual content typos (#1958)
This commit is contained in:
@@ -80,7 +80,9 @@ The following table lists important terms that appear in this book.
|
||||
| level-order traversal |
|
||||
| breadth-first traversal |
|
||||
| depth-first traversal |
|
||||
| binary search tree |
|
||||
| pre-order traversal |
|
||||
| in-order traversal |
|
||||
| post-order traversal |
|
||||
| balanced binary search tree |
|
||||
| balance factor |
|
||||
| heap |
|
||||
|
||||
@@ -166,7 +166,7 @@ As shown in the following code, a linked list node `ListNode` contains not only
|
||||
// Constructor
|
||||
class ListNode(x: Int) {
|
||||
val _val: Int = x // Node value
|
||||
val next: ListNode? = null // Reference to the next node
|
||||
var next: ListNode? = null // Reference to the next node
|
||||
}
|
||||
```
|
||||
|
||||
@@ -658,8 +658,8 @@ As shown in the figure below, there are three common types of linked lists:
|
||||
// Constructor
|
||||
class ListNode(x: Int) {
|
||||
val _val: Int = x // Node value
|
||||
val next: ListNode? = null // Reference to the successor node
|
||||
val prev: ListNode? = null // Reference to the predecessor node
|
||||
var next: ListNode? = null // Reference to the successor node
|
||||
var prev: ListNode? = null // Reference to the predecessor node
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -21,12 +21,12 @@ Based on the above analysis, this problem can be solved using divide and conquer
|
||||
According to the definition, both `preorder` and `inorder` can be divided into three parts.
|
||||
|
||||
- Preorder traversal: `[ Root Node | Left Subtree | Right Subtree ]`, for example, the tree in the figure above corresponds to `[ 3 | 9 | 2 1 7 ]`.
|
||||
- Inorder traversal: `[ Left Subtree | Root Node | Right Subtree ]`, for example, the tree in the figure above corresponds to `[ 9 | 3 | 1 2 7 ]`.
|
||||
- Inorder traversal: `[ Left Subtree | Root Node | Right Subtree ]`, for example, the tree in the figure above corresponds to `[ 9 | 3 | 1 2 7 ]`.
|
||||
|
||||
Using the data from the figure above as an example, we can obtain the division results through the steps shown in the figure below.
|
||||
|
||||
1. The first element 3 in the preorder traversal is the value of the root node.
|
||||
2. Find the index of root node 3 in `inorder`, and use this index to divide `inorder` into `[ 9 | 3 | 1 2 7 ]`.
|
||||
2. Find the index of root node 3 in `inorder`, and use this index to divide `inorder` into `[ 9 | 3 | 1 2 7 ]`.
|
||||
3. Based on the division result of `inorder`, it is easy to determine that the left and right subtrees have 1 and 3 nodes respectively, allowing us to divide `preorder` into `[ 3 | 9 | 2 1 7 ]`.
|
||||
|
||||

|
||||
|
||||
@@ -35,7 +35,7 @@ In other words, each round of decision (edit operation) we make on string $s$ wi
|
||||
|
||||
State $[i, j]$ corresponds to the subproblem: **the minimum number of edits required to change the first $i$ characters of $s$ into the first $j$ characters of $t$**.
|
||||
|
||||
From this, we obtain a two-dimensional $dp$ table of size $(i+1) \times (j+1)$.
|
||||
From this, we obtain a two-dimensional $dp$ table of size $(n+1) \times (m+1)$.
|
||||
|
||||
**Step 2: Identify the optimal substructure, and then derive the state transition equation**
|
||||
|
||||
|
||||
@@ -134,7 +134,7 @@ As shown in the figure below, both time complexity and space complexity are dete
|
||||
|
||||
### Space Optimization
|
||||
|
||||
Since each state is only related to the state in the row above it, we can use two arrays rolling forward to reduce the space complexity from $O(n^2)$ to $O(n)$.
|
||||
Since each state is only related to the state in the row above it, we can use two arrays rolling forward to reduce the space complexity from $O(n \times cap)$ to $O(cap)$.
|
||||
|
||||
Further thinking, can we achieve space optimization using just one array? Observing, we can see that each state is transferred from the cell directly above or the cell in the upper-left. If there is only one array, when we start traversing row $i$, that array still stores the state of row $i-1$.
|
||||
|
||||
|
||||
@@ -21,5 +21,5 @@
|
||||
**Edit distance problem**
|
||||
|
||||
- Edit distance (Levenshtein distance) is used to measure the similarity between two strings, defined as the minimum number of edit steps from one string to another, with edit operations including insert, delete, and replace.
|
||||
- The state definition for the edit distance problem is the minimum number of edit steps required to change the first $i$ characters of $s$ into the first $j$ characters of $t$. When $s[i] \ne t[j]$, there are three decisions: insert, delete, replace, each with corresponding remaining subproblems. From this, the optimal substructure can be identified and the state transition equation constructed. When $s[i] = t[j]$, no edit is required for the current character.
|
||||
- The state definition for the edit distance problem is the minimum number of edit steps required to change the first $i$ characters of $s$ into the first $j$ characters of $t$. When $s[i-1] \ne t[j-1]$, there are three decisions: insert, delete, replace, each with corresponding remaining subproblems. From this, the optimal substructure can be identified and the state transition equation constructed. When $s[i-1] = t[j-1]$, no edit is required for the current character.
|
||||
- In edit distance, the state depends on the state directly above, directly to the left, and to the upper-left, so after space optimization, neither forward nor reverse traversal can correctly perform state transitions. For this reason, we use a variable to temporarily store the upper-left state, thus transforming to a situation equivalent to the unbounded knapsack problem, allowing for forward traversal after space optimization.
|
||||
|
||||
@@ -91,4 +91,4 @@ Greedy algorithms are often applied to optimization problems that satisfy greedy
|
||||
- **Fractional knapsack problem**: Given a set of items and a carrying capacity, your goal is to select a set of items such that the total weight does not exceed the carrying capacity and the total value is maximized. If you always choose the item with the highest value-to-weight ratio (value / weight), then the greedy algorithm can obtain the optimal solution in some cases.
|
||||
- **Stock trading problem**: Given a set of historical stock prices, you can make multiple trades, but if you already hold stocks, you cannot buy again before selling, and the goal is to obtain the maximum profit.
|
||||
- **Huffman coding**: Huffman coding is a greedy algorithm used for lossless data compression. By constructing a Huffman tree and always merging the two nodes with the lowest frequency, the resulting Huffman tree has the minimum weighted path length (encoding length).
|
||||
- **Dijkstra's algorithm**: It is a greedy algorithm for solving the shortest path problem from a given source vertex to all other vertices.
|
||||
- **Dijkstra's algorithm**: For graphs with non-negative edge weights, it is a greedy algorithm for solving the shortest path problem from a given source vertex to all other vertices.
|
||||
|
||||
@@ -31,7 +31,7 @@ It's worth noting that **since leaf nodes have no children, they are naturally v
|
||||
|
||||
Next, let's attempt to derive the time complexity of this second heap construction method.
|
||||
|
||||
- Assuming the complete binary tree has $n$ nodes, then the number of leaf nodes is $(n + 1) / 2$, where $/$ is floor division. Therefore, the number of nodes that need heapification is $(n - 1) / 2$.
|
||||
- Assuming the complete binary tree has $n$ nodes, then the number of leaf nodes is $(n + 1) / 2$, where $/$ is floor division. Therefore, the number of nodes that need heapification is $n / 2$.
|
||||
- In the top-to-bottom heapify process, each node can sink at most to a leaf node, so the maximum number of iterations is the height of the binary tree, $\log n$.
|
||||
|
||||
Multiplying these two together, we get a time complexity of $O(n \log n)$ for the heap construction process. **However, this estimate is not accurate because it doesn't account for the property that binary trees have far more nodes at lower levels than at upper levels**.
|
||||
|
||||
@@ -41,7 +41,7 @@ Note that the returned insertion point is $i$, so we need to subtract $1$ from i
|
||||
|
||||
### Converting to Element Search
|
||||
|
||||
We know that when the array does not contain `target`, $i$ and $j$ will eventually point to the first elements greater than and less than `target`, respectively.
|
||||
We know that when the array does not contain `target`, $i$ and $j$ will eventually point to the first element greater than `target` and the rightmost element less than `target`, respectively.
|
||||
|
||||
Therefore, as shown in the figure below, we can construct an element that does not exist in the array to find the left and right boundaries.
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ The problem requires inserting `target` to the left of equal elements, which mea
|
||||
|
||||
To analyze this further, consider the binary search process: when `nums[m] < target`, $i$ moves, meaning that pointer $i$ is approaching elements greater than or equal to `target`. Similarly, pointer $j$ is always approaching elements less than or equal to `target`.
|
||||
|
||||
Therefore, when the binary search ends, $i$ must point to the first element greater than `target`, and $j$ must point to the first element less than `target`. **It follows that when the array does not contain `target`, the insertion index is $i$**. The code is shown below:
|
||||
Therefore, when the binary search ends, $i$ must point to the first element greater than `target`, and $j$ must point to the rightmost element less than `target`. **It follows that when the array does not contain `target`, the insertion index is $i$**. The code is shown below:
|
||||
|
||||
```src
|
||||
[file]{binary_search_insertion}-[class]{}-[func]{binary_search_insertion_simple}
|
||||
@@ -48,7 +48,7 @@ Now consider extending the binary search code. As shown in the figure below, the
|
||||
- When `nums[m] < target` or `nums[m] > target`, it means `target` has not been found yet, so use the standard interval-shrinking operation of binary search to **move pointers $i$ and $j$ closer to `target`**.
|
||||
- When `nums[m] == target`, it means elements less than `target` are in the interval $[i, m - 1]$, so use $j = m - 1$ to shrink the interval, thereby **moving pointer $j$ closer to elements less than `target`**.
|
||||
|
||||
After the loop completes, $i$ points to the leftmost `target`, and $j$ points to the first element less than `target`, **so index $i$ is the insertion point**.
|
||||
After the loop completes, $i$ points to the leftmost `target`, and $j$ points to the rightmost element less than `target`, **so index $i$ is the insertion point**.
|
||||
|
||||
=== "<1>"
|
||||

|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Bucket Sort
|
||||
|
||||
The sorting algorithms discussed earlier are all comparison-based sorting algorithms, which sort by comparing the relative order of elements. The time complexity of such algorithms cannot beat $O(n \log n)$. Next, we will explore several non-comparison sorting algorithms, whose time complexity can be linear.
|
||||
The sorting algorithms discussed earlier are all comparison-based sorting algorithms, which sort by comparing the relative order of elements. The worst-case time complexity of such algorithms has a lower bound of $\Omega(n \log n)$. Next, we will explore several non-comparison sorting algorithms, whose time complexity can be linear.
|
||||
|
||||
<u>Bucket sort</u> is a typical application of the divide-and-conquer strategy. It works by creating a sequence of ordered buckets, each corresponding to a data range, and distributing the data evenly among them. The elements within each bucket are then sorted separately. Finally, all buckets are merged in order.
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ In the code, we use $k$ to track the smallest element within the unsorted interv
|
||||
|
||||
## Algorithm Characteristics
|
||||
|
||||
- **Time complexity $O(n^2)$, non-adaptive sorting**: The outer loop has $n - 1$ rounds in total. The length of the unsorted interval in the first round is $n$, and the length of the unsorted interval in the last round is $2$. That is, the rounds of the outer loop contain inner loops with $n$, $n - 1$, $\dots$, $3$, and $2$ iterations, summing to $\frac{(n - 1)(n + 2)}{2}$.
|
||||
- **Time complexity $O(n^2)$, non-adaptive sorting**: The outer loop has $n - 1$ rounds in total. The inner loop runs $n - 1$ times in the first round and $1$ time in the last round. Thus, it runs $n - 1$, $n - 2$, $\dots$, $2$, and $1$ times across the rounds, summing to $\frac{n(n - 1)}{2}$.
|
||||
- **Space complexity $O(1)$, in-place sorting**: Pointers $i$ and $j$ use a constant amount of extra space.
|
||||
- **Unstable sorting**: As shown in the figure below, element `nums[i]` may be swapped to the right of an element equal to it, causing a change in their relative order.
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ Stable sorting is a necessary condition for multi-level sorting scenarios. Suppo
|
||||
|
||||
**Adaptability**: <u>Adaptive sorting</u> can utilize the existing order information in the input data to reduce the amount of computation, achieving better time efficiency. The best-case time complexity of adaptive sorting algorithms is typically better than the average time complexity.
|
||||
|
||||
**Comparison-based or non-comparison**: <u>Comparison-based sorting</u> relies on comparison operators ($<$, $=$, $>$) to determine the relative order of elements, thereby sorting the entire array, with a theoretical optimal time complexity of $O(n \log n)$. <u>Non-comparison sorting</u> does not use comparison operators and can achieve a time complexity of $O(n)$, but its versatility is relatively limited.
|
||||
**Comparison-based or non-comparison**: <u>Comparison-based sorting</u> relies on comparison operators ($<$, $=$, $>$) to determine the relative order of elements, thereby sorting the entire array. Its worst-case time complexity has a lower bound of $\Omega(n \log n)$. <u>Non-comparison sorting</u> does not use comparison operators and can achieve a time complexity of $O(n)$, but its versatility is relatively limited.
|
||||
|
||||
## Ideal Sorting Algorithm
|
||||
|
||||
|
||||
@@ -175,7 +175,7 @@ We can directly use the queue classes provided by the programming language:
|
||||
|
||||
/* Dequeue element */
|
||||
// Since it's an array, removeFirst has O(n) complexity
|
||||
let pool = queue.removeFirst()
|
||||
let pop = queue.removeFirst()
|
||||
|
||||
/* Get queue length */
|
||||
let size = queue.count
|
||||
|
||||
@@ -205,9 +205,9 @@ Since the operations related to AVL trees require obtaining node heights, we nee
|
||||
```kotlin title=""
|
||||
/* AVL tree node */
|
||||
class TreeNode(val _val: Int) { // Node value
|
||||
val height: Int = 0 // Node height
|
||||
val left: TreeNode? = null // Left child
|
||||
val right: TreeNode? = null // Right child
|
||||
var height: Int = 0 // Node height
|
||||
var left: TreeNode? = null // Left child
|
||||
var right: TreeNode? = null // Right child
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -181,8 +181,8 @@ A <u>binary tree</u> is a non-linear data structure that models the hierarchical
|
||||
```kotlin title=""
|
||||
/* Binary tree node */
|
||||
class TreeNode(val _val: Int) { // Node value
|
||||
val left: TreeNode? = null // Reference to left child node
|
||||
val right: TreeNode? = null // Reference to right child node
|
||||
var left: TreeNode? = null // Reference to left child node
|
||||
var right: TreeNode? = null // Reference to right child node
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
Reference in New Issue
Block a user