From a6adc8e20a563af411d7174a7de5caff54529396 Mon Sep 17 00:00:00 2001 From: krahets Date: Tue, 9 Apr 2024 20:43:40 +0800 Subject: [PATCH] build --- docs/chapter_data_structure/summary.md | 32 ++ .../build_binary_tree_problem.md | 10 +- .../hanota_problem.md | 2 +- .../dp_problem_features.md | 4 +- .../dp_solution_pipeline.md | 23 +- .../edit_distance_problem.md | 8 +- .../intro_to_dynamic_programming.md | 13 +- .../knapsack_problem.md | 9 +- .../unbounded_knapsack_problem.md | 23 +- docs/chapter_graph/graph_operations.md | 23 +- docs/chapter_graph/graph_traversal.md | 30 +- .../fractional_knapsack_problem.md | 26 +- docs/chapter_greedy/max_capacity_problem.md | 4 +- .../max_product_cutting_problem.md | 6 +- docs/chapter_hashing/hash_collision.md | 26 +- docs/chapter_hashing/hash_map.md | 44 +-- docs/chapter_heap/build_heap.md | 9 +- docs/chapter_sorting/bubble_sort.md | 2 +- docs/chapter_sorting/bucket_sort.md | 4 +- docs/chapter_sorting/counting_sort.md | 4 +- docs/chapter_sorting/heap_sort.md | 9 +- docs/chapter_sorting/insertion_sort.md | 2 +- docs/chapter_sorting/merge_sort.md | 6 +- docs/chapter_sorting/radix_sort.md | 9 +- docs/chapter_sorting/selection_sort.md | 3 +- docs/chapter_stack_and_queue/deque.md | 359 +++++++++++++++--- docs/chapter_stack_and_queue/queue.md | 251 +++++++++--- docs/chapter_stack_and_queue/stack.md | 206 +++++++--- .../array_representation_of_tree.md | 35 +- docs/chapter_tree/avl_tree.md | 30 +- docs/chapter_tree/binary_search_tree.md | 50 ++- docs/chapter_tree/binary_tree_traversal.md | 33 +- docs/index.html | 38 +- en/docs/chapter_graph/graph_operations.md | 23 +- en/docs/chapter_graph/graph_traversal.md | 30 +- en/docs/chapter_hashing/hash_collision.md | 26 +- en/docs/chapter_hashing/hash_map.md | 44 +-- en/docs/chapter_hashing/summary.md | 10 +- en/docs/chapter_heap/build_heap.md | 9 +- en/docs/chapter_stack_and_queue/deque.md | 274 +++++++++++-- en/docs/chapter_stack_and_queue/queue.md | 137 ++++++- en/docs/chapter_stack_and_queue/stack.md | 99 ++++- .../array_representation_of_tree.md | 35 +- en/docs/chapter_tree/avl_tree.md | 30 +- en/docs/chapter_tree/binary_search_tree.md | 50 ++- en/docs/chapter_tree/binary_tree_traversal.md | 33 +- overrides/stylesheets/extra.css | 7 - zh-Hant/docs/index.html | 30 +- 48 files changed, 1599 insertions(+), 571 deletions(-) diff --git a/docs/chapter_data_structure/summary.md b/docs/chapter_data_structure/summary.md index db6889c8c..11ae986c4 100644 --- a/docs/chapter_data_structure/summary.md +++ b/docs/chapter_data_structure/summary.md @@ -36,3 +36,35 @@ comments: true **Q**:在构建栈(队列)的时候,未指定它的大小,为什么它们是“静态数据结构”呢? 在高级编程语言中,我们无须人工指定栈(队列)的初始容量,这个工作由类内部自动完成。例如,Java 的 `ArrayList` 的初始容量通常为 10。另外,扩容操作也是自动实现的。详见后续的“列表”章节。 + +**Q**:原码转补码的方法是“先取反后加 1”,那么补码转原码应该是逆运算“先减 1 后取反”,而补码转原码也一样可以通过“先取反后加 1”得到,这是为什么呢? + +**A**:这是因为原码和补码的相互转换实际上是计算“补数”的过程。我们先给出补数的定义:假设 $a + b = c$ ,那么我们称 $a$ 是 $b$ 到 $c$ 的补数,反之也称 $b$ 是 $a$ 到 $c$ 的补数。 + +给定一个 $n = 4$ 位长度的二进制数 $0010$ ,如果将这个数字看作原码(不考虑符号位),那么它的补码需通过“先取反后加 1”得到: + +$$ +0010 \rightarrow 1101 \rightarrow 1110 +$$ + +我们会发现,原码和补码的和是 $0010 + 1110 = 10000$ ,也就是说,补码 $1110$ 是原码 $0010$ 到 $10000$ 的“补数”。**这意味着上述“先取反后加 1”实际上是计算到 $10000$ 的补数的过程**。 + +那么,补码 $1110$ 到 $10000$ 的“补数”是多少呢?我们依然可以用“先取反后加 1”得到它: + +$$ +1110 \rightarrow 0001 \rightarrow 0010 +$$ + +换句话说,原码和补码互为对方到 $10000$ 的“补数”,因此“原码转补码”和“补码转原码”可以用相同的操作(先取反后加 1 )实现。 + +当然,我们也可以用逆运算来求补码 $1110$ 的原码,即“先减 1 后取反”: + +$$ +1110 \rightarrow 1101 \rightarrow 0010 +$$ + +总结来看,“先取反后加 1”和“先减 1 后取反”这两种运算都是在计算到 $10000$ 的补数,它们是等价的。 + +本质上看,“取反”操作实际上是求到 $1111$ 的补数(因为恒有 `原码 + 反码 = 1111`);而在反码基础上再加 1 得到的补码,就是到 $10000$ 的补数。 + +上述 $n = 4$ 为例,其可推广至任意位数的二进制数。 diff --git a/docs/chapter_divide_and_conquer/build_binary_tree_problem.md b/docs/chapter_divide_and_conquer/build_binary_tree_problem.md index 2efd606cc..41b289560 100644 --- a/docs/chapter_divide_and_conquer/build_binary_tree_problem.md +++ b/docs/chapter_divide_and_conquer/build_binary_tree_problem.md @@ -449,7 +449,13 @@ comments: true ```kotlin title="build_tree.kt" /* 构建二叉树:分治 */ - fun dfs(preorder: IntArray, inorderMap: Map, i: Int, l: Int, r: Int): TreeNode? { + fun dfs( + preorder: IntArray, + inorderMap: Map, + i: Int, + l: Int, + r: Int + ): TreeNode? { // 子树区间为空时终止 if (r - l < 0) return null // 初始化根节点 @@ -467,7 +473,7 @@ comments: true /* 构建二叉树 */ fun buildTree(preorder: IntArray, inorder: IntArray): TreeNode? { // 初始化哈希表,存储 inorder 元素到索引的映射 - val inorderMap: MutableMap = HashMap() + val inorderMap = HashMap() for (i in inorder.indices) { inorderMap[inorder[i]] = i } diff --git a/docs/chapter_divide_and_conquer/hanota_problem.md b/docs/chapter_divide_and_conquer/hanota_problem.md index 651bf8f69..9222b4c1b 100644 --- a/docs/chapter_divide_and_conquer/hanota_problem.md +++ b/docs/chapter_divide_and_conquer/hanota_problem.md @@ -479,7 +479,7 @@ comments: true /* 移动一个圆盘 */ fun move(src: MutableList, tar: MutableList) { // 从 src 顶部拿出一个圆盘 - val pan: Int = src.removeAt(src.size - 1) + val pan = src.removeAt(src.size - 1) // 将圆盘放入 tar 顶部 tar.add(pan) } diff --git a/docs/chapter_dynamic_programming/dp_problem_features.md b/docs/chapter_dynamic_programming/dp_problem_features.md index c1b6db0e5..b189a8ca2 100644 --- a/docs/chapter_dynamic_programming/dp_problem_features.md +++ b/docs/chapter_dynamic_programming/dp_problem_features.md @@ -295,7 +295,7 @@ $$ dp[2] = cost[2] // 状态转移:从较小子问题逐步求解较大子问题 for (i in 3..n) { - dp[i] = (min(dp[i - 1].toDouble(), dp[i - 2].toDouble()) + cost[i]).toInt() + dp[i] = min(dp[i - 1], dp[i - 2]) + cost[i] } return dp[n] } @@ -559,7 +559,7 @@ $$ var b = cost[2] for (i in 3..n) { val tmp = b - b = (min(a.toDouble(), tmp.toDouble()) + cost[i]).toInt() + b = min(a, tmp) + cost[i] a = tmp } return b diff --git a/docs/chapter_dynamic_programming/dp_solution_pipeline.md b/docs/chapter_dynamic_programming/dp_solution_pipeline.md index 68b1f7b69..0568962eb 100644 --- a/docs/chapter_dynamic_programming/dp_solution_pipeline.md +++ b/docs/chapter_dynamic_programming/dp_solution_pipeline.md @@ -349,11 +349,7 @@ $$ ```kotlin title="min_path_sum.kt" /* 最小路径和:暴力搜索 */ - fun minPathSumDFS( - grid: Array>, - i: Int, - j: Int - ): Int { + fun minPathSumDFS(grid: Array, i: Int, j: Int): Int { // 若为左上角单元格,则终止搜索 if (i == 0 && j == 0) { return grid[0][0] @@ -366,7 +362,7 @@ $$ val up = minPathSumDFS(grid, i - 1, j) val left = minPathSumDFS(grid, i, j - 1) // 返回从左上角到 (i, j) 的最小路径代价 - return (min(left.toDouble(), up.toDouble()) + grid[i][j]).toInt() + return min(left, up) + grid[i][j] } ``` @@ -711,8 +707,8 @@ $$ ```kotlin title="min_path_sum.kt" /* 最小路径和:记忆化搜索 */ fun minPathSumDFSMem( - grid: Array>, - mem: Array>, + grid: Array, + mem: Array, i: Int, j: Int ): Int { @@ -732,7 +728,7 @@ $$ val up = minPathSumDFSMem(grid, mem, i - 1, j) val left = minPathSumDFSMem(grid, mem, i, j - 1) // 记录并返回左上角到 (i, j) 的最小路径代价 - mem[i][j] = (min(left.toDouble(), up.toDouble()) + grid[i][j]).toInt() + mem[i][j] = min(left, up) + grid[i][j] return mem[i][j] } ``` @@ -1098,7 +1094,7 @@ $$ ```kotlin title="min_path_sum.kt" /* 最小路径和:动态规划 */ - fun minPathSumDP(grid: Array>): Int { + fun minPathSumDP(grid: Array): Int { val n = grid.size val m = grid[0].size // 初始化 dp 表 @@ -1115,8 +1111,7 @@ $$ // 状态转移:其余行和列 for (i in 1..>): Int { + fun minPathSumDPComp(grid: Array): Int { val n = grid.size val m = grid[0].size // 初始化 dp 表 @@ -1516,7 +1511,7 @@ $$ dp[0] = dp[0] + grid[i][0] // 状态转移:其余列 for (j in 1.., + choices: MutableList, state: Int, n: Int, res: MutableList ) { // 当爬到第 n 阶时,方案数量加 1 - if (state == n) res[0] = res[0] + 1 + if (state == n) + res[0] = res[0] + 1 // 遍历所有选择 for (choice in choices) { // 剪枝:不允许越过第 n 阶 @@ -382,7 +383,7 @@ comments: true fun climbingStairsBacktrack(n: Int): Int { val choices = mutableListOf(1, 2) // 可选择向上爬 1 阶或 2 阶 val state = 0 // 从第 0 阶开始爬 - val res = ArrayList() + val res = mutableListOf() res.add(0) // 使用 res[0] 记录方案数量 backtrack(choices, state, n, res) return res[0] @@ -1054,7 +1055,7 @@ $$ fun climbingStairsDFSMem(n: Int): Int { // mem[i] 记录爬到第 i 阶的方案总数,-1 代表无记录 val mem = IntArray(n + 1) - Arrays.fill(mem, -1) + mem.fill(-1) return dfs(n, mem) } ``` @@ -1596,9 +1597,7 @@ $$ var a = 1 var b = 2 for (i in 3..n) { - val tmp = b - b += a - a = tmp + b += a.also { a = b } } return b } diff --git a/docs/chapter_dynamic_programming/knapsack_problem.md b/docs/chapter_dynamic_programming/knapsack_problem.md index ac736e832..0b802848d 100644 --- a/docs/chapter_dynamic_programming/knapsack_problem.md +++ b/docs/chapter_dynamic_programming/knapsack_problem.md @@ -317,7 +317,7 @@ $$ val no = knapsackDFS(wgt, value, i - 1, c) val yes = knapsackDFS(wgt, value, i - 1, c - wgt[i - 1]) + value[i - 1] // 返回两种方案中价值更大的那一个 - return max(no.toDouble(), yes.toDouble()).toInt() + return max(no, yes) } ``` @@ -692,7 +692,7 @@ $$ val no = knapsackDFSMem(wgt, value, mem, i - 1, c) val yes = knapsackDFSMem(wgt, value, mem, i - 1, c - wgt[i - 1]) + value[i - 1] // 记录并返回两种方案中价值更大的那一个 - mem[i][c] = max(no.toDouble(), yes.toDouble()).toInt() + mem[i][c] = max(no, yes) return mem[i][c] } ``` @@ -1052,8 +1052,7 @@ $$ dp[i][c] = dp[i - 1][c] } else { // 不选和选物品 i 这两种方案的较大值 - dp[i][c] = max(dp[i - 1][c].toDouble(), (dp[i - 1][c - wgt[i - 1]] + value[i - 1]).toDouble()) - .toInt() + dp[i][c] = max(dp[i - 1][c], dp[i - 1][c - wgt[i - 1]] + value[i - 1]) } } } @@ -1445,7 +1444,7 @@ $$ if (wgt[i - 1] <= c) { // 不选和选物品 i 这两种方案的较大值 dp[c] = - max(dp[c].toDouble(), (dp[c - wgt[i - 1]] + value[i - 1]).toDouble()).toInt() + max(dp[c], dp[c - wgt[i - 1]] + value[i - 1]) } } } diff --git a/docs/chapter_dynamic_programming/unbounded_knapsack_problem.md b/docs/chapter_dynamic_programming/unbounded_knapsack_problem.md index cc23b7678..eccd216d2 100644 --- a/docs/chapter_dynamic_programming/unbounded_knapsack_problem.md +++ b/docs/chapter_dynamic_programming/unbounded_knapsack_problem.md @@ -327,11 +327,7 @@ $$ ```kotlin title="unbounded_knapsack.kt" /* 完全背包:动态规划 */ - fun unboundedKnapsackDP( - wgt: IntArray, - value: IntArray, - cap: Int - ): Int { + fun unboundedKnapsackDP(wgt: IntArray, value: IntArray, cap: Int): Int { val n = wgt.size // 初始化 dp 表 val dp = Array(n + 1) { IntArray(cap + 1) } @@ -343,8 +339,7 @@ $$ dp[i][c] = dp[i - 1][c] } else { // 不选和选物品 i 这两种方案的较大值 - dp[i][c] = max(dp[i - 1][c].toDouble(), (dp[i][c - wgt[i - 1]] + value[i - 1]).toDouble()) - .toInt() + dp[i][c] = max(dp[i - 1][c], dp[i][c - wgt[i - 1]] + value[i - 1]) } } } @@ -703,8 +698,7 @@ $$ dp[c] = dp[c] } else { // 不选和选物品 i 这两种方案的较大值 - dp[c] = - max(dp[c].toDouble(), (dp[c - wgt[i - 1]] + value[i - 1]).toDouble()).toInt() + dp[c] = max(dp[c], dp[c - wgt[i - 1]] + value[i - 1]) } } } @@ -1154,8 +1148,7 @@ $$ dp[i][a] = dp[i - 1][a] } else { // 不选和选硬币 i 这两种方案的较小值 - dp[i][a] = min(dp[i - 1][a].toDouble(), (dp[i][a - coins[i - 1]] + 1).toDouble()) - .toInt() + dp[i][a] = min(dp[i - 1][a], dp[i][a - coins[i - 1]] + 1) } } } @@ -1377,7 +1370,7 @@ $$ } // 状态转移 for i := 1; i <= n; i++ { - // 倒序遍历 + // 正序遍历 for a := 1; a <= amt; a++ { if coins[i-1] > a { // 若超过目标金额,则不选硬币 i @@ -1568,7 +1561,7 @@ $$ val MAX = amt + 1 // 初始化 dp 表 val dp = IntArray(amt + 1) - Arrays.fill(dp, MAX) + dp.fill(MAX) dp[0] = 0 // 状态转移 for (i in 1..n) { @@ -1578,7 +1571,7 @@ $$ dp[a] = dp[a] } else { // 不选和选硬币 i 这两种方案的较小值 - dp[a] = min(dp[a].toDouble(), (dp[a - coins[i - 1]] + 1).toDouble()).toInt() + dp[a] = min(dp[a], dp[a - coins[i - 1]] + 1) } } } @@ -2150,7 +2143,7 @@ $$ dp[0] = 1 // 状态转移 for i := 1; i <= n; i++ { - // 倒序遍历 + // 正序遍历 for a := 1; a <= amt; a++ { if coins[i-1] > a { // 若超过目标金额,则不选硬币 i diff --git a/docs/chapter_graph/graph_operations.md b/docs/chapter_graph/graph_operations.md index 2095f9657..f8768ba6f 100644 --- a/docs/chapter_graph/graph_operations.md +++ b/docs/chapter_graph/graph_operations.md @@ -1044,10 +1044,10 @@ comments: true ```kotlin title="graph_adjacency_matrix.kt" /* 基于邻接矩阵实现的无向图类 */ class GraphAdjMat(vertices: IntArray, edges: Array) { - val vertices: MutableList = ArrayList() // 顶点列表,元素代表“顶点值”,索引代表“顶点索引” - val adjMat: MutableList> = ArrayList() // 邻接矩阵,行列索引对应“顶点索引” + val vertices = mutableListOf() // 顶点列表,元素代表“顶点值”,索引代表“顶点索引” + val adjMat = mutableListOf>() // 邻接矩阵,行列索引对应“顶点索引” - /* 构造函数 */ + /* 构造方法 */ init { // 添加顶点 for (vertex in vertices) { @@ -1071,7 +1071,7 @@ comments: true // 向顶点列表中添加新顶点的值 vertices.add(value) // 在邻接矩阵中添加一行 - val newRow: MutableList = mutableListOf() + val newRow = mutableListOf() for (j in 0..= size()) throw IndexOutOfBoundsException() + if (index >= size()) + throw IndexOutOfBoundsException() // 在顶点列表中移除索引 index 的顶点 vertices.removeAt(index) // 在邻接矩阵中删除索引 index 的行 @@ -1099,7 +1100,8 @@ comments: true // 参数 i, j 对应 vertices 元素索引 fun addEdge(i: Int, j: Int) { // 索引越界与相等处理 - if (i < 0 || j < 0 || i >= size() || j >= size() || i == j) throw java.lang.IndexOutOfBoundsException() + if (i < 0 || j < 0 || i >= size() || j >= size() || i == j) + throw IndexOutOfBoundsException() // 在无向图中,邻接矩阵关于主对角线对称,即满足 (i, j) == (j, i) adjMat[i][j] = 1; adjMat[j][i] = 1; @@ -1109,7 +1111,8 @@ comments: true // 参数 i, j 对应 vertices 元素索引 fun removeEdge(i: Int, j: Int) { // 索引越界与相等处理 - if (i < 0 || j < 0 || i >= size() || j >= size() || i == j) throw java.lang.IndexOutOfBoundsException() + if (i < 0 || j < 0 || i >= size() || j >= size() || i == j) + throw IndexOutOfBoundsException() adjMat[i][j] = 0; adjMat[j][i] = 0; } @@ -2158,9 +2161,9 @@ comments: true /* 基于邻接表实现的无向图类 */ class GraphAdjList(edges: Array>) { // 邻接表,key:顶点,value:该顶点的所有邻接顶点 - val adjList: MutableMap> = HashMap() + val adjList = HashMap>() - /* 构造函数 */ + /* 构造方法 */ init { // 添加所有顶点和边 for (edge in edges) { @@ -2217,7 +2220,7 @@ comments: true fun print() { println("邻接表 =") for (pair in adjList.entries) { - val tmp = ArrayList() + val tmp = mutableListOf() for (vertex in pair.value) { tmp.add(vertex.value) } diff --git a/docs/chapter_graph/graph_traversal.md b/docs/chapter_graph/graph_traversal.md index 40b874271..ceb1552e9 100644 --- a/docs/chapter_graph/graph_traversal.md +++ b/docs/chapter_graph/graph_traversal.md @@ -418,24 +418,24 @@ BFS 通常借助队列来实现,代码如下所示。队列具有“先入先 ```kotlin title="graph_bfs.kt" /* 广度优先遍历 */ // 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 - fun graphBFS(graph: GraphAdjList, startVet: Vertex): List { + fun graphBFS(graph: GraphAdjList, startVet: Vertex): MutableList { // 顶点遍历序列 - val res: MutableList = ArrayList() + val res = mutableListOf() // 哈希表,用于记录已被访问过的顶点 - val visited: MutableSet = HashSet() + val visited = HashSet() visited.add(startVet) // 队列用于实现 BFS - val que: Queue = LinkedList() + val que = LinkedList() que.offer(startVet) // 以顶点 vet 为起点,循环直至访问完所有顶点 while (!que.isEmpty()) { val vet = que.poll() // 队首顶点出队 - res.add(vet) // 记录访问顶点 + res.add(vet) // 记录访问顶点 // 遍历该顶点的所有邻接顶点 for (adjVet in graph.adjList[vet]!!) { - if (visited.contains(adjVet)) continue // 跳过已被访问的顶点 - - que.offer(adjVet) // 只入队未访问的顶点 + if (visited.contains(adjVet)) + continue // 跳过已被访问的顶点 + que.offer(adjVet) // 只入队未访问的顶点 visited.add(adjVet) // 标记该顶点已被访问 } } @@ -866,11 +866,12 @@ BFS 通常借助队列来实现,代码如下所示。队列具有“先入先 res: MutableList, vet: Vertex? ) { - res.add(vet) // 记录访问顶点 + res.add(vet) // 记录访问顶点 visited.add(vet) // 标记该顶点已被访问 // 遍历该顶点的所有邻接顶点 for (adjVet in graph.adjList[vet]!!) { - if (visited.contains(adjVet)) continue // 跳过已被访问的顶点 + if (visited.contains(adjVet)) + continue // 跳过已被访问的顶点 // 递归访问邻接顶点 dfs(graph, visited, res, adjVet) } @@ -878,14 +879,11 @@ BFS 通常借助队列来实现,代码如下所示。队列具有“先入先 /* 深度优先遍历 */ // 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 - fun graphDFS( - graph: GraphAdjList, - startVet: Vertex? - ): List { + fun graphDFS(graph: GraphAdjList, startVet: Vertex?): MutableList { // 顶点遍历序列 - val res: MutableList = ArrayList() + val res = mutableListOf() // 哈希表,用于记录已被访问过的顶点 - val visited: MutableSet = HashSet() + val visited = HashSet() dfs(graph, visited, res, startVet) return res } diff --git a/docs/chapter_greedy/fractional_knapsack_problem.md b/docs/chapter_greedy/fractional_knapsack_problem.md index 303d83159..039371add 100644 --- a/docs/chapter_greedy/fractional_knapsack_problem.md +++ b/docs/chapter_greedy/fractional_knapsack_problem.md @@ -462,29 +462,25 @@ comments: true /* 物品 */ class Item( val w: Int, // 物品 - val v: Int // 物品价值 + val v: Int // 物品价值 ) /* 分数背包:贪心 */ - fun fractionalKnapsack( - wgt: IntArray, - value: IntArray, - c: Int - ): Double { + fun fractionalKnapsack(wgt: IntArray, _val: IntArray, c: Int): Double { // 创建物品列表,包含两个属性:重量、价值 var cap = c val items = arrayOfNulls(wgt.size) for (i in wgt.indices) { - items[i] = Item(wgt[i], value[i]) + items[i] = Item(wgt[i], _val[i]) } // 按照单位价值 item.v / item.w 从高到低进行排序 - Arrays.sort(items, Comparator.comparingDouble { item: Item -> -(item.v.toDouble() / item.w) }) + items.sortBy { item: Item? -> -(item!!.v.toDouble() / item.w) } // 循环贪心选择 var res = 0.0 for (item in items) { if (item!!.w <= cap) { // 若剩余容量充足,则将当前物品整个装进背包 - res += item.v.toDouble() + res += item.v cap -= item.w } else { // 若剩余容量不足,则将当前物品的一部分装进背包 @@ -497,25 +493,21 @@ comments: true } /* 分数背包:贪心 */ - fun fractionalKnapsack( - wgt: IntArray, - value: IntArray, - c: Int - ): Double { + fun fractionalKnapsack(wgt: IntArray, _val: IntArray, c: Int): Double { // 创建物品列表,包含两个属性:重量、价值 var cap = c val items = arrayOfNulls(wgt.size) for (i in wgt.indices) { - items[i] = Item(wgt[i], value[i]) + items[i] = Item(wgt[i], _val[i]) } // 按照单位价值 item.v / item.w 从高到低进行排序 - Arrays.sort(items, Comparator.comparingDouble { item: Item -> -(item.v.toDouble() / item.w) }) + items.sortBy { item: Item? -> -(item!!.v.toDouble() / item.w) } // 循环贪心选择 var res = 0.0 for (item in items) { if (item!!.w <= cap) { // 若剩余容量充足,则将当前物品整个装进背包 - res += item.v.toDouble() + res += item.v cap -= item.w } else { // 若剩余容量不足,则将当前物品的一部分装进背包 diff --git a/docs/chapter_greedy/max_capacity_problem.md b/docs/chapter_greedy/max_capacity_problem.md index 8799c7d7c..74a3b6111 100644 --- a/docs/chapter_greedy/max_capacity_problem.md +++ b/docs/chapter_greedy/max_capacity_problem.md @@ -381,8 +381,8 @@ $$ // 循环贪心选择,直至两板相遇 while (i < j) { // 更新最大容量 - val cap = (min(ht[i].toDouble(), ht[j].toDouble()) * (j - i)).toInt() - res = max(res.toDouble(), cap.toDouble()).toInt() + val cap = min(ht[i], ht[j]) * (j - i) + res = max(res, cap) // 向内移动短板 if (ht[i] < ht[j]) { i++ diff --git a/docs/chapter_greedy/max_product_cutting_problem.md b/docs/chapter_greedy/max_product_cutting_problem.md index e284753bf..b1eae8f51 100644 --- a/docs/chapter_greedy/max_product_cutting_problem.md +++ b/docs/chapter_greedy/max_product_cutting_problem.md @@ -357,14 +357,14 @@ $$ val b = n % 3 if (b == 1) { // 当余数为 1 时,将一对 1 * 3 转化为 2 * 2 - return 3.0.pow((a - 1).toDouble()).toInt() * 2 * 2 + return 3.0.pow((a - 1)).toInt() * 2 * 2 } if (b == 2) { // 当余数为 2 时,不做处理 - return 3.0.pow(a.toDouble()).toInt() * 2 * 2 + return 3.0.pow(a).toInt() * 2 * 2 } // 当余数为 0 时,不做处理 - return 3.0.pow(a.toDouble()).toInt() + return 3.0.pow(a).toInt() } ``` diff --git a/docs/chapter_hashing/hash_collision.md b/docs/chapter_hashing/hash_collision.md index 19a3ab807..29a61532c 100644 --- a/docs/chapter_hashing/hash_collision.md +++ b/docs/chapter_hashing/hash_collision.md @@ -1114,17 +1114,14 @@ comments: true // 遍历桶,若遇到指定 key ,则更新对应 val 并返回 for pair in bucket { if pair.key == key { - pair.val = val.clone(); + pair.val = val; return; } } let bucket = &mut self.buckets[index]; // 若无该 key ,则将键值对添加至尾部 - let pair = Pair { - key, - val: val.clone(), - }; + let pair = Pair { key, val }; bucket.push(pair); self.size += 1; } @@ -1328,7 +1325,7 @@ comments: true capacity = 4 loadThres = 2.0 / 3.0 extendRatio = 2 - buckets = ArrayList(capacity) + buckets = mutableListOf() for (i in 0.. // 桶数组 - private val TOMBSTONE = Pair(-1, "-1") // 删除标记 + private var size: Int // 键值对数量 + private var capacity: Int // 哈希表容量 + private val loadThres: Double // 触发扩容的负载因子阈值 + private val extendRatio: Int // 扩容倍数 + private var buckets: Array // 桶数组 + private val TOMBSTONE: Pair // 删除标记 /* 构造方法 */ init { + size = 0 + capacity = 4 + loadThres = 2.0 / 3.0 + extendRatio = 2 buckets = arrayOfNulls(capacity) + TOMBSTONE = Pair(-1, "-1") } /* 哈希函数 */ diff --git a/docs/chapter_hashing/hash_map.md b/docs/chapter_hashing/hash_map.md index d097859df..90caede5b 100755 --- a/docs/chapter_hashing/hash_map.md +++ b/docs/chapter_hashing/hash_map.md @@ -1589,15 +1589,9 @@ index = hash(key) % capacity /* 基于数组实现的哈希表 */ class ArrayHashMap { + // 初始化数组,包含 100 个桶 private val buckets = arrayOfNulls(100) - init { - // 初始化数组,包含 100 个桶 - for (i in 0..<100) { - buckets[i] = null - } - } - /* 哈希函数 */ fun hashFunc(key: Int): Int { val index = key % 100 @@ -1627,25 +1621,27 @@ index = hash(key) % capacity /* 获取所有键值对 */ fun pairSet(): MutableList { - val pairSet = ArrayList() + val pairSet = mutableListOf() for (pair in buckets) { - if (pair != null) pairSet.add(pair) + if (pair != null) + pairSet.add(pair) } return pairSet } /* 获取所有键 */ fun keySet(): MutableList { - val keySet = ArrayList() + val keySet = mutableListOf() for (pair in buckets) { - if (pair != null) keySet.add(pair.key) + if (pair != null) + keySet.add(pair.key) } return keySet } /* 获取所有值 */ fun valueSet(): MutableList { - val valueSet = ArrayList() + val valueSet = mutableListOf() for (pair in buckets) { pair?.let { valueSet.add(it.value) } } @@ -1657,22 +1653,16 @@ index = hash(key) % capacity for (kv in pairSet()) { val key = kv.key val value = kv.value - println("${key}->${value}") + println("${key} -> ${value}") } } } /* 基于数组实现的哈希表 */ class ArrayHashMap { + // 初始化数组,包含 100 个桶 private val buckets = arrayOfNulls(100) - init { - // 初始化数组,包含 100 个桶 - for (i in 0..<100) { - buckets[i] = null - } - } - /* 哈希函数 */ fun hashFunc(key: Int): Int { val index = key % 100 @@ -1702,25 +1692,27 @@ index = hash(key) % capacity /* 获取所有键值对 */ fun pairSet(): MutableList { - val pairSet = ArrayList() + val pairSet = mutableListOf() for (pair in buckets) { - if (pair != null) pairSet.add(pair) + if (pair != null) + pairSet.add(pair) } return pairSet } /* 获取所有键 */ fun keySet(): MutableList { - val keySet = ArrayList() + val keySet = mutableListOf() for (pair in buckets) { - if (pair != null) keySet.add(pair.key) + if (pair != null) + keySet.add(pair.key) } return keySet } /* 获取所有值 */ fun valueSet(): MutableList { - val valueSet = ArrayList() + val valueSet = mutableListOf() for (pair in buckets) { pair?.let { valueSet.add(it.value) } } @@ -1732,7 +1724,7 @@ index = hash(key) % capacity for (kv in pairSet()) { val key = kv.key val value = kv.value - println("${key}->${value}") + println("${key} -> ${value}") } } } diff --git a/docs/chapter_heap/build_heap.md b/docs/chapter_heap/build_heap.md index c19bdd6af..897147dff 100644 --- a/docs/chapter_heap/build_heap.md +++ b/docs/chapter_heap/build_heap.md @@ -189,13 +189,14 @@ comments: true ```kotlin title="my_heap.kt" /* 大顶堆 */ - class MaxHeap(nums: List?) { + class MaxHeap(nums: MutableList?) { // 使用列表而非数组,这样无须考虑扩容问题 - // 将列表元素原封不动添加进堆 - private val maxHeap = ArrayList(nums!!) + private val maxHeap = mutableListOf() - /* 构造函数,根据输入列表建堆 */ + /* 构造方法,根据输入列表建堆 */ init { + // 将列表元素原封不动添加进堆 + maxHeap.addAll(nums!!) // 堆化除叶节点以外的其他所有节点 for (i in parent(size() - 1) downTo 0) { siftDown(i) diff --git a/docs/chapter_sorting/bubble_sort.md b/docs/chapter_sorting/bubble_sort.md index 054b36ba6..072422267 100755 --- a/docs/chapter_sorting/bubble_sort.md +++ b/docs/chapter_sorting/bubble_sort.md @@ -264,7 +264,7 @@ comments: true for (j in 0.. nums[j + 1]) { // 交换 nums[j] 与 nums[j + 1] - nums[j] = nums[j+1].also { nums[j+1] = nums[j] } + nums[j] = nums[j + 1].also { nums[j + 1] = nums[j] } } } } diff --git a/docs/chapter_sorting/bucket_sort.md b/docs/chapter_sorting/bucket_sort.md index 1a74f6a07..fa030145b 100644 --- a/docs/chapter_sorting/bucket_sort.md +++ b/docs/chapter_sorting/bucket_sort.md @@ -395,9 +395,9 @@ comments: true fun bucketSort(nums: FloatArray) { // 初始化 k = n/2 个桶,预期向每个桶分配 2 个元素 val k = nums.size / 2 - val buckets = ArrayList>() + val buckets = mutableListOf>() for (i in 0.. nums[ma]) ma = l - if (r < n && nums[r] > nums[ma]) ma = r + if (l < n && nums[l] > nums[ma]) + ma = l + if (r < n && nums[r] > nums[ma]) + ma = r // 若节点 i 最大或索引 l, r 越界,则无须继续堆化,跳出 - if (ma == i) break + if (ma == i) + break // 交换两节点 nums[i] = nums[ma].also { nums[ma] = nums[i] } // 循环向下堆化 diff --git a/docs/chapter_sorting/insertion_sort.md b/docs/chapter_sorting/insertion_sort.md index 65dc87fdc..40ed3d4ed 100755 --- a/docs/chapter_sorting/insertion_sort.md +++ b/docs/chapter_sorting/insertion_sort.md @@ -240,7 +240,7 @@ comments: true for (i in nums.indices) { val base = nums[i] var j = i - 1 - // 内循环: 将 base 插入到已排序部分的正确位置 + // 内循环:将 base 插入到已排序区间 [0, i-1] 中的正确位置 while (j >= 0 && nums[j] > base) { nums[j + 1] = nums[j] // 将 nums[j] 向右移动一位 j-- diff --git a/docs/chapter_sorting/merge_sort.md b/docs/chapter_sorting/merge_sort.md index 854f2623f..618a9ae3e 100755 --- a/docs/chapter_sorting/merge_sort.md +++ b/docs/chapter_sorting/merge_sort.md @@ -596,8 +596,10 @@ comments: true var k = 0 // 当左右子数组都还有元素时,进行比较并将较小的元素复制到临时数组中 while (i <= mid && j <= right) { - if (nums[i] <= nums[j]) tmp[k++] = nums[i++] - else tmp[k++] = nums[j++] + if (nums[i] <= nums[j]) + tmp[k++] = nums[i++] + else + tmp[k++] = nums[j++] } // 将左子数组和右子数组的剩余元素复制到临时数组中 while (i <= mid) { diff --git a/docs/chapter_sorting/radix_sort.md b/docs/chapter_sorting/radix_sort.md index 60a3db4fd..3f98d3ed1 100644 --- a/docs/chapter_sorting/radix_sort.md +++ b/docs/chapter_sorting/radix_sort.md @@ -636,7 +636,7 @@ $$ // 统计 0~9 各数字的出现次数 for (i in 0.. deque; - + /* 元素入队 */ deque.push_back(2); // 添加至队尾 deque.push_back(5); deque.push_back(4); deque.push_front(3); // 添加至队首 deque.push_front(1); - + /* 访问元素 */ int front = deque.front(); // 队首元素 int back = deque.back(); // 队尾元素 - + /* 元素出队 */ deque.pop_front(); // 队首元素出队 deque.pop_back(); // 队尾元素出队 - + /* 获取双向队列的长度 */ int size = deque.size(); - + /* 判断双向队列是否为空 */ bool empty = deque.empty(); ``` @@ -94,25 +94,25 @@ comments: true ```java title="deque.java" /* 初始化双向队列 */ Deque deque = new LinkedList<>(); - + /* 元素入队 */ deque.offerLast(2); // 添加至队尾 deque.offerLast(5); deque.offerLast(4); deque.offerFirst(3); // 添加至队首 deque.offerFirst(1); - + /* 访问元素 */ int peekFirst = deque.peekFirst(); // 队首元素 int peekLast = deque.peekLast(); // 队尾元素 - + /* 元素出队 */ int popFirst = deque.pollFirst(); // 队首元素出队 int popLast = deque.pollLast(); // 队尾元素出队 - + /* 获取双向队列的长度 */ int size = deque.size(); - + /* 判断双向队列是否为空 */ boolean isEmpty = deque.isEmpty(); ``` @@ -123,25 +123,25 @@ comments: true /* 初始化双向队列 */ // 在 C# 中,将链表 LinkedList 看作双向队列来使用 LinkedList deque = new(); - + /* 元素入队 */ deque.AddLast(2); // 添加至队尾 deque.AddLast(5); deque.AddLast(4); deque.AddFirst(3); // 添加至队首 deque.AddFirst(1); - + /* 访问元素 */ int peekFirst = deque.First.Value; // 队首元素 int peekLast = deque.Last.Value; // 队尾元素 - + /* 元素出队 */ deque.RemoveFirst(); // 队首元素出队 deque.RemoveLast(); // 队尾元素出队 - + /* 获取双向队列的长度 */ int size = deque.Count; - + /* 判断双向队列是否为空 */ bool isEmpty = deque.Count == 0; ``` @@ -152,25 +152,25 @@ comments: true /* 初始化双向队列 */ // 在 Go 中,将 list 作为双向队列使用 deque := list.New() - + /* 元素入队 */ deque.PushBack(2) // 添加至队尾 deque.PushBack(5) deque.PushBack(4) deque.PushFront(3) // 添加至队首 deque.PushFront(1) - + /* 访问元素 */ front := deque.Front() // 队首元素 rear := deque.Back() // 队尾元素 - + /* 元素出队 */ deque.Remove(front) // 队首元素出队 deque.Remove(rear) // 队尾元素出队 - + /* 获取双向队列的长度 */ size := deque.Len() - + /* 判断双向队列是否为空 */ isEmpty := deque.Len() == 0 ``` @@ -339,25 +339,25 @@ comments: true ```kotlin title="deque.kt" /* 初始化双向队列 */ val deque = LinkedList() - + /* 元素入队 */ deque.offerLast(2) // 添加至队尾 deque.offerLast(5) deque.offerLast(4) deque.offerFirst(3) // 添加至队首 deque.offerFirst(1) - + /* 访问元素 */ val peekFirst = deque.peekFirst() // 队首元素 val peekLast = deque.peekLast() // 队尾元素 - + /* 元素出队 */ val popFirst = deque.pollFirst() // 队首元素出队 val popLast = deque.pollLast() // 队尾元素出队 - + /* 获取双向队列的长度 */ val size = deque.size - + /* 判断双向队列是否为空 */ val isEmpty = deque.isEmpty() ``` @@ -365,7 +365,32 @@ comments: true === "Ruby" ```ruby title="deque.rb" + # 初始化双向队列 + # Ruby 没有内直的双端队列,只能把 Array 当作双端队列来使用 + deque = [] + # 元素如队 + deque << 2 + deque << 5 + deque << 4 + # 请注意,由于是数组,Array#unshift 方法的时间复杂度为 O(n) + deque.unshift(3) + deque.unshift(1) + + # 访问元素 + peek_first = deque.first + peek_last = deque.last + + # 元素出队 + # 请注意,由于是数组, Array#shift 方法的时间复杂度为 O(n) + pop_front = deque.shift + pop_back = deque.pop + + # 获取双向队列的长度 + size = deque.length + + # 判断双向队列是否为空 + is_empty = size.zero? ``` === "Zig" @@ -1874,7 +1899,7 @@ comments: true ```kotlin title="linkedlist_deque.kt" /* 双向链表节点 */ - class ListNode(var value: Int) { + class ListNode(var _val: Int) { // 节点值 var next: ListNode? = null // 后继节点引用 var prev: ListNode? = null // 前驱节点引用 @@ -1882,9 +1907,9 @@ comments: true /* 基于双向链表实现的双向队列 */ class LinkedListDeque { - private var front: ListNode? = null // 头节点 front ,尾节点 rear - private var rear: ListNode? = null - private var queSize = 0 // 双向队列的长度 + private var front: ListNode? = null // 头节点 front + private var rear: ListNode? = null // 尾节点 rear + private var queSize: Int = 0 // 双向队列的长度 /* 获取双向队列的长度 */ fun size(): Int { @@ -1931,12 +1956,12 @@ comments: true /* 出队操作 */ fun pop(isFront: Boolean): Int { - if (isEmpty()) throw IndexOutOfBoundsException() - + if (isEmpty()) + throw IndexOutOfBoundsException() val value: Int // 队首出队操作 if (isFront) { - value = front!!.value // 暂存头节点值 + value = front!!._val // 暂存头节点值 // 删除头节点 val fNext = front!!.next if (fNext != null) { @@ -1946,7 +1971,7 @@ comments: true front = fNext // 更新头节点 // 队尾出队操作 } else { - value = rear!!.value // 暂存尾节点值 + value = rear!!._val // 暂存尾节点值 // 删除尾节点 val rPrev = rear!!.prev if (rPrev != null) { @@ -1971,17 +1996,14 @@ comments: true /* 访问队首元素 */ fun peekFirst(): Int { - if (isEmpty()) { - throw IndexOutOfBoundsException() - - } - return front!!.value + if (isEmpty()) throw IndexOutOfBoundsException() + return front!!._val } /* 访问队尾元素 */ fun peekLast(): Int { if (isEmpty()) throw IndexOutOfBoundsException() - return rear!!.value + return rear!!._val } /* 返回数组用于打印 */ @@ -1989,7 +2011,7 @@ comments: true var node = front val res = IntArray(size()) for (i in res.indices) { - res[i] = node!!.value + res[i] = node!!._val node = node.next } return res @@ -2000,9 +2022,138 @@ comments: true === "Ruby" ```ruby title="linkedlist_deque.rb" - [class]{ListNode}-[func]{} + =begin + File: linkedlist_deque.rb + Created Time: 2024-04-06 + Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) + =end - [class]{LinkedListDeque}-[func]{} + ### 双向链表节点 + class ListNode + attr_accessor :val + attr_accessor :next # 后继节点引用 + attr_accessor :prev # 前躯节点引用 + + ### 构造方法 ### + def initialize(val) + @val = val + end + end + + ### 基于双向链表实现的双向队列 ### + class LinkedListDeque + ### 获取双向队列的长度 ### + attr_reader :size + + ### 构造方法 ### + def initialize + @front = nil # 头节点 front + @rear = nil # 尾节点 rear + @size = 0 # 双向队列的长度 + end + + ### 判断双向队列是否为空 ### + def is_empty? + size.zero? + end + + ### 入队操作 ### + def push(num, is_front) + node = ListNode.new(num) + # 若链表为空, 则令 front 和 rear 都指向 node + if is_empty? + @front = @rear = node + # 队首入队操作 + elsif is_front + # 将 node 添加至链表头部 + @front.prev = node + node.next = @front + @front = node # 更新头节点 + # 队尾入队操作 + else + # 将 node 添加至链表尾部 + @rear.next = node + node.prev = @rear + @rear = node # 更新尾节点 + end + @size += 1 # 更新队列长度 + end + + ### 队首入队 ### + def push_first(num) + push(num, true) + end + + ### 队尾入队 ### + def push_last(num) + push(num, false) + end + + ### 出队操作 ### + def pop(is_front) + raise IndexError, '双向队列为空' if is_empty? + + # 队首出队操作 + if is_front + val = @front.val # 暂存头节点值 + # 删除头节点 + fnext = @front.next + unless fnext.nil? + fnext.prev = nil + @front.next = nil + end + @front = fnext # 更新头节点 + # 队尾出队操作 + else + val = @rear.val # 暂存尾节点值 + # 删除尾节点 + rprev = @rear.prev + unless rprev.nil? + rprev.next = nil + @rear.prev = nil + end + @rear = rprev # 更新尾节点 + end + @size -= 1 # 更新队列长度 + + val + end + + ### 队首出队 ### + def pop_first + pop(true) + end + + ### 队首出队 ### + def pop_last + pop(false) + end + + ### 访问队首元素 ### + def peek_first + raise IndexError, '双向队列为空' if is_empty? + + @front.val + end + + ### 访问队尾元素 ### + def peek_last + raise IndexError, '双向队列为空' if is_empty? + + @rear.val + end + + ### 返回数组用于打印 ### + def to_array + node = @front + res = Array.new(size, 0) + for i in 0...size + res[i] = node.val + node = node.next + end + res + end + end ``` === "Zig" @@ -3385,11 +3536,11 @@ comments: true === "Kotlin" ```kotlin title="array_deque.kt" - /* 基于环形数组实现的双向队列 */ + /* 构造方法 */ class ArrayDeque(capacity: Int) { - private var nums = IntArray(capacity) // 用于存储双向队列元素的数组 - private var front = 0 // 队首指针,指向队首元素 - private var queSize = 0 // 双向队列长度 + private var nums: IntArray = IntArray(capacity) // 用于存储双向队列元素的数组 + private var front: Int = 0 // 队首指针,指向队首元素 + private var queSize: Int = 0 // 双向队列长度 /* 获取双向队列的容量 */ fun capacity(): Int { @@ -3450,7 +3601,7 @@ comments: true return num } - /* 访问队尾元素 */ + /* 队尾出队 */ fun popLast(): Int { val num = peekLast() queSize-- @@ -3490,7 +3641,109 @@ comments: true === "Ruby" ```ruby title="array_deque.rb" - [class]{ArrayDeque}-[func]{} + ### 基于环形数组实现的双向队列 ### + class ArrayDeque + ### 获取双向队列的长度 ### + attr_reader :size + + ### 构造方法 ### + def initialize(capacity) + @nums = Array.new(capacity, 0) + @front = 0 + @size = 0 + end + + ### 获取双向队列的容量 ### + def capacity + @nums.length + end + + ### 判断双向队列是否为空 ### + def is_empty? + size.zero? + end + + ### 队首入队 ### + def push_first(num) + if size == capacity + puts '双向队列已满' + return + end + + # 队首指针向左移动一位 + # 通过取余操作实现 front 越过数组头部后回到尾部 + @front = index(@front - 1) + # 将 num 添加至队首 + @nums[@front] = num + @size += 1 + end + + ### 队尾入队 ### + def push_last(num) + if size == capacity + puts '双向队列已满' + return + end + + # 计算队尾指针,指向队尾索引 + 1 + rear = index(@front + size) + # 将 num 添加至队尾 + @nums[rear] = num + @size += 1 + end + + ### 队首出队 ### + def pop_first + num = peek_first + # 队首指针向后移动一位 + @front = index(@front + 1) + @size -= 1 + num + end + + ### 队尾出队 ### + def pop_last + num = peek_last + @size -= 1 + num + end + + ### 访问队首元素 ### + def peek_first + raise IndexError, '双向队列为空' if is_empty? + + @nums[@front] + end + + ### 访问队尾元素 ### + def peek_last + raise IndexError, '双向队列为空' if is_empty? + + # 计算尾元素索引 + last = index(@front + size - 1) + @nums[last] + end + + ### 返回数组用于打印 ### + def to_array + # 仅转换有效长度范围内的列表元素 + res = [] + for i in 0...size + res << @nums[index(@front + i)] + end + res + end + + private + + ### 计算环形数组索引 ### + def index(i) + # 通过取余操作实现数组首尾相连 + # 当 i 越过数组尾部后,回到头部 + # 当 i 越过数组头部后,回到尾部 + (i + capacity) % capacity + end + end ``` === "Zig" diff --git a/docs/chapter_stack_and_queue/queue.md b/docs/chapter_stack_and_queue/queue.md index 9b49b61a5..b862b1f2f 100755 --- a/docs/chapter_stack_and_queue/queue.md +++ b/docs/chapter_stack_and_queue/queue.md @@ -39,23 +39,23 @@ comments: true # 在 Python 中,我们一般将双向队列类 deque 当作队列使用 # 虽然 queue.Queue() 是纯正的队列类,但不太好用,因此不推荐 que: deque[int] = deque() - + # 元素入队 que.append(1) que.append(3) que.append(2) que.append(5) que.append(4) - + # 访问队首元素 front: int = que[0] - + # 元素出队 pop: int = que.popleft() - + # 获取队列的长度 size: int = len(que) - + # 判断队列是否为空 is_empty: bool = len(que) == 0 ``` @@ -65,23 +65,23 @@ comments: true ```cpp title="queue.cpp" /* 初始化队列 */ queue queue; - + /* 元素入队 */ queue.push(1); queue.push(3); queue.push(2); queue.push(5); queue.push(4); - + /* 访问队首元素 */ int front = queue.front(); - + /* 元素出队 */ queue.pop(); - + /* 获取队列的长度 */ int size = queue.size(); - + /* 判断队列是否为空 */ bool empty = queue.empty(); ``` @@ -91,23 +91,23 @@ comments: true ```java title="queue.java" /* 初始化队列 */ Queue queue = new LinkedList<>(); - + /* 元素入队 */ queue.offer(1); queue.offer(3); queue.offer(2); queue.offer(5); queue.offer(4); - + /* 访问队首元素 */ int peek = queue.peek(); - + /* 元素出队 */ int pop = queue.poll(); - + /* 获取队列的长度 */ int size = queue.size(); - + /* 判断队列是否为空 */ boolean isEmpty = queue.isEmpty(); ``` @@ -117,23 +117,23 @@ comments: true ```csharp title="queue.cs" /* 初始化队列 */ Queue queue = new(); - + /* 元素入队 */ queue.Enqueue(1); queue.Enqueue(3); queue.Enqueue(2); queue.Enqueue(5); queue.Enqueue(4); - + /* 访问队首元素 */ int peek = queue.Peek(); - + /* 元素出队 */ int pop = queue.Dequeue(); - + /* 获取队列的长度 */ int size = queue.Count; - + /* 判断队列是否为空 */ bool isEmpty = queue.Count == 0; ``` @@ -144,24 +144,24 @@ comments: true /* 初始化队列 */ // 在 Go 中,将 list 作为队列来使用 queue := list.New() - + /* 元素入队 */ queue.PushBack(1) queue.PushBack(3) queue.PushBack(2) queue.PushBack(5) queue.PushBack(4) - + /* 访问队首元素 */ peek := queue.Front() - + /* 元素出队 */ pop := queue.Front() queue.Remove(pop) - + /* 获取队列的长度 */ size := queue.Len() - + /* 判断队列是否为空 */ isEmpty := queue.Len() == 0 ``` @@ -172,24 +172,24 @@ comments: true /* 初始化队列 */ // Swift 没有内置的队列类,可以把 Array 当作队列来使用 var queue: [Int] = [] - + /* 元素入队 */ queue.append(1) queue.append(3) queue.append(2) queue.append(5) queue.append(4) - + /* 访问队首元素 */ let peek = queue.first! - + /* 元素出队 */ // 由于是数组,因此 removeFirst 的复杂度为 O(n) let pool = queue.removeFirst() - + /* 获取队列的长度 */ let size = queue.count - + /* 判断队列是否为空 */ let isEmpty = queue.isEmpty ``` @@ -200,24 +200,24 @@ comments: true /* 初始化队列 */ // JavaScript 没有内置的队列,可以把 Array 当作队列来使用 const queue = []; - + /* 元素入队 */ queue.push(1); queue.push(3); queue.push(2); queue.push(5); queue.push(4); - + /* 访问队首元素 */ const peek = queue[0]; - + /* 元素出队 */ // 底层是数组,因此 shift() 方法的时间复杂度为 O(n) const pop = queue.shift(); - + /* 获取队列的长度 */ const size = queue.length; - + /* 判断队列是否为空 */ const empty = queue.length === 0; ``` @@ -226,26 +226,26 @@ comments: true ```typescript title="queue.ts" /* 初始化队列 */ - // TypeScript 没有内置的队列,可以把 Array 当作队列来使用 + // TypeScript 没有内置的队列,可以把 Array 当作队列来使用 const queue: number[] = []; - + /* 元素入队 */ queue.push(1); queue.push(3); queue.push(2); queue.push(5); queue.push(4); - + /* 访问队首元素 */ const peek = queue[0]; - + /* 元素出队 */ // 底层是数组,因此 shift() 方法的时间复杂度为 O(n) const pop = queue.shift(); - + /* 获取队列的长度 */ const size = queue.length; - + /* 判断队列是否为空 */ const empty = queue.length === 0; ``` @@ -317,23 +317,23 @@ comments: true ```kotlin title="queue.kt" /* 初始化队列 */ val queue = LinkedList() - + /* 元素入队 */ queue.offer(1) queue.offer(3) queue.offer(2) queue.offer(5) queue.offer(4) - + /* 访问队首元素 */ val peek = queue.peek() - + /* 元素出队 */ val pop = queue.poll() - + /* 获取队列的长度 */ val size = queue.size - + /* 判断队列是否为空 */ val isEmpty = queue.isEmpty() ``` @@ -341,7 +341,29 @@ comments: true === "Ruby" ```ruby title="queue.rb" + # 初始化队列 + # Ruby 内置的队列(Thread::Queue) 没有 peek 和遍历方法,可以把 Array 当作队列来使用 + queue = [] + # 元素入队 + queue.push(1) + queue.push(3) + queue.push(2) + queue.push(5) + queue.push(4) + + # 访问队列元素 + peek = queue.first + + # 元素出队 + # 清注意,由于是数组,Array#shift 方法时间复杂度为 O(n) + pop = queue.shift + + # 获取队列的长度 + size = queue.length + + # 判断队列是否为空 + is_empty = queue.empty? ``` === "Zig" @@ -1206,7 +1228,7 @@ comments: true /* 访问队首元素 */ fun peek(): Int { if (isEmpty()) throw IndexOutOfBoundsException() - return front!!.value + return front!!._val } /* 将链表转化为 Array 并返回 */ @@ -1214,7 +1236,7 @@ comments: true var node = front val res = IntArray(size()) for (i in res.indices) { - res[i] = node!!.value + res[i] = node!!._val node = node.next } return res @@ -1225,7 +1247,68 @@ comments: true === "Ruby" ```ruby title="linkedlist_queue.rb" - [class]{LinkedListQueue}-[func]{} + ### 基于链表头现的队列 ### + class LinkedListQueue + ### 获取队列的长度 ### + attr_reader :size + + ### 构造方法 ### + def initialize + @front = nil # 头节点 front + @rear = nil # 尾节点 rear + @size = 0 + end + + ### 判断队列是否为空 ### + def is_empty? + @front.nil? + end + + ### 入队 ### + def push(num) + # 在尾节点后添加 num + node = ListNode.new(num) + + # 如果队列为空,则令头,尾节点都指向该节点 + if @front.nil? + @front = node + @rear = node + # 如果队列不为空,则令该节点添加到尾节点后 + else + @rear.next = node + @rear = node + end + + @size += 1 + end + + ### 出队 ### + def pop + num = peek + # 删除头节点 + @front = @front.next + @size -= 1 + num + end + + ### 访问队首元素 ### + def peek + raise IndexError, '队列为空' if is_empty? + + @front.val + end + + ### 将链表为 Array 并返回 ### + def to_array + queue = [] + temp = @front + while temp + queue << temp.val + temp = temp.next + end + queue + end + end ``` === "Zig" @@ -2144,9 +2227,9 @@ comments: true ```kotlin title="array_queue.kt" /* 基于环形数组实现的队列 */ class ArrayQueue(capacity: Int) { - private val nums = IntArray(capacity) // 用于存储队列元素的数组 - private var front = 0 // 队首指针,指向队首元素 - private var queSize = 0 // 队列长度 + private val nums: IntArray = IntArray(capacity) // 用于存储队列元素的数组 + private var front: Int = 0 // 队首指针,指向队首元素 + private var queSize: Int = 0 // 队列长度 /* 获取队列的容量 */ fun capacity(): Int { @@ -2211,7 +2294,69 @@ comments: true === "Ruby" ```ruby title="array_queue.rb" - [class]{ArrayQueue}-[func]{} + ### 基于环形数组实现的队列 ### + class ArrayQueue + ### 获取队列的长度 ### + attr_reader :size + + ### 构造方法 ### + def initialize(size) + @nums = Array.new(size, 0) # 用于存储队列元素的数组 + @front = 0 # 队首指针,指向队首元素 + @size = 0 # 队列长度 + end + + ### 获取队列的容量 ### + def capacity + @nums.length + end + + ### 判断队列是否为空 ### + def is_empty? + size.zero? + end + + ### 入队 ### + def push(num) + raise IndexError, '队列已满' if size == capacity + + # 计算队尾指针,指向队尾索引 + 1 + # 通过取余操作实现 rear 越过数组尾部后回到头部 + rear = (@front + size) % capacity + # 将 num 添加至队尾 + @nums[rear] = num + @size += 1 + end + + ### 出队 ### + def pop + num = peek + # 队首指针向后移动一位,若越过尾部,则返回到数组头部 + @front = (@front + 1) % capacity + @size -= 1 + num + end + + ### 访问队首元素 ### + def peek + raise IndexError, '队列为空' if is_empty? + + @nums[@front] + end + + ### 返回列表用于打印 ### + def to_array + res = Array.new(size, 0) + j = @front + + for i in 0...size + res[i] = @nums[j % capacity] + j += 1 + end + + res + end + end ``` === "Zig" diff --git a/docs/chapter_stack_and_queue/stack.md b/docs/chapter_stack_and_queue/stack.md index 489cf077c..9d7324421 100755 --- a/docs/chapter_stack_and_queue/stack.md +++ b/docs/chapter_stack_and_queue/stack.md @@ -36,25 +36,25 @@ comments: true ```python title="stack.py" # 初始化栈 - # Python 没有内置的栈类,可以把 list 当作栈来使用 + # Python 没有内置的栈类,可以把 list 当作栈来使用 stack: list[int] = [] - + # 元素入栈 stack.append(1) stack.append(3) stack.append(2) stack.append(5) stack.append(4) - + # 访问栈顶元素 peek: int = stack[-1] - + # 元素出栈 pop: int = stack.pop() - + # 获取栈的长度 size: int = len(stack) - + # 判断是否为空 is_empty: bool = len(stack) == 0 ``` @@ -64,23 +64,23 @@ comments: true ```cpp title="stack.cpp" /* 初始化栈 */ stack stack; - + /* 元素入栈 */ stack.push(1); stack.push(3); stack.push(2); stack.push(5); stack.push(4); - + /* 访问栈顶元素 */ int top = stack.top(); - + /* 元素出栈 */ stack.pop(); // 无返回值 - + /* 获取栈的长度 */ int size = stack.size(); - + /* 判断是否为空 */ bool empty = stack.empty(); ``` @@ -116,23 +116,23 @@ comments: true ```csharp title="stack.cs" /* 初始化栈 */ Stack stack = new(); - + /* 元素入栈 */ stack.Push(1); stack.Push(3); stack.Push(2); stack.Push(5); stack.Push(4); - + /* 访问栈顶元素 */ int peek = stack.Peek(); - + /* 元素出栈 */ int pop = stack.Pop(); - + /* 获取栈的长度 */ int size = stack.Count; - + /* 判断是否为空 */ bool isEmpty = stack.Count == 0; ``` @@ -143,24 +143,24 @@ comments: true /* 初始化栈 */ // 在 Go 中,推荐将 Slice 当作栈来使用 var stack []int - + /* 元素入栈 */ stack = append(stack, 1) stack = append(stack, 3) stack = append(stack, 2) stack = append(stack, 5) stack = append(stack, 4) - + /* 访问栈顶元素 */ peek := stack[len(stack)-1] - + /* 元素出栈 */ pop := stack[len(stack)-1] stack = stack[:len(stack)-1] - + /* 获取栈的长度 */ size := len(stack) - + /* 判断是否为空 */ isEmpty := len(stack) == 0 ``` @@ -171,23 +171,23 @@ comments: true /* 初始化栈 */ // Swift 没有内置的栈类,可以把 Array 当作栈来使用 var stack: [Int] = [] - + /* 元素入栈 */ stack.append(1) stack.append(3) stack.append(2) stack.append(5) stack.append(4) - + /* 访问栈顶元素 */ let peek = stack.last! - + /* 元素出栈 */ let pop = stack.removeLast() - + /* 获取栈的长度 */ let size = stack.count - + /* 判断是否为空 */ let isEmpty = stack.isEmpty ``` @@ -196,25 +196,25 @@ comments: true ```javascript title="stack.js" /* 初始化栈 */ - // JavaScript 没有内置的栈类,可以把 Array 当作栈来使用 + // JavaScript 没有内置的栈类,可以把 Array 当作栈来使用 const stack = []; - + /* 元素入栈 */ stack.push(1); stack.push(3); stack.push(2); stack.push(5); stack.push(4); - + /* 访问栈顶元素 */ const peek = stack[stack.length-1]; - + /* 元素出栈 */ const pop = stack.pop(); - + /* 获取栈的长度 */ const size = stack.length; - + /* 判断是否为空 */ const is_empty = stack.length === 0; ``` @@ -223,25 +223,25 @@ comments: true ```typescript title="stack.ts" /* 初始化栈 */ - // TypeScript 没有内置的栈类,可以把 Array 当作栈来使用 + // TypeScript 没有内置的栈类,可以把 Array 当作栈来使用 const stack: number[] = []; - + /* 元素入栈 */ stack.push(1); stack.push(3); stack.push(2); stack.push(5); stack.push(4); - + /* 访问栈顶元素 */ const peek = stack[stack.length - 1]; - + /* 元素出栈 */ const pop = stack.pop(); - + /* 获取栈的长度 */ const size = stack.length; - + /* 判断是否为空 */ const is_empty = stack.length === 0; ``` @@ -311,23 +311,23 @@ comments: true ```kotlin title="stack.kt" /* 初始化栈 */ val stack = Stack() - + /* 元素入栈 */ stack.push(1) stack.push(3) stack.push(2) stack.push(5) stack.push(4) - + /* 访问栈顶元素 */ val peek = stack.peek() - + /* 元素出栈 */ val pop = stack.pop() - + /* 获取栈的长度 */ val size = stack.size - + /* 判断是否为空 */ val isEmpty = stack.isEmpty() ``` @@ -335,7 +335,28 @@ comments: true === "Ruby" ```ruby title="stack.rb" + # 初始化栈 + # Ruby 没有内置的栈类,可以把 Array 当作栈来使用 + stack = [] + # 元素入栈 + stack << 1 + stack << 3 + stack << 2 + stack << 5 + stack << 4 + + # 访问栈顶元素 + peek = stack.last + + # 元素出栈 + pop = stack.pop + + # 获取栈的长度 + size = stack.length + + # 判断是否为空 + is_empty = stack.empty? ``` === "Zig" @@ -1078,7 +1099,7 @@ comments: true /* 访问栈顶元素 */ fun peek(): Int? { if (isEmpty()) throw IndexOutOfBoundsException() - return stackPeek?.value + return stackPeek?._val } /* 将 List 转化为 Array 并返回 */ @@ -1086,7 +1107,7 @@ comments: true var node = stackPeek val res = IntArray(size()) for (i in res.size - 1 downTo 0) { - res[i] = node?.value!! + res[i] = node?._val!! node = node.next } return res @@ -1097,7 +1118,54 @@ comments: true === "Ruby" ```ruby title="linkedlist_stack.rb" - [class]{LinkedListStack}-[func]{} + ### 基于链表实现的栈 ### + class LinkedListStack + attr_reader :size + + ### 构造方法 ### + def initialize + @size = 0 + end + + ### 判断栈是否为空 ### + def is_empty? + @peek.nil? + end + + ### 入栈 ### + def push(val) + node = ListNode.new(val) + node.next = @peek + @peek = node + @size += 1 + end + + ### 出栈 ### + def pop + num = peek + @peek = @peek.next + @size -= 1 + num + end + + ### 访问栈顶元素 ### + def peek + raise IndexError, '栈为空' if is_empty? + + @peek.val + end + + ### 将链表转化为 Array 并反回 ### + def to_array + arr = [] + node = @peek + while node + arr << node.val + node = node.next + end + arr.reverse + end + end ``` === "Zig" @@ -1738,7 +1806,7 @@ comments: true /* 基于数组实现的栈 */ class ArrayStack { // 初始化列表(动态数组) - private val stack = ArrayList() + private val stack = mutableListOf() /* 获取栈的长度 */ fun size(): Int { @@ -1769,7 +1837,7 @@ comments: true /* 将 List 转化为 Array 并返回 */ fun toArray(): Array { - return stack.toArray() + return stack.toTypedArray() } } ``` @@ -1777,7 +1845,47 @@ comments: true === "Ruby" ```ruby title="array_stack.rb" - [class]{ArrayStack}-[func]{} + ### 基于数组实现的栈 ### + class ArrayStack + ### 构造方法 ### + def initialize + @stack = [] + end + + ### 获取栈的长度 ### + def size + @stack.length + end + + ### 判断栈是否为空 ### + def is_empty? + @stack.empty? + end + + ### 入栈 ### + def push(item) + @stack << item + end + + ### 出栈 ### + def pop + raise IndexError, '栈为空' if is_empty? + + @stack.pop + end + + ### 访问栈顶元素 ### + def peek + raise IndexError, '栈为空' if is_empty? + + @stack.last + end + + ### 返回列表用于打印 ### + def to_array + @stack + end + end ``` === "Zig" diff --git a/docs/chapter_tree/array_representation_of_tree.md b/docs/chapter_tree/array_representation_of_tree.md index 75aab4ab5..e0a418810 100644 --- a/docs/chapter_tree/array_representation_of_tree.md +++ b/docs/chapter_tree/array_representation_of_tree.md @@ -1172,8 +1172,8 @@ comments: true === "Kotlin" ```kotlin title="array_binary_tree.kt" - /* 数组表示下的二叉树类 */ - class ArrayBinaryTree(val tree: List) { + /* 构造方法 */ + class ArrayBinaryTree(val tree: MutableList) { /* 列表容量 */ fun size(): Int { return tree.size @@ -1202,11 +1202,12 @@ comments: true } /* 层序遍历 */ - fun levelOrder(): List { - val res = ArrayList() + fun levelOrder(): MutableList { + val res = mutableListOf() // 直接遍历数组 for (i in 0..) { // 若为空位,则返回 - if (value(i) == null) return + if (value(i) == null) + return // 前序遍历 - if ("pre" == order) res.add(value(i)) + if ("pre" == order) + res.add(value(i)) dfs(left(i), order, res) // 中序遍历 - if ("in" == order) res.add(value(i)) + if ("in" == order) + res.add(value(i)) dfs(right(i), order, res) // 后序遍历 - if ("post" == order) res.add(value(i)) + if ("post" == order) + res.add(value(i)) } /* 前序遍历 */ - fun preOrder(): List { - val res = ArrayList() + fun preOrder(): MutableList { + val res = mutableListOf() dfs(0, "pre", res) return res } /* 中序遍历 */ - fun inOrder(): List { - val res = ArrayList() + fun inOrder(): MutableList { + val res = mutableListOf() dfs(0, "in", res) return res } /* 后序遍历 */ - fun postOrder(): List { - val res = ArrayList() + fun postOrder(): MutableList { + val res = mutableListOf() dfs(0, "post", res) return res } diff --git a/docs/chapter_tree/avl_tree.md b/docs/chapter_tree/avl_tree.md index 9bf3e3eb0..77f785b81 100644 --- a/docs/chapter_tree/avl_tree.md +++ b/docs/chapter_tree/avl_tree.md @@ -448,7 +448,7 @@ AVL 树既是二叉搜索树,也是平衡二叉树,同时满足这两类二 /* 更新节点高度 */ fun updateHeight(node: TreeNode?) { // 节点高度等于最高子树高度 + 1 - node?.height = (max(height(node?.left).toDouble(), height(node?.right).toDouble()) + 1).toInt() + node?.height = max(height(node?.left), height(node?.right)) + 1 } ``` @@ -2022,10 +2022,12 @@ AVL 树的节点插入操作与二叉搜索树在主体上类似。唯一的区 return TreeNode(value) var node = n /* 1. 查找插入位置并插入节点 */ - if (value < node.value) node.left = insertHelper(node.left, value) - else if (value > node.value) node.right = insertHelper(node.right, value) - else return node // 重复节点不插入,直接返回 - + if (value < node.value) + node.left = insertHelper(node.left, value) + else if (value > node.value) + node.right = insertHelper(node.right, value) + else + return node // 重复节点不插入,直接返回 updateHeight(node) // 更新节点高度 /* 2. 执行旋转操作,使该子树重新恢复平衡 */ node = rotate(node) @@ -2601,14 +2603,22 @@ AVL 树的节点插入操作与二叉搜索树在主体上类似。唯一的区 fun removeHelper(n: TreeNode?, value: Int): TreeNode? { var node = n ?: return null /* 1. 查找节点并删除 */ - if (value < node.value) node.left = removeHelper(node.left, value) - else if (value > node.value) node.right = removeHelper(node.right, value) + if (value < node.value) + node.left = removeHelper(node.left, value) + else if (value > node.value) + node.right = removeHelper(node.right, value) else { if (node.left == null || node.right == null) { - val child = if (node.left != null) node.left else node.right + val child = if (node.left != null) + node.left + else + node.right // 子节点数量 = 0 ,直接删除 node 并返回 - if (child == null) return null - else node = child + if (child == null) + return null + // 子节点数量 = 1 ,直接删除 node + else + node = child } else { // 子节点数量 = 2 ,则将中序遍历的下个节点删除,并用该节点替换当前节点 var temp = node.right diff --git a/docs/chapter_tree/binary_search_tree.md b/docs/chapter_tree/binary_search_tree.md index 1a644d8e5..759e29013 100755 --- a/docs/chapter_tree/binary_search_tree.md +++ b/docs/chapter_tree/binary_search_tree.md @@ -299,11 +299,14 @@ comments: true // 循环查找,越过叶节点后跳出 while (cur != null) { // 目标节点在 cur 的右子树中 - cur = if (cur.value < num) cur.right + cur = if (cur.value < num) + cur.right // 目标节点在 cur 的左子树中 - else if (cur.value > num) cur.left + else if (cur.value > num) + cur.left // 找到目标节点,跳出循环 - else break + else + break } // 返回目标节点 return cur @@ -748,17 +751,22 @@ comments: true // 循环查找,越过叶节点后跳出 while (cur != null) { // 找到重复节点,直接返回 - if (cur.value == num) return + if (cur.value == num) + return pre = cur // 插入位置在 cur 的右子树中 - cur = if (cur.value < num) cur.right + cur = if (cur.value < num) + cur.right // 插入位置在 cur 的左子树中 - else cur.left + else + cur.left } // 插入节点 val node = TreeNode(num) - if (pre?.value!! < num) pre.right = node - else pre.left = node + if (pre?.value!! < num) + pre.right = node + else + pre.left = node } ``` @@ -1482,29 +1490,39 @@ comments: true /* 删除节点 */ fun remove(num: Int) { // 若树为空,直接提前返回 - if (root == null) return + if (root == null) + return var cur = root var pre: TreeNode? = null // 循环查找,越过叶节点后跳出 while (cur != null) { // 找到待删除节点,跳出循环 - if (cur.value == num) break + if (cur.value == num) + break pre = cur // 待删除节点在 cur 的右子树中 - cur = if (cur.value < num) cur.right + cur = if (cur.value < num) + cur.right // 待删除节点在 cur 的左子树中 - else cur.left + else + cur.left } // 若无待删除节点,则直接返回 - if (cur == null) return + if (cur == null) + return // 子节点数量 = 0 or 1 if (cur.left == null || cur.right == null) { // 当子节点数量 = 0 / 1 时, child = null / 该子节点 - val child = if (cur.left != null) cur.left else cur.right + val child = if (cur.left != null) + cur.left + else + cur.right // 删除节点 cur if (cur != root) { - if (pre!!.left == cur) pre.left = child - else pre.right = child + if (pre!!.left == cur) + pre.left = child + else + pre.right = child } else { // 若删除节点为根节点,则重新指定根节点 root = child diff --git a/docs/chapter_tree/binary_tree_traversal.md b/docs/chapter_tree/binary_tree_traversal.md index d5bb75cfb..3688a6359 100755 --- a/docs/chapter_tree/binary_tree_traversal.md +++ b/docs/chapter_tree/binary_tree_traversal.md @@ -229,7 +229,7 @@ comments: true fn level_order(root: &Rc>) -> Vec { // 初始化队列,加入根节点 let mut que = VecDeque::new(); - que.push_back(Rc::clone(&root)); + que.push_back(root.clone()); // 初始化一个列表,用于保存遍历序列 let mut vec = Vec::new(); @@ -237,10 +237,10 @@ comments: true // 队列出队 vec.push(node.borrow().val); // 保存节点值 if let Some(left) = node.borrow().left.as_ref() { - que.push_back(Rc::clone(left)); // 左子节点入队 + que.push_back(left.clone()); // 左子节点入队 } if let Some(right) = node.borrow().right.as_ref() { - que.push_back(Rc::clone(right)); // 右子节点入队 + que.push_back(right.clone()); // 右子节点入队 }; } vec @@ -302,13 +302,14 @@ comments: true val queue = LinkedList() queue.add(root) // 初始化一个列表,用于保存遍历序列 - val list = ArrayList() - while (!queue.isEmpty()) { - val node = queue.poll() // 队列出队 - list.add(node?.value!!) // 保存节点值 - if (node.left != null) queue.offer(node.left) // 左子节点入队 - - if (node.right != null) queue.offer(node.right) // 右子节点入队 + val list = mutableListOf() + while (queue.isNotEmpty()) { + val node = queue.poll() // 队列出队 + list.add(node?.value!!) // 保存节点值 + if (node.left != null) + queue.offer(node.left) // 左子节点入队 + if (node.right != null) + queue.offer(node.right) // 右子节点入队 } return list } @@ -689,8 +690,8 @@ comments: true if let Some(node) = root { // 访问优先级:根节点 -> 左子树 -> 右子树 result.push(node.borrow().val); - result.append(&mut pre_order(node.borrow().left.as_ref())); - result.append(&mut pre_order(node.borrow().right.as_ref())); + result.extend(pre_order(node.borrow().left.as_ref())); + result.extend(pre_order(node.borrow().right.as_ref())); } result } @@ -701,9 +702,9 @@ comments: true if let Some(node) = root { // 访问优先级:左子树 -> 根节点 -> 右子树 - result.append(&mut in_order(node.borrow().left.as_ref())); + result.extend(in_order(node.borrow().left.as_ref())); result.push(node.borrow().val); - result.append(&mut in_order(node.borrow().right.as_ref())); + result.extend(in_order(node.borrow().right.as_ref())); } result } @@ -714,8 +715,8 @@ comments: true if let Some(node) = root { // 访问优先级:左子树 -> 右子树 -> 根节点 - result.append(&mut post_order(node.borrow().left.as_ref())); - result.append(&mut post_order(node.borrow().right.as_ref())); + result.extend(post_order(node.borrow().left.as_ref())); + result.extend(post_order(node.borrow().right.as_ref())); result.push(node.borrow().val); } result diff --git a/docs/index.html b/docs/index.html index 53aa17fcf..c0e3ced64 100644 --- a/docs/index.html +++ b/docs/index.html @@ -111,20 +111,22 @@
- Preview + Preview
- - - - - - - - - - - - + + + + + + + + + + + + + +

500 幅动画图解、12 种编程语言代码、3000 条社区问答,助你快速入门数据结构与算法

@@ -284,6 +286,14 @@
Zig, Rust +
Reviewer: Gonglja @@ -347,7 +357,7 @@

贡献者

-

本书在开源社区 140 多位贡献者的共同努力下不断完善,感谢他们付出的时间与精力!

+

本书在开源社区一百多位贡献者的共同努力下不断完善,感谢他们付出的时间与精力!

Contributors diff --git a/en/docs/chapter_graph/graph_operations.md b/en/docs/chapter_graph/graph_operations.md index fb887187c..b007477cb 100644 --- a/en/docs/chapter_graph/graph_operations.md +++ b/en/docs/chapter_graph/graph_operations.md @@ -1044,10 +1044,10 @@ Below is the implementation code for graphs represented using an adjacency matri ```kotlin title="graph_adjacency_matrix.kt" /* 基于邻接矩阵实现的无向图类 */ class GraphAdjMat(vertices: IntArray, edges: Array) { - val vertices: MutableList = ArrayList() // 顶点列表,元素代表“顶点值”,索引代表“顶点索引” - val adjMat: MutableList> = ArrayList() // 邻接矩阵,行列索引对应“顶点索引” + val vertices = mutableListOf() // 顶点列表,元素代表“顶点值”,索引代表“顶点索引” + val adjMat = mutableListOf>() // 邻接矩阵,行列索引对应“顶点索引” - /* 构造函数 */ + /* 构造方法 */ init { // 添加顶点 for (vertex in vertices) { @@ -1071,7 +1071,7 @@ Below is the implementation code for graphs represented using an adjacency matri // 向顶点列表中添加新顶点的值 vertices.add(value) // 在邻接矩阵中添加一行 - val newRow: MutableList = mutableListOf() + val newRow = mutableListOf() for (j in 0..= size()) throw IndexOutOfBoundsException() + if (index >= size()) + throw IndexOutOfBoundsException() // 在顶点列表中移除索引 index 的顶点 vertices.removeAt(index) // 在邻接矩阵中删除索引 index 的行 @@ -1099,7 +1100,8 @@ Below is the implementation code for graphs represented using an adjacency matri // 参数 i, j 对应 vertices 元素索引 fun addEdge(i: Int, j: Int) { // 索引越界与相等处理 - if (i < 0 || j < 0 || i >= size() || j >= size() || i == j) throw java.lang.IndexOutOfBoundsException() + if (i < 0 || j < 0 || i >= size() || j >= size() || i == j) + throw IndexOutOfBoundsException() // 在无向图中,邻接矩阵关于主对角线对称,即满足 (i, j) == (j, i) adjMat[i][j] = 1; adjMat[j][i] = 1; @@ -1109,7 +1111,8 @@ Below is the implementation code for graphs represented using an adjacency matri // 参数 i, j 对应 vertices 元素索引 fun removeEdge(i: Int, j: Int) { // 索引越界与相等处理 - if (i < 0 || j < 0 || i >= size() || j >= size() || i == j) throw java.lang.IndexOutOfBoundsException() + if (i < 0 || j < 0 || i >= size() || j >= size() || i == j) + throw IndexOutOfBoundsException() adjMat[i][j] = 0; adjMat[j][i] = 0; } @@ -2158,9 +2161,9 @@ Additionally, we use the `Vertex` class to represent vertices in the adjacency l /* 基于邻接表实现的无向图类 */ class GraphAdjList(edges: Array>) { // 邻接表,key:顶点,value:该顶点的所有邻接顶点 - val adjList: MutableMap> = HashMap() + val adjList = HashMap>() - /* 构造函数 */ + /* 构造方法 */ init { // 添加所有顶点和边 for (edge in edges) { @@ -2217,7 +2220,7 @@ Additionally, we use the `Vertex` class to represent vertices in the adjacency l fun print() { println("邻接表 =") for (pair in adjList.entries) { - val tmp = ArrayList() + val tmp = mutableListOf() for (vertex in pair.value) { tmp.add(vertex.value) } diff --git a/en/docs/chapter_graph/graph_traversal.md b/en/docs/chapter_graph/graph_traversal.md index 8f83afc6d..7842c00d9 100644 --- a/en/docs/chapter_graph/graph_traversal.md +++ b/en/docs/chapter_graph/graph_traversal.md @@ -418,24 +418,24 @@ To prevent revisiting vertices, we use a hash table `visited` to record which no ```kotlin title="graph_bfs.kt" /* 广度优先遍历 */ // 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 - fun graphBFS(graph: GraphAdjList, startVet: Vertex): List { + fun graphBFS(graph: GraphAdjList, startVet: Vertex): MutableList { // 顶点遍历序列 - val res: MutableList = ArrayList() + val res = mutableListOf() // 哈希表,用于记录已被访问过的顶点 - val visited: MutableSet = HashSet() + val visited = HashSet() visited.add(startVet) // 队列用于实现 BFS - val que: Queue = LinkedList() + val que = LinkedList() que.offer(startVet) // 以顶点 vet 为起点,循环直至访问完所有顶点 while (!que.isEmpty()) { val vet = que.poll() // 队首顶点出队 - res.add(vet) // 记录访问顶点 + res.add(vet) // 记录访问顶点 // 遍历该顶点的所有邻接顶点 for (adjVet in graph.adjList[vet]!!) { - if (visited.contains(adjVet)) continue // 跳过已被访问的顶点 - - que.offer(adjVet) // 只入队未访问的顶点 + if (visited.contains(adjVet)) + continue // 跳过已被访问的顶点 + que.offer(adjVet) // 只入队未访问的顶点 visited.add(adjVet) // 标记该顶点已被访问 } } @@ -866,11 +866,12 @@ This "go as far as possible and then return" algorithm paradigm is usually imple res: MutableList, vet: Vertex? ) { - res.add(vet) // 记录访问顶点 + res.add(vet) // 记录访问顶点 visited.add(vet) // 标记该顶点已被访问 // 遍历该顶点的所有邻接顶点 for (adjVet in graph.adjList[vet]!!) { - if (visited.contains(adjVet)) continue // 跳过已被访问的顶点 + if (visited.contains(adjVet)) + continue // 跳过已被访问的顶点 // 递归访问邻接顶点 dfs(graph, visited, res, adjVet) } @@ -878,14 +879,11 @@ This "go as far as possible and then return" algorithm paradigm is usually imple /* 深度优先遍历 */ // 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 - fun graphDFS( - graph: GraphAdjList, - startVet: Vertex? - ): List { + fun graphDFS(graph: GraphAdjList, startVet: Vertex?): MutableList { // 顶点遍历序列 - val res: MutableList = ArrayList() + val res = mutableListOf() // 哈希表,用于记录已被访问过的顶点 - val visited: MutableSet = HashSet() + val visited = HashSet() dfs(graph, visited, res, startVet) return res } diff --git a/en/docs/chapter_hashing/hash_collision.md b/en/docs/chapter_hashing/hash_collision.md index c8bf9e90b..ca9b83363 100644 --- a/en/docs/chapter_hashing/hash_collision.md +++ b/en/docs/chapter_hashing/hash_collision.md @@ -1114,17 +1114,14 @@ The code below provides a simple implementation of a separate chaining hash tabl // 遍历桶,若遇到指定 key ,则更新对应 val 并返回 for pair in bucket { if pair.key == key { - pair.val = val.clone(); + pair.val = val; return; } } let bucket = &mut self.buckets[index]; // 若无该 key ,则将键值对添加至尾部 - let pair = Pair { - key, - val: val.clone(), - }; + let pair = Pair { key, val }; bucket.push(pair); self.size += 1; } @@ -1328,7 +1325,7 @@ The code below provides a simple implementation of a separate chaining hash tabl capacity = 4 loadThres = 2.0 / 3.0 extendRatio = 2 - buckets = ArrayList(capacity) + buckets = mutableListOf() for (i in 0.. // 桶数组 - private val TOMBSTONE = Pair(-1, "-1") // 删除标记 + private var size: Int // 键值对数量 + private var capacity: Int // 哈希表容量 + private val loadThres: Double // 触发扩容的负载因子阈值 + private val extendRatio: Int // 扩容倍数 + private var buckets: Array // 桶数组 + private val TOMBSTONE: Pair // 删除标记 /* 构造方法 */ init { + size = 0 + capacity = 4 + loadThres = 2.0 / 3.0 + extendRatio = 2 buckets = arrayOfNulls(capacity) + TOMBSTONE = Pair(-1, "-1") } /* 哈希函数 */ diff --git a/en/docs/chapter_hashing/hash_map.md b/en/docs/chapter_hashing/hash_map.md index 6a0f5a02d..6bab96460 100755 --- a/en/docs/chapter_hashing/hash_map.md +++ b/en/docs/chapter_hashing/hash_map.md @@ -1548,15 +1548,9 @@ The following code implements a simple hash table. Here, we encapsulate `key` an /* 基于数组实现的哈希表 */ class ArrayHashMap { + // 初始化数组,包含 100 个桶 private val buckets = arrayOfNulls(100) - init { - // 初始化数组,包含 100 个桶 - for (i in 0..<100) { - buckets[i] = null - } - } - /* 哈希函数 */ fun hashFunc(key: Int): Int { val index = key % 100 @@ -1586,25 +1580,27 @@ The following code implements a simple hash table. Here, we encapsulate `key` an /* 获取所有键值对 */ fun pairSet(): MutableList { - val pairSet = ArrayList() + val pairSet = mutableListOf() for (pair in buckets) { - if (pair != null) pairSet.add(pair) + if (pair != null) + pairSet.add(pair) } return pairSet } /* 获取所有键 */ fun keySet(): MutableList { - val keySet = ArrayList() + val keySet = mutableListOf() for (pair in buckets) { - if (pair != null) keySet.add(pair.key) + if (pair != null) + keySet.add(pair.key) } return keySet } /* 获取所有值 */ fun valueSet(): MutableList { - val valueSet = ArrayList() + val valueSet = mutableListOf() for (pair in buckets) { pair?.let { valueSet.add(it.value) } } @@ -1616,22 +1612,16 @@ The following code implements a simple hash table. Here, we encapsulate `key` an for (kv in pairSet()) { val key = kv.key val value = kv.value - println("${key}->${value}") + println("${key} -> ${value}") } } } /* 基于数组实现的哈希表 */ class ArrayHashMap { + // 初始化数组,包含 100 个桶 private val buckets = arrayOfNulls(100) - init { - // 初始化数组,包含 100 个桶 - for (i in 0..<100) { - buckets[i] = null - } - } - /* 哈希函数 */ fun hashFunc(key: Int): Int { val index = key % 100 @@ -1661,25 +1651,27 @@ The following code implements a simple hash table. Here, we encapsulate `key` an /* 获取所有键值对 */ fun pairSet(): MutableList { - val pairSet = ArrayList() + val pairSet = mutableListOf() for (pair in buckets) { - if (pair != null) pairSet.add(pair) + if (pair != null) + pairSet.add(pair) } return pairSet } /* 获取所有键 */ fun keySet(): MutableList { - val keySet = ArrayList() + val keySet = mutableListOf() for (pair in buckets) { - if (pair != null) keySet.add(pair.key) + if (pair != null) + keySet.add(pair.key) } return keySet } /* 获取所有值 */ fun valueSet(): MutableList { - val valueSet = ArrayList() + val valueSet = mutableListOf() for (pair in buckets) { pair?.let { valueSet.add(it.value) } } @@ -1691,7 +1683,7 @@ The following code implements a simple hash table. Here, we encapsulate `key` an for (kv in pairSet()) { val key = kv.key val value = kv.value - println("${key}->${value}") + println("${key} -> ${value}") } } } diff --git a/en/docs/chapter_hashing/summary.md b/en/docs/chapter_hashing/summary.md index 780892101..e1053c86b 100644 --- a/en/docs/chapter_hashing/summary.md +++ b/en/docs/chapter_hashing/summary.md @@ -8,12 +8,12 @@ comments: true - Given an input `key`, a hash table can retrieve the corresponding `value` in $O(1)$ time, which is highly efficient. - Common hash table operations include querying, adding key-value pairs, deleting key-value pairs, and traversing the hash table. -- The hash function maps a `key` to an array index, allowing access to the corresponding bucket to retrieve the `value`. +- The hash function maps a `key` to an array index, allowing access to the corresponding bucket and retrieval of the `value`. - Two different keys may end up with the same array index after hashing, leading to erroneous query results. This phenomenon is known as hash collision. - The larger the capacity of the hash table, the lower the probability of hash collisions. Therefore, hash table resizing can mitigate hash collisions. Similar to array resizing, hash table resizing is costly. -- Load factor, defined as the ratio of the number of elements to the number of buckets in the hash table, reflects the severity of hash collisions and is often used as a trigger for resizing the hash table. +- The load factor, defined as the number of elements divided by the number of buckets, reflects the severity of hash collisions and is often used as a condition to trigger hash table resizing. - Chaining addresses hash collisions by converting each element into a linked list, storing all colliding elements in the same list. However, excessively long lists can reduce query efficiency, which can be improved by converting the lists into red-black trees. -- Open addressing handles hash collisions through multiple probes. Linear probing uses a fixed step size but cannot delete elements and is prone to clustering. Multiple hashing uses several hash functions for probing, making it less susceptible to clustering but increasing computational load. +- Open addressing handles hash collisions through multiple probes. Linear probing uses a fixed step size but it cannot delete elements and is prone to clustering. Multiple hashing uses several hash functions for probing which reduces clustering compared to linear probing but increases computational overhead. - Different programming languages adopt various hash table implementations. For example, Java's `HashMap` uses chaining, while Python's `dict` employs open addressing. - In hash tables, we desire hash algorithms with determinism, high efficiency, and uniform distribution. In cryptography, hash algorithms should also possess collision resistance and the avalanche effect. - Hash algorithms typically use large prime numbers as moduli to ensure uniform distribution of hash values and reduce hash collisions. @@ -30,11 +30,11 @@ The time complexity of a hash table can degrade to $O(n)$ when hash collisions a Under the hash function $f(x) = x$, each element corresponds to a unique bucket index, which is equivalent to an array. However, the input space is usually much larger than the output space (array length), so the last step of a hash function is often to take the modulo of the array length. In other words, the goal of a hash table is to map a larger state space to a smaller one while providing $O(1)$ query efficiency. -**Q**: Why can hash tables be more efficient than arrays, linked lists, or binary trees, even though they are implemented using these structures? +**Q**: Why can hash tables be more efficient than arrays, linked lists, or binary trees, even though hash tables are implemented using these structures? Firstly, hash tables have higher time efficiency but lower space efficiency. A significant portion of memory in hash tables remains unused. -Secondly, they are only more efficient in specific use cases. If a feature can be implemented with the same time complexity using an array or a linked list, it's usually faster than using a hash table. This is because the computation of the hash function incurs overhead, making the constant factor in the time complexity larger. +Secondly, hash tables are only more time-efficient in specific use cases. If a feature can be implemented with the same time complexity using an array or a linked list, it's usually faster than using a hash table. This is because the computation of the hash function incurs overhead, making the constant factor in the time complexity larger. Lastly, the time complexity of hash tables can degrade. For example, in chaining, we perform search operations in a linked list or red-black tree, which still risks degrading to $O(n)$ time. diff --git a/en/docs/chapter_heap/build_heap.md b/en/docs/chapter_heap/build_heap.md index 60edf2a5e..a0c3173f9 100644 --- a/en/docs/chapter_heap/build_heap.md +++ b/en/docs/chapter_heap/build_heap.md @@ -189,13 +189,14 @@ It's worth mentioning that **since leaf nodes have no children, they naturally f ```kotlin title="my_heap.kt" /* 大顶堆 */ - class MaxHeap(nums: List?) { + class MaxHeap(nums: MutableList?) { // 使用列表而非数组,这样无须考虑扩容问题 - // 将列表元素原封不动添加进堆 - private val maxHeap = ArrayList(nums!!) + private val maxHeap = mutableListOf() - /* 构造函数,根据输入列表建堆 */ + /* 构造方法,根据输入列表建堆 */ init { + // 将列表元素原封不动添加进堆 + maxHeap.addAll(nums!!) // 堆化除叶节点以外的其他所有节点 for (i in parent(size() - 1) downTo 0) { siftDown(i) diff --git a/en/docs/chapter_stack_and_queue/deque.md b/en/docs/chapter_stack_and_queue/deque.md index 1c5dc9dfa..e6da380a8 100644 --- a/en/docs/chapter_stack_and_queue/deque.md +++ b/en/docs/chapter_stack_and_queue/deque.md @@ -1845,7 +1845,7 @@ The implementation code is as follows: ```kotlin title="linkedlist_deque.kt" /* 双向链表节点 */ - class ListNode(var value: Int) { + class ListNode(var _val: Int) { // 节点值 var next: ListNode? = null // 后继节点引用 var prev: ListNode? = null // 前驱节点引用 @@ -1853,9 +1853,9 @@ The implementation code is as follows: /* 基于双向链表实现的双向队列 */ class LinkedListDeque { - private var front: ListNode? = null // 头节点 front ,尾节点 rear - private var rear: ListNode? = null - private var queSize = 0 // 双向队列的长度 + private var front: ListNode? = null // 头节点 front + private var rear: ListNode? = null // 尾节点 rear + private var queSize: Int = 0 // 双向队列的长度 /* 获取双向队列的长度 */ fun size(): Int { @@ -1902,12 +1902,12 @@ The implementation code is as follows: /* 出队操作 */ fun pop(isFront: Boolean): Int { - if (isEmpty()) throw IndexOutOfBoundsException() - + if (isEmpty()) + throw IndexOutOfBoundsException() val value: Int // 队首出队操作 if (isFront) { - value = front!!.value // 暂存头节点值 + value = front!!._val // 暂存头节点值 // 删除头节点 val fNext = front!!.next if (fNext != null) { @@ -1917,7 +1917,7 @@ The implementation code is as follows: front = fNext // 更新头节点 // 队尾出队操作 } else { - value = rear!!.value // 暂存尾节点值 + value = rear!!._val // 暂存尾节点值 // 删除尾节点 val rPrev = rear!!.prev if (rPrev != null) { @@ -1942,17 +1942,14 @@ The implementation code is as follows: /* 访问队首元素 */ fun peekFirst(): Int { - if (isEmpty()) { - throw IndexOutOfBoundsException() - - } - return front!!.value + if (isEmpty()) throw IndexOutOfBoundsException() + return front!!._val } /* 访问队尾元素 */ fun peekLast(): Int { if (isEmpty()) throw IndexOutOfBoundsException() - return rear!!.value + return rear!!._val } /* 返回数组用于打印 */ @@ -1960,7 +1957,7 @@ The implementation code is as follows: var node = front val res = IntArray(size()) for (i in res.indices) { - res[i] = node!!.value + res[i] = node!!._val node = node.next } return res @@ -1971,9 +1968,138 @@ The implementation code is as follows: === "Ruby" ```ruby title="linkedlist_deque.rb" - [class]{ListNode}-[func]{} + =begin + File: linkedlist_deque.rb + Created Time: 2024-04-06 + Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) + =end - [class]{LinkedListDeque}-[func]{} + ### 双向链表节点 + class ListNode + attr_accessor :val + attr_accessor :next # 后继节点引用 + attr_accessor :prev # 前躯节点引用 + + ### 构造方法 ### + def initialize(val) + @val = val + end + end + + ### 基于双向链表实现的双向队列 ### + class LinkedListDeque + ### 获取双向队列的长度 ### + attr_reader :size + + ### 构造方法 ### + def initialize + @front = nil # 头节点 front + @rear = nil # 尾节点 rear + @size = 0 # 双向队列的长度 + end + + ### 判断双向队列是否为空 ### + def is_empty? + size.zero? + end + + ### 入队操作 ### + def push(num, is_front) + node = ListNode.new(num) + # 若链表为空, 则令 front 和 rear 都指向 node + if is_empty? + @front = @rear = node + # 队首入队操作 + elsif is_front + # 将 node 添加至链表头部 + @front.prev = node + node.next = @front + @front = node # 更新头节点 + # 队尾入队操作 + else + # 将 node 添加至链表尾部 + @rear.next = node + node.prev = @rear + @rear = node # 更新尾节点 + end + @size += 1 # 更新队列长度 + end + + ### 队首入队 ### + def push_first(num) + push(num, true) + end + + ### 队尾入队 ### + def push_last(num) + push(num, false) + end + + ### 出队操作 ### + def pop(is_front) + raise IndexError, '双向队列为空' if is_empty? + + # 队首出队操作 + if is_front + val = @front.val # 暂存头节点值 + # 删除头节点 + fnext = @front.next + unless fnext.nil? + fnext.prev = nil + @front.next = nil + end + @front = fnext # 更新头节点 + # 队尾出队操作 + else + val = @rear.val # 暂存尾节点值 + # 删除尾节点 + rprev = @rear.prev + unless rprev.nil? + rprev.next = nil + @rear.prev = nil + end + @rear = rprev # 更新尾节点 + end + @size -= 1 # 更新队列长度 + + val + end + + ### 队首出队 ### + def pop_first + pop(true) + end + + ### 队首出队 ### + def pop_last + pop(false) + end + + ### 访问队首元素 ### + def peek_first + raise IndexError, '双向队列为空' if is_empty? + + @front.val + end + + ### 访问队尾元素 ### + def peek_last + raise IndexError, '双向队列为空' if is_empty? + + @rear.val + end + + ### 返回数组用于打印 ### + def to_array + node = @front + res = Array.new(size, 0) + for i in 0...size + res[i] = node.val + node = node.next + end + res + end + end ``` === "Zig" @@ -3356,11 +3482,11 @@ The implementation only needs to add methods for "front enqueue" and "rear deque === "Kotlin" ```kotlin title="array_deque.kt" - /* 基于环形数组实现的双向队列 */ + /* 构造方法 */ class ArrayDeque(capacity: Int) { - private var nums = IntArray(capacity) // 用于存储双向队列元素的数组 - private var front = 0 // 队首指针,指向队首元素 - private var queSize = 0 // 双向队列长度 + private var nums: IntArray = IntArray(capacity) // 用于存储双向队列元素的数组 + private var front: Int = 0 // 队首指针,指向队首元素 + private var queSize: Int = 0 // 双向队列长度 /* 获取双向队列的容量 */ fun capacity(): Int { @@ -3421,7 +3547,7 @@ The implementation only needs to add methods for "front enqueue" and "rear deque return num } - /* 访问队尾元素 */ + /* 队尾出队 */ fun popLast(): Int { val num = peekLast() queSize-- @@ -3461,7 +3587,109 @@ The implementation only needs to add methods for "front enqueue" and "rear deque === "Ruby" ```ruby title="array_deque.rb" - [class]{ArrayDeque}-[func]{} + ### 基于环形数组实现的双向队列 ### + class ArrayDeque + ### 获取双向队列的长度 ### + attr_reader :size + + ### 构造方法 ### + def initialize(capacity) + @nums = Array.new(capacity, 0) + @front = 0 + @size = 0 + end + + ### 获取双向队列的容量 ### + def capacity + @nums.length + end + + ### 判断双向队列是否为空 ### + def is_empty? + size.zero? + end + + ### 队首入队 ### + def push_first(num) + if size == capacity + puts '双向队列已满' + return + end + + # 队首指针向左移动一位 + # 通过取余操作实现 front 越过数组头部后回到尾部 + @front = index(@front - 1) + # 将 num 添加至队首 + @nums[@front] = num + @size += 1 + end + + ### 队尾入队 ### + def push_last(num) + if size == capacity + puts '双向队列已满' + return + end + + # 计算队尾指针,指向队尾索引 + 1 + rear = index(@front + size) + # 将 num 添加至队尾 + @nums[rear] = num + @size += 1 + end + + ### 队首出队 ### + def pop_first + num = peek_first + # 队首指针向后移动一位 + @front = index(@front + 1) + @size -= 1 + num + end + + ### 队尾出队 ### + def pop_last + num = peek_last + @size -= 1 + num + end + + ### 访问队首元素 ### + def peek_first + raise IndexError, '双向队列为空' if is_empty? + + @nums[@front] + end + + ### 访问队尾元素 ### + def peek_last + raise IndexError, '双向队列为空' if is_empty? + + # 计算尾元素索引 + last = index(@front + size - 1) + @nums[last] + end + + ### 返回数组用于打印 ### + def to_array + # 仅转换有效长度范围内的列表元素 + res = [] + for i in 0...size + res << @nums[index(@front + i)] + end + res + end + + private + + ### 计算环形数组索引 ### + def index(i) + # 通过取余操作实现数组首尾相连 + # 当 i 越过数组尾部后,回到头部 + # 当 i 越过数组头部后,回到尾部 + (i + capacity) % capacity + end + end ``` === "Zig" diff --git a/en/docs/chapter_stack_and_queue/queue.md b/en/docs/chapter_stack_and_queue/queue.md index 31e8f359d..719952379 100755 --- a/en/docs/chapter_stack_and_queue/queue.md +++ b/en/docs/chapter_stack_and_queue/queue.md @@ -1180,7 +1180,7 @@ Below is the code for implementing a queue using a linked list: /* 访问队首元素 */ fun peek(): Int { if (isEmpty()) throw IndexOutOfBoundsException() - return front!!.value + return front!!._val } /* 将链表转化为 Array 并返回 */ @@ -1188,7 +1188,7 @@ Below is the code for implementing a queue using a linked list: var node = front val res = IntArray(size()) for (i in res.indices) { - res[i] = node!!.value + res[i] = node!!._val node = node.next } return res @@ -1199,7 +1199,68 @@ Below is the code for implementing a queue using a linked list: === "Ruby" ```ruby title="linkedlist_queue.rb" - [class]{LinkedListQueue}-[func]{} + ### 基于链表头现的队列 ### + class LinkedListQueue + ### 获取队列的长度 ### + attr_reader :size + + ### 构造方法 ### + def initialize + @front = nil # 头节点 front + @rear = nil # 尾节点 rear + @size = 0 + end + + ### 判断队列是否为空 ### + def is_empty? + @front.nil? + end + + ### 入队 ### + def push(num) + # 在尾节点后添加 num + node = ListNode.new(num) + + # 如果队列为空,则令头,尾节点都指向该节点 + if @front.nil? + @front = node + @rear = node + # 如果队列不为空,则令该节点添加到尾节点后 + else + @rear.next = node + @rear = node + end + + @size += 1 + end + + ### 出队 ### + def pop + num = peek + # 删除头节点 + @front = @front.next + @size -= 1 + num + end + + ### 访问队首元素 ### + def peek + raise IndexError, '队列为空' if is_empty? + + @front.val + end + + ### 将链表为 Array 并返回 ### + def to_array + queue = [] + temp = @front + while temp + queue << temp.val + temp = temp.next + end + queue + end + end ``` === "Zig" @@ -2118,9 +2179,9 @@ In a circular array, `front` or `rear` needs to loop back to the start of the ar ```kotlin title="array_queue.kt" /* 基于环形数组实现的队列 */ class ArrayQueue(capacity: Int) { - private val nums = IntArray(capacity) // 用于存储队列元素的数组 - private var front = 0 // 队首指针,指向队首元素 - private var queSize = 0 // 队列长度 + private val nums: IntArray = IntArray(capacity) // 用于存储队列元素的数组 + private var front: Int = 0 // 队首指针,指向队首元素 + private var queSize: Int = 0 // 队列长度 /* 获取队列的容量 */ fun capacity(): Int { @@ -2185,7 +2246,69 @@ In a circular array, `front` or `rear` needs to loop back to the start of the ar === "Ruby" ```ruby title="array_queue.rb" - [class]{ArrayQueue}-[func]{} + ### 基于环形数组实现的队列 ### + class ArrayQueue + ### 获取队列的长度 ### + attr_reader :size + + ### 构造方法 ### + def initialize(size) + @nums = Array.new(size, 0) # 用于存储队列元素的数组 + @front = 0 # 队首指针,指向队首元素 + @size = 0 # 队列长度 + end + + ### 获取队列的容量 ### + def capacity + @nums.length + end + + ### 判断队列是否为空 ### + def is_empty? + size.zero? + end + + ### 入队 ### + def push(num) + raise IndexError, '队列已满' if size == capacity + + # 计算队尾指针,指向队尾索引 + 1 + # 通过取余操作实现 rear 越过数组尾部后回到头部 + rear = (@front + size) % capacity + # 将 num 添加至队尾 + @nums[rear] = num + @size += 1 + end + + ### 出队 ### + def pop + num = peek + # 队首指针向后移动一位,若越过尾部,则返回到数组头部 + @front = (@front + 1) % capacity + @size -= 1 + num + end + + ### 访问队首元素 ### + def peek + raise IndexError, '队列为空' if is_empty? + + @nums[@front] + end + + ### 返回列表用于打印 ### + def to_array + res = Array.new(size, 0) + j = @front + + for i in 0...size + res[i] = @nums[j % capacity] + j += 1 + end + + res + end + end ``` === "Zig" diff --git a/en/docs/chapter_stack_and_queue/stack.md b/en/docs/chapter_stack_and_queue/stack.md index 161015a7a..06555e560 100755 --- a/en/docs/chapter_stack_and_queue/stack.md +++ b/en/docs/chapter_stack_and_queue/stack.md @@ -1052,7 +1052,7 @@ Below is an example code for implementing a stack based on a linked list: /* 访问栈顶元素 */ fun peek(): Int? { if (isEmpty()) throw IndexOutOfBoundsException() - return stackPeek?.value + return stackPeek?._val } /* 将 List 转化为 Array 并返回 */ @@ -1060,7 +1060,7 @@ Below is an example code for implementing a stack based on a linked list: var node = stackPeek val res = IntArray(size()) for (i in res.size - 1 downTo 0) { - res[i] = node?.value!! + res[i] = node?._val!! node = node.next } return res @@ -1071,7 +1071,54 @@ Below is an example code for implementing a stack based on a linked list: === "Ruby" ```ruby title="linkedlist_stack.rb" - [class]{LinkedListStack}-[func]{} + ### 基于链表实现的栈 ### + class LinkedListStack + attr_reader :size + + ### 构造方法 ### + def initialize + @size = 0 + end + + ### 判断栈是否为空 ### + def is_empty? + @peek.nil? + end + + ### 入栈 ### + def push(val) + node = ListNode.new(val) + node.next = @peek + @peek = node + @size += 1 + end + + ### 出栈 ### + def pop + num = peek + @peek = @peek.next + @size -= 1 + num + end + + ### 访问栈顶元素 ### + def peek + raise IndexError, '栈为空' if is_empty? + + @peek.val + end + + ### 将链表转化为 Array 并反回 ### + def to_array + arr = [] + node = @peek + while node + arr << node.val + node = node.next + end + arr.reverse + end + end ``` === "Zig" @@ -1712,7 +1759,7 @@ Since the elements to be pushed onto the stack may continuously increase, we can /* 基于数组实现的栈 */ class ArrayStack { // 初始化列表(动态数组) - private val stack = ArrayList() + private val stack = mutableListOf() /* 获取栈的长度 */ fun size(): Int { @@ -1743,7 +1790,7 @@ Since the elements to be pushed onto the stack may continuously increase, we can /* 将 List 转化为 Array 并返回 */ fun toArray(): Array { - return stack.toArray() + return stack.toTypedArray() } } ``` @@ -1751,7 +1798,47 @@ Since the elements to be pushed onto the stack may continuously increase, we can === "Ruby" ```ruby title="array_stack.rb" - [class]{ArrayStack}-[func]{} + ### 基于数组实现的栈 ### + class ArrayStack + ### 构造方法 ### + def initialize + @stack = [] + end + + ### 获取栈的长度 ### + def size + @stack.length + end + + ### 判断栈是否为空 ### + def is_empty? + @stack.empty? + end + + ### 入栈 ### + def push(item) + @stack << item + end + + ### 出栈 ### + def pop + raise IndexError, '栈为空' if is_empty? + + @stack.pop + end + + ### 访问栈顶元素 ### + def peek + raise IndexError, '栈为空' if is_empty? + + @stack.last + end + + ### 返回列表用于打印 ### + def to_array + @stack + end + end ``` === "Zig" diff --git a/en/docs/chapter_tree/array_representation_of_tree.md b/en/docs/chapter_tree/array_representation_of_tree.md index b34dfbe9f..10770f1ec 100644 --- a/en/docs/chapter_tree/array_representation_of_tree.md +++ b/en/docs/chapter_tree/array_representation_of_tree.md @@ -1172,8 +1172,8 @@ The following code implements a binary tree based on array representation, inclu === "Kotlin" ```kotlin title="array_binary_tree.kt" - /* 数组表示下的二叉树类 */ - class ArrayBinaryTree(val tree: List) { + /* 构造方法 */ + class ArrayBinaryTree(val tree: MutableList) { /* 列表容量 */ fun size(): Int { return tree.size @@ -1202,11 +1202,12 @@ The following code implements a binary tree based on array representation, inclu } /* 层序遍历 */ - fun levelOrder(): List { - val res = ArrayList() + fun levelOrder(): MutableList { + val res = mutableListOf() // 直接遍历数组 for (i in 0..) { // 若为空位,则返回 - if (value(i) == null) return + if (value(i) == null) + return // 前序遍历 - if ("pre" == order) res.add(value(i)) + if ("pre" == order) + res.add(value(i)) dfs(left(i), order, res) // 中序遍历 - if ("in" == order) res.add(value(i)) + if ("in" == order) + res.add(value(i)) dfs(right(i), order, res) // 后序遍历 - if ("post" == order) res.add(value(i)) + if ("post" == order) + res.add(value(i)) } /* 前序遍历 */ - fun preOrder(): List { - val res = ArrayList() + fun preOrder(): MutableList { + val res = mutableListOf() dfs(0, "pre", res) return res } /* 中序遍历 */ - fun inOrder(): List { - val res = ArrayList() + fun inOrder(): MutableList { + val res = mutableListOf() dfs(0, "in", res) return res } /* 后序遍历 */ - fun postOrder(): List { - val res = ArrayList() + fun postOrder(): MutableList { + val res = mutableListOf() dfs(0, "post", res) return res } diff --git a/en/docs/chapter_tree/avl_tree.md b/en/docs/chapter_tree/avl_tree.md index f5dc32480..5644a8daa 100644 --- a/en/docs/chapter_tree/avl_tree.md +++ b/en/docs/chapter_tree/avl_tree.md @@ -448,7 +448,7 @@ The "node height" refers to the distance from that node to its farthest leaf nod /* 更新节点高度 */ fun updateHeight(node: TreeNode?) { // 节点高度等于最高子树高度 + 1 - node?.height = (max(height(node?.left).toDouble(), height(node?.right).toDouble()) + 1).toInt() + node?.height = max(height(node?.left), height(node?.right)) + 1 } ``` @@ -2022,10 +2022,12 @@ The node insertion operation in AVL trees is similar to that in binary search tr return TreeNode(value) var node = n /* 1. 查找插入位置并插入节点 */ - if (value < node.value) node.left = insertHelper(node.left, value) - else if (value > node.value) node.right = insertHelper(node.right, value) - else return node // 重复节点不插入,直接返回 - + if (value < node.value) + node.left = insertHelper(node.left, value) + else if (value > node.value) + node.right = insertHelper(node.right, value) + else + return node // 重复节点不插入,直接返回 updateHeight(node) // 更新节点高度 /* 2. 执行旋转操作,使该子树重新恢复平衡 */ node = rotate(node) @@ -2601,14 +2603,22 @@ Similarly, based on the method of removing nodes in binary search trees, rotatio fun removeHelper(n: TreeNode?, value: Int): TreeNode? { var node = n ?: return null /* 1. 查找节点并删除 */ - if (value < node.value) node.left = removeHelper(node.left, value) - else if (value > node.value) node.right = removeHelper(node.right, value) + if (value < node.value) + node.left = removeHelper(node.left, value) + else if (value > node.value) + node.right = removeHelper(node.right, value) else { if (node.left == null || node.right == null) { - val child = if (node.left != null) node.left else node.right + val child = if (node.left != null) + node.left + else + node.right // 子节点数量 = 0 ,直接删除 node 并返回 - if (child == null) return null - else node = child + if (child == null) + return null + // 子节点数量 = 1 ,直接删除 node + else + node = child } else { // 子节点数量 = 2 ,则将中序遍历的下个节点删除,并用该节点替换当前节点 var temp = node.right diff --git a/en/docs/chapter_tree/binary_search_tree.md b/en/docs/chapter_tree/binary_search_tree.md index 6032cec08..11bb5d104 100755 --- a/en/docs/chapter_tree/binary_search_tree.md +++ b/en/docs/chapter_tree/binary_search_tree.md @@ -299,11 +299,14 @@ The search operation in a binary search tree works on the same principle as the // 循环查找,越过叶节点后跳出 while (cur != null) { // 目标节点在 cur 的右子树中 - cur = if (cur.value < num) cur.right + cur = if (cur.value < num) + cur.right // 目标节点在 cur 的左子树中 - else if (cur.value > num) cur.left + else if (cur.value > num) + cur.left // 找到目标节点,跳出循环 - else break + else + break } // 返回目标节点 return cur @@ -748,17 +751,22 @@ In the code implementation, note the following two points. // 循环查找,越过叶节点后跳出 while (cur != null) { // 找到重复节点,直接返回 - if (cur.value == num) return + if (cur.value == num) + return pre = cur // 插入位置在 cur 的右子树中 - cur = if (cur.value < num) cur.right + cur = if (cur.value < num) + cur.right // 插入位置在 cur 的左子树中 - else cur.left + else + cur.left } // 插入节点 val node = TreeNode(num) - if (pre?.value!! < num) pre.right = node - else pre.left = node + if (pre?.value!! < num) + pre.right = node + else + pre.left = node } ``` @@ -1482,29 +1490,39 @@ The operation of removing a node also uses $O(\log n)$ time, where finding the n /* 删除节点 */ fun remove(num: Int) { // 若树为空,直接提前返回 - if (root == null) return + if (root == null) + return var cur = root var pre: TreeNode? = null // 循环查找,越过叶节点后跳出 while (cur != null) { // 找到待删除节点,跳出循环 - if (cur.value == num) break + if (cur.value == num) + break pre = cur // 待删除节点在 cur 的右子树中 - cur = if (cur.value < num) cur.right + cur = if (cur.value < num) + cur.right // 待删除节点在 cur 的左子树中 - else cur.left + else + cur.left } // 若无待删除节点,则直接返回 - if (cur == null) return + if (cur == null) + return // 子节点数量 = 0 or 1 if (cur.left == null || cur.right == null) { // 当子节点数量 = 0 / 1 时, child = null / 该子节点 - val child = if (cur.left != null) cur.left else cur.right + val child = if (cur.left != null) + cur.left + else + cur.right // 删除节点 cur if (cur != root) { - if (pre!!.left == cur) pre.left = child - else pre.right = child + if (pre!!.left == cur) + pre.left = child + else + pre.right = child } else { // 若删除节点为根节点,则重新指定根节点 root = child diff --git a/en/docs/chapter_tree/binary_tree_traversal.md b/en/docs/chapter_tree/binary_tree_traversal.md index 011002271..d08e46673 100755 --- a/en/docs/chapter_tree/binary_tree_traversal.md +++ b/en/docs/chapter_tree/binary_tree_traversal.md @@ -229,7 +229,7 @@ Breadth-first traversal is usually implemented with the help of a "queue". The q fn level_order(root: &Rc>) -> Vec { // 初始化队列,加入根节点 let mut que = VecDeque::new(); - que.push_back(Rc::clone(&root)); + que.push_back(root.clone()); // 初始化一个列表,用于保存遍历序列 let mut vec = Vec::new(); @@ -237,10 +237,10 @@ Breadth-first traversal is usually implemented with the help of a "queue". The q // 队列出队 vec.push(node.borrow().val); // 保存节点值 if let Some(left) = node.borrow().left.as_ref() { - que.push_back(Rc::clone(left)); // 左子节点入队 + que.push_back(left.clone()); // 左子节点入队 } if let Some(right) = node.borrow().right.as_ref() { - que.push_back(Rc::clone(right)); // 右子节点入队 + que.push_back(right.clone()); // 右子节点入队 }; } vec @@ -302,13 +302,14 @@ Breadth-first traversal is usually implemented with the help of a "queue". The q val queue = LinkedList() queue.add(root) // 初始化一个列表,用于保存遍历序列 - val list = ArrayList() - while (!queue.isEmpty()) { - val node = queue.poll() // 队列出队 - list.add(node?.value!!) // 保存节点值 - if (node.left != null) queue.offer(node.left) // 左子节点入队 - - if (node.right != null) queue.offer(node.right) // 右子节点入队 + val list = mutableListOf() + while (queue.isNotEmpty()) { + val node = queue.poll() // 队列出队 + list.add(node?.value!!) // 保存节点值 + if (node.left != null) + queue.offer(node.left) // 左子节点入队 + if (node.right != null) + queue.offer(node.right) // 右子节点入队 } return list } @@ -689,8 +690,8 @@ Depth-first search is usually implemented based on recursion: if let Some(node) = root { // 访问优先级:根节点 -> 左子树 -> 右子树 result.push(node.borrow().val); - result.append(&mut pre_order(node.borrow().left.as_ref())); - result.append(&mut pre_order(node.borrow().right.as_ref())); + result.extend(pre_order(node.borrow().left.as_ref())); + result.extend(pre_order(node.borrow().right.as_ref())); } result } @@ -701,9 +702,9 @@ Depth-first search is usually implemented based on recursion: if let Some(node) = root { // 访问优先级:左子树 -> 根节点 -> 右子树 - result.append(&mut in_order(node.borrow().left.as_ref())); + result.extend(in_order(node.borrow().left.as_ref())); result.push(node.borrow().val); - result.append(&mut in_order(node.borrow().right.as_ref())); + result.extend(in_order(node.borrow().right.as_ref())); } result } @@ -714,8 +715,8 @@ Depth-first search is usually implemented based on recursion: if let Some(node) = root { // 访问优先级:左子树 -> 右子树 -> 根节点 - result.append(&mut post_order(node.borrow().left.as_ref())); - result.append(&mut post_order(node.borrow().right.as_ref())); + result.extend(post_order(node.borrow().left.as_ref())); + result.extend(post_order(node.borrow().right.as_ref())); result.push(node.borrow().val); } result diff --git a/overrides/stylesheets/extra.css b/overrides/stylesheets/extra.css index 84da9d4e2..0ea7f10ce 100644 --- a/overrides/stylesheets/extra.css +++ b/overrides/stylesheets/extra.css @@ -519,13 +519,6 @@ a:hover .text-button span { .text-button { margin: 0.7em auto; } - - .profile-div { - max-width: 30em; - } - .profile-cell { - flex-basis: 25%; - } } .video-container { diff --git a/zh-Hant/docs/index.html b/zh-Hant/docs/index.html index c4d11adc5..b28d5700a 100644 --- a/zh-Hant/docs/index.html +++ b/zh-Hant/docs/index.html @@ -111,20 +111,22 @@
- Preview + Preview
- - - - - - - - - - - - + + + + + + + + + + + + + +

500 幅動畫圖解、12 種程式語言程式碼、3000 條社群問答,助你快速入門資料結構與演算法

@@ -347,7 +349,7 @@