Bug fixes and improvements (#1318)
* Sync zh and zh-hant versions * Update en/README.md * Add a Q&A for chapter of introduction * Update the callout headers * Sync zh ang zh-hant versions * Bug fixes
|
@ -22,7 +22,7 @@ class ArrayBinaryTree:
|
|||
"""列表容量"""
|
||||
return len(self._tree)
|
||||
|
||||
def val(self, i: int) -> int:
|
||||
def val(self, i: int) -> int | None:
|
||||
"""获取索引为 i 节点的值"""
|
||||
# 若索引越界,则返回 None ,代表空位
|
||||
if i < 0 or i >= self.size():
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
|
||||
![计算机存储系统](ram_and_cache.assets/storage_pyramid.png)
|
||||
|
||||
!!! note
|
||||
!!! tip
|
||||
|
||||
计算机的存储层次结构体现了速度、容量和成本三者之间的精妙平衡。实际上,这种权衡普遍存在于所有工业领域,它要求我们在不同的优势和限制之间找到最佳平衡点。
|
||||
|
||||
|
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
|
@ -747,7 +747,7 @@ $T(n)$ 是一次函数,说明其运行时间的增长趋势是线性的,因
|
|||
|
||||
时间复杂度分析本质上是计算“操作数量 $T(n)$”的渐近上界,它具有明确的数学定义。
|
||||
|
||||
!!! abstract "函数渐近上界"
|
||||
!!! note "函数渐近上界"
|
||||
|
||||
若存在正实数 $c$ 和实数 $n_0$ ,使得对于所有的 $n > n_0$ ,均有 $T(n) \leq c \cdot f(n)$ ,则可认为 $f(n)$ 给出了 $T(n)$ 的一个渐近上界,记为 $T(n) = O(f(n))$ 。
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# 数字编码 *
|
||||
|
||||
!!! note
|
||||
!!! tip
|
||||
|
||||
在本书中,标题带有 * 符号的是选读章节。如果你时间有限或感到理解困难,可以先跳过,等学完必读章节后再单独攻克。
|
||||
|
||||
|
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 35 KiB |
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
|
@ -7,3 +7,16 @@
|
|||
- 算法是在有限时间内解决特定问题的一组指令或操作步骤,而数据结构是计算机中组织和存储数据的方式。
|
||||
- 数据结构与算法紧密相连。数据结构是算法的基石,而算法是数据结构发挥作用的舞台。
|
||||
- 我们可以将数据结构与算法类比为拼装积木,积木代表数据,积木的形状和连接方式等代表数据结构,拼装积木的步骤则对应算法。
|
||||
|
||||
### Q & A
|
||||
|
||||
**Q**:作为一名程序员,我在日常工作中从未用算法解决过问题,常用算法都被编程语言封装好了,直接用就可以了;这是否意味着我们工作中的问题还没有到达需要算法的程度?
|
||||
|
||||
如果把具体的工作技能比作是武功的“招式”的话,那么基础科目应该更像是“内功”。
|
||||
|
||||
我认为学算法(以及其他基础科目)的意义不是在于在工作中从零实现它,而是基于学到的知识,在解决问题时能够作出专业的反应和判断,从而提升工作的整体质量。举一个简单例子,每种编程语言都内置了排序函数:
|
||||
|
||||
- 如果我们没有学过数据结构与算法,那么给定任何数据,我们可能都塞给这个排序函数去做了。运行顺畅、性能不错,看上去并没有什么问题。
|
||||
- 但如果学过算法,我们就会知道内置排序函数的时间复杂度是 $O(n \log n)$ ;而如果给定的数据是固定位数的整数(例如学号),那么我们就可以用效率更高的“基数排序”来做,将时间复杂度降为 $O(nk)$ ,其中 $k$ 为位数。当数据体量很大时,节省出来的运行时间就能创造较大价值(成本降低、体验变好等)。
|
||||
|
||||
在工程领域中,大量问题是难以达到最优解的,许多问题只是被“差不多”地解决了。问题的难易程度一方面取决于问题本身的性质,另一方面也取决于观测问题的人的知识储备。人的知识越完备、经验越多,分析问题就会越深入,问题就能被解决得更优雅。
|
||||
|
|
|
@ -27,28 +27,28 @@
|
|||
from collections import deque
|
||||
|
||||
# 初始化双向队列
|
||||
deque: deque[int] = deque()
|
||||
deq: deque[int] = deque()
|
||||
|
||||
# 元素入队
|
||||
deque.append(2) # 添加至队尾
|
||||
deque.append(5)
|
||||
deque.append(4)
|
||||
deque.appendleft(3) # 添加至队首
|
||||
deque.appendleft(1)
|
||||
deq.append(2) # 添加至队尾
|
||||
deq.append(5)
|
||||
deq.append(4)
|
||||
deq.appendleft(3) # 添加至队首
|
||||
deq.appendleft(1)
|
||||
|
||||
# 访问元素
|
||||
front: int = deque[0] # 队首元素
|
||||
rear: int = deque[-1] # 队尾元素
|
||||
front: int = deq[0] # 队首元素
|
||||
rear: int = deq[-1] # 队尾元素
|
||||
|
||||
# 元素出队
|
||||
pop_front: int = deque.popleft() # 队首元素出队
|
||||
pop_rear: int = deque.pop() # 队尾元素出队
|
||||
pop_front: int = deq.popleft() # 队首元素出队
|
||||
pop_rear: int = deq.pop() # 队尾元素出队
|
||||
|
||||
# 获取双向队列的长度
|
||||
size: int = len(deque)
|
||||
size: int = len(deq)
|
||||
|
||||
# 判断双向队列是否为空
|
||||
is_empty: bool = len(deque) == 0
|
||||
is_empty: bool = len(deq) == 0
|
||||
```
|
||||
|
||||
=== "C++"
|
||||
|
|
|
@ -248,7 +248,7 @@ AVL 树既是二叉搜索树,也是平衡二叉树,同时满足这两类二
|
|||
[file]{avl_tree}-[class]{avl_tree}-[func]{balance_factor}
|
||||
```
|
||||
|
||||
!!! note
|
||||
!!! tip
|
||||
|
||||
设平衡因子为 $f$ ,则一棵 AVL 树的任意节点的平衡因子皆满足 $-1 \le f \le 1$ 。
|
||||
|
||||
|
|
|
@ -633,7 +633,7 @@
|
|||
|
||||
https://pythontutor.com/render.html#code=class%20TreeNode%3A%0A%20%20%20%20%22%22%22%E4%BA%8C%E5%8F%89%E6%A0%91%E8%8A%82%E7%82%B9%E7%B1%BB%22%22%22%0A%20%20%20%20def%20__init__%28self,%20val%3A%20int%29%3A%0A%20%20%20%20%20%20%20%20self.val%3A%20int%20%3D%20val%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20%E8%8A%82%E7%82%B9%E5%80%BC%0A%20%20%20%20%20%20%20%20self.left%3A%20TreeNode%20%7C%20None%20%3D%20None%20%20%23%20%E5%B7%A6%E5%AD%90%E8%8A%82%E7%82%B9%E5%BC%95%E7%94%A8%0A%20%20%20%20%20%20%20%20self.right%3A%20TreeNode%20%7C%20None%20%3D%20None%20%23%20%E5%8F%B3%E5%AD%90%E8%8A%82%E7%82%B9%E5%BC%95%E7%94%A8%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20%E5%88%9D%E5%A7%8B%E5%8C%96%E4%BA%8C%E5%8F%89%E6%A0%91%0A%20%20%20%20%23%20%E5%88%9D%E5%A7%8B%E5%8C%96%E8%8A%82%E7%82%B9%0A%20%20%20%20n1%20%3D%20TreeNode%28val%3D1%29%0A%20%20%20%20n2%20%3D%20TreeNode%28val%3D2%29%0A%20%20%20%20n3%20%3D%20TreeNode%28val%3D3%29%0A%20%20%20%20n4%20%3D%20TreeNode%28val%3D4%29%0A%20%20%20%20n5%20%3D%20TreeNode%28val%3D5%29%0A%20%20%20%20%23%20%E6%9E%84%E5%BB%BA%E8%8A%82%E7%82%B9%E4%B9%8B%E9%97%B4%E7%9A%84%E5%BC%95%E7%94%A8%EF%BC%88%E6%8C%87%E9%92%88%EF%BC%89%0A%20%20%20%20n1.left%20%3D%20n2%0A%20%20%20%20n1.right%20%3D%20n3%0A%20%20%20%20n2.left%20%3D%20n4%0A%20%20%20%20n2.right%20%3D%20n5%0A%0A%20%20%20%20%23%20%E6%8F%92%E5%85%A5%E4%B8%8E%E5%88%A0%E9%99%A4%E8%8A%82%E7%82%B9%0A%20%20%20%20p%20%3D%20TreeNode%280%29%0A%20%20%20%20%23%20%E5%9C%A8%20n1%20-%3E%20n2%20%E4%B8%AD%E9%97%B4%E6%8F%92%E5%85%A5%E8%8A%82%E7%82%B9%20P%0A%20%20%20%20n1.left%20%3D%20p%0A%20%20%20%20p.left%20%3D%20n2%0A%20%20%20%20%23%20%E5%88%A0%E9%99%A4%E8%8A%82%E7%82%B9%20P%0A%20%20%20%20n1.left%20%3D%20n2&cumulative=false&curInstr=37&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false
|
||||
|
||||
!!! note
|
||||
!!! tip
|
||||
|
||||
需要注意的是,插入节点可能会改变二叉树的原有逻辑结构,而删除节点通常意味着删除该节点及其所有子树。因此,在二叉树中,插入与删除通常是由一套操作配合完成的,以实现有实际意义的操作。
|
||||
|
||||
|
|
|
@ -43,7 +43,7 @@
|
|||
English
|
||||
</p>
|
||||
|
||||
## About
|
||||
## The book
|
||||
|
||||
This open-source project aims to create a free and beginner-friendly crash course for data structures and algorithms.
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ We can imagine the computer storage system as a pyramid structure shown in the f
|
|||
|
||||
![Computer storage system](ram_and_cache.assets/storage_pyramid.png)
|
||||
|
||||
!!! note
|
||||
!!! tip
|
||||
|
||||
The storage hierarchy of computers reflects a delicate balance between speed, capacity, and cost. In fact, this kind of trade-off is common in all industrial fields, requiring us to find the best balance between different advantages and limitations.
|
||||
|
||||
|
|
|
@ -665,7 +665,7 @@ Since $T(n)$ is a linear function, its growth trend is linear, and therefore, it
|
|||
|
||||
In essence, time complexity analysis is about finding the asymptotic upper bound of the "number of operations $T(n)$". It has a precise mathematical definition.
|
||||
|
||||
!!! abstract "Asymptotic Upper Bound"
|
||||
!!! note "Asymptotic Upper Bound"
|
||||
|
||||
If there exist positive real numbers $c$ and $n_0$ such that for all $n > n_0$, $T(n) \leq c \cdot f(n)$, then $f(n)$ is considered an asymptotic upper bound of $T(n)$, denoted as $T(n) = O(f(n))$.
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# Number encoding *
|
||||
|
||||
!!! note
|
||||
!!! tip
|
||||
|
||||
In this book, chapters marked with an asterisk '*' are optional readings. If you are short on time or find them challenging, you may skip these initially and return to them after completing the essential chapters.
|
||||
|
||||
|
|
|
@ -27,28 +27,28 @@ Similarly, we can directly use the double-ended queue classes implemented in pro
|
|||
from collections import deque
|
||||
|
||||
# Initialize the deque
|
||||
deque: deque[int] = deque()
|
||||
deq: deque[int] = deque()
|
||||
|
||||
# Enqueue elements
|
||||
deque.append(2) # Add to the tail
|
||||
deque.append(5)
|
||||
deque.append(4)
|
||||
deque.appendleft(3) # Add to the head
|
||||
deque.appendleft(1)
|
||||
deq.append(2) # Add to the tail
|
||||
deq.append(5)
|
||||
deq.append(4)
|
||||
deq.appendleft(3) # Add to the head
|
||||
deq.appendleft(1)
|
||||
|
||||
# Access elements
|
||||
front: int = deque[0] # The first element
|
||||
rear: int = deque[-1] # The last element
|
||||
front: int = deq[0] # The first element
|
||||
rear: int = deq[-1] # The last element
|
||||
|
||||
# Dequeue elements
|
||||
pop_front: int = deque.popleft() # The first element dequeued
|
||||
pop_rear: int = deque.pop() # The last element dequeued
|
||||
pop_front: int = deq.popleft() # The first element dequeued
|
||||
pop_rear: int = deq.pop() # The last element dequeued
|
||||
|
||||
# Get the length of the deque
|
||||
size: int = len(deque)
|
||||
size: int = len(deq)
|
||||
|
||||
# Check if the deque is empty
|
||||
is_empty: bool = len(deque) == 0
|
||||
is_empty: bool = len(deq) == 0
|
||||
```
|
||||
|
||||
=== "C++"
|
||||
|
|
|
@ -237,7 +237,7 @@ The "balance factor" of a node is defined as the height of the node's left subtr
|
|||
[file]{avl_tree}-[class]{avl_tree}-[func]{balance_factor}
|
||||
```
|
||||
|
||||
!!! note
|
||||
!!! tip
|
||||
|
||||
Let the balance factor be $f$, then the balance factor of any node in an AVL tree satisfies $-1 \le f \le 1$.
|
||||
|
||||
|
|
|
@ -607,7 +607,7 @@ Similar to a linked list, inserting and removing nodes in a binary tree can be a
|
|||
|
||||
https://pythontutor.com/render.html#code=class%20TreeNode%3A%0A%20%20%20%20%22%22%22%E4%BA%8C%E5%8F%89%E6%A0%91%E8%8A%82%E7%82%B9%E7%B1%BB%22%22%22%0A%20%20%20%20def%20__init__%28self,%20val%3A%20int%29%3A%0A%20%20%20%20%20%20%20%20self.val%3A%20int%20%3D%20val%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20%E8%8A%82%E7%82%B9%E5%80%BC%0A%20%20%20%20%20%20%20%20self.left%3A%20TreeNode%20%7C%20None%20%3D%20None%20%20%23%20%E5%B7%A6%E5%AD%90%E8%8A%82%E7%82%B9%E5%BC%95%E7%94%A8%0A%20%20%20%20%20%20%20%20self.right%3A%20TreeNode%20%7C%20None%20%3D%20None%20%23%20%E5%8F%B3%E5%AD%90%E8%8A%82%E7%82%B9%E5%BC%95%E7%94%A8%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20%E5%88%9D%E5%A7%8B%E5%8C%96%E4%BA%8C%E5%8F%89%E6%A0%91%0A%20%20%20%20%23%20%E5%88%9D%E5%A7%8B%E5%8C%96%E8%8A%82%E7%82%B9%0A%20%20%20%20n1%20%3D%20TreeNode%28val%3D1%29%0A%20%20%20%20n2%20%3D%20TreeNode%28val%3D2%29%0A%20%20%20%20n3%20%3D%20TreeNode%28val%3D3%29%0A%20%20%20%20n4%20%3D%20TreeNode%28val%3D4%29%0A%20%20%20%20n5%20%3D%20TreeNode%28val%3D5%29%0A%20%20%20%20%23%20%E6%9E%84%E5%BB%BA%E8%8A%82%E7%82%B9%E4%B9%8B%E9%97%B4%E7%9A%84%E5%BC%95%E7%94%A8%EF%BC%88%E6%8C%87%E9%92%88%EF%BC%89%0A%20%20%20%20n1.left%20%3D%20n2%0A%20%20%20%20n1.right%20%3D%20n3%0A%20%20%20%20n2.left%20%3D%20n4%0A%20%20%20%20n2.right%20%3D%20n5%0A%0A%20%20%20%20%23%20%E6%8F%92%E5%85%A5%E4%B8%8E%E5%88%A0%E9%99%A4%E8%8A%82%E7%82%B9%0A%20%20%20%20p%20%3D%20TreeNode%280%29%0A%20%20%20%20%23%20%E5%9C%A8%20n1%20-%3E%20n2%20%E4%B8%AD%E9%97%B4%E6%8F%92%E5%85%A5%E8%8A%82%E7%82%B9%20P%0A%20%20%20%20n1.left%20%3D%20p%0A%20%20%20%20p.left%20%3D%20n2%0A%20%20%20%20%23%20%E5%88%A0%E9%99%A4%E8%8A%82%E7%82%B9%20P%0A%20%20%20%20n1.left%20%3D%20n2&cumulative=false&curInstr=37&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false
|
||||
|
||||
!!! note
|
||||
!!! tip
|
||||
|
||||
It's important to note that inserting nodes may change the original logical structure of the binary tree, while removing nodes usually means removing the node and all its subtrees. Therefore, in a binary tree, insertion and removal are usually performed through a set of operations to achieve meaningful actions.
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
vector<Vertex *> graphBFS(GraphAdjList &graph, Vertex *startVet) {
|
||||
// 頂點走訪序列
|
||||
vector<Vertex *> res;
|
||||
// 雜湊表,用於記錄已被訪問過的頂點
|
||||
// 雜湊集合,用於記錄已被訪問過的頂點
|
||||
unordered_set<Vertex *> visited = {startVet};
|
||||
// 佇列用於實現 BFS
|
||||
queue<Vertex *> que;
|
||||
|
|
|
@ -25,7 +25,7 @@ void dfs(GraphAdjList &graph, unordered_set<Vertex *> &visited, vector<Vertex *>
|
|||
vector<Vertex *> graphDFS(GraphAdjList &graph, Vertex *startVet) {
|
||||
// 頂點走訪序列
|
||||
vector<Vertex *> res;
|
||||
// 雜湊表,用於記錄已被訪問過的頂點
|
||||
// 雜湊集合,用於記錄已被訪問過的頂點
|
||||
unordered_set<Vertex *> visited;
|
||||
dfs(graph, visited, res, startVet);
|
||||
return res;
|
||||
|
|
|
@ -12,7 +12,7 @@ public class graph_bfs {
|
|||
List<Vertex> GraphBFS(GraphAdjList graph, Vertex startVet) {
|
||||
// 頂點走訪序列
|
||||
List<Vertex> res = [];
|
||||
// 雜湊表,用於記錄已被訪問過的頂點
|
||||
// 雜湊集合,用於記錄已被訪問過的頂點
|
||||
HashSet<Vertex> visited = [startVet];
|
||||
// 佇列用於實現 BFS
|
||||
Queue<Vertex> que = new();
|
||||
|
|
|
@ -26,7 +26,7 @@ public class graph_dfs {
|
|||
List<Vertex> GraphDFS(GraphAdjList graph, Vertex startVet) {
|
||||
// 頂點走訪序列
|
||||
List<Vertex> res = [];
|
||||
// 雜湊表,用於記錄已被訪問過的頂點
|
||||
// 雜湊集合,用於記錄已被訪問過的頂點
|
||||
HashSet<Vertex> visited = [];
|
||||
DFS(graph, visited, res, startVet);
|
||||
return res;
|
||||
|
|
|
@ -14,7 +14,7 @@ List<Vertex> graphBFS(GraphAdjList graph, Vertex startVet) {
|
|||
// 使用鄰接表來表示圖,以便獲取指定頂點的所有鄰接頂點
|
||||
// 頂點走訪序列
|
||||
List<Vertex> res = [];
|
||||
// 雜湊表,用於記錄已被訪問過的頂點
|
||||
// 雜湊集合,用於記錄已被訪問過的頂點
|
||||
Set<Vertex> visited = {};
|
||||
visited.add(startVet);
|
||||
// 佇列用於實現 BFS
|
||||
|
|
|
@ -30,7 +30,7 @@ void dfs(
|
|||
List<Vertex> graphDFS(GraphAdjList graph, Vertex startVet) {
|
||||
// 頂點走訪序列
|
||||
List<Vertex> res = [];
|
||||
// 雜湊表,用於記錄已被訪問過的頂點
|
||||
// 雜湊集合,用於記錄已被訪問過的頂點
|
||||
Set<Vertex> visited = {};
|
||||
dfs(graph, visited, res, startVet);
|
||||
return res;
|
||||
|
|
|
@ -13,7 +13,7 @@ import (
|
|||
func graphBFS(g *graphAdjList, startVet Vertex) []Vertex {
|
||||
// 頂點走訪序列
|
||||
res := make([]Vertex, 0)
|
||||
// 雜湊表,用於記錄已被訪問過的頂點
|
||||
// 雜湊集合,用於記錄已被訪問過的頂點
|
||||
visited := make(map[Vertex]struct{})
|
||||
visited[startVet] = struct{}{}
|
||||
// 佇列用於實現 BFS, 使用切片模擬佇列
|
||||
|
|
|
@ -28,7 +28,7 @@ func dfs(g *graphAdjList, visited map[Vertex]struct{}, res *[]Vertex, vet Vertex
|
|||
func graphDFS(g *graphAdjList, startVet Vertex) []Vertex {
|
||||
// 頂點走訪序列
|
||||
res := make([]Vertex, 0)
|
||||
// 雜湊表,用於記錄已被訪問過的頂點
|
||||
// 雜湊集合,用於記錄已被訪問過的頂點
|
||||
visited := make(map[Vertex]struct{})
|
||||
dfs(g, visited, &res, startVet)
|
||||
// 返回頂點走訪序列
|
||||
|
|
|
@ -9,7 +9,7 @@ func twoSumBruteForce(nums []int, target int) []int {
|
|||
size := len(nums)
|
||||
// 兩層迴圈,時間複雜度為 O(n^2)
|
||||
for i := 0; i < size-1; i++ {
|
||||
for j := i + 1; i < size; j++ {
|
||||
for j := i + 1; j < size; j++ {
|
||||
if nums[i]+nums[j] == target {
|
||||
return []int{i, j}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ public class graph_bfs {
|
|||
static List<Vertex> graphBFS(GraphAdjList graph, Vertex startVet) {
|
||||
// 頂點走訪序列
|
||||
List<Vertex> res = new ArrayList<>();
|
||||
// 雜湊表,用於記錄已被訪問過的頂點
|
||||
// 雜湊集合,用於記錄已被訪問過的頂點
|
||||
Set<Vertex> visited = new HashSet<>();
|
||||
visited.add(startVet);
|
||||
// 佇列用於實現 BFS
|
||||
|
|
|
@ -28,7 +28,7 @@ public class graph_dfs {
|
|||
static List<Vertex> graphDFS(GraphAdjList graph, Vertex startVet) {
|
||||
// 頂點走訪序列
|
||||
List<Vertex> res = new ArrayList<>();
|
||||
// 雜湊表,用於記錄已被訪問過的頂點
|
||||
// 雜湊集合,用於記錄已被訪問過的頂點
|
||||
Set<Vertex> visited = new HashSet<>();
|
||||
dfs(graph, visited, res, startVet);
|
||||
return res;
|
||||
|
|
|
@ -12,7 +12,7 @@ const { Vertex } = require('../modules/Vertex');
|
|||
function graphBFS(graph, startVet) {
|
||||
// 頂點走訪序列
|
||||
const res = [];
|
||||
// 雜湊表,用於記錄已被訪問過的頂點
|
||||
// 雜湊集合,用於記錄已被訪問過的頂點
|
||||
const visited = new Set();
|
||||
visited.add(startVet);
|
||||
// 佇列用於實現 BFS
|
||||
|
|
|
@ -27,7 +27,7 @@ function dfs(graph, visited, res, vet) {
|
|||
function graphDFS(graph, startVet) {
|
||||
// 頂點走訪序列
|
||||
const res = [];
|
||||
// 雜湊表,用於記錄已被訪問過的頂點
|
||||
// 雜湊集合,用於記錄已被訪問過的頂點
|
||||
const visited = new Set();
|
||||
dfs(graph, visited, res, startVet);
|
||||
return res;
|
||||
|
|
63
zh-hant/codes/javascript/test_all.js
Normal file
|
@ -0,0 +1,63 @@
|
|||
import { bold, brightRed } from 'jsr:@std/fmt/colors';
|
||||
import { expandGlob } from 'jsr:@std/fs';
|
||||
import { relative, resolve } from 'jsr:@std/path';
|
||||
|
||||
/**
|
||||
* @typedef {import('jsr:@std/fs').WalkEntry} WalkEntry
|
||||
* @type {WalkEntry[]}
|
||||
*/
|
||||
const entries = [];
|
||||
|
||||
for await (const entry of expandGlob(
|
||||
resolve(import.meta.dirname, './chapter_*/*.js')
|
||||
)) {
|
||||
entries.push(entry);
|
||||
}
|
||||
|
||||
/** @type {{ status: Promise<Deno.CommandStatus>; stderr: ReadableStream<Uint8Array>; }[]} */
|
||||
const processes = [];
|
||||
|
||||
for (const file of entries) {
|
||||
const execute = new Deno.Command('node', {
|
||||
args: [relative(import.meta.dirname, file.path)],
|
||||
cwd: import.meta.dirname,
|
||||
stdin: 'piped',
|
||||
stdout: 'piped',
|
||||
stderr: 'piped',
|
||||
});
|
||||
|
||||
const process = execute.spawn();
|
||||
processes.push({ status: process.status, stderr: process.stderr });
|
||||
}
|
||||
|
||||
const results = await Promise.all(
|
||||
processes.map(async (item) => {
|
||||
const status = await item.status;
|
||||
return { status, stderr: item.stderr };
|
||||
})
|
||||
);
|
||||
|
||||
/** @type {ReadableStream<Uint8Array>[]} */
|
||||
const errors = [];
|
||||
|
||||
for (const result of results) {
|
||||
if (!result.status.success) {
|
||||
errors.push(result.stderr);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`Tested ${entries.length} files`);
|
||||
console.log(`Found exception in ${errors.length} files`);
|
||||
|
||||
if (errors.length) {
|
||||
console.log();
|
||||
|
||||
for (const error of errors) {
|
||||
const reader = error.getReader();
|
||||
const { value } = await reader.read();
|
||||
const decoder = new TextDecoder();
|
||||
console.log(`${bold(brightRed('error'))}: ${decoder.decode(value)}`);
|
||||
}
|
||||
|
||||
throw new Error('Test failed');
|
||||
}
|
|
@ -14,7 +14,7 @@ import java.util.*
|
|||
fun graphBFS(graph: GraphAdjList, startVet: Vertex): MutableList<Vertex?> {
|
||||
// 頂點走訪序列
|
||||
val res = mutableListOf<Vertex?>()
|
||||
// 雜湊表,用於記錄已被訪問過的頂點
|
||||
// 雜湊集合,用於記錄已被訪問過的頂點
|
||||
val visited = HashSet<Vertex>()
|
||||
visited.add(startVet)
|
||||
// 佇列用於實現 BFS
|
||||
|
|
|
@ -31,7 +31,7 @@ fun dfs(
|
|||
fun graphDFS(graph: GraphAdjList, startVet: Vertex?): MutableList<Vertex?> {
|
||||
// 頂點走訪序列
|
||||
val res = mutableListOf<Vertex?>()
|
||||
// 雜湊表,用於記錄已被訪問過的頂點
|
||||
// 雜湊集合,用於記錄已被訪問過的頂點
|
||||
val visited = HashSet<Vertex?>()
|
||||
dfs(graph, visited, res, startVet)
|
||||
return res
|
||||
|
|
|
@ -18,7 +18,7 @@ def graph_bfs(graph: GraphAdjList, start_vet: Vertex) -> list[Vertex]:
|
|||
# 使用鄰接表來表示圖,以便獲取指定頂點的所有鄰接頂點
|
||||
# 頂點走訪序列
|
||||
res = []
|
||||
# 雜湊表,用於記錄已被訪問過的頂點
|
||||
# 雜湊集合,用於記錄已被訪問過的頂點
|
||||
visited = set[Vertex]([start_vet])
|
||||
# 佇列用於實現 BFS
|
||||
que = deque[Vertex]([start_vet])
|
||||
|
|
|
@ -29,7 +29,7 @@ def graph_dfs(graph: GraphAdjList, start_vet: Vertex) -> list[Vertex]:
|
|||
# 使用鄰接表來表示圖,以便獲取指定頂點的所有鄰接頂點
|
||||
# 頂點走訪序列
|
||||
res = []
|
||||
# 雜湊表,用於記錄已被訪問過的頂點
|
||||
# 雜湊集合,用於記錄已被訪問過的頂點
|
||||
visited = set[Vertex]()
|
||||
dfs(graph, visited, res, start_vet)
|
||||
return res
|
||||
|
|
|
@ -18,7 +18,7 @@ class ArrayStack:
|
|||
|
||||
def is_empty(self) -> bool:
|
||||
"""判斷堆疊是否為空"""
|
||||
return self._size == 0
|
||||
return self.size() == 0
|
||||
|
||||
def push(self, item: int):
|
||||
"""入堆疊"""
|
||||
|
|
|
@ -22,7 +22,7 @@ class ArrayBinaryTree:
|
|||
"""串列容量"""
|
||||
return len(self._tree)
|
||||
|
||||
def val(self, i: int) -> int:
|
||||
def val(self, i: int) -> int | None:
|
||||
"""獲取索引為 i 節點的值"""
|
||||
# 若索引越界,則返回 None ,代表空位
|
||||
if i < 0 or i >= self.size():
|
||||
|
|
116
zh-hant/codes/ruby/chapter_graph/graph_adjacency_list.rb
Normal file
|
@ -0,0 +1,116 @@
|
|||
=begin
|
||||
File: graph_adjacency_list.rb
|
||||
Created Time: 2024-04-25
|
||||
Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com)
|
||||
=end
|
||||
|
||||
require_relative '../utils/vertex'
|
||||
|
||||
### 基於鄰接表實現的無向圖類別 ###
|
||||
class GraphAdjList
|
||||
attr_reader :adj_list
|
||||
|
||||
### 建構子 ###
|
||||
def initialize(edges)
|
||||
# 鄰接表,key:頂點,value:該頂點的所有鄰接頂點
|
||||
@adj_list = {}
|
||||
# 新增所有頂點和邊
|
||||
for edge in edges
|
||||
add_vertex(edge[0])
|
||||
add_vertex(edge[1])
|
||||
add_edge(edge[0], edge[1])
|
||||
end
|
||||
end
|
||||
|
||||
### 獲取頂點數量 ###
|
||||
def size
|
||||
@adj_list.length
|
||||
end
|
||||
|
||||
### 新增邊 ###
|
||||
def add_edge(vet1, vet2)
|
||||
raise ArgumentError if !@adj_list.include?(vet1) || !@adj_list.include?(vet2)
|
||||
|
||||
@adj_list[vet1] << vet2
|
||||
@adj_list[vet2] << vet1
|
||||
end
|
||||
|
||||
### 刪除邊 ###
|
||||
def remove_edge(vet1, vet2)
|
||||
raise ArgumentError if !@adj_list.include?(vet1) || !@adj_list.include?(vet2)
|
||||
|
||||
# 刪除邊 vet1 - vet2
|
||||
@adj_list[vet1].delete(vet2)
|
||||
@adj_list[vet2].delete(vet1)
|
||||
end
|
||||
|
||||
### 新增頂點 ###
|
||||
def add_vertex(vet)
|
||||
return if @adj_list.include?(vet)
|
||||
|
||||
# 在鄰接表中新增一個新鏈結串列
|
||||
@adj_list[vet] = []
|
||||
end
|
||||
|
||||
### 刪除頂點 ###
|
||||
def remove_vertex(vet)
|
||||
raise ArgumentError unless @adj_list.include?(vet)
|
||||
|
||||
# 在鄰接表中刪除頂點 vet 對應的鏈結串列
|
||||
@adj_list.delete(vet)
|
||||
# 走訪其他頂點的鏈結串列,刪除所有包含 vet 的邊
|
||||
for vertex in @adj_list
|
||||
@adj_list[vertex.first].delete(vet) if @adj_list[vertex.first].include?(vet)
|
||||
end
|
||||
end
|
||||
|
||||
### 列印鄰接表 ###
|
||||
def __print__
|
||||
puts '鄰接表 ='
|
||||
for vertex in @adj_list
|
||||
tmp = @adj_list[vertex.first].map { |v| v.val }
|
||||
puts "#{vertex.first.val}: #{tmp},"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
### Driver Code ###
|
||||
if __FILE__ == $0
|
||||
# 初始化無向圖
|
||||
v = vals_to_vets([1, 3, 2, 5, 4])
|
||||
edges = [
|
||||
[v[0], v[1]],
|
||||
[v[0], v[3]],
|
||||
[v[1], v[2]],
|
||||
[v[2], v[3]],
|
||||
[v[2], v[4]],
|
||||
[v[3], v[4]],
|
||||
]
|
||||
graph = GraphAdjList.new(edges)
|
||||
puts "\n初始化後,圖為"
|
||||
graph.__print__
|
||||
|
||||
# 新增邊
|
||||
# 頂點 1,2 即 v[0],v[2]
|
||||
graph.add_edge(v[0], v[2])
|
||||
puts "\n新增邊 1-2 後,圖為"
|
||||
graph.__print__
|
||||
|
||||
# 刪除邊
|
||||
# 頂點 1,3 即 v[0],v[1]
|
||||
graph.remove_edge(v[0], v[1])
|
||||
puts "\n刪除邊 1-3 後,圖為"
|
||||
graph.__print__
|
||||
|
||||
# 新增頂點
|
||||
v5 = Vertex.new(6)
|
||||
graph.add_vertex(v5)
|
||||
puts "\n新增頂點 6 後,圖為"
|
||||
graph.__print__
|
||||
|
||||
# 刪除頂點
|
||||
# 頂點 3 即 v[1]
|
||||
graph.remove_vertex(v[1])
|
||||
puts "\n刪除頂點 3 後,圖為"
|
||||
graph.__print__
|
||||
end
|
116
zh-hant/codes/ruby/chapter_graph/graph_adjacency_matrix.rb
Normal file
|
@ -0,0 +1,116 @@
|
|||
=begin
|
||||
File: graph_adjacency_matrix.rb
|
||||
Created Time: 2024-04-25
|
||||
Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com)
|
||||
=end
|
||||
|
||||
require_relative '../utils/print_util'
|
||||
|
||||
### 基於鄰接矩陣實現的無向圖類別 ###
|
||||
class GraphAdjMat
|
||||
def initialize(vertices, edges)
|
||||
### 建構子 ###
|
||||
# 頂點串列,元素代表“頂點值”,索引代表“頂點索引”
|
||||
@vertices = []
|
||||
# 鄰接矩陣,行列索引對應“頂點索引”
|
||||
@adj_mat = []
|
||||
# 新增頂點
|
||||
vertices.each { |val| add_vertex(val) }
|
||||
# 新增邊
|
||||
# 請注意,edges 元素代表頂點索引,即對應 vertices 元素索引
|
||||
edges.each { |e| add_edge(e[0], e[1]) }
|
||||
end
|
||||
|
||||
### 獲取頂點數量 ###
|
||||
def size
|
||||
@vertices.length
|
||||
end
|
||||
|
||||
### 新增頂點 ###
|
||||
def add_vertex(val)
|
||||
n = size
|
||||
# 向頂點串列中新增新頂點的值
|
||||
@vertices << val
|
||||
# 在鄰接矩陣中新增一行
|
||||
new_row = Array.new(n, 0)
|
||||
@adj_mat << new_row
|
||||
# 在鄰接矩陣中新增一列
|
||||
@adj_mat.each { |row| row << 0 }
|
||||
end
|
||||
|
||||
### 刪除頂點 ###
|
||||
def remove_vertex(index)
|
||||
raise IndexError if index >= size
|
||||
|
||||
# 在頂點串列中移除索引 index 的頂點
|
||||
@vertices.delete_at(index)
|
||||
# 在鄰接矩陣中刪除索引 index 的行
|
||||
@adj_mat.delete_at(index)
|
||||
# 在鄰接矩陣中刪除索引 index 的列
|
||||
@adj_mat.each { |row| row.delete_at(index) }
|
||||
end
|
||||
|
||||
### 新增邊 ###
|
||||
def add_edge(i, j)
|
||||
# 參數 i, j 對應 vertices 元素索引
|
||||
# 索引越界與相等處理
|
||||
if i < 0 || j < 0 || i >= size || j >= size || i == j
|
||||
raise IndexError
|
||||
end
|
||||
# 在無向圖中,鄰接矩陣關於主對角線對稱,即滿足 (i, j) == (j, i)
|
||||
@adj_mat[i][j] = 1
|
||||
@adj_mat[j][i] = 1
|
||||
end
|
||||
|
||||
### 刪除邊 ###
|
||||
def remove_edge(i, j)
|
||||
# 參數 i, j 對應 vertices 元素索引
|
||||
# 索引越界與相等處理
|
||||
if i < 0 || j < 0 || i >= size || j >= size || i == j
|
||||
raise IndexError
|
||||
end
|
||||
@adj_mat[i][j] = 0
|
||||
@adj_mat[j][i] = 0
|
||||
end
|
||||
|
||||
### 列印鄰接矩陣 ###
|
||||
def __print__
|
||||
puts "頂點串列 = #{@vertices}"
|
||||
puts '鄰接矩陣 ='
|
||||
print_matrix(@adj_mat)
|
||||
end
|
||||
end
|
||||
|
||||
### Driver Code ###
|
||||
if __FILE__ == $0
|
||||
# 初始化無向圖
|
||||
# 請注意,edges 元素代表頂點索引,即對應 vertices 元素索引
|
||||
vertices = [1, 3, 2, 5, 4]
|
||||
edges = [[0, 1], [0, 3], [1, 2], [2, 3], [2, 4], [3, 4]]
|
||||
graph = GraphAdjMat.new(vertices, edges)
|
||||
puts "\n初始化後,圖為"
|
||||
graph.__print__
|
||||
|
||||
# 新增邊
|
||||
# 頂點 1, 2 的索引分別為 0, 2
|
||||
graph.add_edge(0, 2)
|
||||
puts "\n新增邊 1-2 後,圖為"
|
||||
graph.__print__
|
||||
|
||||
# 刪除邊
|
||||
# 定點 1, 3 的索引分別為 0, 1
|
||||
graph.remove_edge(0, 1)
|
||||
puts "\n刪除邊 1-3 後,圖為"
|
||||
graph.__print__
|
||||
|
||||
# 新增頂點
|
||||
graph.add_vertex(6)
|
||||
puts "\n新增頂點 6 後,圖為"
|
||||
graph.__print__
|
||||
|
||||
# 刪除頂點
|
||||
# 頂點 3 的索引為 1
|
||||
graph.remove_vertex(1)
|
||||
puts "\n刪除頂點 3 後,圖為"
|
||||
graph.__print__
|
||||
end
|
61
zh-hant/codes/ruby/chapter_graph/graph_bfs.rb
Normal file
|
@ -0,0 +1,61 @@
|
|||
=begin
|
||||
File: graph_bfs.rb
|
||||
Created Time: 2024-04-25
|
||||
Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com)
|
||||
=end
|
||||
|
||||
require 'set'
|
||||
require_relative './graph_adjacency_list'
|
||||
require_relative '../utils/vertex'
|
||||
|
||||
### 廣度優先走訪 ###
|
||||
def graph_bfs(graph, start_vet)
|
||||
# 使用鄰接表來表示圖,以便獲取指定頂點的所有鄰接頂點
|
||||
# 頂點走訪序列
|
||||
res = []
|
||||
# 雜湊集合,用於記錄已被訪問過的頂點
|
||||
visited = Set.new([start_vet])
|
||||
# 佇列用於實現 BFS
|
||||
que = [start_vet]
|
||||
# 以頂點 vet 為起點,迴圈直至訪問完所有頂點
|
||||
while que.length > 0
|
||||
vet = que.shift # 佇列首頂點出隊
|
||||
res << vet # 記錄訪問頂點
|
||||
# 走訪該頂點的所有鄰接頂點
|
||||
for adj_vet in graph.adj_list[vet]
|
||||
next if visited.include?(adj_vet) # 跳過已被訪問的頂點
|
||||
que << adj_vet # 只入列未訪問的頂點
|
||||
visited.add(adj_vet) # 標記該頂點已被訪問
|
||||
end
|
||||
end
|
||||
# 返回頂點走訪序列
|
||||
res
|
||||
end
|
||||
|
||||
### Driver Code ###
|
||||
if __FILE__ == $0
|
||||
# 初始化無向圖
|
||||
v = vals_to_vets([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
|
||||
edges = [
|
||||
[v[0], v[1]],
|
||||
[v[0], v[3]],
|
||||
[v[1], v[2]],
|
||||
[v[1], v[4]],
|
||||
[v[2], v[5]],
|
||||
[v[3], v[4]],
|
||||
[v[3], v[6]],
|
||||
[v[4], v[5]],
|
||||
[v[4], v[7]],
|
||||
[v[5], v[8]],
|
||||
[v[6], v[7]],
|
||||
[v[7], v[8]],
|
||||
]
|
||||
graph = GraphAdjList.new(edges)
|
||||
puts "\n初始化後,圖為"
|
||||
graph.__print__
|
||||
|
||||
# 廣度優先走訪
|
||||
res = graph_bfs(graph, v.first)
|
||||
puts "\n廣度優先便利(BFS)頂點序列為"
|
||||
p vets_to_vals(res)
|
||||
end
|
54
zh-hant/codes/ruby/chapter_graph/graph_dfs.rb
Normal file
|
@ -0,0 +1,54 @@
|
|||
=begin
|
||||
File: graph_dfs.rb
|
||||
Created Time: 2024-04-25
|
||||
Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com)
|
||||
=end
|
||||
|
||||
require 'set'
|
||||
require_relative './graph_adjacency_list'
|
||||
require_relative '../utils/vertex'
|
||||
|
||||
### 深度優先走訪輔助函式 ###
|
||||
def dfs(graph, visited, res, vet)
|
||||
res << vet # 記錄訪問頂點
|
||||
visited.add(vet) # 標記該頂點已被訪問
|
||||
# 走訪該頂點的所有鄰接頂點
|
||||
for adj_vet in graph.adj_list[vet]
|
||||
next if visited.include?(adj_vet) # 跳過已被訪問的頂點
|
||||
# 遞迴訪問鄰接頂點
|
||||
dfs(graph, visited, res, adj_vet)
|
||||
end
|
||||
end
|
||||
|
||||
### 深度優先走訪 ###
|
||||
def graph_dfs(graph, start_vet)
|
||||
# 使用鄰接表來表示圖,以便獲取指定頂點的所有鄰接頂點
|
||||
# 頂點走訪序列
|
||||
res = []
|
||||
# 雜湊集合,用於記錄已被訪問過的頂點
|
||||
visited = Set.new
|
||||
dfs(graph, visited, res, start_vet)
|
||||
res
|
||||
end
|
||||
|
||||
### Driver Code ###
|
||||
if __FILE__ == $0
|
||||
# 初始化無向圖
|
||||
v = vals_to_vets([0, 1, 2, 3, 4, 5, 6])
|
||||
edges = [
|
||||
[v[0], v[1]],
|
||||
[v[0], v[3]],
|
||||
[v[1], v[2]],
|
||||
[v[2], v[5]],
|
||||
[v[4], v[5]],
|
||||
[v[5], v[6]],
|
||||
]
|
||||
graph = GraphAdjList.new(edges)
|
||||
puts "\n初始化後,圖為"
|
||||
graph.__print__
|
||||
|
||||
# 深度優先走訪
|
||||
res = graph_dfs(graph, v[0])
|
||||
puts "\n深度優先走訪(DFS)頂點序列為"
|
||||
p vets_to_vals(res)
|
||||
end
|
147
zh-hant/codes/ruby/chapter_heap/my_heap.rb
Normal file
|
@ -0,0 +1,147 @@
|
|||
=begin
|
||||
File: my_heap.rb
|
||||
Created Time: 2024-04-19
|
||||
Author: Blue Bean (lonnnnnnner@gmail.com)
|
||||
=end
|
||||
|
||||
require_relative '../utils/print_util'
|
||||
|
||||
### 大頂堆積 ###
|
||||
class MaxHeap
|
||||
attr_reader :max_heap
|
||||
|
||||
### 建構子,根據輸入串列建堆積 ###
|
||||
def initialize(nums)
|
||||
# 將串列元素原封不動新增進堆積
|
||||
@max_heap = nums
|
||||
# 堆積化除葉節點以外的其他所有節點
|
||||
parent(size - 1).downto(0) do |i|
|
||||
sift_down(i)
|
||||
end
|
||||
end
|
||||
|
||||
### 獲取左子節點的索引 ###
|
||||
def left(i)
|
||||
2 * i + 1
|
||||
end
|
||||
|
||||
### 獲取右子節點的索引 ###
|
||||
def right(i)
|
||||
2 * i + 2
|
||||
end
|
||||
|
||||
### 獲取父節點的索引 ###
|
||||
def parent(i)
|
||||
(i - 1) / 2 # 向下整除
|
||||
end
|
||||
|
||||
### 交換元素 ###
|
||||
def swap(i, j)
|
||||
@max_heap[i], @max_heap[j] = @max_heap[j], @max_heap[i]
|
||||
end
|
||||
|
||||
### 獲取堆積大小 ###
|
||||
def size
|
||||
@max_heap.length
|
||||
end
|
||||
|
||||
### 判斷堆積是否為空 ###
|
||||
def is_empty?
|
||||
size == 0
|
||||
end
|
||||
|
||||
### 訪問堆積頂元素 ###
|
||||
def peek
|
||||
@max_heap[0]
|
||||
end
|
||||
|
||||
### 元素入堆積 ###
|
||||
def push(val)
|
||||
# 新增節點
|
||||
@max_heap << val
|
||||
# 從底至頂堆積化
|
||||
sift_up(size - 1)
|
||||
end
|
||||
|
||||
### 從節點 i 開始,從底至頂堆積化 ###
|
||||
def sift_up(i)
|
||||
loop do
|
||||
# 獲取節點 i 的父節點
|
||||
p = parent(i)
|
||||
# 當“越過根節點”或“節點無須修復”時,結束堆積化
|
||||
break if p < 0 || @max_heap[i] <= @max_heap[p]
|
||||
# 交換兩節點
|
||||
swap(i, p)
|
||||
# 迴圈向上堆積化
|
||||
i = p
|
||||
end
|
||||
end
|
||||
|
||||
### 元素出堆積 ###
|
||||
def pop
|
||||
# 判空處理
|
||||
raise IndexError, "堆積為空" if is_empty?
|
||||
# 交換根節點與最右葉節點(交換首元素與尾元素)
|
||||
swap(0, size - 1)
|
||||
# 刪除節點
|
||||
val = @max_heap.pop
|
||||
# 從頂至底堆積化
|
||||
sift_down(0)
|
||||
# 返回堆積頂元素
|
||||
val
|
||||
end
|
||||
|
||||
### 從節點 i 開始,從頂至底堆積化 ###
|
||||
def sift_down(i)
|
||||
loop do
|
||||
# 判斷節點 i, l, r 中值最大的節點,記為 ma
|
||||
l, r, ma = left(i), right(i), i
|
||||
ma = l if l < size && @max_heap[l] > @max_heap[ma]
|
||||
ma = r if r < size && @max_heap[r] > @max_heap[ma]
|
||||
|
||||
# 若節點 i 最大或索引 l, r 越界,則無須繼續堆積化,跳出
|
||||
break if ma == i
|
||||
|
||||
# 交換兩節點
|
||||
swap(i, ma)
|
||||
# 迴圈向下堆積化
|
||||
i = ma
|
||||
end
|
||||
end
|
||||
|
||||
### 列印堆積(二元樹)###
|
||||
def __print__
|
||||
print_heap(@max_heap)
|
||||
end
|
||||
end
|
||||
|
||||
### Driver Code ###
|
||||
if __FILE__ == $0
|
||||
# 初始化大頂堆積
|
||||
max_heap = MaxHeap.new([9, 8, 6, 6, 7, 5, 2, 1, 4, 3, 6, 2])
|
||||
puts "\n輸入串列並建堆積後"
|
||||
max_heap.__print__
|
||||
|
||||
# 獲取堆積頂元素
|
||||
peek = max_heap.peek
|
||||
puts "\n堆積頂元素為 #{peek}"
|
||||
|
||||
# 元素入堆積
|
||||
val = 7
|
||||
max_heap.push(val)
|
||||
puts "\n元素 #{val} 入堆積後"
|
||||
max_heap.__print__
|
||||
|
||||
# 堆積頂元素出堆積
|
||||
peek = max_heap.pop
|
||||
puts "\n堆積頂元素 #{peek} 出堆積後"
|
||||
max_heap.__print__
|
||||
|
||||
# 獲取堆積大小
|
||||
size = max_heap.size
|
||||
puts "\n堆積元素數量為 #{size}"
|
||||
|
||||
# 判斷堆積是否為空
|
||||
is_empty = max_heap.is_empty?
|
||||
puts "\n堆積是否為空 #{is_empty}"
|
||||
end
|
64
zh-hant/codes/ruby/chapter_heap/top_k.rb
Normal file
|
@ -0,0 +1,64 @@
|
|||
=begin
|
||||
File: top_k.rb
|
||||
Created Time: 2024-04-19
|
||||
Author: Blue Bean (lonnnnnnner@gmail.com)
|
||||
=end
|
||||
|
||||
require_relative "./my_heap"
|
||||
|
||||
### 元素入堆積 ###
|
||||
def push_min_heap(heap, val)
|
||||
# 元素取反
|
||||
heap.push(-val)
|
||||
end
|
||||
|
||||
### 元素出堆積 ###
|
||||
def pop_min_heap(heap)
|
||||
# 元素取反
|
||||
-heap.pop
|
||||
end
|
||||
|
||||
### 訪問堆積頂元素 ###
|
||||
def peek_min_heap(heap)
|
||||
# 元素取反
|
||||
-heap.peek
|
||||
end
|
||||
|
||||
### 取出堆積中元素 ###
|
||||
def get_min_heap(heap)
|
||||
# 將堆積中所有元素取反
|
||||
heap.max_heap.map { |x| -x }
|
||||
end
|
||||
|
||||
### 基於堆積查詢陣列中最大的 k 個元素 ###
|
||||
def top_k_heap(nums, k)
|
||||
# 初始化小頂堆積
|
||||
# 請注意:我們將堆積中所有元素取反,從而用大頂堆積來模擬小頂堆積
|
||||
max_heap = MaxHeap.new([])
|
||||
|
||||
# 將陣列的前 k 個元素入堆積
|
||||
for i in 0...k
|
||||
push_min_heap(max_heap, nums[i])
|
||||
end
|
||||
|
||||
# 從第 k+1 個元素開始,保持堆積的長度為 k
|
||||
for i in k...nums.length
|
||||
# 若當前元素大於堆積頂元素,則將堆積頂元素出堆積、當前元素入堆積
|
||||
if nums[i] > peek_min_heap(max_heap)
|
||||
pop_min_heap(max_heap)
|
||||
push_min_heap(max_heap, nums[i])
|
||||
end
|
||||
end
|
||||
|
||||
get_min_heap(max_heap)
|
||||
end
|
||||
|
||||
### Driver Code ###
|
||||
if __FILE__ == $0
|
||||
nums = [1, 7, 6, 3, 2]
|
||||
k = 3
|
||||
|
||||
res = top_k_heap(nums, k)
|
||||
puts "最大的 #{k} 個元素為"
|
||||
print_heap(res)
|
||||
end
|
|
@ -4,6 +4,15 @@ Created Time: 2024-03-18
|
|||
Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com)
|
||||
=end
|
||||
|
||||
require_relative "./tree_node"
|
||||
|
||||
### 列印矩陣 ###
|
||||
def print_matrix(mat)
|
||||
s = []
|
||||
mat.each { |arr| s << " #{arr.to_s}" }
|
||||
puts "[\n#{s.join(",\n")}\n]"
|
||||
end
|
||||
|
||||
### 列印鏈結串列 ###
|
||||
def print_linked_list(head)
|
||||
list = []
|
||||
|
@ -61,3 +70,11 @@ end
|
|||
def print_hash_map(hmap)
|
||||
hmap.entries.each { |key, value| puts "#{key} -> #{value}" }
|
||||
end
|
||||
|
||||
### 列印堆積 ###
|
||||
def print_heap(heap)
|
||||
puts "堆積的陣列表示:#{heap}"
|
||||
puts "堆積的樹狀表示:"
|
||||
root = arr_to_tree(heap)
|
||||
print_tree(root)
|
||||
end
|
||||
|
|
24
zh-hant/codes/ruby/utils/vertex.rb
Normal file
|
@ -0,0 +1,24 @@
|
|||
=begin
|
||||
File: vertex.rb
|
||||
Created Time: 2024-04-25
|
||||
Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com)
|
||||
=end
|
||||
|
||||
### 頂點類別 ###
|
||||
class Vertex
|
||||
attr_accessor :val
|
||||
|
||||
def initialize(val)
|
||||
@val = val
|
||||
end
|
||||
end
|
||||
|
||||
### 輸入值串列 vals ,返回頂點串列 vets ###
|
||||
def vals_to_vets(vals)
|
||||
Array.new(vals.length) { |i| Vertex.new(vals[i]) }
|
||||
end
|
||||
|
||||
### 輸入頂點串列 vets, 返回值串列 vals ###
|
||||
def vets_to_vals(vets)
|
||||
Array.new(vets.length) { |i| vets[i].val }
|
||||
end
|
|
@ -15,7 +15,7 @@ use std::collections::{HashSet, VecDeque};
|
|||
fn graph_bfs(graph: GraphAdjList, start_vet: Vertex) -> Vec<Vertex> {
|
||||
// 頂點走訪序列
|
||||
let mut res = vec![];
|
||||
// 雜湊表,用於記錄已被訪問過的頂點
|
||||
// 雜湊集合,用於記錄已被訪問過的頂點
|
||||
let mut visited = HashSet::new();
|
||||
visited.insert(start_vet);
|
||||
// 佇列用於實現 BFS
|
||||
|
|
|
@ -31,7 +31,7 @@ fn dfs(graph: &GraphAdjList, visited: &mut HashSet<Vertex>, res: &mut Vec<Vertex
|
|||
fn graph_dfs(graph: GraphAdjList, start_vet: Vertex) -> Vec<Vertex> {
|
||||
// 頂點走訪序列
|
||||
let mut res = vec![];
|
||||
// 雜湊表,用於記錄已被訪問過的頂點
|
||||
// 雜湊集合,用於記錄已被訪問過的頂點
|
||||
let mut visited = HashSet::new();
|
||||
dfs(&graph, &mut visited, &mut res, start_vet);
|
||||
|
||||
|
|
|
@ -96,7 +96,7 @@ impl MaxHeap {
|
|||
// 交換根節點與最右葉節點(交換首元素與尾元素)
|
||||
self.swap(0, self.size() - 1);
|
||||
// 刪除節點
|
||||
let val = self.max_heap.remove(self.size() - 1);
|
||||
let val = self.max_heap.pop().unwrap();
|
||||
// 從頂至底堆積化
|
||||
self.sift_down(0);
|
||||
// 返回堆積頂元素
|
||||
|
|
|
@ -12,7 +12,7 @@ import utils
|
|||
func graphBFS(graph: GraphAdjList, startVet: Vertex) -> [Vertex] {
|
||||
// 頂點走訪序列
|
||||
var res: [Vertex] = []
|
||||
// 雜湊表,用於記錄已被訪問過的頂點
|
||||
// 雜湊集合,用於記錄已被訪問過的頂點
|
||||
var visited: Set<Vertex> = [startVet]
|
||||
// 佇列用於實現 BFS
|
||||
var que: [Vertex] = [startVet]
|
||||
|
|
|
@ -26,7 +26,7 @@ func dfs(graph: GraphAdjList, visited: inout Set<Vertex>, res: inout [Vertex], v
|
|||
func graphDFS(graph: GraphAdjList, startVet: Vertex) -> [Vertex] {
|
||||
// 頂點走訪序列
|
||||
var res: [Vertex] = []
|
||||
// 雜湊表,用於記錄已被訪問過的頂點
|
||||
// 雜湊集合,用於記錄已被訪問過的頂點
|
||||
var visited: Set<Vertex> = []
|
||||
dfs(graph: graph, visited: &visited, res: &res, vet: startVet)
|
||||
return res
|
||||
|
|
|
@ -12,7 +12,7 @@ import { Vertex } from '../modules/Vertex';
|
|||
function graphBFS(graph: GraphAdjList, startVet: Vertex): Vertex[] {
|
||||
// 頂點走訪序列
|
||||
const res: Vertex[] = [];
|
||||
// 雜湊表,用於記錄已被訪問過的頂點
|
||||
// 雜湊集合,用於記錄已被訪問過的頂點
|
||||
const visited: Set<Vertex> = new Set();
|
||||
visited.add(startVet);
|
||||
// 佇列用於實現 BFS
|
||||
|
|
|
@ -31,7 +31,7 @@ function dfs(
|
|||
function graphDFS(graph: GraphAdjList, startVet: Vertex): Vertex[] {
|
||||
// 頂點走訪序列
|
||||
const res: Vertex[] = [];
|
||||
// 雜湊表,用於記錄已被訪問過的頂點
|
||||
// 雜湊集合,用於記錄已被訪問過的頂點
|
||||
const visited: Set<Vertex> = new Set();
|
||||
dfs(graph, visited, res, startVet);
|
||||
return res;
|
||||
|
|
|
@ -745,7 +745,7 @@
|
|||
|
||||
單向鏈結串列通常用於實現堆疊、佇列、雜湊表和圖等資料結構。
|
||||
|
||||
- **堆疊與佇列**:當插入和刪除操作都在鏈結串列的一端進行時,它表現出先進後出的特性,對應堆疊;當插入操作在鏈結串列的一端進行,刪除操作在鏈結串列的另一端進行,它表現出先進先出的特性,對應佇列。
|
||||
- **堆疊與佇列**:當插入和刪除操作都在鏈結串列的一端進行時,它表現的特性為先進後出,對應堆疊;當插入操作在鏈結串列的一端進行,刪除操作在鏈結串列的另一端進行,它表現的特性為先進先出,對應佇列。
|
||||
- **雜湊表**:鏈式位址是解決雜湊衝突的主流方案之一,在該方案中,所有衝突的元素都會被放到一個鏈結串列中。
|
||||
- **圖**:鄰接表是表示圖的一種常用方式,其中圖的每個頂點都與一個鏈結串列相關聯,鏈結串列中的每個元素都代表與該頂點相連的其他頂點。
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
|
||||
![計算機儲存系統](ram_and_cache.assets/storage_pyramid.png)
|
||||
|
||||
!!! note
|
||||
!!! tip
|
||||
|
||||
計算機的儲存層次結構體現了速度、容量和成本三者之間的精妙平衡。實際上,這種權衡普遍存在於所有工業領域,它要求我們在不同的優勢和限制之間找到最佳平衡點。
|
||||
|
||||
|
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 27 KiB |
|
@ -59,7 +59,7 @@
|
|||
|
||||
![重複排列](permutations_problem.assets/permutations_ii.png)
|
||||
|
||||
那麼如何去除重複的排列呢?最直接地,考慮藉助一個雜湊表,直接對排列結果進行去重。然而這樣做不夠優雅,**因為生成重複排列的搜尋分支沒有必要,應當提前識別並剪枝**,這樣可以進一步提升演算法效率。
|
||||
那麼如何去除重複的排列呢?最直接地,考慮藉助一個雜湊集合,直接對排列結果進行去重。然而這樣做不夠優雅,**因為生成重複排列的搜尋分支沒有必要,應當提前識別並剪枝**,這樣可以進一步提升演算法效率。
|
||||
|
||||
### 相等元素剪枝
|
||||
|
||||
|
@ -73,7 +73,7 @@
|
|||
|
||||
### 程式碼實現
|
||||
|
||||
在上一題的程式碼的基礎上,我們考慮在每一輪選擇中開啟一個雜湊表 `duplicated` ,用於記錄該輪中已經嘗試過的元素,並將重複元素剪枝:
|
||||
在上一題的程式碼的基礎上,我們考慮在每一輪選擇中開啟一個雜湊集合 `duplicated` ,用於記錄該輪中已經嘗試過的元素,並將重複元素剪枝:
|
||||
|
||||
```src
|
||||
[file]{permutations_ii}-[class]{}-[func]{permutations_ii}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
- 回溯問題通常包含多個約束條件,它們可用於實現剪枝操作。剪枝可以提前結束不必要的搜尋分支,大幅提升搜尋效率。
|
||||
- 回溯演算法主要可用於解決搜尋問題和約束滿足問題。組合最佳化問題雖然可以用回溯演算法解決,但往往存在效率更高或效果更好的解法。
|
||||
- 全排列問題旨在搜尋給定集合元素的所有可能的排列。我們藉助一個陣列來記錄每個元素是否被選擇,剪掉重複選擇同一元素的搜尋分支,確保每個元素只被選擇一次。
|
||||
- 在全排列問題中,如果集合中存在重複元素,則最終結果會出現重複排列。我們需要約束相等元素在每輪中只能被選擇一次,這通常藉助一個雜湊表來實現。
|
||||
- 在全排列問題中,如果集合中存在重複元素,則最終結果會出現重複排列。我們需要約束相等元素在每輪中只能被選擇一次,這通常藉助一個雜湊集合來實現。
|
||||
- 子集和問題的目標是在給定集合中找到和為目標值的所有子集。集合不區分元素順序,而搜尋過程會輸出所有順序的結果,產生重複子集。我們在回溯前將資料進行排序,並設定一個變數來指示每一輪的走訪起始點,從而將生成重複子集的搜尋分支進行剪枝。
|
||||
- 對於子集和問題,陣列中的相等元素會產生重複集合。我們利用陣列已排序的前置條件,透過判斷相鄰元素是否相等實現剪枝,從而確保相等元素在每輪中只能被選中一次。
|
||||
- $n$ 皇后問題旨在尋找將 $n$ 個皇后放置到 $n \times n$ 尺寸棋盤上的方案,要求所有皇后兩兩之間無法攻擊對方。該問題的約束條件有行約束、列約束、主對角線和次對角線約束。為滿足行約束,我們採用按行放置的策略,保證每一行放置一個皇后。
|
||||
|
|
|
@ -747,7 +747,7 @@ $T(n)$ 是一次函式,說明其執行時間的增長趨勢是線性的,因
|
|||
|
||||
時間複雜度分析本質上是計算“操作數量 $T(n)$”的漸近上界,它具有明確的數學定義。
|
||||
|
||||
!!! abstract "函式漸近上界"
|
||||
!!! note "函式漸近上界"
|
||||
|
||||
若存在正實數 $c$ 和實數 $n_0$ ,使得對於所有的 $n > n_0$ ,均有 $T(n) \leq c \cdot f(n)$ ,則可認為 $f(n)$ 給出了 $T(n)$ 的一個漸近上界,記為 $T(n) = O(f(n))$ 。
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# 數字編碼 *
|
||||
|
||||
!!! note
|
||||
!!! tip
|
||||
|
||||
在本書中,標題帶有 * 符號的是選讀章節。如果你時間有限或感到理解困難,可以先跳過,等學完必讀章節後再單獨攻克。
|
||||
|
||||
|
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 43 KiB |
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 35 KiB |
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 39 KiB |
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 43 KiB |
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 43 KiB |
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 39 KiB |
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |