This commit is contained in:
krahets 2023-07-25 16:42:55 +08:00
parent 0760e0865e
commit 902087ec81
23 changed files with 154 additions and 177 deletions

View file

@ -494,7 +494,7 @@ elementAddr = firtstElementAddr + elementLength * elementIndex
=== "Python"
```python title="array.py"
def insert(nums: list[int], num: int, index: int) -> None:
def insert(nums: list[int], num: int, index: int):
"""在数组的索引 index 处插入元素 num"""
# 把索引 index 以及之后的所有元素向后移动一位
for i in range(len(nums) - 1, index, -1):
@ -649,7 +649,7 @@ elementAddr = firtstElementAddr + elementLength * elementIndex
=== "Python"
```python title="array.py"
def remove(nums: list[int], index: int) -> None:
def remove(nums: list[int], index: int):
"""删除索引 index 处元素"""
# 把索引 index 之后的所有元素向前移动一位
for i in range(index, len(nums) - 1):
@ -798,7 +798,7 @@ elementAddr = firtstElementAddr + elementLength * elementIndex
=== "Python"
```python title="array.py"
def traverse(nums: list[int]) -> None:
def traverse(nums: list[int]):
"""遍历数组"""
count = 0
# 通过索引遍历数组

View file

@ -117,8 +117,7 @@ comments: true
```csharp title=""
/* 链表节点类 */
class ListNode
{
class ListNode {
int val; // 节点值
ListNode next; // 指向下一节点的引用
ListNode(int x) => val = x; //构造函数
@ -400,7 +399,7 @@ comments: true
=== "Python"
```python title="linked_list.py"
def insert(n0: ListNode, P: ListNode) -> None:
def insert(n0: ListNode, P: ListNode):
"""在链表的节点 n0 之后插入节点 P"""
n1 = n0.next
P.next = n1
@ -534,7 +533,7 @@ comments: true
=== "Python"
```python title="linked_list.py"
def remove(n0: ListNode) -> None:
def remove(n0: ListNode):
"""删除链表的节点 n0 之后的首个节点"""
if not n0.next:
return

View file

@ -543,15 +543,13 @@ comments: true
```csharp title="list.cs"
/* 通过索引遍历列表 */
int count = 0;
for (int i = 0; i < list.Count(); i++)
{
for (int i = 0; i < list.Count; i++) {
count++;
}
/* 直接遍历列表元素 */
count = 0;
foreach (int n in list)
{
foreach (int n in list) {
count++;
}
```
@ -1029,13 +1027,13 @@ comments: true
raise IndexError("索引越界")
return self.__nums[index]
def set(self, num: int, index: int) -> None:
def set(self, num: int, index: int):
"""更新元素"""
if index < 0 or index >= self.__size:
raise IndexError("索引越界")
self.__nums[index] = num
def add(self, num: int) -> None:
def add(self, num: int):
"""尾部添加元素"""
# 元素数量超出容量时,触发扩容机制
if self.size() == self.capacity():
@ -1043,7 +1041,7 @@ comments: true
self.__nums[self.__size] = num
self.__size += 1
def insert(self, num: int, index: int) -> None:
def insert(self, num: int, index: int):
"""中间插入元素"""
if index < 0 or index >= self.__size:
raise IndexError("索引越界")
@ -1070,7 +1068,7 @@ comments: true
# 返回被删除元素
return num
def extend_capacity(self) -> None:
def extend_capacity(self):
"""列表扩容"""
# 新建一个长度为原数组 __extend_ratio 倍的新数组,并将原数组拷贝到新数组
self.__nums = self.__nums + [0] * self.capacity() * (self.__extend_ratio - 1)
@ -1583,7 +1581,7 @@ comments: true
/* 列表扩容 */
public void extendCapacity() {
// 新建一个长度为 numsCapacity * extendRatio 的数组,并将原数组拷贝到新数组
System.Array.Resize(ref nums, numsCapacity * extendRatio);
Array.Resize(ref nums, numsCapacity * extendRatio);
// 更新列表容量
numsCapacity = nums.Length;
}

View file

@ -51,7 +51,7 @@ comments: true
=== "Python"
```python title="preorder_traversal_i_compact.py"
def pre_order(root: TreeNode) -> None:
def pre_order(root: TreeNode):
"""前序遍历:例题一"""
if root is None:
return
@ -228,7 +228,7 @@ comments: true
=== "Python"
```python title="preorder_traversal_ii_compact.py"
def pre_order(root: TreeNode) -> None:
def pre_order(root: TreeNode):
"""前序遍历:例题二"""
if root is None:
return
@ -413,12 +413,9 @@ comments: true
!!! question "例题三"
在二叉树中搜索所有值为 $7$ 的节点,请返回根节点到这些节点的路径,**要求路径中只存在一个值为 $7$ 的节点,并且不允许有值为 $3$ 的节点**。
在二叉树中搜索所有值为 $7$ 的节点,请返回根节点到这些节点的路径,**并要求路径中不包含值为 $3$ 的节点**。
在例题二的基础上添加剪枝操作,包括:
- 当遇到值为 $7$ 的节点时,记录解并返回,停止搜索。
- 当遇到值为 $3$ 的节点时,则直接返回,停止搜索。
为了满足以上约束条件,**我们需要添加剪枝操作**:在搜索过程中,若遇到值为 $3$ 的节点,则提前返回,停止继续搜索。
=== "Java"
@ -471,7 +468,7 @@ comments: true
=== "Python"
```python title="preorder_traversal_iii_compact.py"
def pre_order(root: TreeNode) -> None:
def pre_order(root: TreeNode):
"""前序遍历:例题三"""
# 剪枝
if root is None or root.val == 3:
@ -631,30 +628,13 @@ comments: true
[class]{}-[func]{preOrder}
```
剪枝是一个非常形象的名词。在搜索过程中,**我们利用约束条件“剪掉”了不满足约束条件的搜索分支**,避免许多无意义的尝试,从而提升搜索效率
剪枝是一个非常形象的名词。在搜索过程中,**我们“剪掉”了不满足约束条件的搜索分支**,避免许多无意义的尝试,从而实现搜索效率的提高
![根据约束条件剪枝](backtracking_algorithm.assets/preorder_find_constrained_paths.png)
<p align="center"> Fig. 根据约束条件剪枝 </p>
## 13.1.3. &nbsp; 常用术语
为了更清晰地分析算法问题,我们总结一下回溯算法中常用术语的含义,并对照例题三给出对应示例。
| 名词 | 定义 | 例题三 |
| ------------------- | -------------------------------------------------------------------------- | -------------------------------------------------------------------- |
| 解 Solution | 解是满足问题特定条件的答案,可能有一个或多个 | 根节点到节点 $7$ 的满足约束条件的所有路径 |
| 约束条件 Constraint | 约束条件是问题中限制解的可行性的条件,通常用于剪枝 | 路径中不包含节点 $3$ ,只包含一个节点 $7$ |
| 状态 State | 状态表示问题在某一时刻的情况,包括已经做出的选择 | 当前已访问的节点路径,即 `path` 节点列表 |
| 尝试 Attempt | 尝试是根据可用选择来探索解空间的过程,包括做出选择,更新状态,检查是否为解 | 递归访问左(右)子节点,将节点添加进 `path` ,判断节点的值是否为 $7$ |
| 回退 Backtracking | 回退指遇到不满足约束条件的状态时,撤销前面做出的选择,回到上一个状态 | 当越过叶结点、结束结点访问、遇到值为 $3$ 的节点时终止搜索,函数返回 |
| 剪枝 Pruning | 剪枝是根据问题特性和约束条件避免无意义的搜索路径的方法,可提高搜索效率 | 当遇到值为 $3$ 的节点时,则终止继续搜索 |
!!! tip
问题、解、状态等概念是通用的,在分治、回溯、动态规划、贪心等算法中都有涉及。
## 13.1.4. &nbsp; 框架代码
## 13.1.3. &nbsp; 框架代码
接下来,我们尝试将回溯的“尝试、回退、剪枝”的主体框架提炼出来,提升代码的通用性。
@ -669,6 +649,7 @@ comments: true
if (isSolution(state)) {
// 记录解
recordSolution(state, res);
// 停止继续搜索
return;
}
// 遍历所有选择
@ -694,6 +675,7 @@ comments: true
if (isSolution(state)) {
// 记录解
recordSolution(state, res);
// 停止继续搜索
return;
}
// 遍历所有选择
@ -713,12 +695,13 @@ comments: true
=== "Python"
```python title=""
def backtrack(state: State, choices: list[choice], res: list[state]) -> None:
def backtrack(state: State, choices: list[choice], res: list[state]):
"""回溯算法框架"""
# 判断是否为解
if is_solution(state):
# 记录解
record_solution(state, res)
# 停止继续搜索
return
# 遍历所有选择
for choice in choices:
@ -740,6 +723,7 @@ comments: true
if isSolution(state) {
// 记录解
recordSolution(state, res)
// 停止继续搜索
return
}
// 遍历所有选择
@ -765,6 +749,7 @@ comments: true
if (isSolution(state)) {
// 记录解
recordSolution(state, res);
// 停止继续搜索
return;
}
// 遍历所有选择
@ -790,6 +775,7 @@ comments: true
if (isSolution(state)) {
// 记录解
recordSolution(state, res);
// 停止继续搜索
return;
}
// 遍历所有选择
@ -815,6 +801,7 @@ comments: true
if (isSolution(state)) {
// 记录解
recordSolution(state, res, numRes);
// 停止继续搜索
return;
}
// 遍历所有选择
@ -840,6 +827,7 @@ comments: true
if (isSolution(state)) {
// 记录解
recordSolution(state, res);
// 停止继续搜索
return;
}
// 遍历所有选择
@ -865,6 +853,7 @@ comments: true
if isSolution(state: state) {
// 记录解
recordSolution(state: state, res: &res)
// 停止继续搜索
return
}
// 遍历所有选择
@ -896,6 +885,7 @@ comments: true
if (isSolution(state)) {
// 记录解
recordSolution(state, res);
// 停止继续搜索
return;
}
// 遍历所有选择
@ -912,7 +902,7 @@ comments: true
}
```
下面,我们基于框架代码来解决例题三:状态 `state` 为节点遍历路径,选择 `choices` 为当前节点的左子节点和右子节点,结果 `res` 是路径列表。
接下来,我们基于框架代码来解决例题三。状态 `state` 为节点遍历路径,选择 `choices` 为当前节点的左子节点和右子节点,结果 `res` 是路径列表。
=== "Java"
@ -948,7 +938,6 @@ comments: true
if (isSolution(state)) {
// 记录解
recordSolution(state, res);
return;
}
// 遍历所有选择
for (TreeNode choice : choices) {
@ -999,7 +988,6 @@ comments: true
if (isSolution(state)) {
// 记录解
recordSolution(state, res);
return;
}
// 遍历所有选择
for (TreeNode *choice : choices) {
@ -1048,7 +1036,6 @@ comments: true
if is_solution(state):
# 记录解
record_solution(state, res)
return
# 遍历所有选择
for choice in choices:
# 剪枝:检查选择是否合法
@ -1148,7 +1135,6 @@ comments: true
if (isSolution(state)) {
// 记录解
recordSolution(state, res);
return;
}
// 遍历所有选择
for (const choice of choices) {
@ -1203,7 +1189,6 @@ comments: true
if (isSolution(state)) {
// 记录解
recordSolution(state, res);
return;
}
// 遍历所有选择
for (const choice of choices) {
@ -1270,7 +1255,6 @@ comments: true
if (isSolution(state)) {
// 记录解
recordSolution(state, res);
return;
}
// 遍历所有选择
foreach (TreeNode choice in choices) {
@ -1320,7 +1304,6 @@ comments: true
// 检查是否为解
if isSolution(state: state) {
recordSolution(state: state, res: &res)
return
}
// 遍历所有选择
for choice in choices {
@ -1369,7 +1352,30 @@ comments: true
[class]{}-[func]{backtrack}
```
相比基于前序遍历的代码实现,基于回溯算法框架的代码实现虽然显得啰嗦,但通用性更好。实际上,**许多回溯问题都可以在该框架下解决**。我们只需根据具体问题来定义 `state``choices` ,并实现框架中的各个方法。
根据题意,当找到值为 7 的节点后应该继续搜索,**因此我们需要将记录解之后的 `return` 语句删除**。下图对比了保留或删除 `return` 语句的搜索过程。
![保留与删除 return 的搜索过程对比](backtracking_algorithm.assets/backtrack_remove_return_or_not.png)
<p align="center"> Fig. 保留与删除 return 的搜索过程对比 </p>
相比基于前序遍历的代码实现,基于回溯算法框架的代码实现虽然显得啰嗦,但通用性更好。实际上,**许多回溯问题都可以在该框架下解决**。我们只需根据具体问题来定义 `state``choices` ,并实现框架中的各个方法即可。
## 13.1.4. &nbsp; 常用术语
为了更清晰地分析算法问题,我们总结一下回溯算法中常用术语的含义,并对照例题三给出对应示例。
| 名词 | 定义 | 例题三 |
| ------------------- | -------------------------------------------------------------------------- | -------------------------------------------------------------------- |
| 解 Solution | 解是满足问题特定条件的答案,可能有一个或多个 | 根节点到节点 $7$ 的满足约束条件的所有路径 |
| 约束条件 Constraint | 约束条件是问题中限制解的可行性的条件,通常用于剪枝 | 路径中不包含节点 $3$ ,只包含一个节点 $7$ |
| 状态 State | 状态表示问题在某一时刻的情况,包括已经做出的选择 | 当前已访问的节点路径,即 `path` 节点列表 |
| 尝试 Attempt | 尝试是根据可用选择来探索解空间的过程,包括做出选择,更新状态,检查是否为解 | 递归访问左(右)子节点,将节点添加进 `path` ,判断节点的值是否为 $7$ |
| 回退 Backtracking | 回退指遇到不满足约束条件的状态时,撤销前面做出的选择,回到上一个状态 | 当越过叶结点、结束结点访问、遇到值为 $3$ 的节点时终止搜索,函数返回 |
| 剪枝 Pruning | 剪枝是根据问题特性和约束条件避免无意义的搜索路径的方法,可提高搜索效率 | 当遇到值为 $3$ 的节点时,则终止继续搜索 |
!!! tip
问题、解、状态等概念是通用的,在分治、回溯、动态规划、贪心等算法中都有涉及。
## 13.1.5. &nbsp; 优势与局限性

View file

@ -206,22 +206,19 @@ comments: true
```csharp title=""
/* 类 */
class Node
{
class Node {
int val;
Node next;
Node(int x) { val = x; }
}
/* 函数 */
int function()
{
int function() {
// do something...
return 0;
}
int algorithm(int n) // 输入数据
{
int algorithm(int n) { // 输入数据
const int a = 0; // 暂存数据(常量)
int b = 0; // 暂存数据(变量)
Node node = new Node(0); // 暂存数据(对象)
@ -323,7 +320,7 @@ comments: true
=== "Python"
```python title=""
def algorithm(n: int) -> None:
def algorithm(n: int):
a = 0 # O(1)
b = [0] * 10000 # O(1)
if n > 10:
@ -382,12 +379,10 @@ comments: true
=== "C#"
```csharp title=""
void algorithm(int n)
{
void algorithm(int n) {
int a = 0; // O(1)
int[] b = new int[10000]; // O(1)
if (n > 10)
{
if (n > 10) {
int[] nums = new int[n]; // O(n)
}
}
@ -472,7 +467,7 @@ comments: true
# do something
return 0
def loop(n: int) -> None:
def loop(n: int):
"""循环 O(1)"""
for _ in range(n):
function()
@ -570,22 +565,18 @@ comments: true
=== "C#"
```csharp title=""
int function()
{
int function() {
// do something
return 0;
}
/* 循环 O(1) */
void loop(int n)
{
for (int i = 0; i < n; i++)
{
void loop(int n) {
for (int i = 0; i < n; i++) {
function();
}
}
/* 递归 O(n) */
int recur(int n)
{
int recur(int n) {
if (n == 1) return 1;
return recur(n - 1);
}
@ -712,7 +703,7 @@ $$
=== "Python"
```python title="space_complexity.py"
def constant(n: int) -> None:
def constant(n: int):
"""常数阶"""
# 常量、变量、对象占用 O(1) 空间
a = 0
@ -951,7 +942,7 @@ $$
=== "Python"
```python title="space_complexity.py"
def linear(n: int) -> None:
def linear(n: int):
"""线性阶"""
# 长度为 n 的列表占用 O(n) 空间
nums = [0] * n
@ -1178,7 +1169,7 @@ $$
=== "Python"
```python title="space_complexity.py"
def linear_recur(n: int) -> None:
def linear_recur(n: int):
"""线性阶(递归实现)"""
print("递归 n =", n)
if n == 1:
@ -1326,7 +1317,7 @@ $$
=== "Python"
```python title="space_complexity.py"
def quadratic(n: int) -> None:
def quadratic(n: int):
"""平方阶"""
# 二维列表占用 O(n^2) 空间
num_matrix = [[0] * n for _ in range(n)]
@ -1568,8 +1559,9 @@ $$
return 0;
int *nums = malloc(sizeof(int) * n);
printf("递归 n = %d 中的 nums 长度 = %d\r\n", n, n);
int res = quadraticRecur(n - 1)
free(nums);
return quadraticRecur(n - 1);
return res;
}
```

View file

@ -52,7 +52,7 @@ $$
```python title=""
# 在某运行平台下
def algorithm(n: int) -> None:
def algorithm(n: int):
a = 2 # 1 ns
a = a + 1 # 1 ns
a = a * 2 # 10 ns
@ -66,12 +66,12 @@ $$
```go title=""
// 在某运行平台下
func algorithm(n int) {
a := 2 // 1 ns
a = a + 1 // 1 ns
a = a * 2 // 10 ns
a := 2 // 1 ns
a = a + 1 // 1 ns
a = a * 2 // 10 ns
// 循环 n 次
for i := 0; i < n; i++ { // 1 ns
fmt.Println(a) // 5 ns
for i := 0; i < n; i++ { // 1 ns
fmt.Println(a) // 5 ns
}
}
```
@ -125,15 +125,13 @@ $$
```csharp title=""
// 在某运行平台下
void algorithm(int n)
{
void algorithm(int n) {
int a = 2; // 1 ns
a = a + 1; // 1 ns
a = a * 2; // 10 ns
// 循环 n 次
for (int i = 0; i < n; i++)
{ // 1 ns ,每轮都要执行 i++
Console.WriteLine(0); // 5 ns
for (int i = 0; i < n; i++) { // 1 ns 每轮都要执行 i++
Console.WriteLine(0); // 5 ns
}
}
```
@ -232,14 +230,14 @@ $$
```python title=""
# 算法 A 时间复杂度:常数阶
def algorithm_A(n: int) -> None:
def algorithm_A(n: int):
print(0)
# 算法 B 时间复杂度:线性阶
def algorithm_B(n: int) -> None:
def algorithm_B(n: int):
for _ in range(n):
print(0)
# 算法 C 时间复杂度:常数阶
def algorithm_C(n: int) -> None:
def algorithm_C(n: int):
for _ in range(1000000):
print(0)
```
@ -333,23 +331,18 @@ $$
```csharp title=""
// 算法 A 时间复杂度:常数阶
void algorithm_A(int n)
{
void algorithm_A(int n) {
Console.WriteLine(0);
}
// 算法 B 时间复杂度:线性阶
void algorithm_B(int n)
{
for (int i = 0; i < n; i++)
{
void algorithm_B(int n) {
for (int i = 0; i < n; i++) {
Console.WriteLine(0);
}
}
// 算法 C 时间复杂度:常数阶
void algorithm_C(int n)
{
for (int i = 0; i < 1000000; i++)
{
void algorithm_C(int n) {
for (int i = 0; i < 1000000; i++) {
Console.WriteLine(0);
}
}
@ -456,7 +449,7 @@ $$
=== "Python"
```python title=""
def algorithm(n: int) -> None:
def algorithm(n: int):
a = 1 # +1
a = a + 1 # +1
a = a * 2 # +1
@ -524,14 +517,12 @@ $$
=== "C#"
```csharp title=""
void algorithm(int n)
{
void algorithm(int n) {
int a = 1; // +1
a = a + 1; // +1
a = a * 2; // +1
// 循环 n 次
for (int i = 0; i < n; i++) // +1每轮都执行 i ++
{
for (int i = 0; i < n; i++) { // +1每轮都执行 i ++
Console.WriteLine(0); // +1
}
}
@ -661,7 +652,7 @@ $$
=== "Python"
```python title=""
def algorithm(n: int) -> None:
def algorithm(n: int):
a = 1 # +0技巧 1
a = a + n # +0技巧 1
# +n技巧 2
@ -752,20 +743,16 @@ $$
=== "C#"
```csharp title=""
void algorithm(int n)
{
void algorithm(int n) {
int a = 1; // +0技巧 1
a = a + n; // +0技巧 1
// +n技巧 2
for (int i = 0; i < 5 * n + 1; i++)
{
for (int i = 0; i < 5 * n + 1; i++) {
Console.WriteLine(0);
}
// +n*n技巧 3
for (int i = 0; i < 2 * n; i++)
{
for (int j = 0; j < n + 1; j++)
{
for (int i = 0; i < 2 * n; i++) {
for (int j = 0; j < n + 1; j++) {
Console.WriteLine(0);
}
}
@ -1662,9 +1649,7 @@ $$
for (int j = 0; j < i; j++) {
if (nums[j] > nums[j + 1]) {
// 交换 nums[j] 与 nums[j + 1]
int tmp = nums[j];
nums[j] = nums[j + 1];
nums[j + 1] = tmp;
(nums[j + 1], nums[j]) = (nums[j], nums[j + 1]);
count += 3; // 元素交换包含 3 个单元操作
}
}

View file

@ -221,7 +221,7 @@ comments: true
# 邻接矩阵,行列索引对应“顶点索引”
adj_mat: list[list[int]] = []
def __init__(self, vertices: list[int], edges: list[list[int]]) -> None:
def __init__(self, vertices: list[int], edges: list[list[int]]):
"""构造方法"""
self.vertices: list[int] = []
self.adj_mat: list[list[int]] = []
@ -237,7 +237,7 @@ comments: true
"""获取顶点数量"""
return len(self.vertices)
def add_vertex(self, val: int) -> None:
def add_vertex(self, val: int):
"""添加顶点"""
n = self.size()
# 向顶点列表中添加新顶点的值
@ -249,7 +249,7 @@ comments: true
for row in self.adj_mat:
row.append(0)
def remove_vertex(self, index: int) -> None:
def remove_vertex(self, index: int):
"""删除顶点"""
if index >= self.size():
raise IndexError()
@ -261,7 +261,7 @@ comments: true
for row in self.adj_mat:
row.pop(index)
def add_edge(self, i: int, j: int) -> None:
def add_edge(self, i: int, j: int):
"""添加边"""
# 参数 i, j 对应 vertices 元素索引
# 索引越界与相等处理
@ -271,7 +271,7 @@ comments: true
self.adj_mat[i][j] = 1
self.adj_mat[j][i] = 1
def remove_edge(self, i: int, j: int) -> None:
def remove_edge(self, i: int, j: int):
"""删除边"""
# 参数 i, j 对应 vertices 元素索引
# 索引越界与相等处理
@ -280,7 +280,7 @@ comments: true
self.adj_mat[i][j] = 0
self.adj_mat[j][i] = 0
def print(self) -> None:
def print(self):
"""打印邻接矩阵"""
print("顶点列表 =", self.vertices)
print("邻接矩阵 =")
@ -1218,7 +1218,7 @@ comments: true
class GraphAdjList:
"""基于邻接表实现的无向图类"""
def __init__(self, edges: list[list[Vertex]]) -> None:
def __init__(self, edges: list[list[Vertex]]):
"""构造方法"""
# 邻接表key: 顶点value该顶点的所有邻接顶点
self.adj_list = dict[Vertex, Vertex]()
@ -1232,7 +1232,7 @@ comments: true
"""获取顶点数量"""
return len(self.adj_list)
def add_edge(self, vet1: Vertex, vet2: Vertex) -> None:
def add_edge(self, vet1: Vertex, vet2: Vertex):
"""添加边"""
if vet1 not in self.adj_list or vet2 not in self.adj_list or vet1 == vet2:
raise ValueError()
@ -1240,7 +1240,7 @@ comments: true
self.adj_list[vet1].append(vet2)
self.adj_list[vet2].append(vet1)
def remove_edge(self, vet1: Vertex, vet2: Vertex) -> None:
def remove_edge(self, vet1: Vertex, vet2: Vertex):
"""删除边"""
if vet1 not in self.adj_list or vet2 not in self.adj_list or vet1 == vet2:
raise ValueError()
@ -1248,14 +1248,14 @@ comments: true
self.adj_list[vet1].remove(vet2)
self.adj_list[vet2].remove(vet1)
def add_vertex(self, vet: Vertex) -> None:
def add_vertex(self, vet: Vertex):
"""添加顶点"""
if vet in self.adj_list:
return
# 在邻接表中添加一个新链表
self.adj_list[vet] = []
def remove_vertex(self, vet: Vertex) -> None:
def remove_vertex(self, vet: Vertex):
"""删除顶点"""
if vet not in self.adj_list:
raise ValueError()
@ -1266,7 +1266,7 @@ comments: true
if vet in self.adj_list[vertex]:
self.adj_list[vertex].remove(vet)
def print(self) -> None:
def print(self):
"""打印邻接表"""
print("邻接表 =")
for vertex in self.adj_list:

View file

@ -1170,7 +1170,7 @@ comments: true
if pair not in [None, self.removed]:
self.put(pair.key, pair.val)
def print(self) -> None:
def print(self):
"""打印哈希表"""
for pair in self.buckets:
if pair is not None:

View file

@ -374,18 +374,15 @@ comments: true
```csharp title="hash_map.cs"
/* 遍历哈希表 */
// 遍历键值对 Key->Value
foreach (var kv in map)
{
foreach (var kv in map) {
Console.WriteLine(kv.Key + " -> " + kv.Value);
}
// 单独遍历键 key
foreach (int key in map.Keys)
{
foreach (int key in map.Keys) {
Console.WriteLine(key);
}
// 单独遍历值 value
foreach (String val in map.Values)
{
foreach (String val in map.Values) {
Console.WriteLine(val);
}
```

View file

@ -152,7 +152,7 @@ comments: true
return new int[] { i, j };
}
}
return new int[0];
return Array.Empty<int>();
}
```
@ -402,7 +402,7 @@ comments: true
}
dic.Add(nums[i], i);
}
return new int[0];
return Array.Empty<int>();
}
```

View file

@ -84,7 +84,7 @@ comments: true
=== "Python"
```python title="bubble_sort.py"
def bubble_sort(nums: list[int]) -> None:
def bubble_sort(nums: list[int]):
"""冒泡排序"""
n = len(nums)
# 外循环:未排序区间为 [0, i]
@ -311,7 +311,7 @@ comments: true
=== "Python"
```python title="bubble_sort.py"
def bubble_sort_with_flag(nums: list[int]) -> None:
def bubble_sort_with_flag(nums: list[int]):
"""冒泡排序(标志优化)"""
n = len(nums)
# 外循环:未排序区间为 [0, i]

View file

@ -86,7 +86,7 @@ comments: true
=== "Python"
```python title="bucket_sort.py"
def bucket_sort(nums: list[float]) -> None:
def bucket_sort(nums: list[float]):
"""桶排序"""
# 初始化 k = n/2 个桶,预期向每个桶分配 2 个元素
k = len(nums) // 2

View file

@ -75,7 +75,7 @@ comments: true
=== "Python"
```python title="counting_sort.py"
def counting_sort_naive(nums: list[int]) -> None:
def counting_sort_naive(nums: list[int]):
"""计数排序"""
# 简单实现,无法用于排序对象
# 1. 统计数组最大元素 m
@ -416,7 +416,7 @@ $$
=== "Python"
```python title="counting_sort.py"
def counting_sort(nums: list[int]) -> None:
def counting_sort(nums: list[int]):
"""计数排序"""
# 完整实现,可排序对象,并且是稳定排序
# 1. 统计数组最大元素 m

View file

@ -66,7 +66,7 @@ comments: true
=== "Python"
```python title="insertion_sort.py"
def insertion_sort(nums: list[int]) -> None:
def insertion_sort(nums: list[int]):
"""插入排序"""
# 外循环:已排序区间为 [0, i-1]
for i in range(1, len(nums)):

View file

@ -146,7 +146,7 @@ comments: true
=== "Python"
```python title="merge_sort.py"
def merge(nums: list[int], left: int, mid: int, right: int) -> None:
def merge(nums: list[int], left: int, mid: int, right: int):
"""合并左子数组和右子数组"""
# 左子数组区间 [left, mid]
# 右子数组区间 [mid + 1, right]
@ -176,7 +176,7 @@ comments: true
nums[k] = tmp[j]
j += 1
def merge_sort(nums: list[int], left: int, right: int) -> None:
def merge_sort(nums: list[int], left: int, right: int):
"""归并排序"""
# 终止条件
if left >= right:

View file

@ -382,7 +382,7 @@ comments: true
=== "Python"
```python title="quick_sort.py"
def quick_sort(self, nums: list[int], left: int, right: int) -> None:
def quick_sort(self, nums: list[int], left: int, right: int):
"""快速排序"""
# 子数组长度为 1 时终止递归
if left >= right:
@ -1027,7 +1027,7 @@ comments: true
=== "Python"
```python title="quick_sort.py"
def quick_sort(self, nums: list[int], left: int, right: int) -> None:
def quick_sort(self, nums: list[int], left: int, right: int):
"""快速排序(尾递归优化)"""
# 子数组长度为 1 时终止
while left < right:

View file

@ -141,7 +141,7 @@ $$
# 传入 exp 而非 k 可以避免在此重复执行昂贵的次方计算
return (num // exp) % 10
def counting_sort_digit(nums: list[int], exp: int) -> None:
def counting_sort_digit(nums: list[int], exp: int):
"""计数排序(根据 nums 第 k 位排序)"""
# 十进制的位范围为 0~9 ,因此需要长度为 10 的桶
counter = [0] * 10
@ -164,7 +164,7 @@ $$
for i in range(n):
nums[i] = res[i]
def radix_sort(nums: list[int]) -> None:
def radix_sort(nums: list[int]):
"""基数排序"""
# 获取数组的最大元素,用于判断最大位数
m = max(nums)

View file

@ -625,7 +625,7 @@ comments: true
class ListNode:
"""双向链表节点"""
def __init__(self, val: int) -> None:
def __init__(self, val: int):
"""构造方法"""
self.val: int = val
self.next: ListNode | None = None # 后继节点引用(指针)
@ -634,7 +634,7 @@ comments: true
class LinkedListDeque:
"""基于双向链表实现的双向队列"""
def __init__(self) -> None:
def __init__(self):
"""构造方法"""
self.front: ListNode | None = None # 头节点 front
self.rear: ListNode | None = None # 尾节点 rear
@ -648,7 +648,7 @@ comments: true
"""判断双向队列是否为空"""
return self.size() == 0
def push(self, num: int, is_front: bool) -> None:
def push(self, num: int, is_front: bool):
"""入队操作"""
node = ListNode(num)
# 若链表为空,则令 front, rear 都指向 node
@ -668,11 +668,11 @@ comments: true
self.rear = node # 更新尾节点
self.__size += 1 # 更新队列长度
def push_first(self, num: int) -> None:
def push_first(self, num: int):
"""队首入队"""
self.push(num, True)
def push_last(self, num: int) -> None:
def push_last(self, num: int):
"""队尾入队"""
self.push(num, False)
@ -2042,7 +2042,7 @@ comments: true
class ArrayDeque:
"""基于环形数组实现的双向队列"""
def __init__(self, capacity: int) -> None:
def __init__(self, capacity: int):
"""构造方法"""
self.__nums: list[int] = [0] * capacity
self.__front: int = 0
@ -2067,7 +2067,7 @@ comments: true
# 当 i 越过数组头部后,回到尾部
return (i + self.capacity()) % self.capacity()
def push_first(self, num: int) -> None:
def push_first(self, num: int):
"""队首入队"""
if self.__size == self.capacity():
print("双向队列已满")
@ -2079,7 +2079,7 @@ comments: true
self.__nums[self.__front] = num
self.__size += 1
def push_last(self, num: int) -> None:
def push_last(self, num: int):
"""队尾入队"""
if self.__size == self.capacity():
print("双向队列已满")

View file

@ -218,10 +218,10 @@ comments: true
int pop = queue.Dequeue();
/* 获取队列的长度 */
int size = queue.Count();
int size = queue.Count;
/* 判断队列是否为空 */
bool isEmpty = queue.Count() == 0;
bool isEmpty = queue.Count == 0;
```
=== "Swift"
@ -471,7 +471,7 @@ comments: true
"""判断队列是否为空"""
return not self.__front
def push(self, num: int) -> None:
def push(self, num: int):
"""入队"""
# 尾节点后添加 num
node = ListNode(num)
@ -1272,7 +1272,7 @@ comments: true
class ArrayQueue:
"""基于环形数组实现的队列"""
def __init__(self, size: int) -> None:
def __init__(self, size: int):
"""构造方法"""
self.__nums: list[int] = [0] * size # 用于存储队列元素的数组
self.__front: int = 0 # 队首指针,指向队首元素
@ -1290,7 +1290,7 @@ comments: true
"""判断队列是否为空"""
return self.__size == 0
def push(self, num: int) -> None:
def push(self, num: int):
"""入队"""
if self.__size == self.capacity():
raise IndexError("队列已满")

View file

@ -217,10 +217,10 @@ comments: true
int pop = stack.Pop();
/* 获取栈的长度 */
int size = stack.Count();
int size = stack.Count;
/* 判断是否为空 */
bool isEmpty = stack.Count()==0;
bool isEmpty = stack.Count == 0;
```
=== "Swift"
@ -451,7 +451,7 @@ comments: true
"""判断栈是否为空"""
return not self.__peek
def push(self, val: int) -> None:
def push(self, val: int):
"""入栈"""
node = ListNode(val)
node.next = self.__peek
@ -1102,7 +1102,7 @@ comments: true
class ArrayStack:
"""基于数组实现的栈"""
def __init__(self) -> None:
def __init__(self):
"""构造方法"""
self.__stack: list[int] = []
@ -1114,7 +1114,7 @@ comments: true
"""判断栈是否为空"""
return self.__stack == []
def push(self, item: int) -> None:
def push(self, item: int):
"""入栈"""
self.__stack.append(item)

View file

@ -1448,7 +1448,7 @@ AVL 树的特点在于「旋转 Rotation」操作它能够在不影响二叉
=== "Python"
```python title="avl_tree.py"
def insert(self, val) -> None:
def insert(self, val):
"""插入节点"""
self.root = self.__insert_helper(self.root, val)
@ -1797,7 +1797,7 @@ AVL 树的特点在于「旋转 Rotation」操作它能够在不影响二叉
=== "Python"
```python title="avl_tree.py"
def remove(self, val: int) -> None:
def remove(self, val: int):
"""删除节点"""
self.root = self.__remove_helper(self.root, val)

View file

@ -349,7 +349,7 @@ comments: true
=== "Python"
```python title="binary_search_tree.py"
def insert(self, num: int) -> None:
def insert(self, num: int):
"""插入节点"""
# 若树为空,直接提前返回
if self.root is None:
@ -769,7 +769,7 @@ comments: true
=== "Python"
```python title="binary_search_tree.py"
def remove(self, num: int) -> None:
def remove(self, num: int):
"""删除节点"""
# 若树为空,直接提前返回
if self.root is None:

View file

@ -385,7 +385,7 @@ comments: true
=== "Python"
```python title="binary_tree_dfs.py"
def pre_order(root: TreeNode | None) -> None:
def pre_order(root: TreeNode | None):
"""前序遍历"""
if root is None:
return
@ -394,7 +394,7 @@ comments: true
pre_order(root=root.left)
pre_order(root=root.right)
def in_order(root: TreeNode | None) -> None:
def in_order(root: TreeNode | None):
"""中序遍历"""
if root is None:
return
@ -403,7 +403,7 @@ comments: true
res.append(root.val)
in_order(root=root.right)
def post_order(root: TreeNode | None) -> None:
def post_order(root: TreeNode | None):
"""后序遍历"""
if root is None:
return