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
This commit is contained in:
Yudong Jin 2024-04-30 15:52:05 +08:00 committed by GitHub
parent 84b1ce2497
commit 870e3e5cb2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
107 changed files with 779 additions and 87 deletions

View file

@ -22,7 +22,7 @@ class ArrayBinaryTree:
"""列表容量""" """列表容量"""
return len(self._tree) return len(self._tree)
def val(self, i: int) -> int: def val(self, i: int) -> int | None:
"""获取索引为 i 节点的值""" """获取索引为 i 节点的值"""
# 若索引越界,则返回 None ,代表空位 # 若索引越界,则返回 None ,代表空位
if i < 0 or i >= self.size(): if i < 0 or i >= self.size():

View file

@ -25,7 +25,7 @@
![计算机存储系统](ram_and_cache.assets/storage_pyramid.png) ![计算机存储系统](ram_and_cache.assets/storage_pyramid.png)
!!! note !!! tip
计算机的存储层次结构体现了速度、容量和成本三者之间的精妙平衡。实际上,这种权衡普遍存在于所有工业领域,它要求我们在不同的优势和限制之间找到最佳平衡点。 计算机的存储层次结构体现了速度、容量和成本三者之间的精妙平衡。实际上,这种权衡普遍存在于所有工业领域,它要求我们在不同的优势和限制之间找到最佳平衡点。

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 27 KiB

View file

@ -747,7 +747,7 @@ $T(n)$ 是一次函数,说明其运行时间的增长趋势是线性的,因
时间复杂度分析本质上是计算“操作数量 $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))$ 。 若存在正实数 $c$ 和实数 $n_0$ ,使得对于所有的 $n > n_0$ ,均有 $T(n) \leq c \cdot f(n)$ ,则可认为 $f(n)$ 给出了 $T(n)$ 的一个渐近上界,记为 $T(n) = O(f(n))$ 。

View file

@ -1,6 +1,6 @@
# 数字编码 * # 数字编码 *
!!! note !!! tip
在本书中,标题带有 * 符号的是选读章节。如果你时间有限或感到理解困难,可以先跳过,等学完必读章节后再单独攻克。 在本书中,标题带有 * 符号的是选读章节。如果你时间有限或感到理解困难,可以先跳过,等学完必读章节后再单独攻克。

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

View file

@ -7,3 +7,16 @@
- 算法是在有限时间内解决特定问题的一组指令或操作步骤,而数据结构是计算机中组织和存储数据的方式。 - 算法是在有限时间内解决特定问题的一组指令或操作步骤,而数据结构是计算机中组织和存储数据的方式。
- 数据结构与算法紧密相连。数据结构是算法的基石,而算法是数据结构发挥作用的舞台。 - 数据结构与算法紧密相连。数据结构是算法的基石,而算法是数据结构发挥作用的舞台。
- 我们可以将数据结构与算法类比为拼装积木,积木代表数据,积木的形状和连接方式等代表数据结构,拼装积木的步骤则对应算法。 - 我们可以将数据结构与算法类比为拼装积木,积木代表数据,积木的形状和连接方式等代表数据结构,拼装积木的步骤则对应算法。
### Q & A
**Q**:作为一名程序员,我在日常工作中从未用算法解决过问题,常用算法都被编程语言封装好了,直接用就可以了;这是否意味着我们工作中的问题还没有到达需要算法的程度?
如果把具体的工作技能比作是武功的“招式”的话,那么基础科目应该更像是“内功”。
我认为学算法(以及其他基础科目)的意义不是在于在工作中从零实现它,而是基于学到的知识,在解决问题时能够作出专业的反应和判断,从而提升工作的整体质量。举一个简单例子,每种编程语言都内置了排序函数:
- 如果我们没有学过数据结构与算法,那么给定任何数据,我们可能都塞给这个排序函数去做了。运行顺畅、性能不错,看上去并没有什么问题。
- 但如果学过算法,我们就会知道内置排序函数的时间复杂度是 $O(n \log n)$ ;而如果给定的数据是固定位数的整数(例如学号),那么我们就可以用效率更高的“基数排序”来做,将时间复杂度降为 $O(nk)$ ,其中 $k$ 为位数。当数据体量很大时,节省出来的运行时间就能创造较大价值(成本降低、体验变好等)。
在工程领域中,大量问题是难以达到最优解的,许多问题只是被“差不多”地解决了。问题的难易程度一方面取决于问题本身的性质,另一方面也取决于观测问题的人的知识储备。人的知识越完备、经验越多,分析问题就会越深入,问题就能被解决得更优雅。

