diff --git a/docs/chapter_greedy/fractional_knapsack_problem.md b/docs/chapter_greedy/fractional_knapsack_problem.md index e777e256a..0f324a742 100644 --- a/docs/chapter_greedy/fractional_knapsack_problem.md +++ b/docs/chapter_greedy/fractional_knapsack_problem.md @@ -544,6 +544,8 @@ comments: true
全屏观看 >
+内置排序算法的时间复杂度通常为 $O(\log n)$ ,空间复杂度通常为 $O(\log n)$ 或 $O(n)$ ,取决于编程语言的具体实现。 + 除排序之外,在最差情况下,需要遍历整个物品列表,**因此时间复杂度为 $O(n)$** ,其中 $n$ 为物品数量。 由于初始化了一个 `Item` 对象列表,**因此空间复杂度为 $O(n)$** 。 diff --git a/en/docs/chapter_backtracking/permutations_problem.md b/en/docs/chapter_backtracking/permutations_problem.md index d5d61c78a..54c2a5be8 100644 --- a/en/docs/chapter_backtracking/permutations_problem.md +++ b/en/docs/chapter_backtracking/permutations_problem.md @@ -4,9 +4,9 @@ comments: true # 13.2   Permutation problem -The permutation problem is a typical application of the backtracking algorithm. It is defined as finding all possible arrangements of elements from a given set (such as an array or string). +The permutation problem is a typical application of the backtracking algorithm. It involves finding all possible arrangements (permutations) of elements from a given set, such as an array or a string. -Table 13-2 lists several example data, including the input arrays and their corresponding permutations. +Table 13-2 shows several examples, including input arrays and their corresponding permutations.

Table 13-2   Permutation examples

@@ -20,40 +20,40 @@ Table 13-2 lists several example data, including the input arrays and their corr -## 13.2.1   Cases without equal elements +## 13.2.1   Cases without duplicate elements !!! question - Enter an integer array without duplicate elements and return all possible permutations. + Given an integer array with no duplicate elements, return all possible permutations. -From the perspective of the backtracking algorithm, **we can imagine the process of generating permutations as a series of choices**. Suppose the input array is $[1, 2, 3]$, if we first choose $1$, then $3$, and finally $2$, we obtain the permutation $[1, 3, 2]$. Backtracking means undoing a choice and then continuing to try other choices. +From a backtracking perspective, **we can view the process of generating permutations as a series of choices.** Suppose the input array is $[1, 2, 3]$. If we choose $1$ first, then $3$, and finally $2$, we get the permutation $[1, 3, 2]$. "Backtracking" means undoing a previous choice and exploring alternative options. -From the code perspective, the candidate set `choices` contains all elements of the input array, and the state `state` contains elements that have been selected so far. Please note that each element can only be chosen once, **thus all elements in `state` must be unique**. +From a coding perspective, the candidate set `choices` consists of all elements in the input array, while `state` holds the elements selected so far. Since each element can only be chosen once, **all elements in `state` must be unique**. -As shown in Figure 13-5, we can unfold the search process into a recursive tree, where each node represents the current state `state`. Starting from the root node, after three rounds of choices, we reach the leaf nodes, each corresponding to a permutation. +As illustrated in Figure 13-5, we can expand the search process into a recursive tree, where each node represents the current `state`. Starting from the root node, after three rounds of selections, we reach the leaf nodes—each corresponding to a permutation. ![Permutation recursive tree](permutations_problem.assets/permutations_i.png){ class="animation-figure" }

Figure 13-5   Permutation recursive tree

-### 1.   Pruning of repeated choices +### 1.   Repeated-choice pruning -To ensure that each element is selected only once, we consider introducing a boolean array `selected`, where `selected[i]` indicates whether `choices[i]` has been selected. We base our pruning operations on this array: +To ensure each element is selected only once, we introduce a boolean array `selected`, where `selected[i]` indicates whether `choices[i]` has been chosen. We then base our pruning steps on this array: -- After making the choice `choice[i]`, we set `selected[i]` to $\text{True}$, indicating it has been chosen. -- When iterating through the choice list `choices`, skip all nodes that have already been selected, i.e., prune. +- After choosing `choice[i]`, set `selected[i]` to $\text{True}$ to mark it as chosen. +- While iterating through `choices`, skip all elements marked as chosen (i.e., prune those branches). -As shown in Figure 13-6, suppose we choose 1 in the first round, 3 in the second round, and 2 in the third round, we need to prune the branch of element 1 in the second round and elements 1 and 3 in the third round. +As shown in Figure 13-6, suppose we choose 1 in the first round, then 3 in the second round, and finally 2 in the third round. We need to prune the branch for element 1 in the second round and the branches for elements 1 and 3 in the third round. ![Permutation pruning example](permutations_problem.assets/permutations_i_pruning.png){ class="animation-figure" }

