This commit is contained in:
krahets
2024-04-09 20:43:40 +08:00
parent d8caf02e9e
commit a6adc8e20a
48 changed files with 1599 additions and 571 deletions

View File

@ -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$ 为例,其可推广至任意位数的二进制数。

View File

@ -449,7 +449,13 @@ comments: true
```kotlin title="build_tree.kt"
/* 构建二叉树:分治 */
fun dfs(preorder: IntArray, inorderMap: Map<Int?, Int?>, i: Int, l: Int, r: Int): TreeNode? {
fun dfs(
preorder: IntArray,
inorderMap: Map<Int?, Int?>,
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<Int?, Int?> = HashMap()
val inorderMap = HashMap<Int?, Int?>()
for (i in inorder.indices) {
inorderMap[inorder[i]] = i
}

View File

@ -479,7 +479,7 @@ comments: true
/* 移动一个圆盘 */
fun move(src: MutableList<Int>, tar: MutableList<Int>) {
// 从 src 顶部拿出一个圆盘
val pan: Int = src.removeAt(src.size - 1)
val pan = src.removeAt(src.size - 1)
// 将圆盘放入 tar 顶部
tar.add(pan)
}

View File

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

View File

@ -349,11 +349,7 @@ $$
```kotlin title="min_path_sum.kt"
/* 最小路径和:暴力搜索 */
fun minPathSumDFS(
grid: Array<Array<Int>>,
i: Int,
j: Int
): Int {
fun minPathSumDFS(grid: Array<IntArray>, 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<Array<Int>>,
mem: Array<Array<Int>>,
grid: Array<IntArray>,
mem: Array<IntArray>,
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<Array<Int>>): Int {
fun minPathSumDP(grid: Array<IntArray>): Int {
val n = grid.size
val m = grid[0].size
// 初始化 dp 表
@ -1115,8 +1111,7 @@ $$
// 状态转移:其余行和列
for (i in 1..<n) {
for (j in 1..<m) {
dp[i][j] =
(min(dp[i][j - 1].toDouble(), dp[i - 1][j].toDouble()) + grid[i][j]).toInt()
dp[i][j] = min(dp[i][j - 1], dp[i - 1][j]) + grid[i][j]
}
}
return dp[n - 1][m - 1]
@ -1500,7 +1495,7 @@ $$
```kotlin title="min_path_sum.kt"
/* 最小路径和:空间优化后的动态规划 */
fun minPathSumDPComp(grid: Array<Array<Int>>): Int {
fun minPathSumDPComp(grid: Array<IntArray>): 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..<m) {
dp[j] = (min(dp[j - 1].toDouble(), dp[j].toDouble()) + grid[i][j]).toInt()
dp[j] = min(dp[j - 1], dp[j]) + grid[i][j]
}
}
return dp[m - 1]

View File

@ -443,11 +443,7 @@ $$
dp[i][j] = dp[i - 1][j - 1]
} else {
// 最少编辑步数 = 插入、删除、替换这三种操作的最少编辑步数 + 1
dp[i][j] =
(min(
min(dp[i][j - 1].toDouble(), dp[i - 1][j].toDouble()),
dp[i - 1][j - 1].toDouble()
) + 1).toInt()
dp[i][j] = min(min(dp[i][j - 1], dp[i - 1][j]), dp[i - 1][j - 1]) + 1
}
}
}
@ -941,7 +937,7 @@ $$
dp[j] = leftup
} else {
// 最少编辑步数 = 插入、删除、替换这三种操作的最少编辑步数 + 1
dp[j] = (min(min(dp[j - 1].toDouble(), dp[j].toDouble()), leftup.toDouble()) + 1).toInt()
dp[j] = min(min(dp[j - 1], dp[j]), leftup) + 1
}
leftup = temp // 更新为下一轮的 dp[i-1, j-1]
}

View File

@ -361,13 +361,14 @@ comments: true
```kotlin title="climbing_stairs_backtrack.kt"
/* 回溯 */
fun backtrack(
choices: List<Int>,
choices: MutableList<Int>,
state: Int,
n: Int,
res: MutableList<Int>
) {
// 当爬到第 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<Int>()
val res = mutableListOf<Int>()
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
}

View File

@ -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])
}
}
}