View file

@ -27,28 +27,28 @@
from collections import deque from collections import deque
# 初始化双向队列 # 初始化双向队列
deque: deque[int] = deque() deq: deque[int] = deque()
# 元素入队 # 元素入队
deque.append(2) # 添加至队尾 deq.append(2) # 添加至队尾
deque.append(5) deq.append(5)
deque.append(4) deq.append(4)
deque.appendleft(3) # 添加至队首 deq.appendleft(3) # 添加至队首
deque.appendleft(1) deq.appendleft(1)
# 访问元素 # 访问元素
front: int = deque[0] # 队首元素 front: int = deq[0] # 队首元素
rear: int = deque[-1] # 队尾元素 rear: int = deq[-1] # 队尾元素
# 元素出队 # 元素出队
pop_front: int = deque.popleft() # 队首元素出队 pop_front: int = deq.popleft() # 队首元素出队
pop_rear: int = deque.pop() # 队尾元素出队 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++" === "C++"

View file

@ -248,7 +248,7 @@ AVL 树既是二叉搜索树,也是平衡二叉树,同时满足这两类二
[file]{avl_tree}-[class]{avl_tree}-[func]{balance_factor} [file]{avl_tree}-[class]{avl_tree}-[func]{balance_factor}
``` ```
!!! note !!! tip
设平衡因子为 $f$ ,则一棵 AVL 树的任意节点的平衡因子皆满足 $-1 \le f \le 1$ 。 设平衡因子为 $f$ ,则一棵 AVL 树的任意节点的平衡因子皆满足 $-1 \le f \le 1$ 。

View file

@ -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 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
需要注意的是,插入节点可能会改变二叉树的原有逻辑结构,而删除节点通常意味着删除该节点及其所有子树。因此,在二叉树中,插入与删除通常是由一套操作配合完成的,以实现有实际意义的操作。 需要注意的是,插入节点可能会改变二叉树的原有逻辑结构,而删除节点通常意味着删除该节点及其所有子树。因此,在二叉树中,插入与删除通常是由一套操作配合完成的,以实现有实际意义的操作。

View file

@ -43,7 +43,7 @@
English English
</p> </p>
## About ## The book
This open-source project aims to create a free and beginner-friendly crash course for data structures and algorithms. This open-source project aims to create a free and beginner-friendly crash course for data structures and algorithms.

View file

@ -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) ![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. 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.

View file

@ -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. 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))$. 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))$.

View file

@ -1,6 +1,6 @@
# Number encoding * # 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. 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.

View file

@ -27,28 +27,28 @@ Similarly, we can directly use the double-ended queue classes implemented in pro
from collections import deque from collections import deque
# Initialize the deque # Initialize the deque
deque: deque[int] = deque() deq: deque[int] = deque()
# Enqueue elements # Enqueue elements
deque.append(2) # Add to the tail deq.append(2) # Add to the tail
deque.append(5) deq.append(5)
deque.append(4) deq.append(4)
deque.appendleft(3) # Add to the head deq.appendleft(3) # Add to the head
deque.appendleft(1) deq.appendleft(1)
# Access elements # Access elements
front: int = deque[0] # The first element front: int = deq[0] # The first element
rear: int = deque[-1] # The last element rear: int = deq[-1] # The last element
# Dequeue elements # Dequeue elements
pop_front: int = deque.popleft() # The first element dequeued pop_front: int = deq.popleft() # The first element dequeued
pop_rear: int = deque.pop() # The last element dequeued pop_rear: int = deq.pop() # The last element dequeued
# Get the length of the deque # Get the length of the deque
size: int = len(deque) size: int = len(deq)
# Check if the deque is empty # Check if the deque is empty
is_empty: bool = len(deque) == 0 is_empty: bool = len(deq) == 0
``` ```
=== "C++" === "C++"