Figure 13-6   Permutation pruning example

-Observing Figure 13-6, this pruning operation reduces the search space size from $O(n^n)$ to $O(n!)$. +From the figure, we can see that this pruning process reduces the search space from $O(n^n)$ to $O(n!)$. ### 2.   Code implementation -After understanding the above information, we can "fill in the blanks" in the framework code. To shorten the overall code, we do not implement individual functions within the framework code separately, but expand them in the `backtrack()` function: +With this understanding, we can "fill in the blanks" of our framework code. To keep the overall code concise, we won’t implement each part of the framework separately but instead expand everything in the `backtrack()` function: === "Python" @@ -246,29 +246,29 @@ After understanding the above information, we can "fill in the blanks" in the fr [class]{}-[func]{permutationsI} ``` -## 13.2.2   Considering cases with equal elements +## 13.2.2   Considering duplicate elements !!! question - Enter an integer array, **which may contain duplicate elements**, and return all unique permutations. + Given an integer array**that may contain duplicate elements**, return all unique permutations. -Suppose the input array is $[1, 1, 2]$. To differentiate the two duplicate elements $1$, we mark the second $1$ as $\hat{1}$. +Suppose the input array is $[1, 1, 2]$. To distinguish between the two identical elements $1$, we label the second one as $\hat{1}$. -As shown in Figure 13-7, half of the permutations generated by the above method are duplicates. +As shown in Figure 13-7, half of the permutations produced by this method are duplicates: ![Duplicate permutations](permutations_problem.assets/permutations_ii.png){ class="animation-figure" }

Figure 13-7   Duplicate permutations

