diff --git a/README.md b/README.md index 233f434e5..b3a3ee72e 100644 --- a/README.md +++ b/README.md @@ -17,24 +17,24 @@

- - + +

- - - - - - - - - - - - - + + + + + + + + + + + + +

@@ -51,7 +51,7 @@ - 全书采用动画图解,内容清晰易懂、学习曲线平滑,引导初学者探索数据结构与算法的知识地图。 - 源代码可一键运行,帮助读者在练习中提升编程技能,了解算法工作原理和数据结构底层实现。 -- 鼓励读者互助学习,提问与评论通常可在两日内得到回复。 +- 鼓励读者互助学习,欢迎大家在评论区提出问题、见解和建议。 若本书对您有所帮助,请在页面右上角点个 Star :star: 支持一下,谢谢! diff --git a/codes/python/chapter_stack_and_queue/array_stack.py b/codes/python/chapter_stack_and_queue/array_stack.py index 6f270e933..343f155dc 100644 --- a/codes/python/chapter_stack_and_queue/array_stack.py +++ b/codes/python/chapter_stack_and_queue/array_stack.py @@ -18,7 +18,7 @@ class ArrayStack: def is_empty(self) -> bool: """判断栈是否为空""" - return self._stack == [] + return self.size() == 0 def push(self, item: int): """入栈""" diff --git a/codes/python/chapter_stack_and_queue/linkedlist_deque.py b/codes/python/chapter_stack_and_queue/linkedlist_deque.py index c723fb758..0a39c95c8 100644 --- a/codes/python/chapter_stack_and_queue/linkedlist_deque.py +++ b/codes/python/chapter_stack_and_queue/linkedlist_deque.py @@ -30,7 +30,7 @@ class LinkedListDeque: def is_empty(self) -> bool: """判断双向队列是否为空""" - return self.size() == 0 + return self._size == 0 def push(self, num: int, is_front: bool): """入队操作""" diff --git a/codes/python/chapter_stack_and_queue/linkedlist_queue.py b/codes/python/chapter_stack_and_queue/linkedlist_queue.py index 86171918c..94bbe7399 100644 --- a/codes/python/chapter_stack_and_queue/linkedlist_queue.py +++ b/codes/python/chapter_stack_and_queue/linkedlist_queue.py @@ -26,7 +26,7 @@ class LinkedListQueue: def is_empty(self) -> bool: """判断队列是否为空""" - return not self._front + return self._size == 0 def push(self, num: int): """入队""" diff --git a/codes/python/chapter_stack_and_queue/linkedlist_stack.py b/codes/python/chapter_stack_and_queue/linkedlist_stack.py index 51ef7868f..8d3d7f753 100644 --- a/codes/python/chapter_stack_and_queue/linkedlist_stack.py +++ b/codes/python/chapter_stack_and_queue/linkedlist_stack.py @@ -25,7 +25,7 @@ class LinkedListStack: def is_empty(self) -> bool: """判断栈是否为空""" - return not self._peek + return self._size == 0 def push(self, val: int): """入栈""" diff --git a/codes/ruby/chapter_sorting/quick_sort.rb b/codes/ruby/chapter_sorting/quick_sort.rb index d02916ba7..2dff14c6b 100644 --- a/codes/ruby/chapter_sorting/quick_sort.rb +++ b/codes/ruby/chapter_sorting/quick_sort.rb @@ -136,16 +136,17 @@ class QuickSortTailCall end ### Driver Code ### - if __FILE__ == $0 # 快速排序 nums = [2, 4, 1, 0, 3, 5] QuickSort.quick_sort(nums, 0, nums.length - 1) puts "快速排序完成后 nums = #{nums}" + # 快速排序(中位基准数优化) nums1 = [2, 4, 1, 0, 3, 5] QuickSortMedian.quick_sort(nums1, 0, nums1.length - 1) puts "快速排序(中位基准数优化)完成后 nums1 = #{nums1}" + # 快速排序(尾递归优化) nums2 = [2, 4, 1, 0, 3, 5] QuickSortTailCall.quick_sort(nums2, 0, nums2.length - 1) diff --git a/codes/ruby/chapter_tree/binary_tree_dfs.rb b/codes/ruby/chapter_tree/binary_tree_dfs.rb index 35bfc2589..c7d4270cf 100644 --- a/codes/ruby/chapter_tree/binary_tree_dfs.rb +++ b/codes/ruby/chapter_tree/binary_tree_dfs.rb @@ -8,13 +8,13 @@ require_relative '../utils/tree_node' require_relative '../utils/print_util' ### 前序遍历 ### -def pre_oder(root) +def pre_order(root) return if root.nil? # 访问优先级:根节点 -> 左子树 -> 右子树 $res << root.val - pre_oder(root.left) - pre_oder(root.right) + pre_order(root.left) + pre_order(root.right) end ### 中序遍历 ### @@ -47,7 +47,7 @@ if __FILE__ == $0 # 前序遍历 $res = [] - pre_oder(root) + pre_order(root) puts "\n前序遍历的节点打印序列 = #{$res}" # 中序遍历 diff --git a/codes/typescript/chapter_backtracking/subset_sum_i.ts b/codes/typescript/chapter_backtracking/subset_sum_i.ts index eafd0150b..c0286bd2c 100644 --- a/codes/typescript/chapter_backtracking/subset_sum_i.ts +++ b/codes/typescript/chapter_backtracking/subset_sum_i.ts @@ -51,4 +51,4 @@ const res = subsetSumI(nums, target); console.log(`输入数组 nums = ${JSON.stringify(nums)}, target = ${target}`); console.log(`所有和等于 ${target} 的子集 res = ${JSON.stringify(res)}`); -export { }; +export {}; diff --git a/codes/typescript/chapter_backtracking/subset_sum_ii.ts b/codes/typescript/chapter_backtracking/subset_sum_ii.ts index 0d3175fcf..a499c2e82 100644 --- a/codes/typescript/chapter_backtracking/subset_sum_ii.ts +++ b/codes/typescript/chapter_backtracking/subset_sum_ii.ts @@ -56,4 +56,4 @@ const res = subsetSumII(nums, target); console.log(`输入数组 nums = ${JSON.stringify(nums)}, target = ${target}`); console.log(`所有和等于 ${target} 的子集 res = ${JSON.stringify(res)}`); -export { }; +export {}; diff --git a/codes/typescript/chapter_computational_complexity/recursion.ts b/codes/typescript/chapter_computational_complexity/recursion.ts index adc1a12a4..bd7618574 100644 --- a/codes/typescript/chapter_computational_complexity/recursion.ts +++ b/codes/typescript/chapter_computational_complexity/recursion.ts @@ -67,4 +67,4 @@ console.log(`尾递归函数的求和结果 res = ${res}`); res = fib(n); console.log(`斐波那契数列的第 ${n} 项为 ${res}`); -export { }; +export {}; diff --git a/docs/assets/course/hello-algo-0.1-课程简介.pdf b/docs/assets/course/hello-algo-0.1-课程简介.pdf new file mode 100644 index 000000000..9904b865c Binary files /dev/null and b/docs/assets/course/hello-algo-0.1-课程简介.pdf differ diff --git a/docs/chapter_divide_and_conquer/divide_and_conquer.md b/docs/chapter_divide_and_conquer/divide_and_conquer.md index f0b6517c1..ab030c073 100644 --- a/docs/chapter_divide_and_conquer/divide_and_conquer.md +++ b/docs/chapter_divide_and_conquer/divide_and_conquer.md @@ -64,7 +64,7 @@ $$ 并行优化在多核或多处理器的环境中尤其有效,因为系统可以同时处理多个子问题,更加充分地利用计算资源,从而显著减少总体的运行时间。 -比如在下图所示的“桶排序”中,我们将海量的数据平均分配到各个桶中,则可所有桶的排序任务分散到各个计算单元,完成后再合并结果。 +比如在下图所示的“桶排序”中,我们将海量的数据平均分配到各个桶中,则可将所有桶的排序任务分散到各个计算单元,完成后再合并结果。 ![桶排序的并行计算](divide_and_conquer.assets/divide_and_conquer_parallel_computing.png) diff --git a/docs/chapter_dynamic_programming/knapsack_problem.md b/docs/chapter_dynamic_programming/knapsack_problem.md index af97b264b..f45d13b52 100644 --- a/docs/chapter_dynamic_programming/knapsack_problem.md +++ b/docs/chapter_dynamic_programming/knapsack_problem.md @@ -18,15 +18,15 @@ **第一步:思考每轮的决策,定义状态,从而得到 $dp$ 表** -对于每个物品来说,不放入背包,背包容量不变;放入背包,背包容量减小。由此可得状态定义:当前物品编号 $i$ 和剩余背包容量 $c$ ,记为 $[i, c]$ 。 +对于每个物品来说,不放入背包,背包容量不变;放入背包,背包容量减小。由此可得状态定义:当前物品编号 $i$ 和背包容量 $c$ ,记为 $[i, c]$ 。 -状态 $[i, c]$ 对应的子问题为:**前 $i$ 个物品在剩余容量为 $c$ 的背包中的最大价值**,记为 $dp[i, c]$ 。 +状态 $[i, c]$ 对应的子问题为:**前 $i$ 个物品在容量为 $c$ 的背包中的最大价值**,记为 $dp[i, c]$ 。 待求解的是 $dp[n, cap]$ ,因此需要一个尺寸为 $(n+1) \times (cap+1)$ 的二维 $dp$ 表。 **第二步:找出最优子结构,进而推导出状态转移方程** -当我们做出物品 $i$ 的决策后,剩余的是前 $i-1$ 个物品的决策,可分为以下两种情况。 +当我们做出物品 $i$ 的决策后,剩余的是前 $i-1$ 个物品决策的子问题,可分为以下两种情况。 - **不放入物品 $i$** :背包容量不变,状态变化为 $[i-1, c]$ 。 - **放入物品 $i$** :背包容量减少 $wgt[i-1]$ ,价值增加 $val[i-1]$ ,状态变化为 $[i-1, c-wgt[i-1]]$ 。 @@ -41,7 +41,7 @@ $$ **第三步:确定边界条件和状态转移顺序** -当无物品或无剩余背包容量时最大价值为 $0$ ,即首列 $dp[i, 0]$ 和首行 $dp[0, c]$ 都等于 $0$ 。 +当无物品或背包容量为 $0$ 时最大价值为 $0$ ,即首列 $dp[i, 0]$ 和首行 $dp[0, c]$ 都等于 $0$ 。 当前状态 $[i, c]$ 从上方的状态 $[i-1, c]$ 和左上方的状态 $[i-1, c-wgt[i-1]]$ 转移而来,因此通过两层循环正序遍历整个 $dp$ 表即可。 diff --git a/docs/chapter_dynamic_programming/summary.md b/docs/chapter_dynamic_programming/summary.md index 5d422a9d4..c46f54dfc 100644 --- a/docs/chapter_dynamic_programming/summary.md +++ b/docs/chapter_dynamic_programming/summary.md @@ -11,7 +11,7 @@ **背包问题** - 背包问题是最典型的动态规划问题之一,具有 0-1 背包、完全背包、多重背包等变种。 -- 0-1 背包的状态定义为前 $i$ 个物品在剩余容量为 $c$ 的背包中的最大价值。根据不放入背包和放入背包两种决策,可得到最优子结构,并构建出状态转移方程。在空间优化中,由于每个状态依赖正上方和左上方的状态,因此需要倒序遍历列表,避免左上方状态被覆盖。 +- 0-1 背包的状态定义为前 $i$ 个物品在容量为 $c$ 的背包中的最大价值。根据不放入背包和放入背包两种决策,可得到最优子结构,并构建出状态转移方程。在空间优化中,由于每个状态依赖正上方和左上方的状态,因此需要倒序遍历列表,避免左上方状态被覆盖。 - 完全背包问题的每种物品的选取数量无限制,因此选择放入物品的状态转移与 0-1 背包问题不同。由于状态依赖正上方和正左方的状态,因此在空间优化中应当正序遍历。 - 零钱兑换问题是完全背包问题的一个变种。它从求“最大”价值变为求“最小”硬币数量,因此状态转移方程中的 $\max()$ 应改为 $\min()$ 。从追求“不超过”背包容量到追求“恰好”凑出目标金额,因此使用 $amt + 1$ 来表示“无法凑出目标金额”的无效解。 - 零钱兑换问题 II 从求“最少硬币数量”改为求“硬币组合数量”,状态转移方程相应地从 $\min()$ 改为求和运算符。 diff --git a/docs/chapter_heap/heap.md b/docs/chapter_heap/heap.md index 14cb91f33..a97ce5a79 100644 --- a/docs/chapter_heap/heap.md +++ b/docs/chapter_heap/heap.md @@ -420,7 +420,7 @@ ## 堆的实现 -下文实现的是大顶堆。若要将其转换为小顶堆,只需将所有大小逻辑判断取逆(例如,将 $\geq$ 替换为 $\leq$ )。感兴趣的读者可以自行实现。 +下文实现的是大顶堆。若要将其转换为小顶堆,只需将所有大小逻辑判断进行逆转(例如,将 $\geq$ 替换为 $\leq$ )。感兴趣的读者可以自行实现。 ### 堆的存储与表示 diff --git a/docs/chapter_paperbook/index.md b/docs/chapter_paperbook/index.md index fe1ecd1ad..190c38e62 100644 --- a/docs/chapter_paperbook/index.md +++ b/docs/chapter_paperbook/index.md @@ -36,17 +36,18 @@ status: new - 采用全彩印刷,能够原汁原味地发挥出本书“动画图解”的优势。 - 考究纸张材质,既保证色彩高度还原,也保留纸质书特有的质感。 +- 纸质版比网页版的格式更加规范,例如图中的公式使用斜体。 - 在不提升定价的前提下,附赠思维导图折页、书签。 - 纸质书、网页版、PDF 版内容同步,随意切换阅读。 !!! tip - 由于纸质书和网页版的同步成本较大,因此可能会有一些细节上的不同,请您见谅! + 由于纸质书和网页版的同步难度较大,因此可能会有一些细节上的不同,请您见谅! 当然,纸质书也有一些值得大家入手前考虑的地方: -- 使用 Python 语言,可能不匹配你的主语言(也许可以趁此机会练习 Python)。 -- 全彩印刷虽然大幅提升了阅读体验,但价格会比黑白印刷高一些。 +- 使用 Python 语言,可能不匹配你的主语言(可以把 Python 看作伪代码,重在理解思路)。 +- 全彩印刷虽然大幅提升了图解和代码的阅读体验,但价格会比黑白印刷高一些。 !!! tip diff --git a/docs/chapter_sorting/counting_sort.md b/docs/chapter_sorting/counting_sort.md index 50660c518..fbb119a72 100644 --- a/docs/chapter_sorting/counting_sort.md +++ b/docs/chapter_sorting/counting_sort.md @@ -71,7 +71,7 @@ $$ ## 算法特性 -- **时间复杂度为 $O(n + m)$** :涉及遍历 `nums` 和遍历 `counter` ,都使用线性时间。一般情况下 $n \gg m$ ,时间复杂度趋于 $O(n)$ 。 +- **时间复杂度为 $O(n + m)$、非自适应排序** :涉及遍历 `nums` 和遍历 `counter` ,都使用线性时间。一般情况下 $n \gg m$ ,时间复杂度趋于 $O(n)$ 。 - **空间复杂度为 $O(n + m)$、非原地排序**:借助了长度分别为 $n$ 和 $m$ 的数组 `res` 和 `counter` 。 - **稳定排序**:由于向 `res` 中填充元素的顺序是“从右向左”的,因此倒序遍历 `nums` 可以避免改变相等元素之间的相对位置,从而实现稳定排序。实际上,正序遍历 `nums` 也可以得到正确的排序结果,但结果是非稳定的。 diff --git a/docs/chapter_sorting/radix_sort.md b/docs/chapter_sorting/radix_sort.md index 4fa187c76..15af50b9b 100644 --- a/docs/chapter_sorting/radix_sort.md +++ b/docs/chapter_sorting/radix_sort.md @@ -36,6 +36,6 @@ $$ 相较于计数排序,基数排序适用于数值范围较大的情况,**但前提是数据必须可以表示为固定位数的格式,且位数不能过大**。例如,浮点数不适合使用基数排序,因为其位数 $k$ 过大,可能导致时间复杂度 $O(nk) \gg O(n^2)$ 。 -- **时间复杂度为 $O(nk)$**:设数据量为 $n$、数据为 $d$ 进制、最大位数为 $k$ ,则对某一位执行计数排序使用 $O(n + d)$ 时间,排序所有 $k$ 位使用 $O((n + d)k)$ 时间。通常情况下,$d$ 和 $k$ 都相对较小,时间复杂度趋向 $O(n)$ 。 +- **时间复杂度为 $O(nk)$、非自适应排序**:设数据量为 $n$、数据为 $d$ 进制、最大位数为 $k$ ,则对某一位执行计数排序使用 $O(n + d)$ 时间,排序所有 $k$ 位使用 $O((n + d)k)$ 时间。通常情况下,$d$ 和 $k$ 都相对较小,时间复杂度趋向 $O(n)$ 。 - **空间复杂度为 $O(n + d)$、非原地排序**:与计数排序相同,基数排序需要借助长度为 $n$ 和 $d$ 的数组 `res` 和 `counter` 。 - **稳定排序**:当计数排序稳定时,基数排序也稳定;当计数排序不稳定时,基数排序无法保证得到正确的排序结果。 diff --git a/en/CONTRIBUTING.md b/en/CONTRIBUTING.md index 51e77a4dc..366a16c38 100644 --- a/en/CONTRIBUTING.md +++ b/en/CONTRIBUTING.md @@ -41,13 +41,15 @@ Don't hesitate to join us via WeChat `krahets-jyd` or on [Discord](https://disco **Accuracy**: - Maintain consistency in terminology across translations by referring to the [Terminology](https://www.hello-algo.com/chapter_appendix/terminology/) section. -- Prioritize technical accuracy and maintain the tone and style of the Chinese version. If you think changing the original meaning is necessary, please first submit a PR to modify the Chinese version. +- Prioritize technical accuracy and maintain the tone and style of the Chinese version. +- Always take into account the content and context of the Chinese version to ensure modifications are accurate and comprehensive. **Authenticity**: - Translations should flow naturally and fluently, adhering to English expression conventions. - Always consider the context of the content to harmonize the article. - Be aware of cultural differences between Chinese and English. For instance, Chinese "pinyin" does not exist in English. +- If the optimized sentence could alter the original meaning, please add a comment for discussion. **Formatting**: diff --git a/en/README.md b/en/README.md index a8e62894e..a7ec12d52 100644 --- a/en/README.md +++ b/en/README.md @@ -15,24 +15,24 @@

