From 87af6639290e50cf28c93b7f3edef46e1d68c30e Mon Sep 17 00:00:00 2001 From: krahets Date: Mon, 25 Mar 2024 22:43:12 +0800 Subject: [PATCH] build --- docs/chapter_appendix/terminology.md | 2 +- docs/chapter_array_and_linkedlist/array.md | 90 ++ .../linked_list.md | 69 + docs/chapter_array_and_linkedlist/list.md | 136 ++ .../backtracking_algorithm.md | 120 ++ docs/chapter_backtracking/n_queens_problem.md | 67 + .../permutations_problem.md | 82 + .../subset_sum_problem.md | 134 ++ .../iteration_and_recursion.md | 130 ++ .../space_complexity.md | 139 +- .../time_complexity.md | 236 ++- .../basic_data_types.md | 6 + .../binary_search_recur.md | 36 + .../build_binary_tree_problem.md | 31 + .../hanota_problem.md | 34 + .../dp_problem_features.md | 62 + .../dp_solution_pipeline.md | 112 ++ .../edit_distance_problem.md | 69 + .../intro_to_dynamic_programming.md | 112 ++ .../knapsack_problem.md | 114 ++ .../unbounded_knapsack_problem.md | 168 ++ docs/chapter_graph/graph_operations.md | 160 ++ docs/chapter_graph/graph_traversal.md | 66 + .../fractional_knapsack_problem.md | 72 + docs/chapter_greedy/greedy_algorithm.md | 24 + docs/chapter_greedy/max_capacity_problem.md | 26 + .../max_product_cutting_problem.md | 25 + docs/chapter_hashing/hash_algorithm.md | 46 + docs/chapter_hashing/hash_collision.md | 241 +++ docs/chapter_hashing/hash_map.md | 172 ++ docs/chapter_heap/build_heap.md | 120 ++ docs/chapter_heap/heap.md | 100 ++ docs/chapter_heap/top_k.md | 23 + docs/chapter_preface/suggestions.md | 13 + docs/chapter_searching/binary_search.md | 46 + docs/chapter_searching/binary_search_edge.md | 34 + .../binary_search_insertion.md | 44 + .../replace_linear_by_hashing.md | 35 + docs/chapter_sorting/bubble_sort.md | 39 + docs/chapter_sorting/bucket_sort.md | 33 + docs/chapter_sorting/counting_sort.md | 68 + docs/chapter_sorting/heap_sort.md | 38 + docs/chapter_sorting/insertion_sort.md | 19 + docs/chapter_sorting/merge_sort.md | 43 + docs/chapter_sorting/quick_sort.md | 87 + docs/chapter_sorting/radix_sort.md | 53 + docs/chapter_sorting/selection_sort.md | 19 + docs/chapter_stack_and_queue/deque.md | 238 +++ docs/chapter_stack_and_queue/queue.md | 140 ++ docs/chapter_stack_and_queue/stack.md | 102 ++ .../array_representation_of_tree.md | 85 + docs/chapter_tree/avl_tree.md | 168 ++ docs/chapter_tree/binary_search_tree.md | 97 ++ docs/chapter_tree/binary_tree.md | 18 + docs/chapter_tree/binary_tree_traversal.md | 52 + en/docs/chapter_array_and_linkedlist/array.md | 90 ++ .../linked_list.md | 69 + en/docs/chapter_array_and_linkedlist/list.md | 136 ++ .../iteration_and_recursion.md | 130 ++ .../space_complexity.md | 139 +- .../time_complexity.md | 236 ++- .../basic_data_types.md | 6 + en/docs/chapter_hashing/hash_algorithm.md | 46 + en/docs/chapter_hashing/hash_collision.md | 241 +++ en/docs/chapter_hashing/hash_map.md | 172 ++ en/docs/chapter_preface/suggestions.md | 13 + en/docs/chapter_stack_and_queue/deque.md | 1440 ++++++++++++++++- en/docs/chapter_stack_and_queue/queue.md | 140 ++ en/docs/chapter_stack_and_queue/stack.md | 102 ++ overrides/stylesheets/extra.css | 5 + 70 files changed, 7428 insertions(+), 32 deletions(-) diff --git a/docs/chapter_appendix/terminology.md b/docs/chapter_appendix/terminology.md index 125b9e9c8..e684a026c 100644 --- a/docs/chapter_appendix/terminology.md +++ b/docs/chapter_appendix/terminology.md @@ -136,7 +136,7 @@ comments: true | $n$-queens problem | $n$ 皇后问题 | $n$ 皇后問題 | | dynamic programming | 动态规划 | 動態規劃 | | initial state | 初始状态 | 初始狀態 | -| state-trasition equation | 状态转移方程 | 狀態轉移方程 | +| state-transition equation | 状态转移方程 | 狀態轉移方程 | | knapsack problem | 背包问题 | 背包問題 | | edit distance problem | 编辑距离问题 | 編輯距離問題 | | greedy algorithm | 贪心算法 | 貪婪演算法 | diff --git a/docs/chapter_array_and_linkedlist/array.md b/docs/chapter_array_and_linkedlist/array.md index 847ee1fd9..2c110d915 100755 --- a/docs/chapter_array_and_linkedlist/array.md +++ b/docs/chapter_array_and_linkedlist/array.md @@ -111,6 +111,12 @@ comments: true int nums[5] = { 1, 3, 2, 5, 4 }; ``` +=== "Kotlin" + + ```kotlin title="array.kt" + + ``` + === "Zig" ```zig title="array.zig" @@ -279,6 +285,19 @@ comments: true } ``` +=== "Kotlin" + + ```kotlin title="array.kt" + /* 随机访问元素 */ + fun randomAccess(nums: IntArray): Int { + // 在区间 [0, nums.size) 中随机抽取一个数字 + val randomIndex = ThreadLocalRandom.current().nextInt(0, nums.size) + // 获取并返回随机元素 + val randomNum = nums[randomIndex] + return randomNum + } + ``` + === "Zig" ```zig title="array.zig" @@ -459,6 +478,20 @@ comments: true } ``` +=== "Kotlin" + + ```kotlin title="array.kt" + /* 在数组的索引 index 处插入元素 num */ + fun insert(nums: IntArray, num: Int, index: Int) { + // 把索引 index 以及之后的所有元素向后移动一位 + for (i in nums.size - 1 downTo index + 1) { + nums[i] = nums[i - 1] + } + // 将 num 赋给 index 处的元素 + nums[index] = num + } + ``` + === "Zig" ```zig title="array.zig" @@ -620,6 +653,18 @@ comments: true } ``` +=== "Kotlin" + + ```kotlin title="array.kt" + /* 删除索引 index 处的元素 */ + fun remove(nums: IntArray, index: Int) { + // 把索引 index 之后的所有元素向前移动一位 + for (i in index..next = n4; ``` +=== "Kotlin" + + ```kotlin title="linked_list.kt" + + ``` + === "Zig" ```zig title="linked_list.zig" @@ -534,6 +546,17 @@ comments: true } ``` +=== "Kotlin" + + ```kotlin title="linked_list.kt" + /* 在链表的节点 n0 之后插入节点p */ + fun insert(n0: ListNode?, p: ListNode?) { + val n1 = n0?.next + p?.next = n1 + n0?.next = p + } + ``` + === "Zig" ```zig title="linked_list.zig" @@ -723,6 +746,17 @@ comments: true } ``` +=== "Kotlin" + + ```kotlin title="linked_list.kt" + /* 删除链表的节点 n0 之后的首个节点 */ + fun remove(n0: ListNode?) { + val p = n0?.next + val n1 = p?.next + n0?.next = n1 + } + ``` + === "Zig" ```zig title="linked_list.zig" @@ -903,6 +937,19 @@ comments: true } ``` +=== "Kotlin" + + ```kotlin title="linked_list.kt" + /* 访问链表中索引为 index 的节点 */ + fun access(head: ListNode?, index: Int): ListNode? { + var h = head + for (i in 0..= size) + throw IndexOutOfBoundsException() + return arr[index] + } + + /* 更新元素 */ + fun set(index: Int, num: Int) { + if (index < 0 || index >= size) + throw IndexOutOfBoundsException("索引越界") + arr[index] = num + } + + /* 在尾部添加元素 */ + fun add(num: Int) { + // 元素数量超出容量时,触发扩容机制 + if (size == capacity()) + extendCapacity() + arr[size] = num + // 更新元素数量 + size++ + } + + /* 在中间插入元素 */ + fun insert(index: Int, num: Int) { + if (index < 0 || index >= size) + throw IndexOutOfBoundsException("索引越界") + // 元素数量超出容量时,触发扩容机制 + if (size == capacity()) + extendCapacity() + // 将索引 index 以及之后的元素都向后移动一位 + for (j in size - 1 downTo index) + arr[j + 1] = arr[j] + arr[index] = num + // 更新元素数量 + size++ + } + + /* 删除元素 */ + fun remove(index: Int): Int { + if (index < 0 || index >= size) + throw IndexOutOfBoundsException("索引越界") + val num: Int = arr[index] + // 将将索引 index 之后的元素都向前移动一位 + for (j in index..): Boolean { + return state.isNotEmpty() && state[state.size - 1]?.value == 7 + } + + /* 记录解 */ + fun recordSolution(state: MutableList?, res: MutableList?>) { + res.add(state?.let { ArrayList(it) }) + } + + /* 判断在当前状态下,该选择是否合法 */ + fun isValid(state: List?, choice: TreeNode?): Boolean { + return choice != null && choice.value != 3 + } + + /* 更新状态 */ + fun makeChoice(state: MutableList, choice: TreeNode?) { + state.add(choice) + } + + /* 恢复状态 */ + fun undoChoice(state: MutableList, choice: TreeNode?) { + state.removeLast() + } + + /* 回溯算法:例题三 */ + fun backtrack( + state: MutableList, + choices: List, + res: MutableList?> + ) { + // 检查是否为解 + if (isSolution(state)) { + // 记录解 + recordSolution(state, res) + } + // 遍历所有选择 + for (choice in choices) { + // 剪枝:检查选择是否合法 + if (isValid(state, choice)) { + // 尝试:做出选择,更新状态 + makeChoice(state, choice) + // 进行下一轮选择 + backtrack(state, listOf(choice!!.left, choice.right), res) + // 回退:撤销选择,恢复到之前的状态 + undoChoice(state, choice) + } + } + } + ``` + === "Zig" ```zig title="preorder_traversal_iii_template.zig" diff --git a/docs/chapter_backtracking/n_queens_problem.md b/docs/chapter_backtracking/n_queens_problem.md index c2efa6cbf..646771d24 100644 --- a/docs/chapter_backtracking/n_queens_problem.md +++ b/docs/chapter_backtracking/n_queens_problem.md @@ -639,6 +639,73 @@ comments: true } ``` +=== "Kotlin" + + ```kotlin title="n_queens.kt" + /* 回溯算法:n 皇后 */ + fun backtrack( + row: Int, + n: Int, + state: List>, + res: MutableList>?>, + cols: BooleanArray, + diags1: BooleanArray, + diags2: BooleanArray + ) { + // 当放置完所有行时,记录解 + if (row == n) { + val copyState: MutableList> = ArrayList() + for (sRow in state) { + copyState.add(ArrayList(sRow)) + } + res.add(copyState) + return + } + // 遍历所有列 + for (col in 0..>?> { + // 初始化 n*n 大小的棋盘,其中 'Q' 代表皇后,'#' 代表空位 + val state: MutableList> = ArrayList() + for (i in 0.. = ArrayList() + for (j in 0..>?> = ArrayList() + + backtrack(0, n, state, res, cols, diags1, diags2) + + return res + } + ``` + === "Zig" ```zig title="n_queens.zig" diff --git a/docs/chapter_backtracking/permutations_problem.md b/docs/chapter_backtracking/permutations_problem.md index 950196f76..e29edd4a6 100644 --- a/docs/chapter_backtracking/permutations_problem.md +++ b/docs/chapter_backtracking/permutations_problem.md @@ -463,6 +463,46 @@ comments: true } ``` +=== "Kotlin" + + ```kotlin title="permutations_i.kt" + /* 回溯算法:全排列 I */ + fun backtrack( + state: MutableList, + choices: IntArray, + selected: BooleanArray, + res: MutableList?> + ) { + // 当状态长度等于元素数量时,记录解 + if (state.size == choices.size) { + res.add(ArrayList(state)) + return + } + // 遍历所有选择 + for (i in choices.indices) { + val choice = choices[i] + // 剪枝:不允许重复选择元素 + if (!selected[i]) { + // 尝试:做出选择,更新状态 + selected[i] = true + state.add(choice) + // 进行下一轮选择 + backtrack(state, choices, selected, res) + // 回退:撤销选择,恢复到之前的状态 + selected[i] = false + state.removeAt(state.size - 1) + } + } + } + + /* 全排列 I */ + fun permutationsI(nums: IntArray): List?> { + val res: MutableList?> = ArrayList() + backtrack(ArrayList(), nums, BooleanArray(nums.size), res) + return res + } + ``` + === "Zig" ```zig title="permutations_i.zig" @@ -939,6 +979,48 @@ comments: true } ``` +=== "Kotlin" + + ```kotlin title="permutations_ii.kt" + /* 回溯算法:全排列 II */ + fun backtrack( + state: MutableList, + choices: IntArray, + selected: BooleanArray, + res: MutableList?> + ) { + // 当状态长度等于元素数量时,记录解 + if (state.size == choices.size) { + res.add(ArrayList(state)) + return + } + // 遍历所有选择 + val duplicated: MutableSet = HashSet() + for (i in choices.indices) { + val choice = choices[i] + // 剪枝:不允许重复选择元素 且 不允许重复选择相等元素 + if (!selected[i] && !duplicated.contains(choice)) { + // 尝试:做出选择,更新状态 + duplicated.add(choice) // 记录选择过的元素值 + selected[i] = true + state.add(choice) + // 进行下一轮选择 + backtrack(state, choices, selected, res) + // 回退:撤销选择,恢复到之前的状态 + selected[i] = false + state.removeAt(state.size - 1) + } + } + } + + /* 全排列 II */ + fun permutationsII(nums: IntArray): MutableList?> { + val res: MutableList?> = ArrayList() + backtrack(ArrayList(), nums, BooleanArray(nums.size), res) + return res + } + ``` + === "Zig" ```zig title="permutations_ii.zig" diff --git a/docs/chapter_backtracking/subset_sum_problem.md b/docs/chapter_backtracking/subset_sum_problem.md index 5512b96db..386718046 100644 --- a/docs/chapter_backtracking/subset_sum_problem.md +++ b/docs/chapter_backtracking/subset_sum_problem.md @@ -426,6 +426,47 @@ comments: true } ``` +=== "Kotlin" + + ```kotlin title="subset_sum_i_naive.kt" + /* 回溯算法:子集和 I */ + fun backtrack( + state: MutableList, + target: Int, + total: Int, + choices: IntArray, + res: MutableList?> + ) { + // 子集和等于 target 时,记录解 + if (total == target) { + res.add(ArrayList(state)) + return + } + // 遍历所有选择 + for (i in choices.indices) { + // 剪枝:若子集和超过 target ,则跳过该选择 + if (total + choices[i] > target) { + continue + } + // 尝试:做出选择,更新元素和 total + state.add(choices[i]) + // 进行下一轮选择 + backtrack(state, target, total + choices[i], choices, res) + // 回退:撤销选择,恢复到之前的状态 + state.removeAt(state.size - 1) + } + } + + /* 求解子集和 I(包含重复子集) */ + fun subsetSumINaive(nums: IntArray, target: Int): List?> { + val state: MutableList = ArrayList() // 状态(子集) + val total = 0 // 子集和 + val res: MutableList?> = ArrayList() // 结果列表(子集列表) + backtrack(state, target, total, nums, res) + return res + } + ``` + === "Zig" ```zig title="subset_sum_i_naive.zig" @@ -915,6 +956,50 @@ comments: true } ``` +=== "Kotlin" + + ```kotlin title="subset_sum_i.kt" + /* 回溯算法:子集和 I */ + fun backtrack( + state: MutableList, + target: Int, + choices: IntArray, + start: Int, + res: MutableList?> + ) { + // 子集和等于 target 时,记录解 + if (target == 0) { + res.add(ArrayList(state)) + return + } + // 遍历所有选择 + // 剪枝二:从 start 开始遍历,避免生成重复子集 + for (i in start..?> { + val state: MutableList = ArrayList() // 状态(子集) + Arrays.sort(nums) // 对 nums 进行排序 + val start = 0 // 遍历起始点 + val res: MutableList?> = ArrayList() // 结果列表(子集列表) + backtrack(state, target, nums, start, res) + return res + } + ``` + === "Zig" ```zig title="subset_sum_i.zig" @@ -1445,6 +1530,55 @@ comments: true } ``` +=== "Kotlin" + + ```kotlin title="subset_sum_ii.kt" + /* 回溯算法:子集和 II */ + fun backtrack( + state: MutableList, + target: Int, + choices: IntArray, + start: Int, + res: MutableList?> + ) { + // 子集和等于 target 时,记录解 + if (target == 0) { + res.add(ArrayList(state)) + return + } + // 遍历所有选择 + // 剪枝二:从 start 开始遍历,避免生成重复子集 + // 剪枝三:从 start 开始遍历,避免重复选择同一元素 + for (i in start.. start && choices[i] == choices[i - 1]) { + continue + } + // 尝试:做出选择,更新 target, start + state.add(choices[i]) + // 进行下一轮选择 + backtrack(state, target - choices[i], choices, i + 1, res) + // 回退:撤销选择,恢复到之前的状态 + state.removeAt(state.size - 1) + } + } + + /* 求解子集和 II */ + fun subsetSumII(nums: IntArray, target: Int): List?> { + val state: MutableList = ArrayList() // 状态(子集) + Arrays.sort(nums) // 对 nums 进行排序 + val start = 0 // 遍历起始点 + val res: MutableList?> = ArrayList() // 结果列表(子集列表) + backtrack(state, target, nums, start, res) + return res + } + ``` + === "Zig" ```zig title="subset_sum_ii.zig" diff --git a/docs/chapter_computational_complexity/iteration_and_recursion.md b/docs/chapter_computational_complexity/iteration_and_recursion.md index 7bf08dc5e..8cc8be30a 100644 --- a/docs/chapter_computational_complexity/iteration_and_recursion.md +++ b/docs/chapter_computational_complexity/iteration_and_recursion.md @@ -168,6 +168,20 @@ comments: true } ``` +=== "Kotlin" + + ```kotlin title="iteration.kt" + /* for 循环 */ + fun forLoop(n: Int): Int { + var res = 0 + // 循环求和 1, 2, ..., n-1, n + for (i in 1..n) { + res += i + } + return res + } + ``` + === "Zig" ```zig title="iteration.zig" @@ -378,6 +392,22 @@ comments: true } ``` +=== "Kotlin" + + ```kotlin title="iteration.kt" + /* while 循环 */ + fun whileLoop(n: Int): Int { + var res = 0 + var i = 1 // 初始化条件变量 + // 循环求和 1, 2, ..., n-1, n + while (i <= n) { + res += i + i++ // 更新条件变量 + } + return res + } + ``` + === "Zig" ```zig title="iteration.zig" @@ -601,6 +631,24 @@ comments: true } ``` +=== "Kotlin" + + ```kotlin title="iteration.kt" + /* while 循环(两次更新) */ + fun whileLoopII(n: Int): Int { + var res = 0 + var i = 1 // 初始化条件变量 + // 循环求和 1, 4, 10, ... + while (i <= n) { + res += i + // 更新条件变量 + i++ + i *= 2 + } + return res + } + ``` + === "Zig" ```zig title="iteration.zig" @@ -818,6 +866,23 @@ comments: true } ``` +=== "Kotlin" + + ```kotlin title="iteration.kt" + /* 双层 for 循环 */ + fun nestedForLoop(n: Int): String { + val res = StringBuilder() + // 循环 i = 1, 2, ..., n-1, n + for (i in 1..n) { + // 循环 j = 1, 2, ..., n-1, n + for (j in 1..n) { + res.append(" ($i, $j), ") + } + } + return res.toString() + } + ``` + === "Zig" ```zig title="iteration.zig" @@ -1032,6 +1097,21 @@ comments: true } ``` +=== "Kotlin" + + ```kotlin title="recursion.kt" + /* 递归 */ + fun recur(n: Int): Int { + // 终止条件 + if (n == 1) + return 1 + // 递: 递归调用 + val res = recur(n - 1) + // 归: 返回结果 + return n + res + } + ``` + === "Zig" ```zig title="recursion.zig" @@ -1235,6 +1315,19 @@ comments: true } ``` +=== "Kotlin" + + ```kotlin title="recursion.kt" + /* Kotlin tailrec 关键词使函数实现尾递归优化 */ + tailrec fun tailRecur(n: Int, res: Int): Int { + // 终止条件 + if (n == 0) + return res + // 尾递归调用 + return tailRecur(n - 1, res + n) + } + ``` + === "Zig" ```zig title="recursion.zig" @@ -1446,6 +1539,21 @@ comments: true } ``` +=== "Kotlin" + + ```kotlin title="recursion.kt" + /* 斐波那契数列:递归 */ + fun fib(n: Int): Int { + // 终止条件 f(1) = 0, f(2) = 1 + if (n == 1 || n == 2) + return n - 1 + // 递归调用 f(n) = f(n-1) + f(n-2) + val res = fib(n - 1) + fib(n - 2) + // 返回结果 f(n) + return res + } + ``` + === "Zig" ```zig title="recursion.zig" @@ -1760,6 +1868,28 @@ comments: true } ``` +=== "Kotlin" + + ```kotlin title="recursion.kt" + /* 使用迭代模拟递归 */ + fun forLoopRecur(n: Int): Int { + // 使用一个显式的栈来模拟系统调用栈 + val stack = Stack() + var res = 0 + // 递: 递归调用 + for (i in n downTo 0) { + stack.push(i) + } + // 归: 返回结果 + while (stack.isNotEmpty()) { + // 通过“出栈操作”模拟“归” + res += stack.pop() + } + // res = 1+2+3+...+n + return res + } + ``` + === "Zig" ```zig title="recursion.zig" diff --git a/docs/chapter_computational_complexity/space_complexity.md b/docs/chapter_computational_complexity/space_complexity.md index c2c47eac6..ba694ceb8 100755 --- a/docs/chapter_computational_complexity/space_complexity.md +++ b/docs/chapter_computational_complexity/space_complexity.md @@ -315,6 +315,12 @@ comments: true } ``` +=== "Kotlin" + + ```kotlin title="" + + ``` + === "Zig" ```zig title="" @@ -461,6 +467,12 @@ comments: true } ``` +=== "Kotlin" + + ```kotlin title="" + + ``` + === "Zig" ```zig title="" @@ -481,9 +493,10 @@ comments: true for _ in range(n): function() - def recur(n: int) -> int: + def recur(n: int): """递归的空间复杂度为 O(n)""" - if n == 1: return + if n == 1: + return return recur(n - 1) ``` @@ -698,6 +711,12 @@ comments: true } ``` +=== "Kotlin" + + ```kotlin title="" + + ``` + === "Zig" ```zig title="" @@ -724,7 +743,7 @@ $$

图 2-16   常见的空间复杂度类型

-### 1.   常数阶 $O(1)$ +### 1.   常数阶 $O(1)$ {data-toc-label="常数阶"} 常数阶常见于数量与输入数据大小 $n$ 无关的常量、变量、对象。 @@ -1030,6 +1049,33 @@ $$ } ``` +=== "Kotlin" + + ```kotlin title="space_complexity.kt" + /* 函数 */ + fun function(): Int { + // 执行某些操作 + return 0 + } + + /* 常数阶 */ + fun constant(n: Int) { + // 常量、变量、对象占用 O(1) 空间 + val a = 0 + var b = 0 + val nums = Array(10000) { 0 } + val node = ListNode(0) + // 循环中的变量占用 O(1) 空间 + for (i in 0.. -### 2.   线性阶 $O(n)$ +### 2.   线性阶 $O(n)$ {data-toc-label="线性阶"} 线性阶常见于元素数量与 $n$ 成正比的数组、链表、栈、队列等: @@ -1306,6 +1352,26 @@ $$ } ``` +=== "Kotlin" + + ```kotlin title="space_complexity.kt" + /* 线性阶 */ + fun linear(n: Int) { + // 长度为 n 的数组占用 O(n) 空间 + val nums = Array(n) { 0 } + // 长度为 n 的列表占用 O(n) 空间 + val nodes = mutableListOf() + for (i in 0..() + for (i in 0.. 图 2-17   递归函数产生的线性阶空间复杂度

-### 3.   平方阶 $O(n^2)$ +### 3.   平方阶 $O(n^2)$ {data-toc-label="平方阶"} 平方阶常见于矩阵和图,元素数量与 $n$ 成平方关系: @@ -1685,6 +1763,25 @@ $$ } ``` +=== "Kotlin" + + ```kotlin title="space_complexity.kt" + /* 平方阶 */ + fun quadratic(n: Int) { + // 矩阵占用 O(n^2) 空间 + val numMatrix: Array?> = arrayOfNulls(n) + // 二维列表占用 O(n^2) 空间 + val numList: MutableList> = arrayListOf() + for (i in 0..() + for (j in 0.. 图 2-18   递归函数产生的平方阶空间复杂度

-### 4.   指数阶 $O(2^n)$ +### 4.   指数阶 $O(2^n)$ {data-toc-label="指数阶"} 指数阶常见于二叉树。观察图 2-19 ,层数为 $n$ 的“满二叉树”的节点数量为 $2^n - 1$ ,占用 $O(2^n)$ 空间: @@ -2038,6 +2149,20 @@ $$ } ``` +=== "Kotlin" + + ```kotlin title="space_complexity.kt" + /* 指数阶(建立满二叉树) */ + fun buildTree(n: Int): TreeNode? { + if (n == 0) + return null + val root = TreeNode(0) + root.left = buildTree(n - 1) + root.right = buildTree(n - 1) + return root + } + ``` + === "Zig" ```zig title="space_complexity.zig" @@ -2061,7 +2186,7 @@ $$

图 2-19   满二叉树产生的指数阶空间复杂度

-### 5.   对数阶 $O(\log n)$ +### 5.   对数阶 $O(\log n)$ {data-toc-label="对数阶"} 对数阶常见于分治算法。例如归并排序,输入长度为 $n$ 的数组,每轮递归将数组从中点处划分为两半,形成高度为 $\log n$ 的递归树,使用 $O(\log n)$ 栈帧空间。 diff --git a/docs/chapter_computational_complexity/time_complexity.md b/docs/chapter_computational_complexity/time_complexity.md index b416344d1..ff73e2655 100755 --- a/docs/chapter_computational_complexity/time_complexity.md +++ b/docs/chapter_computational_complexity/time_complexity.md @@ -175,6 +175,12 @@ comments: true } ``` +=== "Kotlin" + + ```kotlin title="" + + ``` + === "Zig" ```zig title="" @@ -433,6 +439,12 @@ $$ } ``` +=== "Kotlin" + + ```kotlin title="" + + ``` + === "Zig" ```zig title="" @@ -629,6 +641,12 @@ $$ } ``` +=== "Kotlin" + + ```kotlin title="" + + ``` + === "Zig" ```zig title="" @@ -888,6 +906,12 @@ $T(n)$ 是一次函数,说明其运行时间的增长趋势是线性的,因 } ``` +=== "Kotlin" + + ```kotlin title="" + + ``` + === "Zig" ```zig title="" @@ -954,7 +978,7 @@ $$

图 2-9   常见的时间复杂度类型

-### 1.   常数阶 $O(1)$ +### 1.   常数阶 $O(1)$ {data-toc-label="常数阶"} 常数阶的操作数量与输入数据大小 $n$ 无关,即不随着 $n$ 的变化而变化。 @@ -1107,6 +1131,19 @@ $$ } ``` +=== "Kotlin" + + ```kotlin title="time_complexity.kt" + /* 常数阶 */ + fun constant(n: Int): Int { + var count = 0 + val size = 10_0000 + for (i in 0.. -### 2.   线性阶 $O(n)$ +### 2.   线性阶 $O(n)$ {data-toc-label="线性阶"} 线性阶的操作数量相对于输入数据大小 $n$ 以线性级别增长。线性阶通常出现在单层循环中: @@ -1266,6 +1303,19 @@ $$ } ``` +=== "Kotlin" + + ```kotlin title="time_complexity.kt" + /* 线性阶 */ + fun linear(n: Int): Int { + var count = 0 + // 循环次数与数组长度成正比 + for (i in 0.. nums[j + 1]) { + // 交换 nums[j] 与 nums[j + 1] + nums[j] = nums[j + 1].also { nums[j + 1] = nums[j] } + count += 3 // 元素交换包含 3 个单元操作 + } + } + } + return count + } + ``` + === "Zig" ```zig title="time_complexity.zig" @@ -1946,7 +2047,7 @@ $$
-### 4.   指数阶 $O(2^n)$ +### 4.   指数阶 $O(2^n)$ {data-toc-label="指数阶"} 生物学的“细胞分裂”是指数阶增长的典型例子:初始状态为 $1$ 个细胞,分裂一轮后变为 $2$ 个,分裂两轮后变为 $4$ 个,以此类推,分裂 $n$ 轮后有 $2^n$ 个细胞。 @@ -2153,6 +2254,25 @@ $$ } ``` +=== "Kotlin" + + ```kotlin title="time_complexity.kt" + /* 指数阶(循环实现) */ + fun exponential(n: Int): Int { + var count = 0 + // 细胞每轮一分为二,形成数列 1, 2, 4, 8, ..., 2^(n-1) + var base = 1 + for (i in 0.. 1) { + n1 /= 2 + count++ + } + return count + } + ``` + === "Zig" ```zig title="time_complexity.zig" @@ -2626,6 +2773,17 @@ $$ } ``` +=== "Kotlin" + + ```kotlin title="time_complexity.kt" + /* 对数阶(递归实现) */ + fun logRecur(n: Int): Int { + if (n <= 1) + return 0 + return logRecur(n / 2) + 1 + } + ``` + === "Zig" ```zig title="time_complexity.zig" @@ -2653,7 +2811,7 @@ $$ 也就是说,底数 $m$ 可以在不影响复杂度的前提下转换。因此我们通常会省略底数 $m$ ,将对数阶直接记为 $O(\log n)$ 。 -### 6.   线性对数阶 $O(n \log n)$ +### 6.   线性对数阶 $O(n \log n)$ {data-toc-label="线性对数阶"} 线性对数阶常出现于嵌套循环中,两层循环的时间复杂度分别为 $O(\log n)$ 和 $O(n)$ 。相关代码如下: @@ -2819,6 +2977,21 @@ $$ } ``` +=== "Kotlin" + + ```kotlin title="time_complexity.kt" + /* 线性对数阶 */ + fun linearLogRecur(n: Int): Int { + if (n <= 1) + return 1 + var count = linearLogRecur(n / 2) + linearLogRecur(n / 2) + for (i in 0.. { + val nums = IntArray(n) + // 生成数组 nums = { 1, 2, 3, ..., n } + for (i in 0.. int[] + val res = arrayOfNulls(n) + for (i in 0..): Int { + for (i in nums.indices) { + // 当元素 1 在数组头部时,达到最佳时间复杂度 O(1) + // 当元素 1 在数组尾部时,达到最差时间复杂度 O(n) + if (nums[i] == 1) + return i + } + return -1 + } + ``` + === "Zig" ```zig title="worst_best_time_complexity.zig" diff --git a/docs/chapter_data_structure/basic_data_types.md b/docs/chapter_data_structure/basic_data_types.md index 16b4e7ba1..37d426228 100644 --- a/docs/chapter_data_structure/basic_data_types.md +++ b/docs/chapter_data_structure/basic_data_types.md @@ -161,6 +161,12 @@ comments: true bool bools[10]; ``` +=== "Kotlin" + + ```kotlin title="" + + ``` + === "Zig" ```zig title="" diff --git a/docs/chapter_divide_and_conquer/binary_search_recur.md b/docs/chapter_divide_and_conquer/binary_search_recur.md index ed8c5f252..3fffe03bb 100644 --- a/docs/chapter_divide_and_conquer/binary_search_recur.md +++ b/docs/chapter_divide_and_conquer/binary_search_recur.md @@ -383,6 +383,42 @@ comments: true } ``` +=== "Kotlin" + + ```kotlin title="binary_search_recur.kt" + /* 二分查找:问题 f(i, j) */ + fun dfs( + nums: IntArray, + target: Int, + i: Int, + j: Int + ): Int { + // 若区间为空,代表无目标元素,则返回 -1 + if (i > j) { + return -1 + } + // 计算中点索引 m + val m = (i + j) / 2 + return if (nums[m] < target) { + // 递归子问题 f(m+1, j) + dfs(nums, target, m + 1, j) + } else if (nums[m] > target) { + // 递归子问题 f(i, m-1) + dfs(nums, target, i, m - 1) + } else { + // 找到目标元素,返回其索引 + m + } + } + + /* 二分查找 */ + fun binarySearch(nums: IntArray, target: Int): Int { + val n = nums.size + // 求解问题 f(0, n-1) + return dfs(nums, target, 0, n - 1) + } + ``` + === "Zig" ```zig title="binary_search_recur.zig" 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 4ed9a14b5..9f4e6b9c0 100644 --- a/docs/chapter_divide_and_conquer/build_binary_tree_problem.md +++ b/docs/chapter_divide_and_conquer/build_binary_tree_problem.md @@ -445,6 +445,37 @@ comments: true } ``` +=== "Kotlin" + + ```kotlin title="build_tree.kt" + /* 构建二叉树:分治 */ + fun dfs(preorder: IntArray, inorderMap: Map, i: Int, l: Int, r: Int): TreeNode? { + // 子树区间为空时终止 + if (r - l < 0) return null + // 初始化根节点 + val root = TreeNode(preorder[i]) + // 查询 m ,从而划分左右子树 + val m = inorderMap[preorder[i]]!! + // 子问题:构建左子树 + root.left = dfs(preorder, inorderMap, i + 1, l, m - 1) + // 子问题:构建右子树 + root.right = dfs(preorder, inorderMap, i + 1 + m - l, m + 1, r) + // 返回根节点 + return root + } + + /* 构建二叉树 */ + fun buildTree(preorder: IntArray, inorder: IntArray): TreeNode? { + // 初始化哈希表,存储 inorder 元素到索引的映射 + val inorderMap: MutableMap = HashMap() + for (i in inorder.indices) { + inorderMap[inorder[i]] = i + } + val root = dfs(preorder, inorderMap, 0, 0, inorder.size - 1) + return root + } + ``` + === "Zig" ```zig title="build_tree.zig" diff --git a/docs/chapter_divide_and_conquer/hanota_problem.md b/docs/chapter_divide_and_conquer/hanota_problem.md index 9a1abd336..c0072e82e 100644 --- a/docs/chapter_divide_and_conquer/hanota_problem.md +++ b/docs/chapter_divide_and_conquer/hanota_problem.md @@ -473,6 +473,40 @@ comments: true } ``` +=== "Kotlin" + + ```kotlin title="hanota.kt" + /* 移动一个圆盘 */ + fun move(src: MutableList, tar: MutableList) { + // 从 src 顶部拿出一个圆盘 + val pan: Int = src.removeAt(src.size - 1) + // 将圆盘放入 tar 顶部 + tar.add(pan) + } + + /* 求解汉诺塔问题 f(i) */ + fun dfs(i: Int, src: MutableList, buf: MutableList, tar: MutableList) { + // 若 src 只剩下一个圆盘,则直接将其移到 tar + if (i == 1) { + move(src, tar) + return + } + // 子问题 f(i-1) :将 src 顶部 i-1 个圆盘借助 tar 移到 buf + dfs(i - 1, src, tar, buf) + // 子问题 f(1) :将 src 剩余一个圆盘移到 tar + move(src, tar) + // 子问题 f(i-1) :将 buf 顶部 i-1 个圆盘借助 src 移到 tar + dfs(i - 1, buf, src, tar) + } + + /* 求解汉诺塔问题 */ + fun solveHanota(A: MutableList, B: MutableList, C: MutableList) { + val n = A.size + // 将 A 顶部 n 个圆盘借助 B 移到 C + dfs(n, A, B, C) + } + ``` + === "Zig" ```zig title="hanota.zig" diff --git a/docs/chapter_dynamic_programming/dp_problem_features.md b/docs/chapter_dynamic_programming/dp_problem_features.md index aab06321b..b6d99f287 100644 --- a/docs/chapter_dynamic_programming/dp_problem_features.md +++ b/docs/chapter_dynamic_programming/dp_problem_features.md @@ -281,6 +281,26 @@ $$ } ``` +=== "Kotlin" + + ```kotlin title="min_cost_climbing_stairs_dp.kt" + /* 爬楼梯最小代价:动态规划 */ + fun minCostClimbingStairsDP(cost: IntArray): Int { + val n = cost.size - 1 + if (n == 1 || n == 2) return cost[n] + // 初始化 dp 表,用于存储子问题的解 + val dp = IntArray(n + 1) + // 初始状态:预设最小子问题的解 + dp[1] = cost[1] + dp[2] = cost[2] + // 状态转移:从较小子问题逐步求解较大子问题 + for (i in 3..n) { + dp[i] = (min(dp[i - 1].toDouble(), dp[i - 2].toDouble()) + cost[i]).toInt() + } + return dp[n] + } + ``` + === "Zig" ```zig title="min_cost_climbing_stairs_dp.zig" @@ -522,6 +542,24 @@ $$ } ``` +=== "Kotlin" + + ```kotlin title="min_cost_climbing_stairs_dp.kt" + /* 爬楼梯最小代价:空间优化后的动态规划 */ + fun minCostClimbingStairsDPComp(cost: IntArray): Int { + val n = cost.size - 1 + if (n == 1 || n == 2) return cost[n] + var a = cost[1] + var b = cost[2] + for (i in 3..n) { + val tmp = b + b = (min(a.toDouble(), tmp.toDouble()) + cost[i]).toInt() + a = tmp + } + return b + } + ``` + === "Zig" ```zig title="min_cost_climbing_stairs_dp.zig" @@ -858,6 +896,30 @@ $$ } ``` +=== "Kotlin" + + ```kotlin title="climbing_stairs_constraint_dp.kt" + /* 带约束爬楼梯:动态规划 */ + fun climbingStairsConstraintDP(n: Int): Int { + if (n == 1 || n == 2) { + return 1 + } + // 初始化 dp 表,用于存储子问题的解 + val dp = Array(n + 1) { IntArray(3) } + // 初始状态:预设最小子问题的解 + dp[1][1] = 1 + dp[1][2] = 0 + dp[2][1] = 0 + dp[2][2] = 1 + // 状态转移:从较小子问题逐步求解较大子问题 + for (i in 3..n) { + dp[i][1] = dp[i - 1][2] + dp[i][2] = dp[i - 2][1] + dp[i - 2][2] + } + return dp[n][1] + dp[n][2] + } + ``` + === "Zig" ```zig title="climbing_stairs_constraint_dp.zig" diff --git a/docs/chapter_dynamic_programming/dp_solution_pipeline.md b/docs/chapter_dynamic_programming/dp_solution_pipeline.md index 8db036484..5502695aa 100644 --- a/docs/chapter_dynamic_programming/dp_solution_pipeline.md +++ b/docs/chapter_dynamic_programming/dp_solution_pipeline.md @@ -345,6 +345,31 @@ $$ } ``` +=== "Kotlin" + + ```kotlin title="min_path_sum.kt" + /* 最小路径和:暴力搜索 */ + fun minPathSumDFS( + grid: Array>, + i: Int, + j: Int + ): Int { + // 若为左上角单元格,则终止搜索 + if (i == 0 && j == 0) { + return grid[0][0] + } + // 若行列索引越界,则返回 +∞ 代价 + if (i < 0 || j < 0) { + return Int.MAX_VALUE + } + // 计算从左上角到 (i-1, j) 和 (i, j-1) 的最小路径代价 + 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() + } + ``` + === "Zig" ```zig title="min_path_sum.zig" @@ -675,6 +700,37 @@ $$ } ``` +=== "Kotlin" + + ```kotlin title="min_path_sum.kt" + /* 最小路径和:记忆化搜索 */ + fun minPathSumDFSMem( + grid: Array>, + mem: Array>, + i: Int, + j: Int + ): Int { + // 若为左上角单元格,则终止搜索 + if (i == 0 && j == 0) { + return grid[0][0] + } + // 若行列索引越界,则返回 +∞ 代价 + if (i < 0 || j < 0) { + return Int.MAX_VALUE + } + // 若已有记录,则直接返回 + if (mem[i][j] != -1) { + return mem[i][j] + } + // 左边和上边单元格的最小路径代价 + 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() + return mem[i][j] + } + ``` + === "Zig" ```zig title="min_path_sum.zig" @@ -1026,6 +1082,35 @@ $$ } ``` +=== "Kotlin" + + ```kotlin title="min_path_sum.kt" + /* 最小路径和:动态规划 */ + fun minPathSumDP(grid: Array>): Int { + val n = grid.size + val m = grid[0].size + // 初始化 dp 表 + val dp = Array(n) { IntArray(m) } + dp[0][0] = grid[0][0] + // 状态转移:首行 + for (j in 1..>): Int { + val n = grid.size + val m = grid[0].size + // 初始化 dp 表 + val dp = IntArray(m) + // 状态转移:首行 + dp[0] = grid[0][0] + for (j in 1.., + state: Int, + n: Int, + res: MutableList + ) { + // 当爬到第 n 阶时,方案数量加 1 + if (state == n) res[0] = res[0] + 1 + // 遍历所有选择 + for (choice in choices) { + // 剪枝:不允许越过第 n 阶 + if (state + choice > n) continue + // 尝试:做出选择,更新状态 + backtrack(choices, state + choice, n, res) + // 回退 + } + } + + /* 爬楼梯:回溯 */ + fun climbingStairsBacktrack(n: Int): Int { + val choices = mutableListOf(1, 2) // 可选择向上爬 1 阶或 2 阶 + val state = 0 // 从第 0 阶开始爬 + val res = ArrayList() + res.add(0) // 使用 res[0] 记录方案数量 + backtrack(choices, state, n, res) + return res[0] + } + ``` + === "Zig" ```zig title="climbing_stairs_backtrack.zig" @@ -629,6 +662,24 @@ $$ } ``` +=== "Kotlin" + + ```kotlin title="climbing_stairs_dfs.kt" + /* 搜索 */ + fun dfs(i: Int): Int { + // 已知 dp[1] 和 dp[2] ,返回之 + if (i == 1 || i == 2) return i + // dp[i] = dp[i-1] + dp[i-2] + val count = dfs(i - 1) + dfs(i - 2) + return count + } + + /* 爬楼梯:搜索 */ + fun climbingStairsDFS(n: Int): Int { + return dfs(n) + } + ``` + === "Zig" ```zig title="climbing_stairs_dfs.zig" @@ -967,6 +1018,31 @@ $$ } ``` +=== "Kotlin" + + ```kotlin title="climbing_stairs_dfs_mem.kt" + /* 记忆化搜索 */ + fun dfs(i: Int, mem: IntArray): Int { + // 已知 dp[1] 和 dp[2] ,返回之 + if (i == 1 || i == 2) return i + // 若存在记录 dp[i] ,则直接返回之 + if (mem[i] != -1) return mem[i] + // dp[i] = dp[i-1] + dp[i-2] + val count = dfs(i - 1, mem) + dfs(i - 2, mem) + // 记录 dp[i] + mem[i] = count + return count + } + + /* 爬楼梯:记忆化搜索 */ + fun climbingStairsDFSMem(n: Int): Int { + // mem[i] 记录爬到第 i 阶的方案总数,-1 代表无记录 + val mem = IntArray(n + 1) + Arrays.fill(mem, -1) + return dfs(n, mem) + } + ``` + === "Zig" ```zig title="climbing_stairs_dfs_mem.zig" @@ -1234,6 +1310,25 @@ $$ } ``` +=== "Kotlin" + + ```kotlin title="climbing_stairs_dp.kt" + /* 爬楼梯:动态规划 */ + fun climbingStairsDP(n: Int): Int { + if (n == 1 || n == 2) return n + // 初始化 dp 表,用于存储子问题的解 + val dp = IntArray(n + 1) + // 初始状态:预设最小子问题的解 + dp[1] = 1 + dp[2] = 2 + // 状态转移:从较小子问题逐步求解较大子问题 + for (i in 3..n) { + dp[i] = dp[i - 1] + dp[i - 2] + } + return dp[n] + } + ``` + === "Zig" ```zig title="climbing_stairs_dp.zig" @@ -1462,6 +1557,23 @@ $$ } ``` +=== "Kotlin" + + ```kotlin title="climbing_stairs_dp.kt" + /* 爬楼梯:空间优化后的动态规划 */ + fun climbingStairsDPComp(n: Int): Int { + if (n == 1 || n == 2) return n + var a = 1 + var b = 2 + for (i in 3..n) { + val tmp = b + b += a + a = tmp + } + return b + } + ``` + === "Zig" ```zig title="climbing_stairs_dp.zig" diff --git a/docs/chapter_dynamic_programming/knapsack_problem.md b/docs/chapter_dynamic_programming/knapsack_problem.md index 58f825393..c8ef3c9a9 100644 --- a/docs/chapter_dynamic_programming/knapsack_problem.md +++ b/docs/chapter_dynamic_programming/knapsack_problem.md @@ -295,6 +295,32 @@ $$ } ``` +=== "Kotlin" + + ```kotlin title="knapsack.kt" + /* 0-1 背包:暴力搜索 */ + fun knapsackDFS( + wgt: IntArray, + value: IntArray, + i: Int, + c: Int + ): Int { + // 若已选完所有物品或背包无剩余容量,则返回价值 0 + if (i == 0 || c == 0) { + return 0 + } + // 若超过背包容量,则只能选择不放入背包 + if (wgt[i - 1] > c) { + return knapsackDFS(wgt, value, i - 1, c) + } + // 计算不放入和放入物品 i 的最大价值 + 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() + } + ``` + === "Zig" ```zig title="knapsack.zig" @@ -633,6 +659,38 @@ $$ } ``` +=== "Kotlin" + + ```kotlin title="knapsack.kt" + /* 0-1 背包:记忆化搜索 */ + fun knapsackDFSMem( + wgt: IntArray, + value: IntArray, + mem: Array, + i: Int, + c: Int + ): Int { + // 若已选完所有物品或背包无剩余容量,则返回价值 0 + if (i == 0 || c == 0) { + return 0 + } + // 若已有记录,则直接返回 + if (mem[i][c] != -1) { + return mem[i][c] + } + // 若超过背包容量,则只能选择不放入背包 + if (wgt[i - 1] > c) { + return knapsackDFSMem(wgt, value, mem, i - 1, c) + } + // 计算不放入和放入物品 i 的最大价值 + 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() + return mem[i][c] + } + ``` + === "Zig" ```zig title="knapsack.zig" @@ -962,6 +1020,35 @@ $$ } ``` +=== "Kotlin" + + ```kotlin title="knapsack.kt" + /* 0-1 背包:动态规划 */ + fun knapsackDP( + wgt: IntArray, + value: IntArray, + cap: Int + ): Int { + val n = wgt.size + // 初始化 dp 表 + val dp = Array(n + 1) { IntArray(cap + 1) } + // 状态转移 + for (i in 1..n) { + for (c in 1..cap) { + if (wgt[i - 1] > c) { + // 若超过背包容量,则不选物品 i + 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() + } + } + } + return dp[n][cap] + } + ``` + === "Zig" ```zig title="knapsack.zig" @@ -1321,6 +1408,33 @@ $$ } ``` +=== "Kotlin" + + ```kotlin title="knapsack.kt" + /* 0-1 背包:空间优化后的动态规划 */ + fun knapsackDPComp( + wgt: IntArray, + value: IntArray, + cap: Int + ): Int { + val n = wgt.size + // 初始化 dp 表 + val dp = IntArray(cap + 1) + // 状态转移 + for (i in 1..n) { + // 倒序遍历 + for (c in cap downTo 1) { + if (wgt[i - 1] <= c) { + // 不选和选物品 i 这两种方案的较大值 + dp[c] = + max(dp[c].toDouble(), (dp[c - wgt[i - 1]] + value[i - 1]).toDouble()).toInt() + } + } + } + return dp[cap] + } + ``` + === "Zig" ```zig title="knapsack.zig" diff --git a/docs/chapter_dynamic_programming/unbounded_knapsack_problem.md b/docs/chapter_dynamic_programming/unbounded_knapsack_problem.md index aae7dd99f..42604d023 100644 --- a/docs/chapter_dynamic_programming/unbounded_knapsack_problem.md +++ b/docs/chapter_dynamic_programming/unbounded_knapsack_problem.md @@ -323,6 +323,35 @@ $$ } ``` +=== "Kotlin" + + ```kotlin title="unbounded_knapsack.kt" + /* 完全背包:动态规划 */ + fun unboundedKnapsackDP( + wgt: IntArray, + value: IntArray, + cap: Int + ): Int { + val n = wgt.size + // 初始化 dp 表 + val dp = Array(n + 1) { IntArray(cap + 1) } + // 状态转移 + for (i in 1..n) { + for (c in 1..cap) { + if (wgt[i - 1] > c) { + // 若超过背包容量,则不选物品 i + 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() + } + } + } + return dp[n][cap] + } + ``` + === "Zig" ```zig title="unbounded_knapsack.zig" @@ -648,6 +677,35 @@ $$ } ``` +=== "Kotlin" + + ```kotlin title="unbounded_knapsack.kt" + /* 完全背包:空间优化后的动态规划 */ + fun unboundedKnapsackDPComp( + wgt: IntArray, + value: IntArray, + cap: Int + ): Int { + val n = wgt.size + // 初始化 dp 表 + val dp = IntArray(cap + 1) + // 状态转移 + for (i in 1..n) { + for (c in 1..cap) { + if (wgt[i - 1] > c) { + // 若超过背包容量,则不选物品 i + dp[c] = dp[c] + } else { + // 不选和选物品 i 这两种方案的较大值 + dp[c] = + max(dp[c].toDouble(), (dp[c - wgt[i - 1]] + value[i - 1]).toDouble()).toInt() + } + } + } + return dp[cap] + } + ``` + === "Zig" ```zig title="unbounded_knapsack.zig" @@ -1063,6 +1121,36 @@ $$ } ``` +=== "Kotlin" + + ```kotlin title="coin_change.kt" + /* 零钱兑换:动态规划 */ + fun coinChangeDP(coins: IntArray, amt: Int): Int { + val n = coins.size + val MAX = amt + 1 + // 初始化 dp 表 + val dp = Array(n + 1) { IntArray(amt + 1) } + // 状态转移:首行首列 + for (a in 1..amt) { + dp[0][a] = MAX + } + // 状态转移:其余行和列 + for (i in 1..n) { + for (a in 1..amt) { + if (coins[i - 1] > a) { + // 若超过目标金额,则不选硬币 i + 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() + } + } + } + return if (dp[n][amt] != MAX) dp[n][amt] else -1 + } + ``` + === "Zig" ```zig title="coin_change.zig" @@ -1453,6 +1541,33 @@ $$ } ``` +=== "Kotlin" + + ```kotlin title="coin_change.kt" + /* 零钱兑换:空间优化后的动态规划 */ + fun coinChangeDPComp(coins: IntArray, amt: Int): Int { + val n = coins.size + val MAX = amt + 1 + // 初始化 dp 表 + val dp = IntArray(amt + 1) + Arrays.fill(dp, MAX) + dp[0] = 0 + // 状态转移 + for (i in 1..n) { + for (a in 1..amt) { + if (coins[i - 1] > a) { + // 若超过目标金额,则不选硬币 i + dp[a] = dp[a] + } else { + // 不选和选硬币 i 这两种方案的较小值 + dp[a] = min(dp[a].toDouble(), (dp[a - coins[i - 1]] + 1).toDouble()).toInt() + } + } + } + return if (dp[amt] != MAX) dp[amt] else -1 + } + ``` + === "Zig" ```zig title="coin_change.zig" @@ -1832,6 +1947,34 @@ $$ } ``` +=== "Kotlin" + + ```kotlin title="coin_change_ii.kt" + /* 零钱兑换 II:动态规划 */ + fun coinChangeIIDP(coins: IntArray, amt: Int): Int { + val n = coins.size + // 初始化 dp 表 + val dp = Array(n + 1) { IntArray(amt + 1) } + // 初始化首列 + for (i in 0..n) { + dp[i][0] = 1 + } + // 状态转移 + for (i in 1..n) { + for (a in 1..amt) { + if (coins[i - 1] > a) { + // 若超过目标金额,则不选硬币 i + dp[i][a] = dp[i - 1][a] + } else { + // 不选和选硬币 i 这两种方案之和 + dp[i][a] = dp[i - 1][a] + dp[i][a - coins[i - 1]] + } + } + } + return dp[n][amt] + } + ``` + === "Zig" ```zig title="coin_change_ii.zig" @@ -2145,6 +2288,31 @@ $$ } ``` +=== "Kotlin" + + ```kotlin title="coin_change_ii.kt" + /* 零钱兑换 II:空间优化后的动态规划 */ + fun coinChangeIIDPComp(coins: IntArray, amt: Int): Int { + val n = coins.size + // 初始化 dp 表 + val dp = IntArray(amt + 1) + dp[0] = 1 + // 状态转移 + for (i in 1..n) { + for (a in 1..amt) { + if (coins[i - 1] > a) { + // 若超过目标金额,则不选硬币 i + dp[a] = dp[a] + } else { + // 不选和选硬币 i 这两种方案之和 + dp[a] = dp[a] + dp[a - coins[i - 1]] + } + } + } + return dp[amt] + } + ``` + === "Zig" ```zig title="coin_change_ii.zig" diff --git a/docs/chapter_graph/graph_operations.md b/docs/chapter_graph/graph_operations.md index f6079aefd..1bbb43caf 100644 --- a/docs/chapter_graph/graph_operations.md +++ b/docs/chapter_graph/graph_operations.md @@ -1039,6 +1039,91 @@ comments: true } ``` +=== "Kotlin" + + ```kotlin title="graph_adjacency_matrix.kt" + /* 基于邻接矩阵实现的无向图类 */ + class GraphAdjMat(vertices: IntArray, edges: Array) { + val vertices: MutableList = ArrayList() // 顶点列表,元素代表“顶点值”,索引代表“顶点索引” + val adjMat: MutableList> = ArrayList() // 邻接矩阵,行列索引对应“顶点索引” + + /* 构造函数 */ + init { + // 添加顶点 + for (vertex in vertices) { + addVertex(vertex) + } + // 添加边 + // 请注意,edges 元素代表顶点索引,即对应 vertices 元素索引 + for (edge in edges) { + addEdge(edge[0], edge[1]) + } + } + + /* 获取顶点数量 */ + fun size(): Int { + return vertices.size + } + + /* 添加顶点 */ + fun addVertex(value: Int) { + val n = size() + // 向顶点列表中添加新顶点的值 + vertices.add(value) + // 在邻接矩阵中添加一行 + val newRow: MutableList = mutableListOf() + for (j in 0..= size()) throw IndexOutOfBoundsException() + // 在顶点列表中移除索引 index 的顶点 + vertices.removeAt(index) + // 在邻接矩阵中删除索引 index 的行 + adjMat.removeAt(index) + // 在邻接矩阵中删除索引 index 的列 + for (row in adjMat) { + row.removeAt(index) + } + } + + /* 添加边 */ + // 参数 i, j 对应 vertices 元素索引 + fun addEdge(i: Int, j: Int) { + // 索引越界与相等处理 + if (i < 0 || j < 0 || i >= size() || j >= size() || i == j) throw java.lang.IndexOutOfBoundsException() + // 在无向图中,邻接矩阵关于主对角线对称,即满足 (i, j) == (j, i) + adjMat[i][j] = 1; + adjMat[j][i] = 1; + } + + /* 删除边 */ + // 参数 i, j 对应 vertices 元素索引 + fun removeEdge(i: Int, j: Int) { + // 索引越界与相等处理 + if (i < 0 || j < 0 || i >= size() || j >= size() || i == j) throw java.lang.IndexOutOfBoundsException() + adjMat[i][j] = 0; + adjMat[j][i] = 0; + } + + /* 打印邻接矩阵 */ + fun print() { + print("顶点列表 = ") + println(vertices); + println("邻接矩阵 ="); + printMatrix(adjMat) + } + } + ``` + === "Zig" ```zig title="graph_adjacency_matrix.zig" @@ -2061,6 +2146,81 @@ comments: true } ``` +=== "Kotlin" + + ```kotlin title="graph_adjacency_list.kt" + /* 基于邻接表实现的无向图类 */ + class GraphAdjList(edges: Array>) { + // 邻接表,key:顶点,value:该顶点的所有邻接顶点 + val adjList: MutableMap> = HashMap() + + /* 构造函数 */ + init { + // 添加所有顶点和边 + for (edge in edges) { + addVertex(edge[0]!!); + addVertex(edge[1]!!); + addEdge(edge[0]!!, edge[1]!!); + } + } + + /* 获取顶点数量 */ + fun size(): Int { + return adjList.size + } + + /* 添加边 */ + fun addEdge(vet1: Vertex, vet2: Vertex) { + if (!adjList.containsKey(vet1) || !adjList.containsKey(vet2) || vet1 == vet2) + throw IllegalArgumentException() + // 添加边 vet1 - vet2 + adjList[vet1]?.add(vet2) + adjList[vet2]?.add(vet1); + } + + /* 删除边 */ + fun removeEdge(vet1: Vertex, vet2: Vertex) { + if (!adjList.containsKey(vet1) || !adjList.containsKey(vet2) || vet1 == vet2) + throw IllegalArgumentException() + // 删除边 vet1 - vet2 + adjList[vet1]?.remove(vet2); + adjList[vet2]?.remove(vet1); + } + + /* 添加顶点 */ + fun addVertex(vet: Vertex) { + if (adjList.containsKey(vet)) + return + // 在邻接表中添加一个新链表 + adjList[vet] = mutableListOf() + } + + /* 删除顶点 */ + fun removeVertex(vet: Vertex) { + if (!adjList.containsKey(vet)) + throw IllegalArgumentException() + // 在邻接表中删除顶点 vet 对应的链表 + adjList.remove(vet); + // 遍历其他顶点的链表,删除所有包含 vet 的边 + for (list in adjList.values) { + list.remove(vet) + } + } + + /* 打印邻接表 */ + fun print() { + println("邻接表 =") + for (pair in adjList.entries) { + val tmp = ArrayList() + for (vertex in pair.value) { + tmp.add(vertex.value) + } + println("${pair.key.value}: $tmp,") + } + } + } + ``` + === "Zig" ```zig title="graph_adjacency_list.zig" diff --git a/docs/chapter_graph/graph_traversal.md b/docs/chapter_graph/graph_traversal.md index 49337afcb..e055930c1 100644 --- a/docs/chapter_graph/graph_traversal.md +++ b/docs/chapter_graph/graph_traversal.md @@ -413,6 +413,37 @@ BFS 通常借助队列来实现,代码如下所示。队列具有“先入先 } ``` +=== "Kotlin" + + ```kotlin title="graph_bfs.kt" + /* 广度优先遍历 */ + // 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 + fun graphBFS(graph: GraphAdjList, startVet: Vertex): List { + // 顶点遍历序列 + val res: MutableList = ArrayList() + // 哈希表,用于记录已被访问过的顶点 + val visited: MutableSet = HashSet() + visited.add(startVet) + // 队列用于实现 BFS + val que: Queue = LinkedList() + que.offer(startVet) + // 以顶点 vet 为起点,循环直至访问完所有顶点 + while (!que.isEmpty()) { + val vet = que.poll() // 队首顶点出队 + res.add(vet) // 记录访问顶点 + // 遍历该顶点的所有邻接顶点 + for (adjVet in graph.adjList[vet]!!) { + if (visited.contains(adjVet)) continue // 跳过已被访问的顶点 + + que.offer(adjVet) // 只入队未访问的顶点 + visited.add(adjVet) // 标记该顶点已被访问 + } + } + // 返回顶点遍历序列 + return res + } + ``` + === "Zig" ```zig title="graph_bfs.zig" @@ -819,6 +850,41 @@ BFS 通常借助队列来实现,代码如下所示。队列具有“先入先 } ``` +=== "Kotlin" + + ```kotlin title="graph_dfs.kt" + /* 深度优先遍历辅助函数 */ + fun dfs( + graph: GraphAdjList, + visited: MutableSet, + res: MutableList, + vet: Vertex? + ) { + res.add(vet) // 记录访问顶点 + visited.add(vet) // 标记该顶点已被访问 + // 遍历该顶点的所有邻接顶点 + for (adjVet in graph.adjList[vet]!!) { + if (visited.contains(adjVet)) continue // 跳过已被访问的顶点 + // 递归访问邻接顶点 + dfs(graph, visited, res, adjVet) + } + } + + /* 深度优先遍历 */ + // 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 + fun graphDFS( + graph: GraphAdjList, + startVet: Vertex? + ): List { + // 顶点遍历序列 + val res: MutableList = ArrayList() + // 哈希表,用于记录已被访问过的顶点 + val visited: MutableSet = HashSet() + dfs(graph, visited, res, startVet) + return res + } + ``` + === "Zig" ```zig title="graph_dfs.zig" diff --git a/docs/chapter_greedy/fractional_knapsack_problem.md b/docs/chapter_greedy/fractional_knapsack_problem.md index 57736823d..5d15d877d 100644 --- a/docs/chapter_greedy/fractional_knapsack_problem.md +++ b/docs/chapter_greedy/fractional_knapsack_problem.md @@ -456,6 +456,78 @@ comments: true } ``` +=== "Kotlin" + + ```kotlin title="fractional_knapsack.kt" + /* 物品 */ + class Item( + val w: Int, // 物品 + val v: Int // 物品价值 + ) + + /* 分数背包:贪心 */ + fun fractionalKnapsack( + wgt: IntArray, + value: IntArray, + c: Int + ): Double { + // 创建物品列表,包含两个属性:重量、价值 + var cap = c + val items = arrayOfNulls(wgt.size) + for (i in wgt.indices) { + items[i] = Item(wgt[i], value[i]) + } + // 按照单位价值 item.v / item.w 从高到低进行排序 + Arrays.sort(items, Comparator.comparingDouble { item: Item -> -(item.v.toDouble() / item.w) }) + // 循环贪心选择 + var res = 0.0 + for (item in items) { + if (item!!.w <= cap) { + // 若剩余容量充足,则将当前物品整个装进背包 + res += item.v.toDouble() + cap -= item.w + } else { + // 若剩余容量不足,则将当前物品的一部分装进背包 + res += item.v.toDouble() / item.w * cap + // 已无剩余容量,因此跳出循环 + break + } + } + return res + } + + /* 分数背包:贪心 */ + fun fractionalKnapsack( + wgt: IntArray, + value: IntArray, + c: Int + ): Double { + // 创建物品列表,包含两个属性:重量、价值 + var cap = c + val items = arrayOfNulls(wgt.size) + for (i in wgt.indices) { + items[i] = Item(wgt[i], value[i]) + } + // 按照单位价值 item.v / item.w 从高到低进行排序 + Arrays.sort(items, Comparator.comparingDouble { item: Item -> -(item.v.toDouble() / item.w) }) + // 循环贪心选择 + var res = 0.0 + for (item in items) { + if (item!!.w <= cap) { + // 若剩余容量充足,则将当前物品整个装进背包 + res += item.v.toDouble() + cap -= item.w + } else { + // 若剩余容量不足,则将当前物品的一部分装进背包 + res += item.v.toDouble() / item.w * cap + // 已无剩余容量,因此跳出循环 + break + } + } + return res + } + ``` + === "Zig" ```zig title="fractional_knapsack.zig" diff --git a/docs/chapter_greedy/greedy_algorithm.md b/docs/chapter_greedy/greedy_algorithm.md index f9baf3968..b8880b360 100644 --- a/docs/chapter_greedy/greedy_algorithm.md +++ b/docs/chapter_greedy/greedy_algorithm.md @@ -283,6 +283,30 @@ comments: true } ``` +=== "Kotlin" + + ```kotlin title="coin_change_greedy.kt" + /* 零钱兑换:贪心 */ + fun coinChangeGreedy(coins: IntArray, amt: Int): Int { + // 假设 coins 列表有序 + var am = amt + var i = coins.size - 1 + var count = 0 + // 循环进行贪心选择,直到无剩余金额 + while (am > 0) { + // 找到小于且最接近剩余金额的硬币 + while (i > 0 && coins[i] > am) { + i-- + } + // 选择 coins[i] + am -= coins[i] + count++ + } + // 若未找到可行方案,则返回 -1 + return if (am == 0) count else -1 + } + ``` + === "Zig" ```zig title="coin_change_greedy.zig" diff --git a/docs/chapter_greedy/max_capacity_problem.md b/docs/chapter_greedy/max_capacity_problem.md index e1b20759f..07bc11803 100644 --- a/docs/chapter_greedy/max_capacity_problem.md +++ b/docs/chapter_greedy/max_capacity_problem.md @@ -368,6 +368,32 @@ $$ } ``` +=== "Kotlin" + + ```kotlin title="max_capacity.kt" + /* 最大容量:贪心 */ + fun maxCapacity(ht: IntArray): Int { + // 初始化 i, j,使其分列数组两端 + var i = 0 + var j = ht.size - 1 + // 初始最大容量为 0 + var res = 0 + // 循环贪心选择,直至两板相遇 + while (i < j) { + // 更新最大容量 + val cap = (min(ht[i].toDouble(), ht[j].toDouble()) * (j - i)).toInt() + res = max(res.toDouble(), cap.toDouble()).toInt() + // 向内移动短板 + if (ht[i] < ht[j]) { + i++ + } else { + j-- + } + } + return res + } + ``` + === "Zig" ```zig title="max_capacity.zig" diff --git a/docs/chapter_greedy/max_product_cutting_problem.md b/docs/chapter_greedy/max_product_cutting_problem.md index 9a74ba2f2..5eff8ffd0 100644 --- a/docs/chapter_greedy/max_product_cutting_problem.md +++ b/docs/chapter_greedy/max_product_cutting_problem.md @@ -343,6 +343,31 @@ $$ } ``` +=== "Kotlin" + + ```kotlin title="max_product_cutting.kt" + /* 最大切分乘积:贪心 */ + fun maxProductCutting(n: Int): Int { + // 当 n <= 3 时,必须切分出一个 1 + if (n <= 3) { + return 1 * (n - 1) + } + // 贪心地切分出 3 ,a 为 3 的个数,b 为余数 + val a = n / 3 + val b = n % 3 + if (b == 1) { + // 当余数为 1 时,将一对 1 * 3 转化为 2 * 2 + return 3.0.pow((a - 1).toDouble()).toInt() * 2 * 2 + } + if (b == 2) { + // 当余数为 2 时,不做处理 + return 3.0.pow(a.toDouble()).toInt() * 2 * 2 + } + // 当余数为 0 时,不做处理 + return 3.0.pow(a.toDouble()).toInt() + } + ``` + === "Zig" ```zig title="max_product_cutting.zig" diff --git a/docs/chapter_hashing/hash_algorithm.md b/docs/chapter_hashing/hash_algorithm.md index 8346c461d..53575f171 100644 --- a/docs/chapter_hashing/hash_algorithm.md +++ b/docs/chapter_hashing/hash_algorithm.md @@ -554,6 +554,46 @@ index = hash(key) % capacity } ``` +=== "Kotlin" + + ```kotlin title="simple_hash.kt" + /* 加法哈希 */ + fun addHash(key: String): Int { + var hash = 0L + for (c in key.toCharArray()) { + hash = (hash + c.code) % MODULUS + } + return hash.toInt() + } + + /* 乘法哈希 */ + fun mulHash(key: String): Int { + var hash = 0L + for (c in key.toCharArray()) { + hash = (31 * hash + c.code) % MODULUS + } + return hash.toInt() + } + + /* 异或哈希 */ + fun xorHash(key: String): Int { + var hash = 0 + for (c in key.toCharArray()) { + hash = hash xor c.code + } + return hash and MODULUS + } + + /* 旋转哈希 */ + fun rotHash(key: String): Int { + var hash = 0L + for (c in key.toCharArray()) { + hash = ((hash shl 4) xor (hash shr 28) xor c.code.toLong()) % MODULUS + } + return hash.toInt() + } + ``` + === "Zig" ```zig title="simple_hash.zig" @@ -868,6 +908,12 @@ $$ // C 未提供内置 hash code 函数 ``` +=== "Kotlin" + + ```kotlin title="built_in_hash.kt" + + ``` + === "Zig" ```zig title="built_in_hash.zig" diff --git a/docs/chapter_hashing/hash_collision.md b/docs/chapter_hashing/hash_collision.md index 208f93bc8..fa50ee408 100644 --- a/docs/chapter_hashing/hash_collision.md +++ b/docs/chapter_hashing/hash_collision.md @@ -1311,6 +1311,121 @@ comments: true } ``` +=== "Kotlin" + + ```kotlin title="hash_map_chaining.kt" + /* 链式地址哈希表 */ + class HashMapChaining() { + var size: Int // 键值对数量 + var capacity: Int // 哈希表容量 + val loadThres: Double // 触发扩容的负载因子阈值 + val extendRatio: Int // 扩容倍数 + var buckets: MutableList> // 桶数组 + + /* 构造方法 */ + init { + size = 0 + capacity = 4 + loadThres = 2.0 / 3.0 + extendRatio = 2 + buckets = ArrayList(capacity) + for (i in 0.. loadThres) { + extend() + } + val index = hashFunc(key) + val bucket = buckets[index] + // 遍历桶,若遇到指定 key ,则更新对应 val 并返回 + for (pair in bucket) { + if (pair.key == key) { + pair.value = value + return + } + } + // 若无该 key ,则将键值对添加至尾部 + val pair = Pair(key, value) + bucket.add(pair) + size++ + } + + /* 删除操作 */ + fun remove(key: Int) { + val index = hashFunc(key) + val bucket = buckets[index] + // 遍历桶,从中删除键值对 + for (pair in bucket) { + if (pair.key == key) { + bucket.remove(pair) + size-- + break + } + } + } + + /* 扩容哈希表 */ + fun extend() { + // 暂存原哈希表 + val bucketsTmp = buckets + // 初始化扩容后的新哈希表 + capacity *= extendRatio + // mutablelist 无固定大小 + buckets = mutableListOf() + for (i in 0..() + for (pair in bucket) { + val k = pair.key + val v = pair.value + res.add("$k -> $v") + } + println(res) + } + } + } + ``` + === "Zig" ```zig title="hash_map_chaining.zig" @@ -2831,6 +2946,132 @@ comments: true } ``` +=== "Kotlin" + + ```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 // 桶数组 + private val TOMBSTONE = Pair(-1, "-1") // 删除标记 + + /* 构造方法 */ + init { + buckets = arrayOfNulls(capacity) + } + + /* 哈希函数 */ + fun hashFunc(key: Int): Int { + return key % capacity + } + + /* 负载因子 */ + fun loadFactor(): Double { + return (size / capacity).toDouble() + } + + /* 搜索 key 对应的桶索引 */ + fun findBucket(key: Int): Int { + var index = hashFunc(key) + var firstTombstone = -1 + // 线性探测,当遇到空桶时跳出 + while (buckets[index] != null) { + // 若遇到 key ,返回对应的桶索引 + if (buckets[index]?.key == key) { + // 若之前遇到了删除标记,则将键值对移动至该索引处 + if (firstTombstone != -1) { + buckets[firstTombstone] = buckets[index] + buckets[index] = TOMBSTONE + return firstTombstone // 返回移动后的桶索引 + } + return index // 返回桶索引 + } + // 记录遇到的首个删除标记 + if (firstTombstone == -1 && buckets[index] == TOMBSTONE) { + firstTombstone = index + } + // 计算桶索引,越过尾部则返回头部 + index = (index + 1) % capacity + } + // 若 key 不存在,则返回添加点的索引 + return if (firstTombstone == -1) index else firstTombstone + } + + /* 查询操作 */ + fun get(key: Int): String? { + // 搜索 key 对应的桶索引 + val index = findBucket(key) + // 若找到键值对,则返回对应 val + if (buckets[index] != null && buckets[index] != TOMBSTONE) { + return buckets[index]?.value + } + // 若键值对不存在,则返回 null + return null + } + + /* 添加操作 */ + fun put(key: Int, value: String) { + // 当负载因子超过阈值时,执行扩容 + if (loadFactor() > loadThres) { + extend() + } + // 搜索 key 对应的桶索引 + val index = findBucket(key) + // 若找到键值对,则覆盖 val 并返回 + if (buckets[index] != null && buckets[index] != TOMBSTONE) { + buckets[index]!!.value = value + return + } + // 若键值对不存在,则添加该键值对 + buckets[index] = Pair(key, value) + size++ + } + + /* 删除操作 */ + fun remove(key: Int) { + // 搜索 key 对应的桶索引 + val index = findBucket(key) + // 若找到键值对,则用删除标记覆盖它 + if (buckets[index] != null && buckets[index] != TOMBSTONE) { + buckets[index] = TOMBSTONE + size-- + } + } + + /* 扩容哈希表 */ + fun extend() { + // 暂存原哈希表 + val bucketsTmp = buckets + // 初始化扩容后的新哈希表 + capacity *= extendRatio + buckets = arrayOfNulls(capacity) + size = 0 + // 将键值对从原哈希表搬运至新哈希表 + for (pair in bucketsTmp) { + if (pair != null && pair != TOMBSTONE) { + put(pair.key, pair.value) + } + } + } + + /* 打印哈希表 */ + fun print() { + for (pair in buckets) { + if (pair == null) { + println("null") + } else if (pair == TOMBSTONE) { + println("TOMESTOME") + } else { + println("${pair.key} -> ${pair.value}") + } + } + } + } + ``` + === "Zig" ```zig title="hash_map_open_addressing.zig" diff --git a/docs/chapter_hashing/hash_map.md b/docs/chapter_hashing/hash_map.md index 7b5be4185..0183aed51 100755 --- a/docs/chapter_hashing/hash_map.md +++ b/docs/chapter_hashing/hash_map.md @@ -277,6 +277,12 @@ comments: true // C 未提供内置哈希表 ``` +=== "Kotlin" + + ```kotlin title="hash_map.kt" + + ``` + === "Zig" ```zig title="hash_map.zig" @@ -473,6 +479,12 @@ comments: true // C 未提供内置哈希表 ``` +=== "Kotlin" + + ```kotlin title="hash_map.kt" + + ``` + === "Zig" ```zig title="hash_map.zig" @@ -1525,6 +1537,166 @@ index = hash(key) % capacity } ``` +=== "Kotlin" + + ```kotlin title="array_hash_map.kt" + /* 键值对 */ + class Pair( + var key: Int, + var value: String + ) + + /* 基于数组实现的哈希表 */ + class ArrayHashMap { + private val buckets = arrayOfNulls(100) + + init { + // 初始化数组,包含 100 个桶 + for (i in 0..<100) { + buckets[i] = null + } + } + + /* 哈希函数 */ + fun hashFunc(key: Int): Int { + val index = key % 100 + return index + } + + /* 查询操作 */ + fun get(key: Int): String? { + val index = hashFunc(key) + val pair = buckets[index] ?: return null + return pair.value + } + + /* 添加操作 */ + fun put(key: Int, value: String) { + val pair = Pair(key, value) + val index = hashFunc(key) + buckets[index] = pair + } + + /* 删除操作 */ + fun remove(key: Int) { + val index = hashFunc(key) + // 置为 null ,代表删除 + buckets[index] = null + } + + /* 获取所有键值对 */ + fun pairSet(): MutableList { + val pairSet = ArrayList() + for (pair in buckets) { + if (pair != null) pairSet.add(pair) + } + return pairSet + } + + /* 获取所有键 */ + fun keySet(): MutableList { + val keySet = ArrayList() + for (pair in buckets) { + if (pair != null) keySet.add(pair.key) + } + return keySet + } + + /* 获取所有值 */ + fun valueSet(): MutableList { + val valueSet = ArrayList() + for (pair in buckets) { + pair?.let { valueSet.add(it.value) } + } + return valueSet + } + + /* 打印哈希表 */ + fun print() { + for (kv in pairSet()) { + val key = kv.key + val value = kv.value + println("${key}->${value}") + } + } + } + + /* 基于数组实现的哈希表 */ + class ArrayHashMap { + private val buckets = arrayOfNulls(100) + + init { + // 初始化数组,包含 100 个桶 + for (i in 0..<100) { + buckets[i] = null + } + } + + /* 哈希函数 */ + fun hashFunc(key: Int): Int { + val index = key % 100 + return index + } + + /* 查询操作 */ + fun get(key: Int): String? { + val index = hashFunc(key) + val pair = buckets[index] ?: return null + return pair.value + } + + /* 添加操作 */ + fun put(key: Int, value: String) { + val pair = Pair(key, value) + val index = hashFunc(key) + buckets[index] = pair + } + + /* 删除操作 */ + fun remove(key: Int) { + val index = hashFunc(key) + // 置为 null ,代表删除 + buckets[index] = null + } + + /* 获取所有键值对 */ + fun pairSet(): MutableList { + val pairSet = ArrayList() + for (pair in buckets) { + if (pair != null) pairSet.add(pair) + } + return pairSet + } + + /* 获取所有键 */ + fun keySet(): MutableList { + val keySet = ArrayList() + for (pair in buckets) { + if (pair != null) keySet.add(pair.key) + } + return keySet + } + + /* 获取所有值 */ + fun valueSet(): MutableList { + val valueSet = ArrayList() + for (pair in buckets) { + pair?.let { valueSet.add(it.value) } + } + return valueSet + } + + /* 打印哈希表 */ + fun print() { + for (kv in pairSet()) { + val key = kv.key + val value = kv.value + println("${key}->${value}") + } + } + } + ``` + === "Zig" ```zig title="array_hash_map.zig" diff --git a/docs/chapter_heap/build_heap.md b/docs/chapter_heap/build_heap.md index 3539c07a5..728780dab 100644 --- a/docs/chapter_heap/build_heap.md +++ b/docs/chapter_heap/build_heap.md @@ -185,6 +185,126 @@ comments: true } ``` +=== "Kotlin" + + ```kotlin title="my_heap.kt" + /* 大顶堆 */ + class MaxHeap(nums: List?) { + // 使用列表而非数组,这样无须考虑扩容问题 + // 将列表元素原封不动添加进堆 + private val maxHeap = ArrayList(nums!!) + + /* 构造函数,根据输入列表建堆 */ + init { + // 堆化除叶节点以外的其他所有节点 + for (i in parent(size() - 1) downTo 0) { + siftDown(i) + } + } + + /* 获取左子节点的索引 */ + private fun left(i: Int): Int { + return 2 * i + 1 + } + + /* 获取右子节点的索引 */ + private fun right(i: Int): Int { + return 2 * i + 2 + } + + /* 获取父节点的索引 */ + private fun parent(i: Int): Int { + return (i - 1) / 2 // 向下整除 + } + + /* 交换元素 */ + private fun swap(i: Int, j: Int) { + maxHeap[i] = maxHeap[j].also { maxHeap[j] = maxHeap[i] } + } + + /* 获取堆大小 */ + fun size(): Int { + return maxHeap.size + } + + /* 判断堆是否为空 */ + fun isEmpty(): Boolean { + /* 判断堆是否为空 */ + return size() == 0 + } + + /* 访问堆顶元素 */ + fun peek(): Int { + return maxHeap[0] + } + + /* 元素入堆 */ + fun push(value: Int) { + // 添加节点 + maxHeap.add(value) + // 从底至顶堆化 + siftUp(size() - 1) + } + + /* 从节点 i 开始,从底至顶堆化 */ + private fun siftUp(it: Int) { + // Kotlin的函数参数不可变,因此创建临时变量 + var i = it + while (true) { + // 获取节点 i 的父节点 + val p = parent(i) + // 当“越过根节点”或“节点无须修复”时,结束堆化 + if (p < 0 || maxHeap[i] <= maxHeap[p]) break + // 交换两节点 + swap(i, p) + // 循环向上堆化 + i = p + } + } + + /* 元素出堆 */ + fun pop(): Int { + // 判空处理 + if (isEmpty()) throw IndexOutOfBoundsException() + // 交换根节点与最右叶节点(交换首元素与尾元素) + swap(0, size() - 1) + // 删除节点 + val value = maxHeap.removeAt(size() - 1) + // 从顶至底堆化 + siftDown(0) + // 返回堆顶元素 + return value + } + + /* 从节点 i 开始,从顶至底堆化 */ + private fun siftDown(it: Int) { + // Kotlin的函数参数不可变,因此创建临时变量 + var i = it + while (true) { + // 判断节点 i, l, r 中值最大的节点,记为 ma + val l = left(i) + val r = right(i) + var ma = i + if (l < size() && maxHeap[l] > maxHeap[ma]) ma = l + if (r < size() && maxHeap[r] > maxHeap[ma]) ma = r + // 若节点 i 最大或索引 l, r 越界,则无须继续堆化,跳出 + if (ma == i) break + // 交换两节点 + swap(i, ma) + // 循环向下堆化 + i = ma + } + } + + /* 打印堆(二叉树) */ + fun print() { + val queue = PriorityQueue { a: Int, b: Int -> b - a } + queue.addAll(maxHeap) + printHeap(queue) + } + } + ``` + === "Zig" ```zig title="my_heap.zig" diff --git a/docs/chapter_heap/heap.md b/docs/chapter_heap/heap.md index 33bf15032..3d0277c55 100644 --- a/docs/chapter_heap/heap.md +++ b/docs/chapter_heap/heap.md @@ -375,6 +375,12 @@ comments: true // C 未提供内置 Heap 类 ``` +=== "Kotlin" + + ```kotlin title="heap.kt" + + ``` + === "Zig" ```zig title="heap.zig" @@ -611,6 +617,25 @@ comments: true } ``` +=== "Kotlin" + + ```kotlin title="my_heap.kt" + /* 获取左子节点的索引 */ + fun left(i: Int): Int { + return 2 * i + 1 + } + + /* 获取右子节点的索引 */ + fun right(i: Int): Int { + return 2 * i + 2 + } + + /* 获取父节点的索引 */ + fun parent(i: Int): Int { + return (i - 1) / 2 // 向下整除 + } + ``` + === "Zig" ```zig title="my_heap.zig" @@ -733,6 +758,15 @@ comments: true } ``` +=== "Kotlin" + + ```kotlin title="my_heap.kt" + /* 访问堆顶元素 */ + fun peek(): Int { + return maxHeap[0] + } + ``` + === "Zig" ```zig title="my_heap.zig" @@ -1093,6 +1127,34 @@ comments: true } ``` +=== "Kotlin" + + ```kotlin title="my_heap.kt" + /* 元素入堆 */ + fun push(value: Int) { + // 添加节点 + maxHeap.add(value) + // 从底至顶堆化 + siftUp(size() - 1) + } + + /* 从节点 i 开始,从底至顶堆化 */ + fun siftUp(it: Int) { + // Kotlin的函数参数不可变,因此创建临时变量 + var i = it + while (true) { + // 获取节点 i 的父节点 + val p = parent(i) + // 当“越过根节点”或“节点无须修复”时,结束堆化 + if (p < 0 || maxHeap[i] <= maxHeap[p]) break + // 交换两节点 + swap(i, p) + // 循环向上堆化 + i = p + } + } + ``` + === "Zig" ```zig title="my_heap.zig" @@ -1603,6 +1665,44 @@ comments: true } ``` +=== "Kotlin" + + ```kotlin title="my_heap.kt" + /* 元素出堆 */ + fun pop(): Int { + // 判空处理 + if (isEmpty()) throw IndexOutOfBoundsException() + // 交换根节点与最右叶节点(交换首元素与尾元素) + swap(0, size() - 1) + // 删除节点 + val value = maxHeap.removeAt(size() - 1) + // 从顶至底堆化 + siftDown(0) + // 返回堆顶元素 + return value + } + + /* 从节点 i 开始,从顶至底堆化 */ + fun siftDown(it: Int) { + // Kotlin的函数参数不可变,因此创建临时变量 + var i = it + while (true) { + // 判断节点 i, l, r 中值最大的节点,记为 ma + val l = left(i) + val r = right(i) + var ma = i + if (l < size() && maxHeap[l] > maxHeap[ma]) ma = l + if (r < size() && maxHeap[r] > maxHeap[ma]) ma = r + // 若节点 i 最大或索引 l, r 越界,则无须继续堆化,跳出 + if (ma == i) break + // 交换两节点 + swap(i, ma) + // 循环向下堆化 + i = ma + } + } + ``` + === "Zig" ```zig title="my_heap.zig" diff --git a/docs/chapter_heap/top_k.md b/docs/chapter_heap/top_k.md index 0671ac155..ca4e54a67 100644 --- a/docs/chapter_heap/top_k.md +++ b/docs/chapter_heap/top_k.md @@ -411,6 +411,29 @@ comments: true } ``` +=== "Kotlin" + + ```kotlin title="top_k.kt" + /* 基于堆查找数组中最大的 k 个元素 */ + fun topKHeap(nums: IntArray, k: Int): Queue { + // 初始化小顶堆 + val heap = PriorityQueue() + // 将数组的前 k 个元素入堆 + for (i in 0.. heap.peek()) { + heap.poll() + heap.offer(nums[i]) + } + } + return heap + } + ``` + === "Zig" ```zig title="top_k.zig" diff --git a/docs/chapter_preface/suggestions.md b/docs/chapter_preface/suggestions.md index 64c001e92..ee744419c 100644 --- a/docs/chapter_preface/suggestions.md +++ b/docs/chapter_preface/suggestions.md @@ -160,6 +160,19 @@ comments: true */ ``` +=== "Kotlin" + + ```kotlin title="" + /* 标题注释,用于标注函数、类、测试样例等 */ + + // 内容注释,用于详解代码 + + /** + * 多行 + * 注释 + */ + ``` + === "Zig" ```zig title="" diff --git a/docs/chapter_searching/binary_search.md b/docs/chapter_searching/binary_search.md index ed76ab8e5..bad38bad4 100755 --- a/docs/chapter_searching/binary_search.md +++ b/docs/chapter_searching/binary_search.md @@ -313,6 +313,29 @@ comments: true } ``` +=== "Kotlin" + + ```kotlin title="binary_search.kt" + /* 二分查找(双闭区间) */ + fun binarySearch(nums: IntArray, target: Int): Int { + // 初始化双闭区间 [0, n-1] ,即 i, j 分别指向数组首元素、尾元素 + var i = 0 + var j = nums.size - 1 + // 循环,当搜索区间为空时跳出(当 i > j 时为空) + while (i <= j) { + val m = i + (j - i) / 2 // 计算中点索引 m + if (nums[m] < target) // 此情况说明 target 在区间 [m+1, j] 中 + i = m + 1 + else if (nums[m] > target) // 此情况说明 target 在区间 [i, m-1] 中 + j = m - 1 + else // 找到目标元素,返回其索引 + return m + } + // 未找到目标元素,返回 -1 + return -1 + } + ``` + === "Zig" ```zig title="binary_search.zig" @@ -612,6 +635,29 @@ comments: true } ``` +=== "Kotlin" + + ```kotlin title="binary_search.kt" + /* 二分查找(左闭右开区间) */ + fun binarySearchLCRO(nums: IntArray, target: Int): Int { + // 初始化左闭右开区间 [0, n) ,即 i, j 分别指向数组首元素、尾元素+1 + var i = 0 + var j = nums.size + // 循环,当搜索区间为空时跳出(当 i = j 时为空) + while (i < j) { + val m = i + (j - i) / 2 // 计算中点索引 m + if (nums[m] < target) // 此情况说明 target 在区间 [m+1, j) 中 + i = m + 1 + else if (nums[m] > target) // 此情况说明 target 在区间 [i, m) 中 + j = m + else // 找到目标元素,返回其索引 + return m + } + // 未找到目标元素,返回 -1 + return -1 + } + ``` + === "Zig" ```zig title="binary_search.zig" diff --git a/docs/chapter_searching/binary_search_edge.md b/docs/chapter_searching/binary_search_edge.md index a722cc647..adcb02c26 100644 --- a/docs/chapter_searching/binary_search_edge.md +++ b/docs/chapter_searching/binary_search_edge.md @@ -193,6 +193,22 @@ comments: true } ``` +=== "Kotlin" + + ```kotlin title="binary_search_edge.kt" + /* 二分查找最左一个 target */ + fun binarySearchLeftEdge(nums: IntArray, target: Int): Int { + // 等价于查找 target 的插入点 + val i = binarySearchInsertion(nums, target) + // 未找到 target ,返回 -1 + if (i == nums.size || nums[i] != target) { + return -1 + } + // 找到 target ,返回索引 i + return i + } + ``` + === "Zig" ```zig title="binary_search_edge.zig" @@ -418,6 +434,24 @@ comments: true } ``` +=== "Kotlin" + + ```kotlin title="binary_search_edge.kt" + /* 二分查找最右一个 target */ + fun binarySearchRightEdge(nums: IntArray, target: Int): Int { + // 转化为查找最左一个 target + 1 + val i = binarySearchInsertion(nums, target + 1) + // j 指向最右一个 target ,i 指向首个大于 target 的元素 + val j = i - 1 + // 未找到 target ,返回 -1 + if (j == -1 || nums[j] != target) { + return -1 + } + // 找到 target ,返回索引 j + return j + } + ``` + === "Zig" ```zig title="binary_search_edge.zig" diff --git a/docs/chapter_searching/binary_search_insertion.md b/docs/chapter_searching/binary_search_insertion.md index 651e8754f..91dcf6c04 100644 --- a/docs/chapter_searching/binary_search_insertion.md +++ b/docs/chapter_searching/binary_search_insertion.md @@ -268,6 +268,28 @@ comments: true } ``` +=== "Kotlin" + + ```kotlin title="binary_search_insertion.kt" + /* 二分查找插入点(无重复元素) */ + fun binarySearchInsertionSimple(nums: IntArray, target: Int): Int { + var i = 0 + var j = nums.size - 1 // 初始化双闭区间 [0, n-1] + while (i <= j) { + val m = i + (j - i) / 2 // 计算中点索引 m + if (nums[m] < target) { + i = m + 1 // target 在区间 [m+1, j] 中 + } else if (nums[m] > target) { + j = m - 1 // target 在区间 [i, m-1] 中 + } else { + return m // 找到 target ,返回插入点 m + } + } + // 未找到 target ,返回插入点 i + return i + } + ``` + === "Zig" ```zig title="binary_search_insertion.zig" @@ -572,6 +594,28 @@ comments: true } ``` +=== "Kotlin" + + ```kotlin title="binary_search_insertion.kt" + /* 二分查找插入点(存在重复元素) */ + fun binarySearchInsertion(nums: IntArray, target: Int): Int { + var i = 0 + var j = nums.size - 1 // 初始化双闭区间 [0, n-1] + while (i <= j) { + val m = i + (j - i) / 2 // 计算中点索引 m + if (nums[m] < target) { + i = m + 1 // target 在区间 [m+1, j] 中 + } else if (nums[m] > target) { + j = m - 1 // target 在区间 [i, m-1] 中 + } else { + j = m - 1 // 首个小于 target 的元素在区间 [i, m-1] 中 + } + } + // 返回插入点 i + return i + } + ``` + === "Zig" ```zig title="binary_search_insertion.zig" diff --git a/docs/chapter_searching/replace_linear_by_hashing.md b/docs/chapter_searching/replace_linear_by_hashing.md index 190f79322..c179231e2 100755 --- a/docs/chapter_searching/replace_linear_by_hashing.md +++ b/docs/chapter_searching/replace_linear_by_hashing.md @@ -209,6 +209,22 @@ comments: true } ``` +=== "Kotlin" + + ```kotlin title="two_sum.kt" + /* 方法一:暴力枚举 */ + fun twoSumBruteForce(nums: IntArray, target: Int): IntArray { + val size = nums.size + // 两层循环,时间复杂度为 O(n^2) + for (i in 0..() + // 单层循环,时间复杂度为 O(n) + for (i in 0.. nums[j + 1]) { + // 交换 nums[j] 与 nums[j + 1] + nums[j] = nums[j+1].also { nums[j+1] = nums[j] } + } + } + } + } + ``` + === "Zig" ```zig title="bubble_sort.zig" @@ -535,6 +553,27 @@ comments: true } ``` +=== "Kotlin" + + ```kotlin title="bubble_sort.kt" + /* 冒泡排序(标志优化) */ + fun bubbleSortWithFlag(nums: IntArray) { + // 外循环:未排序区间为 [0, i] + for (i in nums.size - 1 downTo 1) { + var flag = false // 初始化标志位 + // 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端 + for (j in 0.. nums[j + 1]) { + // 交换 nums[j] 与 nums[j + 1] + nums[j] = nums[j + 1].also { nums[j] = nums[j + 1] } + flag = true // 记录交换元素 + } + } + if (!flag) break // 此轮“冒泡”未交换任何元素,直接跳出 + } + } + ``` + === "Zig" ```zig title="bubble_sort.zig" diff --git a/docs/chapter_sorting/bucket_sort.md b/docs/chapter_sorting/bucket_sort.md index 828deea8f..318e3738c 100644 --- a/docs/chapter_sorting/bucket_sort.md +++ b/docs/chapter_sorting/bucket_sort.md @@ -388,6 +388,39 @@ comments: true } ``` +=== "Kotlin" + + ```kotlin title="bucket_sort.kt" + /* 桶排序 */ + fun bucketSort(nums: FloatArray) { + // 初始化 k = n/2 个桶,预期向每个桶分配 2 个元素 + val k = nums.size / 2 + val buckets = ArrayList>() + for (i in 0.. nums[ma]) ma = l + if (r < n && nums[r] > nums[ma]) ma = r + // 若节点 i 最大或索引 l, r 越界,则无须继续堆化,跳出 + if (ma == i) break + // 交换两节点 + nums[i] = nums[ma].also { nums[ma] = nums[i] } + // 循环向下堆化 + i = ma + } + } + + /* 堆排序 */ + fun heapSort(nums: IntArray) { + // 建堆操作:堆化除叶节点以外的其他所有节点 + for (i in nums.size / 2 - 1 downTo 0) { + siftDown(nums, nums.size, i) + } + // 从堆中提取最大元素,循环 n-1 轮 + for (i in nums.size - 1 downTo 1) { + // 交换根节点与最右叶节点(交换首元素与尾元素) + nums[0] = nums[i].also { nums[i] = nums[0] } + // 以根节点为起点,从顶至底进行堆化 + siftDown(nums, i, 0) + } + } + ``` + === "Zig" ```zig title="heap_sort.zig" diff --git a/docs/chapter_sorting/insertion_sort.md b/docs/chapter_sorting/insertion_sort.md index 145853d83..44c33c237 100755 --- a/docs/chapter_sorting/insertion_sort.md +++ b/docs/chapter_sorting/insertion_sort.md @@ -231,6 +231,25 @@ comments: true } ``` +=== "Kotlin" + + ```kotlin title="insertion_sort.kt" + /* 插入排序 */ + fun insertionSort(nums: IntArray) { + //外循环: 已排序元素为 1, 2, ..., n + for (i in nums.indices) { + val base = nums[i] + var j = i - 1 + // 内循环: 将 base 插入到已排序部分的正确位置 + while (j >= 0 && nums[j] > base) { + nums[j + 1] = nums[j] // 将 nums[j] 向右移动一位 + j-- + } + nums[j + 1] = base // 将 base 赋值到正确位置 + } + } + ``` + === "Zig" ```zig title="insertion_sort.zig" diff --git a/docs/chapter_sorting/merge_sort.md b/docs/chapter_sorting/merge_sort.md index 6a2dcfa9d..542e7b875 100755 --- a/docs/chapter_sorting/merge_sort.md +++ b/docs/chapter_sorting/merge_sort.md @@ -582,6 +582,49 @@ comments: true } ``` +=== "Kotlin" + + ```kotlin title="merge_sort.kt" + /* 合并左子数组和右子数组 */ + fun merge(nums: IntArray, left: Int, mid: Int, right: Int) { + // 左子数组区间为 [left, mid], 右子数组区间为 [mid+1, right] + // 创建一个临时数组 tmp ,用于存放合并后的结果 + val tmp = IntArray(right - left + 1) + // 初始化左子数组和右子数组的起始索引 + var i = left + var j = mid + 1 + var k = 0 + // 当左右子数组都还有元素时,进行比较并将较小的元素复制到临时数组中 + while (i <= mid && j <= right) { + if (nums[i] <= nums[j]) tmp[k++] = nums[i++] + else tmp[k++] = nums[j++] + } + // 将左子数组和右子数组的剩余元素复制到临时数组中 + while (i <= mid) { + tmp[k++] = nums[i++] + } + while (j <= right) { + tmp[k++] = nums[j++] + } + // 将临时数组 tmp 中的元素复制回原数组 nums 的对应区间 + for (l in tmp.indices) { + nums[left + l] = tmp[l] + } + } + + /* 归并排序 */ + fun mergeSort(nums: IntArray, left: Int, right: Int) { + // 终止条件 + if (left >= right) return // 当子数组长度为 1 时终止递归 + // 划分阶段 + val mid = (left + right) / 2 // 计算中点 + mergeSort(nums, left, mid) // 递归左子数组 + mergeSort(nums, mid + 1, right) // 递归右子数组 + // 合并阶段 + merge(nums, left, mid, right) + } + ``` + === "Zig" ```zig title="merge_sort.zig" diff --git a/docs/chapter_sorting/quick_sort.md b/docs/chapter_sorting/quick_sort.md index 6da5c2e74..a191de793 100755 --- a/docs/chapter_sorting/quick_sort.md +++ b/docs/chapter_sorting/quick_sort.md @@ -326,6 +326,31 @@ comments: true } ``` +=== "Kotlin" + + ```kotlin title="quick_sort.kt" + /* 元素交换 */ + fun swap(nums: IntArray, i: Int, j: Int) { + nums[i] = nums[j].also { nums[j] = nums[i] } + } + + /* 哨兵划分 */ + fun partition(nums: IntArray, left: Int, right: Int): Int { + // 以 nums[left] 为基准数 + var i = left + var j = right + while (i < j) { + while (i < j && nums[j] >= nums[left]) + j-- // 从右向左找首个小于基准数的元素 + while (i < j && nums[i] <= nums[left]) + i++ // 从左向右找首个大于基准数的元素 + swap(nums, i, j) // 交换这两个元素 + } + swap(nums, i, left) // 将基准数交换至两子数组的分界线 + return i // 返回基准数的索引 + } + ``` + === "Zig" ```zig title="quick_sort.zig" @@ -569,6 +594,21 @@ comments: true } ``` +=== "Kotlin" + + ```kotlin title="quick_sort.kt" + /* 快速排序 */ + fun quickSort(nums: IntArray, left: Int, right: Int) { + // 子数组长度为 1 时终止递归 + if (left >= right) return + // 哨兵划分 + val pivot = partition(nums, left, right) + // 递归左子数组、右子数组 + quickSort(nums, left, pivot - 1) + quickSort(nums, pivot + 1, right) + } + ``` + === "Zig" ```zig title="quick_sort.zig" @@ -1001,6 +1041,38 @@ comments: true } ``` +=== "Kotlin" + + ```kotlin title="quick_sort.kt" + /* 选取三个候选元素的中位数 */ + fun medianThree(nums: IntArray, left: Int, mid: Int, right: Int): Int { + val l = nums[left] + val m = nums[mid] + val r = nums[right] + if ((m in l..r) || (m in r..l)) + return mid // m 在 l 和 r 之间 + if ((l in m..r) || (l in r..m)) + return left // l 在 m 和 r 之间 + return right + } + + /* 哨兵划分 */ + fun partition(nums: IntArray, left: Int, right: Int): Int { + // 以 nums[left] 为基准数 + var i = left + var j = right + while (i < j) { + while (i < j && nums[j] >= nums[left]) + j-- // 从右向左找首个小于基准数的元素 + while (i < j && nums[i] <= nums[left]) + i++ // 从左向右找首个大于基准数的元素 + swap(nums, i, j) // 交换这两个元素 + } + swap(nums, i, left) // 将基准数交换至两子数组的分界线 + return i // 返回基准数的索引 + } + ``` + === "Zig" ```zig title="quick_sort.zig" @@ -1277,6 +1349,21 @@ comments: true } ``` +=== "Kotlin" + + ```kotlin title="quick_sort.kt" + /* 快速排序 */ + fun quickSort(nums: IntArray, left: Int, right: Int) { + // 子数组长度为 1 时终止递归 + if (left >= right) return + // 哨兵划分 + val pivot = partition(nums, left, right) + // 递归左子数组、右子数组 + quickSort(nums, left, pivot - 1) + quickSort(nums, pivot + 1, right) + } + ``` + === "Zig" ```zig title="quick_sort.zig" diff --git a/docs/chapter_sorting/radix_sort.md b/docs/chapter_sorting/radix_sort.md index 18a5a792f..53e534291 100644 --- a/docs/chapter_sorting/radix_sort.md +++ b/docs/chapter_sorting/radix_sort.md @@ -619,6 +619,59 @@ $$ } ``` +=== "Kotlin" + + ```kotlin title="radix_sort.kt" + /* 获取元素 num 的第 k 位,其中 exp = 10^(k-1) */ + fun digit(num: Int, exp: Int): Int { + // 传入 exp 而非 k 可以避免在此重复执行昂贵的次方计算 + return (num / exp) % 10 + } + + /* 计数排序(根据 nums 第 k 位排序) */ + fun countingSortDigit(nums: IntArray, exp: Int) { + // 十进制的位范围为 0~9 ,因此需要长度为 10 的桶数组 + val counter = IntArray(10) + val n = nums.size + // 统计 0~9 各数字的出现次数 + for (i in 0.. m) m = num + var exp = 1 + // 按照从低位到高位的顺序遍历 + while (exp <= m) { + // 对数组元素的第 k 位执行计数排序 + // k = 1 -> exp = 1 + // k = 2 -> exp = 10 + // 即 exp = 10^(k-1) + countingSortDigit(nums, exp) + exp *= 10 + } + } + ``` + === "Zig" ```zig title="radix_sort.zig" diff --git a/docs/chapter_sorting/selection_sort.md b/docs/chapter_sorting/selection_sort.md index 3e3702fb4..5a35ac2e9 100644 --- a/docs/chapter_sorting/selection_sort.md +++ b/docs/chapter_sorting/selection_sort.md @@ -281,6 +281,25 @@ comments: true } ``` +=== "Kotlin" + + ```kotlin title="selection_sort.kt" + /* 选择排序 */ + fun selectionSort(nums: IntArray) { + val n = nums.size + // 外循环:未排序区间为 [i, n-1] + for (i in 0..() + + /* 获取栈的长度 */ + fun size(): Int { + return stack.size + } + + /* 判断栈是否为空 */ + fun isEmpty(): Boolean { + return size() == 0 + } + + /* 入栈 */ + fun push(num: Int) { + stack.add(num) + } + + /* 出栈 */ + fun pop(): Int { + if (isEmpty()) throw IndexOutOfBoundsException() + return stack.removeAt(size() - 1) + } + + /* 访问栈顶元素 */ + fun peek(): Int { + if (isEmpty()) throw IndexOutOfBoundsException() + return stack[size() - 1] + } + + /* 将 List 转化为 Array 并返回 */ + fun toArray(): Array { + return stack.toArray() + } + } + ``` + === "Zig" ```zig title="array_stack.zig" diff --git a/docs/chapter_tree/array_representation_of_tree.md b/docs/chapter_tree/array_representation_of_tree.md index 4515667c1..89b3d909b 100644 --- a/docs/chapter_tree/array_representation_of_tree.md +++ b/docs/chapter_tree/array_representation_of_tree.md @@ -120,6 +120,12 @@ comments: true int tree[] = {1, 2, 3, 4, INT_MAX, 6, 7, 8, 9, INT_MAX, INT_MAX, 12, INT_MAX, INT_MAX, 15}; ``` +=== "Kotlin" + + ```kotlin title="" + + ``` + === "Zig" ```zig title="" @@ -1155,6 +1161,85 @@ comments: true } ``` +=== "Kotlin" + + ```kotlin title="array_binary_tree.kt" + /* 数组表示下的二叉树类 */ + class ArrayBinaryTree(val tree: List) { + /* 列表容量 */ + fun size(): Int { + return tree.size + } + + /* 获取索引为 i 节点的值 */ + fun value(i: Int): Int? { + // 若索引越界,则返回 null ,代表空位 + if (i < 0 || i >= size()) return null + return tree[i] + } + + /* 获取索引为 i 节点的左子节点的索引 */ + fun left(i: Int): Int { + return 2 * i + 1 + } + + /* 获取索引为 i 节点的右子节点的索引 */ + fun right(i: Int): Int { + return 2 * i + 2 + } + + /* 获取索引为 i 节点的父节点的索引 */ + fun parent(i: Int): Int { + return (i - 1) / 2 + } + + /* 层序遍历 */ + fun levelOrder(): List { + val res = ArrayList() + // 直接遍历数组 + for (i in 0..) { + // 若为空位,则返回 + if (value(i) == null) return + // 前序遍历 + if ("pre" == order) res.add(value(i)) + dfs(left(i), order, res) + // 中序遍历 + if ("in" == order) res.add(value(i)) + dfs(right(i), order, res) + // 后序遍历 + if ("post" == order) res.add(value(i)) + } + + /* 前序遍历 */ + fun preOrder(): List { + val res = ArrayList() + dfs(0, "pre", res) + return res + } + + /* 中序遍历 */ + fun inOrder(): List { + val res = ArrayList() + dfs(0, "in", res) + return res + } + + /* 后序遍历 */ + fun postOrder(): List { + val res = ArrayList() + dfs(0, "post", res) + return res + } + } + ``` + === "Zig" ```zig title="array_binary_tree.zig" diff --git a/docs/chapter_tree/avl_tree.md b/docs/chapter_tree/avl_tree.md index 2e95f1649..06d26d2d1 100644 --- a/docs/chapter_tree/avl_tree.md +++ b/docs/chapter_tree/avl_tree.md @@ -208,6 +208,12 @@ AVL 树既是二叉搜索树,也是平衡二叉树,同时满足这两类二 } ``` +=== "Kotlin" + + ```kotlin title="" + + ``` + === "Zig" ```zig title="" @@ -419,6 +425,22 @@ AVL 树既是二叉搜索树,也是平衡二叉树,同时满足这两类二 } ``` +=== "Kotlin" + + ```kotlin title="avl_tree.kt" + /* 获取节点高度 */ + fun height(node: TreeNode?): Int { + // 空节点高度为 -1 ,叶节点高度为 0 + return node?.height ?: -1 + } + + /* 更新节点高度 */ + fun updateHeight(node: TreeNode?) { + // 节点高度等于最高子树高度 + 1 + node?.height = (max(height(node?.left).toDouble(), height(node?.right).toDouble()) + 1).toInt() + } + ``` + === "Zig" ```zig title="avl_tree.zig" @@ -582,6 +604,18 @@ AVL 树既是二叉搜索树,也是平衡二叉树,同时满足这两类二 } ``` +=== "Kotlin" + + ```kotlin title="avl_tree.kt" + /* 获取平衡因子 */ + fun balanceFactor(node: TreeNode?): Int { + // 空节点平衡因子为 0 + if (node == null) return 0 + // 节点平衡因子 = 左子树高度 - 右子树高度 + return height(node.left) - height(node.right) + } + ``` + === "Zig" ```zig title="avl_tree.zig" @@ -833,6 +867,24 @@ AVL 树的特点在于“旋转”操作,它能够在不影响二叉树的中 } ``` +=== "Kotlin" + + ```kotlin title="avl_tree.kt" + /* 右旋操作 */ + fun rightRotate(node: TreeNode?): TreeNode { + val child = node!!.left + val grandChild = child!!.right + // 以 child 为原点,将 node 向右旋转 + child.right = node + node.left = grandChild + // 更新节点高度 + updateHeight(node) + updateHeight(child) + // 返回旋转后子树的根节点 + return child + } + ``` + === "Zig" ```zig title="avl_tree.zig" @@ -1070,6 +1122,24 @@ AVL 树的特点在于“旋转”操作,它能够在不影响二叉树的中 } ``` +=== "Kotlin" + + ```kotlin title="avl_tree.kt" + /* 左旋操作 */ + fun leftRotate(node: TreeNode?): TreeNode { + val child = node!!.right + val grandChild = child!!.left + // 以 child 为原点,将 node 向左旋转 + child.left = node + node.right = grandChild + // 更新节点高度 + updateHeight(node) + updateHeight(child) + // 返回旋转后子树的根节点 + return child + } + ``` + === "Zig" ```zig title="avl_tree.zig" @@ -1504,6 +1574,40 @@ AVL 树的特点在于“旋转”操作,它能够在不影响二叉树的中 } ``` +=== "Kotlin" + + ```kotlin title="avl_tree.kt" + /* 执行旋转操作,使该子树重新恢复平衡 */ + fun rotate(node: TreeNode): TreeNode { + // 获取节点 node 的平衡因子 + val balanceFactor = balanceFactor(node) + // 左偏树 + if (balanceFactor > 1) { + if (balanceFactor(node.left) >= 0) { + // 右旋 + return rightRotate(node) + } else { + // 先左旋后右旋 + node.left = leftRotate(node.left) + return rightRotate(node) + } + } + // 右偏树 + if (balanceFactor < -1) { + if (balanceFactor(node.right) <= 0) { + // 左旋 + return leftRotate(node) + } else { + // 先右旋后左旋 + node.right = rightRotate(node.right) + return leftRotate(node) + } + } + // 平衡树,无须旋转,直接返回 + return node + } + ``` + === "Zig" ```zig title="avl_tree.zig" @@ -1861,6 +1965,32 @@ AVL 树的节点插入操作与二叉搜索树在主体上类似。唯一的区 } ``` +=== "Kotlin" + + ```kotlin title="avl_tree.kt" + /* 插入节点 */ + fun insert(value: Int) { + root = insertHelper(root, value) + } + + /* 递归插入节点(辅助方法) */ + fun insertHelper(n: TreeNode?, value: Int): TreeNode { + if (n == null) + 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 // 重复节点不插入,直接返回 + + updateHeight(node) // 更新节点高度 + /* 2. 执行旋转操作,使该子树重新恢复平衡 */ + node = rotate(node) + // 返回子树的根节点 + return node + } + ``` + === "Zig" ```zig title="avl_tree.zig" @@ -2408,6 +2538,44 @@ AVL 树的节点插入操作与二叉搜索树在主体上类似。唯一的区 } ``` +=== "Kotlin" + + ```kotlin title="avl_tree.kt" + /* 删除节点 */ + fun remove(value: Int) { + root = removeHelper(root, value) + } + + /* 递归删除节点(辅助方法) */ + 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) + else { + if (node.left == null || node.right == null) { + val child = if (node.left != null) node.left else node.right + // 子节点数量 = 0 ,直接删除 node 并返回 + if (child == null) return null + else node = child + } else { + // 子节点数量 = 2 ,则将中序遍历的下个节点删除,并用该节点替换当前节点 + var temp = node.right + while (temp!!.left != null) { + temp = temp.left + } + node.right = removeHelper(node.right, temp.value) + node.value = temp.value + } + } + updateHeight(node) // 更新节点高度 + /* 2. 执行旋转操作,使该子树重新恢复平衡 */ + node = rotate(node) + // 返回子树的根节点 + return node + } + ``` + === "Zig" ```zig title="avl_tree.zig" diff --git a/docs/chapter_tree/binary_search_tree.md b/docs/chapter_tree/binary_search_tree.md index 280235bd1..b39545533 100755 --- a/docs/chapter_tree/binary_search_tree.md +++ b/docs/chapter_tree/binary_search_tree.md @@ -290,6 +290,26 @@ comments: true } ``` +=== "Kotlin" + + ```kotlin title="binary_search_tree.kt" + /* 查找节点 */ + fun search(num: Int): TreeNode? { + var cur = root + // 循环查找,越过叶节点后跳出 + while (cur != null) { + // 目标节点在 cur 的右子树中 + cur = if (cur.value < num) cur.right + // 目标节点在 cur 的左子树中 + else if (cur.value > num) cur.left + // 找到目标节点,跳出循环 + else break + } + // 返回目标节点 + return cur + } + ``` + === "Zig" ```zig title="binary_search_tree.zig" @@ -707,6 +727,35 @@ comments: true } ``` +=== "Kotlin" + + ```kotlin title="binary_search_tree.kt" + /* 插入节点 */ + fun insert(num: Int) { + // 若树为空,则初始化根节点 + if (root == null) { + root = TreeNode(num) + return + } + var cur = root + var pre: TreeNode? = null + // 循环查找,越过叶节点后跳出 + while (cur != null) { + // 找到重复节点,直接返回 + if (cur.value == num) return + pre = cur + // 插入位置在 cur 的右子树中 + cur = if (cur.value < num) cur.right + // 插入位置在 cur 的左子树中 + else cur.left + } + // 插入节点 + val node = TreeNode(num) + if (pre?.value!! < num) pre.right = node + else pre.left = node + } + ``` + === "Zig" ```zig title="binary_search_tree.zig" @@ -1415,6 +1464,54 @@ comments: true } ``` +=== "Kotlin" + + ```kotlin title="binary_search_tree.kt" + /* 删除节点 */ + fun remove(num: Int) { + // 若树为空,直接提前返回 + if (root == null) return + var cur = root + var pre: TreeNode? = null + // 循环查找,越过叶节点后跳出 + while (cur != null) { + // 找到待删除节点,跳出循环 + if (cur.value == num) break + pre = cur + // 待删除节点在 cur 的右子树中 + cur = if (cur.value < num) cur.right + // 待删除节点在 cur 的左子树中 + else cur.left + } + // 若无待删除节点,则直接返回 + 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 + // 删除节点 cur + if (cur != root) { + if (pre!!.left == cur) pre.left = child + else pre.right = child + } else { + // 若删除节点为根节点,则重新指定根节点 + root = child + } + // 子节点数量 = 2 + } else { + // 获取中序遍历中 cur 的下一个节点 + var tmp = cur.right + while (tmp!!.left != null) { + tmp = tmp.left + } + // 递归删除节点 tmp + remove(tmp.value) + // 用 tmp 覆盖 cur + cur.value = tmp.value + } + } + ``` + === "Zig" ```zig title="binary_search_tree.zig" diff --git a/docs/chapter_tree/binary_tree.md b/docs/chapter_tree/binary_tree.md index 5a4544484..2fc54475f 100644 --- a/docs/chapter_tree/binary_tree.md +++ b/docs/chapter_tree/binary_tree.md @@ -180,6 +180,12 @@ comments: true } ``` +=== "Kotlin" + + ```kotlin title="" + + ``` + === "Zig" ```zig title="" @@ -405,6 +411,12 @@ comments: true n2->right = n5; ``` +=== "Kotlin" + + ```kotlin title="binary_tree.kt" + + ``` + === "Zig" ```zig title="binary_tree.zig" @@ -553,6 +565,12 @@ comments: true n1->left = n2; ``` +=== "Kotlin" + + ```kotlin title="binary_tree.kt" + + ``` + === "Zig" ```zig title="binary_tree.zig" diff --git a/docs/chapter_tree/binary_tree_traversal.md b/docs/chapter_tree/binary_tree_traversal.md index 0e924aa22..057c0d659 100755 --- a/docs/chapter_tree/binary_tree_traversal.md +++ b/docs/chapter_tree/binary_tree_traversal.md @@ -293,6 +293,27 @@ comments: true } ``` +=== "Kotlin" + + ```kotlin title="binary_tree_bfs.kt" + /* 层序遍历 */ + fun levelOrder(root: TreeNode?): MutableList { + // 初始化队列,加入根节点 + 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) // 右子节点入队 + } + return list + } + ``` + === "Zig" ```zig title="binary_tree_bfs.zig" @@ -729,6 +750,37 @@ comments: true } ``` +=== "Kotlin" + + ```kotlin title="binary_tree_dfs.kt" + /* 前序遍历 */ + fun preOrder(root: TreeNode?) { + if (root == null) return + // 访问优先级:根节点 -> 左子树 -> 右子树 + list.add(root.value) + preOrder(root.left) + preOrder(root.right) + } + + /* 中序遍历 */ + fun inOrder(root: TreeNode?) { + if (root == null) return + // 访问优先级:左子树 -> 根节点 -> 右子树 + inOrder(root.left) + list.add(root.value) + inOrder(root.right) + } + + /* 后序遍历 */ + fun postOrder(root: TreeNode?) { + if (root == null) return + // 访问优先级:左子树 -> 右子树 -> 根节点 + postOrder(root.left) + postOrder(root.right) + list.add(root.value) + } + ``` + === "Zig" ```zig title="binary_tree_dfs.zig" diff --git a/en/docs/chapter_array_and_linkedlist/array.md b/en/docs/chapter_array_and_linkedlist/array.md index b8cc64c99..add27422b 100755 --- a/en/docs/chapter_array_and_linkedlist/array.md +++ b/en/docs/chapter_array_and_linkedlist/array.md @@ -111,6 +111,12 @@ Arrays can be initialized in two ways depending on the needs: either without ini int nums[5] = { 1, 3, 2, 5, 4 }; ``` +=== "Kotlin" + + ```kotlin title="array.kt" + + ``` + === "Zig" ```zig title="array.zig" @@ -274,6 +280,19 @@ Accessing elements in an array is highly efficient, allowing us to randomly acce } ``` +=== "Kotlin" + + ```kotlin title="array.kt" + /* 随机访问元素 */ + fun randomAccess(nums: IntArray): Int { + // 在区间 [0, nums.size) 中随机抽取一个数字 + val randomIndex = ThreadLocalRandom.current().nextInt(0, nums.size) + // 获取并返回随机元素 + val randomNum = nums[randomIndex] + return randomNum + } + ``` + === "Zig" ```zig title="array.zig" @@ -454,6 +473,20 @@ It's important to note that due to the fixed length of an array, inserting an el } ``` +=== "Kotlin" + + ```kotlin title="array.kt" + /* 在数组的索引 index 处插入元素 num */ + fun insert(nums: IntArray, num: Int, index: Int) { + // 把索引 index 以及之后的所有元素向后移动一位 + for (i in nums.size - 1 downTo index + 1) { + nums[i] = nums[i - 1] + } + // 将 num 赋给 index 处的元素 + nums[index] = num + } + ``` + === "Zig" ```zig title="array.zig" @@ -615,6 +648,18 @@ Please note that after deletion, the former last element becomes "meaningless," } ``` +=== "Kotlin" + + ```kotlin title="array.kt" + /* 删除索引 index 处的元素 */ + fun remove(nums: IntArray, index: Int) { + // 把索引 index 之后的所有元素向前移动一位 + for (i in index..next = n4; ``` +=== "Kotlin" + + ```kotlin title="linked_list.kt" + + ``` + === "Zig" ```zig title="linked_list.zig" @@ -529,6 +541,17 @@ By comparison, inserting an element into an array has a time complexity of $O(n) } ``` +=== "Kotlin" + + ```kotlin title="linked_list.kt" + /* 在链表的节点 n0 之后插入节点p */ + fun insert(n0: ListNode?, p: ListNode?) { + val n1 = n0?.next + p?.next = n1 + n0?.next = p + } + ``` + === "Zig" ```zig title="linked_list.zig" @@ -718,6 +741,17 @@ It's important to note that even though node `P` continues to point to `n1` afte } ``` +=== "Kotlin" + + ```kotlin title="linked_list.kt" + /* 删除链表的节点 n0 之后的首个节点 */ + fun remove(n0: ListNode?) { + val p = n0?.next + val n1 = p?.next + n0?.next = n1 + } + ``` + === "Zig" ```zig title="linked_list.zig" @@ -898,6 +932,19 @@ It's important to note that even though node `P` continues to point to `n1` afte } ``` +=== "Kotlin" + + ```kotlin title="linked_list.kt" + /* 访问链表中索引为 index 的节点 */ + fun access(head: ListNode?, index: Int): ListNode? { + var h = head + for (i in 0..= size) + throw IndexOutOfBoundsException() + return arr[index] + } + + /* 更新元素 */ + fun set(index: Int, num: Int) { + if (index < 0 || index >= size) + throw IndexOutOfBoundsException("索引越界") + arr[index] = num + } + + /* 在尾部添加元素 */ + fun add(num: Int) { + // 元素数量超出容量时,触发扩容机制 + if (size == capacity()) + extendCapacity() + arr[size] = num + // 更新元素数量 + size++ + } + + /* 在中间插入元素 */ + fun insert(index: Int, num: Int) { + if (index < 0 || index >= size) + throw IndexOutOfBoundsException("索引越界") + // 元素数量超出容量时,触发扩容机制 + if (size == capacity()) + extendCapacity() + // 将索引 index 以及之后的元素都向后移动一位 + for (j in size - 1 downTo index) + arr[j + 1] = arr[j] + arr[index] = num + // 更新元素数量 + size++ + } + + /* 删除元素 */ + fun remove(index: Int): Int { + if (index < 0 || index >= size) + throw IndexOutOfBoundsException("索引越界") + val num: Int = arr[index] + // 将将索引 index 之后的元素都向前移动一位 + for (j in index..() + var res = 0 + // 递: 递归调用 + for (i in n downTo 0) { + stack.push(i) + } + // 归: 返回结果 + while (stack.isNotEmpty()) { + // 通过“出栈操作”模拟“归” + res += stack.pop() + } + // res = 1+2+3+...+n + return res + } + ``` + === "Zig" ```zig title="recursion.zig" diff --git a/en/docs/chapter_computational_complexity/space_complexity.md b/en/docs/chapter_computational_complexity/space_complexity.md index baeb34b63..86b406c3e 100644 --- a/en/docs/chapter_computational_complexity/space_complexity.md +++ b/en/docs/chapter_computational_complexity/space_complexity.md @@ -316,6 +316,12 @@ The relevant code is as follows: } ``` +=== "Kotlin" + + ```kotlin title="" + + ``` + === "Zig" ```zig title="" @@ -462,6 +468,12 @@ Consider the following code, the term "worst-case" in worst-case space complexit } ``` +=== "Kotlin" + + ```kotlin title="" + + ``` + === "Zig" ```zig title="" @@ -482,9 +494,10 @@ Consider the following code, the term "worst-case" in worst-case space complexit for _ in range(n): function() - def recur(n: int) -> int: + def recur(n: int): """Recursion O(n)""""" - if n == 1: return + if n == 1: + return return recur(n - 1) ``` @@ -699,6 +712,12 @@ Consider the following code, the term "worst-case" in worst-case space complexit } ``` +=== "Kotlin" + + ```kotlin title="" + + ``` + === "Zig" ```zig title="" @@ -725,7 +744,7 @@ $$

Figure 2-16   Common Types of Space Complexity

-### 1.   Constant Order $O(1)$ +### 1.   Constant Order $O(1)$ {data-toc-label="Constant Order"} Constant order is common in constants, variables, objects that are independent of the size of input data $n$. @@ -1031,6 +1050,33 @@ Note that memory occupied by initializing variables or calling functions in a lo } ``` +=== "Kotlin" + + ```kotlin title="space_complexity.kt" + /* 函数 */ + fun function(): Int { + // 执行某些操作 + return 0 + } + + /* 常数阶 */ + fun constant(n: Int) { + // 常量、变量、对象占用 O(1) 空间 + val a = 0 + var b = 0 + val nums = Array(10000) { 0 } + val node = ListNode(0) + // 循环中的变量占用 O(1) 空间 + for (i in 0.. -### 2.   Linear Order $O(n)$ +### 2.   Linear Order $O(n)$ {data-toc-label="Linear Order"} Linear order is common in arrays, linked lists, stacks, queues, etc., where the number of elements is proportional to $n$: @@ -1307,6 +1353,26 @@ Linear order is common in arrays, linked lists, stacks, queues, etc., where the } ``` +=== "Kotlin" + + ```kotlin title="space_complexity.kt" + /* 线性阶 */ + fun linear(n: Int) { + // 长度为 n 的数组占用 O(n) 空间 + val nums = Array(n) { 0 } + // 长度为 n 的列表占用 O(n) 空间 + val nodes = mutableListOf() + for (i in 0..() + for (i in 0.. Figure 2-17   Recursive Function Generating Linear Order Space Complexity

-### 3.   Quadratic Order $O(n^2)$ +### 3.   Quadratic Order $O(n^2)$ {data-toc-label="Quadratic Order"} Quadratic order is common in matrices and graphs, where the number of elements is quadratic to $n$: @@ -1686,6 +1764,25 @@ Quadratic order is common in matrices and graphs, where the number of elements i } ``` +=== "Kotlin" + + ```kotlin title="space_complexity.kt" + /* 平方阶 */ + fun quadratic(n: Int) { + // 矩阵占用 O(n^2) 空间 + val numMatrix: Array?> = arrayOfNulls(n) + // 二维列表占用 O(n^2) 空间 + val numList: MutableList> = arrayListOf() + for (i in 0..() + for (j in 0.. Figure 2-18   Recursive Function Generating Quadratic Order Space Complexity

-### 4.   Exponential Order $O(2^n)$ +### 4.   Exponential Order $O(2^n)$ {data-toc-label="Exponential Order"} Exponential order is common in binary trees. Observe the below image, a "full binary tree" with $n$ levels has $2^n - 1$ nodes, occupying $O(2^n)$ space: @@ -2039,6 +2150,20 @@ Exponential order is common in binary trees. Observe the below image, a "full bi } ``` +=== "Kotlin" + + ```kotlin title="space_complexity.kt" + /* 指数阶(建立满二叉树) */ + fun buildTree(n: Int): TreeNode? { + if (n == 0) + return null + val root = TreeNode(0) + root.left = buildTree(n - 1) + root.right = buildTree(n - 1) + return root + } + ``` + === "Zig" ```zig title="space_complexity.zig" @@ -2062,7 +2187,7 @@ Exponential order is common in binary trees. Observe the below image, a "full bi

Figure 2-19   Full Binary Tree Generating Exponential Order Space Complexity

-### 5.   Logarithmic Order $O(\log n)$ +### 5.   Logarithmic Order $O(\log n)$ {data-toc-label="Logarithmic Order"} Logarithmic order is common in divide-and-conquer algorithms. For example, in merge sort, an array of length $n$ is recursively divided in half each round, forming a recursion tree of height $\log n$, using $O(\log n)$ stack frame space. diff --git a/en/docs/chapter_computational_complexity/time_complexity.md b/en/docs/chapter_computational_complexity/time_complexity.md index c97d826a7..a1b996160 100644 --- a/en/docs/chapter_computational_complexity/time_complexity.md +++ b/en/docs/chapter_computational_complexity/time_complexity.md @@ -175,6 +175,12 @@ For example, consider the following code with an input size of $n$: } ``` +=== "Kotlin" + + ```kotlin title="" + + ``` + === "Zig" ```zig title="" @@ -433,6 +439,12 @@ Let's understand this concept of "time growth trend" with an example. Assume the } ``` +=== "Kotlin" + + ```kotlin title="" + + ``` + === "Zig" ```zig title="" @@ -629,6 +641,12 @@ Consider a function with an input size of $n$: } ``` +=== "Kotlin" + + ```kotlin title="" + + ``` + === "Zig" ```zig title="" @@ -886,6 +904,12 @@ Given a function, we can use these techniques to count operations: } ``` +=== "Kotlin" + + ```kotlin title="" + + ``` + === "Zig" ```zig title="" @@ -952,7 +976,7 @@ $$

Figure 2-9   Common Types of Time Complexity

-### 1.   Constant Order $O(1)$ +### 1.   Constant Order $O(1)$ {data-toc-label="Constant Order"} Constant order means the number of operations is independent of the input data size $n$. In the following function, although the number of operations `size` might be large, the time complexity remains $O(1)$ as it's unrelated to $n$: @@ -1103,6 +1127,19 @@ Constant order means the number of operations is independent of the input data s } ``` +=== "Kotlin" + + ```kotlin title="time_complexity.kt" + /* 常数阶 */ + fun constant(n: Int): Int { + var count = 0 + val size = 10_0000 + for (i in 0.. -### 2.   Linear Order $O(n)$ +### 2.   Linear Order $O(n)$ {data-toc-label="Linear Order"} Linear order indicates the number of operations grows linearly with the input data size $n$. Linear order commonly appears in single-loop structures: @@ -1262,6 +1299,19 @@ Linear order indicates the number of operations grows linearly with the input da } ``` +=== "Kotlin" + + ```kotlin title="time_complexity.kt" + /* 线性阶 */ + fun linear(n: Int): Int { + var count = 0 + // 循环次数与数组长度成正比 + for (i in 0.. nums[j + 1]) { + // 交换 nums[j] 与 nums[j + 1] + nums[j] = nums[j + 1].also { nums[j + 1] = nums[j] } + count += 3 // 元素交换包含 3 个单元操作 + } + } + } + return count + } + ``` + === "Zig" ```zig title="time_complexity.zig" @@ -1942,7 +2043,7 @@ For instance, in bubble sort, the outer loop runs $n - 1$ times, and the inner l
-### 4.   Exponential Order $O(2^n)$ +### 4.   Exponential Order $O(2^n)$ {data-toc-label="Exponential Order"} Biological "cell division" is a classic example of exponential order growth: starting with one cell, it becomes two after one division, four after two divisions, and so on, resulting in $2^n$ cells after $n$ divisions. @@ -2149,6 +2250,25 @@ The following image and code simulate the cell division process, with a time com } ``` +=== "Kotlin" + + ```kotlin title="time_complexity.kt" + /* 指数阶(循环实现) */ + fun exponential(n: Int): Int { + var count = 0 + // 细胞每轮一分为二,形成数列 1, 2, 4, 8, ..., 2^(n-1) + var base = 1 + for (i in 0.. 1) { + n1 /= 2 + count++ + } + return count + } + ``` + === "Zig" ```zig title="time_complexity.zig" @@ -2622,6 +2769,17 @@ Like exponential order, logarithmic order also frequently appears in recursive f } ``` +=== "Kotlin" + + ```kotlin title="time_complexity.kt" + /* 对数阶(递归实现) */ + fun logRecur(n: Int): Int { + if (n <= 1) + return 0 + return logRecur(n / 2) + 1 + } + ``` + === "Zig" ```zig title="time_complexity.zig" @@ -2649,7 +2807,7 @@ Logarithmic order is typical in algorithms based on the divide-and-conquer strat This means the base $m$ can be changed without affecting the complexity. Therefore, we often omit the base $m$ and simply denote logarithmic order as $O(\log n)$. -### 6.   Linear-Logarithmic Order $O(n \log n)$ +### 6.   Linear-Logarithmic Order $O(n \log n)$ {data-toc-label="Linear-Logarithmic Order"} Linear-logarithmic order often appears in nested loops, with the complexities of the two loops being $O(\log n)$ and $O(n)$ respectively. The related code is as follows: @@ -2815,6 +2973,21 @@ Linear-logarithmic order often appears in nested loops, with the complexities of } ``` +=== "Kotlin" + + ```kotlin title="time_complexity.kt" + /* 线性对数阶 */ + fun linearLogRecur(n: Int): Int { + if (n <= 1) + return 1 + var count = linearLogRecur(n / 2) + linearLogRecur(n / 2) + for (i in 0.. { + val nums = IntArray(n) + // 生成数组 nums = { 1, 2, 3, ..., n } + for (i in 0.. int[] + val res = arrayOfNulls(n) + for (i in 0..): Int { + for (i in nums.indices) { + // 当元素 1 在数组头部时,达到最佳时间复杂度 O(1) + // 当元素 1 在数组尾部时,达到最差时间复杂度 O(n) + if (nums[i] == 1) + return i + } + return -1 + } + ``` + === "Zig" ```zig title="worst_best_time_complexity.zig" diff --git a/en/docs/chapter_data_structure/basic_data_types.md b/en/docs/chapter_data_structure/basic_data_types.md index f1707fa91..ad9577863 100644 --- a/en/docs/chapter_data_structure/basic_data_types.md +++ b/en/docs/chapter_data_structure/basic_data_types.md @@ -161,6 +161,12 @@ In other words, **basic data types provide the "content type" of data, while dat bool bools[10]; ``` +=== "Kotlin" + + ```kotlin title="" + + ``` + === "Zig" ```zig title="" diff --git a/en/docs/chapter_hashing/hash_algorithm.md b/en/docs/chapter_hashing/hash_algorithm.md index 9cb093294..bdfa6a859 100644 --- a/en/docs/chapter_hashing/hash_algorithm.md +++ b/en/docs/chapter_hashing/hash_algorithm.md @@ -554,6 +554,46 @@ The design of hash algorithms is a complex issue that requires consideration of } ``` +=== "Kotlin" + + ```kotlin title="simple_hash.kt" + /* 加法哈希 */ + fun addHash(key: String): Int { + var hash = 0L + for (c in key.toCharArray()) { + hash = (hash + c.code) % MODULUS + } + return hash.toInt() + } + + /* 乘法哈希 */ + fun mulHash(key: String): Int { + var hash = 0L + for (c in key.toCharArray()) { + hash = (31 * hash + c.code) % MODULUS + } + return hash.toInt() + } + + /* 异或哈希 */ + fun xorHash(key: String): Int { + var hash = 0 + for (c in key.toCharArray()) { + hash = hash xor c.code + } + return hash and MODULUS + } + + /* 旋转哈希 */ + fun rotHash(key: String): Int { + var hash = 0L + for (c in key.toCharArray()) { + hash = ((hash shl 4) xor (hash shr 28) xor c.code.toLong()) % MODULUS + } + return hash.toInt() + } + ``` + === "Zig" ```zig title="simple_hash.zig" @@ -868,6 +908,12 @@ We know that the keys in a hash table can be of various data types such as integ // C does not provide built-in hash code functions ``` +=== "Kotlin" + + ```kotlin title="built_in_hash.kt" + + ``` + === "Zig" ```zig title="built_in_hash.zig" diff --git a/en/docs/chapter_hashing/hash_collision.md b/en/docs/chapter_hashing/hash_collision.md index e44574e6f..4b4ce91c2 100644 --- a/en/docs/chapter_hashing/hash_collision.md +++ b/en/docs/chapter_hashing/hash_collision.md @@ -1311,6 +1311,121 @@ The code below provides a simple implementation of a separate chaining hash tabl } ``` +=== "Kotlin" + + ```kotlin title="hash_map_chaining.kt" + /* 链式地址哈希表 */ + class HashMapChaining() { + var size: Int // 键值对数量 + var capacity: Int // 哈希表容量 + val loadThres: Double // 触发扩容的负载因子阈值 + val extendRatio: Int // 扩容倍数 + var buckets: MutableList> // 桶数组 + + /* 构造方法 */ + init { + size = 0 + capacity = 4 + loadThres = 2.0 / 3.0 + extendRatio = 2 + buckets = ArrayList(capacity) + for (i in 0.. loadThres) { + extend() + } + val index = hashFunc(key) + val bucket = buckets[index] + // 遍历桶,若遇到指定 key ,则更新对应 val 并返回 + for (pair in bucket) { + if (pair.key == key) { + pair.value = value + return + } + } + // 若无该 key ,则将键值对添加至尾部 + val pair = Pair(key, value) + bucket.add(pair) + size++ + } + + /* 删除操作 */ + fun remove(key: Int) { + val index = hashFunc(key) + val bucket = buckets[index] + // 遍历桶,从中删除键值对 + for (pair in bucket) { + if (pair.key == key) { + bucket.remove(pair) + size-- + break + } + } + } + + /* 扩容哈希表 */ + fun extend() { + // 暂存原哈希表 + val bucketsTmp = buckets + // 初始化扩容后的新哈希表 + capacity *= extendRatio + // mutablelist 无固定大小 + buckets = mutableListOf() + for (i in 0..() + for (pair in bucket) { + val k = pair.key + val v = pair.value + res.add("$k -> $v") + } + println(res) + } + } + } + ``` + === "Zig" ```zig title="hash_map_chaining.zig" @@ -2831,6 +2946,132 @@ The code below implements an open addressing (linear probing) hash table with la } ``` +=== "Kotlin" + + ```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 // 桶数组 + private val TOMBSTONE = Pair(-1, "-1") // 删除标记 + + /* 构造方法 */ + init { + buckets = arrayOfNulls(capacity) + } + + /* 哈希函数 */ + fun hashFunc(key: Int): Int { + return key % capacity + } + + /* 负载因子 */ + fun loadFactor(): Double { + return (size / capacity).toDouble() + } + + /* 搜索 key 对应的桶索引 */ + fun findBucket(key: Int): Int { + var index = hashFunc(key) + var firstTombstone = -1 + // 线性探测,当遇到空桶时跳出 + while (buckets[index] != null) { + // 若遇到 key ,返回对应的桶索引 + if (buckets[index]?.key == key) { + // 若之前遇到了删除标记,则将键值对移动至该索引处 + if (firstTombstone != -1) { + buckets[firstTombstone] = buckets[index] + buckets[index] = TOMBSTONE + return firstTombstone // 返回移动后的桶索引 + } + return index // 返回桶索引 + } + // 记录遇到的首个删除标记 + if (firstTombstone == -1 && buckets[index] == TOMBSTONE) { + firstTombstone = index + } + // 计算桶索引,越过尾部则返回头部 + index = (index + 1) % capacity + } + // 若 key 不存在,则返回添加点的索引 + return if (firstTombstone == -1) index else firstTombstone + } + + /* 查询操作 */ + fun get(key: Int): String? { + // 搜索 key 对应的桶索引 + val index = findBucket(key) + // 若找到键值对,则返回对应 val + if (buckets[index] != null && buckets[index] != TOMBSTONE) { + return buckets[index]?.value + } + // 若键值对不存在,则返回 null + return null + } + + /* 添加操作 */ + fun put(key: Int, value: String) { + // 当负载因子超过阈值时,执行扩容 + if (loadFactor() > loadThres) { + extend() + } + // 搜索 key 对应的桶索引 + val index = findBucket(key) + // 若找到键值对,则覆盖 val 并返回 + if (buckets[index] != null && buckets[index] != TOMBSTONE) { + buckets[index]!!.value = value + return + } + // 若键值对不存在,则添加该键值对 + buckets[index] = Pair(key, value) + size++ + } + + /* 删除操作 */ + fun remove(key: Int) { + // 搜索 key 对应的桶索引 + val index = findBucket(key) + // 若找到键值对,则用删除标记覆盖它 + if (buckets[index] != null && buckets[index] != TOMBSTONE) { + buckets[index] = TOMBSTONE + size-- + } + } + + /* 扩容哈希表 */ + fun extend() { + // 暂存原哈希表 + val bucketsTmp = buckets + // 初始化扩容后的新哈希表 + capacity *= extendRatio + buckets = arrayOfNulls(capacity) + size = 0 + // 将键值对从原哈希表搬运至新哈希表 + for (pair in bucketsTmp) { + if (pair != null && pair != TOMBSTONE) { + put(pair.key, pair.value) + } + } + } + + /* 打印哈希表 */ + fun print() { + for (pair in buckets) { + if (pair == null) { + println("null") + } else if (pair == TOMBSTONE) { + println("TOMESTOME") + } else { + println("${pair.key} -> ${pair.value}") + } + } + } + } + ``` + === "Zig" ```zig title="hash_map_open_addressing.zig" diff --git a/en/docs/chapter_hashing/hash_map.md b/en/docs/chapter_hashing/hash_map.md index e05ff9b2a..6cbe29657 100755 --- a/en/docs/chapter_hashing/hash_map.md +++ b/en/docs/chapter_hashing/hash_map.md @@ -277,6 +277,12 @@ Common operations of a hash table include initialization, querying, adding key-v // C does not provide a built-in hash table ``` +=== "Kotlin" + + ```kotlin title="hash_map.kt" + + ``` + === "Zig" ```zig title="hash_map.zig" @@ -473,6 +479,12 @@ There are three common ways to traverse a hash table: traversing key-value pairs // C does not provide a built-in hash table ``` +=== "Kotlin" + + ```kotlin title="hash_map.kt" + + ``` + === "Zig" ```zig title="hash_map.zig" @@ -1525,6 +1537,166 @@ The following code implements a simple hash table. Here, we encapsulate `key` an } ``` +=== "Kotlin" + + ```kotlin title="array_hash_map.kt" + /* 键值对 */ + class Pair( + var key: Int, + var value: String + ) + + /* 基于数组实现的哈希表 */ + class ArrayHashMap { + private val buckets = arrayOfNulls(100) + + init { + // 初始化数组,包含 100 个桶 + for (i in 0..<100) { + buckets[i] = null + } + } + + /* 哈希函数 */ + fun hashFunc(key: Int): Int { + val index = key % 100 + return index + } + + /* 查询操作 */ + fun get(key: Int): String? { + val index = hashFunc(key) + val pair = buckets[index] ?: return null + return pair.value + } + + /* 添加操作 */ + fun put(key: Int, value: String) { + val pair = Pair(key, value) + val index = hashFunc(key) + buckets[index] = pair + } + + /* 删除操作 */ + fun remove(key: Int) { + val index = hashFunc(key) + // 置为 null ,代表删除 + buckets[index] = null + } + + /* 获取所有键值对 */ + fun pairSet(): MutableList { + val pairSet = ArrayList() + for (pair in buckets) { + if (pair != null) pairSet.add(pair) + } + return pairSet + } + + /* 获取所有键 */ + fun keySet(): MutableList { + val keySet = ArrayList() + for (pair in buckets) { + if (pair != null) keySet.add(pair.key) + } + return keySet + } + + /* 获取所有值 */ + fun valueSet(): MutableList { + val valueSet = ArrayList() + for (pair in buckets) { + pair?.let { valueSet.add(it.value) } + } + return valueSet + } + + /* 打印哈希表 */ + fun print() { + for (kv in pairSet()) { + val key = kv.key + val value = kv.value + println("${key}->${value}") + } + } + } + + /* 基于数组实现的哈希表 */ + class ArrayHashMap { + private val buckets = arrayOfNulls(100) + + init { + // 初始化数组,包含 100 个桶 + for (i in 0..<100) { + buckets[i] = null + } + } + + /* 哈希函数 */ + fun hashFunc(key: Int): Int { + val index = key % 100 + return index + } + + /* 查询操作 */ + fun get(key: Int): String? { + val index = hashFunc(key) + val pair = buckets[index] ?: return null + return pair.value + } + + /* 添加操作 */ + fun put(key: Int, value: String) { + val pair = Pair(key, value) + val index = hashFunc(key) + buckets[index] = pair + } + + /* 删除操作 */ + fun remove(key: Int) { + val index = hashFunc(key) + // 置为 null ,代表删除 + buckets[index] = null + } + + /* 获取所有键值对 */ + fun pairSet(): MutableList { + val pairSet = ArrayList() + for (pair in buckets) { + if (pair != null) pairSet.add(pair) + } + return pairSet + } + + /* 获取所有键 */ + fun keySet(): MutableList { + val keySet = ArrayList() + for (pair in buckets) { + if (pair != null) keySet.add(pair.key) + } + return keySet + } + + /* 获取所有值 */ + fun valueSet(): MutableList { + val valueSet = ArrayList() + for (pair in buckets) { + pair?.let { valueSet.add(it.value) } + } + return valueSet + } + + /* 打印哈希表 */ + fun print() { + for (kv in pairSet()) { + val key = kv.key + val value = kv.value + println("${key}->${value}") + } + } + } + ``` + === "Zig" ```zig title="array_hash_map.zig" diff --git a/en/docs/chapter_preface/suggestions.md b/en/docs/chapter_preface/suggestions.md index 9db805709..1efed419e 100644 --- a/en/docs/chapter_preface/suggestions.md +++ b/en/docs/chapter_preface/suggestions.md @@ -160,6 +160,19 @@ comments: true */ ``` +=== "Kotlin" + + ```kotlin title="" + /* Header comments for labeling functions, classes, test samples, etc */ + + // Comments for explaining details. + + /** + * Multiline + * comments + */ + ``` + === "Zig" ```zig title="" diff --git a/en/docs/chapter_stack_and_queue/deque.md b/en/docs/chapter_stack_and_queue/deque.md index 2cffb54c3..2badf8159 100644 --- a/en/docs/chapter_stack_and_queue/deque.md +++ b/en/docs/chapter_stack_and_queue/deque.md @@ -334,6 +334,12 @@ Similarly, we can directly use the double-ended queue classes implemented in pro // C does not provide a built-in deque ``` +=== "Kotlin" + + ```kotlin title="deque.kt" + + ``` + === "Zig" ```zig title="deque.zig" @@ -1835,6 +1841,133 @@ The implementation code is as follows: } ``` +=== "Kotlin" + + ```kotlin title="linkedlist_deque.kt" + /* 双向链表节点 */ + class ListNode(var value: Int) { + // 节点值 + var next: ListNode? = null // 后继节点引用 + var prev: ListNode? = null // 前驱节点引用 + } + + /* 基于双向链表实现的双向队列 */ + class LinkedListDeque { + private var front: ListNode? = null // 头节点 front ,尾节点 rear + private var rear: ListNode? = null + private var queSize = 0 // 双向队列的长度 + + /* 获取双向队列的长度 */ + fun size(): Int { + return queSize + } + + /* 判断双向队列是否为空 */ + fun isEmpty(): Boolean { + return size() == 0 + } + + /* 入队操作 */ + fun push(num: Int, isFront: Boolean) { + val node = ListNode(num) + // 若链表为空,则令 front 和 rear 都指向 node + if (isEmpty()) { + rear = node + front = rear + // 队首入队操作 + } else if (isFront) { + // 将 node 添加至链表头部 + front?.prev = node + node.next = front + front = node // 更新头节点 + // 队尾入队操作 + } else { + // 将 node 添加至链表尾部 + rear?.next = node + node.prev = rear + rear = node // 更新尾节点 + } + queSize++ // 更新队列长度 + } + + /* 队首入队 */ + fun pushFirst(num: Int) { + push(num, true) + } + + /* 队尾入队 */ + fun pushLast(num: Int) { + push(num, false) + } + + /* 出队操作 */ + fun pop(isFront: Boolean): Int { + if (isEmpty()) throw IndexOutOfBoundsException() + + val value: Int + // 队首出队操作 + if (isFront) { + value = front!!.value // 暂存头节点值 + // 删除头节点 + val fNext = front!!.next + if (fNext != null) { + fNext.prev = null + front!!.next = null + } + front = fNext // 更新头节点 + // 队尾出队操作 + } else { + value = rear!!.value // 暂存尾节点值 + // 删除尾节点 + val rPrev = rear!!.prev + if (rPrev != null) { + rPrev.next = null + rear!!.prev = null + } + rear = rPrev // 更新尾节点 + } + queSize-- // 更新队列长度 + return value + } + + /* 队首出队 */ + fun popFirst(): Int { + return pop(true) + } + + /* 队尾出队 */ + fun popLast(): Int { + return pop(false) + } + + /* 访问队首元素 */ + fun peekFirst(): Int { + if (isEmpty()) { + throw IndexOutOfBoundsException() + + } + return front!!.value + } + + /* 访问队尾元素 */ + fun peekLast(): Int { + if (isEmpty()) throw IndexOutOfBoundsException() + return rear!!.value + } + + /* 返回数组用于打印 */ + fun toArray(): IntArray { + var node = front + val res = IntArray(size()) + for (i in res.indices) { + res[i] = node!!.value + node = node.next + } + return res + } + } + ``` + === "Zig" ```zig title="linkedlist_deque.zig" @@ -2018,9 +2151,1310 @@ As shown in the Figure 5-9 , similar to implementing a queue with an array, we c The implementation only needs to add methods for "front enqueue" and "rear dequeue": -```src -[file]{array_deque}-[func]{} -``` +=== "Python" + + ```python title="array_deque.py" + class ArrayDeque: + """基于环形数组实现的双向队列""" + + def __init__(self, capacity: int): + """构造方法""" + self._nums: list[int] = [0] * capacity + self._front: int = 0 + self._size: int = 0 + + def capacity(self) -> int: + """获取双向队列的容量""" + return len(self._nums) + + def size(self) -> int: + """获取双向队列的长度""" + return self._size + + def is_empty(self) -> bool: + """判断双向队列是否为空""" + return self._size == 0 + + def index(self, i: int) -> int: + """计算环形数组索引""" + # 通过取余操作实现数组首尾相连 + # 当 i 越过数组尾部后,回到头部 + # 当 i 越过数组头部后,回到尾部 + return (i + self.capacity()) % self.capacity() + + def push_first(self, num: int): + """队首入队""" + if self._size == self.capacity(): + print("双向队列已满") + return + # 队首指针向左移动一位 + # 通过取余操作实现 front 越过数组头部后回到尾部 + self._front = self.index(self._front - 1) + # 将 num 添加至队首 + self._nums[self._front] = num + self._size += 1 + + def push_last(self, num: int): + """队尾入队""" + if self._size == self.capacity(): + print("双向队列已满") + return + # 计算队尾指针,指向队尾索引 + 1 + rear = self.index(self._front + self._size) + # 将 num 添加至队尾 + self._nums[rear] = num + self._size += 1 + + def pop_first(self) -> int: + """队首出队""" + num = self.peek_first() + # 队首指针向后移动一位 + self._front = self.index(self._front + 1) + self._size -= 1 + return num + + def pop_last(self) -> int: + """队尾出队""" + num = self.peek_last() + self._size -= 1 + return num + + def peek_first(self) -> int: + """访问队首元素""" + if self.is_empty(): + raise IndexError("双向队列为空") + return self._nums[self._front] + + def peek_last(self) -> int: + """访问队尾元素""" + if self.is_empty(): + raise IndexError("双向队列为空") + # 计算尾元素索引 + last = self.index(self._front + self._size - 1) + return self._nums[last] + + def to_array(self) -> list[int]: + """返回数组用于打印""" + # 仅转换有效长度范围内的列表元素 + res = [] + for i in range(self._size): + res.append(self._nums[self.index(self._front + i)]) + return res + ``` + +=== "C++" + + ```cpp title="array_deque.cpp" + /* 基于环形数组实现的双向队列 */ + class ArrayDeque { + private: + vector nums; // 用于存储双向队列元素的数组 + int front; // 队首指针,指向队首元素 + int queSize; // 双向队列长度 + + public: + /* 构造方法 */ + ArrayDeque(int capacity) { + nums.resize(capacity); + front = queSize = 0; + } + + /* 获取双向队列的容量 */ + int capacity() { + return nums.size(); + } + + /* 获取双向队列的长度 */ + int size() { + return queSize; + } + + /* 判断双向队列是否为空 */ + bool isEmpty() { + return queSize == 0; + } + + /* 计算环形数组索引 */ + int index(int i) { + // 通过取余操作实现数组首尾相连 + // 当 i 越过数组尾部后,回到头部 + // 当 i 越过数组头部后,回到尾部 + return (i + capacity()) % capacity(); + } + + /* 队首入队 */ + void pushFirst(int num) { + if (queSize == capacity()) { + cout << "双向队列已满" << endl; + return; + } + // 队首指针向左移动一位 + // 通过取余操作实现 front 越过数组头部后回到尾部 + front = index(front - 1); + // 将 num 添加至队首 + nums[front] = num; + queSize++; + } + + /* 队尾入队 */ + void pushLast(int num) { + if (queSize == capacity()) { + cout << "双向队列已满" << endl; + return; + } + // 计算队尾指针,指向队尾索引 + 1 + int rear = index(front + queSize); + // 将 num 添加至队尾 + nums[rear] = num; + queSize++; + } + + /* 队首出队 */ + int popFirst() { + int num = peekFirst(); + // 队首指针向后移动一位 + front = index(front + 1); + queSize--; + return num; + } + + /* 队尾出队 */ + int popLast() { + int num = peekLast(); + queSize--; + return num; + } + + /* 访问队首元素 */ + int peekFirst() { + if (isEmpty()) + throw out_of_range("双向队列为空"); + return nums[front]; + } + + /* 访问队尾元素 */ + int peekLast() { + if (isEmpty()) + throw out_of_range("双向队列为空"); + // 计算尾元素索引 + int last = index(front + queSize - 1); + return nums[last]; + } + + /* 返回数组用于打印 */ + vector toVector() { + // 仅转换有效长度范围内的列表元素 + vector res(queSize); + for (int i = 0, j = front; i < queSize; i++, j++) { + res[i] = nums[index(j)]; + } + return res; + } + }; + ``` + +=== "Java" + + ```java title="array_deque.java" + /* 基于环形数组实现的双向队列 */ + class ArrayDeque { + private int[] nums; // 用于存储双向队列元素的数组 + private int front; // 队首指针,指向队首元素 + private int queSize; // 双向队列长度 + + /* 构造方法 */ + public ArrayDeque(int capacity) { + this.nums = new int[capacity]; + front = queSize = 0; + } + + /* 获取双向队列的容量 */ + public int capacity() { + return nums.length; + } + + /* 获取双向队列的长度 */ + public int size() { + return queSize; + } + + /* 判断双向队列是否为空 */ + public boolean isEmpty() { + return queSize == 0; + } + + /* 计算环形数组索引 */ + private int index(int i) { + // 通过取余操作实现数组首尾相连 + // 当 i 越过数组尾部后,回到头部 + // 当 i 越过数组头部后,回到尾部 + return (i + capacity()) % capacity(); + } + + /* 队首入队 */ + public void pushFirst(int num) { + if (queSize == capacity()) { + System.out.println("双向队列已满"); + return; + } + // 队首指针向左移动一位 + // 通过取余操作实现 front 越过数组头部后回到尾部 + front = index(front - 1); + // 将 num 添加至队首 + nums[front] = num; + queSize++; + } + + /* 队尾入队 */ + public void pushLast(int num) { + if (queSize == capacity()) { + System.out.println("双向队列已满"); + return; + } + // 计算队尾指针,指向队尾索引 + 1 + int rear = index(front + queSize); + // 将 num 添加至队尾 + nums[rear] = num; + queSize++; + } + + /* 队首出队 */ + public int popFirst() { + int num = peekFirst(); + // 队首指针向后移动一位 + front = index(front + 1); + queSize--; + return num; + } + + /* 队尾出队 */ + public int popLast() { + int num = peekLast(); + queSize--; + return num; + } + + /* 访问队首元素 */ + public int peekFirst() { + if (isEmpty()) + throw new IndexOutOfBoundsException(); + return nums[front]; + } + + /* 访问队尾元素 */ + public int peekLast() { + if (isEmpty()) + throw new IndexOutOfBoundsException(); + // 计算尾元素索引 + int last = index(front + queSize - 1); + return nums[last]; + } + + /* 返回数组用于打印 */ + public int[] toArray() { + // 仅转换有效长度范围内的列表元素 + int[] res = new int[queSize]; + for (int i = 0, j = front; i < queSize; i++, j++) { + res[i] = nums[index(j)]; + } + return res; + } + } + ``` + +=== "C#" + + ```csharp title="array_deque.cs" + /* 基于环形数组实现的双向队列 */ + class ArrayDeque { + int[] nums; // 用于存储双向队列元素的数组 + int front; // 队首指针,指向队首元素 + int queSize; // 双向队列长度 + + /* 构造方法 */ + public ArrayDeque(int capacity) { + nums = new int[capacity]; + front = queSize = 0; + } + + /* 获取双向队列的容量 */ + int Capacity() { + return nums.Length; + } + + /* 获取双向队列的长度 */ + public int Size() { + return queSize; + } + + /* 判断双向队列是否为空 */ + public bool IsEmpty() { + return queSize == 0; + } + + /* 计算环形数组索引 */ + int Index(int i) { + // 通过取余操作实现数组首尾相连 + // 当 i 越过数组尾部后,回到头部 + // 当 i 越过数组头部后,回到尾部 + return (i + Capacity()) % Capacity(); + } + + /* 队首入队 */ + public void PushFirst(int num) { + if (queSize == Capacity()) { + Console.WriteLine("双向队列已满"); + return; + } + // 队首指针向左移动一位 + // 通过取余操作实现 front 越过数组头部后回到尾部 + front = Index(front - 1); + // 将 num 添加至队首 + nums[front] = num; + queSize++; + } + + /* 队尾入队 */ + public void PushLast(int num) { + if (queSize == Capacity()) { + Console.WriteLine("双向队列已满"); + return; + } + // 计算队尾指针,指向队尾索引 + 1 + int rear = Index(front + queSize); + // 将 num 添加至队尾 + nums[rear] = num; + queSize++; + } + + /* 队首出队 */ + public int PopFirst() { + int num = PeekFirst(); + // 队首指针向后移动一位 + front = Index(front + 1); + queSize--; + return num; + } + + /* 队尾出队 */ + public int PopLast() { + int num = PeekLast(); + queSize--; + return num; + } + + /* 访问队首元素 */ + public int PeekFirst() { + if (IsEmpty()) { + throw new InvalidOperationException(); + } + return nums[front]; + } + + /* 访问队尾元素 */ + public int PeekLast() { + if (IsEmpty()) { + throw new InvalidOperationException(); + } + // 计算尾元素索引 + int last = Index(front + queSize - 1); + return nums[last]; + } + + /* 返回数组用于打印 */ + public int[] ToArray() { + // 仅转换有效长度范围内的列表元素 + int[] res = new int[queSize]; + for (int i = 0, j = front; i < queSize; i++, j++) { + res[i] = nums[Index(j)]; + } + return res; + } + } + ``` + +=== "Go" + + ```go title="array_deque.go" + /* 基于环形数组实现的双向队列 */ + type arrayDeque struct { + nums []int // 用于存储双向队列元素的数组 + front int // 队首指针,指向队首元素 + queSize int // 双向队列长度 + queCapacity int // 队列容量(即最大容纳元素数量) + } + + /* 初始化队列 */ + func newArrayDeque(queCapacity int) *arrayDeque { + return &arrayDeque{ + nums: make([]int, queCapacity), + queCapacity: queCapacity, + front: 0, + queSize: 0, + } + } + + /* 获取双向队列的长度 */ + func (q *arrayDeque) size() int { + return q.queSize + } + + /* 判断双向队列是否为空 */ + func (q *arrayDeque) isEmpty() bool { + return q.queSize == 0 + } + + /* 计算环形数组索引 */ + func (q *arrayDeque) index(i int) int { + // 通过取余操作实现数组首尾相连 + // 当 i 越过数组尾部后,回到头部 + // 当 i 越过数组头部后,回到尾部 + return (i + q.queCapacity) % q.queCapacity + } + + /* 队首入队 */ + func (q *arrayDeque) pushFirst(num int) { + if q.queSize == q.queCapacity { + fmt.Println("双向队列已满") + return + } + // 队首指针向左移动一位 + // 通过取余操作实现 front 越过数组头部后回到尾部 + q.front = q.index(q.front - 1) + // 将 num 添加至队首 + q.nums[q.front] = num + q.queSize++ + } + + /* 队尾入队 */ + func (q *arrayDeque) pushLast(num int) { + if q.queSize == q.queCapacity { + fmt.Println("双向队列已满") + return + } + // 计算队尾指针,指向队尾索引 + 1 + rear := q.index(q.front + q.queSize) + // 将 num 添加至队首 + q.nums[rear] = num + q.queSize++ + } + + /* 队首出队 */ + func (q *arrayDeque) popFirst() any { + num := q.peekFirst() + // 队首指针向后移动一位 + q.front = q.index(q.front + 1) + q.queSize-- + return num + } + + /* 队尾出队 */ + func (q *arrayDeque) popLast() any { + num := q.peekLast() + q.queSize-- + return num + } + + /* 访问队首元素 */ + func (q *arrayDeque) peekFirst() any { + if q.isEmpty() { + return nil + } + return q.nums[q.front] + } + + /* 访问队尾元素 */ + func (q *arrayDeque) peekLast() any { + if q.isEmpty() { + return nil + } + // 计算尾元素索引 + last := q.index(q.front + q.queSize - 1) + return q.nums[last] + } + + /* 获取 Slice 用于打印 */ + func (q *arrayDeque) toSlice() []int { + // 仅转换有效长度范围内的列表元素 + res := make([]int, q.queSize) + for i, j := 0, q.front; i < q.queSize; i++ { + res[i] = q.nums[q.index(j)] + j++ + } + return res + } + ``` + +=== "Swift" + + ```swift title="array_deque.swift" + /* 基于环形数组实现的双向队列 */ + class ArrayDeque { + private var nums: [Int] // 用于存储双向队列元素的数组 + private var front: Int // 队首指针,指向队首元素 + private var _size: Int // 双向队列长度 + + /* 构造方法 */ + init(capacity: Int) { + nums = Array(repeating: 0, count: capacity) + front = 0 + _size = 0 + } + + /* 获取双向队列的容量 */ + func capacity() -> Int { + nums.count + } + + /* 获取双向队列的长度 */ + func size() -> Int { + _size + } + + /* 判断双向队列是否为空 */ + func isEmpty() -> Bool { + size() == 0 + } + + /* 计算环形数组索引 */ + private func index(i: Int) -> Int { + // 通过取余操作实现数组首尾相连 + // 当 i 越过数组尾部后,回到头部 + // 当 i 越过数组头部后,回到尾部 + (i + capacity()) % capacity() + } + + /* 队首入队 */ + func pushFirst(num: Int) { + if size() == capacity() { + print("双向队列已满") + return + } + // 队首指针向左移动一位 + // 通过取余操作实现 front 越过数组头部后回到尾部 + front = index(i: front - 1) + // 将 num 添加至队首 + nums[front] = num + _size += 1 + } + + /* 队尾入队 */ + func pushLast(num: Int) { + if size() == capacity() { + print("双向队列已满") + return + } + // 计算队尾指针,指向队尾索引 + 1 + let rear = index(i: front + size()) + // 将 num 添加至队尾 + nums[rear] = num + _size += 1 + } + + /* 队首出队 */ + func popFirst() -> Int { + let num = peekFirst() + // 队首指针向后移动一位 + front = index(i: front + 1) + _size -= 1 + return num + } + + /* 队尾出队 */ + func popLast() -> Int { + let num = peekLast() + _size -= 1 + return num + } + + /* 访问队首元素 */ + func peekFirst() -> Int { + if isEmpty() { + fatalError("双向队列为空") + } + return nums[front] + } + + /* 访问队尾元素 */ + func peekLast() -> Int { + if isEmpty() { + fatalError("双向队列为空") + } + // 计算尾元素索引 + let last = index(i: front + size() - 1) + return nums[last] + } + + /* 返回数组用于打印 */ + func toArray() -> [Int] { + // 仅转换有效长度范围内的列表元素 + (front ..< front + size()).map { nums[index(i: $0)] } + } + } + ``` + +=== "JS" + + ```javascript title="array_deque.js" + /* 基于环形数组实现的双向队列 */ + class ArrayDeque { + #nums; // 用于存储双向队列元素的数组 + #front; // 队首指针,指向队首元素 + #queSize; // 双向队列长度 + + /* 构造方法 */ + constructor(capacity) { + this.#nums = new Array(capacity); + this.#front = 0; + this.#queSize = 0; + } + + /* 获取双向队列的容量 */ + capacity() { + return this.#nums.length; + } + + /* 获取双向队列的长度 */ + size() { + return this.#queSize; + } + + /* 判断双向队列是否为空 */ + isEmpty() { + return this.#queSize === 0; + } + + /* 计算环形数组索引 */ + index(i) { + // 通过取余操作实现数组首尾相连 + // 当 i 越过数组尾部后,回到头部 + // 当 i 越过数组头部后,回到尾部 + return (i + this.capacity()) % this.capacity(); + } + + /* 队首入队 */ + pushFirst(num) { + if (this.#queSize === this.capacity()) { + console.log('双向队列已满'); + return; + } + // 队首指针向左移动一位 + // 通过取余操作实现 front 越过数组头部后回到尾部 + this.#front = this.index(this.#front - 1); + // 将 num 添加至队首 + this.#nums[this.#front] = num; + this.#queSize++; + } + + /* 队尾入队 */ + pushLast(num) { + if (this.#queSize === this.capacity()) { + console.log('双向队列已满'); + return; + } + // 计算队尾指针,指向队尾索引 + 1 + const rear = this.index(this.#front + this.#queSize); + // 将 num 添加至队尾 + this.#nums[rear] = num; + this.#queSize++; + } + + /* 队首出队 */ + popFirst() { + const num = this.peekFirst(); + // 队首指针向后移动一位 + this.#front = this.index(this.#front + 1); + this.#queSize--; + return num; + } + + /* 队尾出队 */ + popLast() { + const num = this.peekLast(); + this.#queSize--; + return num; + } + + /* 访问队首元素 */ + peekFirst() { + if (this.isEmpty()) throw new Error('The Deque Is Empty.'); + return this.#nums[this.#front]; + } + + /* 访问队尾元素 */ + peekLast() { + if (this.isEmpty()) throw new Error('The Deque Is Empty.'); + // 计算尾元素索引 + const last = this.index(this.#front + this.#queSize - 1); + return this.#nums[last]; + } + + /* 返回数组用于打印 */ + toArray() { + // 仅转换有效长度范围内的列表元素 + const res = []; + for (let i = 0, j = this.#front; i < this.#queSize; i++, j++) { + res[i] = this.#nums[this.index(j)]; + } + return res; + } + } + ``` + +=== "TS" + + ```typescript title="array_deque.ts" + /* 基于环形数组实现的双向队列 */ + class ArrayDeque { + private nums: number[]; // 用于存储双向队列元素的数组 + private front: number; // 队首指针,指向队首元素 + private queSize: number; // 双向队列长度 + + /* 构造方法 */ + constructor(capacity: number) { + this.nums = new Array(capacity); + this.front = 0; + this.queSize = 0; + } + + /* 获取双向队列的容量 */ + capacity(): number { + return this.nums.length; + } + + /* 获取双向队列的长度 */ + size(): number { + return this.queSize; + } + + /* 判断双向队列是否为空 */ + isEmpty(): boolean { + return this.queSize === 0; + } + + /* 计算环形数组索引 */ + index(i: number): number { + // 通过取余操作实现数组首尾相连 + // 当 i 越过数组尾部后,回到头部 + // 当 i 越过数组头部后,回到尾部 + return (i + this.capacity()) % this.capacity(); + } + + /* 队首入队 */ + pushFirst(num: number): void { + if (this.queSize === this.capacity()) { + console.log('双向队列已满'); + return; + } + // 队首指针向左移动一位 + // 通过取余操作实现 front 越过数组头部后回到尾部 + this.front = this.index(this.front - 1); + // 将 num 添加至队首 + this.nums[this.front] = num; + this.queSize++; + } + + /* 队尾入队 */ + pushLast(num: number): void { + if (this.queSize === this.capacity()) { + console.log('双向队列已满'); + return; + } + // 计算队尾指针,指向队尾索引 + 1 + const rear: number = this.index(this.front + this.queSize); + // 将 num 添加至队尾 + this.nums[rear] = num; + this.queSize++; + } + + /* 队首出队 */ + popFirst(): number { + const num: number = this.peekFirst(); + // 队首指针向后移动一位 + this.front = this.index(this.front + 1); + this.queSize--; + return num; + } + + /* 队尾出队 */ + popLast(): number { + const num: number = this.peekLast(); + this.queSize--; + return num; + } + + /* 访问队首元素 */ + peekFirst(): number { + if (this.isEmpty()) throw new Error('The Deque Is Empty.'); + return this.nums[this.front]; + } + + /* 访问队尾元素 */ + peekLast(): number { + if (this.isEmpty()) throw new Error('The Deque Is Empty.'); + // 计算尾元素索引 + const last = this.index(this.front + this.queSize - 1); + return this.nums[last]; + } + + /* 返回数组用于打印 */ + toArray(): number[] { + // 仅转换有效长度范围内的列表元素 + const res: number[] = []; + for (let i = 0, j = this.front; i < this.queSize; i++, j++) { + res[i] = this.nums[this.index(j)]; + } + return res; + } + } + ``` + +=== "Dart" + + ```dart title="array_deque.dart" + /* 基于环形数组实现的双向队列 */ + class ArrayDeque { + late List _nums; // 用于存储双向队列元素的数组 + late int _front; // 队首指针,指向队首元素 + late int _queSize; // 双向队列长度 + + /* 构造方法 */ + ArrayDeque(int capacity) { + this._nums = List.filled(capacity, 0); + this._front = this._queSize = 0; + } + + /* 获取双向队列的容量 */ + int capacity() { + return _nums.length; + } + + /* 获取双向队列的长度 */ + int size() { + return _queSize; + } + + /* 判断双向队列是否为空 */ + bool isEmpty() { + return _queSize == 0; + } + + /* 计算环形数组索引 */ + int index(int i) { + // 通过取余操作实现数组首尾相连 + // 当 i 越过数组尾部后,回到头部 + // 当 i 越过数组头部后,回到尾部 + return (i + capacity()) % capacity(); + } + + /* 队首入队 */ + void pushFirst(int _num) { + if (_queSize == capacity()) { + throw Exception("双向队列已满"); + } + // 队首指针向左移动一位 + // 通过取余操作实现 _front 越过数组头部后回到尾部 + _front = index(_front - 1); + // 将 _num 添加至队首 + _nums[_front] = _num; + _queSize++; + } + + /* 队尾入队 */ + void pushLast(int _num) { + if (_queSize == capacity()) { + throw Exception("双向队列已满"); + } + // 计算队尾指针,指向队尾索引 + 1 + int rear = index(_front + _queSize); + // 将 _num 添加至队尾 + _nums[rear] = _num; + _queSize++; + } + + /* 队首出队 */ + int popFirst() { + int _num = peekFirst(); + // 队首指针向右移动一位 + _front = index(_front + 1); + _queSize--; + return _num; + } + + /* 队尾出队 */ + int popLast() { + int _num = peekLast(); + _queSize--; + return _num; + } + + /* 访问队首元素 */ + int peekFirst() { + if (isEmpty()) { + throw Exception("双向队列为空"); + } + return _nums[_front]; + } + + /* 访问队尾元素 */ + int peekLast() { + if (isEmpty()) { + throw Exception("双向队列为空"); + } + // 计算尾元素索引 + int last = index(_front + _queSize - 1); + return _nums[last]; + } + + /* 返回数组用于打印 */ + List toArray() { + // 仅转换有效长度范围内的列表元素 + List res = List.filled(_queSize, 0); + for (int i = 0, j = _front; i < _queSize; i++, j++) { + res[i] = _nums[index(j)]; + } + return res; + } + } + ``` + +=== "Rust" + + ```rust title="array_deque.rs" + /* 基于环形数组实现的双向队列 */ + struct ArrayDeque { + nums: Vec, // 用于存储双向队列元素的数组 + front: usize, // 队首指针,指向队首元素 + que_size: usize, // 双向队列长度 + } + + impl ArrayDeque { + /* 构造方法 */ + pub fn new(capacity: usize) -> Self { + Self { + nums: vec![0; capacity], + front: 0, + que_size: 0, + } + } + + /* 获取双向队列的容量 */ + pub fn capacity(&self) -> usize { + self.nums.len() + } + + /* 获取双向队列的长度 */ + pub fn size(&self) -> usize { + self.que_size + } + + /* 判断双向队列是否为空 */ + pub fn is_empty(&self) -> bool { + self.que_size == 0 + } + + /* 计算环形数组索引 */ + fn index(&self, i: i32) -> usize { + // 通过取余操作实现数组首尾相连 + // 当 i 越过数组尾部后,回到头部 + // 当 i 越过数组头部后,回到尾部 + return ((i + self.capacity() as i32) % self.capacity() as i32) as usize; + } + + /* 队首入队 */ + pub fn push_first(&mut self, num: i32) { + if self.que_size == self.capacity() { + println!("双向队列已满"); + return; + } + // 队首指针向左移动一位 + // 通过取余操作实现 front 越过数组头部后回到尾部 + self.front = self.index(self.front as i32 - 1); + // 将 num 添加至队首 + self.nums[self.front] = num; + self.que_size += 1; + } + + /* 队尾入队 */ + pub fn push_last(&mut self, num: i32) { + if self.que_size == self.capacity() { + println!("双向队列已满"); + return; + } + // 计算队尾指针,指向队尾索引 + 1 + let rear = self.index(self.front as i32 + self.que_size as i32); + // 将 num 添加至队尾 + self.nums[rear] = num; + self.que_size += 1; + } + + /* 队首出队 */ + fn pop_first(&mut self) -> i32 { + let num = self.peek_first(); + // 队首指针向后移动一位 + self.front = self.index(self.front as i32 + 1); + self.que_size -= 1; + num + } + + /* 队尾出队 */ + fn pop_last(&mut self) -> i32 { + let num = self.peek_last(); + self.que_size -= 1; + num + } + + /* 访问队首元素 */ + fn peek_first(&self) -> i32 { + if self.is_empty() { + panic!("双向队列为空") + }; + self.nums[self.front] + } + + /* 访问队尾元素 */ + fn peek_last(&self) -> i32 { + if self.is_empty() { + panic!("双向队列为空") + }; + // 计算尾元素索引 + let last = self.index(self.front as i32 + self.que_size as i32 - 1); + self.nums[last] + } + + /* 返回数组用于打印 */ + fn to_array(&self) -> Vec { + // 仅转换有效长度范围内的列表元素 + let mut res = vec![0; self.que_size]; + let mut j = self.front; + for i in 0..self.que_size { + res[i] = self.nums[self.index(j as i32)]; + j += 1; + } + res + } + } + ``` + +=== "C" + + ```c title="array_deque.c" + /* 基于环形数组实现的双向队列 */ + typedef struct { + int *nums; // 用于存储队列元素的数组 + int front; // 队首指针,指向队首元素 + int queSize; // 尾指针,指向队尾 + 1 + int queCapacity; // 队列容量 + } ArrayDeque; + + /* 构造函数 */ + ArrayDeque *newArrayDeque(int capacity) { + ArrayDeque *deque = (ArrayDeque *)malloc(sizeof(ArrayDeque)); + // 初始化数组 + deque->queCapacity = capacity; + deque->nums = (int *)malloc(sizeof(int) * deque->queCapacity); + deque->front = deque->queSize = 0; + return deque; + } + + /* 析构函数 */ + void delArrayDeque(ArrayDeque *deque) { + free(deque->nums); + free(deque); + } + + /* 获取双向队列的容量 */ + int capacity(ArrayDeque *deque) { + return deque->queCapacity; + } + + /* 获取双向队列的长度 */ + int size(ArrayDeque *deque) { + return deque->queSize; + } + + /* 判断双向队列是否为空 */ + bool empty(ArrayDeque *deque) { + return deque->queSize == 0; + } + + /* 计算环形数组索引 */ + int dequeIndex(ArrayDeque *deque, int i) { + // 通过取余操作实现数组首尾相连 + // 当 i 越过数组尾部时,回到头部 + // 当 i 越过数组头部后,回到尾部 + return ((i + capacity(deque)) % capacity(deque)); + } + + /* 队首入队 */ + void pushFirst(ArrayDeque *deque, int num) { + if (deque->queSize == capacity(deque)) { + printf("双向队列已满\r\n"); + return; + } + // 队首指针向左移动一位 + // 通过取余操作实现 front 越过数组头部回到尾部 + deque->front = dequeIndex(deque, deque->front - 1); + // 将 num 添加到队首 + deque->nums[deque->front] = num; + deque->queSize++; + } + + /* 队尾入队 */ + void pushLast(ArrayDeque *deque, int num) { + if (deque->queSize == capacity(deque)) { + printf("双向队列已满\r\n"); + return; + } + // 计算队尾指针,指向队尾索引 + 1 + int rear = dequeIndex(deque, deque->front + deque->queSize); + // 将 num 添加至队尾 + deque->nums[rear] = num; + deque->queSize++; + } + + /* 访问队首元素 */ + int peekFirst(ArrayDeque *deque) { + // 访问异常:双向队列为空 + assert(empty(deque) == 0); + return deque->nums[deque->front]; + } + + /* 访问队尾元素 */ + int peekLast(ArrayDeque *deque) { + // 访问异常:双向队列为空 + assert(empty(deque) == 0); + int last = dequeIndex(deque, deque->front + deque->queSize - 1); + return deque->nums[last]; + } + + /* 队首出队 */ + int popFirst(ArrayDeque *deque) { + int num = peekFirst(deque); + // 队首指针向后移动一位 + deque->front = dequeIndex(deque, deque->front + 1); + deque->queSize--; + return num; + } + + /* 队尾出队 */ + int popLast(ArrayDeque *deque) { + int num = peekLast(deque); + deque->queSize--; + return num; + } + ``` + +=== "Kotlin" + + ```kotlin title="array_deque.kt" + /* 基于环形数组实现的双向队列 */ + class ArrayDeque(capacity: Int) { + private var nums = IntArray(capacity) // 用于存储双向队列元素的数组 + private var front = 0 // 队首指针,指向队首元素 + private var queSize = 0 // 双向队列长度 + + /* 获取双向队列的容量 */ + fun capacity(): Int { + return nums.size + } + + /* 获取双向队列的长度 */ + fun size(): Int { + return queSize + } + + /* 判断双向队列是否为空 */ + fun isEmpty(): Boolean { + return queSize == 0 + } + + /* 计算环形数组索引 */ + private fun index(i: Int): Int { + // 通过取余操作实现数组首尾相连 + // 当 i 越过数组尾部后,回到头部 + // 当 i 越过数组头部后,回到尾部 + return (i + capacity()) % capacity() + } + + /* 队首入队 */ + fun pushFirst(num: Int) { + if (queSize == capacity()) { + println("双向队列已满") + return + } + // 队首指针向左移动一位 + // 通过取余操作实现 front 越过数组头部后回到尾部 + front = index(front - 1) + // 将 num 添加至队首 + nums[front] = num + queSize++ + } + + /* 队尾入队 */ + fun pushLast(num: Int) { + if (queSize == capacity()) { + println("双向队列已满") + return + } + // 计算队尾指针,指向队尾索引 + 1 + val rear = index(front + queSize) + // 将 num 添加至队尾 + nums[rear] = num + queSize++ + } + + /* 队首出队 */ + fun popFirst(): Int { + val num = peekFirst() + // 队首指针向后移动一位 + front = index(front + 1) + queSize-- + return num + } + + /* 访问队尾元素 */ + fun popLast(): Int { + val num = peekLast() + queSize-- + return num + } + + /* 访问队首元素 */ + fun peekFirst(): Int { + if (isEmpty()) throw IndexOutOfBoundsException() + return nums[front] + } + + /* 访问队尾元素 */ + fun peekLast(): Int { + if (isEmpty()) throw IndexOutOfBoundsException() + // 计算尾元素索引 + val last = index(front + queSize - 1) + return nums[last] + } + + /* 返回数组用于打印 */ + fun toArray(): IntArray { + // 仅转换有效长度范围内的列表元素 + val res = IntArray(queSize) + var i = 0 + var j = front + while (i < queSize) { + res[i] = nums[index(j)] + i++ + j++ + } + return res + } + } + ``` + +=== "Zig" + + ```zig title="array_deque.zig" + [class]{ArrayDeque}-[func]{} + ``` ## 5.3.3   Applications of Double-Ended Queue diff --git a/en/docs/chapter_stack_and_queue/queue.md b/en/docs/chapter_stack_and_queue/queue.md index f4fdf94c6..0a62b4dd5 100755 --- a/en/docs/chapter_stack_and_queue/queue.md +++ b/en/docs/chapter_stack_and_queue/queue.md @@ -312,6 +312,12 @@ We can directly use the ready-made queue classes in programming languages: // C does not provide a built-in queue ``` +=== "Kotlin" + + ```kotlin title="queue.kt" + + ``` + === "Zig" ```zig title="queue.zig" @@ -1125,6 +1131,71 @@ Below is the code for implementing a queue using a linked list: } ``` +=== "Kotlin" + + ```kotlin title="linkedlist_queue.kt" + /* 基于链表实现的队列 */ + class LinkedListQueue( + // 头节点 front ,尾节点 rear + private var front: ListNode? = null, + private var rear: ListNode? = null, + private var queSize: Int = 0 + ) { + + /* 获取队列的长度 */ + fun size(): Int { + return queSize + } + + /* 判断队列是否为空 */ + fun isEmpty(): Boolean { + return size() == 0 + } + + /* 入队 */ + fun push(num: Int) { + // 在尾节点后添加 num + val node = ListNode(num) + // 如果队列为空,则令头、尾节点都指向该节点 + if (front == null) { + front = node + rear = node + // 如果队列不为空,则将该节点添加到尾节点后 + } else { + rear?.next = node + rear = node + } + queSize++ + } + + /* 出队 */ + fun pop(): Int { + val num = peek() + // 删除头节点 + front = front?.next + queSize-- + return num + } + + /* 访问队首元素 */ + fun peek(): Int { + if (isEmpty()) throw IndexOutOfBoundsException() + return front!!.value + } + + /* 将链表转化为 Array 并返回 */ + fun toArray(): IntArray { + var node = front + val res = IntArray(size()) + for (i in res.indices) { + res[i] = node!!.value + node = node.next + } + return res + } + } + ``` + === "Zig" ```zig title="linkedlist_queue.zig" @@ -2036,6 +2107,75 @@ In a circular array, `front` or `rear` needs to loop back to the start of the ar } ``` +=== "Kotlin" + + ```kotlin title="array_queue.kt" + /* 基于环形数组实现的队列 */ + class ArrayQueue(capacity: Int) { + private val nums = IntArray(capacity) // 用于存储队列元素的数组 + private var front = 0 // 队首指针,指向队首元素 + private var queSize = 0 // 队列长度 + + /* 获取队列的容量 */ + fun capacity(): Int { + return nums.size + } + + /* 获取队列的长度 */ + fun size(): Int { + return queSize + } + + /* 判断队列是否为空 */ + fun isEmpty(): Boolean { + return queSize == 0 + } + + /* 入队 */ + fun push(num: Int) { + if (queSize == capacity()) { + println("队列已满") + return + } + // 计算队尾指针,指向队尾索引 + 1 + // 通过取余操作实现 rear 越过数组尾部后回到头部 + val rear = (front + queSize) % capacity() + // 将 num 添加至队尾 + nums[rear] = num + queSize++ + } + + /* 出队 */ + fun pop(): Int { + val num = peek() + // 队首指针向后移动一位,若越过尾部,则返回到数组头部 + front = (front + 1) % capacity() + queSize-- + return num + } + + /* 访问队首元素 */ + fun peek(): Int { + if (isEmpty()) throw IndexOutOfBoundsException() + return nums[front] + } + + /* 返回数组 */ + fun toArray(): IntArray { + // 仅转换有效长度范围内的列表元素 + val res = IntArray(queSize) + var i = 0 + var j = front + while (i < queSize) { + res[i] = nums[j % capacity()] + i++ + j++ + } + return res + } + } + ``` + === "Zig" ```zig title="array_queue.zig" diff --git a/en/docs/chapter_stack_and_queue/stack.md b/en/docs/chapter_stack_and_queue/stack.md index db50d987b..4f028c3ff 100755 --- a/en/docs/chapter_stack_and_queue/stack.md +++ b/en/docs/chapter_stack_and_queue/stack.md @@ -306,6 +306,12 @@ Typically, we can directly use the stack class built into the programming langua // C does not provide a built-in stack ``` +=== "Kotlin" + + ```kotlin title="stack.kt" + + ``` + === "Zig" ```zig title="stack.zig" @@ -1008,6 +1014,60 @@ Below is an example code for implementing a stack based on a linked list: } ``` +=== "Kotlin" + + ```kotlin title="linkedlist_stack.kt" + /* 基于链表实现的栈 */ + class LinkedListStack( + private var stackPeek: ListNode? = null, // 将头节点作为栈顶 + private var stkSize: Int = 0 // 栈的长度 + ) { + + /* 获取栈的长度 */ + fun size(): Int { + return stkSize + } + + /* 判断栈是否为空 */ + fun isEmpty(): Boolean { + return size() == 0 + } + + /* 入栈 */ + fun push(num: Int) { + val node = ListNode(num) + node.next = stackPeek + stackPeek = node + stkSize++ + } + + /* 出栈 */ + fun pop(): Int? { + val num = peek() + stackPeek = stackPeek?.next + stkSize--; + return num + } + + /* 访问栈顶元素 */ + fun peek(): Int? { + if (isEmpty()) throw IndexOutOfBoundsException() + return stackPeek?.value + } + + /* 将 List 转化为 Array 并返回 */ + fun toArray(): IntArray { + var node = stackPeek + val res = IntArray(size()) + for (i in res.size - 1 downTo 0) { + res[i] = node?.value!! + node = node.next + } + return res + } + } + ``` + === "Zig" ```zig title="linkedlist_stack.zig" @@ -1643,6 +1703,48 @@ Since the elements to be pushed onto the stack may continuously increase, we can } ``` +=== "Kotlin" + + ```kotlin title="array_stack.kt" + /* 基于数组实现的栈 */ + class ArrayStack { + // 初始化列表(动态数组) + private val stack = ArrayList() + + /* 获取栈的长度 */ + fun size(): Int { + return stack.size + } + + /* 判断栈是否为空 */ + fun isEmpty(): Boolean { + return size() == 0 + } + + /* 入栈 */ + fun push(num: Int) { + stack.add(num) + } + + /* 出栈 */ + fun pop(): Int { + if (isEmpty()) throw IndexOutOfBoundsException() + return stack.removeAt(size() - 1) + } + + /* 访问栈顶元素 */ + fun peek(): Int { + if (isEmpty()) throw IndexOutOfBoundsException() + return stack[size() - 1] + } + + /* 将 List 转化为 Array 并返回 */ + fun toArray(): Array { + return stack.toArray() + } + } + ``` + === "Zig" ```zig title="array_stack.zig" diff --git a/overrides/stylesheets/extra.css b/overrides/stylesheets/extra.css index cccaabc20..67f0d38d2 100644 --- a/overrides/stylesheets/extra.css +++ b/overrides/stylesheets/extra.css @@ -207,6 +207,11 @@ body { width: 1.25em; } +/* code block tabs */ +.md-typeset .tabbed-labels>label { + font-size: 0.59rem; +} + /* header banner */ .md-banner { background-color: var(--md-code-bg-color);