View file

@ -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} [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$. Let the balance factor be $f$, then the balance factor of any node in an AVL tree satisfies $-1 \le f \le 1$.

View file

@ -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 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. 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.

View file

@ -12,7 +12,7 @@
vector<Vertex *> graphBFS(GraphAdjList &graph, Vertex *startVet) { vector<Vertex *> graphBFS(GraphAdjList &graph, Vertex *startVet) {
// 頂點走訪序列 // 頂點走訪序列
vector<Vertex *> res; vector<Vertex *> res;
// 雜湊,用於記錄已被訪問過的頂點 // 雜湊集合,用於記錄已被訪問過的頂點
unordered_set<Vertex *> visited = {startVet}; unordered_set<Vertex *> visited = {startVet};
// 佇列用於實現 BFS // 佇列用於實現 BFS
queue<Vertex *> que; queue<Vertex *> que;

View file

@ -25,7 +25,7 @@ void dfs(GraphAdjList &graph, unordered_set<Vertex *> &visited, vector<Vertex *>
vector<Vertex *> graphDFS(GraphAdjList &graph, Vertex *startVet) { vector<Vertex *> graphDFS(GraphAdjList &graph, Vertex *startVet) {
// 頂點走訪序列 // 頂點走訪序列
vector<Vertex *> res; vector<Vertex *> res;
// 雜湊,用於記錄已被訪問過的頂點 // 雜湊集合,用於記錄已被訪問過的頂點
unordered_set<Vertex *> visited; unordered_set<Vertex *> visited;
dfs(graph, visited, res, startVet); dfs(graph, visited, res, startVet);
return res; return res;

View file

@ -12,7 +12,7 @@ public class graph_bfs {
List<Vertex> GraphBFS(GraphAdjList graph, Vertex startVet) { List<Vertex> GraphBFS(GraphAdjList graph, Vertex startVet) {
// 頂點走訪序列 // 頂點走訪序列
List<Vertex> res = []; List<Vertex> res = [];
// 雜湊,用於記錄已被訪問過的頂點 // 雜湊集合,用於記錄已被訪問過的頂點
HashSet<Vertex> visited = [startVet]; HashSet<Vertex> visited = [startVet];
// 佇列用於實現 BFS // 佇列用於實現 BFS
Queue<Vertex> que = new(); Queue<Vertex> que = new();

View file

@ -26,7 +26,7 @@ public class graph_dfs {
List<Vertex> GraphDFS(GraphAdjList graph, Vertex startVet) { List<Vertex> GraphDFS(GraphAdjList graph, Vertex startVet) {
// 頂點走訪序列 // 頂點走訪序列
List<Vertex> res = []; List<Vertex> res = [];
// 雜湊,用於記錄已被訪問過的頂點 // 雜湊集合,用於記錄已被訪問過的頂點
HashSet<Vertex> visited = []; HashSet<Vertex> visited = [];
DFS(graph, visited, res, startVet); DFS(graph, visited, res, startVet);
return res; return res;

View file

@ -14,7 +14,7 @@ List<Vertex> graphBFS(GraphAdjList graph, Vertex startVet) {
// 使便 // 使便
// //
List<Vertex> res = []; List<Vertex> res = [];
// //
Set<Vertex> visited = {}; Set<Vertex> visited = {};
visited.add(startVet); visited.add(startVet);
// BFS // BFS

View file

@ -30,7 +30,7 @@ void dfs(
List<Vertex> graphDFS(GraphAdjList graph, Vertex startVet) { List<Vertex> graphDFS(GraphAdjList graph, Vertex startVet) {
// //
List<Vertex> res = []; List<Vertex> res = [];
// //
Set<Vertex> visited = {}; Set<Vertex> visited = {};
dfs(graph, visited, res, startVet); dfs(graph, visited, res, startVet);
return res; return res;

View file

@ -13,7 +13,7 @@ import (
func graphBFS(g *graphAdjList, startVet Vertex) []Vertex { func graphBFS(g *graphAdjList, startVet Vertex) []Vertex {
// 頂點走訪序列 // 頂點走訪序列
res := make([]Vertex, 0) res := make([]Vertex, 0)
// 雜湊,用於記錄已被訪問過的頂點 // 雜湊集合,用於記錄已被訪問過的頂點
visited := make(map[Vertex]struct{}) visited := make(map[Vertex]struct{})
visited[startVet] = struct{}{} visited[startVet] = struct{}{}
// 佇列用於實現 BFS, 使用切片模擬佇列 // 佇列用於實現 BFS, 使用切片模擬佇列

View file

@ -28,7 +28,7 @@ func dfs(g *graphAdjList, visited map[Vertex]struct{}, res *[]Vertex, vet Vertex
func graphDFS(g *graphAdjList, startVet Vertex) []Vertex { func graphDFS(g *graphAdjList, startVet Vertex) []Vertex {
// 頂點走訪序列 // 頂點走訪序列
res := make([]Vertex, 0) res := make([]Vertex, 0)
// 雜湊,用於記錄已被訪問過的頂點 // 雜湊集合,用於記錄已被訪問過的頂點
visited := make(map[Vertex]struct{}) visited := make(map[Vertex]struct{})
dfs(g, visited, &res, startVet) dfs(g, visited, &res, startVet)
// 返回頂點走訪序列 // 返回頂點走訪序列

View file

@ -9,7 +9,7 @@ func twoSumBruteForce(nums []int, target int) []int {
size := len(nums) size := len(nums)
// 兩層迴圈,時間複雜度為 O(n^2) // 兩層迴圈,時間複雜度為 O(n^2)
for i := 0; i < size-1; i++ { 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 { if nums[i]+nums[j] == target {
return []int{i, j} return []int{i, j}
} }

View file

@ -15,7 +15,7 @@ public class graph_bfs {
static List<Vertex> graphBFS(GraphAdjList graph, Vertex startVet) { static List<Vertex> graphBFS(GraphAdjList graph, Vertex startVet) {
// 頂點走訪序列 // 頂點走訪序列
List<Vertex> res = new ArrayList<>(); List<Vertex> res = new ArrayList<>();
// 雜湊用於記錄已被訪問過的頂點 // 雜湊集合用於記錄已被訪問過的頂點
Set<Vertex> visited = new HashSet<>(); Set<Vertex> visited = new HashSet<>();
visited.add(startVet); visited.add(startVet);
// 佇列用於實現 BFS // 佇列用於實現 BFS

View file

@ -28,7 +28,7 @@ public class graph_dfs {
static List<Vertex> graphDFS(GraphAdjList graph, Vertex startVet) { static List<Vertex> graphDFS(GraphAdjList graph, Vertex startVet) {
// 頂點走訪序列 // 頂點走訪序列
List<Vertex> res = new ArrayList<>(); List<Vertex> res = new ArrayList<>();
// 雜湊用於記錄已被訪問過的頂點 // 雜湊集合用於記錄已被訪問過的頂點
Set<Vertex> visited = new HashSet<>(); Set<Vertex> visited = new HashSet<>();
dfs(graph, visited, res, startVet); dfs(graph, visited, res, startVet);
return res; return res;

View file

@ -12,7 +12,7 @@ const { Vertex } = require('../modules/Vertex');
function graphBFS(graph, startVet) { function graphBFS(graph, startVet) {
// 頂點走訪序列 // 頂點走訪序列
const res = []; const res = [];
// 雜湊,用於記錄已被訪問過的頂點 // 雜湊集合,用於記錄已被訪問過的頂點
const visited = new Set(); const visited = new Set();
visited.add(startVet); visited.add(startVet);
// 佇列用於實現 BFS // 佇列用於實現 BFS

View file

@ -27,7 +27,7 @@ function dfs(graph, visited, res, vet) {
function graphDFS(graph, startVet) { function graphDFS(graph, startVet) {
// 頂點走訪序列 // 頂點走訪序列
const res = []; const res = [];
// 雜湊,用於記錄已被訪問過的頂點 // 雜湊集合,用於記錄已被訪問過的頂點
const visited = new Set(); const visited = new Set();
dfs(graph, visited, res, startVet); dfs(graph, visited, res, startVet);
return res; return res;

View 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');
}

View file

@ -14,7 +14,7 @@ import java.util.*
fun graphBFS(graph: GraphAdjList, startVet: Vertex): MutableList<Vertex?> { fun graphBFS(graph: GraphAdjList, startVet: Vertex): MutableList<Vertex?> {
// 頂點走訪序列 // 頂點走訪序列
val res = mutableListOf<Vertex?>() val res = mutableListOf<Vertex?>()
// 雜湊,用於記錄已被訪問過的頂點 // 雜湊集合,用於記錄已被訪問過的頂點
val visited = HashSet<Vertex>() val visited = HashSet<Vertex>()
visited.add(startVet) visited.add(startVet)
// 佇列用於實現 BFS // 佇列用於實現 BFS

View file

@ -31,7 +31,7 @@ fun dfs(
fun graphDFS(graph: GraphAdjList, startVet: Vertex?): MutableList<Vertex?> { fun graphDFS(graph: GraphAdjList, startVet: Vertex?): MutableList<Vertex?> {
// 頂點走訪序列 // 頂點走訪序列
val res = mutableListOf<Vertex?>() val res = mutableListOf<Vertex?>()
// 雜湊,用於記錄已被訪問過的頂點 // 雜湊集合,用於記錄已被訪問過的頂點
val visited = HashSet<Vertex?>() val visited = HashSet<Vertex?>()
dfs(graph, visited, res, startVet) dfs(graph, visited, res, startVet)
return res return res

View file

@ -18,7 +18,7 @@ def graph_bfs(graph: GraphAdjList, start_vet: Vertex) -> list[Vertex]:
# 使用鄰接表來表示圖,以便獲取指定頂點的所有鄰接頂點 # 使用鄰接表來表示圖,以便獲取指定頂點的所有鄰接頂點
# 頂點走訪序列 # 頂點走訪序列
res = [] res = []
# 雜湊,用於記錄已被訪問過的頂點 # 雜湊集合,用於記錄已被訪問過的頂點
visited = set[Vertex]([start_vet]) visited = set[Vertex]([start_vet])
# 佇列用於實現 BFS # 佇列用於實現 BFS
que = deque[Vertex]([start_vet]) que = deque[Vertex]([start_vet])

View file

@ -29,7 +29,7 @@ def graph_dfs(graph: GraphAdjList, start_vet: Vertex) -> list[Vertex]:
# 使用鄰接表來表示圖,以便獲取指定頂點的所有鄰接頂點 # 使用鄰接表來表示圖,以便獲取指定頂點的所有鄰接頂點
# 頂點走訪序列 # 頂點走訪序列
res = [] res = []
# 雜湊,用於記錄已被訪問過的頂點 # 雜湊集合,用於記錄已被訪問過的頂點
visited = set[Vertex]() visited = set[Vertex]()
dfs(graph, visited, res, start_vet) dfs(graph, visited, res, start_vet)
return res return res

View file

@ -18,7 +18,7 @@ class ArrayStack:
def is_empty(self) -> bool: def is_empty(self) -> bool:
"""判斷堆疊是否為空""" """判斷堆疊是否為空"""
return self._size == 0 return self.size() == 0
def push(self, item: int): def push(self, item: int):
"""入堆疊""" """入堆疊"""

View file

@ -22,7 +22,7 @@ class ArrayBinaryTree:
"""串列容量""" """串列容量"""
return len(self._tree) return len(self._tree)
def val(self, i: int) -> int: def val(self, i: int) -> int | None:
"""獲取索引為 i 節點的值""" """獲取索引為 i 節點的值"""
# 若索引越界,則返回 None ,代表空位 # 若索引越界,則返回 None ,代表空位
if i < 0 or i >= self.size(): if i < 0 or i >= self.size():

View 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__
# 新增邊
# 頂點 12 即 v[0]v[2]
graph.add_edge(v[0], v[2])
puts "\n新增邊 1-2 後,圖為"
graph.__print__
# 刪除邊
# 頂點 13 即 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

View 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

View 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

View 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

View 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

View 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

View file

@ -4,6 +4,15 @@ Created Time: 2024-03-18
Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com)
=end =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) def print_linked_list(head)
list = [] list = []
@ -61,3 +70,11 @@ end
def print_hash_map(hmap) def print_hash_map(hmap)
hmap.entries.each { |key, value| puts "#{key} -> #{value}" } hmap.entries.each { |key, value| puts "#{key} -> #{value}" }
end end
### 列印堆積 ###
def print_heap(heap)
puts "堆積的陣列表示:#{heap}"
puts "堆積的樹狀表示:"
root = arr_to_tree(heap)
print_tree(root)
end

View 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

View file

@ -15,7 +15,7 @@ use std::collections::{HashSet, VecDeque};
fn graph_bfs(graph: GraphAdjList, start_vet: Vertex) -> Vec<Vertex> { fn graph_bfs(graph: GraphAdjList, start_vet: Vertex) -> Vec<Vertex> {
// 頂點走訪序列 // 頂點走訪序列
let mut res = vec![]; let mut res = vec![];
// 雜湊,用於記錄已被訪問過的頂點 // 雜湊集合,用於記錄已被訪問過的頂點
let mut visited = HashSet::new(); let mut visited = HashSet::new();
visited.insert(start_vet); visited.insert(start_vet);
// 佇列用於實現 BFS // 佇列用於實現 BFS

View file

@ -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> { fn graph_dfs(graph: GraphAdjList, start_vet: Vertex) -> Vec<Vertex> {
// 頂點走訪序列 // 頂點走訪序列
let mut res = vec![]; let mut res = vec![];
// 雜湊,用於記錄已被訪問過的頂點 // 雜湊集合,用於記錄已被訪問過的頂點
let mut visited = HashSet::new(); let mut visited = HashSet::new();
dfs(&graph, &mut visited, &mut res, start_vet); dfs(&graph, &mut visited, &mut res, start_vet);

View file

@ -96,7 +96,7 @@ impl MaxHeap {
// 交換根節點與最右葉節點(交換首元素與尾元素) // 交換根節點與最右葉節點(交換首元素與尾元素)
self.swap(0, self.size() - 1); 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); self.sift_down(0);
// 返回堆積頂元素 // 返回堆積頂元素

View file

@ -12,7 +12,7 @@ import utils
func graphBFS(graph: GraphAdjList, startVet: Vertex) -> [Vertex] { func graphBFS(graph: GraphAdjList, startVet: Vertex) -> [Vertex] {
// //
var res: [Vertex] = [] var res: [Vertex] = []
// //
var visited: Set<Vertex> = [startVet] var visited: Set<Vertex> = [startVet]
// BFS // BFS
var que: [Vertex] = [startVet] var que: [Vertex] = [startVet]

View file

@ -26,7 +26,7 @@ func dfs(graph: GraphAdjList, visited: inout Set<Vertex>, res: inout [Vertex], v
func graphDFS(graph: GraphAdjList, startVet: Vertex) -> [Vertex] { func graphDFS(graph: GraphAdjList, startVet: Vertex) -> [Vertex] {
// //
var res: [Vertex] = [] var res: [Vertex] = []
// //
var visited: Set<Vertex> = [] var visited: Set<Vertex> = []
dfs(graph: graph, visited: &visited, res: &res, vet: startVet) dfs(graph: graph, visited: &visited, res: &res, vet: startVet)
return res return res

View file

@ -12,7 +12,7 @@ import { Vertex } from '../modules/Vertex';
function graphBFS(graph: GraphAdjList, startVet: Vertex): Vertex[] { function graphBFS(graph: GraphAdjList, startVet: Vertex): Vertex[] {
// 頂點走訪序列 // 頂點走訪序列
const res: Vertex[] = []; const res: Vertex[] = [];
// 雜湊,用於記錄已被訪問過的頂點 // 雜湊集合,用於記錄已被訪問過的頂點
const visited: Set<Vertex> = new Set(); const visited: Set<Vertex> = new Set();
visited.add(startVet); visited.add(startVet);
// 佇列用於實現 BFS // 佇列用於實現 BFS

View file

@ -31,7 +31,7 @@ function dfs(
function graphDFS(graph: GraphAdjList, startVet: Vertex): Vertex[] { function graphDFS(graph: GraphAdjList, startVet: Vertex): Vertex[] {
// 頂點走訪序列 // 頂點走訪序列
const res: Vertex[] = []; const res: Vertex[] = [];
// 雜湊,用於記錄已被訪問過的頂點 // 雜湊集合,用於記錄已被訪問過的頂點
const visited: Set<Vertex> = new Set(); const visited: Set<Vertex> = new Set();
dfs(graph, visited, res, startVet); dfs(graph, visited, res, startVet);
return res; return res;

View file

@ -745,7 +745,7 @@
單向鏈結串列通常用於實現堆疊、佇列、雜湊表和圖等資料結構。 單向鏈結串列通常用於實現堆疊、佇列、雜湊表和圖等資料結構。
- **堆疊與佇列**:當插入和刪除操作都在鏈結串列的一端進行時,它表現出先進後出的特性,對應堆疊;當插入操作在鏈結串列的一端進行,刪除操作在鏈結串列的另一端進行,它表現出先進先出的特性,對應佇列。 - **堆疊與佇列**:當插入和刪除操作都在鏈結串列的一端進行時,它表現的特性為先進後出,對應堆疊;當插入操作在鏈結串列的一端進行,刪除操作在鏈結串列的另一端進行,它表現的特性為先進先出,對應佇列。
- **雜湊表**:鏈式位址是解決雜湊衝突的主流方案之一,在該方案中,所有衝突的元素都會被放到一個鏈結串列中。 - **雜湊表**:鏈式位址是解決雜湊衝突的主流方案之一,在該方案中,所有衝突的元素都會被放到一個鏈結串列中。
- **圖**:鄰接表是表示圖的一種常用方式,其中圖的每個頂點都與一個鏈結串列相關聯,鏈結串列中的每個元素都代表與該頂點相連的其他頂點。 - **圖**:鄰接表是表示圖的一種常用方式,其中圖的每個頂點都與一個鏈結串列相關聯,鏈結串列中的每個元素都代表與該頂點相連的其他頂點。

View file

@ -25,7 +25,7 @@
![計算機儲存系統](ram_and_cache.assets/storage_pyramid.png) ![計算機儲存系統](ram_and_cache.assets/storage_pyramid.png)
!!! note !!! tip
計算機的儲存層次結構體現了速度、容量和成本三者之間的精妙平衡。實際上,這種權衡普遍存在於所有工業領域,它要求我們在不同的優勢和限制之間找到最佳平衡點。 計算機的儲存層次結構體現了速度、容量和成本三者之間的精妙平衡。實際上,這種權衡普遍存在於所有工業領域,它要求我們在不同的優勢和限制之間找到最佳平衡點。

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 27 KiB

View file

@ -59,7 +59,7 @@
![重複排列](permutations_problem.assets/permutations_ii.png) ![重複排列](permutations_problem.assets/permutations_ii.png)
那麼如何去除重複的排列呢?最直接地,考慮藉助一個雜湊,直接對排列結果進行去重。然而這樣做不夠優雅,**因為生成重複排列的搜尋分支沒有必要,應當提前識別並剪枝**,這樣可以進一步提升演算法效率。 那麼如何去除重複的排列呢?最直接地,考慮藉助一個雜湊集合,直接對排列結果進行去重。然而這樣做不夠優雅,**因為生成重複排列的搜尋分支沒有必要,應當提前識別並剪枝**,這樣可以進一步提升演算法效率。
### 相等元素剪枝 ### 相等元素剪枝
@ -73,7 +73,7 @@
### 程式碼實現 ### 程式碼實現
在上一題的程式碼的基礎上,我們考慮在每一輪選擇中開啟一個雜湊 `duplicated` ,用於記錄該輪中已經嘗試過的元素,並將重複元素剪枝: 在上一題的程式碼的基礎上,我們考慮在每一輪選擇中開啟一個雜湊集合 `duplicated` ,用於記錄該輪中已經嘗試過的元素,並將重複元素剪枝:
```src ```src
[file]{permutations_ii}-[class]{}-[func]{permutations_ii} [file]{permutations_ii}-[class]{}-[func]{permutations_ii}

View file

@ -7,7 +7,7 @@
- 回溯問題通常包含多個約束條件,它們可用於實現剪枝操作。剪枝可以提前結束不必要的搜尋分支,大幅提升搜尋效率。 - 回溯問題通常包含多個約束條件,它們可用於實現剪枝操作。剪枝可以提前結束不必要的搜尋分支,大幅提升搜尋效率。
- 回溯演算法主要可用於解決搜尋問題和約束滿足問題。組合最佳化問題雖然可以用回溯演算法解決,但往往存在效率更高或效果更好的解法。 - 回溯演算法主要可用於解決搜尋問題和約束滿足問題。組合最佳化問題雖然可以用回溯演算法解決,但往往存在效率更高或效果更好的解法。
- 全排列問題旨在搜尋給定集合元素的所有可能的排列。我們藉助一個陣列來記錄每個元素是否被選擇,剪掉重複選擇同一元素的搜尋分支,確保每個元素只被選擇一次。 - 全排列問題旨在搜尋給定集合元素的所有可能的排列。我們藉助一個陣列來記錄每個元素是否被選擇,剪掉重複選擇同一元素的搜尋分支,確保每個元素只被選擇一次。
- 在全排列問題中,如果集合中存在重複元素,則最終結果會出現重複排列。我們需要約束相等元素在每輪中只能被選擇一次,這通常藉助一個雜湊來實現。 - 在全排列問題中,如果集合中存在重複元素,則最終結果會出現重複排列。我們需要約束相等元素在每輪中只能被選擇一次,這通常藉助一個雜湊集合來實現。
- 子集和問題的目標是在給定集合中找到和為目標值的所有子集。集合不區分元素順序,而搜尋過程會輸出所有順序的結果,產生重複子集。我們在回溯前將資料進行排序,並設定一個變數來指示每一輪的走訪起始點,從而將生成重複子集的搜尋分支進行剪枝。 - 子集和問題的目標是在給定集合中找到和為目標值的所有子集。集合不區分元素順序,而搜尋過程會輸出所有順序的結果,產生重複子集。我們在回溯前將資料進行排序,並設定一個變數來指示每一輪的走訪起始點,從而將生成重複子集的搜尋分支進行剪枝。
- 對於子集和問題,陣列中的相等元素會產生重複集合。我們利用陣列已排序的前置條件,透過判斷相鄰元素是否相等實現剪枝,從而確保相等元素在每輪中只能被選中一次。 - 對於子集和問題,陣列中的相等元素會產生重複集合。我們利用陣列已排序的前置條件,透過判斷相鄰元素是否相等實現剪枝,從而確保相等元素在每輪中只能被選中一次。
- $n$ 皇后問題旨在尋找將 $n$ 個皇后放置到 $n \times n$ 尺寸棋盤上的方案,要求所有皇后兩兩之間無法攻擊對方。該問題的約束條件有行約束、列約束、主對角線和次對角線約束。為滿足行約束,我們採用按行放置的策略,保證每一行放置一個皇后。 - $n$ 皇后問題旨在尋找將 $n$ 個皇后放置到 $n \times n$ 尺寸棋盤上的方案,要求所有皇后兩兩之間無法攻擊對方。該問題的約束條件有行約束、列約束、主對角線和次對角線約束。為滿足行約束,我們採用按行放置的策略,保證每一行放置一個皇后。

View file

@ -747,7 +747,7 @@ $T(n)$ 是一次函式,說明其執行時間的增長趨勢是線性的,因
時間複雜度分析本質上是計算“操作數量 $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))$ 。 若存在正實數 $c$ 和實數 $n_0$ ,使得對於所有的 $n > n_0$ ,均有 $T(n) \leq c \cdot f(n)$ ,則可認為 $f(n)$ 給出了 $T(n)$ 的一個漸近上界,記為 $T(n) = O(f(n))$ 。

View file

@ -1,6 +1,6 @@
# 數字編碼 * # 數字編碼 *
!!! note !!! tip
在本書中,標題帶有 * 符號的是選讀章節。如果你時間有限或感到理解困難,可以先跳過,等學完必讀章節後再單獨攻克。 在本書中,標題帶有 * 符號的是選讀章節。如果你時間有限或感到理解困難,可以先跳過,等學完必讀章節後再單獨攻克。

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Some files were not shown because too many files have changed in this diff Show more