-So, how do we eliminate duplicate permutations? Most directly, consider using a hash set to deduplicate permutation results. However, this is not elegant, **as branches generating duplicate permutations are unnecessary and should be identified and pruned in advance**, which can further improve algorithm efficiency. +So how can we eliminate these duplicate permutations? One direct approach is to use a hash set to remove duplicates after generating all permutations. However, this is less elegant **because branches that produce duplicates are inherently unnecessary and should be pruned in advance,** thus improving the algorithm’s efficiency. -### 1.   Pruning of equal elements +### 1.   Equal-element pruning -Observing Figure 13-8, in the first round, choosing $1$ or $\hat{1}$ results in identical permutations under both choices, thus we should prune $\hat{1}$. +Looking at Figure 13-8, in the first round, choosing $1$ or $\hat{1}$ leads to the same permutations, so we prune $\hat{1}$. -Similarly, after choosing $2$ in the first round, choosing $1$ and $\hat{1}$ in the second round also produces duplicate branches, so we should also prune $\hat{1}$ in the second round. +Similarly, after choosing $2$ in the first round, choosing $1$ or $\hat{1}$ in the second round also leads to duplicate branches, so we prune $\hat{1}$ then as well. -Essentially, **our goal is to ensure that multiple equal elements are only selected once in each round of choices**. +Essentially, **our goal is to ensure that multiple identical elements are only selected once per round of choices.** ![Duplicate permutations pruning](permutations_problem.assets/permutations_ii_pruning.png){ class="animation-figure" } @@ -276,7 +276,7 @@ Essentially, **our goal is to ensure that multiple equal elements are only selec ### 2.   Code implementation -Based on the code from the previous problem, we consider initiating a hash set `duplicated` in each round of choices, used to record elements that have been tried in that round, and prune duplicate elements: +Based on the code from the previous problem, we introduce a hash set `duplicated` in each round. This set keeps track of elements we have already attempted, so we can prune duplicates: === "Python" @@ -475,18 +475,18 @@ Based on the code from the previous problem, we consider initiating a hash set ` [class]{}-[func]{permutationsII} ``` -Assuming all elements are distinct from each other, there are $n!$ (factorial) permutations of $n$ elements; when recording results, it is necessary to copy a list of length $n$, using $O(n)$ time. **Thus, the time complexity is $O(n!n)$**. +Assuming all elements are distinct, there are $n!$ (factorial) permutations of $n$ elements. Recording each result requires copying a list of length $n$, which takes $O(n)$ time. **Hence, the total time complexity is $O(n!n)$.** -The maximum recursion depth is $n$, using $O(n)$ frame space. `Selected` uses $O(n)$ space. At any one time, there can be up to $n$ `duplicated`, using $O(n^2)$ space. **Therefore, the space complexity is $O(n^2)$**. +The maximum recursion depth is $n$, using $O(n)$ stack space. The `selected` array also requires $O(n)$ space. Because there can be up to $n$ separate `duplicated` sets at any one time, they collectively occupy $O(n^2)$ space. **Therefore, the space complexity is $O(n^2)$.** -### 3.   Comparison of the two pruning methods +### 3.   Comparing the two pruning methods -Please note, although both `selected` and `duplicated` are used for pruning, their targets are different. +Although both `selected` and `duplicated` serve as pruning mechanisms, they target different issues: -- **Repeated choice pruning**: There is only one `selected` throughout the search process. It records which elements are currently in the state, aiming to prevent an element from appearing repeatedly in `state`. -- **Equal element pruning**: Each round of choices (each call to the `backtrack` function) contains a `duplicated`. It records which elements have been chosen in the current traversal (`for` loop), aiming to ensure equal elements are selected only once. +- **Repeated-choice pruning**(via `selected`): There is a single `selected` array for the entire search, indicating which elements are already in the current state. This prevents the same element from appearing more than once in `state`. +- **Equal-element pruning**(via `duplicated`): Each call to the `backtrack` function uses its own `duplicated` set, recording which elements have already been chosen in that specific iteration (`for` loop). This ensures that equal elements are selected only once per round of choices. -Figure 13-9 shows the scope of the two pruning conditions. Note, each node in the tree represents a choice, and the nodes from the root to the leaf form a permutation. +Figure 13-9 shows the scope of these two pruning strategies. Each node in the tree represents a choice; the path from the root to any leaf corresponds to one complete permutation. ![Scope of the two pruning conditions](permutations_problem.assets/permutations_ii_pruning_summary.png){ class="animation-figure" } diff --git a/zh-Hant/docs/chapter_array_and_linkedlist/list.md b/zh-Hant/docs/chapter_array_and_linkedlist/list.md index 047daf271..c4d476d9a 100755 --- a/zh-Hant/docs/chapter_array_and_linkedlist/list.md +++ b/zh-Hant/docs/chapter_array_and_linkedlist/list.md @@ -1963,8 +1963,7 @@ comments: true impl MyList { /* 建構子 */ pub fn new(capacity: usize) -> Self { - let mut vec = Vec::new(); - vec.resize(capacity, 0); + let mut vec = vec![0; capacity]; Self { arr: vec, capacity, @@ -2036,7 +2035,7 @@ comments: true }; let num = self.arr[index]; // 將將索引 index 之後的元素都向前移動一位 - for j in (index..self.size - 1) { + for j in index..self.size - 1 { self.arr[j] = self.arr[j + 1]; } // 更新元素數量 @@ -2055,7 +2054,7 @@ comments: true } /* 將串列轉換為陣列 */ - pub fn to_array(&mut self) -> Vec { + pub fn to_array(&self) -> Vec { // 僅轉換有效長度範圍內的串列元素 let mut arr = Vec::new(); for i in 0..self.size { diff --git a/zh-Hant/docs/chapter_backtracking/summary.md b/zh-Hant/docs/chapter_backtracking/summary.md index a3bc1abdc..150d21de2 100644 --- a/zh-Hant/docs/chapter_backtracking/summary.md +++ b/zh-Hant/docs/chapter_backtracking/summary.md @@ -15,7 +15,7 @@ comments: true - 子集和問題的目標是在給定集合中找到和為目標值的所有子集。集合不區分元素順序,而搜尋過程會輸出所有順序的結果,產生重複子集。我們在回溯前將資料進行排序,並設定一個變數來指示每一輪的走訪起始點,從而將生成重複子集的搜尋分支進行剪枝。 - 對於子集和問題,陣列中的相等元素會產生重複集合。我們利用陣列已排序的前置條件,透過判斷相鄰元素是否相等實現剪枝,從而確保相等元素在每輪中只能被選中一次。 - $n$ 皇后問題旨在尋找將 $n$ 個皇后放置到 $n \times n$ 尺寸棋盤上的方案,要求所有皇后兩兩之間無法攻擊對方。該問題的約束條件有行約束、列約束、主對角線和次對角線約束。為滿足行約束,我們採用按行放置的策略,保證每一行放置一個皇后。 -- 列約束和對角線約束的處理方式類似。對於列約束,我們利用一個陣列來記錄每一列是否有皇后,從而指示選中的格子是否合法。對於對角線約束,我們藉助兩個陣列來分別記錄該主、次對角線上是否存在皇后;難點在於找處在到同一主(副)對角線上格子滿足的行列索引規律。 +- 列約束和對角線約束的處理方式類似。對於列約束,我們利用一個陣列來記錄每一列是否有皇后,從而指示選中的格子是否合法。對於對角線約束,我們藉助兩個陣列來分別記錄該主、次對角線上是否存在皇后;難點在於找出處在同一主(副)對角線上的格子所滿足的行列索引規律。 ### 2.   Q & A diff --git a/zh-Hant/docs/chapter_greedy/fractional_knapsack_problem.md b/zh-Hant/docs/chapter_greedy/fractional_knapsack_problem.md index df610ff56..181026fa7 100644 --- a/zh-Hant/docs/chapter_greedy/fractional_knapsack_problem.md +++ b/zh-Hant/docs/chapter_greedy/fractional_knapsack_problem.md @@ -544,6 +544,8 @@ comments: true
全螢幕觀看 >
+內建排序演算法的時間複雜度通常為 $O(\log n)$ ,空間複雜度通常為 $O(\log n)$ 或 $O(n)$ ,取決於程式語言的具體實現。 + 除排序之外,在最差情況下,需要走訪整個物品串列,**因此時間複雜度為 $O(n)$** ,其中 $n$ 為物品數量。 由於初始化了一個 `Item` 物件串列,**因此空間複雜度為 $O(n)$** 。 diff --git a/zh-Hant/docs/chapter_hashing/hash_algorithm.md b/zh-Hant/docs/chapter_hashing/hash_algorithm.md index 223448c41..ff4dc0657 100644 --- a/zh-Hant/docs/chapter_hashing/hash_algorithm.md +++ b/zh-Hant/docs/chapter_hashing/hash_algorithm.md @@ -354,7 +354,7 @@ index = hash(key) % capacity for (const c of key) { hash ^= c.charCodeAt(0); } - return hash & MODULUS; + return hash % MODULUS; } /* 旋轉雜湊 */ @@ -398,7 +398,7 @@ index = hash(key) % capacity for (const c of key) { hash ^= c.charCodeAt(0); } - return hash & MODULUS; + return hash % MODULUS; } /* 旋轉雜湊 */ diff --git a/zh-Hant/docs/chapter_hashing/hash_collision.md b/zh-Hant/docs/chapter_hashing/hash_collision.md index d1f201dff..48ec626ed 100644 --- a/zh-Hant/docs/chapter_hashing/hash_collision.md +++ b/zh-Hant/docs/chapter_hashing/hash_collision.md @@ -1025,10 +1025,10 @@ comments: true ```rust title="hash_map_chaining.rs" /* 鏈式位址雜湊表 */ struct HashMapChaining { - size: i32, - capacity: i32, + size: usize, + capacity: usize, load_thres: f32, - extend_ratio: i32, + extend_ratio: usize, buckets: Vec>, } @@ -1046,7 +1046,7 @@ comments: true /* 雜湊函式 */ fn hash_func(&self, key: i32) -> usize { - key as usize % self.capacity as usize + key as usize % self.capacity } /* 負載因子 */ @@ -1057,12 +1057,11 @@ comments: true /* 刪除操作 */ fn remove(&mut self, key: i32) -> Option { let index = self.hash_func(key); - let bucket = &mut self.buckets[index]; // 走訪桶,從中刪除鍵值對 - for i in 0..bucket.len() { - if bucket[i].key == key { - let pair = bucket.remove(i); + for (i, p) in self.buckets[index].iter_mut().enumerate() { + if p.key == key { + let pair = self.buckets[index].remove(i); self.size -= 1; return Some(pair.val); } @@ -1075,7 +1074,7 @@ comments: true /* 擴容雜湊表 */ fn extend(&mut self) { // 暫存原雜湊表 - let buckets_tmp = std::mem::replace(&mut self.buckets, vec![]); + let buckets_tmp = std::mem::take(&mut self.buckets); // 初始化擴容後的新雜湊表 self.capacity *= self.extend_ratio; @@ -1109,30 +1108,27 @@ comments: true } let index = self.hash_func(key); - let bucket = &mut self.buckets[index]; // 走訪桶,若遇到指定 key ,則更新對應 val 並返回 - for pair in bucket { + for pair in self.buckets[index].iter_mut() { if pair.key == key { pair.val = val; return; } } - let bucket = &mut self.buckets[index]; // 若無該 key ,則將鍵值對新增至尾部 let pair = Pair { key, val }; - bucket.push(pair); + self.buckets[index].push(pair); self.size += 1; } /* 查詢操作 */ fn get(&self, key: i32) -> Option<&str> { let index = self.hash_func(key); - let bucket = &self.buckets[index]; // 走訪桶,若找到 key ,則返回對應 val - for pair in bucket { + for pair in self.buckets[index].iter() { if pair.key == key { return Some(&pair.val); } diff --git a/zh-Hant/docs/chapter_sorting/counting_sort.md b/zh-Hant/docs/chapter_sorting/counting_sort.md index cd0516ca3..4325ca7f9 100644 --- a/zh-Hant/docs/chapter_sorting/counting_sort.md +++ b/zh-Hant/docs/chapter_sorting/counting_sort.md @@ -27,9 +27,7 @@ comments: true """計數排序""" # 簡單實現,無法用於排序物件 # 1. 統計陣列最大元素 m - m = 0 - for num in nums: - m = max(m, num) + m = max(nums) # 2. 統計各數字的出現次數 # counter[num] 代表 num 的出現次數 counter = [0] * (m + 1) diff --git a/zh-Hant/docs/chapter_stack_and_queue/deque.md b/zh-Hant/docs/chapter_stack_and_queue/deque.md index 168b8999b..d3434bbfd 100644 --- a/zh-Hant/docs/chapter_stack_and_queue/deque.md +++ b/zh-Hant/docs/chapter_stack_and_queue/deque.md @@ -1679,7 +1679,7 @@ comments: true } } self.que_size -= 1; // 更新佇列長度 - Rc::try_unwrap(old_front).ok().unwrap().into_inner().val + old_front.borrow().val }) } // 佇列尾出列操作 @@ -1695,7 +1695,7 @@ comments: true } } self.que_size -= 1; // 更新佇列長度 - Rc::try_unwrap(old_rear).ok().unwrap().into_inner().val + old_rear.borrow().val }) } } @@ -1722,12 +1722,16 @@ comments: true /* 返回陣列用於列印 */ pub fn to_array(&self, head: Option<&Rc>>>) -> Vec { - if let Some(node) = head { - let mut nums = self.to_array(node.borrow().next.as_ref()); - nums.insert(0, node.borrow().val); - return nums; + let mut res: Vec = Vec::new(); + fn recur(cur: Option<&Rc>>>, res: &mut Vec) { + if let Some(cur) = cur { + res.push(cur.borrow().val); + recur(cur.borrow().next.as_ref(), res); + } } - return Vec::new(); + + recur(head, &mut res); + res } } ``` diff --git a/zh-Hant/docs/chapter_stack_and_queue/queue.md b/zh-Hant/docs/chapter_stack_and_queue/queue.md index 70b9e1caa..ba8457e0e 100755 --- a/zh-Hant/docs/chapter_stack_and_queue/queue.md +++ b/zh-Hant/docs/chapter_stack_and_queue/queue.md @@ -1070,7 +1070,7 @@ comments: true } } self.que_size -= 1; - Rc::try_unwrap(old_front).ok().unwrap().into_inner().val + old_front.borrow().val }) } @@ -1081,12 +1081,18 @@ comments: true /* 將鏈結串列轉化為 Array 並返回 */ pub fn to_array(&self, head: Option<&Rc>>>) -> Vec { - if let Some(node) = head { - let mut nums = self.to_array(node.borrow().next.as_ref()); - nums.insert(0, node.borrow().val); - return nums; + let mut res: Vec = Vec::new(); + + fn recur(cur: Option<&Rc>>>, res: &mut Vec) { + if let Some(cur) = cur { + res.push(cur.borrow().val); + recur(cur.borrow().next.as_ref(), res); + } } - return Vec::new(); + + recur(head, &mut res); + + res } } ``` diff --git a/zh-Hant/docs/chapter_stack_and_queue/stack.md b/zh-Hant/docs/chapter_stack_and_queue/stack.md index 6915f23f3..37060a27e 100755 --- a/zh-Hant/docs/chapter_stack_and_queue/stack.md +++ b/zh-Hant/docs/chapter_stack_and_queue/stack.md @@ -964,16 +964,10 @@ comments: true /* 出堆疊 */ pub fn pop(&mut self) -> Option { self.stack_peek.take().map(|old_head| { - match old_head.borrow_mut().next.take() { - Some(new_head) => { - self.stack_peek = Some(new_head); - } - None => { - self.stack_peek = None; - } - } + self.stack_peek = old_head.borrow_mut().next.take(); self.stk_size -= 1; - Rc::try_unwrap(old_head).ok().unwrap().into_inner().val + + old_head.borrow().val }) }