View File

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

View File

@ -1044,10 +1044,10 @@ comments: true
```kotlin title="graph_adjacency_matrix.kt"
/* 基于邻接矩阵实现的无向图类 */
class GraphAdjMat(vertices: IntArray, edges: Array<IntArray>) {
val vertices: MutableList<Int> = ArrayList() // 顶点列表,元素代表“顶点值”,索引代表“顶点索引”
val adjMat: MutableList<MutableList<Int>> = ArrayList() // 邻接矩阵,行列索引对应“顶点索引”
val vertices = mutableListOf<Int>() // 顶点列表,元素代表“顶点值”,索引代表“顶点索引”
val adjMat = mutableListOf<MutableList<Int>>() // 邻接矩阵,行列索引对应“顶点索引”
/* 构造函数 */
/* 构造方法 */
init {
// 添加顶点
for (vertex in vertices) {
@ -1071,7 +1071,7 @@ comments: true
// 向顶点列表中添加新顶点的值
vertices.add(value)
// 在邻接矩阵中添加一行
val newRow: MutableList<Int> = mutableListOf()
val newRow = mutableListOf<Int>()
for (j in 0..<n) {
newRow.add(0)
}
@ -1084,7 +1084,8 @@ comments: true
/* 删除顶点 */
fun removeVertex(index: Int) {
if (index >= 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<Array<Vertex?>>) {
// 邻接表key顶点value该顶点的所有邻接顶点
val adjList: MutableMap<Vertex, MutableList<Vertex>> = HashMap()
val adjList = HashMap<Vertex, MutableList<Vertex>>()
/* 构造函数 */
/* 构造方法 */
init {
// 添加所有顶点和边
for (edge in edges) {
@ -2217,7 +2220,7 @@ comments: true
fun print() {
println("邻接表 =")
for (pair in adjList.entries) {
val tmp = ArrayList<Int>()
val tmp = mutableListOf<Int>()
for (vertex in pair.value) {
tmp.add(vertex.value)
}

View File

@ -418,24 +418,24 @@ BFS 通常借助队列来实现,代码如下所示。队列具有“先入先
```kotlin title="graph_bfs.kt"
/* 广度优先遍历 */
// 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点
fun graphBFS(graph: GraphAdjList, startVet: Vertex): List<Vertex> {
fun graphBFS(graph: GraphAdjList, startVet: Vertex): MutableList<Vertex?> {
// 顶点遍历序列
val res: MutableList<Vertex> = ArrayList()
val res = mutableListOf<Vertex?>()
// 哈希表,用于记录已被访问过的顶点
val visited: MutableSet<Vertex> = HashSet()
val visited = HashSet<Vertex>()
visited.add(startVet)
// 队列用于实现 BFS
val que: Queue<Vertex> = LinkedList()
val que = LinkedList<Vertex>()
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<Vertex?>,
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<Vertex?> {
fun graphDFS(graph: GraphAdjList, startVet: Vertex?): MutableList<Vertex?> {
// 顶点遍历序列
val res: MutableList<Vertex?> = ArrayList()
val res = mutableListOf<Vertex?>()
// 哈希表,用于记录已被访问过的顶点
val visited: MutableSet<Vertex?> = HashSet()
val visited = HashSet<Vertex?>()
dfs(graph, visited, res, startVet)
return res
}

View File

@ -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<Item>(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<Item>(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 {
// 若剩余容量不足,则将当前物品的一部分装进背包

View File

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

View File

@ -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()
}
```

View File

@ -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..<capacity) {
buckets.add(mutableListOf())
}
@ -2960,16 +2957,21 @@ comments: true
```kotlin title="hash_map_open_addressing.kt"
/* 开放寻址哈希表 */
class HashMapOpenAddressing {
private var size: Int = 0 // 键值对数量
private var capacity = 4 // 哈希表容量
private val loadThres: Double = 2.0 / 3.0 // 触发扩容的负载因子阈值
private val extendRatio = 2 // 扩容倍数
private var buckets: Array<Pair?> // 桶数组
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<Pair?> // 桶数组
private val TOMBSTONE: Pair // 删除标记
/* 构造方法 */
init {
size = 0
capacity = 4
loadThres = 2.0 / 3.0
extendRatio = 2
buckets = arrayOfNulls(capacity)
TOMBSTONE = Pair(-1, "-1")
}
/* 哈希函数 */

View File

@ -1589,15 +1589,9 @@ index = hash(key) % capacity
/* 基于数组实现的哈希表 */
class ArrayHashMap {
// 初始化数组,包含 100 个桶
private val buckets = arrayOfNulls<Pair>(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<Pair> {
val pairSet = ArrayList<Pair>()
val pairSet = mutableListOf<Pair>()
for (pair in buckets) {
if (pair != null) pairSet.add(pair)
if (pair != null)
pairSet.add(pair)
}
return pairSet
}
/* 获取所有键 */
fun keySet(): MutableList<Int> {
val keySet = ArrayList<Int>()
val keySet = mutableListOf<Int>()
for (pair in buckets) {
if (pair != null) keySet.add(pair.key)
if (pair != null)
keySet.add(pair.key)
}
return keySet
}
/* 获取所有值 */
fun valueSet(): MutableList<String> {
val valueSet = ArrayList<String>()
val valueSet = mutableListOf<String>()
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<Pair>(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<Pair> {
val pairSet = ArrayList<Pair>()
val pairSet = mutableListOf<Pair>()
for (pair in buckets) {
if (pair != null) pairSet.add(pair)
if (pair != null)
pairSet.add(pair)
}
return pairSet
}
/* 获取所有键 */
fun keySet(): MutableList<Int> {
val keySet = ArrayList<Int>()
val keySet = mutableListOf<Int>()
for (pair in buckets) {
if (pair != null) keySet.add(pair.key)
if (pair != null)
keySet.add(pair.key)
}
return keySet
}
/* 获取所有值 */
fun valueSet(): MutableList<String> {
val valueSet = ArrayList<String>()
val valueSet = mutableListOf<String>()
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}")
}
}
}

View File

@ -189,13 +189,14 @@ comments: true
```kotlin title="my_heap.kt"
/* 大顶堆 */
class MaxHeap(nums: List<Int>?) {
class MaxHeap(nums: MutableList<Int>?) {
// 使用列表而非数组,这样无须考虑扩容问题
// 将列表元素原封不动添加进堆
private val maxHeap = ArrayList(nums!!)
private val maxHeap = mutableListOf<Int>()
/* 构造函数,根据输入列表建堆 */
/* 构造方法,根据输入列表建堆 */
init {
// 将列表元素原封不动添加进堆
maxHeap.addAll(nums!!)
// 堆化除叶节点以外的其他所有节点
for (i in parent(size() - 1) downTo 0) {
siftDown(i)

View File

@ -264,7 +264,7 @@ comments: true
for (j in 0..<i) {
if (nums[j] > 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] }
}
}
}

View File

@ -395,9 +395,9 @@ comments: true
fun bucketSort(nums: FloatArray) {
// 初始化 k = n/2 个桶,预期向每个桶分配 2 个元素
val k = nums.size / 2
val buckets = ArrayList<ArrayList<Float>>()
val buckets = mutableListOf<MutableList<Float>>()
for (i in 0..<k) {
buckets.add(ArrayList())
buckets.add(mutableListOf())
}
// 1. 将数组元素分配到各个桶中
for (num in nums) {

View File

@ -324,7 +324,7 @@ comments: true
// 1. 统计数组最大元素 m
var m = 0
for (num in nums) {
m = max(m.toDouble(), num.toDouble()).toInt()
m = max(m, num)
}
// 2. 统计各数字的出现次数
// counter[num] 代表 num 的出现次数
@ -822,7 +822,7 @@ $$
// 1. 统计数组最大元素 m
var m = 0
for (num in nums) {
m = max(m.toDouble(), num.toDouble()).toInt()
m = max(m, num)
}
// 2. 统计各数字的出现次数
// counter[num] 代表 num 的出现次数

View File

@ -545,10 +545,13 @@ comments: true
val l = 2 * i + 1
val r = 2 * i + 2
var ma = i
if (l < n && nums[l] > 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] }
// 循环向下堆化

View File

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

View File

@ -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) {

View File

@ -636,7 +636,7 @@ $$
// 统计 0~9 各数字的出现次数
for (i in 0..<n) {
val d = digit(nums[i], exp) // 获取 nums[i] 第 k 位,记为 d
counter[d]++ // 统计数字 d 的出现次数
counter[d]++ // 统计数字 d 的出现次数
}
// 求前缀和,将“出现个数”转换为“数组索引”
for (i in 1..9) {
@ -647,11 +647,12 @@ $$
for (i in n - 1 downTo 0) {
val d = digit(nums[i], exp)
val j = counter[d] - 1 // 获取 d 在数组中的索引 j
res[j] = nums[i] // 将当前元素填入索引 j
counter[d]-- // 将 d 的数量减 1
res[j] = nums[i] // 将当前元素填入索引 j
counter[d]-- // 将 d 的数量减 1
}
// 使用结果覆盖原数组 nums
for (i in 0..<n) nums[i] = res[i]
for (i in 0..<n)
nums[i] = res[i]
}
/* 基数排序 */

View File

@ -292,7 +292,8 @@ comments: true
var k = i
// 内循环:找到未排序区间内的最小元素
for (j in i + 1..<n) {
if (nums[j] < nums[k]) k = j // 记录最小元素的索引
if (nums[j] < nums[k])
k = j // 记录最小元素的索引
}
// 将该最小元素与未排序区间的首个元素交换
nums[i] = nums[k].also { nums[k] = nums[i] }

View File

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

View File

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

View File

@ -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<Int>()
private val stack = mutableListOf<Int>()
/* 获取栈的长度 */
fun size(): Int {
@ -1769,7 +1837,7 @@ comments: true
/* 将 List 转化为 Array 并返回 */
fun toArray(): Array<Any> {
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"

View File

@ -1172,8 +1172,8 @@ comments: true
=== "Kotlin"
```kotlin title="array_binary_tree.kt"
/* 数组表示下的二叉树类 */
class ArrayBinaryTree(val tree: List<Int?>) {
/* 构造方法 */
class ArrayBinaryTree(val tree: MutableList<Int?>) {
/* 列表容量 */
fun size(): Int {
return tree.size
@ -1202,11 +1202,12 @@ comments: true
}
/* 层序遍历 */
fun levelOrder(): List<Int?> {
val res = ArrayList<Int?>()
fun levelOrder(): MutableList<Int?> {
val res = mutableListOf<Int?>()
// 直接遍历数组
for (i in 0..<size()) {
if (value(i) != null) res.add(value(i))
if (value(i) != null)
res.add(value(i))
}
return res
}
@ -1214,34 +1215,38 @@ comments: true
/* 深度优先遍历 */
fun dfs(i: Int, order: String, res: MutableList<Int?>) {
// 若为空位,则返回
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<Int?> {
val res = ArrayList<Int?>()
fun preOrder(): MutableList<Int?> {
val res = mutableListOf<Int?>()
dfs(0, "pre", res)
return res
}
/* 中序遍历 */
fun inOrder(): List<Int?> {
val res = ArrayList<Int?>()
fun inOrder(): MutableList<Int?> {
val res = mutableListOf<Int?>()
dfs(0, "in", res)
return res
}
/* 后序遍历 */
fun postOrder(): List<Int?> {
val res = ArrayList<Int?>()
fun postOrder(): MutableList<Int?> {
val res = mutableListOf<Int?>()
dfs(0, "post", res)
return res
}

View File

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

View File

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

View File

@ -229,7 +229,7 @@ comments: true
fn level_order(root: &Rc<RefCell<TreeNode>>) -> Vec<i32> {
// 初始化队列,加入根节点
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<TreeNode?>()
queue.add(root)
// 初始化一个列表,用于保存遍历序列
val list = ArrayList<Int>()
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<Int>()
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

View File

@ -111,20 +111,22 @@
<!-- Section: brief introduction -->
<section data-md-color-scheme="slate" class="home-div">
<div class="section-content">
<img src="index.assets/hello_algo_header.png" style="width: 100%; max-width: 39em;" alt="Preview">
<img src="index.assets/hello_algo_header.png" style="width: 100%; max-width: 41.5em;" alt="Preview">
<div class="code-badge">
<img src="https://img.shields.io/badge/Python-snow?logo=python&logoColor=3776AB" alt="">
<img src="https://img.shields.io/badge/C%2B%2B-snow?logo=c%2B%2B&logoColor=00599C" alt="">
<img src="https://img.shields.io/badge/Java-snow?logo=coffeescript&logoColor=FC4C02" alt="">
<img src="https://img.shields.io/badge/C%23-snow?logo=csharp&logoColor=512BD4" alt="">
<img src="https://img.shields.io/badge/Go-snow?logo=go&logoColor=00ADD8" alt="">
<img src="https://img.shields.io/badge/Swift-snow?logo=swift&logoColor=F05138" alt="">
<img src="https://img.shields.io/badge/JavaScript-snow?logo=javascript&logoColor=E9CE30" alt="">
<img src="https://img.shields.io/badge/TypeScript-snow?logo=typescript&logoColor=3178C6" alt="">
<img src="https://img.shields.io/badge/Dart-snow?logo=dart&logoColor=0175C2" alt="">
<img src="https://img.shields.io/badge/Rust-snow?logo=rust&logoColor=000000" alt="">
<img src="https://img.shields.io/badge/C-snow?logo=c&logoColor=A8B9CC" alt="">
<img src="https://img.shields.io/badge/Zig-snow?logo=zig&logoColor=F7A41D" alt="">
<img src="https://img.shields.io/badge/Python-snow?logo=python&logoColor=3776AB" alt="" />
<img src="https://img.shields.io/badge/Java-snow?logo=coffeescript&logoColor=FC4C02" alt="" />
<img src="https://img.shields.io/badge/C%2B%2B-snow?logo=c%2B%2B&logoColor=00599C" alt="" />
<img src="https://img.shields.io/badge/C-snow?logo=c&logoColor=A8B9CC" alt="" />
<img src="https://img.shields.io/badge/C%23-snow?logo=csharp&logoColor=512BD4" alt="" />
<img src="https://img.shields.io/badge/JS-snow?logo=javascript&logoColor=E9CE30" alt="" />
<img src="https://img.shields.io/badge/Go-snow?logo=go&logoColor=00ADD8" alt="" />
<img src="https://img.shields.io/badge/Swift-snow?logo=swift&logoColor=F05138" alt="" />
<img src="https://img.shields.io/badge/Rust-snow?logo=rust&logoColor=000000" alt="" />
<img src="https://img.shields.io/badge/Ruby-snow?logo=ruby&logoColor=CC342D" alt="" />
<img src="https://img.shields.io/badge/Kotlin-snow?logo=kotlin&logoColor=7F52FF" alt="" />
<img src="https://img.shields.io/badge/TS-snow?logo=typescript&logoColor=3178C6" alt="" />
<img src="https://img.shields.io/badge/Dart-snow?logo=dart&logoColor=0175C2" alt="" />
<img src="https://img.shields.io/badge/Zig-snow?logo=zig&logoColor=F7A41D" alt="" />
</div>
<p style="margin-top: 2em;">500 幅动画图解、12 种编程语言代码、3000 条社区问答,助你快速入门数据结构与算法</p>
</div>
@ -284,6 +286,14 @@
<br><sub>Zig, Rust</sub>
</a>
</div>
<div class="profile-cell">
<a href="https://github.com/curtishd">
<img class="profile-img" src="assets/avatar/avatar_curtishd.jpg"
alt="Reviewer: curtishd" />
<br><b>curtishd</b>
<br><sub>Kotlin</sub>
</a>
</div>
<div class="profile-cell">
<a href="https://github.com/Gonglja">
<img class="profile-img" src="assets/avatar/avatar_Gonglja.jpg" alt="Reviewer: Gonglja" />
@ -347,7 +357,7 @@
<!-- contributors -->
<div style="margin: 2em auto;">
<h3>贡献者</h3>
<p>本书在开源社区 140 多位贡献者的共同努力下不断完善,感谢他们付出的时间与精力!</p>
<p>本书在开源社区一百多位贡献者的共同努力下不断完善,感谢他们付出的时间与精力!</p>
<a href="https://github.com/krahets/hello-algo/graphs/contributors">
<img src="https://contrib.rocks/image?repo=krahets/hello-algo&max=300&columns=16" alt="Contributors"
style="width: 100%; max-width: 38.5em;">

View File

@ -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<IntArray>) {
val vertices: MutableList<Int> = ArrayList() // 顶点列表,元素代表“顶点值”,索引代表“顶点索引”
val adjMat: MutableList<MutableList<Int>> = ArrayList() // 邻接矩阵,行列索引对应“顶点索引”
val vertices = mutableListOf<Int>() // 顶点列表,元素代表“顶点值”,索引代表“顶点索引”
val adjMat = mutableListOf<MutableList<Int>>() // 邻接矩阵,行列索引对应“顶点索引”
/* 构造函数 */
/* 构造方法 */
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<Int> = mutableListOf()
val newRow = mutableListOf<Int>()
for (j in 0..<n) {
newRow.add(0)
}
@ -1084,7 +1084,8 @@ Below is the implementation code for graphs represented using an adjacency matri
/* 删除顶点 */
fun removeVertex(index: Int) {
if (index >= 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<Array<Vertex?>>) {
// 邻接表key顶点value该顶点的所有邻接顶点
val adjList: MutableMap<Vertex, MutableList<Vertex>> = HashMap()
val adjList = HashMap<Vertex, MutableList<Vertex>>()
/* 构造函数 */
/* 构造方法 */
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<Int>()
val tmp = mutableListOf<Int>()
for (vertex in pair.value) {
tmp.add(vertex.value)
}

View File

@ -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<Vertex> {
fun graphBFS(graph: GraphAdjList, startVet: Vertex): MutableList<Vertex?> {
// 顶点遍历序列
val res: MutableList<Vertex> = ArrayList()
val res = mutableListOf<Vertex?>()
// 哈希表,用于记录已被访问过的顶点
val visited: MutableSet<Vertex> = HashSet()
val visited = HashSet<Vertex>()
visited.add(startVet)
// 队列用于实现 BFS
val que: Queue<Vertex> = LinkedList()
val que = LinkedList<Vertex>()
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<Vertex?>,
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<Vertex?> {
fun graphDFS(graph: GraphAdjList, startVet: Vertex?): MutableList<Vertex?> {
// 顶点遍历序列
val res: MutableList<Vertex?> = ArrayList()
val res = mutableListOf<Vertex?>()
// 哈希表,用于记录已被访问过的顶点
val visited: MutableSet<Vertex?> = HashSet()
val visited = HashSet<Vertex?>()
dfs(graph, visited, res, startVet)
return res
}

View File

@ -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..<capacity) {
buckets.add(mutableListOf())
}
@ -2960,16 +2957,21 @@ The code below implements an open addressing (linear probing) hash table with la
```kotlin title="hash_map_open_addressing.kt"
/* 开放寻址哈希表 */
class HashMapOpenAddressing {
private var size: Int = 0 // 键值对数量
private var capacity = 4 // 哈希表容量
private val loadThres: Double = 2.0 / 3.0 // 触发扩容的负载因子阈值
private val extendRatio = 2 // 扩容倍数
private var buckets: Array<Pair?> // 桶数组
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<Pair?> // 桶数组
private val TOMBSTONE: Pair // 删除标记
/* 构造方法 */
init {
size = 0
capacity = 4
loadThres = 2.0 / 3.0
extendRatio = 2
buckets = arrayOfNulls(capacity)
TOMBSTONE = Pair(-1, "-1")
}
/* 哈希函数 */

View File

@ -1548,15 +1548,9 @@ The following code implements a simple hash table. Here, we encapsulate `key` an
/* 基于数组实现的哈希表 */
class ArrayHashMap {
// 初始化数组,包含 100 个桶
private val buckets = arrayOfNulls<Pair>(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<Pair> {
val pairSet = ArrayList<Pair>()
val pairSet = mutableListOf<Pair>()
for (pair in buckets) {
if (pair != null) pairSet.add(pair)
if (pair != null)
pairSet.add(pair)
}
return pairSet
}
/* 获取所有键 */
fun keySet(): MutableList<Int> {
val keySet = ArrayList<Int>()
val keySet = mutableListOf<Int>()
for (pair in buckets) {
if (pair != null) keySet.add(pair.key)
if (pair != null)
keySet.add(pair.key)
}
return keySet
}
/* 获取所有值 */
fun valueSet(): MutableList<String> {
val valueSet = ArrayList<String>()
val valueSet = mutableListOf<String>()
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<Pair>(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<Pair> {
val pairSet = ArrayList<Pair>()
val pairSet = mutableListOf<Pair>()
for (pair in buckets) {
if (pair != null) pairSet.add(pair)
if (pair != null)
pairSet.add(pair)
}
return pairSet
}
/* 获取所有键 */
fun keySet(): MutableList<Int> {
val keySet = ArrayList<Int>()
val keySet = mutableListOf<Int>()
for (pair in buckets) {
if (pair != null) keySet.add(pair.key)
if (pair != null)
keySet.add(pair.key)
}
return keySet
}
/* 获取所有值 */
fun valueSet(): MutableList<String> {
val valueSet = ArrayList<String>()
val valueSet = mutableListOf<String>()
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}")
}
}
}

View File

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

View File

@ -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<Int>?) {
class MaxHeap(nums: MutableList<Int>?) {
// 使用列表而非数组,这样无须考虑扩容问题
// 将列表元素原封不动添加进堆
private val maxHeap = ArrayList(nums!!)
private val maxHeap = mutableListOf<Int>()
/* 构造函数,根据输入列表建堆 */
/* 构造方法,根据输入列表建堆 */
init {
// 将列表元素原封不动添加进堆
maxHeap.addAll(nums!!)
// 堆化除叶节点以外的其他所有节点
for (i in parent(size() - 1) downTo 0) {
siftDown(i)

View File

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

View File

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

View File

@ -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<Int>()
private val stack = mutableListOf<Int>()
/* 获取栈的长度 */
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<Any> {
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"

View File

@ -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<Int?>) {
/* 构造方法 */
class ArrayBinaryTree(val tree: MutableList<Int?>) {
/* 列表容量 */
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<Int?> {
val res = ArrayList<Int?>()
fun levelOrder(): MutableList<Int?> {
val res = mutableListOf<Int?>()
// 直接遍历数组
for (i in 0..<size()) {
if (value(i) != null) res.add(value(i))
if (value(i) != null)
res.add(value(i))
}
return res
}
@ -1214,34 +1215,38 @@ The following code implements a binary tree based on array representation, inclu
/* 深度优先遍历 */
fun dfs(i: Int, order: String, res: MutableList<Int?>) {
// 若为空位,则返回
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<Int?> {
val res = ArrayList<Int?>()
fun preOrder(): MutableList<Int?> {
val res = mutableListOf<Int?>()
dfs(0, "pre", res)
return res
}
/* 中序遍历 */
fun inOrder(): List<Int?> {
val res = ArrayList<Int?>()
fun inOrder(): MutableList<Int?> {
val res = mutableListOf<Int?>()
dfs(0, "in", res)
return res
}
/* 后序遍历 */
fun postOrder(): List<Int?> {
val res = ArrayList<Int?>()
fun postOrder(): MutableList<Int?> {
val res = mutableListOf<Int?>()
dfs(0, "post", res)
return res
}

View File

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

View File

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

View File

@ -229,7 +229,7 @@ Breadth-first traversal is usually implemented with the help of a "queue". The q
fn level_order(root: &Rc<RefCell<TreeNode>>) -> Vec<i32> {
// 初始化队列,加入根节点
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<TreeNode?>()
queue.add(root)
// 初始化一个列表,用于保存遍历序列
val list = ArrayList<Int>()
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<Int>()
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

View File

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

View File

@ -111,20 +111,22 @@
<!-- Section: brief introduction -->
<section data-md-color-scheme="slate" class="home-div">
<div class="section-content">
<img src="index.assets/hello_algo_header.png" style="width: 100%; max-width: 39em;" alt="Preview">
<img src="index.assets/hello_algo_header.png" style="width: 100%; max-width: 41.5em;" alt="Preview">
<div class="code-badge">
<img src="https://img.shields.io/badge/Python-snow?logo=python&logoColor=3776AB" alt="">
<img src="https://img.shields.io/badge/C%2B%2B-snow?logo=c%2B%2B&logoColor=00599C" alt="">
<img src="https://img.shields.io/badge/Java-snow?logo=coffeescript&logoColor=FC4C02" alt="">
<img src="https://img.shields.io/badge/C%23-snow?logo=csharp&logoColor=512BD4" alt="">
<img src="https://img.shields.io/badge/Go-snow?logo=go&logoColor=00ADD8" alt="">
<img src="https://img.shields.io/badge/Swift-snow?logo=swift&logoColor=F05138" alt="">
<img src="https://img.shields.io/badge/JavaScript-snow?logo=javascript&logoColor=E9CE30" alt="">
<img src="https://img.shields.io/badge/TypeScript-snow?logo=typescript&logoColor=3178C6" alt="">
<img src="https://img.shields.io/badge/Dart-snow?logo=dart&logoColor=0175C2" alt="">
<img src="https://img.shields.io/badge/Rust-snow?logo=rust&logoColor=000000" alt="">
<img src="https://img.shields.io/badge/C-snow?logo=c&logoColor=A8B9CC" alt="">
<img src="https://img.shields.io/badge/Zig-snow?logo=zig&logoColor=F7A41D" alt="">
<img src="https://img.shields.io/badge/Python-snow?logo=python&logoColor=3776AB" alt="" />
<img src="https://img.shields.io/badge/Java-snow?logo=coffeescript&logoColor=FC4C02" alt="" />
<img src="https://img.shields.io/badge/C%2B%2B-snow?logo=c%2B%2B&logoColor=00599C" alt="" />
<img src="https://img.shields.io/badge/C-snow?logo=c&logoColor=A8B9CC" alt="" />
<img src="https://img.shields.io/badge/C%23-snow?logo=csharp&logoColor=512BD4" alt="" />
<img src="https://img.shields.io/badge/JS-snow?logo=javascript&logoColor=E9CE30" alt="" />
<img src="https://img.shields.io/badge/Go-snow?logo=go&logoColor=00ADD8" alt="" />
<img src="https://img.shields.io/badge/Swift-snow?logo=swift&logoColor=F05138" alt="" />
<img src="https://img.shields.io/badge/Rust-snow?logo=rust&logoColor=000000" alt="" />
<img src="https://img.shields.io/badge/Ruby-snow?logo=ruby&logoColor=CC342D" alt="" />
<img src="https://img.shields.io/badge/Kotlin-snow?logo=kotlin&logoColor=7F52FF" alt="" />
<img src="https://img.shields.io/badge/TS-snow?logo=typescript&logoColor=3178C6" alt="" />
<img src="https://img.shields.io/badge/Dart-snow?logo=dart&logoColor=0175C2" alt="" />
<img src="https://img.shields.io/badge/Zig-snow?logo=zig&logoColor=F7A41D" alt="" />
</div>
<p style="margin-top: 2em;">500 幅動畫圖解、12 種程式語言程式碼、3000 條社群問答,助你快速入門資料結構與演算法</p>
</div>
@ -347,7 +349,7 @@
<!-- contributors -->
<div style="margin: 2em auto;">
<h3>貢獻者</h3>
<p>本書在開源社群 140 多位貢獻者的共同努力下不斷完善,感謝他們付出的時間與精力!</p>
<p>本書在開源社群一百多位貢獻者的共同努力下不斷完善,感謝他們付出的時間與精力!</p>
<a href="https://github.com/krahets/hello-algo/graphs/contributors">
<img src="https://contrib.rocks/image?repo=krahets/hello-algo&max=300&columns=16" alt="Contributors"
style="width: 100%; max-width: 38.5em;">