- - + +

- - - - - - - - - - - - - + + + + + + + + + + + + +

@@ -49,7 +49,7 @@ This open-source project aims to create a free and beginner-friendly crash cours - Animated illustrations, easy-to-understand content, and a smooth learning curve help beginners explore the "knowledge map" of data structures and algorithms. - Run code with just one click, helping readers improve their programming skills and understand the working principle of algorithms and the underlying implementation of data structures. -- We encourage readers to help each other. Questions and comments are usually replied to within two days. +- Encouraging learning through teaching, feel free to share your questions, insights, and suggestions. If you find this book helpful, please give it a Star :star: to support us, thank you! diff --git a/overrides/stylesheets/extra.css b/overrides/stylesheets/extra.css index 0ea7f10ce..d85e15916 100644 --- a/overrides/stylesheets/extra.css +++ b/overrides/stylesheets/extra.css @@ -209,7 +209,11 @@ body { /* code block tabs */ .md-typeset .tabbed-labels>label { - font-size: 0.545rem; + font-size: 0.61rem; +} + +.md-typeset .tabbed-labels--linked>label>a { + padding: .78125em 1.0em .625em; } /* header banner */ diff --git a/zh-hant/README.md b/zh-hant/README.md index 2d6f1faa8..dd84be801 100644 --- a/zh-hant/README.md +++ b/zh-hant/README.md @@ -15,24 +15,24 @@

- - + +

- - - - - - - - - - - - - + + + + + + + + + + + + +

@@ -49,7 +49,7 @@ - 全書採用動畫圖解,內容清晰易懂、學習曲線平滑,引導初學者探索資料結構與演算法的知識地圖。 - 源程式碼可一鍵執行,幫助讀者在練習中提升程式設計技能,瞭解演算法工作原理和資料結構底層實現。 -- 鼓勵讀者互助學習,提問與評論通常可在兩日內得到回覆。 +- 鼓勵讀者互助學習,歡迎大家在評論區提出問題、見解和建議。 若本書對您有所幫助,請在頁面右上角點個 Star :star: 支持一下,謝謝! diff --git a/zh-hant/codes/c/utils/common.h b/zh-hant/codes/c/utils/common.h index 0b1ca7af8..8b9adeff7 100644 --- a/zh-hant/codes/c/utils/common.h +++ b/zh-hant/codes/c/utils/common.h @@ -4,8 +4,8 @@ * Author: MolDuM (moldum@163.com)、Reanon (793584285@qq.com) */ -#ifndef C_INCLUDE_H -#define C_INCLUDE_H +#ifndef COMMON_H +#define COMMON_H #include #include @@ -33,4 +33,4 @@ extern "C" { } #endif -#endif // C_INCLUDE_H +#endif // COMMON_H diff --git a/zh-hant/codes/python/chapter_stack_and_queue/array_stack.py b/zh-hant/codes/python/chapter_stack_and_queue/array_stack.py index 01129b98f..5e81c9907 100644 --- a/zh-hant/codes/python/chapter_stack_and_queue/array_stack.py +++ b/zh-hant/codes/python/chapter_stack_and_queue/array_stack.py @@ -18,7 +18,7 @@ class ArrayStack: def is_empty(self) -> bool: """判斷堆疊是否為空""" - return self._stack == [] + return self._size == 0 def push(self, item: int): """入堆疊""" diff --git a/zh-hant/codes/python/chapter_stack_and_queue/linkedlist_deque.py b/zh-hant/codes/python/chapter_stack_and_queue/linkedlist_deque.py index 7b706d9fd..67b298510 100644 --- a/zh-hant/codes/python/chapter_stack_and_queue/linkedlist_deque.py +++ b/zh-hant/codes/python/chapter_stack_and_queue/linkedlist_deque.py @@ -30,7 +30,7 @@ class LinkedListDeque: def is_empty(self) -> bool: """判斷雙向佇列是否為空""" - return self.size() == 0 + return self._size == 0 def push(self, num: int, is_front: bool): """入列操作""" diff --git a/zh-hant/codes/python/chapter_stack_and_queue/linkedlist_queue.py b/zh-hant/codes/python/chapter_stack_and_queue/linkedlist_queue.py index 62c641b46..51346c6be 100644 --- a/zh-hant/codes/python/chapter_stack_and_queue/linkedlist_queue.py +++ b/zh-hant/codes/python/chapter_stack_and_queue/linkedlist_queue.py @@ -26,7 +26,7 @@ class LinkedListQueue: def is_empty(self) -> bool: """判斷佇列是否為空""" - return not self._front + return self._size == 0 def push(self, num: int): """入列""" diff --git a/zh-hant/codes/python/chapter_stack_and_queue/linkedlist_stack.py b/zh-hant/codes/python/chapter_stack_and_queue/linkedlist_stack.py index 25f5521a0..5bddf0881 100644 --- a/zh-hant/codes/python/chapter_stack_and_queue/linkedlist_stack.py +++ b/zh-hant/codes/python/chapter_stack_and_queue/linkedlist_stack.py @@ -25,7 +25,7 @@ class LinkedListStack: def is_empty(self) -> bool: """判斷堆疊是否為空""" - return not self._peek + return self._size == 0 def push(self, val: int): """入堆疊""" diff --git a/zh-hant/codes/ruby/chapter_hashing/array_hash_map.rb b/zh-hant/codes/ruby/chapter_hashing/array_hash_map.rb new file mode 100644 index 000000000..b73270904 --- /dev/null +++ b/zh-hant/codes/ruby/chapter_hashing/array_hash_map.rb @@ -0,0 +1,121 @@ +=begin +File: array_hash_map.rb +Created Time: 2024-04-13 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +### 鍵值對 ### +class Pair + attr_accessor :key, :val + + def initialize(key, val) + @key = key + @val = val + end +end + +### 基於陣列實現的雜湊表 ### +class ArrayHashMap + ### 建構子 ### + def initialize + # 初始化陣列,包含 100 個桶 + @buckets = Array.new(100) + end + + ### 雜湊函式 ### + def hash_func(key) + index = key % 100 + end + + ### 查詢操作 ### + def get(key) + index = hash_func(key) + pair = @buckets[index] + + return if pair.nil? + pair.val + end + + ### 新增操作 ### + def put(key, val) + pair = Pair.new(key, val) + index = hash_func(key) + @buckets[index] = pair + end + + ### 刪除操作 ### + def remove(key) + index = hash_func(key) + # 置為 nil ,代表刪除 + @buckets[index] = nil + end + + ### 獲取所有鍵值對 ### + def entry_set + result = [] + @buckets.each { |pair| result << pair unless pair.nil? } + result + end + + ### 獲取所有鍵 ### + def key_set + result = [] + @buckets.each { |pair| result << pair.key unless pair.nil? } + result + end + + ### 獲取所有值 ### + def value_set + result = [] + @buckets.each { |pair| result << pair.val unless pair.nil? } + result + end + + ### 列印雜湊表 ### + def print + @buckets.each { |pair| puts "#{pair.key} -> #{pair.val}" unless pair.nil? } + end +end + +### Driver Code ### +if __FILE__ == $0 + # 初始化雜湊表 + hmap = ArrayHashMap.new + + # 新增操作 + # 在雜湊表中新增鍵值對 (key, value) + hmap.put(12836, "小哈") + hmap.put(15937, "小囉") + hmap.put(16750, "小算") + hmap.put(13276, "小法") + hmap.put(10583, "小鴨") + puts "\n新增完成後,雜湊表為\nKey -> Value" + hmap.print + + # 查詢操作 + # 向雜湊表中輸入鍵 key , 得到值 value + name = hmap.get(15937) + puts "\n輸入學號 15937 ,查詢到姓名 #{name}" + + # 刪除操作 + # 在雜湊表中刪除值對 (key, value) + hmap.remove(10583) + puts "\n刪除 10583 後,雜湊表為\nKey -> Value" + hmap.print + + # 走訪雜湊表 + puts "\n走訪鍵值對 Key->Value" + for pair in hmap.entry_set + puts "#{pair.key} -> #{pair.val}" + end + + puts "\n單獨篇走訪鍵 Key" + for key in hmap.key_set + puts key + end + + puts "\n單獨走訪值 Value" + for val in hmap.value_set + puts val + end +end diff --git a/zh-hant/codes/ruby/chapter_hashing/built_in_hash.rb b/zh-hant/codes/ruby/chapter_hashing/built_in_hash.rb new file mode 100644 index 000000000..28b33f42a --- /dev/null +++ b/zh-hant/codes/ruby/chapter_hashing/built_in_hash.rb @@ -0,0 +1,34 @@ +=begin +File: built_in_hash.rb +Created Time: 2024-04-13 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +require_relative '../utils/list_node' + +### Driver Code ### +if __FILE__ == $0 + num = 3 + hash_num = num.hash + puts "整數 #{num} 的雜湊值為 #{hash_num}" + + bol = true + hash_bol = bol.hash + puts "布林量 #{bol} 的雜湊值為 #{hash_bol}" + + dec = 3.14159 + hash_dec = dec.hash + puts "小數 #{dec} 的雜湊值為 #{hash_dec}" + + str = "Hello 演算法" + hash_str = str.hash + puts "字串 #{str} 的雜湊值為 #{hash_str}" + + tup = [12836, '小哈'] + hash_tup = tup.hash + puts "元組 #{tup} 的雜湊值為 #{hash_tup}" + + obj = ListNode.new(0) + hash_obj = obj.hash + puts "節點物件 #{obj} 的雜湊值為 #{hash_obj}" +end diff --git a/zh-hant/codes/ruby/chapter_hashing/hash_map.rb b/zh-hant/codes/ruby/chapter_hashing/hash_map.rb new file mode 100644 index 000000000..1a6fcd284 --- /dev/null +++ b/zh-hant/codes/ruby/chapter_hashing/hash_map.rb @@ -0,0 +1,44 @@ +=begin +File: hash_map.rb +Created Time: 2024-04-14 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +require_relative '../utils/print_util' + +### Driver Code ### +if __FILE__ == $0 + # 初始化雜湊表 + hmap = {} + + # 新增操作 + # 在雜湊表中新增鍵值對 (key, value) + hmap[12836] = "小哈" + hmap[15937] = "小囉" + hmap[16750] = "小算" + hmap[13276] = "小法" + hmap[10583] = "小鴨" + puts "\n新增完成後,雜湊表為\nKey -> Value" + print_hash_map(hmap) + + # 查詢操作 + # 向雜湊表中輸入鍵 key ,得到值 value + name = hmap[15937] + puts "\n輸入學號 15937 ,查詢到姓名 #{name}" + + # 刪除操作 + # 在雜湊表中刪除鍵值對 (key, value) + hmap.delete(10583) + puts "\n刪除 10583 後,雜湊表為\nKey -> Value" + print_hash_map(hmap) + + # 走訪雜湊表 + puts "\n走訪鍵值對 Key->Value" + hmap.entries.each { |key, value| puts "#{key} -> #{value}" } + + puts "\n單獨走訪鍵 Key" + hmap.keys.each { |key| puts key } + + puts "\n單獨走訪值 Value" + hmap.values.each { |val| puts val } +end diff --git a/zh-hant/codes/ruby/chapter_hashing/hash_map_chaining.rb b/zh-hant/codes/ruby/chapter_hashing/hash_map_chaining.rb new file mode 100644 index 000000000..2d7ef7346 --- /dev/null +++ b/zh-hant/codes/ruby/chapter_hashing/hash_map_chaining.rb @@ -0,0 +1,128 @@ +=begin +File: hash_map_chaining.rb +Created Time: 2024-04-13 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +require_relative './array_hash_map' + +### 鍵式位址雜湊表 ### +class HashMapChaining + ### 建構子 ### + def initialize + @size = 0 # 鍵值對數量 + @capacity = 4 # 雜湊表容量 + @load_thres = 2.0 / 3.0 # 觸發擴容的負載因子閾值 + @extend_ratio = 2 # 擴容倍數 + @buckets = Array.new(@capacity) { [] } # 桶陣列 + end + + ### 雜湊函式 ### + def hash_func(key) + key % @capacity + end + + ### 負載因子 ### + def load_factor + @size / @capacity + end + + ### 查詢操作 ### + def get(key) + index = hash_func(key) + bucket = @buckets[index] + # 走訪桶,若找到 key ,則返回對應 val + for pair in bucket + return pair.val if pair.key == key + end + # 若未找到 key , 則返回 nil + nil + end + + ### 新增操作 ### + def put(key, val) + # 當負載因子超過閾值時,執行擴容 + extend if load_factor > @load_thres + index = hash_func(key) + bucket = @buckets[index] + # 走訪桶,若遇到指定 key ,則更新對應 val 並返回 + for pair in bucket + if pair.key == key + pair.val = val + return + end + end + # 若無該 key ,則將鍵值對新增至尾部 + pair = Pair.new(key, val) + bucket << pair + @size += 1 + end + + ### 刪除操作 ### + def remove(key) + index = hash_func(key) + bucket = @buckets[index] + # 走訪桶,從中刪除鍵值對 + for pair in bucket + if pair.key == key + bucket.delete(pair) + @size -= 1 + break + end + end + end + + ### 擴容雜湊表 ### + def extend + # 暫存原雜湊表 + buckets = @buckets + # 初始化擴容後的新雜湊表 + @capacity *= @extend_ratio + @buckets = Array.new(@capacity) { [] } + @size = 0 + # 將鍵值對從原雜湊表搬運至新雜湊表 + for bucket in buckets + for pair in bucket + put(pair.key, pair.val) + end + end + end + + ### 列印雜湊表 ### + def print + for bucket in @buckets + res = [] + for pair in bucket + res << "#{pair.key} -> #{pair.val}" + end + pp res + end + end +end + +### Driver Code ### +if __FILE__ == $0 + ### 初始化雜湊表 + hashmap = HashMapChaining.new + + # 新增操作 + # 在雜湊表中新增鍵值對 (key, value) + hashmap.put(12836, "小哈") + hashmap.put(15937, "小囉") + hashmap.put(16750, "小算") + hashmap.put(13276, "小法") + hashmap.put(10583, "小鴨") + puts "\n新增完成後,雜湊表為\n[Key1 -> Value1, Key2 -> Value2, ...]" + hashmap.print + + # 查詢操作 + # 向雜湊表中輸入鍵 key ,得到值 value + name = hashmap.get(13276) + puts "\n輸入學號 13276 ,查詢到姓名 #{name}" + + # 刪除操作 + # 在雜湊表中刪除鍵值對 (key, value) + hashmap.remove(12836) + puts "\n刪除 12836 後,雜湊表為\n[Key1 -> Value1, Key2 -> Value2, ...]" + hashmap.print +end diff --git a/zh-hant/codes/ruby/chapter_hashing/hash_map_open_addressing.rb b/zh-hant/codes/ruby/chapter_hashing/hash_map_open_addressing.rb new file mode 100644 index 000000000..c31587eae --- /dev/null +++ b/zh-hant/codes/ruby/chapter_hashing/hash_map_open_addressing.rb @@ -0,0 +1,147 @@ +=begin +File: hash_map_open_addressing.rb +Created Time: 2024-04-13 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +require_relative './array_hash_map' + +### 開放定址雜湊表 ### +class HashMapOpenAddressing + TOMBSTONE = Pair.new(-1, '-1') # 刪除標記 + + ### 建構子 ### + def initialize + @size = 0 # 鍵值對數量 + @capacity = 4 # 雜湊表容量 + @load_thres = 2.0 / 3.0 # 觸發擴容的負載因子閾值 + @extend_ratio = 2 # 擴容倍數 + @buckets = Array.new(@capacity) # 桶陣列 + end + + ### 雜湊函式 ### + def hash_func(key) + key % @capacity + end + + ### 負載因子 ### + def load_factor + @size / @capacity + end + + ### 搜尋 key 對應的桶索引 ### + def find_bucket(key) + index = hash_func(key) + first_tombstone = -1 + # 線性探查,當遇到空桶時跳出 + while !@buckets[index].nil? + # 若遇到 key ,返回對應的桶索引 + if @buckets[index].key == key + # 若之前遇到了刪除標記,則將鍵值對移動至該索引處 + if first_tombstone != -1 + @buckets[first_tombstone] = @buckets[index] + @buckets[index] = TOMBSTONE + return first_tombstone # 返回移動後的桶索引 + end + return index # 返回桶索引 + end + # 記錄遇到的首個刪除標記 + first_tombstone = index if first_tombstone == -1 && @buckets[index] == TOMBSTONE + # 計算桶索引,越過尾部則返回頭部 + index = (index + 1) % @capacity + end + # 若 key 不存在,則返回新增點的索引 + first_tombstone == -1 ? index : first_tombstone + end + + ### 查詢操作 ### + def get(key) + # 搜尋 key 對應的桶索引 + index = find_bucket(key) + # 若找到鍵值對,則返回對應 val + return @buckets[index].val unless [nil, TOMBSTONE].include?(@buckets[index]) + # 若鍵值對不存在,則返回 nil + nil + end + + ### 新增操作 ### + def put(key, val) + # 當負載因子超過閾值時,執行擴容 + extend if load_factor > @load_thres + # 搜尋 key 對應的桶索引 + index = find_bucket(key) + # 若找到鍵值對,則覆蓋 val 開返回 + unless [nil, TOMBSTONE].include?(@buckets[index]) + @buckets[index].val = val + return + end + # 若鍵值對不存在,則新增該鍵值對 + @buckets[index] = Pair.new(key, val) + @size += 1 + end + + ### 刪除操作 ### + def remove(key) + # 搜尋 key 對應的桶索引 + index = find_bucket(key) + # 若找到鍵值對,則用刪除標記覆蓋它 + unless [nil, TOMBSTONE].include?(@buckets[index]) + @buckets[index] = TOMBSTONE + @size -= 1 + end + end + + ### 擴容雜湊表 ### + def extend + # 暫存原雜湊表 + buckets_tmp = @buckets + # 初始化擴容後的新雜湊表 + @capacity *= @extend_ratio + @buckets = Array.new(@capacity) + @size = 0 + # 將鍵值對從原雜湊表搬運至新雜湊表 + for pair in buckets_tmp + put(pair.key, pair.val) unless [nil, TOMBSTONE].include?(pair) + end + end + + ### 列印雜湊表 ### + def print + for pair in @buckets + if pair.nil? + puts "Nil" + elsif pair == TOMBSTONE + puts "TOMBSTONE" + else + puts "#{pair.key} -> #{pair.val}" + end + end + end +end + +### Driver Code ### +if __FILE__ == $0 + # 初始化雜湊表 + hashmap = HashMapOpenAddressing.new + + # 新增操作 + # 在雜湊表中新增鍵值對 (key, val) + hashmap.put(12836, "小哈") + hashmap.put(15937, "小囉") + hashmap.put(16750, "小算") + hashmap.put(13276, "小法") + hashmap.put(10583, "小鴨") + puts "\n新增完成後,雜湊表為\nKey -> Value" + hashmap.print + + # 查詢操作 + # 向雜湊表中輸入鍵 key ,得到值 val + name = hashmap.get(13276) + puts "\n輸入學號 13276 ,查詢到姓名 #{name}" + + # 刪除操作 + # 在雜湊表中刪除鍵值對 (key, val) + hashmap.remove(16750) + puts "\n刪除 16750 後,雜湊表為\nKey -> Value" + hashmap.print +end diff --git a/zh-hant/codes/ruby/chapter_hashing/simple_hash.rb b/zh-hant/codes/ruby/chapter_hashing/simple_hash.rb new file mode 100644 index 000000000..c8838a074 --- /dev/null +++ b/zh-hant/codes/ruby/chapter_hashing/simple_hash.rb @@ -0,0 +1,62 @@ +=begin +File: simple_hash.rb +Created Time: 2024-04-14 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +### 加法雜湊 ### +def add_hash(key) + hash = 0 + modulus = 1_000_000_007 + + key.each_char { |c| hash += c.ord } + + hash % modulus +end + +### 乘法雜湊 ### +def mul_hash(key) + hash = 0 + modulus = 1_000_000_007 + + key.each_char { |c| hash = 31 * hash + c.ord } + + hash % modulus +end + +### 互斥或雜湊 ### +def xor_hash(key) + hash = 0 + modulus = 1_000_000_007 + + key.each_char { |c| hash ^= c.ord } + + hash % modulus +end + +### 旋轉雜湊 ### +def rot_hash(key) + hash = 0 + modulus = 1_000_000_007 + + key.each_char { |c| hash = (hash << 4) ^ (hash >> 28) ^ c.ord } + + hash % modulus +end + +### Driver Code ### +if __FILE__ == $0 + key = "Hello 演算法" + + hash = add_hash(key) + puts "加法雜湊值為 #{hash}" + + hash = mul_hash(key) + puts "乘法雜湊值為 #{hash}" + + hash = xor_hash(key) + puts "互斥或雜湊值為 #{hash}" + + hash = rot_hash(key) + puts "旋轉雜湊值為 #{hash}" +end diff --git a/zh-hant/codes/ruby/chapter_sorting/quick_sort.rb b/zh-hant/codes/ruby/chapter_sorting/quick_sort.rb new file mode 100644 index 000000000..aeb031dbe --- /dev/null +++ b/zh-hant/codes/ruby/chapter_sorting/quick_sort.rb @@ -0,0 +1,154 @@ +=begin +File: quick_sort.rb +Created Time: 2024-04-01 +Author: Cy (3739004@gmail.com), Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +### 快速排序類別 ### +class QuickSort + class << self + ### 哨兵劃分 ### + def partition(nums, left, right) + + # 以 nums[left] 為基準數 + i, j = left, right + while i < j + while i < j && nums[j] >= nums[left] + j -= 1 # 從右向左找首個小於基準數的元素 + end + while i < j && nums[i] <= nums[left] + i += 1 # 從左向右找首個大於基準數的元素 + end + # 元素交換 + nums[i], nums[j] = nums[j], nums[i] + end + # 將基準數交換至兩子陣列的分界線 + nums[i], nums[left] = nums[left], nums[i] + i # 返回基準數的索引 + end + + ### 快速排序類別 ### + def quick_sort(nums, left, right) + # 子陣列長度不為 1 時遞迴 + if left < right + # 哨兵劃分 + pivot = partition(nums, left, right) + # 遞迴左子陣列、右子陣列 + quick_sort(nums, left, pivot - 1) + quick_sort(nums, pivot + 1, right) + end + nums + end + end +end + +### 快速排序類別(中位數最佳化)### +class QuickSortMedian + class << self + ### 選取三個候選元素的中位數 ### + def median_three(nums, left, mid, right) + # 選取三個候選元素的中位數 + _l, _m, _r = nums[left], nums[mid], nums[right] + # m 在 l 和 r 之間 + return mid if (_l <= _m && _m <= _r) || (_r <= _m && _m <= _l) + # l 在 m 和 r 之間 + return left if (_m <= _l && _l <= _r) || (_r <= _l && _l <= _m) + return right + end + + ### 哨兵劃分(三數取中值)### + def partition(nums, left, right) + ### 以 nums[left] 為基準數 + med = median_three(nums, left, (left + right) / 2, right) + # 將中位數交換至陣列最左斷 + nums[left], nums[med] = nums[med], nums[left] + i, j = left, right + while i < j + while i < j && nums[j] >= nums[left] + j -= 1 # 從右向左找首個小於基準數的元素 + end + while i < j && nums[i] <= nums[left] + i += 1 # 從左向右找首個大於基準數的元素 + end + # 元素交換 + nums[i], nums[j] = nums[j], nums[i] + end + # 將基準數交換至兩子陣列的分界線 + nums[i], nums[left] = nums[left], nums[i] + i # 返回基準數的索引 + end + + ### 快速排序 ### + def quick_sort(nums, left, right) + # 子陣列長度不為 1 時遞迴 + if left < right + # 哨兵劃分 + pivot = partition(nums, left, right) + # 遞迴左子陣列、右子陣列 + quick_sort(nums, left, pivot - 1) + quick_sort(nums, pivot + 1, right) + end + nums + end + end +end + +### 快速排序類別(尾遞迴最佳化)### +class QuickSortTailCall + class << self + ### 哨兵劃分 ### + def partition(nums, left, right) + # 以 nums[left]為基準數 + i = left + j = right + while i < j + while i < j && nums[j] >= nums[left] + j -= 1 # 從右向左找首個小於基準數的元素 + end + while i < j && nums[i] <= nums[left] + i += 1 # 從左向右找首個大於基準數的元素 + end + # 元素交換 + nums[i], nums[j] = nums[j], nums[i] + end + # 將基準數交換至兩子陣列的分界線 + nums[i], nums[left] = nums[left], nums[i] + i # 返回基準數的索引 + end + + ### 快速排序(尾遞迴最佳化) + def quick_sort(nums, left, right) + # 子陣列長度不為 1 時遞迴 + while left < right + # 哨兵劃分 + pivot = partition(nums, left, right) + # 對兩個子陣列中較短的那個執行快速排序 + if pivot - left < right - pivot + quick_sort(nums, left, pivot - 1) + left = pivot + 1 # 剩餘未排序區間為 [pivot + 1, right] + else + quick_sort(nums, pivot + 1, right) + right = pivot - 1 # 剩餘未排序區間為 [left, pivot - 1] + end + end + end + end +end + +### Driver Code ### +if __FILE__ == $0 + # 快速排序 + nums = [2, 4, 1, 0, 3, 5] + QuickSort.quick_sort(nums, 0, nums.length - 1) + puts "快速排序完成後 nums = #{nums}" + + # 快速排序(中位基準數最佳化) + nums1 = [2, 4, 1, 0, 3, 5] + QuickSortMedian.quick_sort(nums1, 0, nums1.length - 1) + puts "快速排序(中位基準數最佳化)完成後 nums1 = #{nums1}" + + # 快速排序(尾遞迴最佳化) + nums2 = [2, 4, 1, 0, 3, 5] + QuickSortTailCall.quick_sort(nums2, 0, nums2.length - 1) + puts "快速排序(尾遞迴最佳化)完成後 nums2 = #{nums2}" +end diff --git a/zh-hant/codes/ruby/chapter_tree/array_binary_tree.rb b/zh-hant/codes/ruby/chapter_tree/array_binary_tree.rb new file mode 100644 index 000000000..d200c1512 --- /dev/null +++ b/zh-hant/codes/ruby/chapter_tree/array_binary_tree.rb @@ -0,0 +1,124 @@ +=begin +File: array_binary_tree.rb +Created Time: 2024-04-17 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +require_relative '../utils/tree_node' +require_relative '../utils/print_util' + +### 陣列表示下的二元樹類別 ### +class ArrayBinaryTree + ### 建構子 ### + def initialize(arr) + @tree = arr.to_a + end + + ### 串列容量 ### + def size + @tree.length + end + + ### 獲取索引為 i 節點的值 ### + def val(i) + # 若索引越界,則返回 nil ,代表空位 + return if i < 0 || i >= size + + @tree[i] + end + + ### 獲取索引為 i 節點的左子節點的索引 ### + def left(i) + 2 * i + 1 + end + + ### 獲取索引為 i 節點的右子節點的索引 ### + def right(i) + 2 * i + 2 + end + + ### 獲取索引為 i 節點的父節點的索引 ### + def parent(i) + (i - 1) / 2 + end + + ### 層序走訪 ### + def level_order + @res = [] + + # 直接走訪陣列 + for i in 0...size + @res << val(i) unless val(i).nil? + end + + @res + end + + ### 深度優先走訪 ### + def dfs(i, order) + return if val(i).nil? + # 前序走訪 + @res << val(i) if order == :pre + dfs(left(i), order) + # 中序走訪 + @res << val(i) if order == :in + dfs(right(i), order) + # 後序走訪 + @res << val(i) if order == :post + end + + ### 前序走訪 ### + def pre_order + @res = [] + dfs(0, :pre) + @res + end + + ### 中序走訪 ### + def in_order + @res = [] + dfs(0, :in) + @res + end + + ### 後序走訪 ### + def post_order + @res = [] + dfs(0, :post) + @res + end +end + +### Driver Code ### +if __FILE__ == $0 + # 初始化二元樹 + # 這裡藉助了一個從陣列直接生成二元樹的函式 + arr = [1, 2, 3, 4, nil, 6, 7, 8, 9, nil, nil, 12, nil, nil, 15] + root = arr_to_tree(arr) + puts "\n初始化二元樹\n\n" + puts '二元樹的陣列表示:' + pp arr + puts '二元樹的鏈結串列表示:' + print_tree(root) + + # 陣列表示下的二元樹類別 + abt = ArrayBinaryTree.new(arr) + + # 訪問節點 + i = 1 + l, r, _p = abt.left(i), abt.right(i), abt.parent(i) + puts "\n當前節點的索引為 #{i} ,值為 #{abt.val(i).inspect}" + puts "其左子節點的索引為 #{l} ,值為 #{abt.val(l).inspect}" + puts "其右子節點的索引為 #{r} ,值為 #{abt.val(r).inspect}" + puts "其父節點的索引為 #{_p} ,值為 #{abt.val(_p).inspect}" + + # 走訪樹 + res = abt.level_order + puts "\n層序走訪為: #{res}" + res = abt.pre_order + puts "前序走訪為: #{res}" + res = abt.in_order + puts "中序走訪為: #{res}" + res = abt.post_order + puts "後序走訪為: #{res}" +end diff --git a/zh-hant/codes/ruby/chapter_tree/avl_tree.rb b/zh-hant/codes/ruby/chapter_tree/avl_tree.rb new file mode 100644 index 000000000..0ab4460fa --- /dev/null +++ b/zh-hant/codes/ruby/chapter_tree/avl_tree.rb @@ -0,0 +1,216 @@ +=begin +File: avl_tree.rb +Created Time: 2024-04-17 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +require_relative '../utils/tree_node' +require_relative '../utils/print_util' + +### AVL 樹 ### +class AVLTree + ### 建構子 ### + def initialize + @root = nil + end + + ### 獲取二元樹根節點 ### + def get_root + @root + end + + ### 獲取節點高度 ### + def height(node) + # 空節點高度為 -1 ,葉節點高度為 0 + return node.height unless node.nil? + + -1 + end + + ### 更新節點高度 ### + def update_height(node) + # 節點高度等於最高子樹高度 + 1 + node.height = [height(node.left), height(node.right)].max + 1 + end + + ### 獲取平衡因子 ### + def balance_factor(node) + # 空節點平衡因子為 0 + return 0 if node.nil? + + # 節點平衡因子 = 左子樹高度 - 右子樹高度 + height(node.left) - height(node.right) + end + + ### 右旋操作 ### + def right_rotate(node) + child = node.left + grand_child = child.right + # 以 child 為原點,將 node 向右旋轉 + child.right = node + node.left = grand_child + # 更新節點高度 + update_height(node) + update_height(child) + # 返回旋轉後子樹的根節點 + child + end + + ### 左旋操作 ### + def left_rotate(node) + child = node.right + grand_child = child.left + # 以 child 為原點,將 node 向左旋轉 + child.left = node + node.right = grand_child + # 更新節點高度 + update_height(node) + update_height(child) + # 返回旋轉後子樹的根節點 + child + end + + ### 執行旋轉操作,使該子樹重新恢復平衡 ### + def rotate(node) + # 獲取節點 node 的平衡因子 + balance_factor = balance_factor(node) + # 左遍樹 + if balance_factor > 1 + if balance_factor(node.left) >= 0 + # 右旋 + return right_rotate(node) + else + # 先左旋後右旋 + node.left = left_rotate(node.left) + return right_rotate(node) + end + # 右遍樹 + elsif balance_factor < -1 + if balance_factor(node.right) <= 0 + # 左旋 + return left_rotate(node) + else + # 先右旋後左旋 + node.right = right_rotate(node.right) + return left_rotate(node) + end + end + # 平衡樹,無須旋轉,直接返回 + node + end + + ### 插入節點 ### + def insert(val) + @root = insert_helper(@root, val) + end + + ### 遞迴插入節點(輔助方法)### + def insert_helper(node, val) + return TreeNode.new(val) if node.nil? + # 1. 查詢插入位置並插入節點 + if val < node.val + node.left = insert_helper(node.left, val) + elsif val > node.val + node.right = insert_helper(node.right, val) + else + # 重複節點不插入,直接返回 + return node + end + # 更新節點高度 + update_height(node) + # 2. 執行旋轉操作,使該子樹重新恢復平衡 + rotate(node) + end + + ### 刪除節點 ### + def remove(val) + @root = remove_helper(@root, val) + end + + ### 遞迴刪除節點(輔助方法)### + def remove_helper(node, val) + return if node.nil? + # 1. 查詢節點並刪除 + if val < node.val + node.left = remove_helper(node.left, val) + elsif val > node.val + node.right = remove_helper(node.right, val) + else + if node.left.nil? || node.right.nil? + child = node.left || node.right + # 子節點數量 = 0 ,直接刪除 node 並返回 + return if child.nil? + # 子節點數量 = 1 ,直接刪除 node + node = child + else + # 子節點數量 = 2 ,則將中序走訪的下個節點刪除,並用該節點替換當前節點 + temp = node.right + while !temp.left.nil? + temp = temp.left + end + node.right = remove_helper(node.right, temp.val) + node.val = temp.val + end + end + # 更新節點高度 + update_height(node) + # 2. 執行旋轉操作,使該子樹重新恢復平衡 + rotate(node) + end + + ### 查詢節點 ### + def search(val) + cur = @root + # 迴圈查詢,越過葉節點後跳出 + while !cur.nil? + # 目標節點在 cur 的右子樹中 + if cur.val < val + cur = cur.right + # 目標節點在 cur 的左子樹中 + elsif cur.val > val + cur = cur.left + # 找到目標節點,跳出迴圈 + else + break + end + end + # 返回目標節點 + cur + end +end + +### Driver Code ### +if __FILE__ == $0 + def test_insert(tree, val) + tree.insert(val) + puts "\n插入節點 #{val} 後,AVL 樹為" + print_tree(tree.get_root) + end + + def test_remove(tree, val) + tree.remove(val) + puts "\n刪除節點 #{val} 後,AVL 樹為" + print_tree(tree.get_root) + end + + # 初始化空 AVL 樹 + avl_tree = AVLTree.new + + # 插入節點 + # 請關注插入節點後,AVL 樹是如何保持平衡的 + for val in [1, 2, 3, 4, 5, 8, 7, 9, 10, 6] + test_insert(avl_tree, val) + end + + # 插入重複節點 + test_insert(avl_tree, 7) + + # 刪除節點 + # 請關注刪除節點後,AVL 樹是如何保持平衡的 + test_remove(avl_tree, 8) # 刪除度為 0 的節點 + test_remove(avl_tree, 5) # 刪除度為 1 的節點 + test_remove(avl_tree, 4) # 刪除度為 2 的節點 + + result_node = avl_tree.search(7) + puts "\n查詢到的節點物件為 #{result_node},節點值 = #{result_node.val}" +end diff --git a/zh-hant/codes/ruby/chapter_tree/binary_search_tree.rb b/zh-hant/codes/ruby/chapter_tree/binary_search_tree.rb new file mode 100644 index 000000000..cd73e1b11 --- /dev/null +++ b/zh-hant/codes/ruby/chapter_tree/binary_search_tree.rb @@ -0,0 +1,161 @@ +=begin +File: binary_search_tree.rb +Created Time: 2024-04-18 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +require_relative '../utils/tree_node' +require_relative '../utils/print_util' + +### 二元搜尋樹 ### +class BinarySearchTree + ### 建構子 ### + def initialize + # 初始化空樹 + @root = nil + end + + ### 獲取二元樹根節點 ### + def get_root + @root + end + + ### 查詢節點 ### + def search(num) + cur = @root + + # 迴圈查詢,越過葉節點後跳出 + while !cur.nil? + # 目標節點在 cur 的右子樹中 + if cur.val < num + cur = cur.right + # 目標節點在 cur 的左子樹中 + elsif cur.val > num + cur = cur.left + # 找到目標節點,跳出迴圈 + else + break + end + end + + cur + end + + ### 插入節點 ### + def insert(num) + # 若樹為空,則初始化根節點 + if @root.nil? + @root = TreeNode.new(num) + return + end + + # 迴圈查詢,越過葉節點後跳出 + cur, pre = @root, nil + while !cur.nil? + # 找到重複節點,直接返回 + return if cur.val == num + + pre = cur + # 插入位置在 cur 的右子樹中 + if cur.val < num + cur = cur.right + # 插入位置在 cur 的左子樹中 + else + cur = cur.left + end + end + + # 插入節點 + node = TreeNode.new(num) + if pre.val < num + pre.right = node + else + pre.left = node + end + end + + ### 刪除節點 ### + def remove(num) + # 若樹為空,直接提前返回 + return if @root.nil? + + # 迴圈查詢,越過葉節點後跳出 + cur, pre = @root, nil + while !cur.nil? + # 找到待刪除節點,跳出迴圈 + break if cur.val == num + + pre = cur + # 待刪除節點在 cur 的右子樹中 + if cur.val < num + cur = cur.right + # 待刪除節點在 cur 的左子樹中 + else + cur = cur.left + end + end + # 若無待刪除節點,則直接返回 + return if cur.nil? + + # 子節點數量 = 0 or 1 + if cur.left.nil? || cur.right.nil? + # 當子節點數量 = 0 / 1 時, child = null / 該子節點 + child = cur.left || cur.right + # 刪除節點 cur + if cur != @root + if pre.left == cur + pre.left = child + else + pre.right = child + end + else + # 若刪除節點為根節點,則重新指定根節點 + @root = child + end + # 子節點數量 = 2 + else + # 獲取中序走訪中 cur 的下一個節點 + tmp = cur.right + while !tmp.left.nil? + tmp = tmp.left + end + # 遞迴刪除節點 tmp + remove(tmp.val) + # 用 tmp 覆蓋 cur + cur.val = tmp.val + end + end +end + +### Driver Code ### +if __FILE__ == $0 + # 初始化二元搜尋樹 + bst = BinarySearchTree.new + nums = [8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15] + # 請注意,不同的插入順序會生成不同的二元樹,該序列可以生成一個完美二元樹 + nums.each { |num| bst.insert(num) } + puts "\n初始化的二元樹為\n" + print_tree(bst.get_root) + + # 查詢節點 + node = bst.search(7) + puts "\n查詢到的節點物件為: #{node},節點值 = #{node.val}" + + # 插入節點 + bst.insert(16) + puts "\n插入節點 16 後,二元樹為\n" + print_tree(bst.get_root) + + # 刪除節點 + bst.remove(1) + puts "\n刪除節點 1 後,二元樹為\n" + print_tree(bst.get_root) + + bst.remove(2) + puts "\n刪除節點 2 後,二元樹為\n" + print_tree(bst.get_root) + + bst.remove(4) + puts "\n刪除節點 4 後,二元樹為\n" + print_tree(bst.get_root) +end diff --git a/zh-hant/codes/ruby/chapter_tree/binary_tree.rb b/zh-hant/codes/ruby/chapter_tree/binary_tree.rb new file mode 100644 index 000000000..3de63105c --- /dev/null +++ b/zh-hant/codes/ruby/chapter_tree/binary_tree.rb @@ -0,0 +1,38 @@ +=begin +File: binary_tree.rb +Created Time: 2024-04-18 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +require_relative '../utils/tree_node' +require_relative '../utils/print_util' + +### Driver Code ### +if __FILE__ == $0 + # 初始化二元樹 + # 初始化節點 + n1 = TreeNode.new(1) + n2 = TreeNode.new(2) + n3 = TreeNode.new(3) + n4 = TreeNode.new(4) + n5 = TreeNode.new(5) + # 構建節點之間的引用(指標) + n1.left = n2 + n1.right = n3 + n2.left = n4 + n2.right = n5 + puts "\n初始化二元樹\n\n" + print_tree(n1) + + # 插入與刪除節點 + _p = TreeNode.new(0) + # 在 n1 -> n2 中間插入節點 _p + n1.left = _p + _p.left = n2 + puts "\n插入節點 _p 後\n\n" + print_tree(n1) + # 刪除節點 + n1.left = n2 + puts "\n刪除節點 _p 後\n\n" + print_tree(n1) +end diff --git a/zh-hant/codes/ruby/chapter_tree/binary_tree_bfs.rb b/zh-hant/codes/ruby/chapter_tree/binary_tree_bfs.rb new file mode 100644 index 000000000..ec57ac183 --- /dev/null +++ b/zh-hant/codes/ruby/chapter_tree/binary_tree_bfs.rb @@ -0,0 +1,36 @@ +=begin +File: binary_tree_bfs.rb +Created Time: 2024-04-18 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +require_relative '../utils/tree_node' +require_relative '../utils/print_util' + +### 層序走訪 ### +def level_order(root) + # 初始化佇列,加入根節點 + queue = [root] + # 初始化一個串列,用於儲存走訪序列 + res = [] + while !queue.empty? + node = queue.shift # 隊列出隊 + res << node.val # 儲存節點值 + queue << node.left unless node.left.nil? # 左子節點入列 + queue << node.right unless node.right.nil? # 右子節點入列 + end + res +end + +### Driver Code ### +if __FILE__ == $0 + # 初始化二元樹 + # 這裡藉助了一個從陣列直接生成二元樹的函式 + root = arr_to_tree([1, 2, 3, 4, 5, 6, 7]) + puts "\n初始化二元樹\n\n" + print_tree(root) + + # 層序走訪 + res = level_order(root) + puts "\n層序走訪的節點列印序列 = #{res}" +end diff --git a/zh-hant/codes/ruby/chapter_tree/binary_tree_dfs.rb b/zh-hant/codes/ruby/chapter_tree/binary_tree_dfs.rb new file mode 100644 index 000000000..0a2c4cfd0 --- /dev/null +++ b/zh-hant/codes/ruby/chapter_tree/binary_tree_dfs.rb @@ -0,0 +1,62 @@ +=begin +File: binary_tree_dfs.rb +Created Time: 2024-04-18 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +require_relative '../utils/tree_node' +require_relative '../utils/print_util' + +### 前序走訪 ### +def pre_order(root) + return if root.nil? + + # 訪問優先順序:根節點 -> 左子樹 -> 右子樹 + $res << root.val + pre_order(root.left) + pre_order(root.right) +end + +### 中序走訪 ### +def in_order(root) + return if root.nil? + + # 訪問優先順序:左子樹 -> 根節點 -> 右子樹 + in_order(root.left) + $res << root.val + in_order(root.right) +end + +### 後序走訪 ### +def post_order(root) + return if root.nil? + + # 訪問優先順序:左子樹 -> 右子樹 -> 根節點 + post_order(root.left) + post_order(root.right) + $res << root.val +end + +### Driver Code ### +if __FILE__ == $0 + # 初始化二元樹 + # 這裡藉助了一個從陣列直接生成二元樹的函式 + root = arr_to_tree([1, 2, 3, 4, 5, 6, 7]) + puts "\n初始化二元樹\n\n" + print_tree(root) + + # 前序走訪 + $res = [] + pre_order(root) + puts "\n前序走訪的節點列印序列 = #{$res}" + + # 中序走訪 + $res.clear + in_order(root) + puts "\nn中序走訪的節點列印序列 = #{$res}" + + # 後序走訪 + $res.clear + post_order(root) + puts "\nn後序走訪的節點列印序列 = #{$res}" +end diff --git a/zh-hant/codes/ruby/utils/print_util.rb b/zh-hant/codes/ruby/utils/print_util.rb index 04aa4465b..78ab6621a 100644 --- a/zh-hant/codes/ruby/utils/print_util.rb +++ b/zh-hant/codes/ruby/utils/print_util.rb @@ -56,3 +56,8 @@ def print_tree(root, prev=nil, is_right=false) trunk.str = " |" print_tree(root.left, trunk, false) end + +### 列印雜湊表 ### +def print_hash_map(hmap) + hmap.entries.each { |key, value| puts "#{key} -> #{value}" } +end diff --git a/zh-hant/codes/ruby/utils/tree_node.rb b/zh-hant/codes/ruby/utils/tree_node.rb index 61f8eedd3..186ff12bd 100644 --- a/zh-hant/codes/ruby/utils/tree_node.rb +++ b/zh-hant/codes/ruby/utils/tree_node.rb @@ -16,3 +16,38 @@ class TreeNode @height = 0 end end + +### 將串列反序列化為二元數樹:遞迴 ### +def arr_to_tree_dfs(arr, i) + # 如果索引超出陣列長度,或者對應的元素為 nil ,則返回 nil + return if i < 0 || i >= arr.length || arr[i].nil? + # 構建當前節點 + root = TreeNode.new(arr[i]) + # 遞迴構建左右子樹 + root.left = arr_to_tree_dfs(arr, 2 * i + 1) + root.right = arr_to_tree_dfs(arr, 2 * i + 2) + root +end + +### 將串列反序列化為二元樹 ### +def arr_to_tree(arr) + arr_to_tree_dfs(arr, 0) +end + +### 將二元樹序列化為串列:遞迴 ### +def tree_to_arr_dfs(root, i, res) + return if root.nil? + + res += Array.new(i - res.length + 1) if i >= res.length + res[i] = root.val + + tree_to_arr_dfs(root.left, 2 * i + 1, res) + tree_to_arr_dfs(root.right, 2 * i + 2, res) +end + +### 將二元樹序列化為串列 ### +def tree_to_arr(root) + res = [] + tree_to_arr_dfs(root, 0, res) + res +end diff --git a/zh-hant/codes/typescript/chapter_backtracking/subset_sum_i.ts b/zh-hant/codes/typescript/chapter_backtracking/subset_sum_i.ts index 761cd8df3..9799e534a 100644 --- a/zh-hant/codes/typescript/chapter_backtracking/subset_sum_i.ts +++ b/zh-hant/codes/typescript/chapter_backtracking/subset_sum_i.ts @@ -51,4 +51,4 @@ const res = subsetSumI(nums, target); console.log(`輸入陣列 nums = ${JSON.stringify(nums)}, target = ${target}`); console.log(`所有和等於 ${target} 的子集 res = ${JSON.stringify(res)}`); -export { }; +export {}; diff --git a/zh-hant/codes/typescript/chapter_backtracking/subset_sum_ii.ts b/zh-hant/codes/typescript/chapter_backtracking/subset_sum_ii.ts index c5b56fe70..03de56ae4 100644 --- a/zh-hant/codes/typescript/chapter_backtracking/subset_sum_ii.ts +++ b/zh-hant/codes/typescript/chapter_backtracking/subset_sum_ii.ts @@ -56,4 +56,4 @@ const res = subsetSumII(nums, target); console.log(`輸入陣列 nums = ${JSON.stringify(nums)}, target = ${target}`); console.log(`所有和等於 ${target} 的子集 res = ${JSON.stringify(res)}`); -export { }; +export {}; diff --git a/zh-hant/codes/typescript/chapter_computational_complexity/recursion.ts b/zh-hant/codes/typescript/chapter_computational_complexity/recursion.ts index af5182e54..f65302341 100644 --- a/zh-hant/codes/typescript/chapter_computational_complexity/recursion.ts +++ b/zh-hant/codes/typescript/chapter_computational_complexity/recursion.ts @@ -67,4 +67,4 @@ console.log(`尾遞迴函式的求和結果 res = ${res}`); res = fib(n); console.log(`費波那契數列的第 ${n} 項為 ${res}`); -export { }; +export {}; diff --git a/zh-hant/codes/typescript/chapter_graph/graph_adjacency_list.ts b/zh-hant/codes/typescript/chapter_graph/graph_adjacency_list.ts index 4849143b1..13c76a1e5 100644 --- a/zh-hant/codes/typescript/chapter_graph/graph_adjacency_list.ts +++ b/zh-hant/codes/typescript/chapter_graph/graph_adjacency_list.ts @@ -91,9 +91,8 @@ class GraphAdjList { } } -// need to add the package @types/node contains type definitions for Node.js, npm i --save-dev @types/node -if (require.main === module) { - /* Driver Code */ +/* Driver Code */ +if (import.meta.url.endsWith(process.argv[1])) { /* 初始化無向圖 */ const v0 = new Vertex(1), v1 = new Vertex(3), diff --git a/zh-hant/codes/typescript/chapter_heap/my_heap.ts b/zh-hant/codes/typescript/chapter_heap/my_heap.ts index cdaabad4f..d0aab14dd 100644 --- a/zh-hant/codes/typescript/chapter_heap/my_heap.ts +++ b/zh-hant/codes/typescript/chapter_heap/my_heap.ts @@ -122,7 +122,7 @@ class MaxHeap { } /* Driver Code */ -if (require.main === module) { +if (import.meta.url.endsWith(process.argv[1])) { /* 初始化大頂堆積 */ const maxHeap = new MaxHeap([9, 8, 6, 6, 7, 5, 2, 1, 4, 3, 6, 2]); console.log('\n輸入串列並建堆積後'); diff --git a/zh-hant/codes/typescript/chapter_tree/array_binary_tree.ts b/zh-hant/codes/typescript/chapter_tree/array_binary_tree.ts index 20c9dfb7e..96059d5a2 100644 --- a/zh-hant/codes/typescript/chapter_tree/array_binary_tree.ts +++ b/zh-hant/codes/typescript/chapter_tree/array_binary_tree.ts @@ -4,8 +4,8 @@ * Author: yuan0221 (yl1452491917@gmail.com) */ -const { arrToTree } = require('../modules/TreeNode'); -const { printTree } = require('../modules/PrintUtil'); +import { arrToTree } from '../modules/TreeNode'; +import { printTree } from '../modules/PrintUtil'; type Order = 'pre' | 'in' | 'post'; @@ -148,4 +148,4 @@ console.log('中序走訪為:' + res); res = abt.postOrder(); console.log('後序走訪為:' + res); -export { }; +export {}; diff --git a/zh-hant/codes/typescript/modules/PrintUtil.ts b/zh-hant/codes/typescript/modules/PrintUtil.ts index 58280781a..caa01fbd5 100644 --- a/zh-hant/codes/typescript/modules/PrintUtil.ts +++ b/zh-hant/codes/typescript/modules/PrintUtil.ts @@ -79,9 +79,6 @@ function showTrunks(p: Trunk | null) { showTrunks(p.prev); process.stdout.write(p.str); - // ts-node to execute, we need to install type definitions for node - // solve: npm i --save-dev @types/node - // restart the vscode } /* 列印堆積 */ diff --git a/zh-hant/codes/typescript/tsconfig.json b/zh-hant/codes/typescript/tsconfig.json index 8c057ea0d..954efb8e7 100644 --- a/zh-hant/codes/typescript/tsconfig.json +++ b/zh-hant/codes/typescript/tsconfig.json @@ -1,8 +1,12 @@ { "compilerOptions": { - "target": "ES6", - "module": "CommonJS", - "outDir": "out", - "sourceMap": true - } + "baseUrl": ".", + "module": "esnext", + "moduleResolution": "node", + "types": ["@types/node"], + "noEmit": true, + "target": "esnext", + }, + "include": ["chapter_*/*.ts"], + "exclude": ["node_modules"] } diff --git a/zh-hant/docs/chapter_divide_and_conquer/divide_and_conquer.md b/zh-hant/docs/chapter_divide_and_conquer/divide_and_conquer.md index c80a5aeec..d6ce7d274 100644 --- a/zh-hant/docs/chapter_divide_and_conquer/divide_and_conquer.md +++ b/zh-hant/docs/chapter_divide_and_conquer/divide_and_conquer.md @@ -64,7 +64,7 @@ $$ 並行最佳化在多核或多處理器的環境中尤其有效,因為系統可以同時處理多個子問題,更加充分地利用計算資源,從而顯著減少總體的執行時間。 -比如在下圖所示的“桶排序”中,我們將海量的資料平均分配到各個桶中,則可所有桶的排序任務分散到各個計算單元,完成後再合併結果。 +比如在下圖所示的“桶排序”中,我們將海量的資料平均分配到各個桶中,則可將所有桶的排序任務分散到各個計算單元,完成後再合併結果。 ![桶排序的平行計算](divide_and_conquer.assets/divide_and_conquer_parallel_computing.png) diff --git a/zh-hant/docs/chapter_dynamic_programming/knapsack_problem.md b/zh-hant/docs/chapter_dynamic_programming/knapsack_problem.md index ef83ff6d0..bb675fac1 100644 --- a/zh-hant/docs/chapter_dynamic_programming/knapsack_problem.md +++ b/zh-hant/docs/chapter_dynamic_programming/knapsack_problem.md @@ -18,15 +18,15 @@ **第一步:思考每輪的決策,定義狀態,從而得到 $dp$ 表** -對於每個物品來說,不放入背包,背包容量不變;放入背包,背包容量減小。由此可得狀態定義:當前物品編號 $i$ 和剩餘背包容量 $c$ ,記為 $[i, c]$ 。 +對於每個物品來說,不放入背包,背包容量不變;放入背包,背包容量減小。由此可得狀態定義:當前物品編號 $i$ 和背包容量 $c$ ,記為 $[i, c]$ 。 -狀態 $[i, c]$ 對應的子問題為:**前 $i$ 個物品在剩餘容量為 $c$ 的背包中的最大價值**,記為 $dp[i, c]$ 。 +狀態 $[i, c]$ 對應的子問題為:**前 $i$ 個物品在容量為 $c$ 的背包中的最大價值**,記為 $dp[i, c]$ 。 待求解的是 $dp[n, cap]$ ,因此需要一個尺寸為 $(n+1) \times (cap+1)$ 的二維 $dp$ 表。 **第二步:找出最優子結構,進而推導出狀態轉移方程** -當我們做出物品 $i$ 的決策後,剩餘的是前 $i-1$ 個物品的決策,可分為以下兩種情況。 +當我們做出物品 $i$ 的決策後,剩餘的是前 $i-1$ 個物品決策的子問題,可分為以下兩種情況。 - **不放入物品 $i$** :背包容量不變,狀態變化為 $[i-1, c]$ 。 - **放入物品 $i$** :背包容量減少 $wgt[i-1]$ ,價值增加 $val[i-1]$ ,狀態變化為 $[i-1, c-wgt[i-1]]$ 。 @@ -41,7 +41,7 @@ $$ **第三步:確定邊界條件和狀態轉移順序** -當無物品或無剩餘背包容量時最大價值為 $0$ ,即首列 $dp[i, 0]$ 和首行 $dp[0, c]$ 都等於 $0$ 。 +當無物品或背包容量為 $0$ 時最大價值為 $0$ ,即首列 $dp[i, 0]$ 和首行 $dp[0, c]$ 都等於 $0$ 。 當前狀態 $[i, c]$ 從上方的狀態 $[i-1, c]$ 和左上方的狀態 $[i-1, c-wgt[i-1]]$ 轉移而來,因此透過兩層迴圈正序走訪整個 $dp$ 表即可。 diff --git a/zh-hant/docs/chapter_dynamic_programming/summary.md b/zh-hant/docs/chapter_dynamic_programming/summary.md index 0d85f30b0..582768c2a 100644 --- a/zh-hant/docs/chapter_dynamic_programming/summary.md +++ b/zh-hant/docs/chapter_dynamic_programming/summary.md @@ -11,7 +11,7 @@ **背包問題** - 背包問題是最典型的動態規劃問題之一,具有 0-1 背包、完全背包、多重背包等變種。 -- 0-1 背包的狀態定義為前 $i$ 個物品在剩餘容量為 $c$ 的背包中的最大價值。根據不放入背包和放入背包兩種決策,可得到最優子結構,並構建出狀態轉移方程。在空間最佳化中,由於每個狀態依賴正上方和左上方的狀態,因此需要倒序走訪串列,避免左上方狀態被覆蓋。 +- 0-1 背包的狀態定義為前 $i$ 個物品在容量為 $c$ 的背包中的最大價值。根據不放入背包和放入背包兩種決策,可得到最優子結構,並構建出狀態轉移方程。在空間最佳化中,由於每個狀態依賴正上方和左上方的狀態,因此需要倒序走訪串列,避免左上方狀態被覆蓋。 - 完全背包問題的每種物品的選取數量無限制,因此選擇放入物品的狀態轉移與 0-1 背包問題不同。由於狀態依賴正上方和正左方的狀態,因此在空間最佳化中應當正序走訪。 - 零錢兌換問題是完全背包問題的一個變種。它從求“最大”價值變為求“最小”硬幣數量,因此狀態轉移方程中的 $\max()$ 應改為 $\min()$ 。從追求“不超過”背包容量到追求“恰好”湊出目標金額,因此使用 $amt + 1$ 來表示“無法湊出目標金額”的無效解。 - 零錢兌換問題 II 從求“最少硬幣數量”改為求“硬幣組合數量”,狀態轉移方程相應地從 $\min()$ 改為求和運算子。 diff --git a/zh-hant/docs/chapter_hashing/hash_algorithm.md b/zh-hant/docs/chapter_hashing/hash_algorithm.md index fe95daa89..fc76881a4 100644 --- a/zh-hant/docs/chapter_hashing/hash_algorithm.md +++ b/zh-hant/docs/chapter_hashing/hash_algorithm.md @@ -299,7 +299,7 @@ $$ ```rust title="built_in_hash.rs" use std::collections::hash_map::DefaultHasher; use std::hash::{Hash, Hasher}; - + let num = 3; let mut num_hasher = DefaultHasher::new(); num.hash(&mut num_hasher); @@ -374,7 +374,29 @@ $$ === "Ruby" ```ruby title="built_in_hash.rb" + num = 3 + hash_num = num.hash + # 整數 3 的雜湊值為 -4385856518450339636 + bol = true + hash_bol = bol.hash + # 布林量 true 的雜湊值為 -1617938112149317027 + + dec = 3.14159 + hash_dec = dec.hash + # 小數 3.14159 的雜湊值為 -1479186995943067893 + + str = "Hello 演算法" + hash_str = str.hash + # 字串“Hello 演算法”的雜湊值為 -4075943250025831763 + + tup = [12836, '小哈'] + hash_tup = tup.hash + # 元組 (12836, '小哈') 的雜湊值為 1999544809202288822 + + obj = ListNode.new(0) + hash_obj = obj.hash + # 節點物件 # 的雜湊值為 4302940560806366381 ``` === "Zig" diff --git a/zh-hant/docs/chapter_hashing/hash_map.md b/zh-hant/docs/chapter_hashing/hash_map.md index 340b64483..b785fe178 100755 --- a/zh-hant/docs/chapter_hashing/hash_map.md +++ b/zh-hant/docs/chapter_hashing/hash_map.md @@ -31,7 +31,7 @@ ```python title="hash_map.py" # 初始化雜湊表 hmap: dict = {} - + # 新增操作 # 在雜湊表中新增鍵值對 (key, value) hmap[12836] = "小哈" @@ -39,11 +39,11 @@ hmap[16750] = "小算" hmap[13276] = "小法" hmap[10583] = "小鴨" - + # 查詢操作 # 向雜湊表中輸入鍵 key ,得到值 value name: str = hmap[15937] - + # 刪除操作 # 在雜湊表中刪除鍵值對 (key, value) hmap.pop(10583) @@ -54,7 +54,7 @@ ```cpp title="hash_map.cpp" /* 初始化雜湊表 */ unordered_map map; - + /* 新增操作 */ // 在雜湊表中新增鍵值對 (key, value) map[12836] = "小哈"; @@ -62,11 +62,11 @@ map[16750] = "小算"; map[13276] = "小法"; map[10583] = "小鴨"; - + /* 查詢操作 */ // 向雜湊表中輸入鍵 key ,得到值 value string name = map[15937]; - + /* 刪除操作 */ // 在雜湊表中刪除鍵值對 (key, value) map.erase(10583); @@ -77,19 +77,19 @@ ```java title="hash_map.java" /* 初始化雜湊表 */ Map map = new HashMap<>(); - + /* 新增操作 */ // 在雜湊表中新增鍵值對 (key, value) - map.put(12836, "小哈"); - map.put(15937, "小囉"); - map.put(16750, "小算"); + map.put(12836, "小哈"); + map.put(15937, "小囉"); + map.put(16750, "小算"); map.put(13276, "小法"); map.put(10583, "小鴨"); - + /* 查詢操作 */ // 向雜湊表中輸入鍵 key ,得到值 value String name = map.get(15937); - + /* 刪除操作 */ // 在雜湊表中刪除鍵值對 (key, value) map.remove(10583); @@ -108,11 +108,11 @@ { 13276, "小法" }, { 10583, "小鴨" } }; - + /* 查詢操作 */ // 向雜湊表中輸入鍵 key ,得到值 value string name = map[15937]; - + /* 刪除操作 */ // 在雜湊表中刪除鍵值對 (key, value) map.Remove(10583); @@ -123,7 +123,7 @@ ```go title="hash_map_test.go" /* 初始化雜湊表 */ hmap := make(map[int]string) - + /* 新增操作 */ // 在雜湊表中新增鍵值對 (key, value) hmap[12836] = "小哈" @@ -131,11 +131,11 @@ hmap[16750] = "小算" hmap[13276] = "小法" hmap[10583] = "小鴨" - + /* 查詢操作 */ // 向雜湊表中輸入鍵 key ,得到值 value name := hmap[15937] - + /* 刪除操作 */ // 在雜湊表中刪除鍵值對 (key, value) delete(hmap, 10583) @@ -146,7 +146,7 @@ ```swift title="hash_map.swift" /* 初始化雜湊表 */ var map: [Int: String] = [:] - + /* 新增操作 */ // 在雜湊表中新增鍵值對 (key, value) map[12836] = "小哈" @@ -154,11 +154,11 @@ map[16750] = "小算" map[13276] = "小法" map[10583] = "小鴨" - + /* 查詢操作 */ // 向雜湊表中輸入鍵 key ,得到值 value let name = map[15937]! - + /* 刪除操作 */ // 在雜湊表中刪除鍵值對 (key, value) map.removeValue(forKey: 10583) @@ -176,11 +176,11 @@ map.set(16750, '小算'); map.set(13276, '小法'); map.set(10583, '小鴨'); - + /* 查詢操作 */ // 向雜湊表中輸入鍵 key ,得到值 value let name = map.get(15937); - + /* 刪除操作 */ // 在雜湊表中刪除鍵值對 (key, value) map.delete(10583); @@ -200,12 +200,12 @@ map.set(10583, '小鴨'); console.info('\n新增完成後,雜湊表為\nKey -> Value'); console.info(map); - + /* 查詢操作 */ // 向雜湊表中輸入鍵 key ,得到值 value let name = map.get(15937); console.info('\n輸入學號 15937 ,查詢到姓名 ' + name); - + /* 刪除操作 */ // 在雜湊表中刪除鍵值對 (key, value) map.delete(10583); @@ -240,7 +240,7 @@ ```rust title="hash_map.rs" use std::collections::HashMap; - + /* 初始化雜湊表 */ let mut map: HashMap = HashMap::new(); @@ -272,7 +272,7 @@ ```kotlin title="hash_map.kt" /* 初始化雜湊表 */ val map = HashMap() - + /* 新增操作 */ // 在雜湊表中新增鍵值對 (key, value) map[12836] = "小哈" @@ -280,11 +280,11 @@ map[16750] = "小算" map[13276] = "小法" map[10583] = "小鴨" - + /* 查詢操作 */ // 向雜湊表中輸入鍵 key ,得到值 value val name = map[15937] - + /* 刪除操作 */ // 在雜湊表中刪除鍵值對 (key, value) map.remove(10583) @@ -293,7 +293,24 @@ === "Ruby" ```ruby title="hash_map.rb" + # 初始化雜湊表 + hmap = {} + # 新增操作 + # 在雜湊表中新增鍵值對 (key, value) + hmap[12836] = "小哈" + hmap[15937] = "小囉" + hmap[16750] = "小算" + hmap[13276] = "小法" + hmap[10583] = "小鴨" + + # 查詢操作 + # 向雜湊表中輸入鍵 key ,得到值 value + name = hmap[15937] + + # 刪除操作 + # 在雜湊表中刪除鍵值對 (key, value) + hmap.delete(10583) ``` === "Zig" @@ -476,7 +493,7 @@ // 單獨走訪鍵 Key for key in map.keys() { - println!("{key}"); + println!("{key}"); } // 單獨走訪值 Value @@ -512,7 +529,15 @@ === "Ruby" ```ruby title="hash_map.rb" + # 走訪雜湊表 + # 走訪鍵值對 key->value + hmap.entries.each { |key, value| puts "#{key} -> #{value}" } + # 單獨走訪鍵 key + hmap.keys.each { |key| puts key } + + # 單獨走訪值 value + hmap.values.each { |val| puts val } ``` === "Zig" diff --git a/zh-hant/docs/chapter_heap/heap.md b/zh-hant/docs/chapter_heap/heap.md index 0d34eb263..cf7ecb9ee 100644 --- a/zh-hant/docs/chapter_heap/heap.md +++ b/zh-hant/docs/chapter_heap/heap.md @@ -420,7 +420,7 @@ ## 堆積的實現 -下文實現的是大頂堆積。若要將其轉換為小頂堆積,只需將所有大小邏輯判斷取逆(例如,將 $\geq$ 替換為 $\leq$ )。感興趣的讀者可以自行實現。 +下文實現的是大頂堆積。若要將其轉換為小頂堆積,只需將所有大小邏輯判斷進行逆轉(例如,將 $\geq$ 替換為 $\leq$ )。感興趣的讀者可以自行實現。 ### 堆積的儲存與表示 diff --git a/zh-hant/docs/chapter_sorting/counting_sort.md b/zh-hant/docs/chapter_sorting/counting_sort.md index 17501c48a..aaeeecaae 100644 --- a/zh-hant/docs/chapter_sorting/counting_sort.md +++ b/zh-hant/docs/chapter_sorting/counting_sort.md @@ -71,7 +71,7 @@ $$ ## 演算法特性 -- **時間複雜度為 $O(n + m)$** :涉及走訪 `nums` 和走訪 `counter` ,都使用線性時間。一般情況下 $n \gg m$ ,時間複雜度趨於 $O(n)$ 。 +- **時間複雜度為 $O(n + m)$、非自適應排序** :涉及走訪 `nums` 和走訪 `counter` ,都使用線性時間。一般情況下 $n \gg m$ ,時間複雜度趨於 $O(n)$ 。 - **空間複雜度為 $O(n + m)$、非原地排序**:藉助了長度分別為 $n$ 和 $m$ 的陣列 `res` 和 `counter` 。 - **穩定排序**:由於向 `res` 中填充元素的順序是“從右向左”的,因此倒序走訪 `nums` 可以避免改變相等元素之間的相對位置,從而實現穩定排序。實際上,正序走訪 `nums` 也可以得到正確的排序結果,但結果是非穩定的。 diff --git a/zh-hant/docs/chapter_sorting/radix_sort.md b/zh-hant/docs/chapter_sorting/radix_sort.md index aae68bee5..502fb7dc4 100644 --- a/zh-hant/docs/chapter_sorting/radix_sort.md +++ b/zh-hant/docs/chapter_sorting/radix_sort.md @@ -36,6 +36,6 @@ $$ 相較於計數排序,基數排序適用於數值範圍較大的情況,**但前提是資料必須可以表示為固定位數的格式,且位數不能過大**。例如,浮點數不適合使用基數排序,因為其位數 $k$ 過大,可能導致時間複雜度 $O(nk) \gg O(n^2)$ 。 -- **時間複雜度為 $O(nk)$**:設資料量為 $n$、資料為 $d$ 進位制、最大位數為 $k$ ,則對某一位執行計數排序使用 $O(n + d)$ 時間,排序所有 $k$ 位使用 $O((n + d)k)$ 時間。通常情況下,$d$ 和 $k$ 都相對較小,時間複雜度趨向 $O(n)$ 。 +- **時間複雜度為 $O(nk)$、非自適應排序**:設資料量為 $n$、資料為 $d$ 進位制、最大位數為 $k$ ,則對某一位執行計數排序使用 $O(n + d)$ 時間,排序所有 $k$ 位使用 $O((n + d)k)$ 時間。通常情況下,$d$ 和 $k$ 都相對較小,時間複雜度趨向 $O(n)$ 。 - **空間複雜度為 $O(n + d)$、非原地排序**:與計數排序相同,基數排序需要藉助長度為 $n$ 和 $d$ 的陣列 `res` 和 `counter` 。 - **穩定排序**:當計數排序穩定時,基數排序也穩定;當計數排序不穩定時,基數排序無法保證得到正確的排序結果。 diff --git a/zh-hant/docs/chapter_tree/array_representation_of_tree.md b/zh-hant/docs/chapter_tree/array_representation_of_tree.md index 10e45742c..6f395089b 100644 --- a/zh-hant/docs/chapter_tree/array_representation_of_tree.md +++ b/zh-hant/docs/chapter_tree/array_representation_of_tree.md @@ -123,7 +123,9 @@ === "Ruby" ```ruby title="" - + ### 二元樹的陣列表示 ### + # 使用 nil 來表示空位 + tree = [1, 2, 3, 4, nil, 6, 7, 8, 9, nil, nil, 12, nil, nil, 15] ``` === "Zig" diff --git a/zh-hant/docs/chapter_tree/avl_tree.md b/zh-hant/docs/chapter_tree/avl_tree.md index eb289330d..45d3b07e2 100644 --- a/zh-hant/docs/chapter_tree/avl_tree.md +++ b/zh-hant/docs/chapter_tree/avl_tree.md @@ -129,9 +129,9 @@ AVL 樹既是二元搜尋樹,也是平衡二元樹,同時滿足這兩類二 right: TreeNode | null; // 右子節點指標 constructor(val?: number, height?: number, left?: TreeNode | null, right?: TreeNode | null) { this.val = val === undefined ? 0 : val; - this.height = height === undefined ? 0 : height; - this.left = left === undefined ? null : left; - this.right = right === undefined ? null : right; + this.height = height === undefined ? 0 : height; + this.left = left === undefined ? null : left; + this.right = right === undefined ? null : right; } } ``` @@ -214,7 +214,18 @@ AVL 樹既是二元搜尋樹,也是平衡二元樹,同時滿足這兩類二 === "Ruby" ```ruby title="" + ### AVL 樹節點類別 ### + class TreeNode + attr_accessor :val # 節點值 + attr_accessor :height # 節點高度 + attr_accessor :left # 左子節點引用 + attr_accessor :right # 右子節點引用 + def initialize(val) + @val = val + @height = 0 + end + end ``` === "Zig" diff --git a/zh-hant/docs/chapter_tree/binary_tree.md b/zh-hant/docs/chapter_tree/binary_tree.md index 3edcf638e..e70a0440a 100644 --- a/zh-hant/docs/chapter_tree/binary_tree.md +++ b/zh-hant/docs/chapter_tree/binary_tree.md @@ -106,7 +106,7 @@ val: number; left: TreeNode | null; right: TreeNode | null; - + constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) { this.val = val === undefined ? 0 : val; // 節點值 this.left = left === undefined ? null : left; // 左子節點引用 @@ -189,7 +189,16 @@ === "Ruby" ```ruby title="" + ### 二元樹節點類別 ### + class TreeNode + attr_accessor :val # 節點值 + attr_accessor :left # 左子節點引用 + attr_accessor :right # 右子節點引用 + def initialize(val) + @val = val + end + end ``` === "Zig" @@ -432,7 +441,18 @@ === "Ruby" ```ruby title="binary_tree.rb" - + # 初始化二元樹 + # 初始化節點 + n1 = TreeNode.new(1) + n2 = TreeNode.new(2) + n3 = TreeNode.new(3) + n4 = TreeNode.new(4) + n5 = TreeNode.new(5) + # 構建節點之間的引用(指標) + n1.left = n2 + n1.right = n3 + n2.left = n4 + n2.right = n5 ``` === "Zig" @@ -594,7 +614,13 @@ === "Ruby" ```ruby title="binary_tree.rb" - + # 插入與刪除節點 + _p = TreeNode.new(0) + # 在 n1 -> n2 中間插入節點 _p + n1.left = _p + _p.left = n2 + # 刪除節點 + n1.left = n2 ``` === "Zig"