From 7e7eb6047a4daaf7d36220bd51171de6d51f7f2a Mon Sep 17 00:00:00 2001 From: krahets Date: Mon, 6 May 2024 05:27:10 +0800 Subject: [PATCH] build --- .../dp_problem_features.md | 4 +- .../dp_solution_pipeline.md | 4 +- .../edit_distance_problem.md | 4 +- .../knapsack_problem.md | 4 +- .../unbounded_knapsack_problem.md | 12 +- en/docs/chapter_array_and_linkedlist/array.md | 860 +---- .../linked_list.md | 529 +-- en/docs/chapter_array_and_linkedlist/list.md | 1345 +------- .../backtracking_algorithm.md | 1126 +----- .../chapter_backtracking/n_queens_problem.md | 570 +--- .../permutations_problem.md | 783 +---- .../subset_sum_problem.md | 1296 +------ .../iteration_and_recursion.md | 1374 +------- .../space_complexity.md | 1092 +----- .../time_complexity.md | 1919 ++--------- .../binary_search_recur.md | 312 +- .../build_binary_tree_problem.md | 347 +- .../hanota_problem.md | 352 +- .../dp_problem_features.md | 649 +--- .../dp_solution_pipeline.md | 1073 +----- .../edit_distance_problem.md | 700 +--- .../intro_to_dynamic_programming.md | 1168 +------ .../knapsack_problem.md | 1022 +----- .../unbounded_knapsack_problem.md | 1724 +--------- en/docs/chapter_graph/graph_operations.md | 2085 +----------- en/docs/chapter_graph/graph_traversal.md | 746 +--- .../fractional_knapsack_problem.md | 387 +-- en/docs/chapter_greedy/greedy_algorithm.md | 228 +- .../chapter_greedy/max_capacity_problem.md | 244 +- .../max_product_cutting_problem.md | 239 +- en/docs/chapter_hashing/hash_algorithm.md | 487 +-- en/docs/chapter_hashing/hash_collision.md | 2933 +--------------- en/docs/chapter_hashing/hash_map.md | 1126 +----- en/docs/chapter_heap/build_heap.md | 245 +- en/docs/chapter_heap/heap.md | 1070 +----- en/docs/chapter_heap/top_k.md | 326 +- en/docs/chapter_searching/binary_search.md | 563 +-- .../chapter_searching/binary_search_edge.md | 331 +- .../binary_search_insertion.md | 471 +-- .../replace_linear_by_hashing.md | 400 +-- en/docs/chapter_sorting/bubble_sort.md | 469 +-- en/docs/chapter_sorting/bucket_sort.md | 349 +- en/docs/chapter_sorting/counting_sort.md | 694 +--- en/docs/chapter_sorting/heap_sort.md | 470 +-- en/docs/chapter_sorting/insertion_sort.md | 203 +- en/docs/chapter_sorting/merge_sort.md | 600 +--- en/docs/chapter_sorting/quick_sort.md | 1143 +------ en/docs/chapter_sorting/radix_sort.md | 687 +--- en/docs/chapter_sorting/selection_sort.md | 215 +- en/docs/chapter_stack_and_queue/deque.md | 3015 +---------------- en/docs/chapter_stack_and_queue/queue.md | 1802 +--------- en/docs/chapter_stack_and_queue/stack.md | 1320 +------- .../array_representation_of_tree.md | 1044 +----- en/docs/chapter_tree/avl_tree.md | 1965 ++--------- en/docs/chapter_tree/binary_search_tree.md | 1360 +------- en/docs/chapter_tree/binary_tree_traversal.md | 679 +--- 56 files changed, 3908 insertions(+), 42257 deletions(-) diff --git a/docs/chapter_dynamic_programming/dp_problem_features.md b/docs/chapter_dynamic_programming/dp_problem_features.md index b189a8ca2..33a0c7577 100644 --- a/docs/chapter_dynamic_programming/dp_problem_features.md +++ b/docs/chapter_dynamic_programming/dp_problem_features.md @@ -457,7 +457,7 @@ $$ === "JS" ```javascript title="min_cost_climbing_stairs_dp.js" - /* 爬楼梯最小代价:状态压缩后的动态规划 */ + /* 爬楼梯最小代价:空间优化后的动态规划 */ function minCostClimbingStairsDPComp(cost) { const n = cost.length - 1; if (n === 1 || n === 2) { @@ -477,7 +477,7 @@ $$ === "TS" ```typescript title="min_cost_climbing_stairs_dp.ts" - /* 爬楼梯最小代价:状态压缩后的动态规划 */ + /* 爬楼梯最小代价:空间优化后的动态规划 */ function minCostClimbingStairsDPComp(cost: Array): number { const n = cost.length - 1; if (n === 1 || n === 2) { diff --git a/docs/chapter_dynamic_programming/dp_solution_pipeline.md b/docs/chapter_dynamic_programming/dp_solution_pipeline.md index 0568962eb..ece76eb35 100644 --- a/docs/chapter_dynamic_programming/dp_solution_pipeline.md +++ b/docs/chapter_dynamic_programming/dp_solution_pipeline.md @@ -1361,7 +1361,7 @@ $$ === "JS" ```javascript title="min_path_sum.js" - /* 最小路径和:状态压缩后的动态规划 */ + /* 最小路径和:空间优化后的动态规划 */ function minPathSumDPComp(grid) { const n = grid.length, m = grid[0].length; @@ -1388,7 +1388,7 @@ $$ === "TS" ```typescript title="min_path_sum.ts" - /* 最小路径和:状态压缩后的动态规划 */ + /* 最小路径和:空间优化后的动态规划 */ function minPathSumDPComp(grid: Array>): number { const n = grid.length, m = grid[0].length; diff --git a/docs/chapter_dynamic_programming/edit_distance_problem.md b/docs/chapter_dynamic_programming/edit_distance_problem.md index f1b6e253e..5f440198b 100644 --- a/docs/chapter_dynamic_programming/edit_distance_problem.md +++ b/docs/chapter_dynamic_programming/edit_distance_problem.md @@ -746,7 +746,7 @@ $$ === "JS" ```javascript title="edit_distance.js" - /* 编辑距离:状态压缩后的动态规划 */ + /* 编辑距离:空间优化后的动态规划 */ function editDistanceDPComp(s, t) { const n = s.length, m = t.length; @@ -780,7 +780,7 @@ $$ === "TS" ```typescript title="edit_distance.ts" - /* 编辑距离:状态压缩后的动态规划 */ + /* 编辑距离:空间优化后的动态规划 */ function editDistanceDPComp(s: string, t: string): number { const n = s.length, m = t.length; diff --git a/docs/chapter_dynamic_programming/knapsack_problem.md b/docs/chapter_dynamic_programming/knapsack_problem.md index 1565cc706..b379fc01e 100644 --- a/docs/chapter_dynamic_programming/knapsack_problem.md +++ b/docs/chapter_dynamic_programming/knapsack_problem.md @@ -1307,7 +1307,7 @@ $$ === "JS" ```javascript title="knapsack.js" - /* 0-1 背包:状态压缩后的动态规划 */ + /* 0-1 背包:空间优化后的动态规划 */ function knapsackDPComp(wgt, val, cap) { const n = wgt.length; // 初始化 dp 表 @@ -1329,7 +1329,7 @@ $$ === "TS" ```typescript title="knapsack.ts" - /* 0-1 背包:状态压缩后的动态规划 */ + /* 0-1 背包:空间优化后的动态规划 */ function knapsackDPComp( wgt: Array, val: Array, diff --git a/docs/chapter_dynamic_programming/unbounded_knapsack_problem.md b/docs/chapter_dynamic_programming/unbounded_knapsack_problem.md index 6b62900f1..b04a3a36b 100644 --- a/docs/chapter_dynamic_programming/unbounded_knapsack_problem.md +++ b/docs/chapter_dynamic_programming/unbounded_knapsack_problem.md @@ -554,7 +554,7 @@ $$ === "JS" ```javascript title="unbounded_knapsack.js" - /* 完全背包:状态压缩后的动态规划 */ + /* 完全背包:空间优化后的动态规划 */ function unboundedKnapsackDPComp(wgt, val, cap) { const n = wgt.length; // 初始化 dp 表 @@ -578,7 +578,7 @@ $$ === "TS" ```typescript title="unbounded_knapsack.ts" - /* 完全背包:状态压缩后的动态规划 */ + /* 完全背包:空间优化后的动态规划 */ function unboundedKnapsackDPComp( wgt: Array, val: Array, @@ -1417,7 +1417,7 @@ $$ === "JS" ```javascript title="coin_change.js" - /* 零钱兑换:状态压缩后的动态规划 */ + /* 零钱兑换:空间优化后的动态规划 */ function coinChangeDPComp(coins, amt) { const n = coins.length; const MAX = amt + 1; @@ -1443,7 +1443,7 @@ $$ === "TS" ```typescript title="coin_change.ts" - /* 零钱兑换:状态压缩后的动态规划 */ + /* 零钱兑换:空间优化后的动态规划 */ function coinChangeDPComp(coins: Array, amt: number): number { const n = coins.length; const MAX = amt + 1; @@ -2190,7 +2190,7 @@ $$ === "JS" ```javascript title="coin_change_ii.js" - /* 零钱兑换 II:状态压缩后的动态规划 */ + /* 零钱兑换 II:空间优化后的动态规划 */ function coinChangeIIDPComp(coins, amt) { const n = coins.length; // 初始化 dp 表 @@ -2215,7 +2215,7 @@ $$ === "TS" ```typescript title="coin_change_ii.ts" - /* 零钱兑换 II:状态压缩后的动态规划 */ + /* 零钱兑换 II:空间优化后的动态规划 */ function coinChangeIIDPComp(coins: Array, amt: number): number { const n = coins.length; // 初始化 dp 表 diff --git a/en/docs/chapter_array_and_linkedlist/array.md b/en/docs/chapter_array_and_linkedlist/array.md index f7003ee5a..398e332d9 100755 --- a/en/docs/chapter_array_and_linkedlist/array.md +++ b/en/docs/chapter_array_and_linkedlist/array.md @@ -141,10 +141,10 @@ Accessing elements in an array is highly efficient, allowing us to randomly acce ```python title="array.py" def random_access(nums: list[int]) -> int: - """随机访问元素""" - # 在区间 [0, len(nums)-1] 中随机抽取一个数字 + """Random access to elements""" + # Randomly select a number from the interval [0, len(nums)-1] random_index = random.randint(0, len(nums) - 1) - # 获取并返回随机元素 + # Retrieve and return a random element random_num = nums[random_index] return random_num ``` @@ -152,24 +152,17 @@ Accessing elements in an array is highly efficient, allowing us to randomly acce === "C++" ```cpp title="array.cpp" - /* 随机访问元素 */ - int randomAccess(int *nums, int size) { - // 在区间 [0, size) 中随机抽取一个数字 - int randomIndex = rand() % size; - // 获取并返回随机元素 - int randomNum = nums[randomIndex]; - return randomNum; - } + [class]{}-[func]{randomAccess} ``` === "Java" ```java title="array.java" - /* 随机访问元素 */ + /* Random access to elements */ int randomAccess(int[] nums) { - // 在区间 [0, nums.length) 中随机抽取一个数字 + // Randomly select a number in the interval [0, nums.length) int randomIndex = ThreadLocalRandom.current().nextInt(0, nums.length); - // 获取并返回随机元素 + // Retrieve and return a random element int randomNum = nums[randomIndex]; return randomNum; } @@ -178,152 +171,69 @@ Accessing elements in an array is highly efficient, allowing us to randomly acce === "C#" ```csharp title="array.cs" - /* 随机访问元素 */ - int RandomAccess(int[] nums) { - Random random = new(); - // 在区间 [0, nums.Length) 中随机抽取一个数字 - int randomIndex = random.Next(nums.Length); - // 获取并返回随机元素 - int randomNum = nums[randomIndex]; - return randomNum; - } + [class]{array}-[func]{RandomAccess} ``` === "Go" ```go title="array.go" - /* 随机访问元素 */ - func randomAccess(nums []int) (randomNum int) { - // 在区间 [0, nums.length) 中随机抽取一个数字 - randomIndex := rand.Intn(len(nums)) - // 获取并返回随机元素 - randomNum = nums[randomIndex] - return - } + [class]{}-[func]{randomAccess} ``` === "Swift" ```swift title="array.swift" - /* 随机访问元素 */ - func randomAccess(nums: [Int]) -> Int { - // 在区间 [0, nums.count) 中随机抽取一个数字 - let randomIndex = nums.indices.randomElement()! - // 获取并返回随机元素 - let randomNum = nums[randomIndex] - return randomNum - } + [class]{}-[func]{randomAccess} ``` === "JS" ```javascript title="array.js" - /* 随机访问元素 */ - function randomAccess(nums) { - // 在区间 [0, nums.length) 中随机抽取一个数字 - const random_index = Math.floor(Math.random() * nums.length); - // 获取并返回随机元素 - const random_num = nums[random_index]; - return random_num; - } + [class]{}-[func]{randomAccess} ``` === "TS" ```typescript title="array.ts" - /* 随机访问元素 */ - function randomAccess(nums: number[]): number { - // 在区间 [0, nums.length) 中随机抽取一个数字 - const random_index = Math.floor(Math.random() * nums.length); - // 获取并返回随机元素 - const random_num = nums[random_index]; - return random_num; - } + [class]{}-[func]{randomAccess} ``` === "Dart" ```dart title="array.dart" - /* 随机访问元素 */ - int randomAccess(List nums) { - // 在区间 [0, nums.length) 中随机抽取一个数字 - int randomIndex = Random().nextInt(nums.length); - // 获取并返回随机元素 - int randomNum = nums[randomIndex]; - return randomNum; - } + [class]{}-[func]{randomAccess} ``` === "Rust" ```rust title="array.rs" - /* 随机访问元素 */ - fn random_access(nums: &[i32]) -> i32 { - // 在区间 [0, nums.len()) 中随机抽取一个数字 - let random_index = rand::thread_rng().gen_range(0..nums.len()); - // 获取并返回随机元素 - let random_num = nums[random_index]; - random_num - } + [class]{}-[func]{random_access} ``` === "C" ```c title="array.c" - /* 随机访问元素 */ - int randomAccess(int *nums, int size) { - // 在区间 [0, size) 中随机抽取一个数字 - int randomIndex = rand() % size; - // 获取并返回随机元素 - int randomNum = nums[randomIndex]; - return randomNum; - } + [class]{}-[func]{randomAccess} ``` === "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 - } + [class]{}-[func]{randomAccess} ``` === "Ruby" ```ruby title="array.rb" - ### 随机访问元素 ### - def random_access(nums) - # 在区间 [0, nums.length) 中随机抽取一个数字 - random_index = Random.rand(0...nums.length) - - # 获取并返回随机元素 - nums[random_index] - end + [class]{}-[func]{random_access} ``` === "Zig" ```zig title="array.zig" - // 随机访问元素 - fn randomAccess(nums: []i32) i32 { - // 在区间 [0, nums.len) 中随机抽取一个整数 - var randomIndex = std.crypto.random.intRangeLessThan(usize, 0, nums.len); - // 获取并返回随机元素 - var randomNum = nums[randomIndex]; - return randomNum; - } + [class]{}-[func]{randomAccess} ``` -??? pythontutor "Code Visualization" - -
- - ### 3.   Inserting elements Array elements are tightly packed in memory, with no space available to accommodate additional data between them. Illustrated in Figure below, inserting an element in the middle of an array requires shifting all subsequent elements back by one position to create room for the new element. @@ -338,38 +248,30 @@ It's important to note that due to the fixed length of an array, inserting an el ```python title="array.py" def insert(nums: list[int], num: int, index: int): - """在数组的索引 index 处插入元素 num""" - # 把索引 index 以及之后的所有元素向后移动一位 + """Insert element num at `index`""" + # Move all elements after `index` one position backward for i in range(len(nums) - 1, index, -1): nums[i] = nums[i - 1] - # 将 num 赋给 index 处的元素 + # Assign num to the element at index nums[index] = num ``` === "C++" ```cpp title="array.cpp" - /* 在数组的索引 index 处插入元素 num */ - void insert(int *nums, int size, int num, int index) { - // 把索引 index 以及之后的所有元素向后移动一位 - for (int i = size - 1; i > index; i--) { - nums[i] = nums[i - 1]; - } - // 将 num 赋给 index 处的元素 - nums[index] = num; - } + [class]{}-[func]{insert} ``` === "Java" ```java title="array.java" - /* 在数组的索引 index 处插入元素 num */ + /* Insert element num at `index` */ void insert(int[] nums, int num, int index) { - // 把索引 index 以及之后的所有元素向后移动一位 + // Move all elements after `index` one position backward for (int i = nums.length - 1; i > index; i--) { nums[i] = nums[i - 1]; } - // 将 num 赋给 index 处的元素 + // Assign num to the element at index nums[index] = num; } ``` @@ -377,164 +279,69 @@ It's important to note that due to the fixed length of an array, inserting an el === "C#" ```csharp title="array.cs" - /* 在数组的索引 index 处插入元素 num */ - void Insert(int[] nums, int num, int index) { - // 把索引 index 以及之后的所有元素向后移动一位 - for (int i = nums.Length - 1; i > index; i--) { - nums[i] = nums[i - 1]; - } - // 将 num 赋给 index 处的元素 - nums[index] = num; - } + [class]{array}-[func]{Insert} ``` === "Go" ```go title="array.go" - /* 在数组的索引 index 处插入元素 num */ - func insert(nums []int, num int, index int) { - // 把索引 index 以及之后的所有元素向后移动一位 - for i := len(nums) - 1; i > index; i-- { - nums[i] = nums[i-1] - } - // 将 num 赋给 index 处的元素 - nums[index] = num - } + [class]{}-[func]{insert} ``` === "Swift" ```swift title="array.swift" - /* 在数组的索引 index 处插入元素 num */ - func insert(nums: inout [Int], num: Int, index: Int) { - // 把索引 index 以及之后的所有元素向后移动一位 - for i in nums.indices.dropFirst(index).reversed() { - nums[i] = nums[i - 1] - } - // 将 num 赋给 index 处的元素 - nums[index] = num - } + [class]{}-[func]{insert} ``` === "JS" ```javascript title="array.js" - /* 在数组的索引 index 处插入元素 num */ - function insert(nums, num, index) { - // 把索引 index 以及之后的所有元素向后移动一位 - for (let i = nums.length - 1; i > index; i--) { - nums[i] = nums[i - 1]; - } - // 将 num 赋给 index 处的元素 - nums[index] = num; - } + [class]{}-[func]{insert} ``` === "TS" ```typescript title="array.ts" - /* 在数组的索引 index 处插入元素 num */ - function insert(nums: number[], num: number, index: number): void { - // 把索引 index 以及之后的所有元素向后移动一位 - for (let i = nums.length - 1; i > index; i--) { - nums[i] = nums[i - 1]; - } - // 将 num 赋给 index 处的元素 - nums[index] = num; - } + [class]{}-[func]{insert} ``` === "Dart" ```dart title="array.dart" - /* 在数组的索引 index 处插入元素 _num */ - void insert(List nums, int _num, int index) { - // 把索引 index 以及之后的所有元素向后移动一位 - for (var i = nums.length - 1; i > index; i--) { - nums[i] = nums[i - 1]; - } - // 将 _num 赋给 index 处元素 - nums[index] = _num; - } + [class]{}-[func]{insert} ``` === "Rust" ```rust title="array.rs" - /* 在数组的索引 index 处插入元素 num */ - fn insert(nums: &mut Vec, num: i32, index: usize) { - // 把索引 index 以及之后的所有元素向后移动一位 - for i in (index + 1..nums.len()).rev() { - nums[i] = nums[i - 1]; - } - // 将 num 赋给 index 处的元素 - nums[index] = num; - } + [class]{}-[func]{insert} ``` === "C" ```c title="array.c" - /* 在数组的索引 index 处插入元素 num */ - void insert(int *nums, int size, int num, int index) { - // 把索引 index 以及之后的所有元素向后移动一位 - for (int i = size - 1; i > index; i--) { - nums[i] = nums[i - 1]; - } - // 将 num 赋给 index 处的元素 - nums[index] = num; - } + [class]{}-[func]{insert} ``` === "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 - } + [class]{}-[func]{insert} ``` === "Ruby" ```ruby title="array.rb" - ### 在数组的索引 index 处插入元素 num ### - def insert(nums, num, index) - # 把索引 index 以及之后的所有元素向后移动一位 - for i in (nums.length - 1).downto(index + 1) - nums[i] = nums[i - 1] - end - - # 将 num 赋给 index 处的元素 - nums[index] = num - end + [class]{}-[func]{insert} ``` === "Zig" ```zig title="array.zig" - // 在数组的索引 index 处插入元素 num - fn insert(nums: []i32, num: i32, index: usize) void { - // 把索引 index 以及之后的所有元素向后移动一位 - var i = nums.len - 1; - while (i > index) : (i -= 1) { - nums[i] = nums[i - 1]; - } - // 将 num 赋给 index 处的元素 - nums[index] = num; - } + [class]{}-[func]{insert} ``` -??? pythontutor "Code Visualization" - -
- - ### 4.   Deleting elements Similarly, as depicted in Figure 4-4, to delete an element at index $i$, all elements following index $i$ must be moved forward by one position. @@ -549,8 +356,8 @@ Please note that after deletion, the former last element becomes "meaningless," ```python title="array.py" def remove(nums: list[int], index: int): - """删除索引 index 处的元素""" - # 把索引 index 之后的所有元素向前移动一位 + """Remove the element at `index`""" + # Move all elements after `index` one position forward for i in range(index, len(nums) - 1): nums[i] = nums[i + 1] ``` @@ -558,21 +365,15 @@ Please note that after deletion, the former last element becomes "meaningless," === "C++" ```cpp title="array.cpp" - /* 删除索引 index 处的元素 */ - void remove(int *nums, int size, int index) { - // 把索引 index 之后的所有元素向前移动一位 - for (int i = index; i < size - 1; i++) { - nums[i] = nums[i + 1]; - } - } + [class]{}-[func]{remove} ``` === "Java" ```java title="array.java" - /* 删除索引 index 处的元素 */ + /* Remove the element at `index` */ void remove(int[] nums, int index) { - // 把索引 index 之后的所有元素向前移动一位 + // Move all elements after `index` one position forward for (int i = index; i < nums.length - 1; i++) { nums[i] = nums[i + 1]; } @@ -582,142 +383,69 @@ Please note that after deletion, the former last element becomes "meaningless," === "C#" ```csharp title="array.cs" - /* 删除索引 index 处的元素 */ - void Remove(int[] nums, int index) { - // 把索引 index 之后的所有元素向前移动一位 - for (int i = index; i < nums.Length - 1; i++) { - nums[i] = nums[i + 1]; - } - } + [class]{array}-[func]{Remove} ``` === "Go" ```go title="array.go" - /* 删除索引 index 处的元素 */ - func remove(nums []int, index int) { - // 把索引 index 之后的所有元素向前移动一位 - for i := index; i < len(nums)-1; i++ { - nums[i] = nums[i+1] - } - } + [class]{}-[func]{remove} ``` === "Swift" ```swift title="array.swift" - /* 删除索引 index 处的元素 */ - func remove(nums: inout [Int], index: Int) { - // 把索引 index 之后的所有元素向前移动一位 - for i in nums.indices.dropFirst(index).dropLast() { - nums[i] = nums[i + 1] - } - } + [class]{}-[func]{remove} ``` === "JS" ```javascript title="array.js" - /* 删除索引 index 处的元素 */ - function remove(nums, index) { - // 把索引 index 之后的所有元素向前移动一位 - for (let i = index; i < nums.length - 1; i++) { - nums[i] = nums[i + 1]; - } - } + [class]{}-[func]{remove} ``` === "TS" ```typescript title="array.ts" - /* 删除索引 index 处的元素 */ - function remove(nums: number[], index: number): void { - // 把索引 index 之后的所有元素向前移动一位 - for (let i = index; i < nums.length - 1; i++) { - nums[i] = nums[i + 1]; - } - } + [class]{}-[func]{remove} ``` === "Dart" ```dart title="array.dart" - /* 删除索引 index 处的元素 */ - void remove(List nums, int index) { - // 把索引 index 之后的所有元素向前移动一位 - for (var i = index; i < nums.length - 1; i++) { - nums[i] = nums[i + 1]; - } - } + [class]{}-[func]{remove} ``` === "Rust" ```rust title="array.rs" - /* 删除索引 index 处的元素 */ - fn remove(nums: &mut Vec, index: usize) { - // 把索引 index 之后的所有元素向前移动一位 - for i in index..nums.len() - 1 { - nums[i] = nums[i + 1]; - } - } + [class]{}-[func]{remove} ``` === "C" ```c title="array.c" - /* 删除索引 index 处的元素 */ - // 注意:stdio.h 占用了 remove 关键词 - void removeItem(int *nums, int size, int index) { - // 把索引 index 之后的所有元素向前移动一位 - for (int i = index; i < size - 1; i++) { - nums[i] = nums[i + 1]; - } - } + [class]{}-[func]{removeItem} ``` === "Kotlin" ```kotlin title="array.kt" - /* 删除索引 index 处的元素 */ - fun remove(nums: IntArray, index: Int) { - // 把索引 index 之后的所有元素向前移动一位 - for (i in index.. - - In summary, the insertion and deletion operations in arrays present the following disadvantages: - **High time complexity**: Both insertion and deletion in an array have an average time complexity of $O(n)$, where $n$ is the length of the array. @@ -732,15 +460,15 @@ In most programming languages, we can traverse an array either by using indices ```python title="array.py" def traverse(nums: list[int]): - """遍历数组""" + """Traverse array""" count = 0 - # 通过索引遍历数组 + # Traverse array by index for i in range(len(nums)): count += nums[i] - # 直接遍历数组元素 + # Traverse array elements for num in nums: count += num - # 同时遍历数据索引和元素 + # Traverse both data index and elements for i, num in enumerate(nums): count += nums[i] count += num @@ -749,27 +477,20 @@ In most programming languages, we can traverse an array either by using indices === "C++" ```cpp title="array.cpp" - /* 遍历数组 */ - void traverse(int *nums, int size) { - int count = 0; - // 通过索引遍历数组 - for (int i = 0; i < size; i++) { - count += nums[i]; - } - } + [class]{}-[func]{traverse} ``` === "Java" ```java title="array.java" - /* 遍历数组 */ + /* Traverse array */ void traverse(int[] nums) { int count = 0; - // 通过索引遍历数组 + // Traverse array by index for (int i = 0; i < nums.length; i++) { count += nums[i]; } - // 直接遍历数组元素 + // Traverse array elements for (int num : nums) { count += num; } @@ -779,210 +500,69 @@ In most programming languages, we can traverse an array either by using indices === "C#" ```csharp title="array.cs" - /* 遍历数组 */ - void Traverse(int[] nums) { - int count = 0; - // 通过索引遍历数组 - for (int i = 0; i < nums.Length; i++) { - count += nums[i]; - } - // 直接遍历数组元素 - foreach (int num in nums) { - count += num; - } - } + [class]{array}-[func]{Traverse} ``` === "Go" ```go title="array.go" - /* 遍历数组 */ - func traverse(nums []int) { - count := 0 - // 通过索引遍历数组 - for i := 0; i < len(nums); i++ { - count += nums[i] - } - count = 0 - // 直接遍历数组元素 - for _, num := range nums { - count += num - } - // 同时遍历数据索引和元素 - for i, num := range nums { - count += nums[i] - count += num - } - } + [class]{}-[func]{traverse} ``` === "Swift" ```swift title="array.swift" - /* 遍历数组 */ - func traverse(nums: [Int]) { - var count = 0 - // 通过索引遍历数组 - for i in nums.indices { - count += nums[i] - } - // 直接遍历数组元素 - for num in nums { - count += num - } - // 同时遍历数据索引和元素 - for (i, num) in nums.enumerated() { - count += nums[i] - count += num - } - } + [class]{}-[func]{traverse} ``` === "JS" ```javascript title="array.js" - /* 遍历数组 */ - function traverse(nums) { - let count = 0; - // 通过索引遍历数组 - for (let i = 0; i < nums.length; i++) { - count += nums[i]; - } - // 直接遍历数组元素 - for (const num of nums) { - count += num; - } - } + [class]{}-[func]{traverse} ``` === "TS" ```typescript title="array.ts" - /* 遍历数组 */ - function traverse(nums: number[]): void { - let count = 0; - // 通过索引遍历数组 - for (let i = 0; i < nums.length; i++) { - count += nums[i]; - } - // 直接遍历数组元素 - for (const num of nums) { - count += num; - } - } + [class]{}-[func]{traverse} ``` === "Dart" ```dart title="array.dart" - /* 遍历数组元素 */ - void traverse(List nums) { - int count = 0; - // 通过索引遍历数组 - for (var i = 0; i < nums.length; i++) { - count += nums[i]; - } - // 直接遍历数组元素 - for (int _num in nums) { - count += _num; - } - // 通过 forEach 方法遍历数组 - nums.forEach((_num) { - count += _num; - }); - } + [class]{}-[func]{traverse} ``` === "Rust" ```rust title="array.rs" - /* 遍历数组 */ - fn traverse(nums: &[i32]) { - let mut _count = 0; - // 通过索引遍历数组 - for i in 0..nums.len() { - _count += nums[i]; - } - // 直接遍历数组元素 - for num in nums { - _count += num; - } - } + [class]{}-[func]{traverse} ``` === "C" ```c title="array.c" - /* 遍历数组 */ - void traverse(int *nums, int size) { - int count = 0; - // 通过索引遍历数组 - for (int i = 0; i < size; i++) { - count += nums[i]; - } - } + [class]{}-[func]{traverse} ``` === "Kotlin" ```kotlin title="array.kt" - /* 遍历数组 */ - fun traverse(nums: IntArray) { - var count = 0 - // 通过索引遍历数组 - for (i in nums.indices) { - count += nums[i] - } - // 直接遍历数组元素 - for (j in nums) { - count += j - } - } + [class]{}-[func]{traverse} ``` === "Ruby" ```ruby title="array.rb" - ### 遍历数组 ### - def traverse(nums) - count = 0 - - # 通过索引遍历数组 - for i in 0...nums.length - count += nums[i] - end - - # 直接遍历数组元素 - for num in nums - count += num - end - end + [class]{}-[func]{traverse} ``` === "Zig" ```zig title="array.zig" - // 遍历数组 - fn traverse(nums: []i32) void { - var count: i32 = 0; - // 通过索引遍历数组 - var i: i32 = 0; - while (i < nums.len) : (i += 1) { - count += nums[i]; - } - count = 0; - // 直接遍历数组元素 - for (nums) |num| { - count += num; - } - } + [class]{}-[func]{traverse} ``` -??? pythontutor "Code Visualization" - -
- - ### 6.   Finding elements Locating a specific element within an array involves iterating through the array, checking each element to determine if it matches the desired value. @@ -993,7 +573,7 @@ Because arrays are linear data structures, this operation is commonly referred t ```python title="array.py" def find(nums: list[int], target: int) -> int: - """在数组中查找指定元素""" + """Search for a specified element in the array""" for i in range(len(nums)): if nums[i] == target: return i @@ -1003,20 +583,13 @@ Because arrays are linear data structures, this operation is commonly referred t === "C++" ```cpp title="array.cpp" - /* 在数组中查找指定元素 */ - int find(int *nums, int size, int target) { - for (int i = 0; i < size; i++) { - if (nums[i] == target) - return i; - } - return -1; - } + [class]{}-[func]{find} ``` === "Java" ```java title="array.java" - /* 在数组中查找指定元素 */ + /* Search for a specified element in the array */ int find(int[] nums, int target) { for (int i = 0; i < nums.length; i++) { if (nums[i] == target) @@ -1029,154 +602,69 @@ Because arrays are linear data structures, this operation is commonly referred t === "C#" ```csharp title="array.cs" - /* 在数组中查找指定元素 */ - int Find(int[] nums, int target) { - for (int i = 0; i < nums.Length; i++) { - if (nums[i] == target) - return i; - } - return -1; - } + [class]{array}-[func]{Find} ``` === "Go" ```go title="array.go" - /* 在数组中查找指定元素 */ - func find(nums []int, target int) (index int) { - index = -1 - for i := 0; i < len(nums); i++ { - if nums[i] == target { - index = i - break - } - } - return - } + [class]{}-[func]{find} ``` === "Swift" ```swift title="array.swift" - /* 在数组中查找指定元素 */ - func find(nums: [Int], target: Int) -> Int { - for i in nums.indices { - if nums[i] == target { - return i - } - } - return -1 - } + [class]{}-[func]{find} ``` === "JS" ```javascript title="array.js" - /* 在数组中查找指定元素 */ - function find(nums, target) { - for (let i = 0; i < nums.length; i++) { - if (nums[i] === target) return i; - } - return -1; - } + [class]{}-[func]{find} ``` === "TS" ```typescript title="array.ts" - /* 在数组中查找指定元素 */ - function find(nums: number[], target: number): number { - for (let i = 0; i < nums.length; i++) { - if (nums[i] === target) { - return i; - } - } - return -1; - } + [class]{}-[func]{find} ``` === "Dart" ```dart title="array.dart" - /* 在数组中查找指定元素 */ - int find(List nums, int target) { - for (var i = 0; i < nums.length; i++) { - if (nums[i] == target) return i; - } - return -1; - } + [class]{}-[func]{find} ``` === "Rust" ```rust title="array.rs" - /* 在数组中查找指定元素 */ - fn find(nums: &[i32], target: i32) -> Option { - for i in 0..nums.len() { - if nums[i] == target { - return Some(i); - } - } - None - } + [class]{}-[func]{find} ``` === "C" ```c title="array.c" - /* 在数组中查找指定元素 */ - int find(int *nums, int size, int target) { - for (int i = 0; i < size; i++) { - if (nums[i] == target) - return i; - } - return -1; - } + [class]{}-[func]{find} ``` === "Kotlin" ```kotlin title="array.kt" - /* 在数组中查找指定元素 */ - fun find(nums: IntArray, target: Int): Int { - for (i in nums.indices) { - if (nums[i] == target) - return i - } - return -1 - } + [class]{}-[func]{find} ``` === "Ruby" ```ruby title="array.rb" - ### 在数组中查找指定元素 ### - def find(nums, target) - for i in 0...nums.length - return i if nums[i] == target - end - - -1 - end + [class]{}-[func]{find} ``` === "Zig" ```zig title="array.zig" - // 在数组中查找指定元素 - fn find(nums: []i32, target: i32) i32 { - for (nums, 0..) |num, i| { - if (num == target) return @intCast(i); - } - return -1; - } + [class]{}-[func]{find} ``` -??? pythontutor "Code Visualization" - -
- - ### 7.   Expanding arrays In complex system environments, ensuring the availability of memory space after an array for safe capacity extension becomes challenging. Consequently, in most programming languages, **the length of an array is immutable**. @@ -1187,46 +675,34 @@ To expand an array, it's necessary to create a larger array and then copy the e ```python title="array.py" def extend(nums: list[int], enlarge: int) -> list[int]: - """扩展数组长度""" - # 初始化一个扩展长度后的数组 + """Extend array length""" + # Initialize an extended length array res = [0] * (len(nums) + enlarge) - # 将原数组中的所有元素复制到新数组 + # Copy all elements from the original array to the new array for i in range(len(nums)): res[i] = nums[i] - # 返回扩展后的新数组 + # Return the new array after expansion return res ``` === "C++" ```cpp title="array.cpp" - /* 扩展数组长度 */ - int *extend(int *nums, int size, int enlarge) { - // 初始化一个扩展长度后的数组 - int *res = new int[size + enlarge]; - // 将原数组中的所有元素复制到新数组 - for (int i = 0; i < size; i++) { - res[i] = nums[i]; - } - // 释放内存 - delete[] nums; - // 返回扩展后的新数组 - return res; - } + [class]{}-[func]{extend} ``` === "Java" ```java title="array.java" - /* 扩展数组长度 */ + /* Extend array length */ int[] extend(int[] nums, int enlarge) { - // 初始化一个扩展长度后的数组 + // Initialize an extended length array int[] res = new int[nums.length + enlarge]; - // 将原数组中的所有元素复制到新数组 + // Copy all elements from the original array to the new array for (int i = 0; i < nums.length; i++) { res[i] = nums[i]; } - // 返回扩展后的新数组 + // Return the new array after expansion return res; } ``` @@ -1234,195 +710,69 @@ To expand an array, it's necessary to create a larger array and then copy the e === "C#" ```csharp title="array.cs" - /* 扩展数组长度 */ - int[] Extend(int[] nums, int enlarge) { - // 初始化一个扩展长度后的数组 - int[] res = new int[nums.Length + enlarge]; - // 将原数组中的所有元素复制到新数组 - for (int i = 0; i < nums.Length; i++) { - res[i] = nums[i]; - } - // 返回扩展后的新数组 - return res; - } + [class]{array}-[func]{Extend} ``` === "Go" ```go title="array.go" - /* 扩展数组长度 */ - func extend(nums []int, enlarge int) []int { - // 初始化一个扩展长度后的数组 - res := make([]int, len(nums)+enlarge) - // 将原数组中的所有元素复制到新数组 - for i, num := range nums { - res[i] = num - } - // 返回扩展后的新数组 - return res - } + [class]{}-[func]{extend} ``` === "Swift" ```swift title="array.swift" - /* 扩展数组长度 */ - func extend(nums: [Int], enlarge: Int) -> [Int] { - // 初始化一个扩展长度后的数组 - var res = Array(repeating: 0, count: nums.count + enlarge) - // 将原数组中的所有元素复制到新数组 - for i in nums.indices { - res[i] = nums[i] - } - // 返回扩展后的新数组 - return res - } + [class]{}-[func]{extend} ``` === "JS" ```javascript title="array.js" - /* 扩展数组长度 */ - // 请注意,JavaScript 的 Array 是动态数组,可以直接扩展 - // 为了方便学习,本函数将 Array 看作长度不可变的数组 - function extend(nums, enlarge) { - // 初始化一个扩展长度后的数组 - const res = new Array(nums.length + enlarge).fill(0); - // 将原数组中的所有元素复制到新数组 - for (let i = 0; i < nums.length; i++) { - res[i] = nums[i]; - } - // 返回扩展后的新数组 - return res; - } + [class]{}-[func]{extend} ``` === "TS" ```typescript title="array.ts" - /* 扩展数组长度 */ - // 请注意,TypeScript 的 Array 是动态数组,可以直接扩展 - // 为了方便学习,本函数将 Array 看作长度不可变的数组 - function extend(nums: number[], enlarge: number): number[] { - // 初始化一个扩展长度后的数组 - const res = new Array(nums.length + enlarge).fill(0); - // 将原数组中的所有元素复制到新数组 - for (let i = 0; i < nums.length; i++) { - res[i] = nums[i]; - } - // 返回扩展后的新数组 - return res; - } + [class]{}-[func]{extend} ``` === "Dart" ```dart title="array.dart" - /* 扩展数组长度 */ - List extend(List nums, int enlarge) { - // 初始化一个扩展长度后的数组 - List res = List.filled(nums.length + enlarge, 0); - // 将原数组中的所有元素复制到新数组 - for (var i = 0; i < nums.length; i++) { - res[i] = nums[i]; - } - // 返回扩展后的新数组 - return res; - } + [class]{}-[func]{extend} ``` === "Rust" ```rust title="array.rs" - /* 扩展数组长度 */ - fn extend(nums: Vec, enlarge: usize) -> Vec { - // 初始化一个扩展长度后的数组 - let mut res: Vec = vec![0; nums.len() + enlarge]; - // 将原数组中的所有元素复制到新 - for i in 0..nums.len() { - res[i] = nums[i]; - } - // 返回扩展后的新数组 - res - } + [class]{}-[func]{extend} ``` === "C" ```c title="array.c" - /* 扩展数组长度 */ - int *extend(int *nums, int size, int enlarge) { - // 初始化一个扩展长度后的数组 - int *res = (int *)malloc(sizeof(int) * (size + enlarge)); - // 将原数组中的所有元素复制到新数组 - for (int i = 0; i < size; i++) { - res[i] = nums[i]; - } - // 初始化扩展后的空间 - for (int i = size; i < size + enlarge; i++) { - res[i] = 0; - } - // 返回扩展后的新数组 - return res; - } + [class]{}-[func]{extend} ``` === "Kotlin" ```kotlin title="array.kt" - /* 扩展数组长度 */ - fun extend(nums: IntArray, enlarge: Int): IntArray { - // 初始化一个扩展长度后的数组 - val res = IntArray(nums.size + enlarge) - // 将原数组中的所有元素复制到新数组 - for (i in nums.indices) { - res[i] = nums[i] - } - // 返回扩展后的新数组 - return res - } + [class]{}-[func]{extend} ``` === "Ruby" ```ruby title="array.rb" - ### 扩展数组长度 ### - # 请注意,Ruby 的 Array 是动态数组,可以直接扩展 - # 为了方便学习,本函数将 Array 看作长度不可变的数组 - def extend(nums, enlarge) - # 初始化一个扩展长度后的数组 - res = Array.new(nums.length + enlarge, 0) - - # 将原数组中的所有元素复制到新数组 - for i in 0...nums.length - res[i] = nums[i] - end - - # 返回扩展后的新数组 - res - end + [class]{}-[func]{extend} ``` === "Zig" ```zig title="array.zig" - // 扩展数组长度 - fn extend(mem_allocator: std.mem.Allocator, nums: []i32, enlarge: usize) ![]i32 { - // 初始化一个扩展长度后的数组 - var res = try mem_allocator.alloc(i32, nums.len + enlarge); - @memset(res, 0); - // 将原数组中的所有元素复制到新数组 - std.mem.copy(i32, res, nums); - // 返回扩展后的新数组 - return res; - } + [class]{}-[func]{extend} ``` -??? pythontutor "Code Visualization" - -
- - ## 4.1.2   Advantages and limitations of arrays Arrays are stored in contiguous memory spaces and consist of elements of the same type. This approach provides substantial prior information that systems can leverage to optimize the efficiency of data structure operations. diff --git a/en/docs/chapter_array_and_linkedlist/linked_list.md b/en/docs/chapter_array_and_linkedlist/linked_list.md index 81a1f14a3..c743455cf 100755 --- a/en/docs/chapter_array_and_linkedlist/linked_list.md +++ b/en/docs/chapter_array_and_linkedlist/linked_list.md @@ -424,7 +424,7 @@ By comparison, inserting an element into an array has a time complexity of $O(n) ```python title="linked_list.py" def insert(n0: ListNode, P: ListNode): - """在链表的节点 n0 之后插入节点 P""" + """Insert node P after node n0 in the linked list""" n1 = n0.next P.next = n1 n0.next = P @@ -433,18 +433,13 @@ By comparison, inserting an element into an array has a time complexity of $O(n) === "C++" ```cpp title="linked_list.cpp" - /* 在链表的节点 n0 之后插入节点 P */ - void insert(ListNode *n0, ListNode *P) { - ListNode *n1 = n0->next; - P->next = n1; - n0->next = P; - } + [class]{}-[func]{insert} ``` === "Java" ```java title="linked_list.java" - /* 在链表的节点 n0 之后插入节点 P */ + /* Insert node P after node n0 in the linked list */ void insert(ListNode n0, ListNode P) { ListNode n1 = n0.next; P.next = n1; @@ -455,131 +450,69 @@ By comparison, inserting an element into an array has a time complexity of $O(n) === "C#" ```csharp title="linked_list.cs" - /* 在链表的节点 n0 之后插入节点 P */ - void Insert(ListNode n0, ListNode P) { - ListNode? n1 = n0.next; - P.next = n1; - n0.next = P; - } + [class]{linked_list}-[func]{Insert} ``` === "Go" ```go title="linked_list.go" - /* 在链表的节点 n0 之后插入节点 P */ - func insertNode(n0 *ListNode, P *ListNode) { - n1 := n0.Next - P.Next = n1 - n0.Next = P - } + [class]{}-[func]{insertNode} ``` === "Swift" ```swift title="linked_list.swift" - /* 在链表的节点 n0 之后插入节点 P */ - func insert(n0: ListNode, P: ListNode) { - let n1 = n0.next - P.next = n1 - n0.next = P - } + [class]{}-[func]{insert} ``` === "JS" ```javascript title="linked_list.js" - /* 在链表的节点 n0 之后插入节点 P */ - function insert(n0, P) { - const n1 = n0.next; - P.next = n1; - n0.next = P; - } + [class]{}-[func]{insert} ``` === "TS" ```typescript title="linked_list.ts" - /* 在链表的节点 n0 之后插入节点 P */ - function insert(n0: ListNode, P: ListNode): void { - const n1 = n0.next; - P.next = n1; - n0.next = P; - } + [class]{}-[func]{insert} ``` === "Dart" ```dart title="linked_list.dart" - /* 在链表的节点 n0 之后插入节点 P */ - void insert(ListNode n0, ListNode P) { - ListNode? n1 = n0.next; - P.next = n1; - n0.next = P; - } + [class]{}-[func]{insert} ``` === "Rust" ```rust title="linked_list.rs" - /* 在链表的节点 n0 之后插入节点 P */ - #[allow(non_snake_case)] - pub fn insert(n0: &Rc>>, P: Rc>>) { - let n1 = n0.borrow_mut().next.take(); - P.borrow_mut().next = n1; - n0.borrow_mut().next = Some(P); - } + [class]{}-[func]{insert} ``` === "C" ```c title="linked_list.c" - /* 在链表的节点 n0 之后插入节点 P */ - void insert(ListNode *n0, ListNode *P) { - ListNode *n1 = n0->next; - P->next = n1; - n0->next = P; - } + [class]{}-[func]{insert} ``` === "Kotlin" ```kotlin title="linked_list.kt" - /* 在链表的节点 n0 之后插入节点 P */ - fun insert(n0: ListNode?, p: ListNode?) { - val n1 = n0?.next - p?.next = n1 - n0?.next = p - } + [class]{}-[func]{insert} ``` === "Ruby" ```ruby title="linked_list.rb" - ### 在链表的节点 n0 之后插入节点 _p ### - # Ruby 的 `p` 是一个内置函数, `P` 是一个常量,所以可以使用 `_p` 代替 - def insert(n0, _p) - n1 = n0.next - _p.next = n1 - n0.next = _p - end + [class]{}-[func]{insert} ``` === "Zig" ```zig title="linked_list.zig" - // 在链表的节点 n0 之后插入节点 P - fn insert(n0: ?*inc.ListNode(i32), P: ?*inc.ListNode(i32)) void { - var n1 = n0.?.next; - P.?.next = n1; - n0.?.next = P; - } + [class]{}-[func]{insert} ``` -??? pythontutor "Code Visualization" - -
- - ### 3.   Deleting nodes As shown in the figure, deleting a node from a linked list is also very easy, **involving only the modification of a single node's reference (pointer)**. @@ -594,7 +527,7 @@ It's important to note that even though node `P` continues to point to `n1` afte ```python title="linked_list.py" def remove(n0: ListNode): - """删除链表的节点 n0 之后的首个节点""" + """Remove the first node after node n0 in the linked list""" if not n0.next: return # n0 -> P -> n1 @@ -606,23 +539,13 @@ It's important to note that even though node `P` continues to point to `n1` afte === "C++" ```cpp title="linked_list.cpp" - /* 删除链表的节点 n0 之后的首个节点 */ - void remove(ListNode *n0) { - if (n0->next == nullptr) - return; - // n0 -> P -> n1 - ListNode *P = n0->next; - ListNode *n1 = P->next; - n0->next = n1; - // 释放内存 - delete P; - } + [class]{}-[func]{remove} ``` === "Java" ```java title="linked_list.java" - /* 删除链表的节点 n0 之后的首个节点 */ + /* Remove the first node after node n0 in the linked list */ void remove(ListNode n0) { if (n0.next == null) return; @@ -636,169 +559,69 @@ It's important to note that even though node `P` continues to point to `n1` afte === "C#" ```csharp title="linked_list.cs" - /* 删除链表的节点 n0 之后的首个节点 */ - void Remove(ListNode n0) { - if (n0.next == null) - return; - // n0 -> P -> n1 - ListNode P = n0.next; - ListNode? n1 = P.next; - n0.next = n1; - } + [class]{linked_list}-[func]{Remove} ``` === "Go" ```go title="linked_list.go" - /* 删除链表的节点 n0 之后的首个节点 */ - func removeItem(n0 *ListNode) { - if n0.Next == nil { - return - } - // n0 -> P -> n1 - P := n0.Next - n1 := P.Next - n0.Next = n1 - } + [class]{}-[func]{removeItem} ``` === "Swift" ```swift title="linked_list.swift" - /* 删除链表的节点 n0 之后的首个节点 */ - func remove(n0: ListNode) { - if n0.next == nil { - return - } - // n0 -> P -> n1 - let P = n0.next - let n1 = P?.next - n0.next = n1 - } + [class]{}-[func]{remove} ``` === "JS" ```javascript title="linked_list.js" - /* 删除链表的节点 n0 之后的首个节点 */ - function remove(n0) { - if (!n0.next) return; - // n0 -> P -> n1 - const P = n0.next; - const n1 = P.next; - n0.next = n1; - } + [class]{}-[func]{remove} ``` === "TS" ```typescript title="linked_list.ts" - /* 删除链表的节点 n0 之后的首个节点 */ - function remove(n0: ListNode): void { - if (!n0.next) { - return; - } - // n0 -> P -> n1 - const P = n0.next; - const n1 = P.next; - n0.next = n1; - } + [class]{}-[func]{remove} ``` === "Dart" ```dart title="linked_list.dart" - /* 删除链表的节点 n0 之后的首个节点 */ - void remove(ListNode n0) { - if (n0.next == null) return; - // n0 -> P -> n1 - ListNode P = n0.next!; - ListNode? n1 = P.next; - n0.next = n1; - } + [class]{}-[func]{remove} ``` === "Rust" ```rust title="linked_list.rs" - /* 删除链表的节点 n0 之后的首个节点 */ - #[allow(non_snake_case)] - pub fn remove(n0: &Rc>>) { - if n0.borrow().next.is_none() { - return; - }; - // n0 -> P -> n1 - let P = n0.borrow_mut().next.take(); - if let Some(node) = P { - let n1 = node.borrow_mut().next.take(); - n0.borrow_mut().next = n1; - } - } + [class]{}-[func]{remove} ``` === "C" ```c title="linked_list.c" - /* 删除链表的节点 n0 之后的首个节点 */ - // 注意:stdio.h 占用了 remove 关键词 - void removeItem(ListNode *n0) { - if (!n0->next) - return; - // n0 -> P -> n1 - ListNode *P = n0->next; - ListNode *n1 = P->next; - n0->next = n1; - // 释放内存 - free(P); - } + [class]{}-[func]{removeItem} ``` === "Kotlin" ```kotlin title="linked_list.kt" - /* 删除链表的节点 n0 之后的首个节点 */ - fun remove(n0: ListNode?) { - if (n0?.next == null) - return - // n0 -> P -> n1 - val p = n0.next - val n1 = p?.next - n0.next = n1 - } + [class]{}-[func]{remove} ``` === "Ruby" ```ruby title="linked_list.rb" - ### 删除链表的节点 n0 之后的首个节点 ### - def remove(n0) - return if n0.next.nil? - - # n0 -> remove_node -> n1 - remove_node = n0.next - n1 = remove_node.next - n0.next = n1 - end + [class]{}-[func]{remove} ``` === "Zig" ```zig title="linked_list.zig" - // 删除链表的节点 n0 之后的首个节点 - fn remove(n0: ?*inc.ListNode(i32)) void { - if (n0.?.next == null) return; - // n0 -> P -> n1 - var P = n0.?.next; - var n1 = P.?.next; - n0.?.next = n1; - } + [class]{}-[func]{remove} ``` -??? pythontutor "Code Visualization" - -
- - ### 4.   Accessing nodes **Accessing nodes in a linked list is less efficient**. As previously mentioned, any element in an array can be accessed in $O(1)$ time. In contrast, with a linked list, the program involves starting from the head node and sequentially traversing through the nodes until the desired node is found. In other words, to access the $i$-th node in a linked list, the program must iterate through $i - 1$ nodes, resulting in a time complexity of $O(n)$. @@ -807,7 +630,7 @@ It's important to note that even though node `P` continues to point to `n1` afte ```python title="linked_list.py" def access(head: ListNode, index: int) -> ListNode | None: - """访问链表中索引为 index 的节点""" + """Access the node at `index` in the linked list""" for _ in range(index): if not head: return None @@ -818,21 +641,13 @@ It's important to note that even though node `P` continues to point to `n1` afte === "C++" ```cpp title="linked_list.cpp" - /* 访问链表中索引为 index 的节点 */ - ListNode *access(ListNode *head, int index) { - for (int i = 0; i < index; i++) { - if (head == nullptr) - return nullptr; - head = head->next; - } - return head; - } + [class]{}-[func]{access} ``` === "Java" ```java title="linked_list.java" - /* 访问链表中索引为 index 的节点 */ + /* Access the node at `index` in the linked list */ ListNode access(ListNode head, int index) { for (int i = 0; i < index; i++) { if (head == null) @@ -846,170 +661,69 @@ It's important to note that even though node `P` continues to point to `n1` afte === "C#" ```csharp title="linked_list.cs" - /* 访问链表中索引为 index 的节点 */ - ListNode? Access(ListNode? head, int index) { - for (int i = 0; i < index; i++) { - if (head == null) - return null; - head = head.next; - } - return head; - } + [class]{linked_list}-[func]{Access} ``` === "Go" ```go title="linked_list.go" - /* 访问链表中索引为 index 的节点 */ - func access(head *ListNode, index int) *ListNode { - for i := 0; i < index; i++ { - if head == nil { - return nil - } - head = head.Next - } - return head - } + [class]{}-[func]{access} ``` === "Swift" ```swift title="linked_list.swift" - /* 访问链表中索引为 index 的节点 */ - func access(head: ListNode, index: Int) -> ListNode? { - var head: ListNode? = head - for _ in 0 ..< index { - if head == nil { - return nil - } - head = head?.next - } - return head - } + [class]{}-[func]{access} ``` === "JS" ```javascript title="linked_list.js" - /* 访问链表中索引为 index 的节点 */ - function access(head, index) { - for (let i = 0; i < index; i++) { - if (!head) { - return null; - } - head = head.next; - } - return head; - } + [class]{}-[func]{access} ``` === "TS" ```typescript title="linked_list.ts" - /* 访问链表中索引为 index 的节点 */ - function access(head: ListNode | null, index: number): ListNode | null { - for (let i = 0; i < index; i++) { - if (!head) { - return null; - } - head = head.next; - } - return head; - } + [class]{}-[func]{access} ``` === "Dart" ```dart title="linked_list.dart" - /* 访问链表中索引为 index 的节点 */ - ListNode? access(ListNode? head, int index) { - for (var i = 0; i < index; i++) { - if (head == null) return null; - head = head.next; - } - return head; - } + [class]{}-[func]{access} ``` === "Rust" ```rust title="linked_list.rs" - /* 访问链表中索引为 index 的节点 */ - pub fn access(head: Rc>>, index: i32) -> Rc>> { - if index <= 0 { - return head; - }; - if let Some(node) = &head.borrow().next { - return access(node.clone(), index - 1); - } - - return head; - } + [class]{}-[func]{access} ``` === "C" ```c title="linked_list.c" - /* 访问链表中索引为 index 的节点 */ - ListNode *access(ListNode *head, int index) { - for (int i = 0; i < index; i++) { - if (head == NULL) - return NULL; - head = head->next; - } - return head; - } + [class]{}-[func]{access} ``` === "Kotlin" ```kotlin title="linked_list.kt" - /* 访问链表中索引为 index 的节点 */ - fun access(head: ListNode?, index: Int): ListNode? { - var h = head - for (i in 0.. - - ### 5.   Finding nodes Traverse the linked list to locate a node whose value matches `target`, and then output the index of that node within the linked list. This procedure is also an example of linear search. The corresponding code is provided below: @@ -1018,7 +732,7 @@ Traverse the linked list to locate a node whose value matches `target`, and then ```python title="linked_list.py" def find(head: ListNode, target: int) -> int: - """在链表中查找值为 target 的首个节点""" + """Search for the first node with value target in the linked list""" index = 0 while head: if head.val == target: @@ -1031,23 +745,13 @@ Traverse the linked list to locate a node whose value matches `target`, and then === "C++" ```cpp title="linked_list.cpp" - /* 在链表中查找值为 target 的首个节点 */ - int find(ListNode *head, int target) { - int index = 0; - while (head != nullptr) { - if (head->val == target) - return index; - head = head->next; - index++; - } - return -1; - } + [class]{}-[func]{find} ``` === "Java" ```java title="linked_list.java" - /* 在链表中查找值为 target 的首个节点 */ + /* Search for the first node with value target in the linked list */ int find(ListNode head, int target) { int index = 0; while (head != null) { @@ -1063,190 +767,69 @@ Traverse the linked list to locate a node whose value matches `target`, and then === "C#" ```csharp title="linked_list.cs" - /* 在链表中查找值为 target 的首个节点 */ - int Find(ListNode? head, int target) { - int index = 0; - while (head != null) { - if (head.val == target) - return index; - head = head.next; - index++; - } - return -1; - } + [class]{linked_list}-[func]{Find} ``` === "Go" ```go title="linked_list.go" - /* 在链表中查找值为 target 的首个节点 */ - func findNode(head *ListNode, target int) int { - index := 0 - for head != nil { - if head.Val == target { - return index - } - head = head.Next - index++ - } - return -1 - } + [class]{}-[func]{findNode} ``` === "Swift" ```swift title="linked_list.swift" - /* 在链表中查找值为 target 的首个节点 */ - func find(head: ListNode, target: Int) -> Int { - var head: ListNode? = head - var index = 0 - while head != nil { - if head?.val == target { - return index - } - head = head?.next - index += 1 - } - return -1 - } + [class]{}-[func]{find} ``` === "JS" ```javascript title="linked_list.js" - /* 在链表中查找值为 target 的首个节点 */ - function find(head, target) { - let index = 0; - while (head !== null) { - if (head.val === target) { - return index; - } - head = head.next; - index += 1; - } - return -1; - } + [class]{}-[func]{find} ``` === "TS" ```typescript title="linked_list.ts" - /* 在链表中查找值为 target 的首个节点 */ - function find(head: ListNode | null, target: number): number { - let index = 0; - while (head !== null) { - if (head.val === target) { - return index; - } - head = head.next; - index += 1; - } - return -1; - } + [class]{}-[func]{find} ``` === "Dart" ```dart title="linked_list.dart" - /* 在链表中查找值为 target 的首个节点 */ - int find(ListNode? head, int target) { - int index = 0; - while (head != null) { - if (head.val == target) { - return index; - } - head = head.next; - index++; - } - return -1; - } + [class]{}-[func]{find} ``` === "Rust" ```rust title="linked_list.rs" - /* 在链表中查找值为 target 的首个节点 */ - pub fn find(head: Rc>>, target: T, index: i32) -> i32 { - if head.borrow().val == target { - return index; - }; - if let Some(node) = &head.borrow_mut().next { - return find(node.clone(), target, index + 1); - } - return -1; - } + [class]{}-[func]{find} ``` === "C" ```c title="linked_list.c" - /* 在链表中查找值为 target 的首个节点 */ - int find(ListNode *head, int target) { - int index = 0; - while (head) { - if (head->val == target) - return index; - head = head->next; - index++; - } - return -1; - } + [class]{}-[func]{find} ``` === "Kotlin" ```kotlin title="linked_list.kt" - /* 在链表中查找值为 target 的首个节点 */ - fun find(head: ListNode?, target: Int): Int { - var index = 0 - var h = head - while (h != null) { - if (h._val == target) - return index - h = h.next - index++ - } - return -1 - } + [class]{}-[func]{find} ``` === "Ruby" ```ruby title="linked_list.rb" - ### 在链表中查找值为 target 的首个节点 ### - def find(head, target) - index = 0 - while head - return index if head.val == target - head = head.next - index += 1 - end - - -1 - end + [class]{}-[func]{find} ``` === "Zig" ```zig title="linked_list.zig" - // 在链表中查找值为 target 的首个节点 - fn find(node: ?*inc.ListNode(i32), target: i32) i32 { - var head = node; - var index: i32 = 0; - while (head != null) { - if (head.?.val == target) return index; - head = head.?.next; - index += 1; - } - return -1; - } + [class]{}-[func]{find} ``` -??? pythontutor "Code Visualization" - -
- - ## 4.2.2   Arrays vs. linked lists Table 4-1 summarizes the characteristics of arrays and linked lists, and it also compares their efficiencies in various operations. Because they utilize opposing storage strategies, their respective properties and operational efficiencies exhibit distinct contrasts. diff --git a/en/docs/chapter_array_and_linkedlist/list.md b/en/docs/chapter_array_and_linkedlist/list.md index 67b5dd34c..0fb2bc047 100755 --- a/en/docs/chapter_array_and_linkedlist/list.md +++ b/en/docs/chapter_array_and_linkedlist/list.md @@ -909,291 +909,182 @@ To enhance our understanding of how lists work, we will attempt to implement a s ```python title="my_list.py" class MyList: - """列表类""" + """List class""" def __init__(self): - """构造方法""" - self._capacity: int = 10 # 列表容量 - self._arr: list[int] = [0] * self._capacity # 数组(存储列表元素) - self._size: int = 0 # 列表长度(当前元素数量) - self._extend_ratio: int = 2 # 每次列表扩容的倍数 + """Constructor""" + self._capacity: int = 10 # List capacity + self._arr: list[int] = [0] * self._capacity # Array (stores list elements) + self._size: int = 0 # List length (current number of elements) + self._extend_ratio: int = 2 # Multiple for each list expansion def size(self) -> int: - """获取列表长度(当前元素数量)""" + """Get list length (current number of elements)""" return self._size def capacity(self) -> int: - """获取列表容量""" + """Get list capacity""" return self._capacity def get(self, index: int) -> int: - """访问元素""" - # 索引如果越界,则抛出异常,下同 + """Access element""" + # If the index is out of bounds, throw an exception, as below if index < 0 or index >= self._size: - raise IndexError("索引越界") + raise IndexError("Index out of bounds") return self._arr[index] def set(self, num: int, index: int): - """更新元素""" + """Update element""" if index < 0 or index >= self._size: - raise IndexError("索引越界") + raise IndexError("Index out of bounds") self._arr[index] = num def add(self, num: int): - """在尾部添加元素""" - # 元素数量超出容量时,触发扩容机制 + """Add element at the end""" + # When the number of elements exceeds capacity, trigger the expansion mechanism if self.size() == self.capacity(): self.extend_capacity() self._arr[self._size] = num self._size += 1 def insert(self, num: int, index: int): - """在中间插入元素""" + """Insert element in the middle""" if index < 0 or index >= self._size: - raise IndexError("索引越界") - # 元素数量超出容量时,触发扩容机制 + raise IndexError("Index out of bounds") + # When the number of elements exceeds capacity, trigger the expansion mechanism if self._size == self.capacity(): self.extend_capacity() - # 将索引 index 以及之后的元素都向后移动一位 + # Move all elements after `index` one position backward for j in range(self._size - 1, index - 1, -1): self._arr[j + 1] = self._arr[j] self._arr[index] = num - # 更新元素数量 + # Update the number of elements self._size += 1 def remove(self, index: int) -> int: - """删除元素""" + """Remove element""" if index < 0 or index >= self._size: - raise IndexError("索引越界") + raise IndexError("Index out of bounds") num = self._arr[index] - # 将索引 index 之后的元素都向前移动一位 + # Move all elements after `index` one position forward for j in range(index, self._size - 1): self._arr[j] = self._arr[j + 1] - # 更新元素数量 + # Update the number of elements self._size -= 1 - # 返回被删除的元素 + # Return the removed element return num def extend_capacity(self): - """列表扩容""" - # 新建一个长度为原数组 _extend_ratio 倍的新数组,并将原数组复制到新数组 + """Extend list""" + # Create a new array of _extend_ratio times the length of the original array and copy the original array to the new array self._arr = self._arr + [0] * self.capacity() * (self._extend_ratio - 1) - # 更新列表容量 + # Update list capacity self._capacity = len(self._arr) def to_array(self) -> list[int]: - """返回有效长度的列表""" + """Return a list of valid lengths""" return self._arr[: self._size] ``` === "C++" ```cpp title="my_list.cpp" - /* 列表类 */ - class MyList { - private: - int *arr; // 数组(存储列表元素) - int arrCapacity = 10; // 列表容量 - int arrSize = 0; // 列表长度(当前元素数量) - int extendRatio = 2; // 每次列表扩容的倍数 - - public: - /* 构造方法 */ - MyList() { - arr = new int[arrCapacity]; - } - - /* 析构方法 */ - ~MyList() { - delete[] arr; - } - - /* 获取列表长度(当前元素数量)*/ - int size() { - return arrSize; - } - - /* 获取列表容量 */ - int capacity() { - return arrCapacity; - } - - /* 访问元素 */ - int get(int index) { - // 索引如果越界,则抛出异常,下同 - if (index < 0 || index >= size()) - throw out_of_range("索引越界"); - return arr[index]; - } - - /* 更新元素 */ - void set(int index, int num) { - if (index < 0 || index >= size()) - throw out_of_range("索引越界"); - arr[index] = num; - } - - /* 在尾部添加元素 */ - void add(int num) { - // 元素数量超出容量时,触发扩容机制 - if (size() == capacity()) - extendCapacity(); - arr[size()] = num; - // 更新元素数量 - arrSize++; - } - - /* 在中间插入元素 */ - void insert(int index, int num) { - if (index < 0 || index >= size()) - throw out_of_range("索引越界"); - // 元素数量超出容量时,触发扩容机制 - if (size() == capacity()) - extendCapacity(); - // 将索引 index 以及之后的元素都向后移动一位 - for (int j = size() - 1; j >= index; j--) { - arr[j + 1] = arr[j]; - } - arr[index] = num; - // 更新元素数量 - arrSize++; - } - - /* 删除元素 */ - int remove(int index) { - if (index < 0 || index >= size()) - throw out_of_range("索引越界"); - int num = arr[index]; - // 将索引 index 之后的元素都向前移动一位 - for (int j = index; j < size() - 1; j++) { - arr[j] = arr[j + 1]; - } - // 更新元素数量 - arrSize--; - // 返回被删除的元素 - return num; - } - - /* 列表扩容 */ - void extendCapacity() { - // 新建一个长度为原数组 extendRatio 倍的新数组 - int newCapacity = capacity() * extendRatio; - int *tmp = arr; - arr = new int[newCapacity]; - // 将原数组中的所有元素复制到新数组 - for (int i = 0; i < size(); i++) { - arr[i] = tmp[i]; - } - // 释放内存 - delete[] tmp; - arrCapacity = newCapacity; - } - - /* 将列表转换为 Vector 用于打印 */ - vector toVector() { - // 仅转换有效长度范围内的列表元素 - vector vec(size()); - for (int i = 0; i < size(); i++) { - vec[i] = arr[i]; - } - return vec; - } - }; + [class]{MyList}-[func]{} ``` === "Java" ```java title="my_list.java" - /* 列表类 */ + /* List class */ class MyList { - private int[] arr; // 数组(存储列表元素) - private int capacity = 10; // 列表容量 - private int size = 0; // 列表长度(当前元素数量) - private int extendRatio = 2; // 每次列表扩容的倍数 + private int[] arr; // Array (stores list elements) + private int capacity = 10; // List capacity + private int size = 0; // List length (current number of elements) + private int extendRatio = 2; // Multiple for each list expansion - /* 构造方法 */ + /* Constructor */ public MyList() { arr = new int[capacity]; } - /* 获取列表长度(当前元素数量) */ + /* Get list length (current number of elements) */ public int size() { return size; } - /* 获取列表容量 */ + /* Get list capacity */ public int capacity() { return capacity; } - /* 访问元素 */ + /* Access element */ public int get(int index) { - // 索引如果越界,则抛出异常,下同 + // If the index is out of bounds, throw an exception, as below if (index < 0 || index >= size) - throw new IndexOutOfBoundsException("索引越界"); + throw new IndexOutOfBoundsException("Index out of bounds"); return arr[index]; } - /* 更新元素 */ + /* Update element */ public void set(int index, int num) { if (index < 0 || index >= size) - throw new IndexOutOfBoundsException("索引越界"); + throw new IndexOutOfBoundsException("Index out of bounds"); arr[index] = num; } - /* 在尾部添加元素 */ + /* Add element at the end */ public void add(int num) { - // 元素数量超出容量时,触发扩容机制 + // When the number of elements exceeds capacity, trigger the expansion mechanism if (size == capacity()) extendCapacity(); arr[size] = num; - // 更新元素数量 + // Update the number of elements size++; } - /* 在中间插入元素 */ + /* Insert element in the middle */ public void insert(int index, int num) { if (index < 0 || index >= size) - throw new IndexOutOfBoundsException("索引越界"); - // 元素数量超出容量时,触发扩容机制 + throw new IndexOutOfBoundsException("Index out of bounds"); + // When the number of elements exceeds capacity, trigger the expansion mechanism if (size == capacity()) extendCapacity(); - // 将索引 index 以及之后的元素都向后移动一位 + // Move all elements after `index` one position backward for (int j = size - 1; j >= index; j--) { arr[j + 1] = arr[j]; } arr[index] = num; - // 更新元素数量 + // Update the number of elements size++; } - /* 删除元素 */ + /* Remove element */ public int remove(int index) { if (index < 0 || index >= size) - throw new IndexOutOfBoundsException("索引越界"); + throw new IndexOutOfBoundsException("Index out of bounds"); int num = arr[index]; - // 将将索引 index 之后的元素都向前移动一位 + // Move all elements after `index` one position forward for (int j = index; j < size - 1; j++) { arr[j] = arr[j + 1]; } - // 更新元素数量 + // Update the number of elements size--; - // 返回被删除的元素 + // Return the removed element return num; } - /* 列表扩容 */ + /* Extend list */ public void extendCapacity() { - // 新建一个长度为原数组 extendRatio 倍的新数组,并将原数组复制到新数组 + // Create a new array with a length multiple of the original array by extendRatio, and copy the original array to the new array arr = Arrays.copyOf(arr, capacity() * extendRatio); - // 更新列表容量 + // Update list capacity capacity = arr.length; } - /* 将列表转换为数组 */ + /* Convert the list to an array */ public int[] toArray() { int size = size(); - // 仅转换有效长度范围内的列表元素 + // Only convert elements within valid length range int[] arr = new int[size]; for (int i = 0; i < size; i++) { arr[i] = get(i); @@ -1206,1157 +1097,65 @@ To enhance our understanding of how lists work, we will attempt to implement a s === "C#" ```csharp title="my_list.cs" - /* 列表类 */ - class MyList { - private int[] arr; // 数组(存储列表元素) - private int arrCapacity = 10; // 列表容量 - private int arrSize = 0; // 列表长度(当前元素数量) - private readonly int extendRatio = 2; // 每次列表扩容的倍数 - - /* 构造方法 */ - public MyList() { - arr = new int[arrCapacity]; - } - - /* 获取列表长度(当前元素数量)*/ - public int Size() { - return arrSize; - } - - /* 获取列表容量 */ - public int Capacity() { - return arrCapacity; - } - - /* 访问元素 */ - public int Get(int index) { - // 索引如果越界,则抛出异常,下同 - if (index < 0 || index >= arrSize) - throw new IndexOutOfRangeException("索引越界"); - return arr[index]; - } - - /* 更新元素 */ - public void Set(int index, int num) { - if (index < 0 || index >= arrSize) - throw new IndexOutOfRangeException("索引越界"); - arr[index] = num; - } - - /* 在尾部添加元素 */ - public void Add(int num) { - // 元素数量超出容量时,触发扩容机制 - if (arrSize == arrCapacity) - ExtendCapacity(); - arr[arrSize] = num; - // 更新元素数量 - arrSize++; - } - - /* 在中间插入元素 */ - public void Insert(int index, int num) { - if (index < 0 || index >= arrSize) - throw new IndexOutOfRangeException("索引越界"); - // 元素数量超出容量时,触发扩容机制 - if (arrSize == arrCapacity) - ExtendCapacity(); - // 将索引 index 以及之后的元素都向后移动一位 - for (int j = arrSize - 1; j >= index; j--) { - arr[j + 1] = arr[j]; - } - arr[index] = num; - // 更新元素数量 - arrSize++; - } - - /* 删除元素 */ - public int Remove(int index) { - if (index < 0 || index >= arrSize) - throw new IndexOutOfRangeException("索引越界"); - int num = arr[index]; - // 将将索引 index 之后的元素都向前移动一位 - for (int j = index; j < arrSize - 1; j++) { - arr[j] = arr[j + 1]; - } - // 更新元素数量 - arrSize--; - // 返回被删除的元素 - return num; - } - - /* 列表扩容 */ - public void ExtendCapacity() { - // 新建一个长度为 arrCapacity * extendRatio 的数组,并将原数组复制到新数组 - Array.Resize(ref arr, arrCapacity * extendRatio); - // 更新列表容量 - arrCapacity = arr.Length; - } - - /* 将列表转换为数组 */ - public int[] ToArray() { - // 仅转换有效长度范围内的列表元素 - int[] arr = new int[arrSize]; - for (int i = 0; i < arrSize; i++) { - arr[i] = Get(i); - } - return arr; - } - } + [class]{MyList}-[func]{} ``` === "Go" ```go title="my_list.go" - /* 列表类 */ - type myList struct { - arrCapacity int - arr []int - arrSize int - extendRatio int - } - - /* 构造函数 */ - func newMyList() *myList { - return &myList{ - arrCapacity: 10, // 列表容量 - arr: make([]int, 10), // 数组(存储列表元素) - arrSize: 0, // 列表长度(当前元素数量) - extendRatio: 2, // 每次列表扩容的倍数 - } - } - - /* 获取列表长度(当前元素数量) */ - func (l *myList) size() int { - return l.arrSize - } - - /* 获取列表容量 */ - func (l *myList) capacity() int { - return l.arrCapacity - } - - /* 访问元素 */ - func (l *myList) get(index int) int { - // 索引如果越界,则抛出异常,下同 - if index < 0 || index >= l.arrSize { - panic("索引越界") - } - return l.arr[index] - } - - /* 更新元素 */ - func (l *myList) set(num, index int) { - if index < 0 || index >= l.arrSize { - panic("索引越界") - } - l.arr[index] = num - } - - /* 在尾部添加元素 */ - func (l *myList) add(num int) { - // 元素数量超出容量时,触发扩容机制 - if l.arrSize == l.arrCapacity { - l.extendCapacity() - } - l.arr[l.arrSize] = num - // 更新元素数量 - l.arrSize++ - } - - /* 在中间插入元素 */ - func (l *myList) insert(num, index int) { - if index < 0 || index >= l.arrSize { - panic("索引越界") - } - // 元素数量超出容量时,触发扩容机制 - if l.arrSize == l.arrCapacity { - l.extendCapacity() - } - // 将索引 index 以及之后的元素都向后移动一位 - for j := l.arrSize - 1; j >= index; j-- { - l.arr[j+1] = l.arr[j] - } - l.arr[index] = num - // 更新元素数量 - l.arrSize++ - } - - /* 删除元素 */ - func (l *myList) remove(index int) int { - if index < 0 || index >= l.arrSize { - panic("索引越界") - } - num := l.arr[index] - // 将索引 index 之后的元素都向前移动一位 - for j := index; j < l.arrSize-1; j++ { - l.arr[j] = l.arr[j+1] - } - // 更新元素数量 - l.arrSize-- - // 返回被删除的元素 - return num - } - - /* 列表扩容 */ - func (l *myList) extendCapacity() { - // 新建一个长度为原数组 extendRatio 倍的新数组,并将原数组复制到新数组 - l.arr = append(l.arr, make([]int, l.arrCapacity*(l.extendRatio-1))...) - // 更新列表容量 - l.arrCapacity = len(l.arr) - } - - /* 返回有效长度的列表 */ - func (l *myList) toArray() []int { - // 仅转换有效长度范围内的列表元素 - return l.arr[:l.arrSize] - } + [class]{myList}-[func]{} ``` === "Swift" ```swift title="my_list.swift" - /* 列表类 */ - class MyList { - private var arr: [Int] // 数组(存储列表元素) - private var _capacity: Int // 列表容量 - private var _size: Int // 列表长度(当前元素数量) - private let extendRatio: Int // 每次列表扩容的倍数 - - /* 构造方法 */ - init() { - _capacity = 10 - _size = 0 - extendRatio = 2 - arr = Array(repeating: 0, count: _capacity) - } - - /* 获取列表长度(当前元素数量)*/ - func size() -> Int { - _size - } - - /* 获取列表容量 */ - func capacity() -> Int { - _capacity - } - - /* 访问元素 */ - func get(index: Int) -> Int { - // 索引如果越界则抛出错误,下同 - if index < 0 || index >= size() { - fatalError("索引越界") - } - return arr[index] - } - - /* 更新元素 */ - func set(index: Int, num: Int) { - if index < 0 || index >= size() { - fatalError("索引越界") - } - arr[index] = num - } - - /* 在尾部添加元素 */ - func add(num: Int) { - // 元素数量超出容量时,触发扩容机制 - if size() == capacity() { - extendCapacity() - } - arr[size()] = num - // 更新元素数量 - _size += 1 - } - - /* 在中间插入元素 */ - func insert(index: Int, num: Int) { - if index < 0 || index >= size() { - fatalError("索引越界") - } - // 元素数量超出容量时,触发扩容机制 - if size() == capacity() { - extendCapacity() - } - // 将索引 index 以及之后的元素都向后移动一位 - for j in (index ..< size()).reversed() { - arr[j + 1] = arr[j] - } - arr[index] = num - // 更新元素数量 - _size += 1 - } - - /* 删除元素 */ - @discardableResult - func remove(index: Int) -> Int { - if index < 0 || index >= size() { - fatalError("索引越界") - } - let num = arr[index] - // 将将索引 index 之后的元素都向前移动一位 - for j in index ..< (size() - 1) { - arr[j] = arr[j + 1] - } - // 更新元素数量 - _size -= 1 - // 返回被删除的元素 - return num - } - - /* 列表扩容 */ - func extendCapacity() { - // 新建一个长度为原数组 extendRatio 倍的新数组,并将原数组复制到新数组 - arr = arr + Array(repeating: 0, count: capacity() * (extendRatio - 1)) - // 更新列表容量 - _capacity = arr.count - } - - /* 将列表转换为数组 */ - func toArray() -> [Int] { - Array(arr.prefix(size())) - } - } + [class]{MyList}-[func]{} ``` === "JS" ```javascript title="my_list.js" - /* 列表类 */ - class MyList { - #arr = new Array(); // 数组(存储列表元素) - #capacity = 10; // 列表容量 - #size = 0; // 列表长度(当前元素数量) - #extendRatio = 2; // 每次列表扩容的倍数 - - /* 构造方法 */ - constructor() { - this.#arr = new Array(this.#capacity); - } - - /* 获取列表长度(当前元素数量)*/ - size() { - return this.#size; - } - - /* 获取列表容量 */ - capacity() { - return this.#capacity; - } - - /* 访问元素 */ - get(index) { - // 索引如果越界,则抛出异常,下同 - if (index < 0 || index >= this.#size) throw new Error('索引越界'); - return this.#arr[index]; - } - - /* 更新元素 */ - set(index, num) { - if (index < 0 || index >= this.#size) throw new Error('索引越界'); - this.#arr[index] = num; - } - - /* 在尾部添加元素 */ - add(num) { - // 如果长度等于容量,则需要扩容 - if (this.#size === this.#capacity) { - this.extendCapacity(); - } - // 将新元素添加到列表尾部 - this.#arr[this.#size] = num; - this.#size++; - } - - /* 在中间插入元素 */ - insert(index, num) { - if (index < 0 || index >= this.#size) throw new Error('索引越界'); - // 元素数量超出容量时,触发扩容机制 - if (this.#size === this.#capacity) { - this.extendCapacity(); - } - // 将索引 index 以及之后的元素都向后移动一位 - for (let j = this.#size - 1; j >= index; j--) { - this.#arr[j + 1] = this.#arr[j]; - } - // 更新元素数量 - this.#arr[index] = num; - this.#size++; - } - - /* 删除元素 */ - remove(index) { - if (index < 0 || index >= this.#size) throw new Error('索引越界'); - let num = this.#arr[index]; - // 将将索引 index 之后的元素都向前移动一位 - for (let j = index; j < this.#size - 1; j++) { - this.#arr[j] = this.#arr[j + 1]; - } - // 更新元素数量 - this.#size--; - // 返回被删除的元素 - return num; - } - - /* 列表扩容 */ - extendCapacity() { - // 新建一个长度为原数组 extendRatio 倍的新数组,并将原数组复制到新数组 - this.#arr = this.#arr.concat( - new Array(this.capacity() * (this.#extendRatio - 1)) - ); - // 更新列表容量 - this.#capacity = this.#arr.length; - } - - /* 将列表转换为数组 */ - toArray() { - let size = this.size(); - // 仅转换有效长度范围内的列表元素 - const arr = new Array(size); - for (let i = 0; i < size; i++) { - arr[i] = this.get(i); - } - return arr; - } - } + [class]{MyList}-[func]{} ``` === "TS" ```typescript title="my_list.ts" - /* 列表类 */ - class MyList { - private arr: Array; // 数组(存储列表元素) - private _capacity: number = 10; // 列表容量 - private _size: number = 0; // 列表长度(当前元素数量) - private extendRatio: number = 2; // 每次列表扩容的倍数 - - /* 构造方法 */ - constructor() { - this.arr = new Array(this._capacity); - } - - /* 获取列表长度(当前元素数量)*/ - public size(): number { - return this._size; - } - - /* 获取列表容量 */ - public capacity(): number { - return this._capacity; - } - - /* 访问元素 */ - public get(index: number): number { - // 索引如果越界,则抛出异常,下同 - if (index < 0 || index >= this._size) throw new Error('索引越界'); - return this.arr[index]; - } - - /* 更新元素 */ - public set(index: number, num: number): void { - if (index < 0 || index >= this._size) throw new Error('索引越界'); - this.arr[index] = num; - } - - /* 在尾部添加元素 */ - public add(num: number): void { - // 如果长度等于容量,则需要扩容 - if (this._size === this._capacity) this.extendCapacity(); - // 将新元素添加到列表尾部 - this.arr[this._size] = num; - this._size++; - } - - /* 在中间插入元素 */ - public insert(index: number, num: number): void { - if (index < 0 || index >= this._size) throw new Error('索引越界'); - // 元素数量超出容量时,触发扩容机制 - if (this._size === this._capacity) { - this.extendCapacity(); - } - // 将索引 index 以及之后的元素都向后移动一位 - for (let j = this._size - 1; j >= index; j--) { - this.arr[j + 1] = this.arr[j]; - } - // 更新元素数量 - this.arr[index] = num; - this._size++; - } - - /* 删除元素 */ - public remove(index: number): number { - if (index < 0 || index >= this._size) throw new Error('索引越界'); - let num = this.arr[index]; - // 将将索引 index 之后的元素都向前移动一位 - for (let j = index; j < this._size - 1; j++) { - this.arr[j] = this.arr[j + 1]; - } - // 更新元素数量 - this._size--; - // 返回被删除的元素 - return num; - } - - /* 列表扩容 */ - public extendCapacity(): void { - // 新建一个长度为 size 的数组,并将原数组复制到新数组 - this.arr = this.arr.concat( - new Array(this.capacity() * (this.extendRatio - 1)) - ); - // 更新列表容量 - this._capacity = this.arr.length; - } - - /* 将列表转换为数组 */ - public toArray(): number[] { - let size = this.size(); - // 仅转换有效长度范围内的列表元素 - const arr = new Array(size); - for (let i = 0; i < size; i++) { - arr[i] = this.get(i); - } - return arr; - } - } + [class]{MyList}-[func]{} ``` === "Dart" ```dart title="my_list.dart" - /* 列表类 */ - class MyList { - late List _arr; // 数组(存储列表元素) - int _capacity = 10; // 列表容量 - int _size = 0; // 列表长度(当前元素数量) - int _extendRatio = 2; // 每次列表扩容的倍数 - - /* 构造方法 */ - MyList() { - _arr = List.filled(_capacity, 0); - } - - /* 获取列表长度(当前元素数量)*/ - int size() => _size; - - /* 获取列表容量 */ - int capacity() => _capacity; - - /* 访问元素 */ - int get(int index) { - if (index >= _size) throw RangeError('索引越界'); - return _arr[index]; - } - - /* 更新元素 */ - void set(int index, int _num) { - if (index >= _size) throw RangeError('索引越界'); - _arr[index] = _num; - } - - /* 在尾部添加元素 */ - void add(int _num) { - // 元素数量超出容量时,触发扩容机制 - if (_size == _capacity) extendCapacity(); - _arr[_size] = _num; - // 更新元素数量 - _size++; - } - - /* 在中间插入元素 */ - void insert(int index, int _num) { - if (index >= _size) throw RangeError('索引越界'); - // 元素数量超出容量时,触发扩容机制 - if (_size == _capacity) extendCapacity(); - // 将索引 index 以及之后的元素都向后移动一位 - for (var j = _size - 1; j >= index; j--) { - _arr[j + 1] = _arr[j]; - } - _arr[index] = _num; - // 更新元素数量 - _size++; - } - - /* 删除元素 */ - int remove(int index) { - if (index >= _size) throw RangeError('索引越界'); - int _num = _arr[index]; - // 将将索引 index 之后的元素都向前移动一位 - for (var j = index; j < _size - 1; j++) { - _arr[j] = _arr[j + 1]; - } - // 更新元素数量 - _size--; - // 返回被删除的元素 - return _num; - } - - /* 列表扩容 */ - void extendCapacity() { - // 新建一个长度为原数组 _extendRatio 倍的新数组 - final _newNums = List.filled(_capacity * _extendRatio, 0); - // 将原数组复制到新数组 - List.copyRange(_newNums, 0, _arr); - // 更新 _arr 的引用 - _arr = _newNums; - // 更新列表容量 - _capacity = _arr.length; - } - - /* 将列表转换为数组 */ - List toArray() { - List arr = []; - for (var i = 0; i < _size; i++) { - arr.add(get(i)); - } - return arr; - } - } + [class]{MyList}-[func]{} ``` === "Rust" ```rust title="my_list.rs" - /* 列表类 */ - #[allow(dead_code)] - struct MyList { - arr: Vec, // 数组(存储列表元素) - capacity: usize, // 列表容量 - size: usize, // 列表长度(当前元素数量) - extend_ratio: usize, // 每次列表扩容的倍数 - } - - #[allow(unused, unused_comparisons)] - impl MyList { - /* 构造方法 */ - pub fn new(capacity: usize) -> Self { - let mut vec = Vec::new(); - vec.resize(capacity, 0); - Self { - arr: vec, - capacity, - size: 0, - extend_ratio: 2, - } - } - - /* 获取列表长度(当前元素数量)*/ - pub fn size(&self) -> usize { - return self.size; - } - - /* 获取列表容量 */ - pub fn capacity(&self) -> usize { - return self.capacity; - } - - /* 访问元素 */ - pub fn get(&self, index: usize) -> i32 { - // 索引如果越界,则抛出异常,下同 - if index >= self.size { - panic!("索引越界") - }; - return self.arr[index]; - } - - /* 更新元素 */ - pub fn set(&mut self, index: usize, num: i32) { - if index >= self.size { - panic!("索引越界") - }; - self.arr[index] = num; - } - - /* 在尾部添加元素 */ - pub fn add(&mut self, num: i32) { - // 元素数量超出容量时,触发扩容机制 - if self.size == self.capacity() { - self.extend_capacity(); - } - self.arr[self.size] = num; - // 更新元素数量 - self.size += 1; - } - - /* 在中间插入元素 */ - pub fn insert(&mut self, index: usize, num: i32) { - if index >= self.size() { - panic!("索引越界") - }; - // 元素数量超出容量时,触发扩容机制 - if self.size == self.capacity() { - self.extend_capacity(); - } - // 将索引 index 以及之后的元素都向后移动一位 - for j in (index..self.size).rev() { - self.arr[j + 1] = self.arr[j]; - } - self.arr[index] = num; - // 更新元素数量 - self.size += 1; - } - - /* 删除元素 */ - pub fn remove(&mut self, index: usize) -> i32 { - if index >= self.size() { - panic!("索引越界") - }; - let num = self.arr[index]; - // 将将索引 index 之后的元素都向前移动一位 - for j in (index..self.size - 1) { - self.arr[j] = self.arr[j + 1]; - } - // 更新元素数量 - self.size -= 1; - // 返回被删除的元素 - return num; - } - - /* 列表扩容 */ - pub fn extend_capacity(&mut self) { - // 新建一个长度为原数组 extend_ratio 倍的新数组,并将原数组复制到新数组 - let new_capacity = self.capacity * self.extend_ratio; - self.arr.resize(new_capacity, 0); - // 更新列表容量 - self.capacity = new_capacity; - } - - /* 将列表转换为数组 */ - pub fn to_array(&mut self) -> Vec { - // 仅转换有效长度范围内的列表元素 - let mut arr = Vec::new(); - for i in 0..self.size { - arr.push(self.get(i)); - } - arr - } - } + [class]{MyList}-[func]{} ``` === "C" ```c title="my_list.c" - /* 列表类 */ - typedef struct { - int *arr; // 数组(存储列表元素) - int capacity; // 列表容量 - int size; // 列表大小 - int extendRatio; // 列表每次扩容的倍数 - } MyList; - - /* 构造函数 */ - MyList *newMyList() { - MyList *nums = malloc(sizeof(MyList)); - nums->capacity = 10; - nums->arr = malloc(sizeof(int) * nums->capacity); - nums->size = 0; - nums->extendRatio = 2; - return nums; - } - - /* 析构函数 */ - void delMyList(MyList *nums) { - free(nums->arr); - free(nums); - } - - /* 获取列表长度 */ - int size(MyList *nums) { - return nums->size; - } - - /* 获取列表容量 */ - int capacity(MyList *nums) { - return nums->capacity; - } - - /* 访问元素 */ - int get(MyList *nums, int index) { - assert(index >= 0 && index < nums->size); - return nums->arr[index]; - } - - /* 更新元素 */ - void set(MyList *nums, int index, int num) { - assert(index >= 0 && index < nums->size); - nums->arr[index] = num; - } - - /* 在尾部添加元素 */ - void add(MyList *nums, int num) { - if (size(nums) == capacity(nums)) { - extendCapacity(nums); // 扩容 - } - nums->arr[size(nums)] = num; - nums->size++; - } - - /* 在中间插入元素 */ - void insert(MyList *nums, int index, int num) { - assert(index >= 0 && index < size(nums)); - // 元素数量超出容量时,触发扩容机制 - if (size(nums) == capacity(nums)) { - extendCapacity(nums); // 扩容 - } - for (int i = size(nums); i > index; --i) { - nums->arr[i] = nums->arr[i - 1]; - } - nums->arr[index] = num; - nums->size++; - } - - /* 删除元素 */ - // 注意:stdio.h 占用了 remove 关键词 - int removeItem(MyList *nums, int index) { - assert(index >= 0 && index < size(nums)); - int num = nums->arr[index]; - for (int i = index; i < size(nums) - 1; i++) { - nums->arr[i] = nums->arr[i + 1]; - } - nums->size--; - return num; - } - - /* 列表扩容 */ - void extendCapacity(MyList *nums) { - // 先分配空间 - int newCapacity = capacity(nums) * nums->extendRatio; - int *extend = (int *)malloc(sizeof(int) * newCapacity); - int *temp = nums->arr; - - // 拷贝旧数据到新数据 - for (int i = 0; i < size(nums); i++) - extend[i] = nums->arr[i]; - - // 释放旧数据 - free(temp); - - // 更新新数据 - nums->arr = extend; - nums->capacity = newCapacity; - } - - /* 将列表转换为 Array 用于打印 */ - int *toArray(MyList *nums) { - return nums->arr; - } + [class]{MyList}-[func]{} ``` === "Kotlin" ```kotlin title="my_list.kt" - /* 列表类 */ - class MyList { - private var arr: IntArray = intArrayOf() // 数组(存储列表元素) - private var capacity: Int = 10 // 列表容量 - private var size: Int = 0 // 列表长度(当前元素数量) - private var extendRatio: Int = 2 // 每次列表扩容的倍数 - - /* 构造方法 */ - init { - arr = IntArray(capacity) - } - - /* 获取列表长度(当前元素数量) */ - fun size(): Int { - return size - } - - /* 获取列表容量 */ - fun capacity(): Int { - return capacity - } - - /* 访问元素 */ - fun get(index: Int): Int { - // 索引如果越界,则抛出异常,下同 - if (index < 0 || index >= 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 = arr[index] - // 将将索引 index 之后的元素都向前移动一位 - for (j in index..= size - @arr[index] - end - - ### 访问元素 ### - def set(index, num) - raise IndexError, "索引越界" if index < 0 || index >= size - @arr[index] = num - end - - ### 在尾部添加元素 ### - def add(num) - # 元素数量超出容量时,触发扩容机制 - extend_capacity if size == capacity - @arr[size] = num - - # 更新元素数量 - @size += 1 - end - - ### 在中间插入元素 ### - def insert(index, num) - raise IndexError, "索引越界" if index < 0 || index >= size - - # 元素数量超出容量时,触发扩容机制 - extend_capacity if size == capacity - - # 将索引 index 以及之后的元素都向后移动一位 - for j in (size - 1).downto(index) - @arr[j + 1] = @arr[j] - end - @arr[index] = num - - # 更新元素数量 - @size += 1 - end - - ### 删除元素 ### - def remove(index) - raise IndexError, "索引越界" if index < 0 || index >= size - num = @arr[index] - - # 将将索引 index 之后的元素都向前移动一位 - for j in index...size - @arr[j] = @arr[j + 1] - end - - # 更新元素数量 - @size -= 1 - - # 返回被删除的元素 - num - end - - ### 列表扩容 ### - def extend_capacity - # 新建一个长度为原数组 extend_ratio 倍的新数组,并将原数组复制到新数组 - arr = @arr.dup + Array.new(capacity * (@extend_ratio - 1)) - # 更新列表容量 - @capacity = arr.length - end - - ### 将列表转换为数组 ### - def to_array - sz = size - # 仅转换有效长度范围内的列表元素 - arr = Array.new(sz) - for i in 0...sz - arr[i] = get(i) - end - arr - end - end + [class]{MyList}-[func]{} ``` === "Zig" ```zig title="my_list.zig" - // 列表类 - fn MyList(comptime T: type) type { - return struct { - const Self = @This(); - - arr: []T = undefined, // 数组(存储列表元素) - arrCapacity: usize = 10, // 列表容量 - numSize: usize = 0, // 列表长度(当前元素数量) - extendRatio: usize = 2, // 每次列表扩容的倍数 - mem_arena: ?std.heap.ArenaAllocator = null, - mem_allocator: std.mem.Allocator = undefined, // 内存分配器 - - // 构造函数(分配内存+初始化列表) - pub fn init(self: *Self, allocator: std.mem.Allocator) !void { - if (self.mem_arena == null) { - self.mem_arena = std.heap.ArenaAllocator.init(allocator); - self.mem_allocator = self.mem_arena.?.allocator(); - } - self.arr = try self.mem_allocator.alloc(T, self.arrCapacity); - @memset(self.arr, @as(T, 0)); - } - - // 析构函数(释放内存) - pub fn deinit(self: *Self) void { - if (self.mem_arena == null) return; - self.mem_arena.?.deinit(); - } - - // 获取列表长度(当前元素数量) - pub fn size(self: *Self) usize { - return self.numSize; - } - - // 获取列表容量 - pub fn capacity(self: *Self) usize { - return self.arrCapacity; - } - - // 访问元素 - pub fn get(self: *Self, index: usize) T { - // 索引如果越界,则抛出异常,下同 - if (index < 0 or index >= self.size()) @panic("索引越界"); - return self.arr[index]; - } - - // 更新元素 - pub fn set(self: *Self, index: usize, num: T) void { - // 索引如果越界,则抛出异常,下同 - if (index < 0 or index >= self.size()) @panic("索引越界"); - self.arr[index] = num; - } - - // 在尾部添加元素 - pub fn add(self: *Self, num: T) !void { - // 元素数量超出容量时,触发扩容机制 - if (self.size() == self.capacity()) try self.extendCapacity(); - self.arr[self.size()] = num; - // 更新元素数量 - self.numSize += 1; - } - - // 在中间插入元素 - pub fn insert(self: *Self, index: usize, num: T) !void { - if (index < 0 or index >= self.size()) @panic("索引越界"); - // 元素数量超出容量时,触发扩容机制 - if (self.size() == self.capacity()) try self.extendCapacity(); - // 将索引 index 以及之后的元素都向后移动一位 - var j = self.size() - 1; - while (j >= index) : (j -= 1) { - self.arr[j + 1] = self.arr[j]; - } - self.arr[index] = num; - // 更新元素数量 - self.numSize += 1; - } - - // 删除元素 - pub fn remove(self: *Self, index: usize) T { - if (index < 0 or index >= self.size()) @panic("索引越界"); - var num = self.arr[index]; - // 将索引 index 之后的元素都向前移动一位 - var j = index; - while (j < self.size() - 1) : (j += 1) { - self.arr[j] = self.arr[j + 1]; - } - // 更新元素数量 - self.numSize -= 1; - // 返回被删除的元素 - return num; - } - - // 列表扩容 - pub fn extendCapacity(self: *Self) !void { - // 新建一个长度为 size * extendRatio 的数组,并将原数组复制到新数组 - var newCapacity = self.capacity() * self.extendRatio; - var extend = try self.mem_allocator.alloc(T, newCapacity); - @memset(extend, @as(T, 0)); - // 将原数组中的所有元素复制到新数组 - std.mem.copy(T, extend, self.arr); - self.arr = extend; - // 更新列表容量 - self.arrCapacity = newCapacity; - } - - // 将列表转换为数组 - pub fn toArray(self: *Self) ![]T { - // 仅转换有效长度范围内的列表元素 - var arr = try self.mem_allocator.alloc(T, self.size()); - @memset(arr, @as(T, 0)); - for (arr, 0..) |*num, i| { - num.* = self.get(i); - } - return arr; - } - }; - } + [class]{MyList}-[func]{} ``` - -??? pythontutor "Code Visualization" - -
- diff --git a/en/docs/chapter_backtracking/backtracking_algorithm.md b/en/docs/chapter_backtracking/backtracking_algorithm.md index c339fb232..18bc54a8f 100644 --- a/en/docs/chapter_backtracking/backtracking_algorithm.md +++ b/en/docs/chapter_backtracking/backtracking_algorithm.md @@ -18,11 +18,11 @@ For this problem, we traverse this tree in preorder and check if the current nod ```python title="preorder_traversal_i_compact.py" def pre_order(root: TreeNode): - """前序遍历:例题一""" + """Pre-order traversal: Example one""" if root is None: return if root.val == 7: - # 记录解 + # Record solution res.append(root) pre_order(root.left) pre_order(root.right) @@ -31,30 +31,19 @@ For this problem, we traverse this tree in preorder and check if the current nod === "C++" ```cpp title="preorder_traversal_i_compact.cpp" - /* 前序遍历:例题一 */ - void preOrder(TreeNode *root) { - if (root == nullptr) { - return; - } - if (root->val == 7) { - // 记录解 - res.push_back(root); - } - preOrder(root->left); - preOrder(root->right); - } + [class]{}-[func]{preOrder} ``` === "Java" ```java title="preorder_traversal_i_compact.java" - /* 前序遍历:例题一 */ + /* Pre-order traversal: Example one */ void preOrder(TreeNode root) { if (root == null) { return; } if (root.val == 7) { - // 记录解 + // Record solution res.add(root); } preOrder(root.left); @@ -65,156 +54,55 @@ For this problem, we traverse this tree in preorder and check if the current nod === "C#" ```csharp title="preorder_traversal_i_compact.cs" - /* 前序遍历:例题一 */ - void PreOrder(TreeNode? root) { - if (root == null) { - return; - } - if (root.val == 7) { - // 记录解 - res.Add(root); - } - PreOrder(root.left); - PreOrder(root.right); - } + [class]{preorder_traversal_i_compact}-[func]{PreOrder} ``` === "Go" ```go title="preorder_traversal_i_compact.go" - /* 前序遍历:例题一 */ - func preOrderI(root *TreeNode, res *[]*TreeNode) { - if root == nil { - return - } - if (root.Val).(int) == 7 { - // 记录解 - *res = append(*res, root) - } - preOrderI(root.Left, res) - preOrderI(root.Right, res) - } + [class]{}-[func]{preOrderI} ``` === "Swift" ```swift title="preorder_traversal_i_compact.swift" - /* 前序遍历:例题一 */ - func preOrder(root: TreeNode?) { - guard let root = root else { - return - } - if root.val == 7 { - // 记录解 - res.append(root) - } - preOrder(root: root.left) - preOrder(root: root.right) - } + [class]{}-[func]{preOrder} ``` === "JS" ```javascript title="preorder_traversal_i_compact.js" - /* 前序遍历:例题一 */ - function preOrder(root, res) { - if (root === null) { - return; - } - if (root.val === 7) { - // 记录解 - res.push(root); - } - preOrder(root.left, res); - preOrder(root.right, res); - } + [class]{}-[func]{preOrder} ``` === "TS" ```typescript title="preorder_traversal_i_compact.ts" - /* 前序遍历:例题一 */ - function preOrder(root: TreeNode | null, res: TreeNode[]): void { - if (root === null) { - return; - } - if (root.val === 7) { - // 记录解 - res.push(root); - } - preOrder(root.left, res); - preOrder(root.right, res); - } + [class]{}-[func]{preOrder} ``` === "Dart" ```dart title="preorder_traversal_i_compact.dart" - /* 前序遍历:例题一 */ - void preOrder(TreeNode? root, List res) { - if (root == null) { - return; - } - if (root.val == 7) { - // 记录解 - res.add(root); - } - preOrder(root.left, res); - preOrder(root.right, res); - } + [class]{}-[func]{preOrder} ``` === "Rust" ```rust title="preorder_traversal_i_compact.rs" - /* 前序遍历:例题一 */ - fn pre_order(res: &mut Vec>>, root: Option>>) { - if root.is_none() { - return; - } - if let Some(node) = root { - if node.borrow().val == 7 { - // 记录解 - res.push(node.clone()); - } - pre_order(res, node.borrow().left.clone()); - pre_order(res, node.borrow().right.clone()); - } - } + [class]{}-[func]{pre_order} ``` === "C" ```c title="preorder_traversal_i_compact.c" - /* 前序遍历:例题一 */ - void preOrder(TreeNode *root) { - if (root == NULL) { - return; - } - if (root->val == 7) { - // 记录解 - res[resSize++] = root; - } - preOrder(root->left); - preOrder(root->right); - } + [class]{}-[func]{preOrder} ``` === "Kotlin" ```kotlin title="preorder_traversal_i_compact.kt" - /* 前序遍历:例题一 */ - fun preOrder(root: TreeNode?) { - if (root == null) { - return - } - if (root._val == 7) { - // 记录解 - res!!.add(root) - } - preOrder(root.left) - preOrder(root.right) - } + [class]{}-[func]{preOrder} ``` === "Ruby" @@ -229,11 +117,6 @@ For this problem, we traverse this tree in preorder and check if the current nod [class]{}-[func]{preOrder} ``` -??? pythontutor "Code Visualization" - -
- - ![Searching nodes in preorder traversal](backtracking_algorithm.assets/preorder_find_nodes.png){ class="animation-figure" }

Figure 13-1   Searching nodes in preorder traversal

@@ -256,58 +139,43 @@ Based on the code from Example One, we need to use a list `path` to record the v ```python title="preorder_traversal_ii_compact.py" def pre_order(root: TreeNode): - """前序遍历:例题二""" + """Pre-order traversal: Example two""" if root is None: return - # 尝试 + # Attempt path.append(root) if root.val == 7: - # 记录解 + # Record solution res.append(list(path)) pre_order(root.left) pre_order(root.right) - # 回退 + # Retract path.pop() ``` === "C++" ```cpp title="preorder_traversal_ii_compact.cpp" - /* 前序遍历:例题二 */ - void preOrder(TreeNode *root) { - if (root == nullptr) { - return; - } - // 尝试 - path.push_back(root); - if (root->val == 7) { - // 记录解 - res.push_back(path); - } - preOrder(root->left); - preOrder(root->right); - // 回退 - path.pop_back(); - } + [class]{}-[func]{preOrder} ``` === "Java" ```java title="preorder_traversal_ii_compact.java" - /* 前序遍历:例题二 */ + /* Pre-order traversal: Example two */ void preOrder(TreeNode root) { if (root == null) { return; } - // 尝试 + // Attempt path.add(root); if (root.val == 7) { - // 记录解 + // Record solution res.add(new ArrayList<>(path)); } preOrder(root.left); preOrder(root.right); - // 回退 + // Retract path.remove(path.size() - 1); } ``` @@ -315,208 +183,55 @@ Based on the code from Example One, we need to use a list `path` to record the v === "C#" ```csharp title="preorder_traversal_ii_compact.cs" - /* 前序遍历:例题二 */ - void PreOrder(TreeNode? root) { - if (root == null) { - return; - } - // 尝试 - path.Add(root); - if (root.val == 7) { - // 记录解 - res.Add(new List(path)); - } - PreOrder(root.left); - PreOrder(root.right); - // 回退 - path.RemoveAt(path.Count - 1); - } + [class]{preorder_traversal_ii_compact}-[func]{PreOrder} ``` === "Go" ```go title="preorder_traversal_ii_compact.go" - /* 前序遍历:例题二 */ - func preOrderII(root *TreeNode, res *[][]*TreeNode, path *[]*TreeNode) { - if root == nil { - return - } - // 尝试 - *path = append(*path, root) - if root.Val.(int) == 7 { - // 记录解 - *res = append(*res, append([]*TreeNode{}, *path...)) - } - preOrderII(root.Left, res, path) - preOrderII(root.Right, res, path) - // 回退 - *path = (*path)[:len(*path)-1] - } + [class]{}-[func]{preOrderII} ``` === "Swift" ```swift title="preorder_traversal_ii_compact.swift" - /* 前序遍历:例题二 */ - func preOrder(root: TreeNode?) { - guard let root = root else { - return - } - // 尝试 - path.append(root) - if root.val == 7 { - // 记录解 - res.append(path) - } - preOrder(root: root.left) - preOrder(root: root.right) - // 回退 - path.removeLast() - } + [class]{}-[func]{preOrder} ``` === "JS" ```javascript title="preorder_traversal_ii_compact.js" - /* 前序遍历:例题二 */ - function preOrder(root, path, res) { - if (root === null) { - return; - } - // 尝试 - path.push(root); - if (root.val === 7) { - // 记录解 - res.push([...path]); - } - preOrder(root.left, path, res); - preOrder(root.right, path, res); - // 回退 - path.pop(); - } + [class]{}-[func]{preOrder} ``` === "TS" ```typescript title="preorder_traversal_ii_compact.ts" - /* 前序遍历:例题二 */ - function preOrder( - root: TreeNode | null, - path: TreeNode[], - res: TreeNode[][] - ): void { - if (root === null) { - return; - } - // 尝试 - path.push(root); - if (root.val === 7) { - // 记录解 - res.push([...path]); - } - preOrder(root.left, path, res); - preOrder(root.right, path, res); - // 回退 - path.pop(); - } + [class]{}-[func]{preOrder} ``` === "Dart" ```dart title="preorder_traversal_ii_compact.dart" - /* 前序遍历:例题二 */ - void preOrder( - TreeNode? root, - List path, - List> res, - ) { - if (root == null) { - return; - } - - // 尝试 - path.add(root); - if (root.val == 7) { - // 记录解 - res.add(List.from(path)); - } - preOrder(root.left, path, res); - preOrder(root.right, path, res); - // 回退 - path.removeLast(); - } + [class]{}-[func]{preOrder} ``` === "Rust" ```rust title="preorder_traversal_ii_compact.rs" - /* 前序遍历:例题二 */ - fn pre_order( - res: &mut Vec>>>, - path: &mut Vec>>, - root: Option>>, - ) { - if root.is_none() { - return; - } - if let Some(node) = root { - // 尝试 - path.push(node.clone()); - if node.borrow().val == 7 { - // 记录解 - res.push(path.clone()); - } - pre_order(res, path, node.borrow().left.clone()); - pre_order(res, path, node.borrow().right.clone()); - // 回退 - path.remove(path.len() - 1); - } - } + [class]{}-[func]{pre_order} ``` === "C" ```c title="preorder_traversal_ii_compact.c" - /* 前序遍历:例题二 */ - void preOrder(TreeNode *root) { - if (root == NULL) { - return; - } - // 尝试 - path[pathSize++] = root; - if (root->val == 7) { - // 记录解 - for (int i = 0; i < pathSize; ++i) { - res[resSize][i] = path[i]; - } - resSize++; - } - preOrder(root->left); - preOrder(root->right); - // 回退 - pathSize--; - } + [class]{}-[func]{preOrder} ``` === "Kotlin" ```kotlin title="preorder_traversal_ii_compact.kt" - /* 前序遍历:例题二 */ - fun preOrder(root: TreeNode?) { - if (root == null) { - return - } - // 尝试 - path!!.add(root) - if (root._val == 7) { - // 记录解 - res!!.add(path!!.toMutableList()) - } - preOrder(root.left) - preOrder(root.right) - // 回退 - path!!.removeAt(path!!.size - 1) - } + [class]{}-[func]{preOrder} ``` === "Ruby" @@ -531,11 +246,6 @@ Based on the code from Example One, we need to use a list `path` to record the v [class]{}-[func]{preOrder} ``` -??? pythontutor "Code Visualization" - -
- - In each "try", we record the path by adding the current node to `path`; before "retreating", we need to pop the node from `path` **to restore the state before this attempt**. Observe the process shown in Figure 13-2, **we can understand trying and retreating as "advancing" and "undoing"**, two operations that are reverse to each other. @@ -589,61 +299,45 @@ To meet the above constraints, **we need to add a pruning operation**: during th ```python title="preorder_traversal_iii_compact.py" def pre_order(root: TreeNode): - """前序遍历:例题三""" - # 剪枝 + """Pre-order traversal: Example three""" + # Pruning if root is None or root.val == 3: return - # 尝试 + # Attempt path.append(root) if root.val == 7: - # 记录解 + # Record solution res.append(list(path)) pre_order(root.left) pre_order(root.right) - # 回退 + # Retract path.pop() ``` === "C++" ```cpp title="preorder_traversal_iii_compact.cpp" - /* 前序遍历:例题三 */ - void preOrder(TreeNode *root) { - // 剪枝 - if (root == nullptr || root->val == 3) { - return; - } - // 尝试 - path.push_back(root); - if (root->val == 7) { - // 记录解 - res.push_back(path); - } - preOrder(root->left); - preOrder(root->right); - // 回退 - path.pop_back(); - } + [class]{}-[func]{preOrder} ``` === "Java" ```java title="preorder_traversal_iii_compact.java" - /* 前序遍历:例题三 */ + /* Pre-order traversal: Example three */ void preOrder(TreeNode root) { - // 剪枝 + // Pruning if (root == null || root.val == 3) { return; } - // 尝试 + // Attempt path.add(root); if (root.val == 7) { - // 记录解 + // Record solution res.add(new ArrayList<>(path)); } preOrder(root.left); preOrder(root.right); - // 回退 + // Retract path.remove(path.size() - 1); } ``` @@ -651,216 +345,55 @@ To meet the above constraints, **we need to add a pruning operation**: during th === "C#" ```csharp title="preorder_traversal_iii_compact.cs" - /* 前序遍历:例题三 */ - void PreOrder(TreeNode? root) { - // 剪枝 - if (root == null || root.val == 3) { - return; - } - // 尝试 - path.Add(root); - if (root.val == 7) { - // 记录解 - res.Add(new List(path)); - } - PreOrder(root.left); - PreOrder(root.right); - // 回退 - path.RemoveAt(path.Count - 1); - } + [class]{preorder_traversal_iii_compact}-[func]{PreOrder} ``` === "Go" ```go title="preorder_traversal_iii_compact.go" - /* 前序遍历:例题三 */ - func preOrderIII(root *TreeNode, res *[][]*TreeNode, path *[]*TreeNode) { - // 剪枝 - if root == nil || root.Val == 3 { - return - } - // 尝试 - *path = append(*path, root) - if root.Val.(int) == 7 { - // 记录解 - *res = append(*res, append([]*TreeNode{}, *path...)) - } - preOrderIII(root.Left, res, path) - preOrderIII(root.Right, res, path) - // 回退 - *path = (*path)[:len(*path)-1] - } + [class]{}-[func]{preOrderIII} ``` === "Swift" ```swift title="preorder_traversal_iii_compact.swift" - /* 前序遍历:例题三 */ - func preOrder(root: TreeNode?) { - // 剪枝 - guard let root = root, root.val != 3 else { - return - } - // 尝试 - path.append(root) - if root.val == 7 { - // 记录解 - res.append(path) - } - preOrder(root: root.left) - preOrder(root: root.right) - // 回退 - path.removeLast() - } + [class]{}-[func]{preOrder} ``` === "JS" ```javascript title="preorder_traversal_iii_compact.js" - /* 前序遍历:例题三 */ - function preOrder(root, path, res) { - // 剪枝 - if (root === null || root.val === 3) { - return; - } - // 尝试 - path.push(root); - if (root.val === 7) { - // 记录解 - res.push([...path]); - } - preOrder(root.left, path, res); - preOrder(root.right, path, res); - // 回退 - path.pop(); - } + [class]{}-[func]{preOrder} ``` === "TS" ```typescript title="preorder_traversal_iii_compact.ts" - /* 前序遍历:例题三 */ - function preOrder( - root: TreeNode | null, - path: TreeNode[], - res: TreeNode[][] - ): void { - // 剪枝 - if (root === null || root.val === 3) { - return; - } - // 尝试 - path.push(root); - if (root.val === 7) { - // 记录解 - res.push([...path]); - } - preOrder(root.left, path, res); - preOrder(root.right, path, res); - // 回退 - path.pop(); - } + [class]{}-[func]{preOrder} ``` === "Dart" ```dart title="preorder_traversal_iii_compact.dart" - /* 前序遍历:例题三 */ - void preOrder( - TreeNode? root, - List path, - List> res, - ) { - if (root == null || root.val == 3) { - return; - } - - // 尝试 - path.add(root); - if (root.val == 7) { - // 记录解 - res.add(List.from(path)); - } - preOrder(root.left, path, res); - preOrder(root.right, path, res); - // 回退 - path.removeLast(); - } + [class]{}-[func]{preOrder} ``` === "Rust" ```rust title="preorder_traversal_iii_compact.rs" - /* 前序遍历:例题三 */ - fn pre_order( - res: &mut Vec>>>, - path: &mut Vec>>, - root: Option>>, - ) { - // 剪枝 - if root.is_none() || root.as_ref().unwrap().borrow().val == 3 { - return; - } - if let Some(node) = root { - // 尝试 - path.push(node.clone()); - if node.borrow().val == 7 { - // 记录解 - res.push(path.clone()); - } - pre_order(res, path, node.borrow().left.clone()); - pre_order(res, path, node.borrow().right.clone()); - // 回退 - path.remove(path.len() - 1); - } - } + [class]{}-[func]{pre_order} ``` === "C" ```c title="preorder_traversal_iii_compact.c" - /* 前序遍历:例题三 */ - void preOrder(TreeNode *root) { - // 剪枝 - if (root == NULL || root->val == 3) { - return; - } - // 尝试 - path[pathSize++] = root; - if (root->val == 7) { - // 记录解 - for (int i = 0; i < pathSize; i++) { - res[resSize][i] = path[i]; - } - resSize++; - } - preOrder(root->left); - preOrder(root->right); - // 回退 - pathSize--; - } + [class]{}-[func]{preOrder} ``` === "Kotlin" ```kotlin title="preorder_traversal_iii_compact.kt" - /* 前序遍历:例题三 */ - fun preOrder(root: TreeNode?) { - // 剪枝 - if (root == null || root._val == 3) { - return - } - // 尝试 - path!!.add(root) - if (root._val == 7) { - // 记录解 - res!!.add(path!!.toMutableList()) - } - preOrder(root.left) - preOrder(root.right) - // 回退 - path!!.removeAt(path!!.size - 1) - } + [class]{}-[func]{preOrder} ``` === "Ruby" @@ -875,11 +408,6 @@ To meet the above constraints, **we need to add a pruning operation**: during th [class]{}-[func]{preOrder} ``` -??? pythontutor "Code Visualization" - -
- - "Pruning" is a very vivid noun. As shown in the diagram below, in the search process, **we "cut off" the search branches that do not meet the constraints**, avoiding many meaningless attempts, thus enhancing the search efficiency. ![Pruning based on constraints](backtracking_algorithm.assets/preorder_find_constrained_paths.png){ class="animation-figure" } @@ -1218,140 +746,105 @@ Next, we solve Example Three based on the framework code. The `state` is the nod ```python title="preorder_traversal_iii_template.py" def is_solution(state: list[TreeNode]) -> bool: - """判断当前状态是否为解""" + """Determine if the current state is a solution""" return state and state[-1].val == 7 def record_solution(state: list[TreeNode], res: list[list[TreeNode]]): - """记录解""" + """Record solution""" res.append(list(state)) def is_valid(state: list[TreeNode], choice: TreeNode) -> bool: - """判断在当前状态下,该选择是否合法""" + """Determine if the choice is legal under the current state""" return choice is not None and choice.val != 3 def make_choice(state: list[TreeNode], choice: TreeNode): - """更新状态""" + """Update state""" state.append(choice) def undo_choice(state: list[TreeNode], choice: TreeNode): - """恢复状态""" + """Restore state""" state.pop() def backtrack( state: list[TreeNode], choices: list[TreeNode], res: list[list[TreeNode]] ): - """回溯算法:例题三""" - # 检查是否为解 + """Backtracking algorithm: Example three""" + # Check if it's a solution if is_solution(state): - # 记录解 + # Record solution record_solution(state, res) - # 遍历所有选择 + # Traverse all choices for choice in choices: - # 剪枝:检查选择是否合法 + # Pruning: check if the choice is legal if is_valid(state, choice): - # 尝试:做出选择,更新状态 + # Attempt: make a choice, update the state make_choice(state, choice) - # 进行下一轮选择 + # Proceed to the next round of selection backtrack(state, [choice.left, choice.right], res) - # 回退:撤销选择,恢复到之前的状态 + # Retract: undo the choice, restore to the previous state undo_choice(state, choice) ``` === "C++" ```cpp title="preorder_traversal_iii_template.cpp" - /* 判断当前状态是否为解 */ - bool isSolution(vector &state) { - return !state.empty() && state.back()->val == 7; - } + [class]{}-[func]{isSolution} - /* 记录解 */ - void recordSolution(vector &state, vector> &res) { - res.push_back(state); - } + [class]{}-[func]{recordSolution} - /* 判断在当前状态下,该选择是否合法 */ - bool isValid(vector &state, TreeNode *choice) { - return choice != nullptr && choice->val != 3; - } + [class]{}-[func]{isValid} - /* 更新状态 */ - void makeChoice(vector &state, TreeNode *choice) { - state.push_back(choice); - } + [class]{}-[func]{makeChoice} - /* 恢复状态 */ - void undoChoice(vector &state, TreeNode *choice) { - state.pop_back(); - } + [class]{}-[func]{undoChoice} - /* 回溯算法:例题三 */ - void backtrack(vector &state, vector &choices, vector> &res) { - // 检查是否为解 - if (isSolution(state)) { - // 记录解 - recordSolution(state, res); - } - // 遍历所有选择 - for (TreeNode *choice : choices) { - // 剪枝:检查选择是否合法 - if (isValid(state, choice)) { - // 尝试:做出选择,更新状态 - makeChoice(state, choice); - // 进行下一轮选择 - vector nextChoices{choice->left, choice->right}; - backtrack(state, nextChoices, res); - // 回退:撤销选择,恢复到之前的状态 - undoChoice(state, choice); - } - } - } + [class]{}-[func]{backtrack} ``` === "Java" ```java title="preorder_traversal_iii_template.java" - /* 判断当前状态是否为解 */ + /* Determine if the current state is a solution */ boolean isSolution(List state) { return !state.isEmpty() && state.get(state.size() - 1).val == 7; } - /* 记录解 */ + /* Record solution */ void recordSolution(List state, List> res) { res.add(new ArrayList<>(state)); } - /* 判断在当前状态下,该选择是否合法 */ + /* Determine if the choice is legal under the current state */ boolean isValid(List state, TreeNode choice) { return choice != null && choice.val != 3; } - /* 更新状态 */ + /* Update state */ void makeChoice(List state, TreeNode choice) { state.add(choice); } - /* 恢复状态 */ + /* Restore state */ void undoChoice(List state, TreeNode choice) { state.remove(state.size() - 1); } - /* 回溯算法:例题三 */ + /* Backtracking algorithm: Example three */ void backtrack(List state, List choices, List> res) { - // 检查是否为解 + // Check if it's a solution if (isSolution(state)) { - // 记录解 + // Record solution recordSolution(state, res); } - // 遍历所有选择 + // Traverse all choices for (TreeNode choice : choices) { - // 剪枝:检查选择是否合法 + // Pruning: check if the choice is legal if (isValid(state, choice)) { - // 尝试:做出选择,更新状态 + // Attempt: make a choice, update the state makeChoice(state, choice); - // 进行下一轮选择 + // Proceed to the next round of selection backtrack(state, Arrays.asList(choice.left, choice.right), res); - // 回退:撤销选择,恢复到之前的状态 + // Retract: undo the choice, restore to the previous state undoChoice(state, choice); } } @@ -1361,483 +854,145 @@ Next, we solve Example Three based on the framework code. The `state` is the nod === "C#" ```csharp title="preorder_traversal_iii_template.cs" - /* 判断当前状态是否为解 */ - bool IsSolution(List state) { - return state.Count != 0 && state[^1].val == 7; - } + [class]{preorder_traversal_iii_template}-[func]{IsSolution} - /* 记录解 */ - void RecordSolution(List state, List> res) { - res.Add(new List(state)); - } + [class]{preorder_traversal_iii_template}-[func]{RecordSolution} - /* 判断在当前状态下,该选择是否合法 */ - bool IsValid(List state, TreeNode choice) { - return choice != null && choice.val != 3; - } + [class]{preorder_traversal_iii_template}-[func]{IsValid} - /* 更新状态 */ - void MakeChoice(List state, TreeNode choice) { - state.Add(choice); - } + [class]{preorder_traversal_iii_template}-[func]{MakeChoice} - /* 恢复状态 */ - void UndoChoice(List state, TreeNode choice) { - state.RemoveAt(state.Count - 1); - } + [class]{preorder_traversal_iii_template}-[func]{UndoChoice} - /* 回溯算法:例题三 */ - void Backtrack(List state, List choices, List> res) { - // 检查是否为解 - if (IsSolution(state)) { - // 记录解 - RecordSolution(state, res); - } - // 遍历所有选择 - foreach (TreeNode choice in choices) { - // 剪枝:检查选择是否合法 - if (IsValid(state, choice)) { - // 尝试:做出选择,更新状态 - MakeChoice(state, choice); - // 进行下一轮选择 - Backtrack(state, [choice.left!, choice.right!], res); - // 回退:撤销选择,恢复到之前的状态 - UndoChoice(state, choice); - } - } - } + [class]{preorder_traversal_iii_template}-[func]{Backtrack} ``` === "Go" ```go title="preorder_traversal_iii_template.go" - /* 判断当前状态是否为解 */ - func isSolution(state *[]*TreeNode) bool { - return len(*state) != 0 && (*state)[len(*state)-1].Val == 7 - } + [class]{}-[func]{isSolution} - /* 记录解 */ - func recordSolution(state *[]*TreeNode, res *[][]*TreeNode) { - *res = append(*res, append([]*TreeNode{}, *state...)) - } + [class]{}-[func]{recordSolution} - /* 判断在当前状态下,该选择是否合法 */ - func isValid(state *[]*TreeNode, choice *TreeNode) bool { - return choice != nil && choice.Val != 3 - } + [class]{}-[func]{isValid} - /* 更新状态 */ - func makeChoice(state *[]*TreeNode, choice *TreeNode) { - *state = append(*state, choice) - } + [class]{}-[func]{makeChoice} - /* 恢复状态 */ - func undoChoice(state *[]*TreeNode, choice *TreeNode) { - *state = (*state)[:len(*state)-1] - } + [class]{}-[func]{undoChoice} - /* 回溯算法:例题三 */ - func backtrackIII(state *[]*TreeNode, choices *[]*TreeNode, res *[][]*TreeNode) { - // 检查是否为解 - if isSolution(state) { - // 记录解 - recordSolution(state, res) - } - // 遍历所有选择 - for _, choice := range *choices { - // 剪枝:检查选择是否合法 - if isValid(state, choice) { - // 尝试:做出选择,更新状态 - makeChoice(state, choice) - // 进行下一轮选择 - temp := make([]*TreeNode, 0) - temp = append(temp, choice.Left, choice.Right) - backtrackIII(state, &temp, res) - // 回退:撤销选择,恢复到之前的状态 - undoChoice(state, choice) - } - } - } + [class]{}-[func]{backtrackIII} ``` === "Swift" ```swift title="preorder_traversal_iii_template.swift" - /* 判断当前状态是否为解 */ - func isSolution(state: [TreeNode]) -> Bool { - !state.isEmpty && state.last!.val == 7 - } + [class]{}-[func]{isSolution} - /* 记录解 */ - func recordSolution(state: [TreeNode], res: inout [[TreeNode]]) { - res.append(state) - } + [class]{}-[func]{recordSolution} - /* 判断在当前状态下,该选择是否合法 */ - func isValid(state: [TreeNode], choice: TreeNode?) -> Bool { - choice != nil && choice!.val != 3 - } + [class]{}-[func]{isValid} - /* 更新状态 */ - func makeChoice(state: inout [TreeNode], choice: TreeNode) { - state.append(choice) - } + [class]{}-[func]{makeChoice} - /* 恢复状态 */ - func undoChoice(state: inout [TreeNode], choice: TreeNode) { - state.removeLast() - } + [class]{}-[func]{undoChoice} - /* 回溯算法:例题三 */ - func backtrack(state: inout [TreeNode], choices: [TreeNode], res: inout [[TreeNode]]) { - // 检查是否为解 - if isSolution(state: state) { - recordSolution(state: state, res: &res) - } - // 遍历所有选择 - for choice in choices { - // 剪枝:检查选择是否合法 - if isValid(state: state, choice: choice) { - // 尝试:做出选择,更新状态 - makeChoice(state: &state, choice: choice) - // 进行下一轮选择 - backtrack(state: &state, choices: [choice.left, choice.right].compactMap { $0 }, res: &res) - // 回退:撤销选择,恢复到之前的状态 - undoChoice(state: &state, choice: choice) - } - } - } + [class]{}-[func]{backtrack} ``` === "JS" ```javascript title="preorder_traversal_iii_template.js" - /* 判断当前状态是否为解 */ - function isSolution(state) { - return state && state[state.length - 1]?.val === 7; - } + [class]{}-[func]{isSolution} - /* 记录解 */ - function recordSolution(state, res) { - res.push([...state]); - } + [class]{}-[func]{recordSolution} - /* 判断在当前状态下,该选择是否合法 */ - function isValid(state, choice) { - return choice !== null && choice.val !== 3; - } + [class]{}-[func]{isValid} - /* 更新状态 */ - function makeChoice(state, choice) { - state.push(choice); - } + [class]{}-[func]{makeChoice} - /* 恢复状态 */ - function undoChoice(state) { - state.pop(); - } + [class]{}-[func]{undoChoice} - /* 回溯算法:例题三 */ - function backtrack(state, choices, res) { - // 检查是否为解 - if (isSolution(state)) { - // 记录解 - recordSolution(state, res); - } - // 遍历所有选择 - for (const choice of choices) { - // 剪枝:检查选择是否合法 - if (isValid(state, choice)) { - // 尝试:做出选择,更新状态 - makeChoice(state, choice); - // 进行下一轮选择 - backtrack(state, [choice.left, choice.right], res); - // 回退:撤销选择,恢复到之前的状态 - undoChoice(state); - } - } - } + [class]{}-[func]{backtrack} ``` === "TS" ```typescript title="preorder_traversal_iii_template.ts" - /* 判断当前状态是否为解 */ - function isSolution(state: TreeNode[]): boolean { - return state && state[state.length - 1]?.val === 7; - } + [class]{}-[func]{isSolution} - /* 记录解 */ - function recordSolution(state: TreeNode[], res: TreeNode[][]): void { - res.push([...state]); - } + [class]{}-[func]{recordSolution} - /* 判断在当前状态下,该选择是否合法 */ - function isValid(state: TreeNode[], choice: TreeNode): boolean { - return choice !== null && choice.val !== 3; - } + [class]{}-[func]{isValid} - /* 更新状态 */ - function makeChoice(state: TreeNode[], choice: TreeNode): void { - state.push(choice); - } + [class]{}-[func]{makeChoice} - /* 恢复状态 */ - function undoChoice(state: TreeNode[]): void { - state.pop(); - } + [class]{}-[func]{undoChoice} - /* 回溯算法:例题三 */ - function backtrack( - state: TreeNode[], - choices: TreeNode[], - res: TreeNode[][] - ): void { - // 检查是否为解 - if (isSolution(state)) { - // 记录解 - recordSolution(state, res); - } - // 遍历所有选择 - for (const choice of choices) { - // 剪枝:检查选择是否合法 - if (isValid(state, choice)) { - // 尝试:做出选择,更新状态 - makeChoice(state, choice); - // 进行下一轮选择 - backtrack(state, [choice.left, choice.right], res); - // 回退:撤销选择,恢复到之前的状态 - undoChoice(state); - } - } - } + [class]{}-[func]{backtrack} ``` === "Dart" ```dart title="preorder_traversal_iii_template.dart" - /* 判断当前状态是否为解 */ - bool isSolution(List state) { - return state.isNotEmpty && state.last.val == 7; - } + [class]{}-[func]{isSolution} - /* 记录解 */ - void recordSolution(List state, List> res) { - res.add(List.from(state)); - } + [class]{}-[func]{recordSolution} - /* 判断在当前状态下,该选择是否合法 */ - bool isValid(List state, TreeNode? choice) { - return choice != null && choice.val != 3; - } + [class]{}-[func]{isValid} - /* 更新状态 */ - void makeChoice(List state, TreeNode? choice) { - state.add(choice!); - } + [class]{}-[func]{makeChoice} - /* 恢复状态 */ - void undoChoice(List state, TreeNode? choice) { - state.removeLast(); - } + [class]{}-[func]{undoChoice} - /* 回溯算法:例题三 */ - void backtrack( - List state, - List choices, - List> res, - ) { - // 检查是否为解 - if (isSolution(state)) { - // 记录解 - recordSolution(state, res); - } - // 遍历所有选择 - for (TreeNode? choice in choices) { - // 剪枝:检查选择是否合法 - if (isValid(state, choice)) { - // 尝试:做出选择,更新状态 - makeChoice(state, choice); - // 进行下一轮选择 - backtrack(state, [choice!.left, choice.right], res); - // 回退:撤销选择,恢复到之前的状态 - undoChoice(state, choice); - } - } - } + [class]{}-[func]{backtrack} ``` === "Rust" ```rust title="preorder_traversal_iii_template.rs" - /* 判断当前状态是否为解 */ - fn is_solution(state: &mut Vec>>) -> bool { - return !state.is_empty() && state.get(state.len() - 1).unwrap().borrow().val == 7; - } + [class]{}-[func]{is_solution} - /* 记录解 */ - fn record_solution( - state: &mut Vec>>, - res: &mut Vec>>>, - ) { - res.push(state.clone()); - } + [class]{}-[func]{record_solution} - /* 判断在当前状态下,该选择是否合法 */ - fn is_valid(_: &mut Vec>>, choice: Rc>) -> bool { - return choice.borrow().val != 3; - } + [class]{}-[func]{is_valid} - /* 更新状态 */ - fn make_choice(state: &mut Vec>>, choice: Rc>) { - state.push(choice); - } + [class]{}-[func]{make_choice} - /* 恢复状态 */ - fn undo_choice(state: &mut Vec>>, _: Rc>) { - state.remove(state.len() - 1); - } + [class]{}-[func]{undo_choice} - /* 回溯算法:例题三 */ - fn backtrack( - state: &mut Vec>>, - choices: &mut Vec>>, - res: &mut Vec>>>, - ) { - // 检查是否为解 - if is_solution(state) { - // 记录解 - record_solution(state, res); - } - // 遍历所有选择 - for choice in choices { - // 剪枝:检查选择是否合法 - if is_valid(state, choice.clone()) { - // 尝试:做出选择,更新状态 - make_choice(state, choice.clone()); - // 进行下一轮选择 - backtrack( - state, - &mut vec![ - choice.borrow().left.clone().unwrap(), - choice.borrow().right.clone().unwrap(), - ], - res, - ); - // 回退:撤销选择,恢复到之前的状态 - undo_choice(state, choice.clone()); - } - } - } + [class]{}-[func]{backtrack} ``` === "C" ```c title="preorder_traversal_iii_template.c" - /* 判断当前状态是否为解 */ - bool isSolution(void) { - return pathSize > 0 && path[pathSize - 1]->val == 7; - } + [class]{}-[func]{isSolution} - /* 记录解 */ - void recordSolution(void) { - for (int i = 0; i < pathSize; i++) { - res[resSize][i] = path[i]; - } - resSize++; - } + [class]{}-[func]{recordSolution} - /* 判断在当前状态下,该选择是否合法 */ - bool isValid(TreeNode *choice) { - return choice != NULL && choice->val != 3; - } + [class]{}-[func]{isValid} - /* 更新状态 */ - void makeChoice(TreeNode *choice) { - path[pathSize++] = choice; - } + [class]{}-[func]{makeChoice} - /* 恢复状态 */ - void undoChoice(void) { - pathSize--; - } + [class]{}-[func]{undoChoice} - /* 回溯算法:例题三 */ - void backtrack(TreeNode *choices[2]) { - // 检查是否为解 - if (isSolution()) { - // 记录解 - recordSolution(); - } - // 遍历所有选择 - for (int i = 0; i < 2; i++) { - TreeNode *choice = choices[i]; - // 剪枝:检查选择是否合法 - if (isValid(choice)) { - // 尝试:做出选择,更新状态 - makeChoice(choice); - // 进行下一轮选择 - TreeNode *nextChoices[2] = {choice->left, choice->right}; - backtrack(nextChoices); - // 回退:撤销选择,恢复到之前的状态 - undoChoice(); - } - } - } + [class]{}-[func]{backtrack} ``` === "Kotlin" ```kotlin title="preorder_traversal_iii_template.kt" - /* 判断当前状态是否为解 */ - fun isSolution(state: MutableList): Boolean { - return state.isNotEmpty() && state[state.size - 1]?._val == 7 - } + [class]{}-[func]{isSolution} - /* 记录解 */ - fun recordSolution(state: MutableList?, res: MutableList?>) { - res.add(state!!.toMutableList()) - } + [class]{}-[func]{recordSolution} - /* 判断在当前状态下,该选择是否合法 */ - fun isValid(state: MutableList?, choice: TreeNode?): Boolean { - return choice != null && choice._val != 3 - } + [class]{}-[func]{isValid} - /* 更新状态 */ - fun makeChoice(state: MutableList, choice: TreeNode?) { - state.add(choice) - } + [class]{}-[func]{makeChoice} - /* 恢复状态 */ - fun undoChoice(state: MutableList, choice: TreeNode?) { - state.removeLast() - } + [class]{}-[func]{undoChoice} - /* 回溯算法:例题三 */ - fun backtrack( - state: MutableList, - choices: MutableList, - res: MutableList?> - ) { - // 检查是否为解 - if (isSolution(state)) { - // 记录解 - recordSolution(state, res) - } - // 遍历所有选择 - for (choice in choices) { - // 剪枝:检查选择是否合法 - if (isValid(state, choice)) { - // 尝试:做出选择,更新状态 - makeChoice(state, choice) - // 进行下一轮选择 - backtrack(state, mutableListOf(choice!!.left, choice.right), res) - // 回退:撤销选择,恢复到之前的状态 - undoChoice(state, choice) - } - } - } + [class]{}-[func]{backtrack} ``` === "Ruby" @@ -1872,11 +1027,6 @@ Next, we solve Example Three based on the framework code. The `state` is the nod [class]{}-[func]{backtrack} ``` -??? pythontutor "Code Visualization" - -
- - As per the requirements, after finding a node with a value of $7$, the search should continue, **thus the `return` statement after recording the solution should be removed**. The following diagram compares the search processes with and without retaining the `return` statement. ![Comparison of retaining and removing the return in the search process](backtracking_algorithm.assets/backtrack_remove_return_or_not.png){ class="animation-figure" } diff --git a/en/docs/chapter_backtracking/n_queens_problem.md b/en/docs/chapter_backtracking/n_queens_problem.md index 2659c1bb2..0e68e9120 100644 --- a/en/docs/chapter_backtracking/n_queens_problem.md +++ b/en/docs/chapter_backtracking/n_queens_problem.md @@ -64,34 +64,34 @@ Please note, in an $n$-dimensional matrix, the range of $row - col$ is $[-n + 1, diags1: list[bool], diags2: list[bool], ): - """回溯算法:n 皇后""" - # 当放置完所有行时,记录解 + """Backtracking algorithm: n queens""" + # When all rows are placed, record the solution if row == n: res.append([list(row) for row in state]) return - # 遍历所有列 + # Traverse all columns for col in range(n): - # 计算该格子对应的主对角线和次对角线 + # Calculate the main and minor diagonals corresponding to the cell diag1 = row - col + n - 1 diag2 = row + col - # 剪枝:不允许该格子所在列、主对角线、次对角线上存在皇后 + # Pruning: do not allow queens on the column, main diagonal, or minor diagonal of the cell if not cols[col] and not diags1[diag1] and not diags2[diag2]: - # 尝试:将皇后放置在该格子 + # Attempt: place the queen in the cell state[row][col] = "Q" cols[col] = diags1[diag1] = diags2[diag2] = True - # 放置下一行 + # Place the next row backtrack(row + 1, n, state, res, cols, diags1, diags2) - # 回退:将该格子恢复为空位 + # Retract: restore the cell to an empty spot state[row][col] = "#" cols[col] = diags1[diag1] = diags2[diag2] = False def n_queens(n: int) -> list[list[list[str]]]: - """求解 n 皇后""" - # 初始化 n*n 大小的棋盘,其中 'Q' 代表皇后,'#' 代表空位 + """Solve n queens""" + # Initialize an n*n size chessboard, where 'Q' represents the queen and '#' represents an empty spot state = [["#" for _ in range(n)] for _ in range(n)] - cols = [False] * n # 记录列是否有皇后 - diags1 = [False] * (2 * n - 1) # 记录主对角线上是否有皇后 - diags2 = [False] * (2 * n - 1) # 记录次对角线上是否有皇后 + cols = [False] * n # Record columns with queens + diags1 = [False] * (2 * n - 1) # Record main diagonals with queens + diags2 = [False] * (2 * n - 1) # Record minor diagonals with queens res = [] backtrack(0, n, state, res, cols, diags1, diags2) @@ -101,55 +101,18 @@ Please note, in an $n$-dimensional matrix, the range of $row - col$ is $[-n + 1, === "C++" ```cpp title="n_queens.cpp" - /* 回溯算法:n 皇后 */ - void backtrack(int row, int n, vector> &state, vector>> &res, vector &cols, - vector &diags1, vector &diags2) { - // 当放置完所有行时,记录解 - if (row == n) { - res.push_back(state); - return; - } - // 遍历所有列 - for (int col = 0; col < n; col++) { - // 计算该格子对应的主对角线和次对角线 - int diag1 = row - col + n - 1; - int diag2 = row + col; - // 剪枝:不允许该格子所在列、主对角线、次对角线上存在皇后 - if (!cols[col] && !diags1[diag1] && !diags2[diag2]) { - // 尝试:将皇后放置在该格子 - state[row][col] = "Q"; - cols[col] = diags1[diag1] = diags2[diag2] = true; - // 放置下一行 - backtrack(row + 1, n, state, res, cols, diags1, diags2); - // 回退:将该格子恢复为空位 - state[row][col] = "#"; - cols[col] = diags1[diag1] = diags2[diag2] = false; - } - } - } + [class]{}-[func]{backtrack} - /* 求解 n 皇后 */ - vector>> nQueens(int n) { - // 初始化 n*n 大小的棋盘,其中 'Q' 代表皇后,'#' 代表空位 - vector> state(n, vector(n, "#")); - vector cols(n, false); // 记录列是否有皇后 - vector diags1(2 * n - 1, false); // 记录主对角线上是否有皇后 - vector diags2(2 * n - 1, false); // 记录次对角线上是否有皇后 - vector>> res; - - backtrack(0, n, state, res, cols, diags1, diags2); - - return res; - } + [class]{}-[func]{nQueens} ``` === "Java" ```java title="n_queens.java" - /* 回溯算法:n 皇后 */ + /* Backtracking algorithm: n queens */ void backtrack(int row, int n, List> state, List>> res, boolean[] cols, boolean[] diags1, boolean[] diags2) { - // 当放置完所有行时,记录解 + // When all rows are placed, record the solution if (row == n) { List> copyState = new ArrayList<>(); for (List sRow : state) { @@ -158,28 +121,28 @@ Please note, in an $n$-dimensional matrix, the range of $row - col$ is $[-n + 1, res.add(copyState); return; } - // 遍历所有列 + // Traverse all columns for (int col = 0; col < n; col++) { - // 计算该格子对应的主对角线和次对角线 + // Calculate the main and minor diagonals corresponding to the cell int diag1 = row - col + n - 1; int diag2 = row + col; - // 剪枝:不允许该格子所在列、主对角线、次对角线上存在皇后 + // Pruning: do not allow queens on the column, main diagonal, or minor diagonal of the cell if (!cols[col] && !diags1[diag1] && !diags2[diag2]) { - // 尝试:将皇后放置在该格子 + // Attempt: place the queen in the cell state.get(row).set(col, "Q"); cols[col] = diags1[diag1] = diags2[diag2] = true; - // 放置下一行 + // Place the next row backtrack(row + 1, n, state, res, cols, diags1, diags2); - // 回退:将该格子恢复为空位 + // Retract: restore the cell to an empty spot state.get(row).set(col, "#"); cols[col] = diags1[diag1] = diags2[diag2] = false; } } } - /* 求解 n 皇后 */ + /* Solve n queens */ List>> nQueens(int n) { - // 初始化 n*n 大小的棋盘,其中 'Q' 代表皇后,'#' 代表空位 + // Initialize an n*n size chessboard, where 'Q' represents the queen and '#' represents an empty spot List> state = new ArrayList<>(); for (int i = 0; i < n; i++) { List row = new ArrayList<>(); @@ -188,9 +151,9 @@ Please note, in an $n$-dimensional matrix, the range of $row - col$ is $[-n + 1, } state.add(row); } - boolean[] cols = new boolean[n]; // 记录列是否有皇后 - boolean[] diags1 = new boolean[2 * n - 1]; // 记录主对角线上是否有皇后 - boolean[] diags2 = new boolean[2 * n - 1]; // 记录次对角线上是否有皇后 + boolean[] cols = new boolean[n]; // Record columns with queens + boolean[] diags1 = new boolean[2 * n - 1]; // Record main diagonals with queens + boolean[] diags2 = new boolean[2 * n - 1]; // Record minor diagonals with queens List>> res = new ArrayList<>(); backtrack(0, n, state, res, cols, diags1, diags2); @@ -202,509 +165,73 @@ Please note, in an $n$-dimensional matrix, the range of $row - col$ is $[-n + 1, === "C#" ```csharp title="n_queens.cs" - /* 回溯算法:n 皇后 */ - void Backtrack(int row, int n, List> state, List>> res, - bool[] cols, bool[] diags1, bool[] diags2) { - // 当放置完所有行时,记录解 - if (row == n) { - List> copyState = []; - foreach (List sRow in state) { - copyState.Add(new List(sRow)); - } - res.Add(copyState); - return; - } - // 遍历所有列 - for (int col = 0; col < n; col++) { - // 计算该格子对应的主对角线和次对角线 - int diag1 = row - col + n - 1; - int diag2 = row + col; - // 剪枝:不允许该格子所在列、主对角线、次对角线上存在皇后 - if (!cols[col] && !diags1[diag1] && !diags2[diag2]) { - // 尝试:将皇后放置在该格子 - state[row][col] = "Q"; - cols[col] = diags1[diag1] = diags2[diag2] = true; - // 放置下一行 - Backtrack(row + 1, n, state, res, cols, diags1, diags2); - // 回退:将该格子恢复为空位 - state[row][col] = "#"; - cols[col] = diags1[diag1] = diags2[diag2] = false; - } - } - } + [class]{n_queens}-[func]{Backtrack} - /* 求解 n 皇后 */ - List>> NQueens(int n) { - // 初始化 n*n 大小的棋盘,其中 'Q' 代表皇后,'#' 代表空位 - List> state = []; - for (int i = 0; i < n; i++) { - List row = []; - for (int j = 0; j < n; j++) { - row.Add("#"); - } - state.Add(row); - } - bool[] cols = new bool[n]; // 记录列是否有皇后 - bool[] diags1 = new bool[2 * n - 1]; // 记录主对角线上是否有皇后 - bool[] diags2 = new bool[2 * n - 1]; // 记录次对角线上是否有皇后 - List>> res = []; - - Backtrack(0, n, state, res, cols, diags1, diags2); - - return res; - } + [class]{n_queens}-[func]{NQueens} ``` === "Go" ```go title="n_queens.go" - /* 回溯算法:n 皇后 */ - func backtrack(row, n int, state *[][]string, res *[][][]string, cols, diags1, diags2 *[]bool) { - // 当放置完所有行时,记录解 - if row == n { - newState := make([][]string, len(*state)) - for i, _ := range newState { - newState[i] = make([]string, len((*state)[0])) - copy(newState[i], (*state)[i]) + [class]{}-[func]{backtrack} - } - *res = append(*res, newState) - return - } - // 遍历所有列 - for col := 0; col < n; col++ { - // 计算该格子对应的主对角线和次对角线 - diag1 := row - col + n - 1 - diag2 := row + col - // 剪枝:不允许该格子所在列、主对角线、次对角线上存在皇后 - if !(*cols)[col] && !(*diags1)[diag1] && !(*diags2)[diag2] { - // 尝试:将皇后放置在该格子 - (*state)[row][col] = "Q" - (*cols)[col], (*diags1)[diag1], (*diags2)[diag2] = true, true, true - // 放置下一行 - backtrack(row+1, n, state, res, cols, diags1, diags2) - // 回退:将该格子恢复为空位 - (*state)[row][col] = "#" - (*cols)[col], (*diags1)[diag1], (*diags2)[diag2] = false, false, false - } - } - } - - /* 求解 n 皇后 */ - func nQueens(n int) [][][]string { - // 初始化 n*n 大小的棋盘,其中 'Q' 代表皇后,'#' 代表空位 - state := make([][]string, n) - for i := 0; i < n; i++ { - row := make([]string, n) - for i := 0; i < n; i++ { - row[i] = "#" - } - state[i] = row - } - // 记录列是否有皇后 - cols := make([]bool, n) - diags1 := make([]bool, 2*n-1) - diags2 := make([]bool, 2*n-1) - res := make([][][]string, 0) - backtrack(0, n, &state, &res, &cols, &diags1, &diags2) - return res - } + [class]{}-[func]{nQueens} ``` === "Swift" ```swift title="n_queens.swift" - /* 回溯算法:n 皇后 */ - func backtrack(row: Int, n: Int, state: inout [[String]], res: inout [[[String]]], cols: inout [Bool], diags1: inout [Bool], diags2: inout [Bool]) { - // 当放置完所有行时,记录解 - if row == n { - res.append(state) - return - } - // 遍历所有列 - for col in 0 ..< n { - // 计算该格子对应的主对角线和次对角线 - let diag1 = row - col + n - 1 - let diag2 = row + col - // 剪枝:不允许该格子所在列、主对角线、次对角线上存在皇后 - if !cols[col] && !diags1[diag1] && !diags2[diag2] { - // 尝试:将皇后放置在该格子 - state[row][col] = "Q" - cols[col] = true - diags1[diag1] = true - diags2[diag2] = true - // 放置下一行 - backtrack(row: row + 1, n: n, state: &state, res: &res, cols: &cols, diags1: &diags1, diags2: &diags2) - // 回退:将该格子恢复为空位 - state[row][col] = "#" - cols[col] = false - diags1[diag1] = false - diags2[diag2] = false - } - } - } + [class]{}-[func]{backtrack} - /* 求解 n 皇后 */ - func nQueens(n: Int) -> [[[String]]] { - // 初始化 n*n 大小的棋盘,其中 'Q' 代表皇后,'#' 代表空位 - var state = Array(repeating: Array(repeating: "#", count: n), count: n) - var cols = Array(repeating: false, count: n) // 记录列是否有皇后 - var diags1 = Array(repeating: false, count: 2 * n - 1) // 记录主对角线上是否有皇后 - var diags2 = Array(repeating: false, count: 2 * n - 1) // 记录次对角线上是否有皇后 - var res: [[[String]]] = [] - - backtrack(row: 0, n: n, state: &state, res: &res, cols: &cols, diags1: &diags1, diags2: &diags2) - - return res - } + [class]{}-[func]{nQueens} ``` === "JS" ```javascript title="n_queens.js" - /* 回溯算法:n 皇后 */ - function backtrack(row, n, state, res, cols, diags1, diags2) { - // 当放置完所有行时,记录解 - if (row === n) { - res.push(state.map((row) => row.slice())); - return; - } - // 遍历所有列 - for (let col = 0; col < n; col++) { - // 计算该格子对应的主对角线和次对角线 - const diag1 = row - col + n - 1; - const diag2 = row + col; - // 剪枝:不允许该格子所在列、主对角线、次对角线上存在皇后 - if (!cols[col] && !diags1[diag1] && !diags2[diag2]) { - // 尝试:将皇后放置在该格子 - state[row][col] = 'Q'; - cols[col] = diags1[diag1] = diags2[diag2] = true; - // 放置下一行 - backtrack(row + 1, n, state, res, cols, diags1, diags2); - // 回退:将该格子恢复为空位 - state[row][col] = '#'; - cols[col] = diags1[diag1] = diags2[diag2] = false; - } - } - } + [class]{}-[func]{backtrack} - /* 求解 n 皇后 */ - function nQueens(n) { - // 初始化 n*n 大小的棋盘,其中 'Q' 代表皇后,'#' 代表空位 - const state = Array.from({ length: n }, () => Array(n).fill('#')); - const cols = Array(n).fill(false); // 记录列是否有皇后 - const diags1 = Array(2 * n - 1).fill(false); // 记录主对角线上是否有皇后 - const diags2 = Array(2 * n - 1).fill(false); // 记录次对角线上是否有皇后 - const res = []; - - backtrack(0, n, state, res, cols, diags1, diags2); - return res; - } + [class]{}-[func]{nQueens} ``` === "TS" ```typescript title="n_queens.ts" - /* 回溯算法:n 皇后 */ - function backtrack( - row: number, - n: number, - state: string[][], - res: string[][][], - cols: boolean[], - diags1: boolean[], - diags2: boolean[] - ): void { - // 当放置完所有行时,记录解 - if (row === n) { - res.push(state.map((row) => row.slice())); - return; - } - // 遍历所有列 - for (let col = 0; col < n; col++) { - // 计算该格子对应的主对角线和次对角线 - const diag1 = row - col + n - 1; - const diag2 = row + col; - // 剪枝:不允许该格子所在列、主对角线、次对角线上存在皇后 - if (!cols[col] && !diags1[diag1] && !diags2[diag2]) { - // 尝试:将皇后放置在该格子 - state[row][col] = 'Q'; - cols[col] = diags1[diag1] = diags2[diag2] = true; - // 放置下一行 - backtrack(row + 1, n, state, res, cols, diags1, diags2); - // 回退:将该格子恢复为空位 - state[row][col] = '#'; - cols[col] = diags1[diag1] = diags2[diag2] = false; - } - } - } + [class]{}-[func]{backtrack} - /* 求解 n 皇后 */ - function nQueens(n: number): string[][][] { - // 初始化 n*n 大小的棋盘,其中 'Q' 代表皇后,'#' 代表空位 - const state = Array.from({ length: n }, () => Array(n).fill('#')); - const cols = Array(n).fill(false); // 记录列是否有皇后 - const diags1 = Array(2 * n - 1).fill(false); // 记录主对角线上是否有皇后 - const diags2 = Array(2 * n - 1).fill(false); // 记录次对角线上是否有皇后 - const res: string[][][] = []; - - backtrack(0, n, state, res, cols, diags1, diags2); - return res; - } + [class]{}-[func]{nQueens} ``` === "Dart" ```dart title="n_queens.dart" - /* 回溯算法:n 皇后 */ - void backtrack( - int row, - int n, - List> state, - List>> res, - List cols, - List diags1, - List diags2, - ) { - // 当放置完所有行时,记录解 - if (row == n) { - List> copyState = []; - for (List sRow in state) { - copyState.add(List.from(sRow)); - } - res.add(copyState); - return; - } - // 遍历所有列 - for (int col = 0; col < n; col++) { - // 计算该格子对应的主对角线和次对角线 - int diag1 = row - col + n - 1; - int diag2 = row + col; - // 剪枝:不允许该格子所在列、主对角线、次对角线上存在皇后 - if (!cols[col] && !diags1[diag1] && !diags2[diag2]) { - // 尝试:将皇后放置在该格子 - state[row][col] = "Q"; - cols[col] = true; - diags1[diag1] = true; - diags2[diag2] = true; - // 放置下一行 - backtrack(row + 1, n, state, res, cols, diags1, diags2); - // 回退:将该格子恢复为空位 - state[row][col] = "#"; - cols[col] = false; - diags1[diag1] = false; - diags2[diag2] = false; - } - } - } + [class]{}-[func]{backtrack} - /* 求解 n 皇后 */ - List>> nQueens(int n) { - // 初始化 n*n 大小的棋盘,其中 'Q' 代表皇后,'#' 代表空位 - List> state = List.generate(n, (index) => List.filled(n, "#")); - List cols = List.filled(n, false); // 记录列是否有皇后 - List diags1 = List.filled(2 * n - 1, false); // 记录主对角线上是否有皇后 - List diags2 = List.filled(2 * n - 1, false); // 记录次对角线上是否有皇后 - List>> res = []; - - backtrack(0, n, state, res, cols, diags1, diags2); - - return res; - } + [class]{}-[func]{nQueens} ``` === "Rust" ```rust title="n_queens.rs" - /* 回溯算法:n 皇后 */ - fn backtrack( - row: usize, - n: usize, - state: &mut Vec>, - res: &mut Vec>>, - cols: &mut [bool], - diags1: &mut [bool], - diags2: &mut [bool], - ) { - // 当放置完所有行时,记录解 - if row == n { - let mut copy_state: Vec> = Vec::new(); - for s_row in state.clone() { - copy_state.push(s_row); - } - res.push(copy_state); - return; - } - // 遍历所有列 - for col in 0..n { - // 计算该格子对应的主对角线和次对角线 - let diag1 = row + n - 1 - col; - let diag2 = row + col; - // 剪枝:不允许该格子所在列、主对角线、次对角线上存在皇后 - if !cols[col] && !diags1[diag1] && !diags2[diag2] { - // 尝试:将皇后放置在该格子 - state.get_mut(row).unwrap()[col] = "Q".into(); - (cols[col], diags1[diag1], diags2[diag2]) = (true, true, true); - // 放置下一行 - backtrack(row + 1, n, state, res, cols, diags1, diags2); - // 回退:将该格子恢复为空位 - state.get_mut(row).unwrap()[col] = "#".into(); - (cols[col], diags1[diag1], diags2[diag2]) = (false, false, false); - } - } - } + [class]{}-[func]{backtrack} - /* 求解 n 皇后 */ - fn n_queens(n: usize) -> Vec>> { - // 初始化 n*n 大小的棋盘,其中 'Q' 代表皇后,'#' 代表空位 - let mut state: Vec> = Vec::new(); - for _ in 0..n { - let mut row: Vec = Vec::new(); - for _ in 0..n { - row.push("#".into()); - } - state.push(row); - } - let mut cols = vec![false; n]; // 记录列是否有皇后 - let mut diags1 = vec![false; 2 * n - 1]; // 记录主对角线上是否有皇后 - let mut diags2 = vec![false; 2 * n - 1]; // 记录次对角线上是否有皇后 - let mut res: Vec>> = Vec::new(); - - backtrack( - 0, - n, - &mut state, - &mut res, - &mut cols, - &mut diags1, - &mut diags2, - ); - - res - } + [class]{}-[func]{n_queens} ``` === "C" ```c title="n_queens.c" - /* 回溯算法:n 皇后 */ - void backtrack(int row, int n, char state[MAX_SIZE][MAX_SIZE], char ***res, int *resSize, bool cols[MAX_SIZE], - bool diags1[2 * MAX_SIZE - 1], bool diags2[2 * MAX_SIZE - 1]) { - // 当放置完所有行时,记录解 - if (row == n) { - res[*resSize] = (char **)malloc(sizeof(char *) * n); - for (int i = 0; i < n; ++i) { - res[*resSize][i] = (char *)malloc(sizeof(char) * (n + 1)); - strcpy(res[*resSize][i], state[i]); - } - (*resSize)++; - return; - } - // 遍历所有列 - for (int col = 0; col < n; col++) { - // 计算该格子对应的主对角线和次对角线 - int diag1 = row - col + n - 1; - int diag2 = row + col; - // 剪枝:不允许该格子所在列、主对角线、次对角线上存在皇后 - if (!cols[col] && !diags1[diag1] && !diags2[diag2]) { - // 尝试:将皇后放置在该格子 - state[row][col] = 'Q'; - cols[col] = diags1[diag1] = diags2[diag2] = true; - // 放置下一行 - backtrack(row + 1, n, state, res, resSize, cols, diags1, diags2); - // 回退:将该格子恢复为空位 - state[row][col] = '#'; - cols[col] = diags1[diag1] = diags2[diag2] = false; - } - } - } + [class]{}-[func]{backtrack} - /* 求解 n 皇后 */ - char ***nQueens(int n, int *returnSize) { - char state[MAX_SIZE][MAX_SIZE]; - // 初始化 n*n 大小的棋盘,其中 'Q' 代表皇后,'#' 代表空位 - for (int i = 0; i < n; ++i) { - for (int j = 0; j < n; ++j) { - state[i][j] = '#'; - } - state[i][n] = '\0'; - } - bool cols[MAX_SIZE] = {false}; // 记录列是否有皇后 - bool diags1[2 * MAX_SIZE - 1] = {false}; // 记录主对角线上是否有皇后 - bool diags2[2 * MAX_SIZE - 1] = {false}; // 记录次对角线上是否有皇后 - - char ***res = (char ***)malloc(sizeof(char **) * MAX_SIZE); - *returnSize = 0; - backtrack(0, n, state, res, returnSize, cols, diags1, diags2); - return res; - } + [class]{}-[func]{nQueens} ``` === "Kotlin" ```kotlin title="n_queens.kt" - /* 回溯算法:n 皇后 */ - fun backtrack( - row: Int, - n: Int, - state: MutableList>, - res: MutableList>?>, - cols: BooleanArray, - diags1: BooleanArray, - diags2: BooleanArray - ) { - // 当放置完所有行时,记录解 - if (row == n) { - val copyState = mutableListOf>() - for (sRow in state) { - copyState.add(sRow.toMutableList()) - } - res.add(copyState) - return - } - // 遍历所有列 - for (col in 0..>?> { - // 初始化 n*n 大小的棋盘,其中 'Q' 代表皇后,'#' 代表空位 - val state = mutableListOf>() - for (i in 0..() - for (j in 0..>?>() - - backtrack(0, n, state, res, cols, diags1, diags2) - - return res - } + [class]{}-[func]{nQueens} ``` === "Ruby" @@ -723,11 +250,6 @@ Please note, in an $n$-dimensional matrix, the range of $row - col$ is $[-n + 1, [class]{}-[func]{nQueens} ``` -??? pythontutor "Code Visualization" - -
- - Placing $n$ queens row-by-row, considering column constraints, from the first row to the last row there are $n$, $n-1$, $\dots$, $2$, $1$ choices, using $O(n!)$ time. When recording a solution, it is necessary to copy the matrix `state` and add it to `res`, with the copying operation using $O(n^2)$ time. Therefore, **the overall time complexity is $O(n! \cdot n^2)$**. In practice, pruning based on diagonal constraints can significantly reduce the search space, thus often the search efficiency is better than the above time complexity. Array `state` uses $O(n^2)$ space, and arrays `cols`, `diags1`, and `diags2` each use $O(n)$ space. The maximum recursion depth is $n$, using $O(n)$ stack space. Therefore, **the space complexity is $O(n^2)$**. diff --git a/en/docs/chapter_backtracking/permutations_problem.md b/en/docs/chapter_backtracking/permutations_problem.md index 82e592e10..bc7d10265 100644 --- a/en/docs/chapter_backtracking/permutations_problem.md +++ b/en/docs/chapter_backtracking/permutations_problem.md @@ -61,26 +61,26 @@ After understanding the above information, we can "fill in the blanks" in the fr def backtrack( state: list[int], choices: list[int], selected: list[bool], res: list[list[int]] ): - """回溯算法:全排列 I""" - # 当状态长度等于元素数量时,记录解 + """Backtracking algorithm: Permutation I""" + # When the state length equals the number of elements, record the solution if len(state) == len(choices): res.append(list(state)) return - # 遍历所有选择 + # Traverse all choices for i, choice in enumerate(choices): - # 剪枝:不允许重复选择元素 + # Pruning: do not allow repeated selection of elements if not selected[i]: - # 尝试:做出选择,更新状态 + # Attempt: make a choice, update the state selected[i] = True state.append(choice) - # 进行下一轮选择 + # Proceed to the next round of selection backtrack(state, choices, selected, res) - # 回退:撤销选择,恢复到之前的状态 + # Retract: undo the choice, restore to the previous state selected[i] = False state.pop() def permutations_i(nums: list[int]) -> list[list[int]]: - """全排列 I""" + """Permutation I""" res = [] backtrack(state=[], choices=nums, selected=[False] * len(nums), res=res) return res @@ -89,68 +89,39 @@ After understanding the above information, we can "fill in the blanks" in the fr === "C++" ```cpp title="permutations_i.cpp" - /* 回溯算法:全排列 I */ - void backtrack(vector &state, const vector &choices, vector &selected, vector> &res) { - // 当状态长度等于元素数量时,记录解 - if (state.size() == choices.size()) { - res.push_back(state); - return; - } - // 遍历所有选择 - for (int i = 0; i < choices.size(); i++) { - int choice = choices[i]; - // 剪枝:不允许重复选择元素 - if (!selected[i]) { - // 尝试:做出选择,更新状态 - selected[i] = true; - state.push_back(choice); - // 进行下一轮选择 - backtrack(state, choices, selected, res); - // 回退:撤销选择,恢复到之前的状态 - selected[i] = false; - state.pop_back(); - } - } - } + [class]{}-[func]{backtrack} - /* 全排列 I */ - vector> permutationsI(vector nums) { - vector state; - vector selected(nums.size(), false); - vector> res; - backtrack(state, nums, selected, res); - return res; - } + [class]{}-[func]{permutationsI} ``` === "Java" ```java title="permutations_i.java" - /* 回溯算法:全排列 I */ + /* Backtracking algorithm: Permutation I */ void backtrack(List state, int[] choices, boolean[] selected, List> res) { - // 当状态长度等于元素数量时,记录解 + // When the state length equals the number of elements, record the solution if (state.size() == choices.length) { res.add(new ArrayList(state)); return; } - // 遍历所有选择 + // Traverse all choices for (int i = 0; i < choices.length; i++) { int choice = choices[i]; - // 剪枝:不允许重复选择元素 + // Pruning: do not allow repeated selection of elements if (!selected[i]) { - // 尝试:做出选择,更新状态 + // Attempt: make a choice, update the state selected[i] = true; state.add(choice); - // 进行下一轮选择 + // Proceed to the next round of selection backtrack(state, choices, selected, res); - // 回退:撤销选择,恢复到之前的状态 + // Retract: undo the choice, restore to the previous state selected[i] = false; state.remove(state.size() - 1); } } } - /* 全排列 I */ + /* Permutation I */ List> permutationsI(int[] nums) { List> res = new ArrayList>(); backtrack(new ArrayList(), nums, new boolean[nums.length], res); @@ -161,346 +132,73 @@ After understanding the above information, we can "fill in the blanks" in the fr === "C#" ```csharp title="permutations_i.cs" - /* 回溯算法:全排列 I */ - void Backtrack(List state, int[] choices, bool[] selected, List> res) { - // 当状态长度等于元素数量时,记录解 - if (state.Count == choices.Length) { - res.Add(new List(state)); - return; - } - // 遍历所有选择 - for (int i = 0; i < choices.Length; i++) { - int choice = choices[i]; - // 剪枝:不允许重复选择元素 - if (!selected[i]) { - // 尝试:做出选择,更新状态 - selected[i] = true; - state.Add(choice); - // 进行下一轮选择 - Backtrack(state, choices, selected, res); - // 回退:撤销选择,恢复到之前的状态 - selected[i] = false; - state.RemoveAt(state.Count - 1); - } - } - } + [class]{permutations_i}-[func]{Backtrack} - /* 全排列 I */ - List> PermutationsI(int[] nums) { - List> res = []; - Backtrack([], nums, new bool[nums.Length], res); - return res; - } + [class]{permutations_i}-[func]{PermutationsI} ``` === "Go" ```go title="permutations_i.go" - /* 回溯算法:全排列 I */ - func backtrackI(state *[]int, choices *[]int, selected *[]bool, res *[][]int) { - // 当状态长度等于元素数量时,记录解 - if len(*state) == len(*choices) { - newState := append([]int{}, *state...) - *res = append(*res, newState) - } - // 遍历所有选择 - for i := 0; i < len(*choices); i++ { - choice := (*choices)[i] - // 剪枝:不允许重复选择元素 - if !(*selected)[i] { - // 尝试:做出选择,更新状态 - (*selected)[i] = true - *state = append(*state, choice) - // 进行下一轮选择 - backtrackI(state, choices, selected, res) - // 回退:撤销选择,恢复到之前的状态 - (*selected)[i] = false - *state = (*state)[:len(*state)-1] - } - } - } + [class]{}-[func]{backtrackI} - /* 全排列 I */ - func permutationsI(nums []int) [][]int { - res := make([][]int, 0) - state := make([]int, 0) - selected := make([]bool, len(nums)) - backtrackI(&state, &nums, &selected, &res) - return res - } + [class]{}-[func]{permutationsI} ``` === "Swift" ```swift title="permutations_i.swift" - /* 回溯算法:全排列 I */ - func backtrack(state: inout [Int], choices: [Int], selected: inout [Bool], res: inout [[Int]]) { - // 当状态长度等于元素数量时,记录解 - if state.count == choices.count { - res.append(state) - return - } - // 遍历所有选择 - for (i, choice) in choices.enumerated() { - // 剪枝:不允许重复选择元素 - if !selected[i] { - // 尝试:做出选择,更新状态 - selected[i] = true - state.append(choice) - // 进行下一轮选择 - backtrack(state: &state, choices: choices, selected: &selected, res: &res) - // 回退:撤销选择,恢复到之前的状态 - selected[i] = false - state.removeLast() - } - } - } + [class]{}-[func]{backtrack} - /* 全排列 I */ - func permutationsI(nums: [Int]) -> [[Int]] { - var state: [Int] = [] - var selected = Array(repeating: false, count: nums.count) - var res: [[Int]] = [] - backtrack(state: &state, choices: nums, selected: &selected, res: &res) - return res - } + [class]{}-[func]{permutationsI} ``` === "JS" ```javascript title="permutations_i.js" - /* 回溯算法:全排列 I */ - function backtrack(state, choices, selected, res) { - // 当状态长度等于元素数量时,记录解 - if (state.length === choices.length) { - res.push([...state]); - return; - } - // 遍历所有选择 - choices.forEach((choice, i) => { - // 剪枝:不允许重复选择元素 - if (!selected[i]) { - // 尝试:做出选择,更新状态 - selected[i] = true; - state.push(choice); - // 进行下一轮选择 - backtrack(state, choices, selected, res); - // 回退:撤销选择,恢复到之前的状态 - selected[i] = false; - state.pop(); - } - }); - } + [class]{}-[func]{backtrack} - /* 全排列 I */ - function permutationsI(nums) { - const res = []; - backtrack([], nums, Array(nums.length).fill(false), res); - return res; - } + [class]{}-[func]{permutationsI} ``` === "TS" ```typescript title="permutations_i.ts" - /* 回溯算法:全排列 I */ - function backtrack( - state: number[], - choices: number[], - selected: boolean[], - res: number[][] - ): void { - // 当状态长度等于元素数量时,记录解 - if (state.length === choices.length) { - res.push([...state]); - return; - } - // 遍历所有选择 - choices.forEach((choice, i) => { - // 剪枝:不允许重复选择元素 - if (!selected[i]) { - // 尝试:做出选择,更新状态 - selected[i] = true; - state.push(choice); - // 进行下一轮选择 - backtrack(state, choices, selected, res); - // 回退:撤销选择,恢复到之前的状态 - selected[i] = false; - state.pop(); - } - }); - } + [class]{}-[func]{backtrack} - /* 全排列 I */ - function permutationsI(nums: number[]): number[][] { - const res: number[][] = []; - backtrack([], nums, Array(nums.length).fill(false), res); - return res; - } + [class]{}-[func]{permutationsI} ``` === "Dart" ```dart title="permutations_i.dart" - /* 回溯算法:全排列 I */ - void backtrack( - List state, - List choices, - List selected, - List> res, - ) { - // 当状态长度等于元素数量时,记录解 - if (state.length == choices.length) { - res.add(List.from(state)); - return; - } - // 遍历所有选择 - for (int i = 0; i < choices.length; i++) { - int choice = choices[i]; - // 剪枝:不允许重复选择元素 - if (!selected[i]) { - // 尝试:做出选择,更新状态 - selected[i] = true; - state.add(choice); - // 进行下一轮选择 - backtrack(state, choices, selected, res); - // 回退:撤销选择,恢复到之前的状态 - selected[i] = false; - state.removeLast(); - } - } - } + [class]{}-[func]{backtrack} - /* 全排列 I */ - List> permutationsI(List nums) { - List> res = []; - backtrack([], nums, List.filled(nums.length, false), res); - return res; - } + [class]{}-[func]{permutationsI} ``` === "Rust" ```rust title="permutations_i.rs" - /* 回溯算法:全排列 I */ - fn backtrack(mut state: Vec, choices: &[i32], selected: &mut [bool], res: &mut Vec>) { - // 当状态长度等于元素数量时,记录解 - if state.len() == choices.len() { - res.push(state); - return; - } - // 遍历所有选择 - for i in 0..choices.len() { - let choice = choices[i]; - // 剪枝:不允许重复选择元素 - if !selected[i] { - // 尝试:做出选择,更新状态 - selected[i] = true; - state.push(choice); - // 进行下一轮选择 - backtrack(state.clone(), choices, selected, res); - // 回退:撤销选择,恢复到之前的状态 - selected[i] = false; - state.remove(state.len() - 1); - } - } - } + [class]{}-[func]{backtrack} - /* 全排列 I */ - fn permutations_i(nums: &mut [i32]) -> Vec> { - let mut res = Vec::new(); // 状态(子集) - backtrack(Vec::new(), nums, &mut vec![false; nums.len()], &mut res); - res - } + [class]{}-[func]{permutations_i} ``` === "C" ```c title="permutations_i.c" - /* 回溯算法:全排列 I */ - void backtrack(int *state, int stateSize, int *choices, int choicesSize, bool *selected, int **res, int *resSize) { - // 当状态长度等于元素数量时,记录解 - if (stateSize == choicesSize) { - res[*resSize] = (int *)malloc(choicesSize * sizeof(int)); - for (int i = 0; i < choicesSize; i++) { - res[*resSize][i] = state[i]; - } - (*resSize)++; - return; - } - // 遍历所有选择 - for (int i = 0; i < choicesSize; i++) { - int choice = choices[i]; - // 剪枝:不允许重复选择元素 - if (!selected[i]) { - // 尝试:做出选择,更新状态 - selected[i] = true; - state[stateSize] = choice; - // 进行下一轮选择 - backtrack(state, stateSize + 1, choices, choicesSize, selected, res, resSize); - // 回退:撤销选择,恢复到之前的状态 - selected[i] = false; - } - } - } + [class]{}-[func]{backtrack} - /* 全排列 I */ - int **permutationsI(int *nums, int numsSize, int *returnSize) { - int *state = (int *)malloc(numsSize * sizeof(int)); - bool *selected = (bool *)malloc(numsSize * sizeof(bool)); - for (int i = 0; i < numsSize; i++) { - selected[i] = false; - } - int **res = (int **)malloc(MAX_SIZE * sizeof(int *)); - *returnSize = 0; - - backtrack(state, 0, nums, numsSize, selected, res, returnSize); - - free(state); - free(selected); - - return res; - } + [class]{}-[func]{permutationsI} ``` === "Kotlin" ```kotlin title="permutations_i.kt" - /* 回溯算法:全排列 I */ - fun backtrack( - state: MutableList, - choices: IntArray, - selected: BooleanArray, - res: MutableList?> - ) { - // 当状态长度等于元素数量时,记录解 - if (state.size == choices.size) { - res.add(state.toMutableList()) - 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) - } - } - } + [class]{}-[func]{backtrack} - /* 全排列 I */ - fun permutationsI(nums: IntArray): MutableList?> { - val res = mutableListOf?>() - backtrack(mutableListOf(), nums, BooleanArray(nums.size), res) - return res - } + [class]{}-[func]{permutationsI} ``` === "Ruby" @@ -519,11 +217,6 @@ After understanding the above information, we can "fill in the blanks" in the fr [class]{}-[func]{permutationsI} ``` -??? pythontutor "Code Visualization" - -
- - ## 13.2.2   Considering cases with equal elements !!! question @@ -562,28 +255,28 @@ Based on the code from the previous problem, we consider initiating a hash set ` def backtrack( state: list[int], choices: list[int], selected: list[bool], res: list[list[int]] ): - """回溯算法:全排列 II""" - # 当状态长度等于元素数量时,记录解 + """Backtracking algorithm: Permutation II""" + # When the state length equals the number of elements, record the solution if len(state) == len(choices): res.append(list(state)) return - # 遍历所有选择 + # Traverse all choices duplicated = set[int]() for i, choice in enumerate(choices): - # 剪枝:不允许重复选择元素 且 不允许重复选择相等元素 + # Pruning: do not allow repeated selection of elements and do not allow repeated selection of equal elements if not selected[i] and choice not in duplicated: - # 尝试:做出选择,更新状态 - duplicated.add(choice) # 记录选择过的元素值 + # Attempt: make a choice, update the state + duplicated.add(choice) # Record selected element values selected[i] = True state.append(choice) - # 进行下一轮选择 + # Proceed to the next round of selection backtrack(state, choices, selected, res) - # 回退:撤销选择,恢复到之前的状态 + # Retract: undo the choice, restore to the previous state selected[i] = False state.pop() def permutations_ii(nums: list[int]) -> list[list[int]]: - """全排列 II""" + """Permutation II""" res = [] backtrack(state=[], choices=nums, selected=[False] * len(nums), res=res) return res @@ -592,72 +285,41 @@ Based on the code from the previous problem, we consider initiating a hash set ` === "C++" ```cpp title="permutations_ii.cpp" - /* 回溯算法:全排列 II */ - void backtrack(vector &state, const vector &choices, vector &selected, vector> &res) { - // 当状态长度等于元素数量时,记录解 - if (state.size() == choices.size()) { - res.push_back(state); - return; - } - // 遍历所有选择 - unordered_set duplicated; - for (int i = 0; i < choices.size(); i++) { - int choice = choices[i]; - // 剪枝:不允许重复选择元素 且 不允许重复选择相等元素 - if (!selected[i] && duplicated.find(choice) == duplicated.end()) { - // 尝试:做出选择,更新状态 - duplicated.emplace(choice); // 记录选择过的元素值 - selected[i] = true; - state.push_back(choice); - // 进行下一轮选择 - backtrack(state, choices, selected, res); - // 回退:撤销选择,恢复到之前的状态 - selected[i] = false; - state.pop_back(); - } - } - } + [class]{}-[func]{backtrack} - /* 全排列 II */ - vector> permutationsII(vector nums) { - vector state; - vector selected(nums.size(), false); - vector> res; - backtrack(state, nums, selected, res); - return res; - } + [class]{}-[func]{permutationsII} ``` === "Java" ```java title="permutations_ii.java" - /* 回溯算法:全排列 II */ + /* Backtracking algorithm: Permutation II */ void backtrack(List state, int[] choices, boolean[] selected, List> res) { - // 当状态长度等于元素数量时,记录解 + // When the state length equals the number of elements, record the solution if (state.size() == choices.length) { res.add(new ArrayList(state)); return; } - // 遍历所有选择 + // Traverse all choices Set duplicated = new HashSet(); for (int i = 0; i < choices.length; i++) { int choice = choices[i]; - // 剪枝:不允许重复选择元素 且 不允许重复选择相等元素 + // Pruning: do not allow repeated selection of elements and do not allow repeated selection of equal elements if (!selected[i] && !duplicated.contains(choice)) { - // 尝试:做出选择,更新状态 - duplicated.add(choice); // 记录选择过的元素值 + // Attempt: make a choice, update the state + duplicated.add(choice); // Record selected element values selected[i] = true; state.add(choice); - // 进行下一轮选择 + // Proceed to the next round of selection backtrack(state, choices, selected, res); - // 回退:撤销选择,恢复到之前的状态 + // Retract: undo the choice, restore to the previous state selected[i] = false; state.remove(state.size() - 1); } } } - /* 全排列 II */ + /* Permutation II */ List> permutationsII(int[] nums) { List> res = new ArrayList>(); backtrack(new ArrayList(), nums, new boolean[nums.length], res); @@ -668,365 +330,73 @@ Based on the code from the previous problem, we consider initiating a hash set ` === "C#" ```csharp title="permutations_ii.cs" - /* 回溯算法:全排列 II */ - void Backtrack(List state, int[] choices, bool[] selected, List> res) { - // 当状态长度等于元素数量时,记录解 - if (state.Count == choices.Length) { - res.Add(new List(state)); - return; - } - // 遍历所有选择 - HashSet duplicated = []; - for (int i = 0; i < choices.Length; i++) { - int 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.Count - 1); - } - } - } + [class]{permutations_ii}-[func]{Backtrack} - /* 全排列 II */ - List> PermutationsII(int[] nums) { - List> res = []; - Backtrack([], nums, new bool[nums.Length], res); - return res; - } + [class]{permutations_ii}-[func]{PermutationsII} ``` === "Go" ```go title="permutations_ii.go" - /* 回溯算法:全排列 II */ - func backtrackII(state *[]int, choices *[]int, selected *[]bool, res *[][]int) { - // 当状态长度等于元素数量时,记录解 - if len(*state) == len(*choices) { - newState := append([]int{}, *state...) - *res = append(*res, newState) - } - // 遍历所有选择 - duplicated := make(map[int]struct{}, 0) - for i := 0; i < len(*choices); i++ { - choice := (*choices)[i] - // 剪枝:不允许重复选择元素 且 不允许重复选择相等元素 - if _, ok := duplicated[choice]; !ok && !(*selected)[i] { - // 尝试:做出选择,更新状态 - // 记录选择过的元素值 - duplicated[choice] = struct{}{} - (*selected)[i] = true - *state = append(*state, choice) - // 进行下一轮选择 - backtrackII(state, choices, selected, res) - // 回退:撤销选择,恢复到之前的状态 - (*selected)[i] = false - *state = (*state)[:len(*state)-1] - } - } - } + [class]{}-[func]{backtrackII} - /* 全排列 II */ - func permutationsII(nums []int) [][]int { - res := make([][]int, 0) - state := make([]int, 0) - selected := make([]bool, len(nums)) - backtrackII(&state, &nums, &selected, &res) - return res - } + [class]{}-[func]{permutationsII} ``` === "Swift" ```swift title="permutations_ii.swift" - /* 回溯算法:全排列 II */ - func backtrack(state: inout [Int], choices: [Int], selected: inout [Bool], res: inout [[Int]]) { - // 当状态长度等于元素数量时,记录解 - if state.count == choices.count { - res.append(state) - return - } - // 遍历所有选择 - var duplicated: Set = [] - for (i, choice) in choices.enumerated() { - // 剪枝:不允许重复选择元素 且 不允许重复选择相等元素 - if !selected[i], !duplicated.contains(choice) { - // 尝试:做出选择,更新状态 - duplicated.insert(choice) // 记录选择过的元素值 - selected[i] = true - state.append(choice) - // 进行下一轮选择 - backtrack(state: &state, choices: choices, selected: &selected, res: &res) - // 回退:撤销选择,恢复到之前的状态 - selected[i] = false - state.removeLast() - } - } - } + [class]{}-[func]{backtrack} - /* 全排列 II */ - func permutationsII(nums: [Int]) -> [[Int]] { - var state: [Int] = [] - var selected = Array(repeating: false, count: nums.count) - var res: [[Int]] = [] - backtrack(state: &state, choices: nums, selected: &selected, res: &res) - return res - } + [class]{}-[func]{permutationsII} ``` === "JS" ```javascript title="permutations_ii.js" - /* 回溯算法:全排列 II */ - function backtrack(state, choices, selected, res) { - // 当状态长度等于元素数量时,记录解 - if (state.length === choices.length) { - res.push([...state]); - return; - } - // 遍历所有选择 - const duplicated = new Set(); - choices.forEach((choice, i) => { - // 剪枝:不允许重复选择元素 且 不允许重复选择相等元素 - if (!selected[i] && !duplicated.has(choice)) { - // 尝试:做出选择,更新状态 - duplicated.add(choice); // 记录选择过的元素值 - selected[i] = true; - state.push(choice); - // 进行下一轮选择 - backtrack(state, choices, selected, res); - // 回退:撤销选择,恢复到之前的状态 - selected[i] = false; - state.pop(); - } - }); - } + [class]{}-[func]{backtrack} - /* 全排列 II */ - function permutationsII(nums) { - const res = []; - backtrack([], nums, Array(nums.length).fill(false), res); - return res; - } + [class]{}-[func]{permutationsII} ``` === "TS" ```typescript title="permutations_ii.ts" - /* 回溯算法:全排列 II */ - function backtrack( - state: number[], - choices: number[], - selected: boolean[], - res: number[][] - ): void { - // 当状态长度等于元素数量时,记录解 - if (state.length === choices.length) { - res.push([...state]); - return; - } - // 遍历所有选择 - const duplicated = new Set(); - choices.forEach((choice, i) => { - // 剪枝:不允许重复选择元素 且 不允许重复选择相等元素 - if (!selected[i] && !duplicated.has(choice)) { - // 尝试:做出选择,更新状态 - duplicated.add(choice); // 记录选择过的元素值 - selected[i] = true; - state.push(choice); - // 进行下一轮选择 - backtrack(state, choices, selected, res); - // 回退:撤销选择,恢复到之前的状态 - selected[i] = false; - state.pop(); - } - }); - } + [class]{}-[func]{backtrack} - /* 全排列 II */ - function permutationsII(nums: number[]): number[][] { - const res: number[][] = []; - backtrack([], nums, Array(nums.length).fill(false), res); - return res; - } + [class]{}-[func]{permutationsII} ``` === "Dart" ```dart title="permutations_ii.dart" - /* 回溯算法:全排列 II */ - void backtrack( - List state, - List choices, - List selected, - List> res, - ) { - // 当状态长度等于元素数量时,记录解 - if (state.length == choices.length) { - res.add(List.from(state)); - return; - } - // 遍历所有选择 - Set duplicated = {}; - for (int i = 0; i < choices.length; i++) { - int 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.removeLast(); - } - } - } + [class]{}-[func]{backtrack} - /* 全排列 II */ - List> permutationsII(List nums) { - List> res = []; - backtrack([], nums, List.filled(nums.length, false), res); - return res; - } + [class]{}-[func]{permutationsII} ``` === "Rust" ```rust title="permutations_ii.rs" - /* 回溯算法:全排列 II */ - fn backtrack(mut state: Vec, choices: &[i32], selected: &mut [bool], res: &mut Vec>) { - // 当状态长度等于元素数量时,记录解 - if state.len() == choices.len() { - res.push(state); - return; - } - // 遍历所有选择 - let mut duplicated = HashSet::::new(); - for i in 0..choices.len() { - let choice = choices[i]; - // 剪枝:不允许重复选择元素 且 不允许重复选择相等元素 - if !selected[i] && !duplicated.contains(&choice) { - // 尝试:做出选择,更新状态 - duplicated.insert(choice); // 记录选择过的元素值 - selected[i] = true; - state.push(choice); - // 进行下一轮选择 - backtrack(state.clone(), choices, selected, res); - // 回退:撤销选择,恢复到之前的状态 - selected[i] = false; - state.remove(state.len() - 1); - } - } - } + [class]{}-[func]{backtrack} - /* 全排列 II */ - fn permutations_ii(nums: &mut [i32]) -> Vec> { - let mut res = Vec::new(); - backtrack(Vec::new(), nums, &mut vec![false; nums.len()], &mut res); - res - } + [class]{}-[func]{permutations_ii} ``` === "C" ```c title="permutations_ii.c" - /* 回溯算法:全排列 II */ - void backtrack(int *state, int stateSize, int *choices, int choicesSize, bool *selected, int **res, int *resSize) { - // 当状态长度等于元素数量时,记录解 - if (stateSize == choicesSize) { - res[*resSize] = (int *)malloc(choicesSize * sizeof(int)); - for (int i = 0; i < choicesSize; i++) { - res[*resSize][i] = state[i]; - } - (*resSize)++; - return; - } - // 遍历所有选择 - bool duplicated[MAX_SIZE] = {false}; - for (int i = 0; i < choicesSize; i++) { - int choice = choices[i]; - // 剪枝:不允许重复选择元素 且 不允许重复选择相等元素 - if (!selected[i] && !duplicated[choice]) { - // 尝试:做出选择,更新状态 - duplicated[choice] = true; // 记录选择过的元素值 - selected[i] = true; - state[stateSize] = choice; - // 进行下一轮选择 - backtrack(state, stateSize + 1, choices, choicesSize, selected, res, resSize); - // 回退:撤销选择,恢复到之前的状态 - selected[i] = false; - } - } - } + [class]{}-[func]{backtrack} - /* 全排列 II */ - int **permutationsII(int *nums, int numsSize, int *returnSize) { - int *state = (int *)malloc(numsSize * sizeof(int)); - bool *selected = (bool *)malloc(numsSize * sizeof(bool)); - for (int i = 0; i < numsSize; i++) { - selected[i] = false; - } - int **res = (int **)malloc(MAX_SIZE * sizeof(int *)); - *returnSize = 0; - - backtrack(state, 0, nums, numsSize, selected, res, returnSize); - - free(state); - free(selected); - - return res; - } + [class]{}-[func]{permutationsII} ``` === "Kotlin" ```kotlin title="permutations_ii.kt" - /* 回溯算法:全排列 II */ - fun backtrack( - state: MutableList, - choices: IntArray, - selected: BooleanArray, - res: MutableList?> - ) { - // 当状态长度等于元素数量时,记录解 - if (state.size == choices.size) { - res.add(state.toMutableList()) - return - } - // 遍历所有选择 - val duplicated = 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) - } - } - } + [class]{}-[func]{backtrack} - /* 全排列 II */ - fun permutationsII(nums: IntArray): MutableList?> { - val res = mutableListOf?>() - backtrack(mutableListOf(), nums, BooleanArray(nums.size), res) - return res - } + [class]{}-[func]{permutationsII} ``` === "Ruby" @@ -1045,11 +415,6 @@ Based on the code from the previous problem, we consider initiating a hash set ` [class]{}-[func]{permutationsII} ``` -??? pythontutor "Code Visualization" - -
- - Assuming all elements are distinct from each other, there are $n!$ (factorial) permutations of $n$ elements; when recording results, it is necessary to copy a list of length $n$, using $O(n)$ time. **Thus, the time complexity is $O(n!n)$**. The maximum recursion depth is $n$, using $O(n)$ frame space. `Selected` uses $O(n)$ space. At any one time, there can be up to $n$ `duplicated`, using $O(n^2)$ space. **Therefore, the space complexity is $O(n^2)$**. diff --git a/en/docs/chapter_backtracking/subset_sum_problem.md b/en/docs/chapter_backtracking/subset_sum_problem.md index b9f825a41..e31f5aaaf 100644 --- a/en/docs/chapter_backtracking/subset_sum_problem.md +++ b/en/docs/chapter_backtracking/subset_sum_problem.md @@ -31,28 +31,28 @@ Unlike the permutation problem, **elements in this problem can be chosen an unli choices: list[int], res: list[list[int]], ): - """回溯算法:子集和 I""" - # 子集和等于 target 时,记录解 + """Backtracking algorithm: Subset Sum I""" + # When the subset sum equals target, record the solution if total == target: res.append(list(state)) return - # 遍历所有选择 + # Traverse all choices for i in range(len(choices)): - # 剪枝:若子集和超过 target ,则跳过该选择 + # Pruning: if the subset sum exceeds target, skip that choice if total + choices[i] > target: continue - # 尝试:做出选择,更新元素和 total + # Attempt: make a choice, update elements and total state.append(choices[i]) - # 进行下一轮选择 + # Proceed to the next round of selection backtrack(state, target, total + choices[i], choices, res) - # 回退:撤销选择,恢复到之前的状态 + # Retract: undo the choice, restore to the previous state state.pop() def subset_sum_i_naive(nums: list[int], target: int) -> list[list[int]]: - """求解子集和 I(包含重复子集)""" - state = [] # 状态(子集) - total = 0 # 子集和 - res = [] # 结果列表(子集列表) + """Solve Subset Sum I (including duplicate subsets)""" + state = [] # State (subset) + total = 0 # Subset sum + res = [] # Result list (subset list) backtrack(state, target, total, nums, res) return res ``` @@ -60,68 +60,41 @@ Unlike the permutation problem, **elements in this problem can be chosen an unli === "C++" ```cpp title="subset_sum_i_naive.cpp" - /* 回溯算法:子集和 I */ - void backtrack(vector &state, int target, int total, vector &choices, vector> &res) { - // 子集和等于 target 时,记录解 - if (total == target) { - res.push_back(state); - return; - } - // 遍历所有选择 - for (size_t i = 0; i < choices.size(); i++) { - // 剪枝:若子集和超过 target ,则跳过该选择 - if (total + choices[i] > target) { - continue; - } - // 尝试:做出选择,更新元素和 total - state.push_back(choices[i]); - // 进行下一轮选择 - backtrack(state, target, total + choices[i], choices, res); - // 回退:撤销选择,恢复到之前的状态 - state.pop_back(); - } - } + [class]{}-[func]{backtrack} - /* 求解子集和 I(包含重复子集) */ - vector> subsetSumINaive(vector &nums, int target) { - vector state; // 状态(子集) - int total = 0; // 子集和 - vector> res; // 结果列表(子集列表) - backtrack(state, target, total, nums, res); - return res; - } + [class]{}-[func]{subsetSumINaive} ``` === "Java" ```java title="subset_sum_i_naive.java" - /* 回溯算法:子集和 I */ + /* Backtracking algorithm: Subset Sum I */ void backtrack(List state, int target, int total, int[] choices, List> res) { - // 子集和等于 target 时,记录解 + // When the subset sum equals target, record the solution if (total == target) { res.add(new ArrayList<>(state)); return; } - // 遍历所有选择 + // Traverse all choices for (int i = 0; i < choices.length; i++) { - // 剪枝:若子集和超过 target ,则跳过该选择 + // Pruning: if the subset sum exceeds target, skip that choice if (total + choices[i] > target) { continue; } - // 尝试:做出选择,更新元素和 total + // Attempt: make a choice, update elements and total state.add(choices[i]); - // 进行下一轮选择 + // Proceed to the next round of selection backtrack(state, target, total + choices[i], choices, res); - // 回退:撤销选择,恢复到之前的状态 + // Retract: undo the choice, restore to the previous state state.remove(state.size() - 1); } } - /* 求解子集和 I(包含重复子集) */ + /* Solve Subset Sum I (including duplicate subsets) */ List> subsetSumINaive(int[] nums, int target) { - List state = new ArrayList<>(); // 状态(子集) - int total = 0; // 子集和 - List> res = new ArrayList<>(); // 结果列表(子集列表) + List state = new ArrayList<>(); // State (subset) + int total = 0; // Subset sum + List> res = new ArrayList<>(); // Result list (subset list) backtrack(state, target, total, nums, res); return res; } @@ -130,341 +103,73 @@ Unlike the permutation problem, **elements in this problem can be chosen an unli === "C#" ```csharp title="subset_sum_i_naive.cs" - /* 回溯算法:子集和 I */ - void Backtrack(List state, int target, int total, int[] choices, List> res) { - // 子集和等于 target 时,记录解 - if (total == target) { - res.Add(new List(state)); - return; - } - // 遍历所有选择 - for (int i = 0; i < choices.Length; i++) { - // 剪枝:若子集和超过 target ,则跳过该选择 - if (total + choices[i] > target) { - continue; - } - // 尝试:做出选择,更新元素和 total - state.Add(choices[i]); - // 进行下一轮选择 - Backtrack(state, target, total + choices[i], choices, res); - // 回退:撤销选择,恢复到之前的状态 - state.RemoveAt(state.Count - 1); - } - } + [class]{subset_sum_i_naive}-[func]{Backtrack} - /* 求解子集和 I(包含重复子集) */ - List> SubsetSumINaive(int[] nums, int target) { - List state = []; // 状态(子集) - int total = 0; // 子集和 - List> res = []; // 结果列表(子集列表) - Backtrack(state, target, total, nums, res); - return res; - } + [class]{subset_sum_i_naive}-[func]{SubsetSumINaive} ``` === "Go" ```go title="subset_sum_i_naive.go" - /* 回溯算法:子集和 I */ - func backtrackSubsetSumINaive(total, target int, state, choices *[]int, res *[][]int) { - // 子集和等于 target 时,记录解 - if target == total { - newState := append([]int{}, *state...) - *res = append(*res, newState) - return - } - // 遍历所有选择 - for i := 0; i < len(*choices); i++ { - // 剪枝:若子集和超过 target ,则跳过该选择 - if total+(*choices)[i] > target { - continue - } - // 尝试:做出选择,更新元素和 total - *state = append(*state, (*choices)[i]) - // 进行下一轮选择 - backtrackSubsetSumINaive(total+(*choices)[i], target, state, choices, res) - // 回退:撤销选择,恢复到之前的状态 - *state = (*state)[:len(*state)-1] - } - } + [class]{}-[func]{backtrackSubsetSumINaive} - /* 求解子集和 I(包含重复子集) */ - func subsetSumINaive(nums []int, target int) [][]int { - state := make([]int, 0) // 状态(子集) - total := 0 // 子集和 - res := make([][]int, 0) // 结果列表(子集列表) - backtrackSubsetSumINaive(total, target, &state, &nums, &res) - return res - } + [class]{}-[func]{subsetSumINaive} ``` === "Swift" ```swift title="subset_sum_i_naive.swift" - /* 回溯算法:子集和 I */ - func backtrack(state: inout [Int], target: Int, total: Int, choices: [Int], res: inout [[Int]]) { - // 子集和等于 target 时,记录解 - if total == target { - res.append(state) - return - } - // 遍历所有选择 - for i in choices.indices { - // 剪枝:若子集和超过 target ,则跳过该选择 - if total + choices[i] > target { - continue - } - // 尝试:做出选择,更新元素和 total - state.append(choices[i]) - // 进行下一轮选择 - backtrack(state: &state, target: target, total: total + choices[i], choices: choices, res: &res) - // 回退:撤销选择,恢复到之前的状态 - state.removeLast() - } - } + [class]{}-[func]{backtrack} - /* 求解子集和 I(包含重复子集) */ - func subsetSumINaive(nums: [Int], target: Int) -> [[Int]] { - var state: [Int] = [] // 状态(子集) - let total = 0 // 子集和 - var res: [[Int]] = [] // 结果列表(子集列表) - backtrack(state: &state, target: target, total: total, choices: nums, res: &res) - return res - } + [class]{}-[func]{subsetSumINaive} ``` === "JS" ```javascript title="subset_sum_i_naive.js" - /* 回溯算法:子集和 I */ - function backtrack(state, target, total, choices, res) { - // 子集和等于 target 时,记录解 - if (total === target) { - res.push([...state]); - return; - } - // 遍历所有选择 - for (let i = 0; i < choices.length; i++) { - // 剪枝:若子集和超过 target ,则跳过该选择 - if (total + choices[i] > target) { - continue; - } - // 尝试:做出选择,更新元素和 total - state.push(choices[i]); - // 进行下一轮选择 - backtrack(state, target, total + choices[i], choices, res); - // 回退:撤销选择,恢复到之前的状态 - state.pop(); - } - } + [class]{}-[func]{backtrack} - /* 求解子集和 I(包含重复子集) */ - function subsetSumINaive(nums, target) { - const state = []; // 状态(子集) - const total = 0; // 子集和 - const res = []; // 结果列表(子集列表) - backtrack(state, target, total, nums, res); - return res; - } + [class]{}-[func]{subsetSumINaive} ``` === "TS" ```typescript title="subset_sum_i_naive.ts" - /* 回溯算法:子集和 I */ - function backtrack( - state: number[], - target: number, - total: number, - choices: number[], - res: number[][] - ): void { - // 子集和等于 target 时,记录解 - if (total === target) { - res.push([...state]); - return; - } - // 遍历所有选择 - for (let i = 0; i < choices.length; i++) { - // 剪枝:若子集和超过 target ,则跳过该选择 - if (total + choices[i] > target) { - continue; - } - // 尝试:做出选择,更新元素和 total - state.push(choices[i]); - // 进行下一轮选择 - backtrack(state, target, total + choices[i], choices, res); - // 回退:撤销选择,恢复到之前的状态 - state.pop(); - } - } + [class]{}-[func]{backtrack} - /* 求解子集和 I(包含重复子集) */ - function subsetSumINaive(nums: number[], target: number): number[][] { - const state = []; // 状态(子集) - const total = 0; // 子集和 - const res = []; // 结果列表(子集列表) - backtrack(state, target, total, nums, res); - return res; - } + [class]{}-[func]{subsetSumINaive} ``` === "Dart" ```dart title="subset_sum_i_naive.dart" - /* 回溯算法:子集和 I */ - void backtrack( - List state, - int target, - int total, - List choices, - List> res, - ) { - // 子集和等于 target 时,记录解 - if (total == target) { - res.add(List.from(state)); - return; - } - // 遍历所有选择 - for (int i = 0; i < choices.length; i++) { - // 剪枝:若子集和超过 target ,则跳过该选择 - if (total + choices[i] > target) { - continue; - } - // 尝试:做出选择,更新元素和 total - state.add(choices[i]); - // 进行下一轮选择 - backtrack(state, target, total + choices[i], choices, res); - // 回退:撤销选择,恢复到之前的状态 - state.removeLast(); - } - } + [class]{}-[func]{backtrack} - /* 求解子集和 I(包含重复子集) */ - List> subsetSumINaive(List nums, int target) { - List state = []; // 状态(子集) - int total = 0; // 元素和 - List> res = []; // 结果列表(子集列表) - backtrack(state, target, total, nums, res); - return res; - } + [class]{}-[func]{subsetSumINaive} ``` === "Rust" ```rust title="subset_sum_i_naive.rs" - /* 回溯算法:子集和 I */ - fn backtrack( - mut state: Vec, - target: i32, - total: i32, - choices: &[i32], - res: &mut Vec>, - ) { - // 子集和等于 target 时,记录解 - if total == target { - res.push(state); - return; - } - // 遍历所有选择 - for i in 0..choices.len() { - // 剪枝:若子集和超过 target ,则跳过该选择 - if total + choices[i] > target { - continue; - } - // 尝试:做出选择,更新元素和 total - state.push(choices[i]); - // 进行下一轮选择 - backtrack(state.clone(), target, total + choices[i], choices, res); - // 回退:撤销选择,恢复到之前的状态 - state.pop(); - } - } + [class]{}-[func]{backtrack} - /* 求解子集和 I(包含重复子集) */ - fn subset_sum_i_naive(nums: &[i32], target: i32) -> Vec> { - let state = Vec::new(); // 状态(子集) - let total = 0; // 子集和 - let mut res = Vec::new(); // 结果列表(子集列表) - backtrack(state, target, total, nums, &mut res); - res - } + [class]{}-[func]{subset_sum_i_naive} ``` === "C" ```c title="subset_sum_i_naive.c" - /* 回溯算法:子集和 I */ - void backtrack(int target, int total, int *choices, int choicesSize) { - // 子集和等于 target 时,记录解 - if (total == target) { - for (int i = 0; i < stateSize; i++) { - res[resSize][i] = state[i]; - } - resColSizes[resSize++] = stateSize; - return; - } - // 遍历所有选择 - for (int i = 0; i < choicesSize; i++) { - // 剪枝:若子集和超过 target ,则跳过该选择 - if (total + choices[i] > target) { - continue; - } - // 尝试:做出选择,更新元素和 total - state[stateSize++] = choices[i]; - // 进行下一轮选择 - backtrack(target, total + choices[i], choices, choicesSize); - // 回退:撤销选择,恢复到之前的状态 - stateSize--; - } - } + [class]{}-[func]{backtrack} - /* 求解子集和 I(包含重复子集) */ - void subsetSumINaive(int *nums, int numsSize, int target) { - resSize = 0; // 初始化解的数量为0 - backtrack(target, 0, nums, numsSize); - } + [class]{}-[func]{subsetSumINaive} ``` === "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(state.toMutableList()) - 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) - } - } + [class]{}-[func]{backtrack} - /* 求解子集和 I(包含重复子集) */ - fun subsetSumINaive(nums: IntArray, target: Int): MutableList?> { - val state = mutableListOf() // 状态(子集) - val total = 0 // 子集和 - val res = mutableListOf?>() // 结果列表(子集列表) - backtrack(state, target, total, nums, res) - return res - } + [class]{}-[func]{subsetSumINaive} ``` === "Ruby" @@ -483,11 +188,6 @@ Unlike the permutation problem, **elements in this problem can be chosen an unli [class]{}-[func]{subsetSumINaive} ``` -??? pythontutor "Code Visualization" - -
- - Inputting the array $[3, 4, 5]$ and target element $9$ into the above code yields the results $[3, 3, 3], [4, 5], [5, 4]$. **Although it successfully finds all subsets with a sum of $9$, it includes the duplicate subset $[4, 5]$ and $[5, 4]$**. This is because the search process distinguishes the order of choices, however, subsets do not distinguish the choice order. As shown in Figure 13-10, choosing $4$ before $5$ and choosing $5$ before $4$ are different branches, but correspond to the same subset. @@ -535,31 +235,31 @@ Besides, we have made the following two optimizations to the code. def backtrack( state: list[int], target: int, choices: list[int], start: int, res: list[list[int]] ): - """回溯算法:子集和 I""" - # 子集和等于 target 时,记录解 + """Backtracking algorithm: Subset Sum I""" + # When the subset sum equals target, record the solution if target == 0: res.append(list(state)) return - # 遍历所有选择 - # 剪枝二:从 start 开始遍历,避免生成重复子集 + # Traverse all choices + # Pruning two: start traversing from start to avoid generating duplicate subsets for i in range(start, len(choices)): - # 剪枝一:若子集和超过 target ,则直接结束循环 - # 这是因为数组已排序,后边元素更大,子集和一定超过 target + # Pruning one: if the subset sum exceeds target, end the loop immediately + # This is because the array is sorted, and later elements are larger, so the subset sum will definitely exceed target if target - choices[i] < 0: break - # 尝试:做出选择,更新 target, start + # Attempt: make a choice, update target, start state.append(choices[i]) - # 进行下一轮选择 + # Proceed to the next round of selection backtrack(state, target - choices[i], choices, i, res) - # 回退:撤销选择,恢复到之前的状态 + # Retract: undo the choice, restore to the previous state state.pop() def subset_sum_i(nums: list[int], target: int) -> list[list[int]]: - """求解子集和 I""" - state = [] # 状态(子集) - nums.sort() # 对 nums 进行排序 - start = 0 # 遍历起始点 - res = [] # 结果列表(子集列表) + """Solve Subset Sum I""" + state = [] # State (subset) + nums.sort() # Sort nums + start = 0 # Start point for traversal + res = [] # Result list (subset list) backtrack(state, target, nums, start, res) return res ``` @@ -567,74 +267,44 @@ Besides, we have made the following two optimizations to the code. === "C++" ```cpp title="subset_sum_i.cpp" - /* 回溯算法:子集和 I */ - void backtrack(vector &state, int target, vector &choices, int start, vector> &res) { - // 子集和等于 target 时,记录解 - if (target == 0) { - res.push_back(state); - return; - } - // 遍历所有选择 - // 剪枝二:从 start 开始遍历,避免生成重复子集 - for (int i = start; i < choices.size(); i++) { - // 剪枝一:若子集和超过 target ,则直接结束循环 - // 这是因为数组已排序,后边元素更大,子集和一定超过 target - if (target - choices[i] < 0) { - break; - } - // 尝试:做出选择,更新 target, start - state.push_back(choices[i]); - // 进行下一轮选择 - backtrack(state, target - choices[i], choices, i, res); - // 回退:撤销选择,恢复到之前的状态 - state.pop_back(); - } - } + [class]{}-[func]{backtrack} - /* 求解子集和 I */ - vector> subsetSumI(vector &nums, int target) { - vector state; // 状态(子集) - sort(nums.begin(), nums.end()); // 对 nums 进行排序 - int start = 0; // 遍历起始点 - vector> res; // 结果列表(子集列表) - backtrack(state, target, nums, start, res); - return res; - } + [class]{}-[func]{subsetSumI} ``` === "Java" ```java title="subset_sum_i.java" - /* 回溯算法:子集和 I */ + /* Backtracking algorithm: Subset Sum I */ void backtrack(List state, int target, int[] choices, int start, List> res) { - // 子集和等于 target 时,记录解 + // When the subset sum equals target, record the solution if (target == 0) { res.add(new ArrayList<>(state)); return; } - // 遍历所有选择 - // 剪枝二:从 start 开始遍历,避免生成重复子集 + // Traverse all choices + // Pruning two: start traversing from start to avoid generating duplicate subsets for (int i = start; i < choices.length; i++) { - // 剪枝一:若子集和超过 target ,则直接结束循环 - // 这是因为数组已排序,后边元素更大,子集和一定超过 target + // Pruning one: if the subset sum exceeds target, end the loop immediately + // This is because the array is sorted, and later elements are larger, so the subset sum will definitely exceed target if (target - choices[i] < 0) { break; } - // 尝试:做出选择,更新 target, start + // Attempt: make a choice, update target, start state.add(choices[i]); - // 进行下一轮选择 + // Proceed to the next round of selection backtrack(state, target - choices[i], choices, i, res); - // 回退:撤销选择,恢复到之前的状态 + // Retract: undo the choice, restore to the previous state state.remove(state.size() - 1); } } - /* 求解子集和 I */ + /* Solve Subset Sum I */ List> subsetSumI(int[] nums, int target) { - List state = new ArrayList<>(); // 状态(子集) - Arrays.sort(nums); // 对 nums 进行排序 - int start = 0; // 遍历起始点 - List> res = new ArrayList<>(); // 结果列表(子集列表) + List state = new ArrayList<>(); // State (subset) + Arrays.sort(nums); // Sort nums + int start = 0; // Start point for traversal + List> res = new ArrayList<>(); // Result list (subset list) backtrack(state, target, nums, start, res); return res; } @@ -643,369 +313,73 @@ Besides, we have made the following two optimizations to the code. === "C#" ```csharp title="subset_sum_i.cs" - /* 回溯算法:子集和 I */ - void Backtrack(List state, int target, int[] choices, int start, List> res) { - // 子集和等于 target 时,记录解 - if (target == 0) { - res.Add(new List(state)); - return; - } - // 遍历所有选择 - // 剪枝二:从 start 开始遍历,避免生成重复子集 - for (int i = start; i < choices.Length; i++) { - // 剪枝一:若子集和超过 target ,则直接结束循环 - // 这是因为数组已排序,后边元素更大,子集和一定超过 target - if (target - choices[i] < 0) { - break; - } - // 尝试:做出选择,更新 target, start - state.Add(choices[i]); - // 进行下一轮选择 - Backtrack(state, target - choices[i], choices, i, res); - // 回退:撤销选择,恢复到之前的状态 - state.RemoveAt(state.Count - 1); - } - } + [class]{subset_sum_i}-[func]{Backtrack} - /* 求解子集和 I */ - List> SubsetSumI(int[] nums, int target) { - List state = []; // 状态(子集) - Array.Sort(nums); // 对 nums 进行排序 - int start = 0; // 遍历起始点 - List> res = []; // 结果列表(子集列表) - Backtrack(state, target, nums, start, res); - return res; - } + [class]{subset_sum_i}-[func]{SubsetSumI} ``` === "Go" ```go title="subset_sum_i.go" - /* 回溯算法:子集和 I */ - func backtrackSubsetSumI(start, target int, state, choices *[]int, res *[][]int) { - // 子集和等于 target 时,记录解 - if target == 0 { - newState := append([]int{}, *state...) - *res = append(*res, newState) - return - } - // 遍历所有选择 - // 剪枝二:从 start 开始遍历,避免生成重复子集 - for i := start; i < len(*choices); i++ { - // 剪枝一:若子集和超过 target ,则直接结束循环 - // 这是因为数组已排序,后边元素更大,子集和一定超过 target - if target-(*choices)[i] < 0 { - break - } - // 尝试:做出选择,更新 target, start - *state = append(*state, (*choices)[i]) - // 进行下一轮选择 - backtrackSubsetSumI(i, target-(*choices)[i], state, choices, res) - // 回退:撤销选择,恢复到之前的状态 - *state = (*state)[:len(*state)-1] - } - } + [class]{}-[func]{backtrackSubsetSumI} - /* 求解子集和 I */ - func subsetSumI(nums []int, target int) [][]int { - state := make([]int, 0) // 状态(子集) - sort.Ints(nums) // 对 nums 进行排序 - start := 0 // 遍历起始点 - res := make([][]int, 0) // 结果列表(子集列表) - backtrackSubsetSumI(start, target, &state, &nums, &res) - return res - } + [class]{}-[func]{subsetSumI} ``` === "Swift" ```swift title="subset_sum_i.swift" - /* 回溯算法:子集和 I */ - func backtrack(state: inout [Int], target: Int, choices: [Int], start: Int, res: inout [[Int]]) { - // 子集和等于 target 时,记录解 - if target == 0 { - res.append(state) - return - } - // 遍历所有选择 - // 剪枝二:从 start 开始遍历,避免生成重复子集 - for i in choices.indices.dropFirst(start) { - // 剪枝一:若子集和超过 target ,则直接结束循环 - // 这是因为数组已排序,后边元素更大,子集和一定超过 target - if target - choices[i] < 0 { - break - } - // 尝试:做出选择,更新 target, start - state.append(choices[i]) - // 进行下一轮选择 - backtrack(state: &state, target: target - choices[i], choices: choices, start: i, res: &res) - // 回退:撤销选择,恢复到之前的状态 - state.removeLast() - } - } + [class]{}-[func]{backtrack} - /* 求解子集和 I */ - func subsetSumI(nums: [Int], target: Int) -> [[Int]] { - var state: [Int] = [] // 状态(子集) - let nums = nums.sorted() // 对 nums 进行排序 - let start = 0 // 遍历起始点 - var res: [[Int]] = [] // 结果列表(子集列表) - backtrack(state: &state, target: target, choices: nums, start: start, res: &res) - return res - } + [class]{}-[func]{subsetSumI} ``` === "JS" ```javascript title="subset_sum_i.js" - /* 回溯算法:子集和 I */ - function backtrack(state, target, choices, start, res) { - // 子集和等于 target 时,记录解 - if (target === 0) { - res.push([...state]); - return; - } - // 遍历所有选择 - // 剪枝二:从 start 开始遍历,避免生成重复子集 - for (let i = start; i < choices.length; i++) { - // 剪枝一:若子集和超过 target ,则直接结束循环 - // 这是因为数组已排序,后边元素更大,子集和一定超过 target - if (target - choices[i] < 0) { - break; - } - // 尝试:做出选择,更新 target, start - state.push(choices[i]); - // 进行下一轮选择 - backtrack(state, target - choices[i], choices, i, res); - // 回退:撤销选择,恢复到之前的状态 - state.pop(); - } - } + [class]{}-[func]{backtrack} - /* 求解子集和 I */ - function subsetSumI(nums, target) { - const state = []; // 状态(子集) - nums.sort((a, b) => a - b); // 对 nums 进行排序 - const start = 0; // 遍历起始点 - const res = []; // 结果列表(子集列表) - backtrack(state, target, nums, start, res); - return res; - } + [class]{}-[func]{subsetSumI} ``` === "TS" ```typescript title="subset_sum_i.ts" - /* 回溯算法:子集和 I */ - function backtrack( - state: number[], - target: number, - choices: number[], - start: number, - res: number[][] - ): void { - // 子集和等于 target 时,记录解 - if (target === 0) { - res.push([...state]); - return; - } - // 遍历所有选择 - // 剪枝二:从 start 开始遍历,避免生成重复子集 - for (let i = start; i < choices.length; i++) { - // 剪枝一:若子集和超过 target ,则直接结束循环 - // 这是因为数组已排序,后边元素更大,子集和一定超过 target - if (target - choices[i] < 0) { - break; - } - // 尝试:做出选择,更新 target, start - state.push(choices[i]); - // 进行下一轮选择 - backtrack(state, target - choices[i], choices, i, res); - // 回退:撤销选择,恢复到之前的状态 - state.pop(); - } - } + [class]{}-[func]{backtrack} - /* 求解子集和 I */ - function subsetSumI(nums: number[], target: number): number[][] { - const state = []; // 状态(子集) - nums.sort((a, b) => a - b); // 对 nums 进行排序 - const start = 0; // 遍历起始点 - const res = []; // 结果列表(子集列表) - backtrack(state, target, nums, start, res); - return res; - } + [class]{}-[func]{subsetSumI} ``` === "Dart" ```dart title="subset_sum_i.dart" - /* 回溯算法:子集和 I */ - void backtrack( - List state, - int target, - List choices, - int start, - List> res, - ) { - // 子集和等于 target 时,记录解 - if (target == 0) { - res.add(List.from(state)); - return; - } - // 遍历所有选择 - // 剪枝二:从 start 开始遍历,避免生成重复子集 - for (int i = start; i < choices.length; i++) { - // 剪枝一:若子集和超过 target ,则直接结束循环 - // 这是因为数组已排序,后边元素更大,子集和一定超过 target - if (target - choices[i] < 0) { - break; - } - // 尝试:做出选择,更新 target, start - state.add(choices[i]); - // 进行下一轮选择 - backtrack(state, target - choices[i], choices, i, res); - // 回退:撤销选择,恢复到之前的状态 - state.removeLast(); - } - } + [class]{}-[func]{backtrack} - /* 求解子集和 I */ - List> subsetSumI(List nums, int target) { - List state = []; // 状态(子集) - nums.sort(); // 对 nums 进行排序 - int start = 0; // 遍历起始点 - List> res = []; // 结果列表(子集列表) - backtrack(state, target, nums, start, res); - return res; - } + [class]{}-[func]{subsetSumI} ``` === "Rust" ```rust title="subset_sum_i.rs" - /* 回溯算法:子集和 I */ - fn backtrack( - mut state: Vec, - target: i32, - choices: &[i32], - start: usize, - res: &mut Vec>, - ) { - // 子集和等于 target 时,记录解 - if target == 0 { - res.push(state); - return; - } - // 遍历所有选择 - // 剪枝二:从 start 开始遍历,避免生成重复子集 - for i in start..choices.len() { - // 剪枝一:若子集和超过 target ,则直接结束循环 - // 这是因为数组已排序,后边元素更大,子集和一定超过 target - if target - choices[i] < 0 { - break; - } - // 尝试:做出选择,更新 target, start - state.push(choices[i]); - // 进行下一轮选择 - backtrack(state.clone(), target - choices[i], choices, i, res); - // 回退:撤销选择,恢复到之前的状态 - state.pop(); - } - } + [class]{}-[func]{backtrack} - /* 求解子集和 I */ - fn subset_sum_i(nums: &mut [i32], target: i32) -> Vec> { - let state = Vec::new(); // 状态(子集) - nums.sort(); // 对 nums 进行排序 - let start = 0; // 遍历起始点 - let mut res = Vec::new(); // 结果列表(子集列表) - backtrack(state, target, nums, start, &mut res); - res - } + [class]{}-[func]{subset_sum_i} ``` === "C" ```c title="subset_sum_i.c" - /* 回溯算法:子集和 I */ - void backtrack(int target, int *choices, int choicesSize, int start) { - // 子集和等于 target 时,记录解 - if (target == 0) { - for (int i = 0; i < stateSize; ++i) { - res[resSize][i] = state[i]; - } - resColSizes[resSize++] = stateSize; - return; - } - // 遍历所有选择 - // 剪枝二:从 start 开始遍历,避免生成重复子集 - for (int i = start; i < choicesSize; i++) { - // 剪枝一:若子集和超过 target ,则直接结束循环 - // 这是因为数组已排序,后边元素更大,子集和一定超过 target - if (target - choices[i] < 0) { - break; - } - // 尝试:做出选择,更新 target, start - state[stateSize] = choices[i]; - stateSize++; - // 进行下一轮选择 - backtrack(target - choices[i], choices, choicesSize, i); - // 回退:撤销选择,恢复到之前的状态 - stateSize--; - } - } + [class]{}-[func]{backtrack} - /* 求解子集和 I */ - void subsetSumI(int *nums, int numsSize, int target) { - qsort(nums, numsSize, sizeof(int), cmp); // 对 nums 进行排序 - int start = 0; // 遍历起始点 - backtrack(target, nums, numsSize, start); - } + [class]{}-[func]{subsetSumI} ``` === "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(state.toMutableList()) - return - } - // 遍历所有选择 - // 剪枝二:从 start 开始遍历,避免生成重复子集 - for (i in start..?> { - val state = mutableListOf() // 状态(子集) - nums.sort() // 对 nums 进行排序 - val start = 0 // 遍历起始点 - val res = mutableListOf?>() // 结果列表(子集列表) - backtrack(state, target, nums, start, res) - return res - } + [class]{}-[func]{subsetSumI} ``` === "Ruby" @@ -1024,11 +398,6 @@ Besides, we have made the following two optimizations to the code. [class]{}-[func]{subsetSumI} ``` -??? pythontutor "Code Visualization" - -
- - Figure 13-12 shows the overall backtracking process after inputting the array $[3, 4, 5]$ and target element $9$ into the above code. ![Subset sum I backtracking process](subset_sum_problem.assets/subset_sum_i.png){ class="animation-figure" } @@ -1063,35 +432,35 @@ At the same time, **this question stipulates that each array element can only be def backtrack( state: list[int], target: int, choices: list[int], start: int, res: list[list[int]] ): - """回溯算法:子集和 II""" - # 子集和等于 target 时,记录解 + """Backtracking algorithm: Subset Sum II""" + # When the subset sum equals target, record the solution if target == 0: res.append(list(state)) return - # 遍历所有选择 - # 剪枝二:从 start 开始遍历,避免生成重复子集 - # 剪枝三:从 start 开始遍历,避免重复选择同一元素 + # Traverse all choices + # Pruning two: start traversing from start to avoid generating duplicate subsets + # Pruning three: start traversing from start to avoid repeatedly selecting the same element for i in range(start, len(choices)): - # 剪枝一:若子集和超过 target ,则直接结束循环 - # 这是因为数组已排序,后边元素更大,子集和一定超过 target + # Pruning one: if the subset sum exceeds target, end the loop immediately + # This is because the array is sorted, and later elements are larger, so the subset sum will definitely exceed target if target - choices[i] < 0: break - # 剪枝四:如果该元素与左边元素相等,说明该搜索分支重复,直接跳过 + # Pruning four: if the element equals the left element, it indicates that the search branch is repeated, skip it if i > start and choices[i] == choices[i - 1]: continue - # 尝试:做出选择,更新 target, start + # Attempt: make a choice, update target, start state.append(choices[i]) - # 进行下一轮选择 + # Proceed to the next round of selection backtrack(state, target - choices[i], choices, i + 1, res) - # 回退:撤销选择,恢复到之前的状态 + # Retract: undo the choice, restore to the previous state state.pop() def subset_sum_ii(nums: list[int], target: int) -> list[list[int]]: - """求解子集和 II""" - state = [] # 状态(子集) - nums.sort() # 对 nums 进行排序 - start = 0 # 遍历起始点 - res = [] # 结果列表(子集列表) + """Solve Subset Sum II""" + state = [] # State (subset) + nums.sort() # Sort nums + start = 0 # Start point for traversal + res = [] # Result list (subset list) backtrack(state, target, nums, start, res) return res ``` @@ -1099,84 +468,49 @@ At the same time, **this question stipulates that each array element can only be === "C++" ```cpp title="subset_sum_ii.cpp" - /* 回溯算法:子集和 II */ - void backtrack(vector &state, int target, vector &choices, int start, vector> &res) { - // 子集和等于 target 时,记录解 - if (target == 0) { - res.push_back(state); - return; - } - // 遍历所有选择 - // 剪枝二:从 start 开始遍历,避免生成重复子集 - // 剪枝三:从 start 开始遍历,避免重复选择同一元素 - for (int i = start; i < choices.size(); i++) { - // 剪枝一:若子集和超过 target ,则直接结束循环 - // 这是因为数组已排序,后边元素更大,子集和一定超过 target - if (target - choices[i] < 0) { - break; - } - // 剪枝四:如果该元素与左边元素相等,说明该搜索分支重复,直接跳过 - if (i > start && choices[i] == choices[i - 1]) { - continue; - } - // 尝试:做出选择,更新 target, start - state.push_back(choices[i]); - // 进行下一轮选择 - backtrack(state, target - choices[i], choices, i + 1, res); - // 回退:撤销选择,恢复到之前的状态 - state.pop_back(); - } - } + [class]{}-[func]{backtrack} - /* 求解子集和 II */ - vector> subsetSumII(vector &nums, int target) { - vector state; // 状态(子集) - sort(nums.begin(), nums.end()); // 对 nums 进行排序 - int start = 0; // 遍历起始点 - vector> res; // 结果列表(子集列表) - backtrack(state, target, nums, start, res); - return res; - } + [class]{}-[func]{subsetSumII} ``` === "Java" ```java title="subset_sum_ii.java" - /* 回溯算法:子集和 II */ + /* Backtracking algorithm: Subset Sum II */ void backtrack(List state, int target, int[] choices, int start, List> res) { - // 子集和等于 target 时,记录解 + // When the subset sum equals target, record the solution if (target == 0) { res.add(new ArrayList<>(state)); return; } - // 遍历所有选择 - // 剪枝二:从 start 开始遍历,避免生成重复子集 - // 剪枝三:从 start 开始遍历,避免重复选择同一元素 + // Traverse all choices + // Pruning two: start traversing from start to avoid generating duplicate subsets + // Pruning three: start traversing from start to avoid repeatedly selecting the same element for (int i = start; i < choices.length; i++) { - // 剪枝一:若子集和超过 target ,则直接结束循环 - // 这是因为数组已排序,后边元素更大,子集和一定超过 target + // Pruning one: if the subset sum exceeds target, end the loop immediately + // This is because the array is sorted, and later elements are larger, so the subset sum will definitely exceed target if (target - choices[i] < 0) { break; } - // 剪枝四:如果该元素与左边元素相等,说明该搜索分支重复,直接跳过 + // Pruning four: if the element equals the left element, it indicates that the search branch is repeated, skip it if (i > start && choices[i] == choices[i - 1]) { continue; } - // 尝试:做出选择,更新 target, start + // Attempt: make a choice, update target, start state.add(choices[i]); - // 进行下一轮选择 + // Proceed to the next round of selection backtrack(state, target - choices[i], choices, i + 1, res); - // 回退:撤销选择,恢复到之前的状态 + // Retract: undo the choice, restore to the previous state state.remove(state.size() - 1); } } - /* 求解子集和 II */ + /* Solve Subset Sum II */ List> subsetSumII(int[] nums, int target) { - List state = new ArrayList<>(); // 状态(子集) - Arrays.sort(nums); // 对 nums 进行排序 - int start = 0; // 遍历起始点 - List> res = new ArrayList<>(); // 结果列表(子集列表) + List state = new ArrayList<>(); // State (subset) + Arrays.sort(nums); // Sort nums + int start = 0; // Start point for traversal + List> res = new ArrayList<>(); // Result list (subset list) backtrack(state, target, nums, start, res); return res; } @@ -1185,414 +519,73 @@ At the same time, **this question stipulates that each array element can only be === "C#" ```csharp title="subset_sum_ii.cs" - /* 回溯算法:子集和 II */ - void Backtrack(List state, int target, int[] choices, int start, List> res) { - // 子集和等于 target 时,记录解 - if (target == 0) { - res.Add(new List(state)); - return; - } - // 遍历所有选择 - // 剪枝二:从 start 开始遍历,避免生成重复子集 - // 剪枝三:从 start 开始遍历,避免重复选择同一元素 - for (int i = start; i < choices.Length; i++) { - // 剪枝一:若子集和超过 target ,则直接结束循环 - // 这是因为数组已排序,后边元素更大,子集和一定超过 target - if (target - choices[i] < 0) { - break; - } - // 剪枝四:如果该元素与左边元素相等,说明该搜索分支重复,直接跳过 - if (i > 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.Count - 1); - } - } + [class]{subset_sum_ii}-[func]{Backtrack} - /* 求解子集和 II */ - List> SubsetSumII(int[] nums, int target) { - List state = []; // 状态(子集) - Array.Sort(nums); // 对 nums 进行排序 - int start = 0; // 遍历起始点 - List> res = []; // 结果列表(子集列表) - Backtrack(state, target, nums, start, res); - return res; - } + [class]{subset_sum_ii}-[func]{SubsetSumII} ``` === "Go" ```go title="subset_sum_ii.go" - /* 回溯算法:子集和 II */ - func backtrackSubsetSumII(start, target int, state, choices *[]int, res *[][]int) { - // 子集和等于 target 时,记录解 - if target == 0 { - newState := append([]int{}, *state...) - *res = append(*res, newState) - return - } - // 遍历所有选择 - // 剪枝二:从 start 开始遍历,避免生成重复子集 - // 剪枝三:从 start 开始遍历,避免重复选择同一元素 - for i := start; i < len(*choices); i++ { - // 剪枝一:若子集和超过 target ,则直接结束循环 - // 这是因为数组已排序,后边元素更大,子集和一定超过 target - if target-(*choices)[i] < 0 { - break - } - // 剪枝四:如果该元素与左边元素相等,说明该搜索分支重复,直接跳过 - if i > start && (*choices)[i] == (*choices)[i-1] { - continue - } - // 尝试:做出选择,更新 target, start - *state = append(*state, (*choices)[i]) - // 进行下一轮选择 - backtrackSubsetSumII(i+1, target-(*choices)[i], state, choices, res) - // 回退:撤销选择,恢复到之前的状态 - *state = (*state)[:len(*state)-1] - } - } + [class]{}-[func]{backtrackSubsetSumII} - /* 求解子集和 II */ - func subsetSumII(nums []int, target int) [][]int { - state := make([]int, 0) // 状态(子集) - sort.Ints(nums) // 对 nums 进行排序 - start := 0 // 遍历起始点 - res := make([][]int, 0) // 结果列表(子集列表) - backtrackSubsetSumII(start, target, &state, &nums, &res) - return res - } + [class]{}-[func]{subsetSumII} ``` === "Swift" ```swift title="subset_sum_ii.swift" - /* 回溯算法:子集和 II */ - func backtrack(state: inout [Int], target: Int, choices: [Int], start: Int, res: inout [[Int]]) { - // 子集和等于 target 时,记录解 - if target == 0 { - res.append(state) - return - } - // 遍历所有选择 - // 剪枝二:从 start 开始遍历,避免生成重复子集 - // 剪枝三:从 start 开始遍历,避免重复选择同一元素 - for i in choices.indices.dropFirst(start) { - // 剪枝一:若子集和超过 target ,则直接结束循环 - // 这是因为数组已排序,后边元素更大,子集和一定超过 target - if target - choices[i] < 0 { - break - } - // 剪枝四:如果该元素与左边元素相等,说明该搜索分支重复,直接跳过 - if i > start, choices[i] == choices[i - 1] { - continue - } - // 尝试:做出选择,更新 target, start - state.append(choices[i]) - // 进行下一轮选择 - backtrack(state: &state, target: target - choices[i], choices: choices, start: i + 1, res: &res) - // 回退:撤销选择,恢复到之前的状态 - state.removeLast() - } - } + [class]{}-[func]{backtrack} - /* 求解子集和 II */ - func subsetSumII(nums: [Int], target: Int) -> [[Int]] { - var state: [Int] = [] // 状态(子集) - let nums = nums.sorted() // 对 nums 进行排序 - let start = 0 // 遍历起始点 - var res: [[Int]] = [] // 结果列表(子集列表) - backtrack(state: &state, target: target, choices: nums, start: start, res: &res) - return res - } + [class]{}-[func]{subsetSumII} ``` === "JS" ```javascript title="subset_sum_ii.js" - /* 回溯算法:子集和 II */ - function backtrack(state, target, choices, start, res) { - // 子集和等于 target 时,记录解 - if (target === 0) { - res.push([...state]); - return; - } - // 遍历所有选择 - // 剪枝二:从 start 开始遍历,避免生成重复子集 - // 剪枝三:从 start 开始遍历,避免重复选择同一元素 - for (let i = start; i < choices.length; i++) { - // 剪枝一:若子集和超过 target ,则直接结束循环 - // 这是因为数组已排序,后边元素更大,子集和一定超过 target - if (target - choices[i] < 0) { - break; - } - // 剪枝四:如果该元素与左边元素相等,说明该搜索分支重复,直接跳过 - if (i > start && choices[i] === choices[i - 1]) { - continue; - } - // 尝试:做出选择,更新 target, start - state.push(choices[i]); - // 进行下一轮选择 - backtrack(state, target - choices[i], choices, i + 1, res); - // 回退:撤销选择,恢复到之前的状态 - state.pop(); - } - } + [class]{}-[func]{backtrack} - /* 求解子集和 II */ - function subsetSumII(nums, target) { - const state = []; // 状态(子集) - nums.sort((a, b) => a - b); // 对 nums 进行排序 - const start = 0; // 遍历起始点 - const res = []; // 结果列表(子集列表) - backtrack(state, target, nums, start, res); - return res; - } + [class]{}-[func]{subsetSumII} ``` === "TS" ```typescript title="subset_sum_ii.ts" - /* 回溯算法:子集和 II */ - function backtrack( - state: number[], - target: number, - choices: number[], - start: number, - res: number[][] - ): void { - // 子集和等于 target 时,记录解 - if (target === 0) { - res.push([...state]); - return; - } - // 遍历所有选择 - // 剪枝二:从 start 开始遍历,避免生成重复子集 - // 剪枝三:从 start 开始遍历,避免重复选择同一元素 - for (let i = start; i < choices.length; i++) { - // 剪枝一:若子集和超过 target ,则直接结束循环 - // 这是因为数组已排序,后边元素更大,子集和一定超过 target - if (target - choices[i] < 0) { - break; - } - // 剪枝四:如果该元素与左边元素相等,说明该搜索分支重复,直接跳过 - if (i > start && choices[i] === choices[i - 1]) { - continue; - } - // 尝试:做出选择,更新 target, start - state.push(choices[i]); - // 进行下一轮选择 - backtrack(state, target - choices[i], choices, i + 1, res); - // 回退:撤销选择,恢复到之前的状态 - state.pop(); - } - } + [class]{}-[func]{backtrack} - /* 求解子集和 II */ - function subsetSumII(nums: number[], target: number): number[][] { - const state = []; // 状态(子集) - nums.sort((a, b) => a - b); // 对 nums 进行排序 - const start = 0; // 遍历起始点 - const res = []; // 结果列表(子集列表) - backtrack(state, target, nums, start, res); - return res; - } + [class]{}-[func]{subsetSumII} ``` === "Dart" ```dart title="subset_sum_ii.dart" - /* 回溯算法:子集和 II */ - void backtrack( - List state, - int target, - List choices, - int start, - List> res, - ) { - // 子集和等于 target 时,记录解 - if (target == 0) { - res.add(List.from(state)); - return; - } - // 遍历所有选择 - // 剪枝二:从 start 开始遍历,避免生成重复子集 - // 剪枝三:从 start 开始遍历,避免重复选择同一元素 - for (int i = start; i < choices.length; i++) { - // 剪枝一:若子集和超过 target ,则直接结束循环 - // 这是因为数组已排序,后边元素更大,子集和一定超过 target - if (target - choices[i] < 0) { - break; - } - // 剪枝四:如果该元素与左边元素相等,说明该搜索分支重复,直接跳过 - if (i > start && choices[i] == choices[i - 1]) { - continue; - } - // 尝试:做出选择,更新 target, start - state.add(choices[i]); - // 进行下一轮选择 - backtrack(state, target - choices[i], choices, i + 1, res); - // 回退:撤销选择,恢复到之前的状态 - state.removeLast(); - } - } + [class]{}-[func]{backtrack} - /* 求解子集和 II */ - List> subsetSumII(List nums, int target) { - List state = []; // 状态(子集) - nums.sort(); // 对 nums 进行排序 - int start = 0; // 遍历起始点 - List> res = []; // 结果列表(子集列表) - backtrack(state, target, nums, start, res); - return res; - } + [class]{}-[func]{subsetSumII} ``` === "Rust" ```rust title="subset_sum_ii.rs" - /* 回溯算法:子集和 II */ - fn backtrack( - mut state: Vec, - target: i32, - choices: &[i32], - start: usize, - res: &mut Vec>, - ) { - // 子集和等于 target 时,记录解 - if target == 0 { - res.push(state); - return; - } - // 遍历所有选择 - // 剪枝二:从 start 开始遍历,避免生成重复子集 - // 剪枝三:从 start 开始遍历,避免重复选择同一元素 - for i in start..choices.len() { - // 剪枝一:若子集和超过 target ,则直接结束循环 - // 这是因为数组已排序,后边元素更大,子集和一定超过 target - if target - choices[i] < 0 { - break; - } - // 剪枝四:如果该元素与左边元素相等,说明该搜索分支重复,直接跳过 - if i > start && choices[i] == choices[i - 1] { - continue; - } - // 尝试:做出选择,更新 target, start - state.push(choices[i]); - // 进行下一轮选择 - backtrack(state.clone(), target - choices[i], choices, i, res); - // 回退:撤销选择,恢复到之前的状态 - state.pop(); - } - } + [class]{}-[func]{backtrack} - /* 求解子集和 II */ - fn subset_sum_ii(nums: &mut [i32], target: i32) -> Vec> { - let state = Vec::new(); // 状态(子集) - nums.sort(); // 对 nums 进行排序 - let start = 0; // 遍历起始点 - let mut res = Vec::new(); // 结果列表(子集列表) - backtrack(state, target, nums, start, &mut res); - res - } + [class]{}-[func]{subset_sum_ii} ``` === "C" ```c title="subset_sum_ii.c" - /* 回溯算法:子集和 II */ - void backtrack(int target, int *choices, int choicesSize, int start) { - // 子集和等于 target 时,记录解 - if (target == 0) { - for (int i = 0; i < stateSize; i++) { - res[resSize][i] = state[i]; - } - resColSizes[resSize++] = stateSize; - return; - } - // 遍历所有选择 - // 剪枝二:从 start 开始遍历,避免生成重复子集 - // 剪枝三:从 start 开始遍历,避免重复选择同一元素 - for (int i = start; i < choicesSize; i++) { - // 剪枝一:若子集和超过 target ,则直接跳过 - if (target - choices[i] < 0) { - continue; - } - // 剪枝四:如果该元素与左边元素相等,说明该搜索分支重复,直接跳过 - if (i > start && choices[i] == choices[i - 1]) { - continue; - } - // 尝试:做出选择,更新 target, start - state[stateSize] = choices[i]; - stateSize++; - // 进行下一轮选择 - backtrack(target - choices[i], choices, choicesSize, i + 1); - // 回退:撤销选择,恢复到之前的状态 - stateSize--; - } - } + [class]{}-[func]{backtrack} - /* 求解子集和 II */ - void subsetSumII(int *nums, int numsSize, int target) { - // 对 nums 进行排序 - qsort(nums, numsSize, sizeof(int), cmp); - // 开始回溯 - backtrack(target, nums, numsSize, 0); - } + [class]{}-[func]{subsetSumII} ``` === "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(state.toMutableList()) - 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) - } - } + [class]{}-[func]{backtrack} - /* 求解子集和 II */ - fun subsetSumII(nums: IntArray, target: Int): MutableList?> { - val state = mutableListOf() // 状态(子集) - nums.sort() // 对 nums 进行排序 - val start = 0 // 遍历起始点 - val res = mutableListOf?>() // 结果列表(子集列表) - backtrack(state, target, nums, start, res) - return res - } + [class]{}-[func]{subsetSumII} ``` === "Ruby" @@ -1611,11 +604,6 @@ At the same time, **this question stipulates that each array element can only be [class]{}-[func]{subsetSumII} ``` -??? pythontutor "Code Visualization" - -
- - Figure 13-14 shows the backtracking process for the array $[4, 4, 5]$ and target element $9$, including four types of pruning operations. Please combine the illustration with the code comments to understand the entire search process and how each type of pruning operation works. ![Subset sum II backtracking process](subset_sum_problem.assets/subset_sum_ii.png){ class="animation-figure" } diff --git a/en/docs/chapter_computational_complexity/iteration_and_recursion.md b/en/docs/chapter_computational_complexity/iteration_and_recursion.md index 6650f2bcf..5585d3cd7 100644 --- a/en/docs/chapter_computational_complexity/iteration_and_recursion.md +++ b/en/docs/chapter_computational_complexity/iteration_and_recursion.md @@ -20,9 +20,9 @@ The following function uses a `for` loop to perform a summation of $1 + 2 + \dot ```python title="iteration.py" def for_loop(n: int) -> int: - """for 循环""" + """for loop""" res = 0 - # 循环求和 1, 2, ..., n-1, n + # Loop sum 1, 2, ..., n-1, n for i in range(1, n + 1): res += i return res @@ -31,24 +31,16 @@ The following function uses a `for` loop to perform a summation of $1 + 2 + \dot === "C++" ```cpp title="iteration.cpp" - /* for 循环 */ - int forLoop(int n) { - int res = 0; - // 循环求和 1, 2, ..., n-1, n - for (int i = 1; i <= n; ++i) { - res += i; - } - return res; - } + [class]{}-[func]{forLoop} ``` === "Java" ```java title="iteration.java" - /* for 循环 */ + /* for loop */ int forLoop(int n) { int res = 0; - // 循环求和 1, 2, ..., n-1, n + // Loop sum 1, 2, ..., n-1, n for (int i = 1; i <= n; i++) { res += i; } @@ -59,164 +51,69 @@ The following function uses a `for` loop to perform a summation of $1 + 2 + \dot === "C#" ```csharp title="iteration.cs" - /* for 循环 */ - int ForLoop(int n) { - int res = 0; - // 循环求和 1, 2, ..., n-1, n - for (int i = 1; i <= n; i++) { - res += i; - } - return res; - } + [class]{iteration}-[func]{ForLoop} ``` === "Go" ```go title="iteration.go" - /* for 循环 */ - func forLoop(n int) int { - res := 0 - // 循环求和 1, 2, ..., n-1, n - for i := 1; i <= n; i++ { - res += i - } - return res - } + [class]{}-[func]{forLoop} ``` === "Swift" ```swift title="iteration.swift" - /* for 循环 */ - func forLoop(n: Int) -> Int { - var res = 0 - // 循环求和 1, 2, ..., n-1, n - for i in 1 ... n { - res += i - } - return res - } + [class]{}-[func]{forLoop} ``` === "JS" ```javascript title="iteration.js" - /* for 循环 */ - function forLoop(n) { - let res = 0; - // 循环求和 1, 2, ..., n-1, n - for (let i = 1; i <= n; i++) { - res += i; - } - return res; - } + [class]{}-[func]{forLoop} ``` === "TS" ```typescript title="iteration.ts" - /* for 循环 */ - function forLoop(n: number): number { - let res = 0; - // 循环求和 1, 2, ..., n-1, n - for (let i = 1; i <= n; i++) { - res += i; - } - return res; - } + [class]{}-[func]{forLoop} ``` === "Dart" ```dart title="iteration.dart" - /* for 循环 */ - int forLoop(int n) { - int res = 0; - // 循环求和 1, 2, ..., n-1, n - for (int i = 1; i <= n; i++) { - res += i; - } - return res; - } + [class]{}-[func]{forLoop} ``` === "Rust" ```rust title="iteration.rs" - /* for 循环 */ - fn for_loop(n: i32) -> i32 { - let mut res = 0; - // 循环求和 1, 2, ..., n-1, n - for i in 1..=n { - res += i; - } - res - } + [class]{}-[func]{for_loop} ``` === "C" ```c title="iteration.c" - /* for 循环 */ - int forLoop(int n) { - int res = 0; - // 循环求和 1, 2, ..., n-1, n - for (int i = 1; i <= n; i++) { - res += i; - } - return res; - } + [class]{}-[func]{forLoop} ``` === "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 - } + [class]{}-[func]{forLoop} ``` === "Ruby" ```ruby title="iteration.rb" - ### for 循环 ### - def for_loop(n) - res = 0 - - # 循环求和 1, 2, ..., n-1, n - for i in 1..n - res += i - end - - res - end + [class]{}-[func]{for_loop} ``` === "Zig" ```zig title="iteration.zig" - // for 循环 - fn forLoop(n: usize) i32 { - var res: i32 = 0; - // 循环求和 1, 2, ..., n-1, n - for (1..n+1) |i| { - res = res + @as(i32, @intCast(i)); - } - return res; - } + [class]{}-[func]{forLoop} ``` -??? pythontutor "Code Visualization" - -
- - The flowchart below represents this sum function. ![Flowchart of the sum function](iteration_and_recursion.assets/iteration.png){ class="animation-figure" } @@ -235,43 +132,33 @@ Below we use a `while` loop to implement the sum $1 + 2 + \dots + n$. ```python title="iteration.py" def while_loop(n: int) -> int: - """while 循环""" + """while loop""" res = 0 - i = 1 # 初始化条件变量 - # 循环求和 1, 2, ..., n-1, n + i = 1 # Initialize condition variable + # Loop sum 1, 2, ..., n-1, n while i <= n: res += i - i += 1 # 更新条件变量 + i += 1 # Update condition variable return res ``` === "C++" ```cpp title="iteration.cpp" - /* while 循环 */ - int whileLoop(int n) { - int res = 0; - int i = 1; // 初始化条件变量 - // 循环求和 1, 2, ..., n-1, n - while (i <= n) { - res += i; - i++; // 更新条件变量 - } - return res; - } + [class]{}-[func]{whileLoop} ``` === "Java" ```java title="iteration.java" - /* while 循环 */ + /* while loop */ int whileLoop(int n) { int res = 0; - int i = 1; // 初始化条件变量 - // 循环求和 1, 2, ..., n-1, n + int i = 1; // Initialize condition variable + // Loop sum 1, 2, ..., n-1, n while (i <= n) { res += i; - i++; // 更新条件变量 + i++; // Update condition variable } return res; } @@ -280,189 +167,69 @@ Below we use a `while` loop to implement the sum $1 + 2 + \dots + n$. === "C#" ```csharp title="iteration.cs" - /* while 循环 */ - int WhileLoop(int n) { - int res = 0; - int i = 1; // 初始化条件变量 - // 循环求和 1, 2, ..., n-1, n - while (i <= n) { - res += i; - i += 1; // 更新条件变量 - } - return res; - } + [class]{iteration}-[func]{WhileLoop} ``` === "Go" ```go title="iteration.go" - /* while 循环 */ - func whileLoop(n int) int { - res := 0 - // 初始化条件变量 - i := 1 - // 循环求和 1, 2, ..., n-1, n - for i <= n { - res += i - // 更新条件变量 - i++ - } - return res - } + [class]{}-[func]{whileLoop} ``` === "Swift" ```swift title="iteration.swift" - /* while 循环 */ - func whileLoop(n: Int) -> Int { - var res = 0 - var i = 1 // 初始化条件变量 - // 循环求和 1, 2, ..., n-1, n - while i <= n { - res += i - i += 1 // 更新条件变量 - } - return res - } + [class]{}-[func]{whileLoop} ``` === "JS" ```javascript title="iteration.js" - /* while 循环 */ - function whileLoop(n) { - let res = 0; - let i = 1; // 初始化条件变量 - // 循环求和 1, 2, ..., n-1, n - while (i <= n) { - res += i; - i++; // 更新条件变量 - } - return res; - } + [class]{}-[func]{whileLoop} ``` === "TS" ```typescript title="iteration.ts" - /* while 循环 */ - function whileLoop(n: number): number { - let res = 0; - let i = 1; // 初始化条件变量 - // 循环求和 1, 2, ..., n-1, n - while (i <= n) { - res += i; - i++; // 更新条件变量 - } - return res; - } + [class]{}-[func]{whileLoop} ``` === "Dart" ```dart title="iteration.dart" - /* while 循环 */ - int whileLoop(int n) { - int res = 0; - int i = 1; // 初始化条件变量 - // 循环求和 1, 2, ..., n-1, n - while (i <= n) { - res += i; - i++; // 更新条件变量 - } - return res; - } + [class]{}-[func]{whileLoop} ``` === "Rust" ```rust title="iteration.rs" - /* while 循环 */ - fn while_loop(n: i32) -> i32 { - let mut res = 0; - let mut i = 1; // 初始化条件变量 - - // 循环求和 1, 2, ..., n-1, n - while i <= n { - res += i; - i += 1; // 更新条件变量 - } - res - } + [class]{}-[func]{while_loop} ``` === "C" ```c title="iteration.c" - /* while 循环 */ - int whileLoop(int n) { - int res = 0; - int i = 1; // 初始化条件变量 - // 循环求和 1, 2, ..., n-1, n - while (i <= n) { - res += i; - i++; // 更新条件变量 - } - return res; - } + [class]{}-[func]{whileLoop} ``` === "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 - } + [class]{}-[func]{whileLoop} ``` === "Ruby" ```ruby title="iteration.rb" - ### while 循环 ### - def while_loop(n) - res = 0 - i = 1 # 初始化条件变量 - - # 循环求和 1, 2, ..., n-1, n - while i <= n - res += i - i += 1 # 更新条件变量 - end - - res - end + [class]{}-[func]{while_loop} ``` === "Zig" ```zig title="iteration.zig" - // while 循环 - fn whileLoop(n: i32) i32 { - var res: i32 = 0; - var i: i32 = 1; // 初始化条件变量 - // 循环求和 1, 2, ..., n-1, n - while (i <= n) { - res += @intCast(i); - i += 1; - } - return res; - } + [class]{}-[func]{whileLoop} ``` -??? pythontutor "Code Visualization" - -
- - **`while` loops provide more flexibility than `for` loops**, especially since they allow for custom initialization and modification of the condition variable at each step. For example, in the following code, the condition variable $i$ is updated twice each round, which would be inconvenient to implement with a `for` loop. @@ -471,13 +238,13 @@ For example, in the following code, the condition variable $i$ is updated twice ```python title="iteration.py" def while_loop_ii(n: int) -> int: - """while 循环(两次更新)""" + """while loop (two updates)""" res = 0 - i = 1 # 初始化条件变量 - # 循环求和 1, 4, 10, ... + i = 1 # Initialize condition variable + # Loop sum 1, 4, 10, ... while i <= n: res += i - # 更新条件变量 + # Update condition variable i += 1 i *= 2 return res @@ -486,32 +253,20 @@ For example, in the following code, the condition variable $i$ is updated twice === "C++" ```cpp title="iteration.cpp" - /* while 循环(两次更新) */ - int whileLoopII(int n) { - int res = 0; - int i = 1; // 初始化条件变量 - // 循环求和 1, 4, 10, ... - while (i <= n) { - res += i; - // 更新条件变量 - i++; - i *= 2; - } - return res; - } + [class]{}-[func]{whileLoopII} ``` === "Java" ```java title="iteration.java" - /* while 循环(两次更新) */ + /* while loop (two updates) */ int whileLoopII(int n) { int res = 0; - int i = 1; // 初始化条件变量 - // 循环求和 1, 4, 10, ... + int i = 1; // Initialize condition variable + // Loop sum 1, 4, 10, ... while (i <= n) { res += i; - // 更新条件变量 + // Update condition variable i++; i *= 2; } @@ -522,210 +277,69 @@ For example, in the following code, the condition variable $i$ is updated twice === "C#" ```csharp title="iteration.cs" - /* while 循环(两次更新) */ - int WhileLoopII(int n) { - int res = 0; - int i = 1; // 初始化条件变量 - // 循环求和 1, 4, 10, ... - while (i <= n) { - res += i; - // 更新条件变量 - i += 1; - i *= 2; - } - return res; - } + [class]{iteration}-[func]{WhileLoopII} ``` === "Go" ```go title="iteration.go" - /* while 循环(两次更新) */ - func whileLoopII(n int) int { - res := 0 - // 初始化条件变量 - i := 1 - // 循环求和 1, 4, 10, ... - for i <= n { - res += i - // 更新条件变量 - i++ - i *= 2 - } - return res - } + [class]{}-[func]{whileLoopII} ``` === "Swift" ```swift title="iteration.swift" - /* while 循环(两次更新) */ - func whileLoopII(n: Int) -> Int { - var res = 0 - var i = 1 // 初始化条件变量 - // 循环求和 1, 4, 10, ... - while i <= n { - res += i - // 更新条件变量 - i += 1 - i *= 2 - } - return res - } + [class]{}-[func]{whileLoopII} ``` === "JS" ```javascript title="iteration.js" - /* while 循环(两次更新) */ - function whileLoopII(n) { - let res = 0; - let i = 1; // 初始化条件变量 - // 循环求和 1, 4, 10, ... - while (i <= n) { - res += i; - // 更新条件变量 - i++; - i *= 2; - } - return res; - } + [class]{}-[func]{whileLoopII} ``` === "TS" ```typescript title="iteration.ts" - /* while 循环(两次更新) */ - function whileLoopII(n: number): number { - let res = 0; - let i = 1; // 初始化条件变量 - // 循环求和 1, 4, 10, ... - while (i <= n) { - res += i; - // 更新条件变量 - i++; - i *= 2; - } - return res; - } + [class]{}-[func]{whileLoopII} ``` === "Dart" ```dart title="iteration.dart" - /* while 循环(两次更新) */ - int whileLoopII(int n) { - int res = 0; - int i = 1; // 初始化条件变量 - // 循环求和 1, 4, 10, ... - while (i <= n) { - res += i; - // 更新条件变量 - i++; - i *= 2; - } - return res; - } + [class]{}-[func]{whileLoopII} ``` === "Rust" ```rust title="iteration.rs" - /* while 循环(两次更新) */ - fn while_loop_ii(n: i32) -> i32 { - let mut res = 0; - let mut i = 1; // 初始化条件变量 - - // 循环求和 1, 4, 10, ... - while i <= n { - res += i; - // 更新条件变量 - i += 1; - i *= 2; - } - res - } + [class]{}-[func]{while_loop_ii} ``` === "C" ```c title="iteration.c" - /* while 循环(两次更新) */ - int whileLoopII(int n) { - int res = 0; - int i = 1; // 初始化条件变量 - // 循环求和 1, 4, 10, ... - while (i <= n) { - res += i; - // 更新条件变量 - i++; - i *= 2; - } - return res; - } + [class]{}-[func]{whileLoopII} ``` === "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 - } + [class]{}-[func]{whileLoopII} ``` === "Ruby" ```ruby title="iteration.rb" - ### while 循环(两次更新)### - def while_loop_ii(n) - res = 0 - i = 1 # 初始化条件变量 - - # 循环求和 1, 4, 10, ... - while i <= n - res += i - # 更新条件变量 - i += 1 - i *= 2 - end - - res - end + [class]{}-[func]{while_loop_ii} ``` === "Zig" ```zig title="iteration.zig" - // while 循环(两次更新) - fn whileLoopII(n: i32) i32 { - var res: i32 = 0; - var i: i32 = 1; // 初始化条件变量 - // 循环求和 1, 4, 10, ... - while (i <= n) { - res += @intCast(i); - // 更新条件变量 - i += 1; - i *= 2; - } - return res; - } + [class]{}-[func]{whileLoopII} ``` -??? pythontutor "Code Visualization" - -
- - Overall, **`for` loops are more concise, while `while` loops are more flexible**. Both can implement iterative structures. Which one to use should be determined based on the specific requirements of the problem. ### 3.   Nested loops @@ -736,11 +350,11 @@ We can nest one loop structure within another. Below is an example using `for` l ```python title="iteration.py" def nested_for_loop(n: int) -> str: - """双层 for 循环""" + """Double for loop""" res = "" - # 循环 i = 1, 2, ..., n-1, n + # Loop i = 1, 2, ..., n-1, n for i in range(1, n + 1): - # 循环 j = 1, 2, ..., n-1, n + # Loop j = 1, 2, ..., n-1, n for j in range(1, n + 1): res += f"({i}, {j}), " return res @@ -749,29 +363,18 @@ We can nest one loop structure within another. Below is an example using `for` l === "C++" ```cpp title="iteration.cpp" - /* 双层 for 循环 */ - string nestedForLoop(int n) { - ostringstream res; - // 循环 i = 1, 2, ..., n-1, n - for (int i = 1; i <= n; ++i) { - // 循环 j = 1, 2, ..., n-1, n - for (int j = 1; j <= n; ++j) { - res << "(" << i << ", " << j << "), "; - } - } - return res.str(); - } + [class]{}-[func]{nestedForLoop} ``` === "Java" ```java title="iteration.java" - /* 双层 for 循环 */ + /* Double for loop */ String nestedForLoop(int n) { StringBuilder res = new StringBuilder(); - // 循环 i = 1, 2, ..., n-1, n + // Loop i = 1, 2, ..., n-1, n for (int i = 1; i <= n; i++) { - // 循环 j = 1, 2, ..., n-1, n + // Loop j = 1, 2, ..., n-1, n for (int j = 1; j <= n; j++) { res.append("(" + i + ", " + j + "), "); } @@ -783,204 +386,69 @@ We can nest one loop structure within another. Below is an example using `for` l === "C#" ```csharp title="iteration.cs" - /* 双层 for 循环 */ - string NestedForLoop(int n) { - StringBuilder res = new(); - // 循环 i = 1, 2, ..., n-1, n - for (int i = 1; i <= n; i++) { - // 循环 j = 1, 2, ..., n-1, n - for (int j = 1; j <= n; j++) { - res.Append($"({i}, {j}), "); - } - } - return res.ToString(); - } + [class]{iteration}-[func]{NestedForLoop} ``` === "Go" ```go title="iteration.go" - /* 双层 for 循环 */ - func nestedForLoop(n int) string { - res := "" - // 循环 i = 1, 2, ..., n-1, n - for i := 1; i <= n; i++ { - for j := 1; j <= n; j++ { - // 循环 j = 1, 2, ..., n-1, n - res += fmt.Sprintf("(%d, %d), ", i, j) - } - } - return res - } + [class]{}-[func]{nestedForLoop} ``` === "Swift" ```swift title="iteration.swift" - /* 双层 for 循环 */ - func nestedForLoop(n: Int) -> String { - var res = "" - // 循环 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 - } + [class]{}-[func]{nestedForLoop} ``` === "JS" ```javascript title="iteration.js" - /* 双层 for 循环 */ - function nestedForLoop(n) { - let res = ''; - // 循环 i = 1, 2, ..., n-1, n - for (let i = 1; i <= n; i++) { - // 循环 j = 1, 2, ..., n-1, n - for (let j = 1; j <= n; j++) { - res += `(${i}, ${j}), `; - } - } - return res; - } + [class]{}-[func]{nestedForLoop} ``` === "TS" ```typescript title="iteration.ts" - /* 双层 for 循环 */ - function nestedForLoop(n: number): string { - let res = ''; - // 循环 i = 1, 2, ..., n-1, n - for (let i = 1; i <= n; i++) { - // 循环 j = 1, 2, ..., n-1, n - for (let j = 1; j <= n; j++) { - res += `(${i}, ${j}), `; - } - } - return res; - } + [class]{}-[func]{nestedForLoop} ``` === "Dart" ```dart title="iteration.dart" - /* 双层 for 循环 */ - String nestedForLoop(int n) { - String res = ""; - // 循环 i = 1, 2, ..., n-1, n - for (int i = 1; i <= n; i++) { - // 循环 j = 1, 2, ..., n-1, n - for (int j = 1; j <= n; j++) { - res += "($i, $j), "; - } - } - return res; - } + [class]{}-[func]{nestedForLoop} ``` === "Rust" ```rust title="iteration.rs" - /* 双层 for 循环 */ - fn nested_for_loop(n: i32) -> String { - let mut res = vec![]; - // 循环 i = 1, 2, ..., n-1, n - for i in 1..=n { - // 循环 j = 1, 2, ..., n-1, n - for j in 1..=n { - res.push(format!("({}, {}), ", i, j)); - } - } - res.join("") - } + [class]{}-[func]{nested_for_loop} ``` === "C" ```c title="iteration.c" - /* 双层 for 循环 */ - char *nestedForLoop(int n) { - // n * n 为对应点数量,"(i, j), " 对应字符串长最大为 6+10*2,加上最后一个空字符 \0 的额外空间 - int size = n * n * 26 + 1; - char *res = malloc(size * sizeof(char)); - // 循环 i = 1, 2, ..., n-1, n - for (int i = 1; i <= n; i++) { - // 循环 j = 1, 2, ..., n-1, n - for (int j = 1; j <= n; j++) { - char tmp[26]; - snprintf(tmp, sizeof(tmp), "(%d, %d), ", i, j); - strncat(res, tmp, size - strlen(res) - 1); - } - } - return res; - } + [class]{}-[func]{nestedForLoop} ``` === "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() - } + [class]{}-[func]{nestedForLoop} ``` === "Ruby" ```ruby title="iteration.rb" - ### 双层 for 循环 ### - def nested_for_loop(n) - res = "" - - # 循环 i = 1, 2, ..., n-1, n - for i in 1..n - # 循环 j = 1, 2, ..., n-1, n - for j in 1..n - res += "(#{i}, #{j}), " - end - end - - res - end + [class]{}-[func]{nested_for_loop} ``` === "Zig" ```zig title="iteration.zig" - // 双层 for 循环 - fn nestedForLoop(allocator: Allocator, n: usize) ![]const u8 { - var res = std.ArrayList(u8).init(allocator); - defer res.deinit(); - var buffer: [20]u8 = undefined; - // 循环 i = 1, 2, ..., n-1, n - for (1..n+1) |i| { - // 循环 j = 1, 2, ..., n-1, n - for (1..n+1) |j| { - var _str = try std.fmt.bufPrint(&buffer, "({d}, {d}), ", .{i, j}); - try res.appendSlice(_str); - } - } - return res.toOwnedSlice(); - } + [class]{}-[func]{nestedForLoop} ``` -??? pythontutor "Code Visualization" - -
- - The flowchart below represents this nested loop. ![Flowchart of the nested loop](iteration_and_recursion.assets/nested_iteration.png){ class="animation-figure" } @@ -1010,42 +478,33 @@ Observe the following code, where simply calling the function `recur(n)` can com ```python title="recursion.py" def recur(n: int) -> int: - """递归""" - # 终止条件 + """Recursion""" + # Termination condition if n == 1: return 1 - # 递:递归调用 + # Recursive: recursive call res = recur(n - 1) - # 归:返回结果 + # Return: return result return n + res ``` === "C++" ```cpp title="recursion.cpp" - /* 递归 */ - int recur(int n) { - // 终止条件 - if (n == 1) - return 1; - // 递:递归调用 - int res = recur(n - 1); - // 归:返回结果 - return n + res; - } + [class]{}-[func]{recur} ``` === "Java" ```java title="recursion.java" - /* 递归 */ + /* Recursion */ int recur(int n) { - // 终止条件 + // Termination condition if (n == 1) return 1; - // 递:递归调用 + // Recursive: recursive call int res = recur(n - 1); - // 归:返回结果 + // Return: return result return n + res; } ``` @@ -1053,173 +512,69 @@ Observe the following code, where simply calling the function `recur(n)` can com === "C#" ```csharp title="recursion.cs" - /* 递归 */ - int Recur(int n) { - // 终止条件 - if (n == 1) - return 1; - // 递:递归调用 - int res = Recur(n - 1); - // 归:返回结果 - return n + res; - } + [class]{recursion}-[func]{Recur} ``` === "Go" ```go title="recursion.go" - /* 递归 */ - func recur(n int) int { - // 终止条件 - if n == 1 { - return 1 - } - // 递:递归调用 - res := recur(n - 1) - // 归:返回结果 - return n + res - } + [class]{}-[func]{recur} ``` === "Swift" ```swift title="recursion.swift" - /* 递归 */ - func recur(n: Int) -> Int { - // 终止条件 - if n == 1 { - return 1 - } - // 递:递归调用 - let res = recur(n: n - 1) - // 归:返回结果 - return n + res - } + [class]{}-[func]{recur} ``` === "JS" ```javascript title="recursion.js" - /* 递归 */ - function recur(n) { - // 终止条件 - if (n === 1) return 1; - // 递:递归调用 - const res = recur(n - 1); - // 归:返回结果 - return n + res; - } + [class]{}-[func]{recur} ``` === "TS" ```typescript title="recursion.ts" - /* 递归 */ - function recur(n: number): number { - // 终止条件 - if (n === 1) return 1; - // 递:递归调用 - const res = recur(n - 1); - // 归:返回结果 - return n + res; - } + [class]{}-[func]{recur} ``` === "Dart" ```dart title="recursion.dart" - /* 递归 */ - int recur(int n) { - // 终止条件 - if (n == 1) return 1; - // 递:递归调用 - int res = recur(n - 1); - // 归:返回结果 - return n + res; - } + [class]{}-[func]{recur} ``` === "Rust" ```rust title="recursion.rs" - /* 递归 */ - fn recur(n: i32) -> i32 { - // 终止条件 - if n == 1 { - return 1; - } - // 递:递归调用 - let res = recur(n - 1); - // 归:返回结果 - n + res - } + [class]{}-[func]{recur} ``` === "C" ```c title="recursion.c" - /* 递归 */ - int recur(int n) { - // 终止条件 - if (n == 1) - return 1; - // 递:递归调用 - int res = recur(n - 1); - // 归:返回结果 - return n + res; - } + [class]{}-[func]{recur} ``` === "Kotlin" ```kotlin title="recursion.kt" - /* 递归 */ - fun recur(n: Int): Int { - // 终止条件 - if (n == 1) - return 1 - // 递: 递归调用 - val res = recur(n - 1) - // 归: 返回结果 - return n + res - } + [class]{}-[func]{recur} ``` === "Ruby" ```ruby title="recursion.rb" - ### 递归 ### - def recur(n) - # 终止条件 - return 1 if n == 1 - # 递:递归调用 - res = recur(n - 1) - # 归:返回结果 - n + res - end + [class]{}-[func]{recur} ``` === "Zig" ```zig title="recursion.zig" - // 递归函数 - fn recur(n: i32) i32 { - // 终止条件 - if (n == 1) { - return 1; - } - // 递:递归调用 - var res: i32 = recur(n - 1); - // 归:返回结果 - return n + res; - } + [class]{}-[func]{recur} ``` -??? pythontutor "Code Visualization" - -
- - Figure 2-3 shows the recursive process of this function. ![Recursive process of the sum function](iteration_and_recursion.assets/recursion_sum.png){ class="animation-figure" } @@ -1264,36 +619,29 @@ For example, in calculating $1 + 2 + \dots + n$, we can make the result variable ```python title="recursion.py" def tail_recur(n, res): - """尾递归""" - # 终止条件 + """Tail recursion""" + # Termination condition if n == 0: return res - # 尾递归调用 + # Tail recursive call return tail_recur(n - 1, res + n) ``` === "C++" ```cpp title="recursion.cpp" - /* 尾递归 */ - int tailRecur(int n, int res) { - // 终止条件 - if (n == 0) - return res; - // 尾递归调用 - return tailRecur(n - 1, res + n); - } + [class]{}-[func]{tailRecur} ``` === "Java" ```java title="recursion.java" - /* 尾递归 */ + /* Tail recursion */ int tailRecur(int n, int res) { - // 终止条件 + // Termination condition if (n == 0) return res; - // 尾递归调用 + // Tail recursive call return tailRecur(n - 1, res + n); } ``` @@ -1301,152 +649,69 @@ For example, in calculating $1 + 2 + \dots + n$, we can make the result variable === "C#" ```csharp title="recursion.cs" - /* 尾递归 */ - int TailRecur(int n, int res) { - // 终止条件 - if (n == 0) - return res; - // 尾递归调用 - return TailRecur(n - 1, res + n); - } + [class]{recursion}-[func]{TailRecur} ``` === "Go" ```go title="recursion.go" - /* 尾递归 */ - func tailRecur(n int, res int) int { - // 终止条件 - if n == 0 { - return res - } - // 尾递归调用 - return tailRecur(n-1, res+n) - } + [class]{}-[func]{tailRecur} ``` === "Swift" ```swift title="recursion.swift" - /* 尾递归 */ - func tailRecur(n: Int, res: Int) -> Int { - // 终止条件 - if n == 0 { - return res - } - // 尾递归调用 - return tailRecur(n: n - 1, res: res + n) - } + [class]{}-[func]{tailRecur} ``` === "JS" ```javascript title="recursion.js" - /* 尾递归 */ - function tailRecur(n, res) { - // 终止条件 - if (n === 0) return res; - // 尾递归调用 - return tailRecur(n - 1, res + n); - } + [class]{}-[func]{tailRecur} ``` === "TS" ```typescript title="recursion.ts" - /* 尾递归 */ - function tailRecur(n: number, res: number): number { - // 终止条件 - if (n === 0) return res; - // 尾递归调用 - return tailRecur(n - 1, res + n); - } + [class]{}-[func]{tailRecur} ``` === "Dart" ```dart title="recursion.dart" - /* 尾递归 */ - int tailRecur(int n, int res) { - // 终止条件 - if (n == 0) return res; - // 尾递归调用 - return tailRecur(n - 1, res + n); - } + [class]{}-[func]{tailRecur} ``` === "Rust" ```rust title="recursion.rs" - /* 尾递归 */ - fn tail_recur(n: i32, res: i32) -> i32 { - // 终止条件 - if n == 0 { - return res; - } - // 尾递归调用 - tail_recur(n - 1, res + n) - } + [class]{}-[func]{tail_recur} ``` === "C" ```c title="recursion.c" - /* 尾递归 */ - int tailRecur(int n, int res) { - // 终止条件 - if (n == 0) - return res; - // 尾递归调用 - return tailRecur(n - 1, res + n); - } + [class]{}-[func]{tailRecur} ``` === "Kotlin" ```kotlin title="recursion.kt" - /* 尾递归 */ - tailrec fun tailRecur(n: Int, res: Int): Int { - // 添加 tailrec 关键词,以开启尾递归优化 - // 终止条件 - if (n == 0) - return res - // 尾递归调用 - return tailRecur(n - 1, res + n) - } + [class]{}-[func]{tailRecur} ``` === "Ruby" ```ruby title="recursion.rb" - ### 尾递归 ### - def tail_recur(n, res) - # 终止条件 - return res if n == 0 - # 尾递归调用 - tail_recur(n - 1, res + n) - end + [class]{}-[func]{tail_recur} ``` === "Zig" ```zig title="recursion.zig" - // 尾递归函数 - fn tailRecur(n: i32, res: i32) i32 { - // 终止条件 - if (n == 0) { - return res; - } - // 尾递归调用 - return tailRecur(n - 1, res + n); - } + [class]{}-[func]{tailRecur} ``` -??? pythontutor "Code Visualization" - -
- - The execution process of tail recursion is shown in Figure 2-5. Comparing regular recursion and tail recursion, the point of the summation operation is different. - **Regular recursion**: The summation operation occurs during the "returning" phase, requiring another summation after each layer returns. @@ -1479,42 +744,33 @@ Using the recursive relation, and considering the first two numbers as terminati ```python title="recursion.py" def fib(n: int) -> int: - """斐波那契数列:递归""" - # 终止条件 f(1) = 0, f(2) = 1 + """Fibonacci sequence: Recursion""" + # Termination condition f(1) = 0, f(2) = 1 if n == 1 or n == 2: return n - 1 - # 递归调用 f(n) = f(n-1) + f(n-2) + # Recursive call f(n) = f(n-1) + f(n-2) res = fib(n - 1) + fib(n - 2) - # 返回结果 f(n) + # Return result f(n) return res ``` === "C++" ```cpp title="recursion.cpp" - /* 斐波那契数列:递归 */ - int fib(int n) { - // 终止条件 f(1) = 0, f(2) = 1 - if (n == 1 || n == 2) - return n - 1; - // 递归调用 f(n) = f(n-1) + f(n-2) - int res = fib(n - 1) + fib(n - 2); - // 返回结果 f(n) - return res; - } + [class]{}-[func]{fib} ``` === "Java" ```java title="recursion.java" - /* 斐波那契数列:递归 */ + /* Fibonacci sequence: Recursion */ int fib(int n) { - // 终止条件 f(1) = 0, f(2) = 1 + // Termination condition f(1) = 0, f(2) = 1 if (n == 1 || n == 2) return n - 1; - // 递归调用 f(n) = f(n-1) + f(n-2) + // Recursive call f(n) = f(n-1) + f(n-2) int res = fib(n - 1) + fib(n - 2); - // 返回结果 f(n) + // Return result f(n) return res; } ``` @@ -1522,173 +778,69 @@ Using the recursive relation, and considering the first two numbers as terminati === "C#" ```csharp title="recursion.cs" - /* 斐波那契数列:递归 */ - int Fib(int n) { - // 终止条件 f(1) = 0, f(2) = 1 - if (n == 1 || n == 2) - return n - 1; - // 递归调用 f(n) = f(n-1) + f(n-2) - int res = Fib(n - 1) + Fib(n - 2); - // 返回结果 f(n) - return res; - } + [class]{recursion}-[func]{Fib} ``` === "Go" ```go title="recursion.go" - /* 斐波那契数列:递归 */ - func 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) - res := fib(n-1) + fib(n-2) - // 返回结果 f(n) - return res - } + [class]{}-[func]{fib} ``` === "Swift" ```swift title="recursion.swift" - /* 斐波那契数列:递归 */ - func 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) - let res = fib(n: n - 1) + fib(n: n - 2) - // 返回结果 f(n) - return res - } + [class]{}-[func]{fib} ``` === "JS" ```javascript title="recursion.js" - /* 斐波那契数列:递归 */ - function fib(n) { - // 终止条件 f(1) = 0, f(2) = 1 - if (n === 1 || n === 2) return n - 1; - // 递归调用 f(n) = f(n-1) + f(n-2) - const res = fib(n - 1) + fib(n - 2); - // 返回结果 f(n) - return res; - } + [class]{}-[func]{fib} ``` === "TS" ```typescript title="recursion.ts" - /* 斐波那契数列:递归 */ - function fib(n: number): number { - // 终止条件 f(1) = 0, f(2) = 1 - if (n === 1 || n === 2) return n - 1; - // 递归调用 f(n) = f(n-1) + f(n-2) - const res = fib(n - 1) + fib(n - 2); - // 返回结果 f(n) - return res; - } + [class]{}-[func]{fib} ``` === "Dart" ```dart title="recursion.dart" - /* 斐波那契数列:递归 */ - int fib(int n) { - // 终止条件 f(1) = 0, f(2) = 1 - if (n == 1 || n == 2) return n - 1; - // 递归调用 f(n) = f(n-1) + f(n-2) - int res = fib(n - 1) + fib(n - 2); - // 返回结果 f(n) - return res; - } + [class]{}-[func]{fib} ``` === "Rust" ```rust title="recursion.rs" - /* 斐波那契数列:递归 */ - fn fib(n: i32) -> i32 { - // 终止条件 f(1) = 0, f(2) = 1 - if n == 1 || n == 2 { - return n - 1; - } - // 递归调用 f(n) = f(n-1) + f(n-2) - let res = fib(n - 1) + fib(n - 2); - // 返回结果 - res - } + [class]{}-[func]{fib} ``` === "C" ```c title="recursion.c" - /* 斐波那契数列:递归 */ - int fib(int n) { - // 终止条件 f(1) = 0, f(2) = 1 - if (n == 1 || n == 2) - return n - 1; - // 递归调用 f(n) = f(n-1) + f(n-2) - int res = fib(n - 1) + fib(n - 2); - // 返回结果 f(n) - return res; - } + [class]{}-[func]{fib} ``` === "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 - } + [class]{}-[func]{fib} ``` === "Ruby" ```ruby title="recursion.rb" - ### 斐波那契数列:递归 ### - def fib(n) - # 终止条件 f(1) = 0, f(2) = 1 - return n - 1 if n == 1 || n == 2 - # 递归调用 f(n) = f(n-1) + f(n-2) - res = fib(n - 1) + fib(n - 2) - # 返回结果 f(n) - res - end + [class]{}-[func]{fib} ``` === "Zig" ```zig title="recursion.zig" - // 斐波那契数列 - fn fib(n: i32) i32 { - // 终止条件 f(1) = 0, f(2) = 1 - if (n == 1 or n == 2) { - return n - 1; - } - // 递归调用 f(n) = f(n-1) + f(n-2) - var res: i32 = fib(n - 1) + fib(n - 2); - // 返回结果 f(n) - return res; - } + [class]{}-[func]{fib} ``` -??? pythontutor "Code Visualization" - -
- - Observing the above code, we see that it recursively calls two functions within itself, **meaning that one call generates two branching calls**. As illustrated below, this continuous recursive calling eventually creates a recursion tree with a depth of $n$. ![Fibonacci sequence recursion tree](iteration_and_recursion.assets/recursion_tree.png){ class="animation-figure" } @@ -1734,17 +886,17 @@ Therefore, **we can use an explicit stack to simulate the behavior of the call s ```python title="recursion.py" def for_loop_recur(n: int) -> int: - """使用迭代模拟递归""" - # 使用一个显式的栈来模拟系统调用栈 + """Simulate recursion with iteration""" + # Use an explicit stack to simulate the system call stack stack = [] res = 0 - # 递:递归调用 + # Recursive: recursive call for i in range(n, 0, -1): - # 通过“入栈操作”模拟“递” + # Simulate "recursive" by "pushing onto the stack" stack.append(i) - # 归:返回结果 + # Return: return result while stack: - # 通过“出栈操作”模拟“归” + # Simulate "return" by "popping from the stack" res += stack.pop() # res = 1+2+3+...+n return res @@ -1753,43 +905,25 @@ Therefore, **we can use an explicit stack to simulate the behavior of the call s === "C++" ```cpp title="recursion.cpp" - /* 使用迭代模拟递归 */ - int forLoopRecur(int n) { - // 使用一个显式的栈来模拟系统调用栈 - stack stack; - int res = 0; - // 递:递归调用 - for (int i = n; i > 0; i--) { - // 通过“入栈操作”模拟“递” - stack.push(i); - } - // 归:返回结果 - while (!stack.empty()) { - // 通过“出栈操作”模拟“归” - res += stack.top(); - stack.pop(); - } - // res = 1+2+3+...+n - return res; - } + [class]{}-[func]{forLoopRecur} ``` === "Java" ```java title="recursion.java" - /* 使用迭代模拟递归 */ + /* Simulate recursion with iteration */ int forLoopRecur(int n) { - // 使用一个显式的栈来模拟系统调用栈 + // Use an explicit stack to simulate the system call stack Stack stack = new Stack<>(); int res = 0; - // 递:递归调用 + // Recursive: recursive call for (int i = n; i > 0; i--) { - // 通过“入栈操作”模拟“递” + // Simulate "recursive" by "pushing onto the stack" stack.push(i); } - // 归:返回结果 + // Return: return result while (!stack.isEmpty()) { - // 通过“出栈操作”模拟“归” + // Simulate "return" by "popping from the stack" res += stack.pop(); } // res = 1+2+3+...+n @@ -1800,265 +934,69 @@ Therefore, **we can use an explicit stack to simulate the behavior of the call s === "C#" ```csharp title="recursion.cs" - /* 使用迭代模拟递归 */ - int ForLoopRecur(int n) { - // 使用一个显式的栈来模拟系统调用栈 - Stack stack = new(); - int res = 0; - // 递:递归调用 - for (int i = n; i > 0; i--) { - // 通过“入栈操作”模拟“递” - stack.Push(i); - } - // 归:返回结果 - while (stack.Count > 0) { - // 通过“出栈操作”模拟“归” - res += stack.Pop(); - } - // res = 1+2+3+...+n - return res; - } + [class]{recursion}-[func]{ForLoopRecur} ``` === "Go" ```go title="recursion.go" - /* 使用迭代模拟递归 */ - func forLoopRecur(n int) int { - // 使用一个显式的栈来模拟系统调用栈 - stack := list.New() - res := 0 - // 递:递归调用 - for i := n; i > 0; i-- { - // 通过“入栈操作”模拟“递” - stack.PushBack(i) - } - // 归:返回结果 - for stack.Len() != 0 { - // 通过“出栈操作”模拟“归” - res += stack.Back().Value.(int) - stack.Remove(stack.Back()) - } - // res = 1+2+3+...+n - return res - } + [class]{}-[func]{forLoopRecur} ``` === "Swift" ```swift title="recursion.swift" - /* 使用迭代模拟递归 */ - func forLoopRecur(n: Int) -> Int { - // 使用一个显式的栈来模拟系统调用栈 - var stack: [Int] = [] - var res = 0 - // 递:递归调用 - for i in (1 ... n).reversed() { - // 通过“入栈操作”模拟“递” - stack.append(i) - } - // 归:返回结果 - while !stack.isEmpty { - // 通过“出栈操作”模拟“归” - res += stack.removeLast() - } - // res = 1+2+3+...+n - return res - } + [class]{}-[func]{forLoopRecur} ``` === "JS" ```javascript title="recursion.js" - /* 使用迭代模拟递归 */ - function forLoopRecur(n) { - // 使用一个显式的栈来模拟系统调用栈 - const stack = []; - let res = 0; - // 递:递归调用 - for (let i = n; i > 0; i--) { - // 通过“入栈操作”模拟“递” - stack.push(i); - } - // 归:返回结果 - while (stack.length) { - // 通过“出栈操作”模拟“归” - res += stack.pop(); - } - // res = 1+2+3+...+n - return res; - } + [class]{}-[func]{forLoopRecur} ``` === "TS" ```typescript title="recursion.ts" - /* 使用迭代模拟递归 */ - function forLoopRecur(n: number): number { - // 使用一个显式的栈来模拟系统调用栈 - const stack: number[] = []; - let res: number = 0; - // 递:递归调用 - for (let i = n; i > 0; i--) { - // 通过“入栈操作”模拟“递” - stack.push(i); - } - // 归:返回结果 - while (stack.length) { - // 通过“出栈操作”模拟“归” - res += stack.pop(); - } - // res = 1+2+3+...+n - return res; - } + [class]{}-[func]{forLoopRecur} ``` === "Dart" ```dart title="recursion.dart" - /* 使用迭代模拟递归 */ - int forLoopRecur(int n) { - // 使用一个显式的栈来模拟系统调用栈 - List stack = []; - int res = 0; - // 递:递归调用 - for (int i = n; i > 0; i--) { - // 通过“入栈操作”模拟“递” - stack.add(i); - } - // 归:返回结果 - while (!stack.isEmpty) { - // 通过“出栈操作”模拟“归” - res += stack.removeLast(); - } - // res = 1+2+3+...+n - return res; - } + [class]{}-[func]{forLoopRecur} ``` === "Rust" ```rust title="recursion.rs" - /* 使用迭代模拟递归 */ - fn for_loop_recur(n: i32) -> i32 { - // 使用一个显式的栈来模拟系统调用栈 - let mut stack = Vec::new(); - let mut res = 0; - // 递:递归调用 - for i in (1..=n).rev() { - // 通过“入栈操作”模拟“递” - stack.push(i); - } - // 归:返回结果 - while !stack.is_empty() { - // 通过“出栈操作”模拟“归” - res += stack.pop().unwrap(); - } - // res = 1+2+3+...+n - res - } + [class]{}-[func]{for_loop_recur} ``` === "C" ```c title="recursion.c" - /* 使用迭代模拟递归 */ - int forLoopRecur(int n) { - int stack[1000]; // 借助一个大数组来模拟栈 - int top = -1; // 栈顶索引 - int res = 0; - // 递:递归调用 - for (int i = n; i > 0; i--) { - // 通过“入栈操作”模拟“递” - stack[1 + top++] = i; - } - // 归:返回结果 - while (top >= 0) { - // 通过“出栈操作”模拟“归” - res += stack[top--]; - } - // res = 1+2+3+...+n - return res; - } + [class]{}-[func]{forLoopRecur} ``` === "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 - } + [class]{}-[func]{forLoopRecur} ``` === "Ruby" ```ruby title="recursion.rb" - ### 使用迭代模拟递归 ### - def for_loop_recur(n) - # 使用一个显式的栈来模拟系统调用栈 - stack = [] - res = 0 - - # 递:递归调用 - for i in n.downto(0) - # 通过“入栈操作”模拟“递” - stack << i - end - # 归:返回结果 - while !stack.empty? - res += stack.pop - end - - # res = 1+2+3+...+n - res - end + [class]{}-[func]{for_loop_recur} ``` === "Zig" ```zig title="recursion.zig" - // 使用迭代模拟递归 - fn forLoopRecur(comptime n: i32) i32 { - // 使用一个显式的栈来模拟系统调用栈 - var stack: [n]i32 = undefined; - var res: i32 = 0; - // 递:递归调用 - var i: usize = n; - while (i > 0) { - stack[i - 1] = @intCast(i); - i -= 1; - } - // 归:返回结果 - var index: usize = n; - while (index > 0) { - index -= 1; - res += stack[index]; - } - // res = 1+2+3+...+n - return res; - } + [class]{}-[func]{forLoopRecur} ``` -??? pythontutor "Code Visualization" - -
- - Observing the above code, when recursion is transformed into iteration, the code becomes more complex. Although iteration and recursion can often be transformed into each other, it's not always advisable to do so for two reasons: - The transformed code may become more challenging to understand and less readable. diff --git a/en/docs/chapter_computational_complexity/space_complexity.md b/en/docs/chapter_computational_complexity/space_complexity.md index 5b1ae61d6..c9ba227b0 100644 --- a/en/docs/chapter_computational_complexity/space_complexity.md +++ b/en/docs/chapter_computational_complexity/space_complexity.md @@ -754,20 +754,20 @@ Note that memory occupied by initializing variables or calling functions in a lo ```python title="space_complexity.py" def function() -> int: - """函数""" - # 执行某些操作 + """Function""" + # Perform some operations return 0 def constant(n: int): - """常数阶""" - # 常量、变量、对象占用 O(1) 空间 + """Constant complexity""" + # Constants, variables, objects occupy O(1) space a = 0 nums = [0] * 10000 node = ListNode(0) - # 循环中的变量占用 O(1) 空间 + # Variables in a loop occupy O(1) space for _ in range(n): c = 0 - # 循环中的函数占用 O(1) 空间 + # Functions in a loop occupy O(1) space for _ in range(n): function() ``` @@ -775,51 +775,32 @@ Note that memory occupied by initializing variables or calling functions in a lo === "C++" ```cpp title="space_complexity.cpp" - /* 函数 */ - int func() { - // 执行某些操作 - return 0; - } + [class]{}-[func]{func} - /* 常数阶 */ - void constant(int n) { - // 常量、变量、对象占用 O(1) 空间 - const int a = 0; - int b = 0; - vector nums(10000); - ListNode node(0); - // 循环中的变量占用 O(1) 空间 - for (int i = 0; i < n; i++) { - int c = 0; - } - // 循环中的函数占用 O(1) 空间 - for (int i = 0; i < n; i++) { - func(); - } - } + [class]{}-[func]{constant} ``` === "Java" ```java title="space_complexity.java" - /* 函数 */ + /* Function */ int function() { - // 执行某些操作 + // Perform some operations return 0; } - /* 常数阶 */ + /* Constant complexity */ void constant(int n) { - // 常量、变量、对象占用 O(1) 空间 + // Constants, variables, objects occupy O(1) space final int a = 0; int b = 0; int[] nums = new int[10000]; ListNode node = new ListNode(0); - // 循环中的变量占用 O(1) 空间 + // Variables in a loop occupy O(1) space for (int i = 0; i < n; i++) { int c = 0; } - // 循环中的函数占用 O(1) 空间 + // Functions in a loop occupy O(1) space for (int i = 0; i < n; i++) { function(); } @@ -829,316 +810,91 @@ Note that memory occupied by initializing variables or calling functions in a lo === "C#" ```csharp title="space_complexity.cs" - /* 函数 */ - int Function() { - // 执行某些操作 - return 0; - } + [class]{space_complexity}-[func]{Function} - /* 常数阶 */ - void Constant(int n) { - // 常量、变量、对象占用 O(1) 空间 - int a = 0; - int b = 0; - int[] nums = new int[10000]; - ListNode node = new(0); - // 循环中的变量占用 O(1) 空间 - for (int i = 0; i < n; i++) { - int c = 0; - } - // 循环中的函数占用 O(1) 空间 - for (int i = 0; i < n; i++) { - Function(); - } - } + [class]{space_complexity}-[func]{Constant} ``` === "Go" ```go title="space_complexity.go" - /* 函数 */ - func function() int { - // 执行某些操作... - return 0 - } + [class]{}-[func]{function} - /* 常数阶 */ - func spaceConstant(n int) { - // 常量、变量、对象占用 O(1) 空间 - const a = 0 - b := 0 - nums := make([]int, 10000) - node := newNode(0) - // 循环中的变量占用 O(1) 空间 - var c int - for i := 0; i < n; i++ { - c = 0 - } - // 循环中的函数占用 O(1) 空间 - for i := 0; i < n; i++ { - function() - } - b += 0 - c += 0 - nums[0] = 0 - node.val = 0 - } + [class]{}-[func]{spaceConstant} ``` === "Swift" ```swift title="space_complexity.swift" - /* 函数 */ - @discardableResult - func function() -> Int { - // 执行某些操作 - return 0 - } + [class]{}-[func]{function} - /* 常数阶 */ - func constant(n: Int) { - // 常量、变量、对象占用 O(1) 空间 - let a = 0 - var b = 0 - let nums = Array(repeating: 0, count: 10000) - let node = ListNode(x: 0) - // 循环中的变量占用 O(1) 空间 - for _ in 0 ..< n { - let c = 0 - } - // 循环中的函数占用 O(1) 空间 - for _ in 0 ..< n { - function() - } - } + [class]{}-[func]{constant} ``` === "JS" ```javascript title="space_complexity.js" - /* 函数 */ - function constFunc() { - // 执行某些操作 - return 0; - } + [class]{}-[func]{constFunc} - /* 常数阶 */ - function constant(n) { - // 常量、变量、对象占用 O(1) 空间 - const a = 0; - const b = 0; - const nums = new Array(10000); - const node = new ListNode(0); - // 循环中的变量占用 O(1) 空间 - for (let i = 0; i < n; i++) { - const c = 0; - } - // 循环中的函数占用 O(1) 空间 - for (let i = 0; i < n; i++) { - constFunc(); - } - } + [class]{}-[func]{constant} ``` === "TS" ```typescript title="space_complexity.ts" - /* 函数 */ - function constFunc(): number { - // 执行某些操作 - return 0; - } + [class]{}-[func]{constFunc} - /* 常数阶 */ - function constant(n: number): void { - // 常量、变量、对象占用 O(1) 空间 - const a = 0; - const b = 0; - const nums = new Array(10000); - const node = new ListNode(0); - // 循环中的变量占用 O(1) 空间 - for (let i = 0; i < n; i++) { - const c = 0; - } - // 循环中的函数占用 O(1) 空间 - for (let i = 0; i < n; i++) { - constFunc(); - } - } + [class]{}-[func]{constant} ``` === "Dart" ```dart title="space_complexity.dart" - /* 函数 */ - int function() { - // 执行某些操作 - return 0; - } + [class]{}-[func]{function} - /* 常数阶 */ - void constant(int n) { - // 常量、变量、对象占用 O(1) 空间 - final int a = 0; - int b = 0; - List nums = List.filled(10000, 0); - ListNode node = ListNode(0); - // 循环中的变量占用 O(1) 空间 - for (var i = 0; i < n; i++) { - int c = 0; - } - // 循环中的函数占用 O(1) 空间 - for (var i = 0; i < n; i++) { - function(); - } - } + [class]{}-[func]{constant} ``` === "Rust" ```rust title="space_complexity.rs" - /* 函数 */ - fn function() -> i32 { - // 执行某些操作 - return 0; - } + [class]{}-[func]{function} - /* 常数阶 */ - #[allow(unused)] - fn constant(n: i32) { - // 常量、变量、对象占用 O(1) 空间 - const A: i32 = 0; - let b = 0; - let nums = vec![0; 10000]; - let node = ListNode::new(0); - // 循环中的变量占用 O(1) 空间 - for i in 0..n { - let c = 0; - } - // 循环中的函数占用 O(1) 空间 - for i in 0..n { - function(); - } - } + [class]{}-[func]{constant} ``` === "C" ```c title="space_complexity.c" - /* 函数 */ - int func() { - // 执行某些操作 - return 0; - } + [class]{}-[func]{func} - /* 常数阶 */ - void constant(int n) { - // 常量、变量、对象占用 O(1) 空间 - const int a = 0; - int b = 0; - int nums[1000]; - ListNode *node = newListNode(0); - free(node); - // 循环中的变量占用 O(1) 空间 - for (int i = 0; i < n; i++) { - int c = 0; - } - // 循环中的函数占用 O(1) 空间 - for (int i = 0; i < n; i++) { - func(); - } - } + [class]{}-[func]{constant} ``` === "Kotlin" ```kotlin title="space_complexity.kt" - /* 函数 */ - fun function(): Int { - // 执行某些操作 - return 0 - } + [class]{}-[func]{function} - /* 常数阶 */ - 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)$ {data-toc-label="2.   Linear order"} Linear order is common in arrays, linked lists, stacks, queues, etc., where the number of elements is proportional to $n$: @@ -1147,10 +903,10 @@ Linear order is common in arrays, linked lists, stacks, queues, etc., where the ```python title="space_complexity.py" def linear(n: int): - """线性阶""" - # 长度为 n 的列表占用 O(n) 空间 + """Linear complexity""" + # A list of length n occupies O(n) space nums = [0] * n - # 长度为 n 的哈希表占用 O(n) 空间 + # A hash table of length n occupies O(n) space hmap = dict[int, str]() for i in range(n): hmap[i] = str(i) @@ -1159,36 +915,22 @@ Linear order is common in arrays, linked lists, stacks, queues, etc., where the === "C++" ```cpp title="space_complexity.cpp" - /* 线性阶 */ - void linear(int n) { - // 长度为 n 的数组占用 O(n) 空间 - vector nums(n); - // 长度为 n 的列表占用 O(n) 空间 - vector nodes; - for (int i = 0; i < n; i++) { - nodes.push_back(ListNode(i)); - } - // 长度为 n 的哈希表占用 O(n) 空间 - unordered_map map; - for (int i = 0; i < n; i++) { - map[i] = to_string(i); - } - } + [class]{}-[func]{linear} ``` === "Java" ```java title="space_complexity.java" - /* 线性阶 */ + /* Linear complexity */ void linear(int n) { - // 长度为 n 的数组占用 O(n) 空间 + // Array of length n occupies O(n) space int[] nums = new int[n]; - // 长度为 n 的列表占用 O(n) 空间 + // A list of length n occupies O(n) space List nodes = new ArrayList<>(); for (int i = 0; i < n; i++) { nodes.add(new ListNode(i)); } - // 长度为 n 的哈希表占用 O(n) 空间 + // A hash table of length n occupies O(n) space Map map = new HashMap<>(); for (int i = 0; i < n; i++) { map.put(i, String.valueOf(i)); @@ -1199,259 +941,79 @@ Linear order is common in arrays, linked lists, stacks, queues, etc., where the === "C#" ```csharp title="space_complexity.cs" - /* 线性阶 */ - void Linear(int n) { - // 长度为 n 的数组占用 O(n) 空间 - int[] nums = new int[n]; - // 长度为 n 的列表占用 O(n) 空间 - List nodes = []; - for (int i = 0; i < n; i++) { - nodes.Add(new ListNode(i)); - } - // 长度为 n 的哈希表占用 O(n) 空间 - Dictionary map = []; - for (int i = 0; i < n; i++) { - map.Add(i, i.ToString()); - } - } + [class]{space_complexity}-[func]{Linear} ``` === "Go" ```go title="space_complexity.go" - /* 线性阶 */ - func spaceLinear(n int) { - // 长度为 n 的数组占用 O(n) 空间 - _ = make([]int, n) - // 长度为 n 的列表占用 O(n) 空间 - var nodes []*node - for i := 0; i < n; i++ { - nodes = append(nodes, newNode(i)) - } - // 长度为 n 的哈希表占用 O(n) 空间 - m := make(map[int]string, n) - for i := 0; i < n; i++ { - m[i] = strconv.Itoa(i) - } - } + [class]{}-[func]{spaceLinear} ``` === "Swift" ```swift title="space_complexity.swift" - /* 线性阶 */ - func linear(n: Int) { - // 长度为 n 的数组占用 O(n) 空间 - let nums = Array(repeating: 0, count: n) - // 长度为 n 的列表占用 O(n) 空间 - let nodes = (0 ..< n).map { ListNode(x: $0) } - // 长度为 n 的哈希表占用 O(n) 空间 - let map = Dictionary(uniqueKeysWithValues: (0 ..< n).map { ($0, "\($0)") }) - } + [class]{}-[func]{linear} ``` === "JS" ```javascript title="space_complexity.js" - /* 线性阶 */ - function linear(n) { - // 长度为 n 的数组占用 O(n) 空间 - const nums = new Array(n); - // 长度为 n 的列表占用 O(n) 空间 - const nodes = []; - for (let i = 0; i < n; i++) { - nodes.push(new ListNode(i)); - } - // 长度为 n 的哈希表占用 O(n) 空间 - const map = new Map(); - for (let i = 0; i < n; i++) { - map.set(i, i.toString()); - } - } + [class]{}-[func]{linear} ``` === "TS" ```typescript title="space_complexity.ts" - /* 线性阶 */ - function linear(n: number): void { - // 长度为 n 的数组占用 O(n) 空间 - const nums = new Array(n); - // 长度为 n 的列表占用 O(n) 空间 - const nodes: ListNode[] = []; - for (let i = 0; i < n; i++) { - nodes.push(new ListNode(i)); - } - // 长度为 n 的哈希表占用 O(n) 空间 - const map = new Map(); - for (let i = 0; i < n; i++) { - map.set(i, i.toString()); - } - } + [class]{}-[func]{linear} ``` === "Dart" ```dart title="space_complexity.dart" - /* 线性阶 */ - void linear(int n) { - // 长度为 n 的数组占用 O(n) 空间 - List nums = List.filled(n, 0); - // 长度为 n 的列表占用 O(n) 空间 - List nodes = []; - for (var i = 0; i < n; i++) { - nodes.add(ListNode(i)); - } - // 长度为 n 的哈希表占用 O(n) 空间 - Map map = HashMap(); - for (var i = 0; i < n; i++) { - map.putIfAbsent(i, () => i.toString()); - } - } + [class]{}-[func]{linear} ``` === "Rust" ```rust title="space_complexity.rs" - /* 线性阶 */ - #[allow(unused)] - fn linear(n: i32) { - // 长度为 n 的数组占用 O(n) 空间 - let mut nums = vec![0; n as usize]; - // 长度为 n 的列表占用 O(n) 空间 - let mut nodes = Vec::new(); - for i in 0..n { - nodes.push(ListNode::new(i)) - } - // 长度为 n 的哈希表占用 O(n) 空间 - let mut map = HashMap::new(); - for i in 0..n { - map.insert(i, i.to_string()); - } - } + [class]{}-[func]{linear} ``` === "C" ```c title="space_complexity.c" - /* 哈希表 */ - typedef struct { - int key; - int val; - UT_hash_handle hh; // 基于 uthash.h 实现 - } HashTable; + [class]{HashTable}-[func]{} - /* 线性阶 */ - void linear(int n) { - // 长度为 n 的数组占用 O(n) 空间 - int *nums = malloc(sizeof(int) * n); - free(nums); - - // 长度为 n 的列表占用 O(n) 空间 - ListNode **nodes = malloc(sizeof(ListNode *) * n); - for (int i = 0; i < n; i++) { - nodes[i] = newListNode(i); - } - // 内存释放 - for (int i = 0; i < n; i++) { - free(nodes[i]); - } - free(nodes); - - // 长度为 n 的哈希表占用 O(n) 空间 - HashTable *h = NULL; - for (int i = 0; i < n; i++) { - HashTable *tmp = malloc(sizeof(HashTable)); - tmp->key = i; - tmp->val = i; - HASH_ADD_INT(h, key, tmp); - } - - // 内存释放 - HashTable *curr, *tmp; - HASH_ITER(hh, h, curr, tmp) { - HASH_DEL(h, curr); - free(curr); - } - } + [class]{}-[func]{linear} ``` === "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.. - - As shown in Figure 2-17, this function's recursive depth is $n$, meaning there are $n$ instances of unreturned `linear_recur()` function, using $O(n)$ size of stack frame space: === "Python" ```python title="space_complexity.py" def linear_recur(n: int): - """线性阶(递归实现)""" - print("递归 n =", n) + """Linear complexity (recursive implementation)""" + print("Recursive n =", n) if n == 1: return linear_recur(n - 1) @@ -1460,21 +1022,15 @@ As shown in Figure 2-17, this function's recursive depth is $n$, meaning there a === "C++" ```cpp title="space_complexity.cpp" - /* 线性阶(递归实现) */ - void linearRecur(int n) { - cout << "递归 n = " << n << endl; - if (n == 1) - return; - linearRecur(n - 1); - } + [class]{}-[func]{linearRecur} ``` === "Java" ```java title="space_complexity.java" - /* 线性阶(递归实现) */ + /* Linear complexity (recursive implementation) */ void linearRecur(int n) { - System.out.println("递归 n = " + n); + System.out.println("Recursion n = " + n); if (n == 1) return; linearRecur(n - 1); @@ -1484,137 +1040,69 @@ As shown in Figure 2-17, this function's recursive depth is $n$, meaning there a === "C#" ```csharp title="space_complexity.cs" - /* 线性阶(递归实现) */ - void LinearRecur(int n) { - Console.WriteLine("递归 n = " + n); - if (n == 1) return; - LinearRecur(n - 1); - } + [class]{space_complexity}-[func]{LinearRecur} ``` === "Go" ```go title="space_complexity.go" - /* 线性阶(递归实现) */ - func spaceLinearRecur(n int) { - fmt.Println("递归 n =", n) - if n == 1 { - return - } - spaceLinearRecur(n - 1) - } + [class]{}-[func]{spaceLinearRecur} ``` === "Swift" ```swift title="space_complexity.swift" - /* 线性阶(递归实现) */ - func linearRecur(n: Int) { - print("递归 n = \(n)") - if n == 1 { - return - } - linearRecur(n: n - 1) - } + [class]{}-[func]{linearRecur} ``` === "JS" ```javascript title="space_complexity.js" - /* 线性阶(递归实现) */ - function linearRecur(n) { - console.log(`递归 n = ${n}`); - if (n === 1) return; - linearRecur(n - 1); - } + [class]{}-[func]{linearRecur} ``` === "TS" ```typescript title="space_complexity.ts" - /* 线性阶(递归实现) */ - function linearRecur(n: number): void { - console.log(`递归 n = ${n}`); - if (n === 1) return; - linearRecur(n - 1); - } + [class]{}-[func]{linearRecur} ``` === "Dart" ```dart title="space_complexity.dart" - /* 线性阶(递归实现) */ - void linearRecur(int n) { - print('递归 n = $n'); - if (n == 1) return; - linearRecur(n - 1); - } + [class]{}-[func]{linearRecur} ``` === "Rust" ```rust title="space_complexity.rs" - /* 线性阶(递归实现) */ - fn linear_recur(n: i32) { - println!("递归 n = {}", n); - if n == 1 { - return; - }; - linear_recur(n - 1); - } + [class]{}-[func]{linear_recur} ``` === "C" ```c title="space_complexity.c" - /* 线性阶(递归实现) */ - void linearRecur(int n) { - printf("递归 n = %d\r\n", n); - if (n == 1) - return; - linearRecur(n - 1); - } + [class]{}-[func]{linearRecur} ``` === "Kotlin" ```kotlin title="space_complexity.kt" - /* 线性阶(递归实现) */ - fun linearRecur(n: Int) { - println("递归 n = $n") - if (n == 1) - return - linearRecur(n - 1) - } + [class]{}-[func]{linearRecur} ``` === "Ruby" ```ruby title="space_complexity.rb" - ### 线性阶(递归实现)### - def linear_recur(n) - puts "递归 n = #{n}" - return if n == 1 - linear_recur(n - 1) - end + [class]{}-[func]{linear_recur} ``` === "Zig" ```zig title="space_complexity.zig" - // 线性阶(递归实现) - fn linearRecur(comptime n: i32) void { - std.debug.print("递归 n = {}\n", .{n}); - if (n == 1) return; - linearRecur(n - 1); - } + [class]{}-[func]{linearRecur} ``` -??? pythontutor "Code Visualization" - -
- - ![Recursive function generating linear order space complexity](space_complexity.assets/space_complexity_recursive_linear.png){ class="animation-figure" }

Figure 2-17   Recursive function generating linear order space complexity

@@ -1627,36 +1115,25 @@ Quadratic order is common in matrices and graphs, where the number of elements i ```python title="space_complexity.py" def quadratic(n: int): - """平方阶""" - # 二维列表占用 O(n^2) 空间 + """Quadratic complexity""" + # A two-dimensional list occupies O(n^2) space num_matrix = [[0] * n for _ in range(n)] ``` === "C++" ```cpp title="space_complexity.cpp" - /* 平方阶 */ - void quadratic(int n) { - // 二维列表占用 O(n^2) 空间 - vector> numMatrix; - for (int i = 0; i < n; i++) { - vector tmp; - for (int j = 0; j < n; j++) { - tmp.push_back(0); - } - numMatrix.push_back(tmp); - } - } + [class]{}-[func]{quadratic} ``` === "Java" ```java title="space_complexity.java" - /* 平方阶 */ + /* Quadratic complexity */ void quadratic(int n) { - // 矩阵占用 O(n^2) 空间 + // Matrix occupies O(n^2) space int[][] numMatrix = new int[n][n]; - // 二维列表占用 O(n^2) 空间 + // A two-dimensional list occupies O(n^2) space List> numList = new ArrayList<>(); for (int i = 0; i < n; i++) { List tmp = new ArrayList<>(); @@ -1671,214 +1148,79 @@ Quadratic order is common in matrices and graphs, where the number of elements i === "C#" ```csharp title="space_complexity.cs" - /* 平方阶 */ - void Quadratic(int n) { - // 矩阵占用 O(n^2) 空间 - int[,] numMatrix = new int[n, n]; - // 二维列表占用 O(n^2) 空间 - List> numList = []; - for (int i = 0; i < n; i++) { - List tmp = []; - for (int j = 0; j < n; j++) { - tmp.Add(0); - } - numList.Add(tmp); - } - } + [class]{space_complexity}-[func]{Quadratic} ``` === "Go" ```go title="space_complexity.go" - /* 平方阶 */ - func spaceQuadratic(n int) { - // 矩阵占用 O(n^2) 空间 - numMatrix := make([][]int, n) - for i := 0; i < n; i++ { - numMatrix[i] = make([]int, n) - } - } + [class]{}-[func]{spaceQuadratic} ``` === "Swift" ```swift title="space_complexity.swift" - /* 平方阶 */ - func quadratic(n: Int) { - // 二维列表占用 O(n^2) 空间 - let numList = Array(repeating: Array(repeating: 0, count: n), count: n) - } + [class]{}-[func]{quadratic} ``` === "JS" ```javascript title="space_complexity.js" - /* 平方阶 */ - function quadratic(n) { - // 矩阵占用 O(n^2) 空间 - const numMatrix = Array(n) - .fill(null) - .map(() => Array(n).fill(null)); - // 二维列表占用 O(n^2) 空间 - const numList = []; - for (let i = 0; i < n; i++) { - const tmp = []; - for (let j = 0; j < n; j++) { - tmp.push(0); - } - numList.push(tmp); - } - } + [class]{}-[func]{quadratic} ``` === "TS" ```typescript title="space_complexity.ts" - /* 平方阶 */ - function quadratic(n: number): void { - // 矩阵占用 O(n^2) 空间 - const numMatrix = Array(n) - .fill(null) - .map(() => Array(n).fill(null)); - // 二维列表占用 O(n^2) 空间 - const numList = []; - for (let i = 0; i < n; i++) { - const tmp = []; - for (let j = 0; j < n; j++) { - tmp.push(0); - } - numList.push(tmp); - } - } + [class]{}-[func]{quadratic} ``` === "Dart" ```dart title="space_complexity.dart" - /* 平方阶 */ - void quadratic(int n) { - // 矩阵占用 O(n^2) 空间 - List> numMatrix = List.generate(n, (_) => List.filled(n, 0)); - // 二维列表占用 O(n^2) 空间 - List> numList = []; - for (var i = 0; i < n; i++) { - List tmp = []; - for (int j = 0; j < n; j++) { - tmp.add(0); - } - numList.add(tmp); - } - } + [class]{}-[func]{quadratic} ``` === "Rust" ```rust title="space_complexity.rs" - /* 平方阶 */ - #[allow(unused)] - fn quadratic(n: i32) { - // 矩阵占用 O(n^2) 空间 - let num_matrix = vec![vec![0; n as usize]; n as usize]; - // 二维列表占用 O(n^2) 空间 - let mut num_list = Vec::new(); - for i in 0..n { - let mut tmp = Vec::new(); - for j in 0..n { - tmp.push(0); - } - num_list.push(tmp); - } - } + [class]{}-[func]{quadratic} ``` === "C" ```c title="space_complexity.c" - /* 平方阶 */ - void quadratic(int n) { - // 二维列表占用 O(n^2) 空间 - int **numMatrix = malloc(sizeof(int *) * n); - for (int i = 0; i < n; i++) { - int *tmp = malloc(sizeof(int) * n); - for (int j = 0; j < n; j++) { - tmp[j] = 0; - } - numMatrix[i] = tmp; - } - - // 内存释放 - for (int i = 0; i < n; i++) { - free(numMatrix[i]); - } - free(numMatrix); - } + [class]{}-[func]{quadratic} ``` === "Kotlin" ```kotlin title="space_complexity.kt" - /* 平方阶 */ - fun quadratic(n: Int) { - // 矩阵占用 O(n^2) 空间 - val numMatrix = arrayOfNulls?>(n) - // 二维列表占用 O(n^2) 空间 - val numList = mutableListOf>() - for (i in 0..() - for (j in 0.. - - As shown in Figure 2-18, the recursive depth of this function is $n$, and in each recursive call, an array is initialized with lengths $n$, $n-1$, $\dots$, $2$, $1$, averaging $n/2$, thus overall occupying $O(n^2)$ space: === "Python" ```python title="space_complexity.py" def quadratic_recur(n: int) -> int: - """平方阶(递归实现)""" + """Quadratic complexity (recursive implementation)""" if n <= 0: return 0 - # 数组 nums 长度为 n, n-1, ..., 2, 1 + # Array nums length = n, n-1, ..., 2, 1 nums = [0] * n return quadratic_recur(n - 1) ``` @@ -1886,26 +1228,19 @@ As shown in Figure 2-18, the recursive depth of this function is $n$, and in eac === "C++" ```cpp title="space_complexity.cpp" - /* 平方阶(递归实现) */ - int quadraticRecur(int n) { - if (n <= 0) - return 0; - vector nums(n); - cout << "递归 n = " << n << " 中的 nums 长度 = " << nums.size() << endl; - return quadraticRecur(n - 1); - } + [class]{}-[func]{quadraticRecur} ``` === "Java" ```java title="space_complexity.java" - /* 平方阶(递归实现) */ + /* Quadratic complexity (recursive implementation) */ int quadraticRecur(int n) { if (n <= 0) return 0; - // 数组 nums 长度为 n, n-1, ..., 2, 1 + // Array nums length = n, n-1, ..., 2, 1 int[] nums = new int[n]; - System.out.println("递归 n = " + n + " 中的 nums 长度 = " + nums.length); + System.out.println("Recursion n = " + n + " in the length of nums = " + nums.length); return quadraticRecur(n - 1); } ``` @@ -1913,155 +1248,69 @@ As shown in Figure 2-18, the recursive depth of this function is $n$, and in eac === "C#" ```csharp title="space_complexity.cs" - /* 平方阶(递归实现) */ - int QuadraticRecur(int n) { - if (n <= 0) return 0; - int[] nums = new int[n]; - Console.WriteLine("递归 n = " + n + " 中的 nums 长度 = " + nums.Length); - return QuadraticRecur(n - 1); - } + [class]{space_complexity}-[func]{QuadraticRecur} ``` === "Go" ```go title="space_complexity.go" - /* 平方阶(递归实现) */ - func spaceQuadraticRecur(n int) int { - if n <= 0 { - return 0 - } - nums := make([]int, n) - fmt.Printf("递归 n = %d 中的 nums 长度 = %d \n", n, len(nums)) - return spaceQuadraticRecur(n - 1) - } + [class]{}-[func]{spaceQuadraticRecur} ``` === "Swift" ```swift title="space_complexity.swift" - /* 平方阶(递归实现) */ - @discardableResult - func quadraticRecur(n: Int) -> Int { - if n <= 0 { - return 0 - } - // 数组 nums 长度为 n, n-1, ..., 2, 1 - let nums = Array(repeating: 0, count: n) - print("递归 n = \(n) 中的 nums 长度 = \(nums.count)") - return quadraticRecur(n: n - 1) - } + [class]{}-[func]{quadraticRecur} ``` === "JS" ```javascript title="space_complexity.js" - /* 平方阶(递归实现) */ - function quadraticRecur(n) { - if (n <= 0) return 0; - const nums = new Array(n); - console.log(`递归 n = ${n} 中的 nums 长度 = ${nums.length}`); - return quadraticRecur(n - 1); - } + [class]{}-[func]{quadraticRecur} ``` === "TS" ```typescript title="space_complexity.ts" - /* 平方阶(递归实现) */ - function quadraticRecur(n: number): number { - if (n <= 0) return 0; - const nums = new Array(n); - console.log(`递归 n = ${n} 中的 nums 长度 = ${nums.length}`); - return quadraticRecur(n - 1); - } + [class]{}-[func]{quadraticRecur} ``` === "Dart" ```dart title="space_complexity.dart" - /* 平方阶(递归实现) */ - int quadraticRecur(int n) { - if (n <= 0) return 0; - List nums = List.filled(n, 0); - print('递归 n = $n 中的 nums 长度 = ${nums.length}'); - return quadraticRecur(n - 1); - } + [class]{}-[func]{quadraticRecur} ``` === "Rust" ```rust title="space_complexity.rs" - /* 平方阶(递归实现) */ - fn quadratic_recur(n: i32) -> i32 { - if n <= 0 { - return 0; - }; - // 数组 nums 长度为 n, n-1, ..., 2, 1 - let nums = vec![0; n as usize]; - println!("递归 n = {} 中的 nums 长度 = {}", n, nums.len()); - return quadratic_recur(n - 1); - } + [class]{}-[func]{quadratic_recur} ``` === "C" ```c title="space_complexity.c" - /* 平方阶(递归实现) */ - int quadraticRecur(int n) { - if (n <= 0) - return 0; - int *nums = malloc(sizeof(int) * n); - printf("递归 n = %d 中的 nums 长度 = %d\r\n", n, n); - int res = quadraticRecur(n - 1); - free(nums); - return res; - } + [class]{}-[func]{quadraticRecur} ``` === "Kotlin" ```kotlin title="space_complexity.kt" - /* 平方阶(递归实现) */ - tailrec fun quadraticRecur(n: Int): Int { - if (n <= 0) - return 0 - // 数组 nums 长度为 n, n-1, ..., 2, 1 - val nums = Array(n) { 0 } - println("递归 n = $n 中的 nums 长度 = ${nums.size}") - return quadraticRecur(n - 1) - } + [class]{}-[func]{quadraticRecur} ``` === "Ruby" ```ruby title="space_complexity.rb" - ### 平方阶(递归实现)### - def quadratic_recur(n) - return 0 unless n > 0 - - # 数组 nums 长度为 n, n-1, ..., 2, 1 - nums = Array.new(n, 0) - quadratic_recur(n - 1) - end + [class]{}-[func]{quadratic_recur} ``` === "Zig" ```zig title="space_complexity.zig" - // 平方阶(递归实现) - fn quadraticRecur(comptime n: i32) i32 { - if (n <= 0) return 0; - var nums = [_]i32{0}**n; - std.debug.print("递归 n = {} 中的 nums 长度 = {}\n", .{n, nums.len}); - return quadraticRecur(n - 1); - } + [class]{}-[func]{quadraticRecur} ``` -??? pythontutor "Code Visualization" - -
- - ![Recursive function generating quadratic order space complexity](space_complexity.assets/space_complexity_recursive_quadratic.png){ class="animation-figure" }

Figure 2-18   Recursive function generating quadratic order space complexity

@@ -2074,7 +1323,7 @@ Exponential order is common in binary trees. Observe Figure 2-19, a "full binary ```python title="space_complexity.py" def build_tree(n: int) -> TreeNode | None: - """指数阶(建立满二叉树)""" + """Exponential complexity (building a full binary tree)""" if n == 0: return None root = TreeNode(0) @@ -2086,21 +1335,13 @@ Exponential order is common in binary trees. Observe Figure 2-19, a "full binary === "C++" ```cpp title="space_complexity.cpp" - /* 指数阶(建立满二叉树) */ - TreeNode *buildTree(int n) { - if (n == 0) - return nullptr; - TreeNode *root = new TreeNode(0); - root->left = buildTree(n - 1); - root->right = buildTree(n - 1); - return root; - } + [class]{}-[func]{buildTree} ``` === "Java" ```java title="space_complexity.java" - /* 指数阶(建立满二叉树) */ + /* Exponential complexity (building a full binary tree) */ TreeNode buildTree(int n) { if (n == 0) return null; @@ -2114,162 +1355,69 @@ Exponential order is common in binary trees. Observe Figure 2-19, a "full binary === "C#" ```csharp title="space_complexity.cs" - /* 指数阶(建立满二叉树) */ - TreeNode? BuildTree(int n) { - if (n == 0) return null; - TreeNode root = new(0) { - left = BuildTree(n - 1), - right = BuildTree(n - 1) - }; - return root; - } + [class]{space_complexity}-[func]{BuildTree} ``` === "Go" ```go title="space_complexity.go" - /* 指数阶(建立满二叉树) */ - func buildTree(n int) *TreeNode { - if n == 0 { - return nil - } - root := NewTreeNode(0) - root.Left = buildTree(n - 1) - root.Right = buildTree(n - 1) - return root - } + [class]{}-[func]{buildTree} ``` === "Swift" ```swift title="space_complexity.swift" - /* 指数阶(建立满二叉树) */ - func buildTree(n: Int) -> TreeNode? { - if n == 0 { - return nil - } - let root = TreeNode(x: 0) - root.left = buildTree(n: n - 1) - root.right = buildTree(n: n - 1) - return root - } + [class]{}-[func]{buildTree} ``` === "JS" ```javascript title="space_complexity.js" - /* 指数阶(建立满二叉树) */ - function buildTree(n) { - if (n === 0) return null; - const root = new TreeNode(0); - root.left = buildTree(n - 1); - root.right = buildTree(n - 1); - return root; - } + [class]{}-[func]{buildTree} ``` === "TS" ```typescript title="space_complexity.ts" - /* 指数阶(建立满二叉树) */ - function buildTree(n: number): TreeNode | null { - if (n === 0) return null; - const root = new TreeNode(0); - root.left = buildTree(n - 1); - root.right = buildTree(n - 1); - return root; - } + [class]{}-[func]{buildTree} ``` === "Dart" ```dart title="space_complexity.dart" - /* 指数阶(建立满二叉树) */ - TreeNode? buildTree(int n) { - if (n == 0) return null; - TreeNode root = TreeNode(0); - root.left = buildTree(n - 1); - root.right = buildTree(n - 1); - return root; - } + [class]{}-[func]{buildTree} ``` === "Rust" ```rust title="space_complexity.rs" - /* 指数阶(建立满二叉树) */ - fn build_tree(n: i32) -> Option>> { - if n == 0 { - return None; - }; - let root = TreeNode::new(0); - root.borrow_mut().left = build_tree(n - 1); - root.borrow_mut().right = build_tree(n - 1); - return Some(root); - } + [class]{}-[func]{build_tree} ``` === "C" ```c title="space_complexity.c" - /* 指数阶(建立满二叉树) */ - TreeNode *buildTree(int n) { - if (n == 0) - return NULL; - TreeNode *root = newTreeNode(0); - root->left = buildTree(n - 1); - root->right = buildTree(n - 1); - return root; - } + [class]{}-[func]{buildTree} ``` === "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 - } + [class]{}-[func]{buildTree} ``` === "Ruby" ```ruby title="space_complexity.rb" - ### 指数阶(建立满二叉树)### - def build_tree(n) - return if n == 0 - - TreeNode.new.tap do |root| - root.left = build_tree(n - 1) - root.right = build_tree(n - 1) - end - end + [class]{}-[func]{build_tree} ``` === "Zig" ```zig title="space_complexity.zig" - // 指数阶(建立满二叉树) - fn buildTree(mem_allocator: std.mem.Allocator, n: i32) !?*inc.TreeNode(i32) { - if (n == 0) return null; - const root = try mem_allocator.create(inc.TreeNode(i32)); - root.init(0); - root.left = try buildTree(mem_allocator, n - 1); - root.right = try buildTree(mem_allocator, n - 1); - return root; - } + [class]{}-[func]{buildTree} ``` -??? pythontutor "Code Visualization" - -
- - ![Full binary tree generating exponential order space complexity](space_complexity.assets/space_complexity_exponential.png){ class="animation-figure" }

Figure 2-19   Full binary tree generating exponential order space complexity

diff --git a/en/docs/chapter_computational_complexity/time_complexity.md b/en/docs/chapter_computational_complexity/time_complexity.md index 614096f3e..f9ae785ce 100644 --- a/en/docs/chapter_computational_complexity/time_complexity.md +++ b/en/docs/chapter_computational_complexity/time_complexity.md @@ -984,7 +984,7 @@ Constant order means the number of operations is independent of the input data s ```python title="time_complexity.py" def constant(n: int) -> int: - """常数阶""" + """Constant complexity""" count = 0 size = 100000 for _ in range(size): @@ -995,20 +995,13 @@ Constant order means the number of operations is independent of the input data s === "C++" ```cpp title="time_complexity.cpp" - /* 常数阶 */ - int constant(int n) { - int count = 0; - int size = 100000; - for (int i = 0; i < size; i++) - count++; - return count; - } + [class]{}-[func]{constant} ``` === "Java" ```java title="time_complexity.java" - /* 常数阶 */ + /* Constant complexity */ int constant(int n) { int count = 0; int size = 100000; @@ -1021,160 +1014,69 @@ Constant order means the number of operations is independent of the input data s === "C#" ```csharp title="time_complexity.cs" - /* 常数阶 */ - int Constant(int n) { - int count = 0; - int size = 100000; - for (int i = 0; i < size; i++) - count++; - return count; - } + [class]{time_complexity}-[func]{Constant} ``` === "Go" ```go title="time_complexity.go" - /* 常数阶 */ - func constant(n int) int { - count := 0 - size := 100000 - for i := 0; i < size; i++ { - count++ - } - return count - } + [class]{}-[func]{constant} ``` === "Swift" ```swift title="time_complexity.swift" - /* 常数阶 */ - func constant(n: Int) -> Int { - var count = 0 - let size = 100_000 - for _ in 0 ..< size { - count += 1 - } - return count - } + [class]{}-[func]{constant} ``` === "JS" ```javascript title="time_complexity.js" - /* 常数阶 */ - function constant(n) { - let count = 0; - const size = 100000; - for (let i = 0; i < size; i++) count++; - return count; - } + [class]{}-[func]{constant} ``` === "TS" ```typescript title="time_complexity.ts" - /* 常数阶 */ - function constant(n: number): number { - let count = 0; - const size = 100000; - for (let i = 0; i < size; i++) count++; - return count; - } + [class]{}-[func]{constant} ``` === "Dart" ```dart title="time_complexity.dart" - /* 常数阶 */ - int constant(int n) { - int count = 0; - int size = 100000; - for (var i = 0; i < size; i++) { - count++; - } - return count; - } + [class]{}-[func]{constant} ``` === "Rust" ```rust title="time_complexity.rs" - /* 常数阶 */ - fn constant(n: i32) -> i32 { - _ = n; - let mut count = 0; - let size = 100_000; - for _ in 0..size { - count += 1; - } - count - } + [class]{}-[func]{constant} ``` === "C" ```c title="time_complexity.c" - /* 常数阶 */ - int constant(int n) { - int count = 0; - int size = 100000; - int i = 0; - for (int i = 0; i < size; i++) { - count++; - } - return count; - } + [class]{}-[func]{constant} ``` === "Kotlin" ```kotlin title="time_complexity.kt" - /* 常数阶 */ - fun constant(n: Int): Int { - var count = 0 - val size = 100000 - for (i in 0.. - - ### 2.   Linear order $O(n)$ {data-toc-label="2.   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: @@ -1183,7 +1085,7 @@ Linear order indicates the number of operations grows linearly with the input da ```python title="time_complexity.py" def linear(n: int) -> int: - """线性阶""" + """Linear complexity""" count = 0 for _ in range(n): count += 1 @@ -1193,19 +1095,13 @@ Linear order indicates the number of operations grows linearly with the input da === "C++" ```cpp title="time_complexity.cpp" - /* 线性阶 */ - int linear(int n) { - int count = 0; - for (int i = 0; i < n; i++) - count++; - return count; - } + [class]{}-[func]{linear} ``` === "Java" ```java title="time_complexity.java" - /* 线性阶 */ + /* Linear complexity */ int linear(int n) { int count = 0; for (int i = 0; i < n; i++) @@ -1217,153 +1113,78 @@ Linear order indicates the number of operations grows linearly with the input da === "C#" ```csharp title="time_complexity.cs" - /* 线性阶 */ - int Linear(int n) { - int count = 0; - for (int i = 0; i < n; i++) - count++; - return count; - } + [class]{time_complexity}-[func]{Linear} ``` === "Go" ```go title="time_complexity.go" - /* 线性阶 */ - func linear(n int) int { - count := 0 - for i := 0; i < n; i++ { - count++ - } - return count - } + [class]{}-[func]{linear} ``` === "Swift" ```swift title="time_complexity.swift" - /* 线性阶 */ - func linear(n: Int) -> Int { - var count = 0 - for _ in 0 ..< n { - count += 1 - } - return count - } + [class]{}-[func]{linear} ``` === "JS" ```javascript title="time_complexity.js" - /* 线性阶 */ - function linear(n) { - let count = 0; - for (let i = 0; i < n; i++) count++; - return count; - } + [class]{}-[func]{linear} ``` === "TS" ```typescript title="time_complexity.ts" - /* 线性阶 */ - function linear(n: number): number { - let count = 0; - for (let i = 0; i < n; i++) count++; - return count; - } + [class]{}-[func]{linear} ``` === "Dart" ```dart title="time_complexity.dart" - /* 线性阶 */ - int linear(int n) { - int count = 0; - for (var i = 0; i < n; i++) { - count++; - } - return count; - } + [class]{}-[func]{linear} ``` === "Rust" ```rust title="time_complexity.rs" - /* 线性阶 */ - fn linear(n: i32) -> i32 { - let mut count = 0; - for _ in 0..n { - count += 1; - } - count - } + [class]{}-[func]{linear} ``` === "C" ```c title="time_complexity.c" - /* 线性阶 */ - int linear(int n) { - int count = 0; - for (int i = 0; i < n; i++) { - count++; - } - return count; - } + [class]{}-[func]{linear} ``` === "Kotlin" ```kotlin title="time_complexity.kt" - /* 线性阶 */ - fun linear(n: Int): Int { - var count = 0 - for (i in 0.. - - Operations like array traversal and linked list traversal have a time complexity of $O(n)$, where $n$ is the length of the array or list: === "Python" ```python title="time_complexity.py" def array_traversal(nums: list[int]) -> int: - """线性阶(遍历数组)""" + """Linear complexity (traversing an array)""" count = 0 - # 循环次数与数组长度成正比 + # Loop count is proportional to the length of the array for num in nums: count += 1 return count @@ -1372,24 +1193,16 @@ Operations like array traversal and linked list traversal have a time complexity === "C++" ```cpp title="time_complexity.cpp" - /* 线性阶(遍历数组) */ - int arrayTraversal(vector &nums) { - int count = 0; - // 循环次数与数组长度成正比 - for (int num : nums) { - count++; - } - return count; - } + [class]{}-[func]{arrayTraversal} ``` === "Java" ```java title="time_complexity.java" - /* 线性阶(遍历数组) */ + /* Linear complexity (traversing an array) */ int arrayTraversal(int[] nums) { int count = 0; - // 循环次数与数组长度成正比 + // Loop count is proportional to the length of the array for (int num : nums) { count++; } @@ -1400,164 +1213,69 @@ Operations like array traversal and linked list traversal have a time complexity === "C#" ```csharp title="time_complexity.cs" - /* 线性阶(遍历数组) */ - int ArrayTraversal(int[] nums) { - int count = 0; - // 循环次数与数组长度成正比 - foreach (int num in nums) { - count++; - } - return count; - } + [class]{time_complexity}-[func]{ArrayTraversal} ``` === "Go" ```go title="time_complexity.go" - /* 线性阶(遍历数组) */ - func arrayTraversal(nums []int) int { - count := 0 - // 循环次数与数组长度成正比 - for range nums { - count++ - } - return count - } + [class]{}-[func]{arrayTraversal} ``` === "Swift" ```swift title="time_complexity.swift" - /* 线性阶(遍历数组) */ - func arrayTraversal(nums: [Int]) -> Int { - var count = 0 - // 循环次数与数组长度成正比 - for _ in nums { - count += 1 - } - return count - } + [class]{}-[func]{arrayTraversal} ``` === "JS" ```javascript title="time_complexity.js" - /* 线性阶(遍历数组) */ - function arrayTraversal(nums) { - let count = 0; - // 循环次数与数组长度成正比 - for (let i = 0; i < nums.length; i++) { - count++; - } - return count; - } + [class]{}-[func]{arrayTraversal} ``` === "TS" ```typescript title="time_complexity.ts" - /* 线性阶(遍历数组) */ - function arrayTraversal(nums: number[]): number { - let count = 0; - // 循环次数与数组长度成正比 - for (let i = 0; i < nums.length; i++) { - count++; - } - return count; - } + [class]{}-[func]{arrayTraversal} ``` === "Dart" ```dart title="time_complexity.dart" - /* 线性阶(遍历数组) */ - int arrayTraversal(List nums) { - int count = 0; - // 循环次数与数组长度成正比 - for (var _num in nums) { - count++; - } - return count; - } + [class]{}-[func]{arrayTraversal} ``` === "Rust" ```rust title="time_complexity.rs" - /* 线性阶(遍历数组) */ - fn array_traversal(nums: &[i32]) -> i32 { - let mut count = 0; - // 循环次数与数组长度成正比 - for _ in nums { - count += 1; - } - count - } + [class]{}-[func]{array_traversal} ``` === "C" ```c title="time_complexity.c" - /* 线性阶(遍历数组) */ - int arrayTraversal(int *nums, int n) { - int count = 0; - // 循环次数与数组长度成正比 - for (int i = 0; i < n; i++) { - count++; - } - return count; - } + [class]{}-[func]{arrayTraversal} ``` === "Kotlin" ```kotlin title="time_complexity.kt" - /* 线性阶(遍历数组) */ - fun arrayTraversal(nums: IntArray): Int { - var count = 0 - // 循环次数与数组长度成正比 - for (num in nums) { - count++ - } - return count - } + [class]{}-[func]{arrayTraversal} ``` === "Ruby" ```ruby title="time_complexity.rb" - ### 线性阶(遍历数组)### - def array_traversal(nums) - count = 0 - - # 循环次数与数组长度成正比 - for num in nums - count += 1 - end - - count - end + [class]{}-[func]{array_traversal} ``` === "Zig" ```zig title="time_complexity.zig" - // 线性阶(遍历数组) - fn arrayTraversal(nums: []i32) i32 { - var count: i32 = 0; - // 循环次数与数组长度成正比 - for (nums) |_| { - count += 1; - } - return count; - } + [class]{}-[func]{arrayTraversal} ``` -??? pythontutor "Code Visualization" - -
- - It's important to note that **the input data size $n$ should be determined based on the type of input data**. For example, in the first example, $n$ represents the input data size, while in the second example, the length of the array $n$ is the data size. ### 3.   Quadratic order $O(n^2)$ {data-toc-label="3.   Quadratic order"} @@ -1568,9 +1286,9 @@ Quadratic order means the number of operations grows quadratically with the inpu ```python title="time_complexity.py" def quadratic(n: int) -> int: - """平方阶""" + """Quadratic complexity""" count = 0 - # 循环次数与数据大小 n 成平方关系 + # Loop count is squared in relation to the data size n for i in range(n): for j in range(n): count += 1 @@ -1580,26 +1298,16 @@ Quadratic order means the number of operations grows quadratically with the inpu === "C++" ```cpp title="time_complexity.cpp" - /* 平方阶 */ - int quadratic(int n) { - int count = 0; - // 循环次数与数据大小 n 成平方关系 - for (int i = 0; i < n; i++) { - for (int j = 0; j < n; j++) { - count++; - } - } - return count; - } + [class]{}-[func]{quadratic} ``` === "Java" ```java title="time_complexity.java" - /* 平方阶 */ + /* Quadratic complexity */ int quadratic(int n) { int count = 0; - // 循环次数与数据大小 n 成平方关系 + // Loop count is squared in relation to the data size n for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { count++; @@ -1612,188 +1320,69 @@ Quadratic order means the number of operations grows quadratically with the inpu === "C#" ```csharp title="time_complexity.cs" - /* 平方阶 */ - int Quadratic(int n) { - int count = 0; - // 循环次数与数据大小 n 成平方关系 - for (int i = 0; i < n; i++) { - for (int j = 0; j < n; j++) { - count++; - } - } - return count; - } + [class]{time_complexity}-[func]{Quadratic} ``` === "Go" ```go title="time_complexity.go" - /* 平方阶 */ - func quadratic(n int) int { - count := 0 - // 循环次数与数据大小 n 成平方关系 - for i := 0; i < n; i++ { - for j := 0; j < n; j++ { - count++ - } - } - return count - } + [class]{}-[func]{quadratic} ``` === "Swift" ```swift title="time_complexity.swift" - /* 平方阶 */ - func quadratic(n: Int) -> Int { - var count = 0 - // 循环次数与数据大小 n 成平方关系 - for _ in 0 ..< n { - for _ in 0 ..< n { - count += 1 - } - } - return count - } + [class]{}-[func]{quadratic} ``` === "JS" ```javascript title="time_complexity.js" - /* 平方阶 */ - function quadratic(n) { - let count = 0; - // 循环次数与数据大小 n 成平方关系 - for (let i = 0; i < n; i++) { - for (let j = 0; j < n; j++) { - count++; - } - } - return count; - } + [class]{}-[func]{quadratic} ``` === "TS" ```typescript title="time_complexity.ts" - /* 平方阶 */ - function quadratic(n: number): number { - let count = 0; - // 循环次数与数据大小 n 成平方关系 - for (let i = 0; i < n; i++) { - for (let j = 0; j < n; j++) { - count++; - } - } - return count; - } + [class]{}-[func]{quadratic} ``` === "Dart" ```dart title="time_complexity.dart" - /* 平方阶 */ - int quadratic(int n) { - int count = 0; - // 循环次数与数据大小 n 成平方关系 - for (int i = 0; i < n; i++) { - for (int j = 0; j < n; j++) { - count++; - } - } - return count; - } + [class]{}-[func]{quadratic} ``` === "Rust" ```rust title="time_complexity.rs" - /* 平方阶 */ - fn quadratic(n: i32) -> i32 { - let mut count = 0; - // 循环次数与数据大小 n 成平方关系 - for _ in 0..n { - for _ in 0..n { - count += 1; - } - } - count - } + [class]{}-[func]{quadratic} ``` === "C" ```c title="time_complexity.c" - /* 平方阶 */ - int quadratic(int n) { - int count = 0; - // 循环次数与数据大小 n 成平方关系 - for (int i = 0; i < n; i++) { - for (int j = 0; j < n; j++) { - count++; - } - } - return count; - } + [class]{}-[func]{quadratic} ``` === "Kotlin" ```kotlin title="time_complexity.kt" - /* 平方阶 */ - fun quadratic(n: Int): Int { - var count = 0 - // 循环次数与数据大小 n 成平方关系 - for (i in 0.. - - Figure 2-10 compares constant order, linear order, and quadratic order time complexities. ![Constant, linear, and quadratic order time complexities](time_complexity.assets/time_complexity_constant_linear_quadratic.png){ class="animation-figure" } @@ -1806,60 +1395,43 @@ For instance, in bubble sort, the outer loop runs $n - 1$ times, and the inner l ```python title="time_complexity.py" def bubble_sort(nums: list[int]) -> int: - """平方阶(冒泡排序)""" - count = 0 # 计数器 - # 外循环:未排序区间为 [0, i] + """Quadratic complexity (bubble sort)""" + count = 0 # Counter + # Outer loop: unsorted range is [0, i] for i in range(len(nums) - 1, 0, -1): - # 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端 + # Inner loop: swap the largest element in the unsorted range [0, i] to the right end of the range for j in range(i): if nums[j] > nums[j + 1]: - # 交换 nums[j] 与 nums[j + 1] + # Swap nums[j] and nums[j + 1] tmp: int = nums[j] nums[j] = nums[j + 1] nums[j + 1] = tmp - count += 3 # 元素交换包含 3 个单元操作 + count += 3 # Element swap includes 3 individual operations return count ``` === "C++" ```cpp title="time_complexity.cpp" - /* 平方阶(冒泡排序) */ - int bubbleSort(vector &nums) { - int count = 0; // 计数器 - // 外循环:未排序区间为 [0, i] - for (int i = nums.size() - 1; i > 0; i--) { - // 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端 - for (int j = 0; j < i; j++) { - if (nums[j] > nums[j + 1]) { - // 交换 nums[j] 与 nums[j + 1] - int tmp = nums[j]; - nums[j] = nums[j + 1]; - nums[j + 1] = tmp; - count += 3; // 元素交换包含 3 个单元操作 - } - } - } - return count; - } + [class]{}-[func]{bubbleSort} ``` === "Java" ```java title="time_complexity.java" - /* 平方阶(冒泡排序) */ + /* Quadratic complexity (bubble sort) */ int bubbleSort(int[] nums) { - int count = 0; // 计数器 - // 外循环:未排序区间为 [0, i] + int count = 0; // Counter + // Outer loop: unsorted range is [0, i] for (int i = nums.length - 1; i > 0; i--) { - // 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端 + // Inner loop: swap the largest element in the unsorted range [0, i] to the right end of the range for (int j = 0; j < i; j++) { if (nums[j] > nums[j + 1]) { - // 交换 nums[j] 与 nums[j + 1] + // Swap nums[j] and nums[j + 1] int tmp = nums[j]; nums[j] = nums[j + 1]; nums[j + 1] = tmp; - count += 3; // 元素交换包含 3 个单元操作 + count += 3; // Element swap includes 3 individual operations } } } @@ -1870,264 +1442,69 @@ For instance, in bubble sort, the outer loop runs $n - 1$ times, and the inner l === "C#" ```csharp title="time_complexity.cs" - /* 平方阶(冒泡排序) */ - int BubbleSort(int[] nums) { - int count = 0; // 计数器 - // 外循环:未排序区间为 [0, i] - for (int i = nums.Length - 1; i > 0; i--) { - // 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端 - for (int j = 0; j < i; j++) { - if (nums[j] > nums[j + 1]) { - // 交换 nums[j] 与 nums[j + 1] - (nums[j + 1], nums[j]) = (nums[j], nums[j + 1]); - count += 3; // 元素交换包含 3 个单元操作 - } - } - } - return count; - } + [class]{time_complexity}-[func]{BubbleSort} ``` === "Go" ```go title="time_complexity.go" - /* 平方阶(冒泡排序) */ - func bubbleSort(nums []int) int { - count := 0 // 计数器 - // 外循环:未排序区间为 [0, i] - for i := len(nums) - 1; i > 0; i-- { - // 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端 - for j := 0; j < i; j++ { - if nums[j] > nums[j+1] { - // 交换 nums[j] 与 nums[j + 1] - tmp := nums[j] - nums[j] = nums[j+1] - nums[j+1] = tmp - count += 3 // 元素交换包含 3 个单元操作 - } - } - } - return count - } + [class]{}-[func]{bubbleSort} ``` === "Swift" ```swift title="time_complexity.swift" - /* 平方阶(冒泡排序) */ - func bubbleSort(nums: inout [Int]) -> Int { - var count = 0 // 计数器 - // 外循环:未排序区间为 [0, i] - for i in nums.indices.dropFirst().reversed() { - // 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端 - for j in 0 ..< i { - if nums[j] > nums[j + 1] { - // 交换 nums[j] 与 nums[j + 1] - let tmp = nums[j] - nums[j] = nums[j + 1] - nums[j + 1] = tmp - count += 3 // 元素交换包含 3 个单元操作 - } - } - } - return count - } + [class]{}-[func]{bubbleSort} ``` === "JS" ```javascript title="time_complexity.js" - /* 平方阶(冒泡排序) */ - function bubbleSort(nums) { - let count = 0; // 计数器 - // 外循环:未排序区间为 [0, i] - for (let i = nums.length - 1; i > 0; i--) { - // 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端 - for (let j = 0; j < i; j++) { - if (nums[j] > nums[j + 1]) { - // 交换 nums[j] 与 nums[j + 1] - let tmp = nums[j]; - nums[j] = nums[j + 1]; - nums[j + 1] = tmp; - count += 3; // 元素交换包含 3 个单元操作 - } - } - } - return count; - } + [class]{}-[func]{bubbleSort} ``` === "TS" ```typescript title="time_complexity.ts" - /* 平方阶(冒泡排序) */ - function bubbleSort(nums: number[]): number { - let count = 0; // 计数器 - // 外循环:未排序区间为 [0, i] - for (let i = nums.length - 1; i > 0; i--) { - // 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端 - for (let j = 0; j < i; j++) { - if (nums[j] > nums[j + 1]) { - // 交换 nums[j] 与 nums[j + 1] - let tmp = nums[j]; - nums[j] = nums[j + 1]; - nums[j + 1] = tmp; - count += 3; // 元素交换包含 3 个单元操作 - } - } - } - return count; - } + [class]{}-[func]{bubbleSort} ``` === "Dart" ```dart title="time_complexity.dart" - /* 平方阶(冒泡排序) */ - int bubbleSort(List nums) { - int count = 0; // 计数器 - // 外循环:未排序区间为 [0, i] - for (var i = nums.length - 1; i > 0; i--) { - // 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端 - for (var j = 0; j < i; j++) { - if (nums[j] > nums[j + 1]) { - // 交换 nums[j] 与 nums[j + 1] - int tmp = nums[j]; - nums[j] = nums[j + 1]; - nums[j + 1] = tmp; - count += 3; // 元素交换包含 3 个单元操作 - } - } - } - return count; - } + [class]{}-[func]{bubbleSort} ``` === "Rust" ```rust title="time_complexity.rs" - /* 平方阶(冒泡排序) */ - fn bubble_sort(nums: &mut [i32]) -> i32 { - let mut count = 0; // 计数器 - - // 外循环:未排序区间为 [0, i] - for i in (1..nums.len()).rev() { - // 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端 - for j in 0..i { - if nums[j] > nums[j + 1] { - // 交换 nums[j] 与 nums[j + 1] - let tmp = nums[j]; - nums[j] = nums[j + 1]; - nums[j + 1] = tmp; - count += 3; // 元素交换包含 3 个单元操作 - } - } - } - count - } + [class]{}-[func]{bubble_sort} ``` === "C" ```c title="time_complexity.c" - /* 平方阶(冒泡排序) */ - int bubbleSort(int *nums, int n) { - int count = 0; // 计数器 - // 外循环:未排序区间为 [0, i] - for (int i = n - 1; i > 0; i--) { - // 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端 - for (int j = 0; j < i; j++) { - if (nums[j] > nums[j + 1]) { - // 交换 nums[j] 与 nums[j + 1] - int tmp = nums[j]; - nums[j] = nums[j + 1]; - nums[j + 1] = tmp; - count += 3; // 元素交换包含 3 个单元操作 - } - } - } - return count; - } + [class]{}-[func]{bubbleSort} ``` === "Kotlin" ```kotlin title="time_complexity.kt" - /* 平方阶(冒泡排序) */ - fun bubbleSort(nums: IntArray): Int { - var count = 0 // 计数器 - // 外循环:未排序区间为 [0, i] - for (i in nums.size - 1 downTo 1) { - // 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端 - for (j in 0.. nums[j + 1]) { - // 交换 nums[j] 与 nums[j + 1] - val temp = nums[j] - nums[j] = nums[j + 1] - nums[j + 1] = temp - count += 3 // 元素交换包含 3 个单元操作 - } - } - } - return count - } + [class]{}-[func]{bubbleSort} ``` === "Ruby" ```ruby title="time_complexity.rb" - ### 平方阶(冒泡排序)### - def bubble_sort(nums) - count = 0 # 计数器 - - # 外循环:未排序区间为 [0, i] - for i in (nums.length - 1).downto(0) - # 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端 - for j in 0...i - if nums[j] > nums[j + 1] - # 交换 nums[j] 与 nums[j + 1] - tmp = nums[j] - nums[j] = nums[j + 1] - nums[j + 1] = tmp - count += 3 # 元素交换包含 3 个单元操作 - end - end - end - - count - end + [class]{}-[func]{bubble_sort} ``` === "Zig" ```zig title="time_complexity.zig" - // 平方阶(冒泡排序) - fn bubbleSort(nums: []i32) i32 { - var count: i32 = 0; // 计数器 - // 外循环:未排序区间为 [0, i] - var i: i32 = @as(i32, @intCast(nums.len)) - 1; - while (i > 0) : (i -= 1) { - var j: usize = 0; - // 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端 - while (j < i) : (j += 1) { - if (nums[j] > nums[j + 1]) { - // 交换 nums[j] 与 nums[j + 1] - var tmp = nums[j]; - nums[j] = nums[j + 1]; - nums[j + 1] = tmp; - count += 3; // 元素交换包含 3 个单元操作 - } - } - } - return count; - } + [class]{}-[func]{bubbleSort} ``` -??? pythontutor "Code Visualization" - -
- - ### 4.   Exponential order $O(2^n)$ {data-toc-label="4.   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. @@ -2138,10 +1515,10 @@ Figure 2-11 and code simulate the cell division process, with a time complexity ```python title="time_complexity.py" def exponential(n: int) -> int: - """指数阶(循环实现)""" + """Exponential complexity (loop implementation)""" count = 0 base = 1 - # 细胞每轮一分为二,形成数列 1, 2, 4, 8, ..., 2^(n-1) + # Cells split into two every round, forming the sequence 1, 2, 4, 8, ..., 2^(n-1) for _ in range(n): for _ in range(base): count += 1 @@ -2153,28 +1530,16 @@ Figure 2-11 and code simulate the cell division process, with a time complexity === "C++" ```cpp title="time_complexity.cpp" - /* 指数阶(循环实现) */ - int exponential(int n) { - int count = 0, base = 1; - // 细胞每轮一分为二,形成数列 1, 2, 4, 8, ..., 2^(n-1) - for (int i = 0; i < n; i++) { - for (int j = 0; j < base; j++) { - count++; - } - base *= 2; - } - // count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1 - return count; - } + [class]{}-[func]{exponential} ``` === "Java" ```java title="time_complexity.java" - /* 指数阶(循环实现) */ + /* Exponential complexity (loop implementation) */ int exponential(int n) { int count = 0, base = 1; - // 细胞每轮一分为二,形成数列 1, 2, 4, 8, ..., 2^(n-1) + // Cells split into two every round, forming the sequence 1, 2, 4, 8, ..., 2^(n-1) for (int i = 0; i < n; i++) { for (int j = 0; j < base; j++) { count++; @@ -2189,215 +1554,69 @@ Figure 2-11 and code simulate the cell division process, with a time complexity === "C#" ```csharp title="time_complexity.cs" - /* 指数阶(循环实现) */ - int Exponential(int n) { - int count = 0, bas = 1; - // 细胞每轮一分为二,形成数列 1, 2, 4, 8, ..., 2^(n-1) - for (int i = 0; i < n; i++) { - for (int j = 0; j < bas; j++) { - count++; - } - bas *= 2; - } - // count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1 - return count; - } + [class]{time_complexity}-[func]{Exponential} ``` === "Go" ```go title="time_complexity.go" - /* 指数阶(循环实现)*/ - func exponential(n int) int { - count, base := 0, 1 - // 细胞每轮一分为二,形成数列 1, 2, 4, 8, ..., 2^(n-1) - for i := 0; i < n; i++ { - for j := 0; j < base; j++ { - count++ - } - base *= 2 - } - // count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1 - return count - } + [class]{}-[func]{exponential} ``` === "Swift" ```swift title="time_complexity.swift" - /* 指数阶(循环实现) */ - func exponential(n: Int) -> Int { - var count = 0 - var base = 1 - // 细胞每轮一分为二,形成数列 1, 2, 4, 8, ..., 2^(n-1) - for _ in 0 ..< n { - for _ in 0 ..< base { - count += 1 - } - base *= 2 - } - // count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1 - return count - } + [class]{}-[func]{exponential} ``` === "JS" ```javascript title="time_complexity.js" - /* 指数阶(循环实现) */ - function exponential(n) { - let count = 0, - base = 1; - // 细胞每轮一分为二,形成数列 1, 2, 4, 8, ..., 2^(n-1) - for (let i = 0; i < n; i++) { - for (let j = 0; j < base; j++) { - count++; - } - base *= 2; - } - // count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1 - return count; - } + [class]{}-[func]{exponential} ``` === "TS" ```typescript title="time_complexity.ts" - /* 指数阶(循环实现) */ - function exponential(n: number): number { - let count = 0, - base = 1; - // 细胞每轮一分为二,形成数列 1, 2, 4, 8, ..., 2^(n-1) - for (let i = 0; i < n; i++) { - for (let j = 0; j < base; j++) { - count++; - } - base *= 2; - } - // count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1 - return count; - } + [class]{}-[func]{exponential} ``` === "Dart" ```dart title="time_complexity.dart" - /* 指数阶(循环实现) */ - int exponential(int n) { - int count = 0, base = 1; - // 细胞每轮一分为二,形成数列 1, 2, 4, 8, ..., 2^(n-1) - for (var i = 0; i < n; i++) { - for (var j = 0; j < base; j++) { - count++; - } - base *= 2; - } - // count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1 - return count; - } + [class]{}-[func]{exponential} ``` === "Rust" ```rust title="time_complexity.rs" - /* 指数阶(循环实现) */ - fn exponential(n: i32) -> i32 { - let mut count = 0; - let mut base = 1; - // 细胞每轮一分为二,形成数列 1, 2, 4, 8, ..., 2^(n-1) - for _ in 0..n { - for _ in 0..base { - count += 1 - } - base *= 2; - } - // count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1 - count - } + [class]{}-[func]{exponential} ``` === "C" ```c title="time_complexity.c" - /* 指数阶(循环实现) */ - int exponential(int n) { - int count = 0; - int bas = 1; - // 细胞每轮一分为二,形成数列 1, 2, 4, 8, ..., 2^(n-1) - for (int i = 0; i < n; i++) { - for (int j = 0; j < bas; j++) { - count++; - } - bas *= 2; - } - // count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1 - return count; - } + [class]{}-[func]{exponential} ``` === "Kotlin" ```kotlin title="time_complexity.kt" - /* 指数阶(循环实现) */ - fun exponential(n: Int): Int { - var count = 0 - var base = 1 - // 细胞每轮一分为二,形成数列 1, 2, 4, 8, ..., 2^(n-1) - for (i in 0.. - - ![Exponential order time complexity](time_complexity.assets/time_complexity_exponential.png){ class="animation-figure" }

Figure 2-11   Exponential order time complexity

@@ -2408,7 +1627,7 @@ In practice, exponential order often appears in recursive functions. For example ```python title="time_complexity.py" def exp_recur(n: int) -> int: - """指数阶(递归实现)""" + """Exponential complexity (recursive implementation)""" if n == 1: return 1 return exp_recur(n - 1) + exp_recur(n - 1) + 1 @@ -2417,18 +1636,13 @@ In practice, exponential order often appears in recursive functions. For example === "C++" ```cpp title="time_complexity.cpp" - /* 指数阶(递归实现) */ - int expRecur(int n) { - if (n == 1) - return 1; - return expRecur(n - 1) + expRecur(n - 1) + 1; - } + [class]{}-[func]{expRecur} ``` === "Java" ```java title="time_complexity.java" - /* 指数阶(递归实现) */ + /* Exponential complexity (recursive implementation) */ int expRecur(int n) { if (n == 1) return 1; @@ -2439,127 +1653,69 @@ In practice, exponential order often appears in recursive functions. For example === "C#" ```csharp title="time_complexity.cs" - /* 指数阶(递归实现) */ - int ExpRecur(int n) { - if (n == 1) return 1; - return ExpRecur(n - 1) + ExpRecur(n - 1) + 1; - } + [class]{time_complexity}-[func]{ExpRecur} ``` === "Go" ```go title="time_complexity.go" - /* 指数阶(递归实现)*/ - func expRecur(n int) int { - if n == 1 { - return 1 - } - return expRecur(n-1) + expRecur(n-1) + 1 - } + [class]{}-[func]{expRecur} ``` === "Swift" ```swift title="time_complexity.swift" - /* 指数阶(递归实现) */ - func expRecur(n: Int) -> Int { - if n == 1 { - return 1 - } - return expRecur(n: n - 1) + expRecur(n: n - 1) + 1 - } + [class]{}-[func]{expRecur} ``` === "JS" ```javascript title="time_complexity.js" - /* 指数阶(递归实现) */ - function expRecur(n) { - if (n === 1) return 1; - return expRecur(n - 1) + expRecur(n - 1) + 1; - } + [class]{}-[func]{expRecur} ``` === "TS" ```typescript title="time_complexity.ts" - /* 指数阶(递归实现) */ - function expRecur(n: number): number { - if (n === 1) return 1; - return expRecur(n - 1) + expRecur(n - 1) + 1; - } + [class]{}-[func]{expRecur} ``` === "Dart" ```dart title="time_complexity.dart" - /* 指数阶(递归实现) */ - int expRecur(int n) { - if (n == 1) return 1; - return expRecur(n - 1) + expRecur(n - 1) + 1; - } + [class]{}-[func]{expRecur} ``` === "Rust" ```rust title="time_complexity.rs" - /* 指数阶(递归实现) */ - fn exp_recur(n: i32) -> i32 { - if n == 1 { - return 1; - } - exp_recur(n - 1) + exp_recur(n - 1) + 1 - } + [class]{}-[func]{exp_recur} ``` === "C" ```c title="time_complexity.c" - /* 指数阶(递归实现) */ - int expRecur(int n) { - if (n == 1) - return 1; - return expRecur(n - 1) + expRecur(n - 1) + 1; - } + [class]{}-[func]{expRecur} ``` === "Kotlin" ```kotlin title="time_complexity.kt" - /* 指数阶(递归实现) */ - fun expRecur(n: Int): Int { - if (n == 1) { - return 1 - } - return expRecur(n - 1) + expRecur(n - 1) + 1 - } + [class]{}-[func]{expRecur} ``` === "Ruby" ```ruby title="time_complexity.rb" - ### 指数阶(递归实现)### - def exp_recur(n) - return 1 if n == 1 - exp_recur(n - 1) + exp_recur(n - 1) + 1 - end + [class]{}-[func]{exp_recur} ``` === "Zig" ```zig title="time_complexity.zig" - // 指数阶(递归实现) - fn expRecur(n: i32) i32 { - if (n == 1) return 1; - return expRecur(n - 1) + expRecur(n - 1) + 1; - } + [class]{}-[func]{expRecur} ``` -??? pythontutor "Code Visualization" - -
- - Exponential order growth is extremely rapid and is commonly seen in exhaustive search methods (brute force, backtracking, etc.). For large-scale problems, exponential order is unacceptable, often requiring dynamic programming or greedy algorithms as solutions. ### 5.   Logarithmic order $O(\log n)$ {data-toc-label="5.   Logarithmic order"} @@ -2572,7 +1728,7 @@ Figure 2-12 and code simulate the "halving each round" process, with a time comp ```python title="time_complexity.py" def logarithmic(n: int) -> int: - """对数阶(循环实现)""" + """Logarithmic complexity (loop implementation)""" count = 0 while n > 1: n = n / 2 @@ -2583,21 +1739,13 @@ Figure 2-12 and code simulate the "halving each round" process, with a time comp === "C++" ```cpp title="time_complexity.cpp" - /* 对数阶(循环实现) */ - int logarithmic(int n) { - int count = 0; - while (n > 1) { - n = n / 2; - count++; - } - return count; - } + [class]{}-[func]{logarithmic} ``` === "Java" ```java title="time_complexity.java" - /* 对数阶(循环实现) */ + /* Logarithmic complexity (loop implementation) */ int logarithmic(int n) { int count = 0; while (n > 1) { @@ -2611,168 +1759,69 @@ Figure 2-12 and code simulate the "halving each round" process, with a time comp === "C#" ```csharp title="time_complexity.cs" - /* 对数阶(循环实现) */ - int Logarithmic(int n) { - int count = 0; - while (n > 1) { - n /= 2; - count++; - } - return count; - } + [class]{time_complexity}-[func]{Logarithmic} ``` === "Go" ```go title="time_complexity.go" - /* 对数阶(循环实现)*/ - func logarithmic(n int) int { - count := 0 - for n > 1 { - n = n / 2 - count++ - } - return count - } + [class]{}-[func]{logarithmic} ``` === "Swift" ```swift title="time_complexity.swift" - /* 对数阶(循环实现) */ - func logarithmic(n: Int) -> Int { - var count = 0 - var n = n - while n > 1 { - n = n / 2 - count += 1 - } - return count - } + [class]{}-[func]{logarithmic} ``` === "JS" ```javascript title="time_complexity.js" - /* 对数阶(循环实现) */ - function logarithmic(n) { - let count = 0; - while (n > 1) { - n = n / 2; - count++; - } - return count; - } + [class]{}-[func]{logarithmic} ``` === "TS" ```typescript title="time_complexity.ts" - /* 对数阶(循环实现) */ - function logarithmic(n: number): number { - let count = 0; - while (n > 1) { - n = n / 2; - count++; - } - return count; - } + [class]{}-[func]{logarithmic} ``` === "Dart" ```dart title="time_complexity.dart" - /* 对数阶(循环实现) */ - int logarithmic(int n) { - int count = 0; - while (n > 1) { - n = n ~/ 2; - count++; - } - return count; - } + [class]{}-[func]{logarithmic} ``` === "Rust" ```rust title="time_complexity.rs" - /* 对数阶(循环实现) */ - fn logarithmic(mut n: i32) -> i32 { - let mut count = 0; - while n > 1 { - n = n / 2; - count += 1; - } - count - } + [class]{}-[func]{logarithmic} ``` === "C" ```c title="time_complexity.c" - /* 对数阶(循环实现) */ - int logarithmic(int n) { - int count = 0; - while (n > 1) { - n = n / 2; - count++; - } - return count; - } + [class]{}-[func]{logarithmic} ``` === "Kotlin" ```kotlin title="time_complexity.kt" - /* 对数阶(循环实现) */ - fun logarithmic(n: Int): Int { - var n1 = n - var count = 0 - while (n1 > 1) { - n1 /= 2 - count++ - } - return count - } + [class]{}-[func]{logarithmic} ``` === "Ruby" ```ruby title="time_complexity.rb" - ### 对数阶(循环实现)### - def logarithmic(n) - count = 0 - - while n > 1 - n /= 2 - count += 1 - end - - count - end + [class]{}-[func]{logarithmic} ``` === "Zig" ```zig title="time_complexity.zig" - // 对数阶(循环实现) - fn logarithmic(n: i32) i32 { - var count: i32 = 0; - var n_var = n; - while (n_var > 1) - { - n_var = n_var / 2; - count +=1; - } - return count; - } + [class]{}-[func]{logarithmic} ``` -??? pythontutor "Code Visualization" - -
- - ![Logarithmic order time complexity](time_complexity.assets/time_complexity_logarithmic.png){ class="animation-figure" }

Figure 2-12   Logarithmic order time complexity

@@ -2783,7 +1832,7 @@ Like exponential order, logarithmic order also frequently appears in recursive f ```python title="time_complexity.py" def log_recur(n: int) -> int: - """对数阶(递归实现)""" + """Logarithmic complexity (recursive implementation)""" if n <= 1: return 0 return log_recur(n / 2) + 1 @@ -2792,18 +1841,13 @@ Like exponential order, logarithmic order also frequently appears in recursive f === "C++" ```cpp title="time_complexity.cpp" - /* 对数阶(递归实现) */ - int logRecur(int n) { - if (n <= 1) - return 0; - return logRecur(n / 2) + 1; - } + [class]{}-[func]{logRecur} ``` === "Java" ```java title="time_complexity.java" - /* 对数阶(递归实现) */ + /* Logarithmic complexity (recursive implementation) */ int logRecur(int n) { if (n <= 1) return 0; @@ -2814,126 +1858,69 @@ Like exponential order, logarithmic order also frequently appears in recursive f === "C#" ```csharp title="time_complexity.cs" - /* 对数阶(递归实现) */ - int LogRecur(int n) { - if (n <= 1) return 0; - return LogRecur(n / 2) + 1; - } + [class]{time_complexity}-[func]{LogRecur} ``` === "Go" ```go title="time_complexity.go" - /* 对数阶(递归实现)*/ - func logRecur(n int) int { - if n <= 1 { - return 0 - } - return logRecur(n/2) + 1 - } + [class]{}-[func]{logRecur} ``` === "Swift" ```swift title="time_complexity.swift" - /* 对数阶(递归实现) */ - func logRecur(n: Int) -> Int { - if n <= 1 { - return 0 - } - return logRecur(n: n / 2) + 1 - } + [class]{}-[func]{logRecur} ``` === "JS" ```javascript title="time_complexity.js" - /* 对数阶(递归实现) */ - function logRecur(n) { - if (n <= 1) return 0; - return logRecur(n / 2) + 1; - } + [class]{}-[func]{logRecur} ``` === "TS" ```typescript title="time_complexity.ts" - /* 对数阶(递归实现) */ - function logRecur(n: number): number { - if (n <= 1) return 0; - return logRecur(n / 2) + 1; - } + [class]{}-[func]{logRecur} ``` === "Dart" ```dart title="time_complexity.dart" - /* 对数阶(递归实现) */ - int logRecur(int n) { - if (n <= 1) return 0; - return logRecur(n ~/ 2) + 1; - } + [class]{}-[func]{logRecur} ``` === "Rust" ```rust title="time_complexity.rs" - /* 对数阶(递归实现) */ - fn log_recur(n: i32) -> i32 { - if n <= 1 { - return 0; - } - log_recur(n / 2) + 1 - } + [class]{}-[func]{log_recur} ``` === "C" ```c title="time_complexity.c" - /* 对数阶(递归实现) */ - int logRecur(int n) { - if (n <= 1) - return 0; - return logRecur(n / 2) + 1; - } + [class]{}-[func]{logRecur} ``` === "Kotlin" ```kotlin title="time_complexity.kt" - /* 对数阶(递归实现) */ - fun logRecur(n: Int): Int { - if (n <= 1) - return 0 - return logRecur(n / 2) + 1 - } + [class]{}-[func]{logRecur} ``` === "Ruby" ```ruby title="time_complexity.rb" - ### 对数阶(递归实现)### - def log_recur(n) - return 0 unless n > 1 - log_recur(n / 2) + 1 - end + [class]{}-[func]{log_recur} ``` === "Zig" ```zig title="time_complexity.zig" - // 对数阶(递归实现) - fn logRecur(n: i32) i32 { - if (n <= 1) return 0; - return logRecur(n / 2) + 1; - } + [class]{}-[func]{logRecur} ``` -??? pythontutor "Code Visualization" - -
- - Logarithmic order is typical in algorithms based on the divide-and-conquer strategy, embodying the "split into many" and "simplify complex problems" approach. It's slow-growing and is the most ideal time complexity after constant order. !!! tip "What is the base of $O(\log n)$?" @@ -2954,7 +1941,7 @@ Linear-logarithmic order often appears in nested loops, with the complexities of ```python title="time_complexity.py" def linear_log_recur(n: int) -> int: - """线性对数阶""" + """Linear logarithmic complexity""" if n <= 1: return 1 count: int = linear_log_recur(n // 2) + linear_log_recur(n // 2) @@ -2966,22 +1953,13 @@ Linear-logarithmic order often appears in nested loops, with the complexities of === "C++" ```cpp title="time_complexity.cpp" - /* 线性对数阶 */ - int linearLogRecur(int n) { - if (n <= 1) - return 1; - int count = linearLogRecur(n / 2) + linearLogRecur(n / 2); - for (int i = 0; i < n; i++) { - count++; - } - return count; - } + [class]{}-[func]{linearLogRecur} ``` === "Java" ```java title="time_complexity.java" - /* 线性对数阶 */ + /* Linear logarithmic complexity */ int linearLogRecur(int n) { if (n <= 1) return 1; @@ -2996,171 +1974,69 @@ Linear-logarithmic order often appears in nested loops, with the complexities of === "C#" ```csharp title="time_complexity.cs" - /* 线性对数阶 */ - int LinearLogRecur(int n) { - if (n <= 1) return 1; - int count = LinearLogRecur(n / 2) + LinearLogRecur(n / 2); - for (int i = 0; i < n; i++) { - count++; - } - return count; - } + [class]{time_complexity}-[func]{LinearLogRecur} ``` === "Go" ```go title="time_complexity.go" - /* 线性对数阶 */ - func linearLogRecur(n int) int { - if n <= 1 { - return 1 - } - count := linearLogRecur(n/2) + linearLogRecur(n/2) - for i := 0; i < n; i++ { - count++ - } - return count - } + [class]{}-[func]{linearLogRecur} ``` === "Swift" ```swift title="time_complexity.swift" - /* 线性对数阶 */ - func linearLogRecur(n: Int) -> Int { - if n <= 1 { - return 1 - } - var count = linearLogRecur(n: n / 2) + linearLogRecur(n: n / 2) - for _ in stride(from: 0, to: n, by: 1) { - count += 1 - } - return count - } + [class]{}-[func]{linearLogRecur} ``` === "JS" ```javascript title="time_complexity.js" - /* 线性对数阶 */ - function linearLogRecur(n) { - if (n <= 1) return 1; - let count = linearLogRecur(n / 2) + linearLogRecur(n / 2); - for (let i = 0; i < n; i++) { - count++; - } - return count; - } + [class]{}-[func]{linearLogRecur} ``` === "TS" ```typescript title="time_complexity.ts" - /* 线性对数阶 */ - function linearLogRecur(n: number): number { - if (n <= 1) return 1; - let count = linearLogRecur(n / 2) + linearLogRecur(n / 2); - for (let i = 0; i < n; i++) { - count++; - } - return count; - } + [class]{}-[func]{linearLogRecur} ``` === "Dart" ```dart title="time_complexity.dart" - /* 线性对数阶 */ - int linearLogRecur(int n) { - if (n <= 1) return 1; - int count = linearLogRecur(n ~/ 2) + linearLogRecur(n ~/ 2); - for (var i = 0; i < n; i++) { - count++; - } - return count; - } + [class]{}-[func]{linearLogRecur} ``` === "Rust" ```rust title="time_complexity.rs" - /* 线性对数阶 */ - fn linear_log_recur(n: i32) -> i32 { - if n <= 1 { - return 1; - } - let mut count = linear_log_recur(n / 2) + linear_log_recur(n / 2); - for _ in 0..n as i32 { - count += 1; - } - return count; - } + [class]{}-[func]{linear_log_recur} ``` === "C" ```c title="time_complexity.c" - /* 线性对数阶 */ - int linearLogRecur(int n) { - if (n <= 1) - return 1; - int count = linearLogRecur(n / 2) + linearLogRecur(n / 2); - for (int i = 0; i < n; i++) { - count++; - } - return count; - } + [class]{}-[func]{linearLogRecur} ``` === "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.. 1 - - count = linear_log_recur(n / 2) + linear_log_recur(n / 2) - (0...n).each { count += 1 } - - count - end + [class]{}-[func]{linear_log_recur} ``` === "Zig" ```zig title="time_complexity.zig" - // 线性对数阶 - fn linearLogRecur(n: i32) i32 { - if (n <= 1) return 1; - var count: i32 = linearLogRecur(n / 2) + linearLogRecur(n / 2); - var i: i32 = 0; - while (i < n) : (i += 1) { - count += 1; - } - return count; - } + [class]{}-[func]{linearLogRecur} ``` -??? pythontutor "Code Visualization" - -
- - Figure 2-13 demonstrates how linear-logarithmic order is generated. Each level of a binary tree has $n$ operations, and the tree has $\log_2 n + 1$ levels, resulting in a time complexity of $O(n \log n)$. ![Linear-logarithmic order time complexity](time_complexity.assets/time_complexity_logarithmic_linear.png){ class="animation-figure" } @@ -3183,11 +2059,11 @@ Factorials are typically implemented using recursion. As shown in the code and F ```python title="time_complexity.py" def factorial_recur(n: int) -> int: - """阶乘阶(递归实现)""" + """Factorial complexity (recursive implementation)""" if n == 0: return 1 count = 0 - # 从 1 个分裂出 n 个 + # From 1 split into n for _ in range(n): count += factorial_recur(n - 1) return count @@ -3196,28 +2072,18 @@ Factorials are typically implemented using recursion. As shown in the code and F === "C++" ```cpp title="time_complexity.cpp" - /* 阶乘阶(递归实现) */ - int factorialRecur(int n) { - if (n == 0) - return 1; - int count = 0; - // 从 1 个分裂出 n 个 - for (int i = 0; i < n; i++) { - count += factorialRecur(n - 1); - } - return count; - } + [class]{}-[func]{factorialRecur} ``` === "Java" ```java title="time_complexity.java" - /* 阶乘阶(递归实现) */ + /* Factorial complexity (recursive implementation) */ int factorialRecur(int n) { if (n == 0) return 1; int count = 0; - // 从 1 个分裂出 n 个 + // From 1 split into n for (int i = 0; i < n; i++) { count += factorialRecur(n - 1); } @@ -3228,181 +2094,69 @@ Factorials are typically implemented using recursion. As shown in the code and F === "C#" ```csharp title="time_complexity.cs" - /* 阶乘阶(递归实现) */ - int FactorialRecur(int n) { - if (n == 0) return 1; - int count = 0; - // 从 1 个分裂出 n 个 - for (int i = 0; i < n; i++) { - count += FactorialRecur(n - 1); - } - return count; - } + [class]{time_complexity}-[func]{FactorialRecur} ``` === "Go" ```go title="time_complexity.go" - /* 阶乘阶(递归实现) */ - func factorialRecur(n int) int { - if n == 0 { - return 1 - } - count := 0 - // 从 1 个分裂出 n 个 - for i := 0; i < n; i++ { - count += factorialRecur(n - 1) - } - return count - } + [class]{}-[func]{factorialRecur} ``` === "Swift" ```swift title="time_complexity.swift" - /* 阶乘阶(递归实现) */ - func factorialRecur(n: Int) -> Int { - if n == 0 { - return 1 - } - var count = 0 - // 从 1 个分裂出 n 个 - for _ in 0 ..< n { - count += factorialRecur(n: n - 1) - } - return count - } + [class]{}-[func]{factorialRecur} ``` === "JS" ```javascript title="time_complexity.js" - /* 阶乘阶(递归实现) */ - function factorialRecur(n) { - if (n === 0) return 1; - let count = 0; - // 从 1 个分裂出 n 个 - for (let i = 0; i < n; i++) { - count += factorialRecur(n - 1); - } - return count; - } + [class]{}-[func]{factorialRecur} ``` === "TS" ```typescript title="time_complexity.ts" - /* 阶乘阶(递归实现) */ - function factorialRecur(n: number): number { - if (n === 0) return 1; - let count = 0; - // 从 1 个分裂出 n 个 - for (let i = 0; i < n; i++) { - count += factorialRecur(n - 1); - } - return count; - } + [class]{}-[func]{factorialRecur} ``` === "Dart" ```dart title="time_complexity.dart" - /* 阶乘阶(递归实现) */ - int factorialRecur(int n) { - if (n == 0) return 1; - int count = 0; - // 从 1 个分裂出 n 个 - for (var i = 0; i < n; i++) { - count += factorialRecur(n - 1); - } - return count; - } + [class]{}-[func]{factorialRecur} ``` === "Rust" ```rust title="time_complexity.rs" - /* 阶乘阶(递归实现) */ - fn factorial_recur(n: i32) -> i32 { - if n == 0 { - return 1; - } - let mut count = 0; - // 从 1 个分裂出 n 个 - for _ in 0..n { - count += factorial_recur(n - 1); - } - count - } + [class]{}-[func]{factorial_recur} ``` === "C" ```c title="time_complexity.c" - /* 阶乘阶(递归实现) */ - int factorialRecur(int n) { - if (n == 0) - return 1; - int count = 0; - for (int i = 0; i < n; i++) { - count += factorialRecur(n - 1); - } - return count; - } + [class]{}-[func]{factorialRecur} ``` === "Kotlin" ```kotlin title="time_complexity.kt" - /* 阶乘阶(递归实现) */ - fun factorialRecur(n: Int): Int { - if (n == 0) - return 1 - var count = 0 - // 从 1 个分裂出 n 个 - for (i in 0.. - - ![Factorial order time complexity](time_complexity.assets/time_complexity_factorial.png){ class="animation-figure" }

Figure 2-14   Factorial order time complexity

@@ -3422,18 +2176,18 @@ The "worst-case time complexity" corresponds to the asymptotic upper bound, deno ```python title="worst_best_time_complexity.py" def random_numbers(n: int) -> list[int]: - """生成一个数组,元素为: 1, 2, ..., n ,顺序被打乱""" - # 生成数组 nums =: 1, 2, 3, ..., n + """Generate an array with elements: 1, 2, ..., n, order shuffled""" + # Generate array nums =: 1, 2, 3, ..., n nums = [i for i in range(1, n + 1)] - # 随机打乱数组元素 + # Randomly shuffle array elements random.shuffle(nums) return nums def find_one(nums: list[int]) -> int: - """查找数组 nums 中数字 1 所在索引""" + """Find the index of number 1 in array nums""" for i in range(len(nums)): - # 当元素 1 在数组头部时,达到最佳时间复杂度 O(1) - # 当元素 1 在数组尾部时,达到最差时间复杂度 O(n) + # When element 1 is at the start of the array, achieve best time complexity O(1) + # When element 1 is at the end of the array, achieve worst time complexity O(n) if nums[i] == 1: return i return -1 @@ -3442,43 +2196,22 @@ The "worst-case time complexity" corresponds to the asymptotic upper bound, deno === "C++" ```cpp title="worst_best_time_complexity.cpp" - /* 生成一个数组,元素为 { 1, 2, ..., n },顺序被打乱 */ - vector randomNumbers(int n) { - vector nums(n); - // 生成数组 nums = { 1, 2, 3, ..., n } - for (int i = 0; i < n; i++) { - nums[i] = i + 1; - } - // 使用系统时间生成随机种子 - unsigned seed = chrono::system_clock::now().time_since_epoch().count(); - // 随机打乱数组元素 - shuffle(nums.begin(), nums.end(), default_random_engine(seed)); - return nums; - } + [class]{}-[func]{randomNumbers} - /* 查找数组 nums 中数字 1 所在索引 */ - int findOne(vector &nums) { - for (int i = 0; i < nums.size(); i++) { - // 当元素 1 在数组头部时,达到最佳时间复杂度 O(1) - // 当元素 1 在数组尾部时,达到最差时间复杂度 O(n) - if (nums[i] == 1) - return i; - } - return -1; - } + [class]{}-[func]{findOne} ``` === "Java" ```java title="worst_best_time_complexity.java" - /* 生成一个数组,元素为 { 1, 2, ..., n },顺序被打乱 */ + /* Generate an array with elements {1, 2, ..., n} in a randomly shuffled order */ int[] randomNumbers(int n) { Integer[] nums = new Integer[n]; - // 生成数组 nums = { 1, 2, 3, ..., n } + // Generate array nums = { 1, 2, 3, ..., n } for (int i = 0; i < n; i++) { nums[i] = i + 1; } - // 随机打乱数组元素 + // Randomly shuffle array elements Collections.shuffle(Arrays.asList(nums)); // Integer[] -> int[] int[] res = new int[n]; @@ -3488,11 +2221,11 @@ The "worst-case time complexity" corresponds to the asymptotic upper bound, deno return res; } - /* 查找数组 nums 中数字 1 所在索引 */ + /* Find the index of number 1 in array nums */ int findOne(int[] nums) { for (int i = 0; i < nums.length; i++) { - // 当元素 1 在数组头部时,达到最佳时间复杂度 O(1) - // 当元素 1 在数组尾部时,达到最差时间复杂度 O(n) + // When element 1 is at the start of the array, achieve best time complexity O(1) + // When element 1 is at the end of the array, achieve worst time complexity O(n) if (nums[i] == 1) return i; } @@ -3503,327 +2236,91 @@ The "worst-case time complexity" corresponds to the asymptotic upper bound, deno === "C#" ```csharp title="worst_best_time_complexity.cs" - /* 生成一个数组,元素为 { 1, 2, ..., n },顺序被打乱 */ - int[] RandomNumbers(int n) { - int[] nums = new int[n]; - // 生成数组 nums = { 1, 2, 3, ..., n } - for (int i = 0; i < n; i++) { - nums[i] = i + 1; - } + [class]{worst_best_time_complexity}-[func]{RandomNumbers} - // 随机打乱数组元素 - for (int i = 0; i < nums.Length; i++) { - int index = new Random().Next(i, nums.Length); - (nums[i], nums[index]) = (nums[index], nums[i]); - } - return nums; - } - - /* 查找数组 nums 中数字 1 所在索引 */ - int FindOne(int[] nums) { - for (int i = 0; i < nums.Length; i++) { - // 当元素 1 在数组头部时,达到最佳时间复杂度 O(1) - // 当元素 1 在数组尾部时,达到最差时间复杂度 O(n) - if (nums[i] == 1) - return i; - } - return -1; - } + [class]{worst_best_time_complexity}-[func]{FindOne} ``` === "Go" ```go title="worst_best_time_complexity.go" - /* 生成一个数组,元素为 { 1, 2, ..., n },顺序被打乱 */ - func randomNumbers(n int) []int { - nums := make([]int, n) - // 生成数组 nums = { 1, 2, 3, ..., n } - for i := 0; i < n; i++ { - nums[i] = i + 1 - } - // 随机打乱数组元素 - rand.Shuffle(len(nums), func(i, j int) { - nums[i], nums[j] = nums[j], nums[i] - }) - return nums - } + [class]{}-[func]{randomNumbers} - /* 查找数组 nums 中数字 1 所在索引 */ - func findOne(nums []int) int { - for i := 0; i < len(nums); i++ { - // 当元素 1 在数组头部时,达到最佳时间复杂度 O(1) - // 当元素 1 在数组尾部时,达到最差时间复杂度 O(n) - if nums[i] == 1 { - return i - } - } - return -1 - } + [class]{}-[func]{findOne} ``` === "Swift" ```swift title="worst_best_time_complexity.swift" - /* 生成一个数组,元素为 { 1, 2, ..., n },顺序被打乱 */ - func randomNumbers(n: Int) -> [Int] { - // 生成数组 nums = { 1, 2, 3, ..., n } - var nums = Array(1 ... n) - // 随机打乱数组元素 - nums.shuffle() - return nums - } + [class]{}-[func]{randomNumbers} - /* 查找数组 nums 中数字 1 所在索引 */ - func findOne(nums: [Int]) -> Int { - for i in nums.indices { - // 当元素 1 在数组头部时,达到最佳时间复杂度 O(1) - // 当元素 1 在数组尾部时,达到最差时间复杂度 O(n) - if nums[i] == 1 { - return i - } - } - return -1 - } + [class]{}-[func]{findOne} ``` === "JS" ```javascript title="worst_best_time_complexity.js" - /* 生成一个数组,元素为 { 1, 2, ..., n },顺序被打乱 */ - function randomNumbers(n) { - const nums = Array(n); - // 生成数组 nums = { 1, 2, 3, ..., n } - for (let i = 0; i < n; i++) { - nums[i] = i + 1; - } - // 随机打乱数组元素 - for (let i = 0; i < n; i++) { - const r = Math.floor(Math.random() * (i + 1)); - const temp = nums[i]; - nums[i] = nums[r]; - nums[r] = temp; - } - return nums; - } + [class]{}-[func]{randomNumbers} - /* 查找数组 nums 中数字 1 所在索引 */ - function findOne(nums) { - for (let i = 0; i < nums.length; i++) { - // 当元素 1 在数组头部时,达到最佳时间复杂度 O(1) - // 当元素 1 在数组尾部时,达到最差时间复杂度 O(n) - if (nums[i] === 1) { - return i; - } - } - return -1; - } + [class]{}-[func]{findOne} ``` === "TS" ```typescript title="worst_best_time_complexity.ts" - /* 生成一个数组,元素为 { 1, 2, ..., n },顺序被打乱 */ - function randomNumbers(n: number): number[] { - const nums = Array(n); - // 生成数组 nums = { 1, 2, 3, ..., n } - for (let i = 0; i < n; i++) { - nums[i] = i + 1; - } - // 随机打乱数组元素 - for (let i = 0; i < n; i++) { - const r = Math.floor(Math.random() * (i + 1)); - const temp = nums[i]; - nums[i] = nums[r]; - nums[r] = temp; - } - return nums; - } + [class]{}-[func]{randomNumbers} - /* 查找数组 nums 中数字 1 所在索引 */ - function findOne(nums: number[]): number { - for (let i = 0; i < nums.length; i++) { - // 当元素 1 在数组头部时,达到最佳时间复杂度 O(1) - // 当元素 1 在数组尾部时,达到最差时间复杂度 O(n) - if (nums[i] === 1) { - return i; - } - } - return -1; - } + [class]{}-[func]{findOne} ``` === "Dart" ```dart title="worst_best_time_complexity.dart" - /* 生成一个数组,元素为 { 1, 2, ..., n },顺序被打乱 */ - List randomNumbers(int n) { - final nums = List.filled(n, 0); - // 生成数组 nums = { 1, 2, 3, ..., n } - for (var i = 0; i < n; i++) { - nums[i] = i + 1; - } - // 随机打乱数组元素 - nums.shuffle(); + [class]{}-[func]{randomNumbers} - return nums; - } - - /* 查找数组 nums 中数字 1 所在索引 */ - int findOne(List nums) { - for (var i = 0; i < nums.length; i++) { - // 当元素 1 在数组头部时,达到最佳时间复杂度 O(1) - // 当元素 1 在数组尾部时,达到最差时间复杂度 O(n) - if (nums[i] == 1) return i; - } - - return -1; - } + [class]{}-[func]{findOne} ``` === "Rust" ```rust title="worst_best_time_complexity.rs" - /* 生成一个数组,元素为 { 1, 2, ..., n },顺序被打乱 */ - fn random_numbers(n: i32) -> Vec { - // 生成数组 nums = { 1, 2, 3, ..., n } - let mut nums = (1..=n).collect::>(); - // 随机打乱数组元素 - nums.shuffle(&mut thread_rng()); - nums - } + [class]{}-[func]{random_numbers} - /* 查找数组 nums 中数字 1 所在索引 */ - fn find_one(nums: &[i32]) -> Option { - for i in 0..nums.len() { - // 当元素 1 在数组头部时,达到最佳时间复杂度 O(1) - // 当元素 1 在数组尾部时,达到最差时间复杂度 O(n) - if nums[i] == 1 { - return Some(i); - } - } - None - } + [class]{}-[func]{find_one} ``` === "C" ```c title="worst_best_time_complexity.c" - /* 生成一个数组,元素为 { 1, 2, ..., n },顺序被打乱 */ - int *randomNumbers(int n) { - // 分配堆区内存(创建一维可变长数组:数组中元素数量为 n ,元素类型为 int ) - int *nums = (int *)malloc(n * sizeof(int)); - // 生成数组 nums = { 1, 2, 3, ..., n } - for (int i = 0; i < n; i++) { - nums[i] = i + 1; - } - // 随机打乱数组元素 - for (int i = n - 1; i > 0; i--) { - int j = rand() % (i + 1); - int temp = nums[i]; - nums[i] = nums[j]; - nums[j] = temp; - } - return nums; - } + [class]{}-[func]{randomNumbers} - /* 查找数组 nums 中数字 1 所在索引 */ - int findOne(int *nums, int n) { - for (int i = 0; i < n; i++) { - // 当元素 1 在数组头部时,达到最佳时间复杂度 O(1) - // 当元素 1 在数组尾部时,达到最差时间复杂度 O(n) - if (nums[i] == 1) - return i; - } - return -1; - } + [class]{}-[func]{findOne} ``` === "Kotlin" ```kotlin title="worst_best_time_complexity.kt" - /* 生成一个数组,元素为 { 1, 2, ..., n },顺序被打乱 */ - fun randomNumbers(n: Int): Array { - val nums = IntArray(n) - // 生成数组 nums = { 1, 2, 3, ..., n } - for (i in 0..(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 - } + [class]{}-[func]{findOne} ``` === "Ruby" ```ruby title="worst_best_time_complexity.rb" - ### 生成一个数组,元素为: 1, 2, ..., n ,顺序被打乱 ### - def random_numbers(n) - # 生成数组 nums =: 1, 2, 3, ..., n - nums = Array.new(n) { |i| i + 1 } - # 随机打乱数组元素 - nums.shuffle! - end + [class]{}-[func]{random_numbers} - ### 查找数组 nums 中数字 1 所在索引 ### - def find_one(nums) - for i in 0...nums.length - # 当元素 1 在数组头部时,达到最佳时间复杂度 O(1) - # 当元素 1 在数组尾部时,达到最差时间复杂度 O(n) - return i if nums[i] == 1 - end - - -1 - end + [class]{}-[func]{find_one} ``` === "Zig" ```zig title="worst_best_time_complexity.zig" - // 生成一个数组,元素为 { 1, 2, ..., n },顺序被打乱 - fn randomNumbers(comptime n: usize) [n]i32 { - var nums: [n]i32 = undefined; - // 生成数组 nums = { 1, 2, 3, ..., n } - for (&nums, 0..) |*num, i| { - num.* = @as(i32, @intCast(i)) + 1; - } - // 随机打乱数组元素 - const rand = std.crypto.random; - rand.shuffle(i32, &nums); - return nums; - } + [class]{}-[func]{randomNumbers} - // 查找数组 nums 中数字 1 所在索引 - fn findOne(nums: []i32) i32 { - for (nums, 0..) |num, i| { - // 当元素 1 在数组头部时,达到最佳时间复杂度 O(1) - // 当元素 1 在数组尾部时,达到最差时间复杂度 O(n) - if (num == 1) return @intCast(i); - } - return -1; - } + [class]{}-[func]{findOne} ``` -??? pythontutor "Code Visualization" - -
- - It's important to note that the best-case time complexity is rarely used in practice, as it is usually only achievable under very low probabilities and might be misleading. **The worst-case time complexity is more practical as it provides a safety value for efficiency**, allowing us to confidently use the algorithm. From the above example, it's clear that both the worst-case and best-case time complexities only occur under "special data distributions," which may have a small probability of occurrence and may not accurately reflect the algorithm's run efficiency. In contrast, **the average time complexity can reflect the algorithm's efficiency under random input data**, denoted by the $\Theta$ notation. diff --git a/en/docs/chapter_divide_and_conquer/binary_search_recur.md b/en/docs/chapter_divide_and_conquer/binary_search_recur.md index 847b501e2..1b502be49 100644 --- a/en/docs/chapter_divide_and_conquer/binary_search_recur.md +++ b/en/docs/chapter_divide_and_conquer/binary_search_recur.md @@ -50,87 +50,64 @@ In the implementation code, we declare a recursive function `dfs()` to solve the ```python title="binary_search_recur.py" def dfs(nums: list[int], target: int, i: int, j: int) -> int: - """二分查找:问题 f(i, j)""" - # 若区间为空,代表无目标元素,则返回 -1 + """Binary search: problem f(i, j)""" + # If the interval is empty, indicating no target element, return -1 if i > j: return -1 - # 计算中点索引 m + # Calculate midpoint index m m = (i + j) // 2 if nums[m] < target: - # 递归子问题 f(m+1, j) + # Recursive subproblem f(m+1, j) return dfs(nums, target, m + 1, j) elif nums[m] > target: - # 递归子问题 f(i, m-1) + # Recursive subproblem f(i, m-1) return dfs(nums, target, i, m - 1) else: - # 找到目标元素,返回其索引 + # Found the target element, thus return its index return m def binary_search(nums: list[int], target: int) -> int: - """二分查找""" + """Binary search""" n = len(nums) - # 求解问题 f(0, n-1) + # Solve problem f(0, n-1) return dfs(nums, target, 0, n - 1) ``` === "C++" ```cpp title="binary_search_recur.cpp" - /* 二分查找:问题 f(i, j) */ - int dfs(vector &nums, int target, int i, int j) { - // 若区间为空,代表无目标元素,则返回 -1 - if (i > j) { - return -1; - } - // 计算中点索引 m - int m = (i + j) / 2; - if (nums[m] < target) { - // 递归子问题 f(m+1, j) - return dfs(nums, target, m + 1, j); - } else if (nums[m] > target) { - // 递归子问题 f(i, m-1) - return dfs(nums, target, i, m - 1); - } else { - // 找到目标元素,返回其索引 - return m; - } - } + [class]{}-[func]{dfs} - /* 二分查找 */ - int binarySearch(vector &nums, int target) { - int n = nums.size(); - // 求解问题 f(0, n-1) - return dfs(nums, target, 0, n - 1); - } + [class]{}-[func]{binarySearch} ``` === "Java" ```java title="binary_search_recur.java" - /* 二分查找:问题 f(i, j) */ + /* Binary search: problem f(i, j) */ int dfs(int[] nums, int target, int i, int j) { - // 若区间为空,代表无目标元素,则返回 -1 + // If the interval is empty, indicating no target element, return -1 if (i > j) { return -1; } - // 计算中点索引 m + // Calculate midpoint index m int m = (i + j) / 2; if (nums[m] < target) { - // 递归子问题 f(m+1, j) + // Recursive subproblem f(m+1, j) return dfs(nums, target, m + 1, j); } else if (nums[m] > target) { - // 递归子问题 f(i, m-1) + // Recursive subproblem f(i, m-1) return dfs(nums, target, i, m - 1); } else { - // 找到目标元素,返回其索引 + // Found the target element, thus return its index return m; } } - /* 二分查找 */ + /* Binary search */ int binarySearch(int[] nums, int target) { int n = nums.length; - // 求解问题 f(0, n-1) + // Solve problem f(0, n-1) return dfs(nums, target, 0, n - 1); } ``` @@ -138,285 +115,73 @@ In the implementation code, we declare a recursive function `dfs()` to solve the === "C#" ```csharp title="binary_search_recur.cs" - /* 二分查找:问题 f(i, j) */ - int DFS(int[] nums, int target, int i, int j) { - // 若区间为空,代表无目标元素,则返回 -1 - if (i > j) { - return -1; - } - // 计算中点索引 m - int m = (i + j) / 2; - if (nums[m] < target) { - // 递归子问题 f(m+1, j) - return DFS(nums, target, m + 1, j); - } else if (nums[m] > target) { - // 递归子问题 f(i, m-1) - return DFS(nums, target, i, m - 1); - } else { - // 找到目标元素,返回其索引 - return m; - } - } + [class]{binary_search_recur}-[func]{DFS} - /* 二分查找 */ - int BinarySearch(int[] nums, int target) { - int n = nums.Length; - // 求解问题 f(0, n-1) - return DFS(nums, target, 0, n - 1); - } + [class]{binary_search_recur}-[func]{BinarySearch} ``` === "Go" ```go title="binary_search_recur.go" - /* 二分查找:问题 f(i, j) */ - func dfs(nums []int, target, i, j int) int { - // 如果区间为空,代表没有目标元素,则返回 -1 - if i > j { - return -1 - } - // 计算索引中点 - m := i + ((j - i) >> 1) - //判断中点与目标元素大小 - if nums[m] < target { - // 小于则递归右半数组 - // 递归子问题 f(m+1, j) - return dfs(nums, target, m+1, j) - } else if nums[m] > target { - // 小于则递归左半数组 - // 递归子问题 f(i, m-1) - return dfs(nums, target, i, m-1) - } else { - // 找到目标元素,返回其索引 - return m - } - } + [class]{}-[func]{dfs} - /* 二分查找 */ - func binarySearch(nums []int, target int) int { - n := len(nums) - return dfs(nums, target, 0, n-1) - } + [class]{}-[func]{binarySearch} ``` === "Swift" ```swift title="binary_search_recur.swift" - /* 二分查找:问题 f(i, j) */ - func dfs(nums: [Int], target: Int, i: Int, j: Int) -> Int { - // 若区间为空,代表无目标元素,则返回 -1 - if i > j { - return -1 - } - // 计算中点索引 m - let m = (i + j) / 2 - if nums[m] < target { - // 递归子问题 f(m+1, j) - return dfs(nums: nums, target: target, i: m + 1, j: j) - } else if nums[m] > target { - // 递归子问题 f(i, m-1) - return dfs(nums: nums, target: target, i: i, j: m - 1) - } else { - // 找到目标元素,返回其索引 - return m - } - } + [class]{}-[func]{dfs} - /* 二分查找 */ - func binarySearch(nums: [Int], target: Int) -> Int { - // 求解问题 f(0, n-1) - dfs(nums: nums, target: target, i: nums.startIndex, j: nums.endIndex - 1) - } + [class]{}-[func]{binarySearch} ``` === "JS" ```javascript title="binary_search_recur.js" - /* 二分查找:问题 f(i, j) */ - function dfs(nums, target, i, j) { - // 若区间为空,代表无目标元素,则返回 -1 - if (i > j) { - return -1; - } - // 计算中点索引 m - const m = i + ((j - i) >> 1); - if (nums[m] < target) { - // 递归子问题 f(m+1, j) - return dfs(nums, target, m + 1, j); - } else if (nums[m] > target) { - // 递归子问题 f(i, m-1) - return dfs(nums, target, i, m - 1); - } else { - // 找到目标元素,返回其索引 - return m; - } - } + [class]{}-[func]{dfs} - /* 二分查找 */ - function binarySearch(nums, target) { - const n = nums.length; - // 求解问题 f(0, n-1) - return dfs(nums, target, 0, n - 1); - } + [class]{}-[func]{binarySearch} ``` === "TS" ```typescript title="binary_search_recur.ts" - /* 二分查找:问题 f(i, j) */ - function dfs(nums: number[], target: number, i: number, j: number): number { - // 若区间为空,代表无目标元素,则返回 -1 - if (i > j) { - return -1; - } - // 计算中点索引 m - const m = i + ((j - i) >> 1); - if (nums[m] < target) { - // 递归子问题 f(m+1, j) - return dfs(nums, target, m + 1, j); - } else if (nums[m] > target) { - // 递归子问题 f(i, m-1) - return dfs(nums, target, i, m - 1); - } else { - // 找到目标元素,返回其索引 - return m; - } - } + [class]{}-[func]{dfs} - /* 二分查找 */ - function binarySearch(nums: number[], target: number): number { - const n = nums.length; - // 求解问题 f(0, n-1) - return dfs(nums, target, 0, n - 1); - } + [class]{}-[func]{binarySearch} ``` === "Dart" ```dart title="binary_search_recur.dart" - /* 二分查找:问题 f(i, j) */ - int dfs(List nums, int target, int i, int j) { - // 若区间为空,代表无目标元素,则返回 -1 - if (i > j) { - return -1; - } - // 计算中点索引 m - int m = (i + j) ~/ 2; - if (nums[m] < target) { - // 递归子问题 f(m+1, j) - return dfs(nums, target, m + 1, j); - } else if (nums[m] > target) { - // 递归子问题 f(i, m-1) - return dfs(nums, target, i, m - 1); - } else { - // 找到目标元素,返回其索引 - return m; - } - } + [class]{}-[func]{dfs} - /* 二分查找 */ - int binarySearch(List nums, int target) { - int n = nums.length; - // 求解问题 f(0, n-1) - return dfs(nums, target, 0, n - 1); - } + [class]{}-[func]{binarySearch} ``` === "Rust" ```rust title="binary_search_recur.rs" - /* 二分查找:问题 f(i, j) */ - fn dfs(nums: &[i32], target: i32, i: i32, j: i32) -> i32 { - // 若区间为空,代表无目标元素,则返回 -1 - if i > j { - return -1; - } - let m: i32 = (i + j) / 2; - if nums[m as usize] < target { - // 递归子问题 f(m+1, j) - return dfs(nums, target, m + 1, j); - } else if nums[m as usize] > target { - // 递归子问题 f(i, m-1) - return dfs(nums, target, i, m - 1); - } else { - // 找到目标元素,返回其索引 - return m; - } - } + [class]{}-[func]{dfs} - /* 二分查找 */ - fn binary_search(nums: &[i32], target: i32) -> i32 { - let n = nums.len() as i32; - // 求解问题 f(0, n-1) - dfs(nums, target, 0, n - 1) - } + [class]{}-[func]{binary_search} ``` === "C" ```c title="binary_search_recur.c" - /* 二分查找:问题 f(i, j) */ - int dfs(int nums[], int target, int i, int j) { - // 若区间为空,代表无目标元素,则返回 -1 - if (i > j) { - return -1; - } - // 计算中点索引 m - int m = (i + j) / 2; - if (nums[m] < target) { - // 递归子问题 f(m+1, j) - return dfs(nums, target, m + 1, j); - } else if (nums[m] > target) { - // 递归子问题 f(i, m-1) - return dfs(nums, target, i, m - 1); - } else { - // 找到目标元素,返回其索引 - return m; - } - } + [class]{}-[func]{dfs} - /* 二分查找 */ - int binarySearch(int nums[], int target, int numsSize) { - int n = numsSize; - // 求解问题 f(0, n-1) - return dfs(nums, target, 0, n - 1); - } + [class]{}-[func]{binarySearch} ``` === "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 - } - } + [class]{}-[func]{dfs} - /* 二分查找 */ - fun binarySearch(nums: IntArray, target: Int): Int { - val n = nums.size - // 求解问题 f(0, n-1) - return dfs(nums, target, 0, n - 1) - } + [class]{}-[func]{binarySearch} ``` === "Ruby" @@ -434,8 +199,3 @@ In the implementation code, we declare a recursive function `dfs()` to solve the [class]{}-[func]{binarySearch} ``` - -??? pythontutor "Code Visualization" - -
- diff --git a/en/docs/chapter_divide_and_conquer/build_binary_tree_problem.md b/en/docs/chapter_divide_and_conquer/build_binary_tree_problem.md index 00bf188ca..bfe2cc893 100644 --- a/en/docs/chapter_divide_and_conquer/build_binary_tree_problem.md +++ b/en/docs/chapter_divide_and_conquer/build_binary_tree_problem.md @@ -81,24 +81,24 @@ To improve the efficiency of querying $m$, we use a hash table `hmap` to store t l: int, r: int, ) -> TreeNode | None: - """构建二叉树:分治""" - # 子树区间为空时终止 + """Build binary tree: Divide and conquer""" + # Terminate when subtree interval is empty if r - l < 0: return None - # 初始化根节点 + # Initialize root node root = TreeNode(preorder[i]) - # 查询 m ,从而划分左右子树 + # Query m to divide left and right subtrees m = inorder_map[preorder[i]] - # 子问题:构建左子树 + # Subproblem: build left subtree root.left = dfs(preorder, inorder_map, i + 1, l, m - 1) - # 子问题:构建右子树 + # Subproblem: build right subtree root.right = dfs(preorder, inorder_map, i + 1 + m - l, m + 1, r) - # 返回根节点 + # Return root node return root def build_tree(preorder: list[int], inorder: list[int]) -> TreeNode | None: - """构建二叉树""" - # 初始化哈希表,存储 inorder 元素到索引的映射 + """Build binary tree""" + # Initialize hash table, storing in-order elements to indices mapping inorder_map = {val: i for i, val in enumerate(inorder)} root = dfs(preorder, inorder_map, 0, 0, len(inorder) - 1) return root @@ -107,58 +107,34 @@ To improve the efficiency of querying $m$, we use a hash table `hmap` to store t === "C++" ```cpp title="build_tree.cpp" - /* 构建二叉树:分治 */ - TreeNode *dfs(vector &preorder, unordered_map &inorderMap, int i, int l, int r) { - // 子树区间为空时终止 - if (r - l < 0) - return NULL; - // 初始化根节点 - TreeNode *root = new TreeNode(preorder[i]); - // 查询 m ,从而划分左右子树 - int 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; - } + [class]{}-[func]{dfs} - /* 构建二叉树 */ - TreeNode *buildTree(vector &preorder, vector &inorder) { - // 初始化哈希表,存储 inorder 元素到索引的映射 - unordered_map inorderMap; - for (int i = 0; i < inorder.size(); i++) { - inorderMap[inorder[i]] = i; - } - TreeNode *root = dfs(preorder, inorderMap, 0, 0, inorder.size() - 1); - return root; - } + [class]{}-[func]{buildTree} ``` === "Java" ```java title="build_tree.java" - /* 构建二叉树:分治 */ + /* Build binary tree: Divide and conquer */ TreeNode dfs(int[] preorder, Map inorderMap, int i, int l, int r) { - // 子树区间为空时终止 + // Terminate when subtree interval is empty if (r - l < 0) return null; - // 初始化根节点 + // Initialize root node TreeNode root = new TreeNode(preorder[i]); - // 查询 m ,从而划分左右子树 + // Query m to divide left and right subtrees int m = inorderMap.get(preorder[i]); - // 子问题:构建左子树 + // Subproblem: build left subtree root.left = dfs(preorder, inorderMap, i + 1, l, m - 1); - // 子问题:构建右子树 + // Subproblem: build right subtree root.right = dfs(preorder, inorderMap, i + 1 + m - l, m + 1, r); - // 返回根节点 + // Return root node return root; } - /* 构建二叉树 */ + /* Build binary tree */ TreeNode buildTree(int[] preorder, int[] inorder) { - // 初始化哈希表,存储 inorder 元素到索引的映射 + // Initialize hash table, storing in-order elements to indices mapping Map inorderMap = new HashMap<>(); for (int i = 0; i < inorder.length; i++) { inorderMap.put(inorder[i], i); @@ -171,315 +147,73 @@ To improve the efficiency of querying $m$, we use a hash table `hmap` to store t === "C#" ```csharp title="build_tree.cs" - /* 构建二叉树:分治 */ - TreeNode? DFS(int[] preorder, Dictionary inorderMap, int i, int l, int r) { - // 子树区间为空时终止 - if (r - l < 0) - return null; - // 初始化根节点 - TreeNode root = new(preorder[i]); - // 查询 m ,从而划分左右子树 - int 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; - } + [class]{build_tree}-[func]{DFS} - /* 构建二叉树 */ - TreeNode? BuildTree(int[] preorder, int[] inorder) { - // 初始化哈希表,存储 inorder 元素到索引的映射 - Dictionary inorderMap = []; - for (int i = 0; i < inorder.Length; i++) { - inorderMap.TryAdd(inorder[i], i); - } - TreeNode? root = DFS(preorder, inorderMap, 0, 0, inorder.Length - 1); - return root; - } + [class]{build_tree}-[func]{BuildTree} ``` === "Go" ```go title="build_tree.go" - /* 构建二叉树:分治 */ - func dfsBuildTree(preorder []int, inorderMap map[int]int, i, l, r int) *TreeNode { - // 子树区间为空时终止 - if r-l < 0 { - return nil - } - // 初始化根节点 - root := NewTreeNode(preorder[i]) - // 查询 m ,从而划分左右子树 - m := inorderMap[preorder[i]] - // 子问题:构建左子树 - root.Left = dfsBuildTree(preorder, inorderMap, i+1, l, m-1) - // 子问题:构建右子树 - root.Right = dfsBuildTree(preorder, inorderMap, i+1+m-l, m+1, r) - // 返回根节点 - return root - } + [class]{}-[func]{dfsBuildTree} - /* 构建二叉树 */ - func buildTree(preorder, inorder []int) *TreeNode { - // 初始化哈希表,存储 inorder 元素到索引的映射 - inorderMap := make(map[int]int, len(inorder)) - for i := 0; i < len(inorder); i++ { - inorderMap[inorder[i]] = i - } - - root := dfsBuildTree(preorder, inorderMap, 0, 0, len(inorder)-1) - return root - } + [class]{}-[func]{buildTree} ``` === "Swift" ```swift title="build_tree.swift" - /* 构建二叉树:分治 */ - func dfs(preorder: [Int], inorderMap: [Int: Int], i: Int, l: Int, r: Int) -> TreeNode? { - // 子树区间为空时终止 - if r - l < 0 { - return nil - } - // 初始化根节点 - let root = TreeNode(x: preorder[i]) - // 查询 m ,从而划分左右子树 - let m = inorderMap[preorder[i]]! - // 子问题:构建左子树 - root.left = dfs(preorder: preorder, inorderMap: inorderMap, i: i + 1, l: l, r: m - 1) - // 子问题:构建右子树 - root.right = dfs(preorder: preorder, inorderMap: inorderMap, i: i + 1 + m - l, l: m + 1, r: r) - // 返回根节点 - return root - } + [class]{}-[func]{dfs} - /* 构建二叉树 */ - func buildTree(preorder: [Int], inorder: [Int]) -> TreeNode? { - // 初始化哈希表,存储 inorder 元素到索引的映射 - let inorderMap = inorder.enumerated().reduce(into: [:]) { $0[$1.element] = $1.offset } - return dfs(preorder: preorder, inorderMap: inorderMap, i: inorder.startIndex, l: inorder.startIndex, r: inorder.endIndex - 1) - } + [class]{}-[func]{buildTree} ``` === "JS" ```javascript title="build_tree.js" - /* 构建二叉树:分治 */ - function dfs(preorder, inorderMap, i, l, r) { - // 子树区间为空时终止 - if (r - l < 0) return null; - // 初始化根节点 - const root = new TreeNode(preorder[i]); - // 查询 m ,从而划分左右子树 - const m = inorderMap.get(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; - } + [class]{}-[func]{dfs} - /* 构建二叉树 */ - function buildTree(preorder, inorder) { - // 初始化哈希表,存储 inorder 元素到索引的映射 - let inorderMap = new Map(); - for (let i = 0; i < inorder.length; i++) { - inorderMap.set(inorder[i], i); - } - const root = dfs(preorder, inorderMap, 0, 0, inorder.length - 1); - return root; - } + [class]{}-[func]{buildTree} ``` === "TS" ```typescript title="build_tree.ts" - /* 构建二叉树:分治 */ - function dfs( - preorder: number[], - inorderMap: Map, - i: number, - l: number, - r: number - ): TreeNode | null { - // 子树区间为空时终止 - if (r - l < 0) return null; - // 初始化根节点 - const root: TreeNode = new TreeNode(preorder[i]); - // 查询 m ,从而划分左右子树 - const m = inorderMap.get(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; - } + [class]{}-[func]{dfs} - /* 构建二叉树 */ - function buildTree(preorder: number[], inorder: number[]): TreeNode | null { - // 初始化哈希表,存储 inorder 元素到索引的映射 - let inorderMap = new Map(); - for (let i = 0; i < inorder.length; i++) { - inorderMap.set(inorder[i], i); - } - const root = dfs(preorder, inorderMap, 0, 0, inorder.length - 1); - return root; - } + [class]{}-[func]{buildTree} ``` === "Dart" ```dart title="build_tree.dart" - /* 构建二叉树:分治 */ - TreeNode? dfs( - List preorder, - Map inorderMap, - int i, - int l, - int r, - ) { - // 子树区间为空时终止 - if (r - l < 0) { - return null; - } - // 初始化根节点 - TreeNode? root = TreeNode(preorder[i]); - // 查询 m ,从而划分左右子树 - int 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; - } + [class]{}-[func]{dfs} - /* 构建二叉树 */ - TreeNode? buildTree(List preorder, List inorder) { - // 初始化哈希表,存储 inorder 元素到索引的映射 - Map inorderMap = {}; - for (int i = 0; i < inorder.length; i++) { - inorderMap[inorder[i]] = i; - } - TreeNode? root = dfs(preorder, inorderMap, 0, 0, inorder.length - 1); - return root; - } + [class]{}-[func]{buildTree} ``` === "Rust" ```rust title="build_tree.rs" - /* 构建二叉树:分治 */ - fn dfs( - preorder: &[i32], - inorder_map: &HashMap, - i: i32, - l: i32, - r: i32, - ) -> Option>> { - // 子树区间为空时终止 - if r - l < 0 { - return None; - } - // 初始化根节点 - let root = TreeNode::new(preorder[i as usize]); - // 查询 m ,从而划分左右子树 - let m = inorder_map.get(&preorder[i as usize]).unwrap(); - // 子问题:构建左子树 - root.borrow_mut().left = dfs(preorder, inorder_map, i + 1, l, m - 1); - // 子问题:构建右子树 - root.borrow_mut().right = dfs(preorder, inorder_map, i + 1 + m - l, m + 1, r); - // 返回根节点 - Some(root) - } + [class]{}-[func]{dfs} - /* 构建二叉树 */ - fn build_tree(preorder: &[i32], inorder: &[i32]) -> Option>> { - // 初始化哈希表,存储 inorder 元素到索引的映射 - let mut inorder_map: HashMap = HashMap::new(); - for i in 0..inorder.len() { - inorder_map.insert(inorder[i], i as i32); - } - let root = dfs(preorder, &inorder_map, 0, 0, inorder.len() as i32 - 1); - root - } + [class]{}-[func]{build_tree} ``` === "C" ```c title="build_tree.c" - /* 构建二叉树:分治 */ - TreeNode *dfs(int *preorder, int *inorderMap, int i, int l, int r, int size) { - // 子树区间为空时终止 - if (r - l < 0) - return NULL; - // 初始化根节点 - TreeNode *root = (TreeNode *)malloc(sizeof(TreeNode)); - root->val = preorder[i]; - root->left = NULL; - root->right = NULL; - // 查询 m ,从而划分左右子树 - int m = inorderMap[preorder[i]]; - // 子问题:构建左子树 - root->left = dfs(preorder, inorderMap, i + 1, l, m - 1, size); - // 子问题:构建右子树 - root->right = dfs(preorder, inorderMap, i + 1 + m - l, m + 1, r, size); - // 返回根节点 - return root; - } + [class]{}-[func]{dfs} - /* 构建二叉树 */ - TreeNode *buildTree(int *preorder, int preorderSize, int *inorder, int inorderSize) { - // 初始化哈希表,存储 inorder 元素到索引的映射 - int *inorderMap = (int *)malloc(sizeof(int) * MAX_SIZE); - for (int i = 0; i < inorderSize; i++) { - inorderMap[inorder[i]] = i; - } - TreeNode *root = dfs(preorder, inorderMap, 0, 0, inorderSize - 1, inorderSize); - free(inorderMap); - return root; - } + [class]{}-[func]{buildTree} ``` === "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 - } + [class]{}-[func]{dfs} - /* 构建二叉树 */ - fun buildTree(preorder: IntArray, inorder: IntArray): TreeNode? { - // 初始化哈希表,存储 inorder 元素到索引的映射 - val inorderMap = HashMap() - for (i in inorder.indices) { - inorderMap[inorder[i]] = i - } - val root = dfs(preorder, inorderMap, 0, 0, inorder.size - 1) - return root - } + [class]{}-[func]{buildTree} ``` === "Ruby" @@ -498,11 +232,6 @@ To improve the efficiency of querying $m$, we use a hash table `hmap` to store t [class]{}-[func]{buildTree} ``` -??? pythontutor "Code Visualization" - -
- - The diagram below shows the recursive process of building the binary tree, where each node is established during the "descending" process, and each edge (reference) is established during the "ascending" process. === "<1>" diff --git a/en/docs/chapter_divide_and_conquer/hanota_problem.md b/en/docs/chapter_divide_and_conquer/hanota_problem.md index c8059fc22..e4393f31f 100644 --- a/en/docs/chapter_divide_and_conquer/hanota_problem.md +++ b/en/docs/chapter_divide_and_conquer/hanota_problem.md @@ -100,97 +100,72 @@ In the code, we declare a recursive function `dfs(i, src, buf, tar)` whose role ```python title="hanota.py" def move(src: list[int], tar: list[int]): - """移动一个圆盘""" - # 从 src 顶部拿出一个圆盘 + """Move a disc""" + # Take out a disc from the top of src pan = src.pop() - # 将圆盘放入 tar 顶部 + # Place the disc on top of tar tar.append(pan) def dfs(i: int, src: list[int], buf: list[int], tar: list[int]): - """求解汉诺塔问题 f(i)""" - # 若 src 只剩下一个圆盘,则直接将其移到 tar + """Solve the Tower of Hanoi problem f(i)""" + # If only one disc remains on src, move it to tar if i == 1: move(src, tar) return - # 子问题 f(i-1) :将 src 顶部 i-1 个圆盘借助 tar 移到 buf + # Subproblem f(i-1): move the top i-1 discs from src with the help of tar to buf dfs(i - 1, src, tar, buf) - # 子问题 f(1) :将 src 剩余一个圆盘移到 tar + # Subproblem f(1): move the remaining one disc from src to tar move(src, tar) - # 子问题 f(i-1) :将 buf 顶部 i-1 个圆盘借助 src 移到 tar + # Subproblem f(i-1): move the top i-1 discs from buf with the help of src to tar dfs(i - 1, buf, src, tar) def solve_hanota(A: list[int], B: list[int], C: list[int]): - """求解汉诺塔问题""" + """Solve the Tower of Hanoi problem""" n = len(A) - # 将 A 顶部 n 个圆盘借助 B 移到 C + # Move the top n discs from A with the help of B to C dfs(n, A, B, C) ``` === "C++" ```cpp title="hanota.cpp" - /* 移动一个圆盘 */ - void move(vector &src, vector &tar) { - // 从 src 顶部拿出一个圆盘 - int pan = src.back(); - src.pop_back(); - // 将圆盘放入 tar 顶部 - tar.push_back(pan); - } + [class]{}-[func]{move} - /* 求解汉诺塔问题 f(i) */ - void dfs(int i, vector &src, vector &buf, vector &tar) { - // 若 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); - } + [class]{}-[func]{dfs} - /* 求解汉诺塔问题 */ - void solveHanota(vector &A, vector &B, vector &C) { - int n = A.size(); - // 将 A 顶部 n 个圆盘借助 B 移到 C - dfs(n, A, B, C); - } + [class]{}-[func]{solveHanota} ``` === "Java" ```java title="hanota.java" - /* 移动一个圆盘 */ + /* Move a disc */ void move(List src, List tar) { - // 从 src 顶部拿出一个圆盘 + // Take out a disc from the top of src Integer pan = src.remove(src.size() - 1); - // 将圆盘放入 tar 顶部 + // Place the disc on top of tar tar.add(pan); } - /* 求解汉诺塔问题 f(i) */ + /* Solve the Tower of Hanoi problem f(i) */ void dfs(int i, List src, List buf, List tar) { - // 若 src 只剩下一个圆盘,则直接将其移到 tar + // If only one disc remains on src, move it to tar if (i == 1) { move(src, tar); return; } - // 子问题 f(i-1) :将 src 顶部 i-1 个圆盘借助 tar 移到 buf + // Subproblem f(i-1): move the top i-1 discs from src with the help of tar to buf dfs(i - 1, src, tar, buf); - // 子问题 f(1) :将 src 剩余一个圆盘移到 tar + // Subproblem f(1): move the remaining one disc from src to tar move(src, tar); - // 子问题 f(i-1) :将 buf 顶部 i-1 个圆盘借助 src 移到 tar + // Subproblem f(i-1): move the top i-1 discs from buf with the help of src to tar dfs(i - 1, buf, src, tar); } - /* 求解汉诺塔问题 */ + /* Solve the Tower of Hanoi problem */ void solveHanota(List A, List B, List C) { int n = A.size(); - // 将 A 顶部 n 个圆盘借助 B 移到 C + // Move the top n discs from A with the help of B to C dfs(n, A, B, C); } ``` @@ -198,313 +173,91 @@ In the code, we declare a recursive function `dfs(i, src, buf, tar)` whose role === "C#" ```csharp title="hanota.cs" - /* 移动一个圆盘 */ - void Move(List src, List tar) { - // 从 src 顶部拿出一个圆盘 - int pan = src[^1]; - src.RemoveAt(src.Count - 1); - // 将圆盘放入 tar 顶部 - tar.Add(pan); - } + [class]{hanota}-[func]{Move} - /* 求解汉诺塔问题 f(i) */ - void DFS(int i, List src, List buf, List tar) { - // 若 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); - } + [class]{hanota}-[func]{DFS} - /* 求解汉诺塔问题 */ - void SolveHanota(List A, List B, List C) { - int n = A.Count; - // 将 A 顶部 n 个圆盘借助 B 移到 C - DFS(n, A, B, C); - } + [class]{hanota}-[func]{SolveHanota} ``` === "Go" ```go title="hanota.go" - /* 移动一个圆盘 */ - func move(src, tar *list.List) { - // 从 src 顶部拿出一个圆盘 - pan := src.Back() - // 将圆盘放入 tar 顶部 - tar.PushBack(pan.Value) - // 移除 src 顶部圆盘 - src.Remove(pan) - } + [class]{}-[func]{move} - /* 求解汉诺塔问题 f(i) */ - func dfsHanota(i int, src, buf, tar *list.List) { - // 若 src 只剩下一个圆盘,则直接将其移到 tar - if i == 1 { - move(src, tar) - return - } - // 子问题 f(i-1) :将 src 顶部 i-1 个圆盘借助 tar 移到 buf - dfsHanota(i-1, src, tar, buf) - // 子问题 f(1) :将 src 剩余一个圆盘移到 tar - move(src, tar) - // 子问题 f(i-1) :将 buf 顶部 i-1 个圆盘借助 src 移到 tar - dfsHanota(i-1, buf, src, tar) - } + [class]{}-[func]{dfsHanota} - /* 求解汉诺塔问题 */ - func solveHanota(A, B, C *list.List) { - n := A.Len() - // 将 A 顶部 n 个圆盘借助 B 移到 C - dfsHanota(n, A, B, C) - } + [class]{}-[func]{solveHanota} ``` === "Swift" ```swift title="hanota.swift" - /* 移动一个圆盘 */ - func move(src: inout [Int], tar: inout [Int]) { - // 从 src 顶部拿出一个圆盘 - let pan = src.popLast()! - // 将圆盘放入 tar 顶部 - tar.append(pan) - } + [class]{}-[func]{move} - /* 求解汉诺塔问题 f(i) */ - func dfs(i: Int, src: inout [Int], buf: inout [Int], tar: inout [Int]) { - // 若 src 只剩下一个圆盘,则直接将其移到 tar - if i == 1 { - move(src: &src, tar: &tar) - return - } - // 子问题 f(i-1) :将 src 顶部 i-1 个圆盘借助 tar 移到 buf - dfs(i: i - 1, src: &src, buf: &tar, tar: &buf) - // 子问题 f(1) :将 src 剩余一个圆盘移到 tar - move(src: &src, tar: &tar) - // 子问题 f(i-1) :将 buf 顶部 i-1 个圆盘借助 src 移到 tar - dfs(i: i - 1, src: &buf, buf: &src, tar: &tar) - } + [class]{}-[func]{dfs} - /* 求解汉诺塔问题 */ - func solveHanota(A: inout [Int], B: inout [Int], C: inout [Int]) { - let n = A.count - // 列表尾部是柱子顶部 - // 将 src 顶部 n 个圆盘借助 B 移到 C - dfs(i: n, src: &A, buf: &B, tar: &C) - } + [class]{}-[func]{solveHanota} ``` === "JS" ```javascript title="hanota.js" - /* 移动一个圆盘 */ - function move(src, tar) { - // 从 src 顶部拿出一个圆盘 - const pan = src.pop(); - // 将圆盘放入 tar 顶部 - tar.push(pan); - } + [class]{}-[func]{move} - /* 求解汉诺塔问题 f(i) */ - function dfs(i, src, buf, tar) { - // 若 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); - } + [class]{}-[func]{dfs} - /* 求解汉诺塔问题 */ - function solveHanota(A, B, C) { - const n = A.length; - // 将 A 顶部 n 个圆盘借助 B 移到 C - dfs(n, A, B, C); - } + [class]{}-[func]{solveHanota} ``` === "TS" ```typescript title="hanota.ts" - /* 移动一个圆盘 */ - function move(src: number[], tar: number[]): void { - // 从 src 顶部拿出一个圆盘 - const pan = src.pop(); - // 将圆盘放入 tar 顶部 - tar.push(pan); - } + [class]{}-[func]{move} - /* 求解汉诺塔问题 f(i) */ - function dfs(i: number, src: number[], buf: number[], tar: number[]): void { - // 若 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); - } + [class]{}-[func]{dfs} - /* 求解汉诺塔问题 */ - function solveHanota(A: number[], B: number[], C: number[]): void { - const n = A.length; - // 将 A 顶部 n 个圆盘借助 B 移到 C - dfs(n, A, B, C); - } + [class]{}-[func]{solveHanota} ``` === "Dart" ```dart title="hanota.dart" - /* 移动一个圆盘 */ - void move(List src, List tar) { - // 从 src 顶部拿出一个圆盘 - int pan = src.removeLast(); - // 将圆盘放入 tar 顶部 - tar.add(pan); - } + [class]{}-[func]{move} - /* 求解汉诺塔问题 f(i) */ - void dfs(int i, List src, List buf, List tar) { - // 若 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); - } + [class]{}-[func]{dfs} - /* 求解汉诺塔问题 */ - void solveHanota(List A, List B, List C) { - int n = A.length; - // 将 A 顶部 n 个圆盘借助 B 移到 C - dfs(n, A, B, C); - } + [class]{}-[func]{solveHanota} ``` === "Rust" ```rust title="hanota.rs" - /* 移动一个圆盘 */ - fn move_pan(src: &mut Vec, tar: &mut Vec) { - // 从 src 顶部拿出一个圆盘 - let pan = src.remove(src.len() - 1); - // 将圆盘放入 tar 顶部 - tar.push(pan); - } + [class]{}-[func]{move_pan} - /* 求解汉诺塔问题 f(i) */ - fn dfs(i: i32, src: &mut Vec, buf: &mut Vec, tar: &mut Vec) { - // 若 src 只剩下一个圆盘,则直接将其移到 tar - if i == 1 { - move_pan(src, tar); - return; - } - // 子问题 f(i-1) :将 src 顶部 i-1 个圆盘借助 tar 移到 buf - dfs(i - 1, src, tar, buf); - // 子问题 f(1) :将 src 剩余一个圆盘移到 tar - move_pan(src, tar); - // 子问题 f(i-1) :将 buf 顶部 i-1 个圆盘借助 src 移到 tar - dfs(i - 1, buf, src, tar); - } + [class]{}-[func]{dfs} - /* 求解汉诺塔问题 */ - fn solve_hanota(A: &mut Vec, B: &mut Vec, C: &mut Vec) { - let n = A.len() as i32; - // 将 A 顶部 n 个圆盘借助 B 移到 C - dfs(n, A, B, C); - } + [class]{}-[func]{solve_hanota} ``` === "C" ```c title="hanota.c" - /* 移动一个圆盘 */ - void move(int *src, int *srcSize, int *tar, int *tarSize) { - // 从 src 顶部拿出一个圆盘 - int pan = src[*srcSize - 1]; - src[*srcSize - 1] = 0; - (*srcSize)--; - // 将圆盘放入 tar 顶部 - tar[*tarSize] = pan; - (*tarSize)++; - } + [class]{}-[func]{move} - /* 求解汉诺塔问题 f(i) */ - void dfs(int i, int *src, int *srcSize, int *buf, int *bufSize, int *tar, int *tarSize) { - // 若 src 只剩下一个圆盘,则直接将其移到 tar - if (i == 1) { - move(src, srcSize, tar, tarSize); - return; - } - // 子问题 f(i-1) :将 src 顶部 i-1 个圆盘借助 tar 移到 buf - dfs(i - 1, src, srcSize, tar, tarSize, buf, bufSize); - // 子问题 f(1) :将 src 剩余一个圆盘移到 tar - move(src, srcSize, tar, tarSize); - // 子问题 f(i-1) :将 buf 顶部 i-1 个圆盘借助 src 移到 tar - dfs(i - 1, buf, bufSize, src, srcSize, tar, tarSize); - } + [class]{}-[func]{dfs} - /* 求解汉诺塔问题 */ - void solveHanota(int *A, int *ASize, int *B, int *BSize, int *C, int *CSize) { - // 将 A 顶部 n 个圆盘借助 B 移到 C - dfs(*ASize, A, ASize, B, BSize, C, CSize); - } + [class]{}-[func]{solveHanota} ``` === "Kotlin" ```kotlin title="hanota.kt" - /* 移动一个圆盘 */ - fun move(src: MutableList, tar: MutableList) { - // 从 src 顶部拿出一个圆盘 - val pan = src.removeAt(src.size - 1) - // 将圆盘放入 tar 顶部 - tar.add(pan) - } + [class]{}-[func]{move} - /* 求解汉诺塔问题 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) - } + [class]{}-[func]{dfs} - /* 求解汉诺塔问题 */ - fun solveHanota(A: MutableList, B: MutableList, C: MutableList) { - val n = A.size - // 将 A 顶部 n 个圆盘借助 B 移到 C - dfs(n, A, B, C) - } + [class]{}-[func]{solveHanota} ``` === "Ruby" @@ -527,11 +280,6 @@ In the code, we declare a recursive function `dfs(i, src, buf, tar)` whose role [class]{}-[func]{solveHanota} ``` -??? pythontutor "Code Visualization" - -
- - As shown in Figure 12-15, the Tower of Hanoi forms a recursive tree with a height of $n$, each node representing a subproblem, corresponding to an open `dfs()` function, **thus the time complexity is $O(2^n)$, and the space complexity is $O(n)$**. ![Recursive tree of the Tower of Hanoi](hanota_problem.assets/hanota_recursive_tree.png){ class="animation-figure" } diff --git a/en/docs/chapter_dynamic_programming/dp_problem_features.md b/en/docs/chapter_dynamic_programming/dp_problem_features.md index a0eac59dd..369ad6ff9 100644 --- a/en/docs/chapter_dynamic_programming/dp_problem_features.md +++ b/en/docs/chapter_dynamic_programming/dp_problem_features.md @@ -44,15 +44,15 @@ According to the state transition equation, and the initial states $dp[1] = cost ```python title="min_cost_climbing_stairs_dp.py" def min_cost_climbing_stairs_dp(cost: list[int]) -> int: - """爬楼梯最小代价:动态规划""" + """Climbing stairs with minimum cost: Dynamic programming""" n = len(cost) - 1 if n == 1 or n == 2: return cost[n] - # 初始化 dp 表,用于存储子问题的解 + # Initialize dp table, used to store subproblem solutions dp = [0] * (n + 1) - # 初始状态:预设最小子问题的解 + # Initial state: preset the smallest subproblem solution dp[1], dp[2] = cost[1], cost[2] - # 状态转移:从较小子问题逐步求解较大子问题 + # State transition: gradually solve larger subproblems from smaller ones for i in range(3, n + 1): dp[i] = min(dp[i - 1], dp[i - 2]) + cost[i] return dp[n] @@ -61,38 +61,23 @@ According to the state transition equation, and the initial states $dp[1] = cost === "C++" ```cpp title="min_cost_climbing_stairs_dp.cpp" - /* 爬楼梯最小代价:动态规划 */ - int minCostClimbingStairsDP(vector &cost) { - int n = cost.size() - 1; - if (n == 1 || n == 2) - return cost[n]; - // 初始化 dp 表,用于存储子问题的解 - vector dp(n + 1); - // 初始状态:预设最小子问题的解 - dp[1] = cost[1]; - dp[2] = cost[2]; - // 状态转移:从较小子问题逐步求解较大子问题 - for (int i = 3; i <= n; i++) { - dp[i] = min(dp[i - 1], dp[i - 2]) + cost[i]; - } - return dp[n]; - } + [class]{}-[func]{minCostClimbingStairsDP} ``` === "Java" ```java title="min_cost_climbing_stairs_dp.java" - /* 爬楼梯最小代价:动态规划 */ + /* Climbing stairs with minimum cost: Dynamic programming */ int minCostClimbingStairsDP(int[] cost) { int n = cost.length - 1; if (n == 1 || n == 2) return cost[n]; - // 初始化 dp 表,用于存储子问题的解 + // Initialize dp table, used to store subproblem solutions int[] dp = new int[n + 1]; - // 初始状态:预设最小子问题的解 + // Initial state: preset the smallest subproblem solution dp[1] = cost[1]; dp[2] = cost[2]; - // 状态转移:从较小子问题逐步求解较大子问题 + // State transition: gradually solve larger subproblems from smaller ones for (int i = 3; i <= n; i++) { dp[i] = Math.min(dp[i - 1], dp[i - 2]) + cost[i]; } @@ -103,202 +88,55 @@ According to the state transition equation, and the initial states $dp[1] = cost === "C#" ```csharp title="min_cost_climbing_stairs_dp.cs" - /* 爬楼梯最小代价:动态规划 */ - int MinCostClimbingStairsDP(int[] cost) { - int n = cost.Length - 1; - if (n == 1 || n == 2) - return cost[n]; - // 初始化 dp 表,用于存储子问题的解 - int[] dp = new int[n + 1]; - // 初始状态:预设最小子问题的解 - dp[1] = cost[1]; - dp[2] = cost[2]; - // 状态转移:从较小子问题逐步求解较大子问题 - for (int i = 3; i <= n; i++) { - dp[i] = Math.Min(dp[i - 1], dp[i - 2]) + cost[i]; - } - return dp[n]; - } + [class]{min_cost_climbing_stairs_dp}-[func]{MinCostClimbingStairsDP} ``` === "Go" ```go title="min_cost_climbing_stairs_dp.go" - /* 爬楼梯最小代价:动态规划 */ - func minCostClimbingStairsDP(cost []int) int { - n := len(cost) - 1 - if n == 1 || n == 2 { - return cost[n] - } - min := func(a, b int) int { - if a < b { - return a - } - return b - } - // 初始化 dp 表,用于存储子问题的解 - dp := make([]int, n+1) - // 初始状态:预设最小子问题的解 - dp[1] = cost[1] - dp[2] = cost[2] - // 状态转移:从较小子问题逐步求解较大子问题 - for i := 3; i <= n; i++ { - dp[i] = min(dp[i-1], dp[i-2]) + cost[i] - } - return dp[n] - } + [class]{}-[func]{minCostClimbingStairsDP} ``` === "Swift" ```swift title="min_cost_climbing_stairs_dp.swift" - /* 爬楼梯最小代价:动态规划 */ - func minCostClimbingStairsDP(cost: [Int]) -> Int { - let n = cost.count - 1 - if n == 1 || n == 2 { - return cost[n] - } - // 初始化 dp 表,用于存储子问题的解 - var dp = Array(repeating: 0, count: n + 1) - // 初始状态:预设最小子问题的解 - dp[1] = cost[1] - dp[2] = cost[2] - // 状态转移:从较小子问题逐步求解较大子问题 - for i in 3 ... n { - dp[i] = min(dp[i - 1], dp[i - 2]) + cost[i] - } - return dp[n] - } + [class]{}-[func]{minCostClimbingStairsDP} ``` === "JS" ```javascript title="min_cost_climbing_stairs_dp.js" - /* 爬楼梯最小代价:动态规划 */ - function minCostClimbingStairsDP(cost) { - const n = cost.length - 1; - if (n === 1 || n === 2) { - return cost[n]; - } - // 初始化 dp 表,用于存储子问题的解 - const dp = new Array(n + 1); - // 初始状态:预设最小子问题的解 - dp[1] = cost[1]; - dp[2] = cost[2]; - // 状态转移:从较小子问题逐步求解较大子问题 - for (let i = 3; i <= n; i++) { - dp[i] = Math.min(dp[i - 1], dp[i - 2]) + cost[i]; - } - return dp[n]; - } + [class]{}-[func]{minCostClimbingStairsDP} ``` === "TS" ```typescript title="min_cost_climbing_stairs_dp.ts" - /* 爬楼梯最小代价:动态规划 */ - function minCostClimbingStairsDP(cost: Array): number { - const n = cost.length - 1; - if (n === 1 || n === 2) { - return cost[n]; - } - // 初始化 dp 表,用于存储子问题的解 - const dp = new Array(n + 1); - // 初始状态:预设最小子问题的解 - dp[1] = cost[1]; - dp[2] = cost[2]; - // 状态转移:从较小子问题逐步求解较大子问题 - for (let i = 3; i <= n; i++) { - dp[i] = Math.min(dp[i - 1], dp[i - 2]) + cost[i]; - } - return dp[n]; - } + [class]{}-[func]{minCostClimbingStairsDP} ``` === "Dart" ```dart title="min_cost_climbing_stairs_dp.dart" - /* 爬楼梯最小代价:动态规划 */ - int minCostClimbingStairsDP(List cost) { - int n = cost.length - 1; - if (n == 1 || n == 2) return cost[n]; - // 初始化 dp 表,用于存储子问题的解 - List dp = List.filled(n + 1, 0); - // 初始状态:预设最小子问题的解 - dp[1] = cost[1]; - dp[2] = cost[2]; - // 状态转移:从较小子问题逐步求解较大子问题 - for (int i = 3; i <= n; i++) { - dp[i] = min(dp[i - 1], dp[i - 2]) + cost[i]; - } - return dp[n]; - } + [class]{}-[func]{minCostClimbingStairsDP} ``` === "Rust" ```rust title="min_cost_climbing_stairs_dp.rs" - /* 爬楼梯最小代价:动态规划 */ - fn min_cost_climbing_stairs_dp(cost: &[i32]) -> i32 { - let n = cost.len() - 1; - if n == 1 || n == 2 { - return cost[n]; - } - // 初始化 dp 表,用于存储子问题的解 - let mut dp = vec![-1; n + 1]; - // 初始状态:预设最小子问题的解 - dp[1] = cost[1]; - dp[2] = cost[2]; - // 状态转移:从较小子问题逐步求解较大子问题 - for i in 3..=n { - dp[i] = cmp::min(dp[i - 1], dp[i - 2]) + cost[i]; - } - dp[n] - } + [class]{}-[func]{min_cost_climbing_stairs_dp} ``` === "C" ```c title="min_cost_climbing_stairs_dp.c" - /* 爬楼梯最小代价:动态规划 */ - int minCostClimbingStairsDP(int cost[], int costSize) { - int n = costSize - 1; - if (n == 1 || n == 2) - return cost[n]; - // 初始化 dp 表,用于存储子问题的解 - int *dp = calloc(n + 1, sizeof(int)); - // 初始状态:预设最小子问题的解 - dp[1] = cost[1]; - dp[2] = cost[2]; - // 状态转移:从较小子问题逐步求解较大子问题 - for (int i = 3; i <= n; i++) { - dp[i] = myMin(dp[i - 1], dp[i - 2]) + cost[i]; - } - int res = dp[n]; - // 释放内存 - free(dp); - return res; - } + [class]{}-[func]{minCostClimbingStairsDP} ``` === "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], dp[i - 2]) + cost[i] - } - return dp[n] - } + [class]{}-[func]{minCostClimbingStairsDP} ``` === "Ruby" @@ -310,30 +148,9 @@ According to the state transition equation, and the initial states $dp[1] = cost === "Zig" ```zig title="min_cost_climbing_stairs_dp.zig" - // 爬楼梯最小代价:动态规划 - fn minCostClimbingStairsDP(comptime cost: []i32) i32 { - comptime var n = cost.len - 1; - if (n == 1 or n == 2) { - return cost[n]; - } - // 初始化 dp 表,用于存储子问题的解 - var dp = [_]i32{-1} ** (n + 1); - // 初始状态:预设最小子问题的解 - dp[1] = cost[1]; - dp[2] = cost[2]; - // 状态转移:从较小子问题逐步求解较大子问题 - for (3..n + 1) |i| { - dp[i] = @min(dp[i - 1], dp[i - 2]) + cost[i]; - } - return dp[n]; - } + [class]{}-[func]{minCostClimbingStairsDP} ``` -??? pythontutor "Code Visualization" - -
- - Figure 14-7 shows the dynamic programming process for the above code. ![Dynamic programming process for minimum cost of climbing stairs](dp_problem_features.assets/min_cost_cs_dp.png){ class="animation-figure" } @@ -346,7 +163,7 @@ This problem can also be space-optimized, compressing one dimension to zero, red ```python title="min_cost_climbing_stairs_dp.py" def min_cost_climbing_stairs_dp_comp(cost: list[int]) -> int: - """爬楼梯最小代价:空间优化后的动态规划""" + """Climbing stairs with minimum cost: Space-optimized dynamic programming""" n = len(cost) - 1 if n == 1 or n == 2: return cost[n] @@ -359,25 +176,13 @@ This problem can also be space-optimized, compressing one dimension to zero, red === "C++" ```cpp title="min_cost_climbing_stairs_dp.cpp" - /* 爬楼梯最小代价:空间优化后的动态规划 */ - int minCostClimbingStairsDPComp(vector &cost) { - int n = cost.size() - 1; - if (n == 1 || n == 2) - return cost[n]; - int a = cost[1], b = cost[2]; - for (int i = 3; i <= n; i++) { - int tmp = b; - b = min(a, tmp) + cost[i]; - a = tmp; - } - return b; - } + [class]{}-[func]{minCostClimbingStairsDPComp} ``` === "Java" ```java title="min_cost_climbing_stairs_dp.java" - /* 爬楼梯最小代价:空间优化后的动态规划 */ + /* Climbing stairs with minimum cost: Space-optimized dynamic programming */ int minCostClimbingStairsDPComp(int[] cost) { int n = cost.length - 1; if (n == 1 || n == 2) @@ -395,175 +200,55 @@ This problem can also be space-optimized, compressing one dimension to zero, red === "C#" ```csharp title="min_cost_climbing_stairs_dp.cs" - /* 爬楼梯最小代价:空间优化后的动态规划 */ - int MinCostClimbingStairsDPComp(int[] cost) { - int n = cost.Length - 1; - if (n == 1 || n == 2) - return cost[n]; - int a = cost[1], b = cost[2]; - for (int i = 3; i <= n; i++) { - int tmp = b; - b = Math.Min(a, tmp) + cost[i]; - a = tmp; - } - return b; - } + [class]{min_cost_climbing_stairs_dp}-[func]{MinCostClimbingStairsDPComp} ``` === "Go" ```go title="min_cost_climbing_stairs_dp.go" - /* 爬楼梯最小代价:空间优化后的动态规划 */ - func minCostClimbingStairsDPComp(cost []int) int { - n := len(cost) - 1 - if n == 1 || n == 2 { - return cost[n] - } - min := func(a, b int) int { - if a < b { - return a - } - return b - } - // 初始状态:预设最小子问题的解 - a, b := cost[1], cost[2] - // 状态转移:从较小子问题逐步求解较大子问题 - for i := 3; i <= n; i++ { - tmp := b - b = min(a, tmp) + cost[i] - a = tmp - } - return b - } + [class]{}-[func]{minCostClimbingStairsDPComp} ``` === "Swift" ```swift title="min_cost_climbing_stairs_dp.swift" - /* 爬楼梯最小代价:空间优化后的动态规划 */ - func minCostClimbingStairsDPComp(cost: [Int]) -> Int { - let n = cost.count - 1 - if n == 1 || n == 2 { - return cost[n] - } - var (a, b) = (cost[1], cost[2]) - for i in 3 ... n { - (a, b) = (b, min(a, b) + cost[i]) - } - return b - } + [class]{}-[func]{minCostClimbingStairsDPComp} ``` === "JS" ```javascript title="min_cost_climbing_stairs_dp.js" - /* 爬楼梯最小代价:状态压缩后的动态规划 */ - function minCostClimbingStairsDPComp(cost) { - const n = cost.length - 1; - if (n === 1 || n === 2) { - return cost[n]; - } - let a = cost[1], - b = cost[2]; - for (let i = 3; i <= n; i++) { - const tmp = b; - b = Math.min(a, tmp) + cost[i]; - a = tmp; - } - return b; - } + [class]{}-[func]{minCostClimbingStairsDPComp} ``` === "TS" ```typescript title="min_cost_climbing_stairs_dp.ts" - /* 爬楼梯最小代价:状态压缩后的动态规划 */ - function minCostClimbingStairsDPComp(cost: Array): number { - const n = cost.length - 1; - if (n === 1 || n === 2) { - return cost[n]; - } - let a = cost[1], - b = cost[2]; - for (let i = 3; i <= n; i++) { - const tmp = b; - b = Math.min(a, tmp) + cost[i]; - a = tmp; - } - return b; - } + [class]{}-[func]{minCostClimbingStairsDPComp} ``` === "Dart" ```dart title="min_cost_climbing_stairs_dp.dart" - /* 爬楼梯最小代价:空间优化后的动态规划 */ - int minCostClimbingStairsDPComp(List cost) { - int n = cost.length - 1; - if (n == 1 || n == 2) return cost[n]; - int a = cost[1], b = cost[2]; - for (int i = 3; i <= n; i++) { - int tmp = b; - b = min(a, tmp) + cost[i]; - a = tmp; - } - return b; - } + [class]{}-[func]{minCostClimbingStairsDPComp} ``` === "Rust" ```rust title="min_cost_climbing_stairs_dp.rs" - /* 爬楼梯最小代价:空间优化后的动态规划 */ - fn min_cost_climbing_stairs_dp_comp(cost: &[i32]) -> i32 { - let n = cost.len() - 1; - if n == 1 || n == 2 { - return cost[n]; - }; - let (mut a, mut b) = (cost[1], cost[2]); - for i in 3..=n { - let tmp = b; - b = cmp::min(a, tmp) + cost[i]; - a = tmp; - } - b - } + [class]{}-[func]{min_cost_climbing_stairs_dp_comp} ``` === "C" ```c title="min_cost_climbing_stairs_dp.c" - /* 爬楼梯最小代价:空间优化后的动态规划 */ - int minCostClimbingStairsDPComp(int cost[], int costSize) { - int n = costSize - 1; - if (n == 1 || n == 2) - return cost[n]; - int a = cost[1], b = cost[2]; - for (int i = 3; i <= n; i++) { - int tmp = b; - b = myMin(a, tmp) + cost[i]; - a = tmp; - } - return b; - } + [class]{}-[func]{minCostClimbingStairsDPComp} ``` === "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, tmp) + cost[i] - a = tmp - } - return b - } + [class]{}-[func]{minCostClimbingStairsDPComp} ``` === "Ruby" @@ -575,29 +260,9 @@ This problem can also be space-optimized, compressing one dimension to zero, red === "Zig" ```zig title="min_cost_climbing_stairs_dp.zig" - // 爬楼梯最小代价:空间优化后的动态规划 - fn minCostClimbingStairsDPComp(cost: []i32) i32 { - var n = cost.len - 1; - if (n == 1 or n == 2) { - return cost[n]; - } - var a = cost[1]; - var b = cost[2]; - // 状态转移:从较小子问题逐步求解较大子问题 - for (3..n + 1) |i| { - var tmp = b; - b = @min(a, tmp) + cost[i]; - a = tmp; - } - return b; - } + [class]{}-[func]{minCostClimbingStairsDPComp} ``` -??? pythontutor "Code Visualization" - -
- - ## 14.2.2   Statelessness Statelessness is one of the important characteristics that make dynamic programming effective in solving problems. Its definition is: **Given a certain state, its future development is only related to the current state and unrelated to all past states experienced**. @@ -644,15 +309,15 @@ In the end, returning $dp[n, 1] + dp[n, 2]$ will do, the sum of the two represen ```python title="climbing_stairs_constraint_dp.py" def climbing_stairs_constraint_dp(n: int) -> int: - """带约束爬楼梯:动态规划""" + """Constrained climbing stairs: Dynamic programming""" if n == 1 or n == 2: return 1 - # 初始化 dp 表,用于存储子问题的解 + # Initialize dp table, used to store subproblem solutions dp = [[0] * 3 for _ in range(n + 1)] - # 初始状态:预设最小子问题的解 + # Initial state: preset the smallest subproblem solution dp[1][1], dp[1][2] = 1, 0 dp[2][1], dp[2][2] = 0, 1 - # 状态转移:从较小子问题逐步求解较大子问题 + # State transition: gradually solve larger subproblems from smaller ones for i in range(3, n + 1): dp[i][1] = dp[i - 1][2] dp[i][2] = dp[i - 2][1] + dp[i - 2][2] @@ -662,43 +327,25 @@ In the end, returning $dp[n, 1] + dp[n, 2]$ will do, the sum of the two represen === "C++" ```cpp title="climbing_stairs_constraint_dp.cpp" - /* 带约束爬楼梯:动态规划 */ - int climbingStairsConstraintDP(int n) { - if (n == 1 || n == 2) { - return 1; - } - // 初始化 dp 表,用于存储子问题的解 - vector> dp(n + 1, vector(3, 0)); - // 初始状态:预设最小子问题的解 - dp[1][1] = 1; - dp[1][2] = 0; - dp[2][1] = 0; - dp[2][2] = 1; - // 状态转移:从较小子问题逐步求解较大子问题 - for (int i = 3; i <= n; i++) { - 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]; - } + [class]{}-[func]{climbingStairsConstraintDP} ``` === "Java" ```java title="climbing_stairs_constraint_dp.java" - /* 带约束爬楼梯:动态规划 */ + /* Constrained climbing stairs: Dynamic programming */ int climbingStairsConstraintDP(int n) { if (n == 1 || n == 2) { return 1; } - // 初始化 dp 表,用于存储子问题的解 + // Initialize dp table, used to store subproblem solutions int[][] dp = new int[n + 1][3]; - // 初始状态:预设最小子问题的解 + // Initial state: preset the smallest subproblem solution dp[1][1] = 1; dp[1][2] = 0; dp[2][1] = 0; dp[2][2] = 1; - // 状态转移:从较小子问题逐步求解较大子问题 + // State transition: gradually solve larger subproblems from smaller ones for (int i = 3; i <= n; i++) { dp[i][1] = dp[i - 1][2]; dp[i][2] = dp[i - 2][1] + dp[i - 2][2]; @@ -710,226 +357,55 @@ In the end, returning $dp[n, 1] + dp[n, 2]$ will do, the sum of the two represen === "C#" ```csharp title="climbing_stairs_constraint_dp.cs" - /* 带约束爬楼梯:动态规划 */ - int ClimbingStairsConstraintDP(int n) { - if (n == 1 || n == 2) { - return 1; - } - // 初始化 dp 表,用于存储子问题的解 - int[,] dp = new int[n + 1, 3]; - // 初始状态:预设最小子问题的解 - dp[1, 1] = 1; - dp[1, 2] = 0; - dp[2, 1] = 0; - dp[2, 2] = 1; - // 状态转移:从较小子问题逐步求解较大子问题 - for (int i = 3; i <= n; i++) { - 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]; - } + [class]{climbing_stairs_constraint_dp}-[func]{ClimbingStairsConstraintDP} ``` === "Go" ```go title="climbing_stairs_constraint_dp.go" - /* 带约束爬楼梯:动态规划 */ - func climbingStairsConstraintDP(n int) int { - if n == 1 || n == 2 { - return 1 - } - // 初始化 dp 表,用于存储子问题的解 - dp := make([][3]int, n+1) - // 初始状态:预设最小子问题的解 - dp[1][1] = 1 - dp[1][2] = 0 - dp[2][1] = 0 - dp[2][2] = 1 - // 状态转移:从较小子问题逐步求解较大子问题 - for i := 3; i <= n; i++ { - 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] - } + [class]{}-[func]{climbingStairsConstraintDP} ``` === "Swift" ```swift title="climbing_stairs_constraint_dp.swift" - /* 带约束爬楼梯:动态规划 */ - func climbingStairsConstraintDP(n: Int) -> Int { - if n == 1 || n == 2 { - return 1 - } - // 初始化 dp 表,用于存储子问题的解 - var dp = Array(repeating: Array(repeating: 0, count: 3), count: n + 1) - // 初始状态:预设最小子问题的解 - 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] - } + [class]{}-[func]{climbingStairsConstraintDP} ``` === "JS" ```javascript title="climbing_stairs_constraint_dp.js" - /* 带约束爬楼梯:动态规划 */ - function climbingStairsConstraintDP(n) { - if (n === 1 || n === 2) { - return 1; - } - // 初始化 dp 表,用于存储子问题的解 - const dp = Array.from(new Array(n + 1), () => new Array(3)); - // 初始状态:预设最小子问题的解 - dp[1][1] = 1; - dp[1][2] = 0; - dp[2][1] = 0; - dp[2][2] = 1; - // 状态转移:从较小子问题逐步求解较大子问题 - for (let i = 3; i <= n; i++) { - 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]; - } + [class]{}-[func]{climbingStairsConstraintDP} ``` === "TS" ```typescript title="climbing_stairs_constraint_dp.ts" - /* 带约束爬楼梯:动态规划 */ - function climbingStairsConstraintDP(n: number): number { - if (n === 1 || n === 2) { - return 1; - } - // 初始化 dp 表,用于存储子问题的解 - const dp = Array.from({ length: n + 1 }, () => new Array(3)); - // 初始状态:预设最小子问题的解 - dp[1][1] = 1; - dp[1][2] = 0; - dp[2][1] = 0; - dp[2][2] = 1; - // 状态转移:从较小子问题逐步求解较大子问题 - for (let i = 3; i <= n; i++) { - 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]; - } + [class]{}-[func]{climbingStairsConstraintDP} ``` === "Dart" ```dart title="climbing_stairs_constraint_dp.dart" - /* 带约束爬楼梯:动态规划 */ - int climbingStairsConstraintDP(int n) { - if (n == 1 || n == 2) { - return 1; - } - // 初始化 dp 表,用于存储子问题的解 - List> dp = List.generate(n + 1, (index) => List.filled(3, 0)); - // 初始状态:预设最小子问题的解 - dp[1][1] = 1; - dp[1][2] = 0; - dp[2][1] = 0; - dp[2][2] = 1; - // 状态转移:从较小子问题逐步求解较大子问题 - for (int i = 3; i <= n; i++) { - 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]; - } + [class]{}-[func]{climbingStairsConstraintDP} ``` === "Rust" ```rust title="climbing_stairs_constraint_dp.rs" - /* 带约束爬楼梯:动态规划 */ - fn climbing_stairs_constraint_dp(n: usize) -> i32 { - if n == 1 || n == 2 { - return 1; - }; - // 初始化 dp 表,用于存储子问题的解 - let mut dp = vec![vec![-1; 3]; n + 1]; - // 初始状态:预设最小子问题的解 - 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]; - } - dp[n][1] + dp[n][2] - } + [class]{}-[func]{climbing_stairs_constraint_dp} ``` === "C" ```c title="climbing_stairs_constraint_dp.c" - /* 带约束爬楼梯:动态规划 */ - int climbingStairsConstraintDP(int n) { - if (n == 1 || n == 2) { - return 1; - } - // 初始化 dp 表,用于存储子问题的解 - int **dp = malloc((n + 1) * sizeof(int *)); - for (int i = 0; i <= n; i++) { - dp[i] = calloc(3, sizeof(int)); - } - // 初始状态:预设最小子问题的解 - dp[1][1] = 1; - dp[1][2] = 0; - dp[2][1] = 0; - dp[2][2] = 1; - // 状态转移:从较小子问题逐步求解较大子问题 - for (int i = 3; i <= n; i++) { - dp[i][1] = dp[i - 1][2]; - dp[i][2] = dp[i - 2][1] + dp[i - 2][2]; - } - int res = dp[n][1] + dp[n][2]; - // 释放内存 - for (int i = 0; i <= n; i++) { - free(dp[i]); - } - free(dp); - return res; - } + [class]{}-[func]{climbingStairsConstraintDP} ``` === "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] - } + [class]{}-[func]{climbingStairsConstraintDP} ``` === "Ruby" @@ -941,32 +417,9 @@ In the end, returning $dp[n, 1] + dp[n, 2]$ will do, the sum of the two represen === "Zig" ```zig title="climbing_stairs_constraint_dp.zig" - // 带约束爬楼梯:动态规划 - fn climbingStairsConstraintDP(comptime n: usize) i32 { - if (n == 1 or n == 2) { - return 1; - } - // 初始化 dp 表,用于存储子问题的解 - var dp = [_][3]i32{ [_]i32{ -1, -1, -1 } } ** (n + 1); - // 初始状态:预设最小子问题的解 - dp[1][1] = 1; - dp[1][2] = 0; - dp[2][1] = 0; - dp[2][2] = 1; - // 状态转移:从较小子问题逐步求解较大子问题 - for (3..n + 1) |i| { - 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]; - } + [class]{}-[func]{climbingStairsConstraintDP} ``` -??? pythontutor "Code Visualization" - -
- - In the above cases, since we only need to consider the previous state, we can still meet the statelessness by expanding the state definition. However, some problems have very serious "state effects". !!! question "Stair climbing with obstacle generation" diff --git a/en/docs/chapter_dynamic_programming/dp_solution_pipeline.md b/en/docs/chapter_dynamic_programming/dp_solution_pipeline.md index 086cc0b12..7b46f8a2f 100644 --- a/en/docs/chapter_dynamic_programming/dp_solution_pipeline.md +++ b/en/docs/chapter_dynamic_programming/dp_solution_pipeline.md @@ -116,58 +116,43 @@ Implementation code as follows: ```python title="min_path_sum.py" def min_path_sum_dfs(grid: list[list[int]], i: int, j: int) -> int: - """最小路径和:暴力搜索""" - # 若为左上角单元格,则终止搜索 + """Minimum path sum: Brute force search""" + # If it's the top-left cell, terminate the search if i == 0 and j == 0: return grid[0][0] - # 若行列索引越界,则返回 +∞ 代价 + # If the row or column index is out of bounds, return a +∞ cost if i < 0 or j < 0: return inf - # 计算从左上角到 (i-1, j) 和 (i, j-1) 的最小路径代价 + # Calculate the minimum path cost from the top-left to (i-1, j) and (i, j-1) up = min_path_sum_dfs(grid, i - 1, j) left = min_path_sum_dfs(grid, i, j - 1) - # 返回从左上角到 (i, j) 的最小路径代价 + # Return the minimum path cost from the top-left to (i, j) return min(left, up) + grid[i][j] ``` === "C++" ```cpp title="min_path_sum.cpp" - /* 最小路径和:暴力搜索 */ - int minPathSumDFS(vector> &grid, int i, int j) { - // 若为左上角单元格,则终止搜索 - if (i == 0 && j == 0) { - return grid[0][0]; - } - // 若行列索引越界,则返回 +∞ 代价 - if (i < 0 || j < 0) { - return INT_MAX; - } - // 计算从左上角到 (i-1, j) 和 (i, j-1) 的最小路径代价 - int up = minPathSumDFS(grid, i - 1, j); - int left = minPathSumDFS(grid, i, j - 1); - // 返回从左上角到 (i, j) 的最小路径代价 - return min(left, up) != INT_MAX ? min(left, up) + grid[i][j] : INT_MAX; - } + [class]{}-[func]{minPathSumDFS} ``` === "Java" ```java title="min_path_sum.java" - /* 最小路径和:暴力搜索 */ + /* Minimum path sum: Brute force search */ int minPathSumDFS(int[][] grid, int i, int j) { - // 若为左上角单元格,则终止搜索 + // If it's the top-left cell, terminate the search if (i == 0 && j == 0) { return grid[0][0]; } - // 若行列索引越界,则返回 +∞ 代价 + // If the row or column index is out of bounds, return a +∞ cost if (i < 0 || j < 0) { return Integer.MAX_VALUE; } - // 计算从左上角到 (i-1, j) 和 (i, j-1) 的最小路径代价 + // Calculate the minimum path cost from the top-left to (i-1, j) and (i, j-1) int up = minPathSumDFS(grid, i - 1, j); int left = minPathSumDFS(grid, i, j - 1); - // 返回从左上角到 (i, j) 的最小路径代价 + // Return the minimum path cost from the top-left to (i, j) return Math.min(left, up) + grid[i][j]; } ``` @@ -175,195 +160,55 @@ Implementation code as follows: === "C#" ```csharp title="min_path_sum.cs" - /* 最小路径和:暴力搜索 */ - int MinPathSumDFS(int[][] grid, int i, int j) { - // 若为左上角单元格,则终止搜索 - if (i == 0 && j == 0) { - return grid[0][0]; - } - // 若行列索引越界,则返回 +∞ 代价 - if (i < 0 || j < 0) { - return int.MaxValue; - } - // 计算从左上角到 (i-1, j) 和 (i, j-1) 的最小路径代价 - int up = MinPathSumDFS(grid, i - 1, j); - int left = MinPathSumDFS(grid, i, j - 1); - // 返回从左上角到 (i, j) 的最小路径代价 - return Math.Min(left, up) + grid[i][j]; - } + [class]{min_path_sum}-[func]{MinPathSumDFS} ``` === "Go" ```go title="min_path_sum.go" - /* 最小路径和:暴力搜索 */ - func minPathSumDFS(grid [][]int, i, j int) int { - // 若为左上角单元格,则终止搜索 - if i == 0 && j == 0 { - return grid[0][0] - } - // 若行列索引越界,则返回 +∞ 代价 - if i < 0 || j < 0 { - return math.MaxInt - } - // 计算从左上角到 (i-1, j) 和 (i, j-1) 的最小路径代价 - up := minPathSumDFS(grid, i-1, j) - left := minPathSumDFS(grid, i, j-1) - // 返回从左上角到 (i, j) 的最小路径代价 - return int(math.Min(float64(left), float64(up))) + grid[i][j] - } + [class]{}-[func]{minPathSumDFS} ``` === "Swift" ```swift title="min_path_sum.swift" - /* 最小路径和:暴力搜索 */ - func minPathSumDFS(grid: [[Int]], i: Int, j: Int) -> Int { - // 若为左上角单元格,则终止搜索 - if i == 0, j == 0 { - return grid[0][0] - } - // 若行列索引越界,则返回 +∞ 代价 - if i < 0 || j < 0 { - return .max - } - // 计算从左上角到 (i-1, j) 和 (i, j-1) 的最小路径代价 - let up = minPathSumDFS(grid: grid, i: i - 1, j: j) - let left = minPathSumDFS(grid: grid, i: i, j: j - 1) - // 返回从左上角到 (i, j) 的最小路径代价 - return min(left, up) + grid[i][j] - } + [class]{}-[func]{minPathSumDFS} ``` === "JS" ```javascript title="min_path_sum.js" - /* 最小路径和:暴力搜索 */ - function minPathSumDFS(grid, i, j) { - // 若为左上角单元格,则终止搜索 - if (i === 0 && j === 0) { - return grid[0][0]; - } - // 若行列索引越界,则返回 +∞ 代价 - if (i < 0 || j < 0) { - return Infinity; - } - // 计算从左上角到 (i-1, j) 和 (i, j-1) 的最小路径代价 - const up = minPathSumDFS(grid, i - 1, j); - const left = minPathSumDFS(grid, i, j - 1); - // 返回从左上角到 (i, j) 的最小路径代价 - return Math.min(left, up) + grid[i][j]; - } + [class]{}-[func]{minPathSumDFS} ``` === "TS" ```typescript title="min_path_sum.ts" - /* 最小路径和:暴力搜索 */ - function minPathSumDFS( - grid: Array>, - i: number, - j: number - ): number { - // 若为左上角单元格,则终止搜索 - if (i === 0 && j == 0) { - return grid[0][0]; - } - // 若行列索引越界,则返回 +∞ 代价 - if (i < 0 || j < 0) { - return Infinity; - } - // 计算从左上角到 (i-1, j) 和 (i, j-1) 的最小路径代价 - const up = minPathSumDFS(grid, i - 1, j); - const left = minPathSumDFS(grid, i, j - 1); - // 返回从左上角到 (i, j) 的最小路径代价 - return Math.min(left, up) + grid[i][j]; - } + [class]{}-[func]{minPathSumDFS} ``` === "Dart" ```dart title="min_path_sum.dart" - /* 最小路径和:暴力搜索 */ - int minPathSumDFS(List> grid, int i, int j) { - // 若为左上角单元格,则终止搜索 - if (i == 0 && j == 0) { - return grid[0][0]; - } - // 若行列索引越界,则返回 +∞ 代价 - if (i < 0 || j < 0) { - // 在 Dart 中,int 类型是固定范围的整数,不存在表示“无穷大”的值 - return BigInt.from(2).pow(31).toInt(); - } - // 计算从左上角到 (i-1, j) 和 (i, j-1) 的最小路径代价 - int up = minPathSumDFS(grid, i - 1, j); - int left = minPathSumDFS(grid, i, j - 1); - // 返回从左上角到 (i, j) 的最小路径代价 - return min(left, up) + grid[i][j]; - } + [class]{}-[func]{minPathSumDFS} ``` === "Rust" ```rust title="min_path_sum.rs" - /* 最小路径和:暴力搜索 */ - fn min_path_sum_dfs(grid: &Vec>, i: i32, j: i32) -> i32 { - // 若为左上角单元格,则终止搜索 - if i == 0 && j == 0 { - return grid[0][0]; - } - // 若行列索引越界,则返回 +∞ 代价 - if i < 0 || j < 0 { - return i32::MAX; - } - // 计算从左上角到 (i-1, j) 和 (i, j-1) 的最小路径代价 - let up = min_path_sum_dfs(grid, i - 1, j); - let left = min_path_sum_dfs(grid, i, j - 1); - // 返回从左上角到 (i, j) 的最小路径代价 - std::cmp::min(left, up) + grid[i as usize][j as usize] - } + [class]{}-[func]{min_path_sum_dfs} ``` === "C" ```c title="min_path_sum.c" - /* 最小路径和:暴力搜索 */ - int minPathSumDFS(int grid[MAX_SIZE][MAX_SIZE], int i, int j) { - // 若为左上角单元格,则终止搜索 - if (i == 0 && j == 0) { - return grid[0][0]; - } - // 若行列索引越界,则返回 +∞ 代价 - if (i < 0 || j < 0) { - return INT_MAX; - } - // 计算从左上角到 (i-1, j) 和 (i, j-1) 的最小路径代价 - int up = minPathSumDFS(grid, i - 1, j); - int left = minPathSumDFS(grid, i, j - 1); - // 返回从左上角到 (i, j) 的最小路径代价 - return myMin(left, up) != INT_MAX ? myMin(left, up) + grid[i][j] : INT_MAX; - } + [class]{}-[func]{minPathSumDFS} ``` === "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, up) + grid[i][j] - } + [class]{}-[func]{minPathSumDFS} ``` === "Ruby" @@ -375,29 +220,9 @@ Implementation code as follows: === "Zig" ```zig title="min_path_sum.zig" - // 最小路径和:暴力搜索 - fn minPathSumDFS(grid: anytype, i: i32, j: i32) i32 { - // 若为左上角单元格,则终止搜索 - if (i == 0 and j == 0) { - return grid[0][0]; - } - // 若行列索引越界,则返回 +∞ 代价 - if (i < 0 or j < 0) { - return std.math.maxInt(i32); - } - // 计算从左上角到 (i-1, j) 和 (i, j-1) 的最小路径代价 - var up = minPathSumDFS(grid, i - 1, j); - var left = minPathSumDFS(grid, i, j - 1); - // 返回从左上角到 (i, j) 的最小路径代价 - return @min(left, up) + grid[@as(usize, @intCast(i))][@as(usize, @intCast(j))]; - } + [class]{}-[func]{minPathSumDFS} ``` -??? pythontutor "Code Visualization" - -
- - Figure 14-14 shows the recursive tree rooted at $dp[2, 1]$, which includes some overlapping subproblems, the number of which increases sharply as the size of the grid `grid` increases. Essentially, the reason for overlapping subproblems is: **there are multiple paths to reach a certain cell from the top-left corner**. @@ -418,20 +243,20 @@ We introduce a memo list `mem` of the same size as the grid `grid`, used to reco def min_path_sum_dfs_mem( grid: list[list[int]], mem: list[list[int]], i: int, j: int ) -> int: - """最小路径和:记忆化搜索""" - # 若为左上角单元格,则终止搜索 + """Minimum path sum: Memoized search""" + # If it's the top-left cell, terminate the search if i == 0 and j == 0: return grid[0][0] - # 若行列索引越界,则返回 +∞ 代价 + # If the row or column index is out of bounds, return a +∞ cost if i < 0 or j < 0: return inf - # 若已有记录,则直接返回 + # If there is a record, return it if mem[i][j] != -1: return mem[i][j] - # 左边和上边单元格的最小路径代价 + # The minimum path cost from the left and top cells up = min_path_sum_dfs_mem(grid, mem, i - 1, j) left = min_path_sum_dfs_mem(grid, mem, i, j - 1) - # 记录并返回左上角到 (i, j) 的最小路径代价 + # Record and return the minimum path cost from the top-left to (i, j) mem[i][j] = min(left, up) + grid[i][j] return mem[i][j] ``` @@ -439,50 +264,30 @@ We introduce a memo list `mem` of the same size as the grid `grid`, used to reco === "C++" ```cpp title="min_path_sum.cpp" - /* 最小路径和:记忆化搜索 */ - int minPathSumDFSMem(vector> &grid, vector> &mem, int i, int j) { - // 若为左上角单元格,则终止搜索 - if (i == 0 && j == 0) { - return grid[0][0]; - } - // 若行列索引越界,则返回 +∞ 代价 - if (i < 0 || j < 0) { - return INT_MAX; - } - // 若已有记录,则直接返回 - if (mem[i][j] != -1) { - return mem[i][j]; - } - // 左边和上边单元格的最小路径代价 - int up = minPathSumDFSMem(grid, mem, i - 1, j); - int left = minPathSumDFSMem(grid, mem, i, j - 1); - // 记录并返回左上角到 (i, j) 的最小路径代价 - mem[i][j] = min(left, up) != INT_MAX ? min(left, up) + grid[i][j] : INT_MAX; - return mem[i][j]; - } + [class]{}-[func]{minPathSumDFSMem} ``` === "Java" ```java title="min_path_sum.java" - /* 最小路径和:记忆化搜索 */ + /* Minimum path sum: Memoized search */ int minPathSumDFSMem(int[][] grid, int[][] mem, int i, int j) { - // 若为左上角单元格,则终止搜索 + // If it's the top-left cell, terminate the search if (i == 0 && j == 0) { return grid[0][0]; } - // 若行列索引越界,则返回 +∞ 代价 + // If the row or column index is out of bounds, return a +∞ cost if (i < 0 || j < 0) { return Integer.MAX_VALUE; } - // 若已有记录,则直接返回 + // If there is a record, return it if (mem[i][j] != -1) { return mem[i][j]; } - // 左边和上边单元格的最小路径代价 + // The minimum path cost from the left and top cells int up = minPathSumDFSMem(grid, mem, i - 1, j); int left = minPathSumDFSMem(grid, mem, i, j - 1); - // 记录并返回左上角到 (i, j) 的最小路径代价 + // Record and return the minimum path cost from the top-left to (i, j) mem[i][j] = Math.min(left, up) + grid[i][j]; return mem[i][j]; } @@ -491,246 +296,55 @@ We introduce a memo list `mem` of the same size as the grid `grid`, used to reco === "C#" ```csharp title="min_path_sum.cs" - /* 最小路径和:记忆化搜索 */ - int MinPathSumDFSMem(int[][] grid, int[][] mem, int i, int j) { - // 若为左上角单元格,则终止搜索 - if (i == 0 && j == 0) { - return grid[0][0]; - } - // 若行列索引越界,则返回 +∞ 代价 - if (i < 0 || j < 0) { - return int.MaxValue; - } - // 若已有记录,则直接返回 - if (mem[i][j] != -1) { - return mem[i][j]; - } - // 左边和上边单元格的最小路径代价 - int up = MinPathSumDFSMem(grid, mem, i - 1, j); - int left = MinPathSumDFSMem(grid, mem, i, j - 1); - // 记录并返回左上角到 (i, j) 的最小路径代价 - mem[i][j] = Math.Min(left, up) + grid[i][j]; - return mem[i][j]; - } + [class]{min_path_sum}-[func]{MinPathSumDFSMem} ``` === "Go" ```go title="min_path_sum.go" - /* 最小路径和:记忆化搜索 */ - func minPathSumDFSMem(grid, mem [][]int, i, j int) int { - // 若为左上角单元格,则终止搜索 - if i == 0 && j == 0 { - return grid[0][0] - } - // 若行列索引越界,则返回 +∞ 代价 - if i < 0 || j < 0 { - return math.MaxInt - } - // 若已有记录,则直接返回 - if mem[i][j] != -1 { - return mem[i][j] - } - // 左边和上边单元格的最小路径代价 - up := minPathSumDFSMem(grid, mem, i-1, j) - left := minPathSumDFSMem(grid, mem, i, j-1) - // 记录并返回左上角到 (i, j) 的最小路径代价 - mem[i][j] = int(math.Min(float64(left), float64(up))) + grid[i][j] - return mem[i][j] - } + [class]{}-[func]{minPathSumDFSMem} ``` === "Swift" ```swift title="min_path_sum.swift" - /* 最小路径和:记忆化搜索 */ - func minPathSumDFSMem(grid: [[Int]], mem: inout [[Int]], i: Int, j: Int) -> Int { - // 若为左上角单元格,则终止搜索 - if i == 0, j == 0 { - return grid[0][0] - } - // 若行列索引越界,则返回 +∞ 代价 - if i < 0 || j < 0 { - return .max - } - // 若已有记录,则直接返回 - if mem[i][j] != -1 { - return mem[i][j] - } - // 左边和上边单元格的最小路径代价 - let up = minPathSumDFSMem(grid: grid, mem: &mem, i: i - 1, j: j) - let left = minPathSumDFSMem(grid: grid, mem: &mem, i: i, j: j - 1) - // 记录并返回左上角到 (i, j) 的最小路径代价 - mem[i][j] = min(left, up) + grid[i][j] - return mem[i][j] - } + [class]{}-[func]{minPathSumDFSMem} ``` === "JS" ```javascript title="min_path_sum.js" - /* 最小路径和:记忆化搜索 */ - function minPathSumDFSMem(grid, mem, i, j) { - // 若为左上角单元格,则终止搜索 - if (i === 0 && j === 0) { - return grid[0][0]; - } - // 若行列索引越界,则返回 +∞ 代价 - if (i < 0 || j < 0) { - return Infinity; - } - // 若已有记录,则直接返回 - if (mem[i][j] !== -1) { - return mem[i][j]; - } - // 左边和上边单元格的最小路径代价 - const up = minPathSumDFSMem(grid, mem, i - 1, j); - const left = minPathSumDFSMem(grid, mem, i, j - 1); - // 记录并返回左上角到 (i, j) 的最小路径代价 - mem[i][j] = Math.min(left, up) + grid[i][j]; - return mem[i][j]; - } + [class]{}-[func]{minPathSumDFSMem} ``` === "TS" ```typescript title="min_path_sum.ts" - /* 最小路径和:记忆化搜索 */ - function minPathSumDFSMem( - grid: Array>, - mem: Array>, - i: number, - j: number - ): number { - // 若为左上角单元格,则终止搜索 - if (i === 0 && j === 0) { - return grid[0][0]; - } - // 若行列索引越界,则返回 +∞ 代价 - if (i < 0 || j < 0) { - return Infinity; - } - // 若已有记录,则直接返回 - if (mem[i][j] != -1) { - return mem[i][j]; - } - // 左边和上边单元格的最小路径代价 - const up = minPathSumDFSMem(grid, mem, i - 1, j); - const left = minPathSumDFSMem(grid, mem, i, j - 1); - // 记录并返回左上角到 (i, j) 的最小路径代价 - mem[i][j] = Math.min(left, up) + grid[i][j]; - return mem[i][j]; - } + [class]{}-[func]{minPathSumDFSMem} ``` === "Dart" ```dart title="min_path_sum.dart" - /* 最小路径和:记忆化搜索 */ - int minPathSumDFSMem(List> grid, List> mem, int i, int j) { - // 若为左上角单元格,则终止搜索 - if (i == 0 && j == 0) { - return grid[0][0]; - } - // 若行列索引越界,则返回 +∞ 代价 - if (i < 0 || j < 0) { - // 在 Dart 中,int 类型是固定范围的整数,不存在表示“无穷大”的值 - return BigInt.from(2).pow(31).toInt(); - } - // 若已有记录,则直接返回 - if (mem[i][j] != -1) { - return mem[i][j]; - } - // 左边和上边单元格的最小路径代价 - int up = minPathSumDFSMem(grid, mem, i - 1, j); - int left = minPathSumDFSMem(grid, mem, i, j - 1); - // 记录并返回左上角到 (i, j) 的最小路径代价 - mem[i][j] = min(left, up) + grid[i][j]; - return mem[i][j]; - } + [class]{}-[func]{minPathSumDFSMem} ``` === "Rust" ```rust title="min_path_sum.rs" - /* 最小路径和:记忆化搜索 */ - fn min_path_sum_dfs_mem(grid: &Vec>, mem: &mut Vec>, i: i32, j: i32) -> i32 { - // 若为左上角单元格,则终止搜索 - if i == 0 && j == 0 { - return grid[0][0]; - } - // 若行列索引越界,则返回 +∞ 代价 - if i < 0 || j < 0 { - return i32::MAX; - } - // 若已有记录,则直接返回 - if mem[i as usize][j as usize] != -1 { - return mem[i as usize][j as usize]; - } - // 左边和上边单元格的最小路径代价 - let up = min_path_sum_dfs_mem(grid, mem, i - 1, j); - let left = min_path_sum_dfs_mem(grid, mem, i, j - 1); - // 记录并返回左上角到 (i, j) 的最小路径代价 - mem[i as usize][j as usize] = std::cmp::min(left, up) + grid[i as usize][j as usize]; - mem[i as usize][j as usize] - } + [class]{}-[func]{min_path_sum_dfs_mem} ``` === "C" ```c title="min_path_sum.c" - /* 最小路径和:记忆化搜索 */ - int minPathSumDFSMem(int grid[MAX_SIZE][MAX_SIZE], int mem[MAX_SIZE][MAX_SIZE], int i, int j) { - // 若为左上角单元格,则终止搜索 - if (i == 0 && j == 0) { - return grid[0][0]; - } - // 若行列索引越界,则返回 +∞ 代价 - if (i < 0 || j < 0) { - return INT_MAX; - } - // 若已有记录,则直接返回 - if (mem[i][j] != -1) { - return mem[i][j]; - } - // 左边和上边单元格的最小路径代价 - int up = minPathSumDFSMem(grid, mem, i - 1, j); - int left = minPathSumDFSMem(grid, mem, i, j - 1); - // 记录并返回左上角到 (i, j) 的最小路径代价 - mem[i][j] = myMin(left, up) != INT_MAX ? myMin(left, up) + grid[i][j] : INT_MAX; - return mem[i][j]; - } + [class]{}-[func]{minPathSumDFSMem} ``` === "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, up) + grid[i][j] - return mem[i][j] - } + [class]{}-[func]{minPathSumDFSMem} ``` === "Ruby" @@ -742,35 +356,9 @@ We introduce a memo list `mem` of the same size as the grid `grid`, used to reco === "Zig" ```zig title="min_path_sum.zig" - // 最小路径和:记忆化搜索 - fn minPathSumDFSMem(grid: anytype, mem: anytype, i: i32, j: i32) i32 { - // 若为左上角单元格,则终止搜索 - if (i == 0 and j == 0) { - return grid[0][0]; - } - // 若行列索引越界,则返回 +∞ 代价 - if (i < 0 or j < 0) { - return std.math.maxInt(i32); - } - // 若已有记录,则直接返回 - if (mem[@as(usize, @intCast(i))][@as(usize, @intCast(j))] != -1) { - return mem[@as(usize, @intCast(i))][@as(usize, @intCast(j))]; - } - // 计算从左上角到 (i-1, j) 和 (i, j-1) 的最小路径代价 - var up = minPathSumDFSMem(grid, mem, i - 1, j); - var left = minPathSumDFSMem(grid, mem, i, j - 1); - // 返回从左上角到 (i, j) 的最小路径代价 - // 记录并返回左上角到 (i, j) 的最小路径代价 - mem[@as(usize, @intCast(i))][@as(usize, @intCast(j))] = @min(left, up) + grid[@as(usize, @intCast(i))][@as(usize, @intCast(j))]; - return mem[@as(usize, @intCast(i))][@as(usize, @intCast(j))]; - } + [class]{}-[func]{minPathSumDFSMem} ``` -??? pythontutor "Code Visualization" - -
- - As shown in Figure 14-15, after introducing memoization, all subproblem solutions only need to be calculated once, so the time complexity depends on the total number of states, i.e., the grid size $O(nm)$. ![Memoized search recursive tree](dp_solution_pipeline.assets/min_path_sum_dfs_mem.png){ class="animation-figure" } @@ -785,18 +373,18 @@ Implement the dynamic programming solution iteratively, code as shown below: ```python title="min_path_sum.py" def min_path_sum_dp(grid: list[list[int]]) -> int: - """最小路径和:动态规划""" + """Minimum path sum: Dynamic programming""" n, m = len(grid), len(grid[0]) - # 初始化 dp 表 + # Initialize dp table dp = [[0] * m for _ in range(n)] dp[0][0] = grid[0][0] - # 状态转移:首行 + # State transition: first row for j in range(1, m): dp[0][j] = dp[0][j - 1] + grid[0][j] - # 状态转移:首列 + # State transition: first column for i in range(1, n): dp[i][0] = dp[i - 1][0] + grid[i][0] - # 状态转移:其余行和列 + # State transition: the rest of the rows and columns for i in range(1, n): for j in range(1, m): dp[i][j] = min(dp[i][j - 1], dp[i - 1][j]) + grid[i][j] @@ -806,48 +394,27 @@ Implement the dynamic programming solution iteratively, code as shown below: === "C++" ```cpp title="min_path_sum.cpp" - /* 最小路径和:动态规划 */ - int minPathSumDP(vector> &grid) { - int n = grid.size(), m = grid[0].size(); - // 初始化 dp 表 - vector> dp(n, vector(m)); - dp[0][0] = grid[0][0]; - // 状态转移:首行 - for (int j = 1; j < m; j++) { - dp[0][j] = dp[0][j - 1] + grid[0][j]; - } - // 状态转移:首列 - for (int i = 1; i < n; i++) { - dp[i][0] = dp[i - 1][0] + grid[i][0]; - } - // 状态转移:其余行和列 - for (int i = 1; i < n; i++) { - for (int j = 1; j < m; j++) { - dp[i][j] = min(dp[i][j - 1], dp[i - 1][j]) + grid[i][j]; - } - } - return dp[n - 1][m - 1]; - } + [class]{}-[func]{minPathSumDP} ``` === "Java" ```java title="min_path_sum.java" - /* 最小路径和:动态规划 */ + /* Minimum path sum: Dynamic programming */ int minPathSumDP(int[][] grid) { int n = grid.length, m = grid[0].length; - // 初始化 dp 表 + // Initialize dp table int[][] dp = new int[n][m]; dp[0][0] = grid[0][0]; - // 状态转移:首行 + // State transition: first row for (int j = 1; j < m; j++) { dp[0][j] = dp[0][j - 1] + grid[0][j]; } - // 状态转移:首列 + // State transition: first column for (int i = 1; i < n; i++) { dp[i][0] = dp[i - 1][0] + grid[i][0]; } - // 状态转移:其余行和列 + // State transition: the rest of the rows and columns for (int i = 1; i < n; i++) { for (int j = 1; j < m; j++) { dp[i][j] = Math.min(dp[i][j - 1], dp[i - 1][j]) + grid[i][j]; @@ -860,262 +427,55 @@ Implement the dynamic programming solution iteratively, code as shown below: === "C#" ```csharp title="min_path_sum.cs" - /* 最小路径和:动态规划 */ - int MinPathSumDP(int[][] grid) { - int n = grid.Length, m = grid[0].Length; - // 初始化 dp 表 - int[,] dp = new int[n, m]; - dp[0, 0] = grid[0][0]; - // 状态转移:首行 - for (int j = 1; j < m; j++) { - dp[0, j] = dp[0, j - 1] + grid[0][j]; - } - // 状态转移:首列 - for (int i = 1; i < n; i++) { - dp[i, 0] = dp[i - 1, 0] + grid[i][0]; - } - // 状态转移:其余行和列 - for (int i = 1; i < n; i++) { - for (int j = 1; j < m; j++) { - dp[i, j] = Math.Min(dp[i, j - 1], dp[i - 1, j]) + grid[i][j]; - } - } - return dp[n - 1, m - 1]; - } + [class]{min_path_sum}-[func]{MinPathSumDP} ``` === "Go" ```go title="min_path_sum.go" - /* 最小路径和:动态规划 */ - func minPathSumDP(grid [][]int) int { - n, m := len(grid), len(grid[0]) - // 初始化 dp 表 - dp := make([][]int, n) - for i := 0; i < n; i++ { - dp[i] = make([]int, m) - } - dp[0][0] = grid[0][0] - // 状态转移:首行 - for j := 1; j < m; j++ { - dp[0][j] = dp[0][j-1] + grid[0][j] - } - // 状态转移:首列 - for i := 1; i < n; i++ { - dp[i][0] = dp[i-1][0] + grid[i][0] - } - // 状态转移:其余行和列 - for i := 1; i < n; i++ { - for j := 1; j < m; j++ { - dp[i][j] = int(math.Min(float64(dp[i][j-1]), float64(dp[i-1][j]))) + grid[i][j] - } - } - return dp[n-1][m-1] - } + [class]{}-[func]{minPathSumDP} ``` === "Swift" ```swift title="min_path_sum.swift" - /* 最小路径和:动态规划 */ - func minPathSumDP(grid: [[Int]]) -> Int { - let n = grid.count - let m = grid[0].count - // 初始化 dp 表 - var dp = Array(repeating: Array(repeating: 0, count: m), count: n) - dp[0][0] = grid[0][0] - // 状态转移:首行 - for j in 1 ..< m { - dp[0][j] = dp[0][j - 1] + grid[0][j] - } - // 状态转移:首列 - for i in 1 ..< n { - dp[i][0] = dp[i - 1][0] + grid[i][0] - } - // 状态转移:其余行和列 - for i in 1 ..< n { - for j in 1 ..< m { - dp[i][j] = min(dp[i][j - 1], dp[i - 1][j]) + grid[i][j] - } - } - return dp[n - 1][m - 1] - } + [class]{}-[func]{minPathSumDP} ``` === "JS" ```javascript title="min_path_sum.js" - /* 最小路径和:动态规划 */ - function minPathSumDP(grid) { - const n = grid.length, - m = grid[0].length; - // 初始化 dp 表 - const dp = Array.from({ length: n }, () => - Array.from({ length: m }, () => 0) - ); - dp[0][0] = grid[0][0]; - // 状态转移:首行 - for (let j = 1; j < m; j++) { - dp[0][j] = dp[0][j - 1] + grid[0][j]; - } - // 状态转移:首列 - for (let i = 1; i < n; i++) { - dp[i][0] = dp[i - 1][0] + grid[i][0]; - } - // 状态转移:其余行和列 - for (let i = 1; i < n; i++) { - for (let j = 1; j < m; j++) { - dp[i][j] = Math.min(dp[i][j - 1], dp[i - 1][j]) + grid[i][j]; - } - } - return dp[n - 1][m - 1]; - } + [class]{}-[func]{minPathSumDP} ``` === "TS" ```typescript title="min_path_sum.ts" - /* 最小路径和:动态规划 */ - function minPathSumDP(grid: Array>): number { - const n = grid.length, - m = grid[0].length; - // 初始化 dp 表 - const dp = Array.from({ length: n }, () => - Array.from({ length: m }, () => 0) - ); - dp[0][0] = grid[0][0]; - // 状态转移:首行 - for (let j = 1; j < m; j++) { - dp[0][j] = dp[0][j - 1] + grid[0][j]; - } - // 状态转移:首列 - for (let i = 1; i < n; i++) { - dp[i][0] = dp[i - 1][0] + grid[i][0]; - } - // 状态转移:其余行和列 - for (let i = 1; i < n; i++) { - for (let j: number = 1; j < m; j++) { - dp[i][j] = Math.min(dp[i][j - 1], dp[i - 1][j]) + grid[i][j]; - } - } - return dp[n - 1][m - 1]; - } + [class]{}-[func]{minPathSumDP} ``` === "Dart" ```dart title="min_path_sum.dart" - /* 最小路径和:动态规划 */ - int minPathSumDP(List> grid) { - int n = grid.length, m = grid[0].length; - // 初始化 dp 表 - List> dp = List.generate(n, (i) => List.filled(m, 0)); - dp[0][0] = grid[0][0]; - // 状态转移:首行 - for (int j = 1; j < m; j++) { - dp[0][j] = dp[0][j - 1] + grid[0][j]; - } - // 状态转移:首列 - for (int i = 1; i < n; i++) { - dp[i][0] = dp[i - 1][0] + grid[i][0]; - } - // 状态转移:其余行和列 - for (int i = 1; i < n; i++) { - for (int j = 1; j < m; j++) { - dp[i][j] = min(dp[i][j - 1], dp[i - 1][j]) + grid[i][j]; - } - } - return dp[n - 1][m - 1]; - } + [class]{}-[func]{minPathSumDP} ``` === "Rust" ```rust title="min_path_sum.rs" - /* 最小路径和:动态规划 */ - fn min_path_sum_dp(grid: &Vec>) -> i32 { - let (n, m) = (grid.len(), grid[0].len()); - // 初始化 dp 表 - let mut dp = vec![vec![0; m]; n]; - dp[0][0] = grid[0][0]; - // 状态转移:首行 - for j in 1..m { - dp[0][j] = dp[0][j - 1] + grid[0][j]; - } - // 状态转移:首列 - for i in 1..n { - dp[i][0] = dp[i - 1][0] + grid[i][0]; - } - // 状态转移:其余行和列 - for i in 1..n { - for j in 1..m { - dp[i][j] = std::cmp::min(dp[i][j - 1], dp[i - 1][j]) + grid[i][j]; - } - } - dp[n - 1][m - 1] - } + [class]{}-[func]{min_path_sum_dp} ``` === "C" ```c title="min_path_sum.c" - /* 最小路径和:动态规划 */ - int minPathSumDP(int grid[MAX_SIZE][MAX_SIZE], int n, int m) { - // 初始化 dp 表 - int **dp = malloc(n * sizeof(int *)); - for (int i = 0; i < n; i++) { - dp[i] = calloc(m, sizeof(int)); - } - dp[0][0] = grid[0][0]; - // 状态转移:首行 - for (int j = 1; j < m; j++) { - dp[0][j] = dp[0][j - 1] + grid[0][j]; - } - // 状态转移:首列 - for (int i = 1; i < n; i++) { - dp[i][0] = dp[i - 1][0] + grid[i][0]; - } - // 状态转移:其余行和列 - for (int i = 1; i < n; i++) { - for (int j = 1; j < m; j++) { - dp[i][j] = myMin(dp[i][j - 1], dp[i - 1][j]) + grid[i][j]; - } - } - int res = dp[n - 1][m - 1]; - // 释放内存 - for (int i = 0; i < n; i++) { - free(dp[i]); - } - return res; - } + [class]{}-[func]{minPathSumDP} ``` === "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.. - - Figure 14-16 show the state transition process of the minimum path sum, traversing the entire grid, **thus the time complexity is $O(nm)$**. The array `dp` is of size $n \times m$, **therefore the space complexity is $O(nm)$**. @@ -1209,19 +542,19 @@ Please note, since the array `dp` can only represent the state of one row, we ca ```python title="min_path_sum.py" def min_path_sum_dp_comp(grid: list[list[int]]) -> int: - """最小路径和:空间优化后的动态规划""" + """Minimum path sum: Space-optimized dynamic programming""" n, m = len(grid), len(grid[0]) - # 初始化 dp 表 + # Initialize dp table dp = [0] * m - # 状态转移:首行 + # State transition: first row dp[0] = grid[0][0] for j in range(1, m): dp[j] = dp[j - 1] + grid[0][j] - # 状态转移:其余行 + # State transition: the rest of the rows for i in range(1, n): - # 状态转移:首列 + # State transition: first column dp[0] = dp[0] + grid[i][0] - # 状态转移:其余列 + # State transition: the rest of the columns for j in range(1, m): dp[j] = min(dp[j - 1], dp[j]) + grid[i][j] return dp[m - 1] @@ -1230,47 +563,27 @@ Please note, since the array `dp` can only represent the state of one row, we ca === "C++" ```cpp title="min_path_sum.cpp" - /* 最小路径和:空间优化后的动态规划 */ - int minPathSumDPComp(vector> &grid) { - int n = grid.size(), m = grid[0].size(); - // 初始化 dp 表 - vector dp(m); - // 状态转移:首行 - dp[0] = grid[0][0]; - for (int j = 1; j < m; j++) { - dp[j] = dp[j - 1] + grid[0][j]; - } - // 状态转移:其余行 - for (int i = 1; i < n; i++) { - // 状态转移:首列 - dp[0] = dp[0] + grid[i][0]; - // 状态转移:其余列 - for (int j = 1; j < m; j++) { - dp[j] = min(dp[j - 1], dp[j]) + grid[i][j]; - } - } - return dp[m - 1]; - } + [class]{}-[func]{minPathSumDPComp} ``` === "Java" ```java title="min_path_sum.java" - /* 最小路径和:空间优化后的动态规划 */ + /* Minimum path sum: Space-optimized dynamic programming */ int minPathSumDPComp(int[][] grid) { int n = grid.length, m = grid[0].length; - // 初始化 dp 表 + // Initialize dp table int[] dp = new int[m]; - // 状态转移:首行 + // State transition: first row dp[0] = grid[0][0]; for (int j = 1; j < m; j++) { dp[j] = dp[j - 1] + grid[0][j]; } - // 状态转移:其余行 + // State transition: the rest of the rows for (int i = 1; i < n; i++) { - // 状态转移:首列 + // State transition: first column dp[0] = dp[0] + grid[i][0]; - // 状态转移:其余列 + // State transition: the rest of the columns for (int j = 1; j < m; j++) { dp[j] = Math.min(dp[j - 1], dp[j]) + grid[i][j]; } @@ -1282,240 +595,55 @@ Please note, since the array `dp` can only represent the state of one row, we ca === "C#" ```csharp title="min_path_sum.cs" - /* 最小路径和:空间优化后的动态规划 */ - int MinPathSumDPComp(int[][] grid) { - int n = grid.Length, m = grid[0].Length; - // 初始化 dp 表 - int[] dp = new int[m]; - dp[0] = grid[0][0]; - // 状态转移:首行 - for (int j = 1; j < m; j++) { - dp[j] = dp[j - 1] + grid[0][j]; - } - // 状态转移:其余行 - for (int i = 1; i < n; i++) { - // 状态转移:首列 - dp[0] = dp[0] + grid[i][0]; - // 状态转移:其余列 - for (int j = 1; j < m; j++) { - dp[j] = Math.Min(dp[j - 1], dp[j]) + grid[i][j]; - } - } - return dp[m - 1]; - } + [class]{min_path_sum}-[func]{MinPathSumDPComp} ``` === "Go" ```go title="min_path_sum.go" - /* 最小路径和:空间优化后的动态规划 */ - func minPathSumDPComp(grid [][]int) int { - n, m := len(grid), len(grid[0]) - // 初始化 dp 表 - dp := make([]int, m) - // 状态转移:首行 - dp[0] = grid[0][0] - for j := 1; j < m; j++ { - dp[j] = dp[j-1] + grid[0][j] - } - // 状态转移:其余行和列 - for i := 1; i < n; i++ { - // 状态转移:首列 - dp[0] = dp[0] + grid[i][0] - // 状态转移:其余列 - for j := 1; j < m; j++ { - dp[j] = int(math.Min(float64(dp[j-1]), float64(dp[j]))) + grid[i][j] - } - } - return dp[m-1] - } + [class]{}-[func]{minPathSumDPComp} ``` === "Swift" ```swift title="min_path_sum.swift" - /* 最小路径和:空间优化后的动态规划 */ - func minPathSumDPComp(grid: [[Int]]) -> Int { - let n = grid.count - let m = grid[0].count - // 初始化 dp 表 - var dp = Array(repeating: 0, count: m) - // 状态转移:首行 - dp[0] = grid[0][0] - for j in 1 ..< m { - dp[j] = dp[j - 1] + grid[0][j] - } - // 状态转移:其余行 - for i in 1 ..< n { - // 状态转移:首列 - dp[0] = dp[0] + grid[i][0] - // 状态转移:其余列 - for j in 1 ..< m { - dp[j] = min(dp[j - 1], dp[j]) + grid[i][j] - } - } - return dp[m - 1] - } + [class]{}-[func]{minPathSumDPComp} ``` === "JS" ```javascript title="min_path_sum.js" - /* 最小路径和:状态压缩后的动态规划 */ - function minPathSumDPComp(grid) { - const n = grid.length, - m = grid[0].length; - // 初始化 dp 表 - const dp = new Array(m); - // 状态转移:首行 - dp[0] = grid[0][0]; - for (let j = 1; j < m; j++) { - dp[j] = dp[j - 1] + grid[0][j]; - } - // 状态转移:其余行 - for (let i = 1; i < n; i++) { - // 状态转移:首列 - dp[0] = dp[0] + grid[i][0]; - // 状态转移:其余列 - for (let j = 1; j < m; j++) { - dp[j] = Math.min(dp[j - 1], dp[j]) + grid[i][j]; - } - } - return dp[m - 1]; - } + [class]{}-[func]{minPathSumDPComp} ``` === "TS" ```typescript title="min_path_sum.ts" - /* 最小路径和:状态压缩后的动态规划 */ - function minPathSumDPComp(grid: Array>): number { - const n = grid.length, - m = grid[0].length; - // 初始化 dp 表 - const dp = new Array(m); - // 状态转移:首行 - dp[0] = grid[0][0]; - for (let j = 1; j < m; j++) { - dp[j] = dp[j - 1] + grid[0][j]; - } - // 状态转移:其余行 - for (let i = 1; i < n; i++) { - // 状态转移:首列 - dp[0] = dp[0] + grid[i][0]; - // 状态转移:其余列 - for (let j = 1; j < m; j++) { - dp[j] = Math.min(dp[j - 1], dp[j]) + grid[i][j]; - } - } - return dp[m - 1]; - } + [class]{}-[func]{minPathSumDPComp} ``` === "Dart" ```dart title="min_path_sum.dart" - /* 最小路径和:空间优化后的动态规划 */ - int minPathSumDPComp(List> grid) { - int n = grid.length, m = grid[0].length; - // 初始化 dp 表 - List dp = List.filled(m, 0); - dp[0] = grid[0][0]; - for (int j = 1; j < m; j++) { - dp[j] = dp[j - 1] + grid[0][j]; - } - // 状态转移:其余行 - for (int i = 1; i < n; i++) { - // 状态转移:首列 - dp[0] = dp[0] + grid[i][0]; - // 状态转移:其余列 - for (int j = 1; j < m; j++) { - dp[j] = min(dp[j - 1], dp[j]) + grid[i][j]; - } - } - return dp[m - 1]; - } + [class]{}-[func]{minPathSumDPComp} ``` === "Rust" ```rust title="min_path_sum.rs" - /* 最小路径和:空间优化后的动态规划 */ - fn min_path_sum_dp_comp(grid: &Vec>) -> i32 { - let (n, m) = (grid.len(), grid[0].len()); - // 初始化 dp 表 - let mut dp = vec![0; m]; - // 状态转移:首行 - dp[0] = grid[0][0]; - for j in 1..m { - dp[j] = dp[j - 1] + grid[0][j]; - } - // 状态转移:其余行 - for i in 1..n { - // 状态转移:首列 - dp[0] = dp[0] + grid[i][0]; - // 状态转移:其余列 - for j in 1..m { - dp[j] = std::cmp::min(dp[j - 1], dp[j]) + grid[i][j]; - } - } - dp[m - 1] - } + [class]{}-[func]{min_path_sum_dp_comp} ``` === "C" ```c title="min_path_sum.c" - /* 最小路径和:空间优化后的动态规划 */ - int minPathSumDPComp(int grid[MAX_SIZE][MAX_SIZE], int n, int m) { - // 初始化 dp 表 - int *dp = calloc(m, sizeof(int)); - // 状态转移:首行 - dp[0] = grid[0][0]; - for (int j = 1; j < m; j++) { - dp[j] = dp[j - 1] + grid[0][j]; - } - // 状态转移:其余行 - for (int i = 1; i < n; i++) { - // 状态转移:首列 - dp[0] = dp[0] + grid[i][0]; - // 状态转移:其余列 - for (int j = 1; j < m; j++) { - dp[j] = myMin(dp[j - 1], dp[j]) + grid[i][j]; - } - } - int res = dp[m - 1]; - // 释放内存 - free(dp); - return res; - } + [class]{}-[func]{minPathSumDPComp} ``` === "Kotlin" ```kotlin title="min_path_sum.kt" - /* 最小路径和:空间优化后的动态规划 */ - fun minPathSumDPComp(grid: Array): Int { - val n = grid.size - val m = grid[0].size - // 初始化 dp 表 - val dp = IntArray(m) - // 状态转移:首行 - dp[0] = grid[0][0] - for (j in 1.. - diff --git a/en/docs/chapter_dynamic_programming/edit_distance_problem.md b/en/docs/chapter_dynamic_programming/edit_distance_problem.md index 44a8f9f8b..c9efb6536 100644 --- a/en/docs/chapter_dynamic_programming/edit_distance_problem.md +++ b/en/docs/chapter_dynamic_programming/edit_distance_problem.md @@ -81,22 +81,22 @@ Observing the state transition equation, solving $dp[i, j]$ depends on the solut ```python title="edit_distance.py" def edit_distance_dp(s: str, t: str) -> int: - """编辑距离:动态规划""" + """Edit distance: Dynamic programming""" n, m = len(s), len(t) dp = [[0] * (m + 1) for _ in range(n + 1)] - # 状态转移:首行首列 + # State transition: first row and first column for i in range(1, n + 1): dp[i][0] = i for j in range(1, m + 1): dp[0][j] = j - # 状态转移:其余行和列 + # State transition: the rest of the rows and columns for i in range(1, n + 1): for j in range(1, m + 1): if s[i - 1] == t[j - 1]: - # 若两字符相等,则直接跳过此两字符 + # If the two characters are equal, skip these two characters dp[i][j] = dp[i - 1][j - 1] else: - # 最少编辑步数 = 插入、删除、替换这三种操作的最少编辑步数 + 1 + # The minimum number of edits = the minimum number of edits from three operations (insert, remove, replace) + 1 dp[i][j] = min(dp[i][j - 1], dp[i - 1][j], dp[i - 1][j - 1]) + 1 return dp[n][m] ``` @@ -104,55 +104,31 @@ Observing the state transition equation, solving $dp[i, j]$ depends on the solut === "C++" ```cpp title="edit_distance.cpp" - /* 编辑距离:动态规划 */ - int editDistanceDP(string s, string t) { - int n = s.length(), m = t.length(); - vector> dp(n + 1, vector(m + 1, 0)); - // 状态转移:首行首列 - for (int i = 1; i <= n; i++) { - dp[i][0] = i; - } - for (int j = 1; j <= m; j++) { - dp[0][j] = j; - } - // 状态转移:其余行和列 - for (int i = 1; i <= n; i++) { - for (int j = 1; j <= m; j++) { - if (s[i - 1] == t[j - 1]) { - // 若两字符相等,则直接跳过此两字符 - dp[i][j] = dp[i - 1][j - 1]; - } else { - // 最少编辑步数 = 插入、删除、替换这三种操作的最少编辑步数 + 1 - dp[i][j] = min(min(dp[i][j - 1], dp[i - 1][j]), dp[i - 1][j - 1]) + 1; - } - } - } - return dp[n][m]; - } + [class]{}-[func]{editDistanceDP} ``` === "Java" ```java title="edit_distance.java" - /* 编辑距离:动态规划 */ + /* Edit distance: Dynamic programming */ int editDistanceDP(String s, String t) { int n = s.length(), m = t.length(); int[][] dp = new int[n + 1][m + 1]; - // 状态转移:首行首列 + // State transition: first row and first column for (int i = 1; i <= n; i++) { dp[i][0] = i; } for (int j = 1; j <= m; j++) { dp[0][j] = j; } - // 状态转移:其余行和列 + // State transition: the rest of the rows and columns for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { if (s.charAt(i - 1) == t.charAt(j - 1)) { - // 若两字符相等,则直接跳过此两字符 + // If the two characters are equal, skip these two characters dp[i][j] = dp[i - 1][j - 1]; } else { - // 最少编辑步数 = 插入、删除、替换这三种操作的最少编辑步数 + 1 + // The minimum number of edits = the minimum number of edits from three operations (insert, remove, replace) + 1 dp[i][j] = Math.min(Math.min(dp[i][j - 1], dp[i - 1][j]), dp[i - 1][j - 1]) + 1; } } @@ -164,291 +140,55 @@ Observing the state transition equation, solving $dp[i, j]$ depends on the solut === "C#" ```csharp title="edit_distance.cs" - /* 编辑距离:动态规划 */ - int EditDistanceDP(string s, string t) { - int n = s.Length, m = t.Length; - int[,] dp = new int[n + 1, m + 1]; - // 状态转移:首行首列 - for (int i = 1; i <= n; i++) { - dp[i, 0] = i; - } - for (int j = 1; j <= m; j++) { - dp[0, j] = j; - } - // 状态转移:其余行和列 - for (int i = 1; i <= n; i++) { - for (int j = 1; j <= m; j++) { - if (s[i - 1] == t[j - 1]) { - // 若两字符相等,则直接跳过此两字符 - dp[i, j] = dp[i - 1, j - 1]; - } else { - // 最少编辑步数 = 插入、删除、替换这三种操作的最少编辑步数 + 1 - dp[i, j] = Math.Min(Math.Min(dp[i, j - 1], dp[i - 1, j]), dp[i - 1, j - 1]) + 1; - } - } - } - return dp[n, m]; - } + [class]{edit_distance}-[func]{EditDistanceDP} ``` === "Go" ```go title="edit_distance.go" - /* 编辑距离:动态规划 */ - func editDistanceDP(s string, t string) int { - n := len(s) - m := len(t) - dp := make([][]int, n+1) - for i := 0; i <= n; i++ { - dp[i] = make([]int, m+1) - } - // 状态转移:首行首列 - for i := 1; i <= n; i++ { - dp[i][0] = i - } - for j := 1; j <= m; j++ { - dp[0][j] = j - } - // 状态转移:其余行和列 - for i := 1; i <= n; i++ { - for j := 1; j <= m; j++ { - if s[i-1] == t[j-1] { - // 若两字符相等,则直接跳过此两字符 - dp[i][j] = dp[i-1][j-1] - } else { - // 最少编辑步数 = 插入、删除、替换这三种操作的最少编辑步数 + 1 - dp[i][j] = MinInt(MinInt(dp[i][j-1], dp[i-1][j]), dp[i-1][j-1]) + 1 - } - } - } - return dp[n][m] - } + [class]{}-[func]{editDistanceDP} ``` === "Swift" ```swift title="edit_distance.swift" - /* 编辑距离:动态规划 */ - func editDistanceDP(s: String, t: String) -> Int { - let n = s.utf8CString.count - let m = t.utf8CString.count - var dp = Array(repeating: Array(repeating: 0, count: m + 1), count: n + 1) - // 状态转移:首行首列 - for i in 1 ... n { - dp[i][0] = i - } - for j in 1 ... m { - dp[0][j] = j - } - // 状态转移:其余行和列 - for i in 1 ... n { - for j in 1 ... m { - if s.utf8CString[i - 1] == t.utf8CString[j - 1] { - // 若两字符相等,则直接跳过此两字符 - dp[i][j] = dp[i - 1][j - 1] - } else { - // 最少编辑步数 = 插入、删除、替换这三种操作的最少编辑步数 + 1 - dp[i][j] = min(min(dp[i][j - 1], dp[i - 1][j]), dp[i - 1][j - 1]) + 1 - } - } - } - return dp[n][m] - } + [class]{}-[func]{editDistanceDP} ``` === "JS" ```javascript title="edit_distance.js" - /* 编辑距离:动态规划 */ - function editDistanceDP(s, t) { - const n = s.length, - m = t.length; - const dp = Array.from({ length: n + 1 }, () => new Array(m + 1).fill(0)); - // 状态转移:首行首列 - for (let i = 1; i <= n; i++) { - dp[i][0] = i; - } - for (let j = 1; j <= m; j++) { - dp[0][j] = j; - } - // 状态转移:其余行和列 - for (let i = 1; i <= n; i++) { - for (let j = 1; j <= m; j++) { - if (s.charAt(i - 1) === t.charAt(j - 1)) { - // 若两字符相等,则直接跳过此两字符 - dp[i][j] = dp[i - 1][j - 1]; - } else { - // 最少编辑步数 = 插入、删除、替换这三种操作的最少编辑步数 + 1 - dp[i][j] = - Math.min(dp[i][j - 1], dp[i - 1][j], dp[i - 1][j - 1]) + 1; - } - } - } - return dp[n][m]; - } + [class]{}-[func]{editDistanceDP} ``` === "TS" ```typescript title="edit_distance.ts" - /* 编辑距离:动态规划 */ - function editDistanceDP(s: string, t: string): number { - const n = s.length, - m = t.length; - const dp = Array.from({ length: n + 1 }, () => - Array.from({ length: m + 1 }, () => 0) - ); - // 状态转移:首行首列 - for (let i = 1; i <= n; i++) { - dp[i][0] = i; - } - for (let j = 1; j <= m; j++) { - dp[0][j] = j; - } - // 状态转移:其余行和列 - for (let i = 1; i <= n; i++) { - for (let j = 1; j <= m; j++) { - if (s.charAt(i - 1) === t.charAt(j - 1)) { - // 若两字符相等,则直接跳过此两字符 - dp[i][j] = dp[i - 1][j - 1]; - } else { - // 最少编辑步数 = 插入、删除、替换这三种操作的最少编辑步数 + 1 - dp[i][j] = - Math.min(dp[i][j - 1], dp[i - 1][j], dp[i - 1][j - 1]) + 1; - } - } - } - return dp[n][m]; - } + [class]{}-[func]{editDistanceDP} ``` === "Dart" ```dart title="edit_distance.dart" - /* 编辑距离:动态规划 */ - int editDistanceDP(String s, String t) { - int n = s.length, m = t.length; - List> dp = List.generate(n + 1, (_) => List.filled(m + 1, 0)); - // 状态转移:首行首列 - for (int i = 1; i <= n; i++) { - dp[i][0] = i; - } - for (int j = 1; j <= m; j++) { - dp[0][j] = j; - } - // 状态转移:其余行和列 - for (int i = 1; i <= n; i++) { - for (int j = 1; j <= m; j++) { - if (s[i - 1] == t[j - 1]) { - // 若两字符相等,则直接跳过此两字符 - dp[i][j] = dp[i - 1][j - 1]; - } else { - // 最少编辑步数 = 插入、删除、替换这三种操作的最少编辑步数 + 1 - dp[i][j] = min(min(dp[i][j - 1], dp[i - 1][j]), dp[i - 1][j - 1]) + 1; - } - } - } - return dp[n][m]; - } + [class]{}-[func]{editDistanceDP} ``` === "Rust" ```rust title="edit_distance.rs" - /* 编辑距离:动态规划 */ - fn edit_distance_dp(s: &str, t: &str) -> i32 { - let (n, m) = (s.len(), t.len()); - let mut dp = vec![vec![0; m + 1]; n + 1]; - // 状态转移:首行首列 - for i in 1..=n { - dp[i][0] = i as i32; - } - for j in 1..m { - dp[0][j] = j as i32; - } - // 状态转移:其余行和列 - for i in 1..=n { - for j in 1..=m { - if s.chars().nth(i - 1) == t.chars().nth(j - 1) { - // 若两字符相等,则直接跳过此两字符 - dp[i][j] = dp[i - 1][j - 1]; - } else { - // 最少编辑步数 = 插入、删除、替换这三种操作的最少编辑步数 + 1 - dp[i][j] = - std::cmp::min(std::cmp::min(dp[i][j - 1], dp[i - 1][j]), dp[i - 1][j - 1]) + 1; - } - } - } - dp[n][m] - } + [class]{}-[func]{edit_distance_dp} ``` === "C" ```c title="edit_distance.c" - /* 编辑距离:动态规划 */ - int editDistanceDP(char *s, char *t, int n, int m) { - int **dp = malloc((n + 1) * sizeof(int *)); - for (int i = 0; i <= n; i++) { - dp[i] = calloc(m + 1, sizeof(int)); - } - // 状态转移:首行首列 - for (int i = 1; i <= n; i++) { - dp[i][0] = i; - } - for (int j = 1; j <= m; j++) { - dp[0][j] = j; - } - // 状态转移:其余行和列 - for (int i = 1; i <= n; i++) { - for (int j = 1; j <= m; j++) { - if (s[i - 1] == t[j - 1]) { - // 若两字符相等,则直接跳过此两字符 - dp[i][j] = dp[i - 1][j - 1]; - } else { - // 最少编辑步数 = 插入、删除、替换这三种操作的最少编辑步数 + 1 - dp[i][j] = myMin(myMin(dp[i][j - 1], dp[i - 1][j]), dp[i - 1][j - 1]) + 1; - } - } - } - int res = dp[n][m]; - // 释放内存 - for (int i = 0; i <= n; i++) { - free(dp[i]); - } - return res; - } + [class]{}-[func]{editDistanceDP} ``` === "Kotlin" ```kotlin title="edit_distance.kt" - /* 编辑距离:动态规划 */ - fun editDistanceDP(s: String, t: String): Int { - val n = s.length - val m = t.length - val dp = Array(n + 1) { IntArray(m + 1) } - // 状态转移:首行首列 - for (i in 1..n) { - dp[i][0] = i - } - for (j in 1..m) { - dp[0][j] = j - } - // 状态转移:其余行和列 - for (i in 1..n) { - for (j in 1..m) { - if (s[i - 1] == t[j - 1]) { - // 若两字符相等,则直接跳过此两字符 - dp[i][j] = dp[i - 1][j - 1] - } else { - // 最少编辑步数 = 插入、删除、替换这三种操作的最少编辑步数 + 1 - dp[i][j] = min(min(dp[i][j - 1], dp[i - 1][j]), dp[i - 1][j - 1]) + 1 - } - } - } - return dp[n][m] - } + [class]{}-[func]{editDistanceDP} ``` === "Ruby" @@ -460,39 +200,9 @@ Observing the state transition equation, solving $dp[i, j]$ depends on the solut === "Zig" ```zig title="edit_distance.zig" - // 编辑距离:动态规划 - fn editDistanceDP(comptime s: []const u8, comptime t: []const u8) i32 { - comptime var n = s.len; - comptime var m = t.len; - var dp = [_][m + 1]i32{[_]i32{0} ** (m + 1)} ** (n + 1); - // 状态转移:首行首列 - for (1..n + 1) |i| { - dp[i][0] = @intCast(i); - } - for (1..m + 1) |j| { - dp[0][j] = @intCast(j); - } - // 状态转移:其余行和列 - for (1..n + 1) |i| { - for (1..m + 1) |j| { - if (s[i - 1] == t[j - 1]) { - // 若两字符相等,则直接跳过此两字符 - dp[i][j] = dp[i - 1][j - 1]; - } else { - // 最少编辑步数 = 插入、删除、替换这三种操作的最少编辑步数 + 1 - dp[i][j] = @min(@min(dp[i][j - 1], dp[i - 1][j]), dp[i - 1][j - 1]) + 1; - } - } - } - return dp[n][m]; - } + [class]{}-[func]{editDistanceDP} ``` -??? pythontutor "Code Visualization" - -
- - As shown in Figure 14-30, the process of state transition in the edit distance problem is very similar to that in the knapsack problem, which can be seen as filling a two-dimensional grid. === "<1>" @@ -552,90 +262,63 @@ For this reason, we can use a variable `leftup` to temporarily store the solutio ```python title="edit_distance.py" def edit_distance_dp_comp(s: str, t: str) -> int: - """编辑距离:空间优化后的动态规划""" + """Edit distance: Space-optimized dynamic programming""" n, m = len(s), len(t) dp = [0] * (m + 1) - # 状态转移:首行 + # State transition: first row for j in range(1, m + 1): dp[j] = j - # 状态转移:其余行 + # State transition: the rest of the rows for i in range(1, n + 1): - # 状态转移:首列 - leftup = dp[0] # 暂存 dp[i-1, j-1] + # State transition: first column + leftup = dp[0] # Temporarily store dp[i-1, j-1] dp[0] += 1 - # 状态转移:其余列 + # State transition: the rest of the columns for j in range(1, m + 1): temp = dp[j] if s[i - 1] == t[j - 1]: - # 若两字符相等,则直接跳过此两字符 + # If the two characters are equal, skip these two characters dp[j] = leftup else: - # 最少编辑步数 = 插入、删除、替换这三种操作的最少编辑步数 + 1 + # The minimum number of edits = the minimum number of edits from three operations (insert, remove, replace) + 1 dp[j] = min(dp[j - 1], dp[j], leftup) + 1 - leftup = temp # 更新为下一轮的 dp[i-1, j-1] + leftup = temp # Update for the next round of dp[i-1, j-1] return dp[m] ``` === "C++" ```cpp title="edit_distance.cpp" - /* 编辑距离:空间优化后的动态规划 */ - int editDistanceDPComp(string s, string t) { - int n = s.length(), m = t.length(); - vector dp(m + 1, 0); - // 状态转移:首行 - for (int j = 1; j <= m; j++) { - dp[j] = j; - } - // 状态转移:其余行 - for (int i = 1; i <= n; i++) { - // 状态转移:首列 - int leftup = dp[0]; // 暂存 dp[i-1, j-1] - dp[0] = i; - // 状态转移:其余列 - for (int j = 1; j <= m; j++) { - int temp = dp[j]; - if (s[i - 1] == t[j - 1]) { - // 若两字符相等,则直接跳过此两字符 - dp[j] = leftup; - } else { - // 最少编辑步数 = 插入、删除、替换这三种操作的最少编辑步数 + 1 - dp[j] = min(min(dp[j - 1], dp[j]), leftup) + 1; - } - leftup = temp; // 更新为下一轮的 dp[i-1, j-1] - } - } - return dp[m]; - } + [class]{}-[func]{editDistanceDPComp} ``` === "Java" ```java title="edit_distance.java" - /* 编辑距离:空间优化后的动态规划 */ + /* Edit distance: Space-optimized dynamic programming */ int editDistanceDPComp(String s, String t) { int n = s.length(), m = t.length(); int[] dp = new int[m + 1]; - // 状态转移:首行 + // State transition: first row for (int j = 1; j <= m; j++) { dp[j] = j; } - // 状态转移:其余行 + // State transition: the rest of the rows for (int i = 1; i <= n; i++) { - // 状态转移:首列 - int leftup = dp[0]; // 暂存 dp[i-1, j-1] + // State transition: first column + int leftup = dp[0]; // Temporarily store dp[i-1, j-1] dp[0] = i; - // 状态转移:其余列 + // State transition: the rest of the columns for (int j = 1; j <= m; j++) { int temp = dp[j]; if (s.charAt(i - 1) == t.charAt(j - 1)) { - // 若两字符相等,则直接跳过此两字符 + // If the two characters are equal, skip these two characters dp[j] = leftup; } else { - // 最少编辑步数 = 插入、删除、替换这三种操作的最少编辑步数 + 1 + // The minimum number of edits = the minimum number of edits from three operations (insert, remove, replace) + 1 dp[j] = Math.min(Math.min(dp[j - 1], dp[j]), leftup) + 1; } - leftup = temp; // 更新为下一轮的 dp[i-1, j-1] + leftup = temp; // Update for the next round of dp[i-1, j-1] } } return dp[m]; @@ -645,305 +328,55 @@ For this reason, we can use a variable `leftup` to temporarily store the solutio === "C#" ```csharp title="edit_distance.cs" - /* 编辑距离:空间优化后的动态规划 */ - int EditDistanceDPComp(string s, string t) { - int n = s.Length, m = t.Length; - int[] dp = new int[m + 1]; - // 状态转移:首行 - for (int j = 1; j <= m; j++) { - dp[j] = j; - } - // 状态转移:其余行 - for (int i = 1; i <= n; i++) { - // 状态转移:首列 - int leftup = dp[0]; // 暂存 dp[i-1, j-1] - dp[0] = i; - // 状态转移:其余列 - for (int j = 1; j <= m; j++) { - int temp = dp[j]; - if (s[i - 1] == t[j - 1]) { - // 若两字符相等,则直接跳过此两字符 - dp[j] = leftup; - } else { - // 最少编辑步数 = 插入、删除、替换这三种操作的最少编辑步数 + 1 - dp[j] = Math.Min(Math.Min(dp[j - 1], dp[j]), leftup) + 1; - } - leftup = temp; // 更新为下一轮的 dp[i-1, j-1] - } - } - return dp[m]; - } + [class]{edit_distance}-[func]{EditDistanceDPComp} ``` === "Go" ```go title="edit_distance.go" - /* 编辑距离:空间优化后的动态规划 */ - func editDistanceDPComp(s string, t string) int { - n := len(s) - m := len(t) - dp := make([]int, m+1) - // 状态转移:首行 - for j := 1; j <= m; j++ { - dp[j] = j - } - // 状态转移:其余行 - for i := 1; i <= n; i++ { - // 状态转移:首列 - leftUp := dp[0] // 暂存 dp[i-1, j-1] - dp[0] = i - // 状态转移:其余列 - for j := 1; j <= m; j++ { - temp := dp[j] - if s[i-1] == t[j-1] { - // 若两字符相等,则直接跳过此两字符 - dp[j] = leftUp - } else { - // 最少编辑步数 = 插入、删除、替换这三种操作的最少编辑步数 + 1 - dp[j] = MinInt(MinInt(dp[j-1], dp[j]), leftUp) + 1 - } - leftUp = temp // 更新为下一轮的 dp[i-1, j-1] - } - } - return dp[m] - } + [class]{}-[func]{editDistanceDPComp} ``` === "Swift" ```swift title="edit_distance.swift" - /* 编辑距离:空间优化后的动态规划 */ - func editDistanceDPComp(s: String, t: String) -> Int { - let n = s.utf8CString.count - let m = t.utf8CString.count - var dp = Array(repeating: 0, count: m + 1) - // 状态转移:首行 - for j in 1 ... m { - dp[j] = j - } - // 状态转移:其余行 - for i in 1 ... n { - // 状态转移:首列 - var leftup = dp[0] // 暂存 dp[i-1, j-1] - dp[0] = i - // 状态转移:其余列 - for j in 1 ... m { - let temp = dp[j] - if s.utf8CString[i - 1] == t.utf8CString[j - 1] { - // 若两字符相等,则直接跳过此两字符 - dp[j] = leftup - } else { - // 最少编辑步数 = 插入、删除、替换这三种操作的最少编辑步数 + 1 - dp[j] = min(min(dp[j - 1], dp[j]), leftup) + 1 - } - leftup = temp // 更新为下一轮的 dp[i-1, j-1] - } - } - return dp[m] - } + [class]{}-[func]{editDistanceDPComp} ``` === "JS" ```javascript title="edit_distance.js" - /* 编辑距离:状态压缩后的动态规划 */ - function editDistanceDPComp(s, t) { - const n = s.length, - m = t.length; - const dp = new Array(m + 1).fill(0); - // 状态转移:首行 - for (let j = 1; j <= m; j++) { - dp[j] = j; - } - // 状态转移:其余行 - for (let i = 1; i <= n; i++) { - // 状态转移:首列 - let leftup = dp[0]; // 暂存 dp[i-1, j-1] - dp[0] = i; - // 状态转移:其余列 - for (let j = 1; j <= m; j++) { - const temp = dp[j]; - if (s.charAt(i - 1) === t.charAt(j - 1)) { - // 若两字符相等,则直接跳过此两字符 - dp[j] = leftup; - } else { - // 最少编辑步数 = 插入、删除、替换这三种操作的最少编辑步数 + 1 - dp[j] = Math.min(dp[j - 1], dp[j], leftup) + 1; - } - leftup = temp; // 更新为下一轮的 dp[i-1, j-1] - } - } - return dp[m]; - } + [class]{}-[func]{editDistanceDPComp} ``` === "TS" ```typescript title="edit_distance.ts" - /* 编辑距离:状态压缩后的动态规划 */ - function editDistanceDPComp(s: string, t: string): number { - const n = s.length, - m = t.length; - const dp = new Array(m + 1).fill(0); - // 状态转移:首行 - for (let j = 1; j <= m; j++) { - dp[j] = j; - } - // 状态转移:其余行 - for (let i = 1; i <= n; i++) { - // 状态转移:首列 - let leftup = dp[0]; // 暂存 dp[i-1, j-1] - dp[0] = i; - // 状态转移:其余列 - for (let j = 1; j <= m; j++) { - const temp = dp[j]; - if (s.charAt(i - 1) === t.charAt(j - 1)) { - // 若两字符相等,则直接跳过此两字符 - dp[j] = leftup; - } else { - // 最少编辑步数 = 插入、删除、替换这三种操作的最少编辑步数 + 1 - dp[j] = Math.min(dp[j - 1], dp[j], leftup) + 1; - } - leftup = temp; // 更新为下一轮的 dp[i-1, j-1] - } - } - return dp[m]; - } + [class]{}-[func]{editDistanceDPComp} ``` === "Dart" ```dart title="edit_distance.dart" - /* 编辑距离:空间优化后的动态规划 */ - int editDistanceDPComp(String s, String t) { - int n = s.length, m = t.length; - List dp = List.filled(m + 1, 0); - // 状态转移:首行 - for (int j = 1; j <= m; j++) { - dp[j] = j; - } - // 状态转移:其余行 - for (int i = 1; i <= n; i++) { - // 状态转移:首列 - int leftup = dp[0]; // 暂存 dp[i-1, j-1] - dp[0] = i; - // 状态转移:其余列 - for (int j = 1; j <= m; j++) { - int temp = dp[j]; - if (s[i - 1] == t[j - 1]) { - // 若两字符相等,则直接跳过此两字符 - dp[j] = leftup; - } else { - // 最少编辑步数 = 插入、删除、替换这三种操作的最少编辑步数 + 1 - dp[j] = min(min(dp[j - 1], dp[j]), leftup) + 1; - } - leftup = temp; // 更新为下一轮的 dp[i-1, j-1] - } - } - return dp[m]; - } + [class]{}-[func]{editDistanceDPComp} ``` === "Rust" ```rust title="edit_distance.rs" - /* 编辑距离:空间优化后的动态规划 */ - fn edit_distance_dp_comp(s: &str, t: &str) -> i32 { - let (n, m) = (s.len(), t.len()); - let mut dp = vec![0; m + 1]; - // 状态转移:首行 - for j in 1..m { - dp[j] = j as i32; - } - // 状态转移:其余行 - for i in 1..=n { - // 状态转移:首列 - let mut leftup = dp[0]; // 暂存 dp[i-1, j-1] - dp[0] = i as i32; - // 状态转移:其余列 - for j in 1..=m { - let temp = dp[j]; - if s.chars().nth(i - 1) == t.chars().nth(j - 1) { - // 若两字符相等,则直接跳过此两字符 - dp[j] = leftup; - } else { - // 最少编辑步数 = 插入、删除、替换这三种操作的最少编辑步数 + 1 - dp[j] = std::cmp::min(std::cmp::min(dp[j - 1], dp[j]), leftup) + 1; - } - leftup = temp; // 更新为下一轮的 dp[i-1, j-1] - } - } - dp[m] - } + [class]{}-[func]{edit_distance_dp_comp} ``` === "C" ```c title="edit_distance.c" - /* 编辑距离:空间优化后的动态规划 */ - int editDistanceDPComp(char *s, char *t, int n, int m) { - int *dp = calloc(m + 1, sizeof(int)); - // 状态转移:首行 - for (int j = 1; j <= m; j++) { - dp[j] = j; - } - // 状态转移:其余行 - for (int i = 1; i <= n; i++) { - // 状态转移:首列 - int leftup = dp[0]; // 暂存 dp[i-1, j-1] - dp[0] = i; - // 状态转移:其余列 - for (int j = 1; j <= m; j++) { - int temp = dp[j]; - if (s[i - 1] == t[j - 1]) { - // 若两字符相等,则直接跳过此两字符 - dp[j] = leftup; - } else { - // 最少编辑步数 = 插入、删除、替换这三种操作的最少编辑步数 + 1 - dp[j] = myMin(myMin(dp[j - 1], dp[j]), leftup) + 1; - } - leftup = temp; // 更新为下一轮的 dp[i-1, j-1] - } - } - int res = dp[m]; - // 释放内存 - free(dp); - return res; - } + [class]{}-[func]{editDistanceDPComp} ``` === "Kotlin" ```kotlin title="edit_distance.kt" - /* 编辑距离:空间优化后的动态规划 */ - fun editDistanceDPComp(s: String, t: String): Int { - val n = s.length - val m = t.length - val dp = IntArray(m + 1) - // 状态转移:首行 - for (j in 1..m) { - dp[j] = j - } - // 状态转移:其余行 - for (i in 1..n) { - // 状态转移:首列 - var leftup = dp[0] // 暂存 dp[i-1, j-1] - dp[0] = i - // 状态转移:其余列 - for (j in 1..m) { - val temp = dp[j] - if (s[i - 1] == t[j - 1]) { - // 若两字符相等,则直接跳过此两字符 - dp[j] = leftup - } else { - // 最少编辑步数 = 插入、删除、替换这三种操作的最少编辑步数 + 1 - dp[j] = min(min(dp[j - 1], dp[j]), leftup) + 1 - } - leftup = temp // 更新为下一轮的 dp[i-1, j-1] - } - } - return dp[m] - } + [class]{}-[func]{editDistanceDPComp} ``` === "Ruby" @@ -955,38 +388,5 @@ For this reason, we can use a variable `leftup` to temporarily store the solutio === "Zig" ```zig title="edit_distance.zig" - // 编辑距离:空间优化后的动态规划 - fn editDistanceDPComp(comptime s: []const u8, comptime t: []const u8) i32 { - comptime var n = s.len; - comptime var m = t.len; - var dp = [_]i32{0} ** (m + 1); - // 状态转移:首行 - for (1..m + 1) |j| { - dp[j] = @intCast(j); - } - // 状态转移:其余行 - for (1..n + 1) |i| { - // 状态转移:首列 - var leftup = dp[0]; // 暂存 dp[i-1, j-1] - dp[0] = @intCast(i); - // 状态转移:其余列 - for (1..m + 1) |j| { - var temp = dp[j]; - if (s[i - 1] == t[j - 1]) { - // 若两字符相等,则直接跳过此两字符 - dp[j] = leftup; - } else { - // 最少编辑步数 = 插入、删除、替换这三种操作的最少编辑步数 + 1 - dp[j] = @min(@min(dp[j - 1], dp[j]), leftup) + 1; - } - leftup = temp; // 更新为下一轮的 dp[i-1, j-1] - } - } - return dp[m]; - } + [class]{}-[func]{editDistanceDPComp} ``` - -??? pythontutor "Code Visualization" - -
- diff --git a/en/docs/chapter_dynamic_programming/intro_to_dynamic_programming.md b/en/docs/chapter_dynamic_programming/intro_to_dynamic_programming.md index bd192b70e..382580a6a 100644 --- a/en/docs/chapter_dynamic_programming/intro_to_dynamic_programming.md +++ b/en/docs/chapter_dynamic_programming/intro_to_dynamic_programming.md @@ -24,24 +24,24 @@ The goal of this problem is to determine the number of ways, **considering using ```python title="climbing_stairs_backtrack.py" def backtrack(choices: list[int], state: int, n: int, res: list[int]) -> int: - """回溯""" - # 当爬到第 n 阶时,方案数量加 1 + """Backtracking""" + # When climbing to the nth step, add 1 to the number of solutions if state == n: res[0] += 1 - # 遍历所有选择 + # Traverse all choices for choice in choices: - # 剪枝:不允许越过第 n 阶 + # Pruning: do not allow climbing beyond the nth step if state + choice > n: continue - # 尝试:做出选择,更新状态 + # Attempt: make a choice, update the state backtrack(choices, state + choice, n, res) - # 回退 + # Retract def climbing_stairs_backtrack(n: int) -> int: - """爬楼梯:回溯""" - choices = [1, 2] # 可选择向上爬 1 阶或 2 阶 - state = 0 # 从第 0 阶开始爬 - res = [0] # 使用 res[0] 记录方案数量 + """Climbing stairs: Backtracking""" + choices = [1, 2] # Can choose to climb up 1 step or 2 steps + state = 0 # Start climbing from the 0th step + res = [0] # Use res[0] to record the number of solutions backtrack(choices, state, n, res) return res[0] ``` @@ -49,57 +49,36 @@ The goal of this problem is to determine the number of ways, **considering using === "C++" ```cpp title="climbing_stairs_backtrack.cpp" - /* 回溯 */ - void backtrack(vector &choices, int state, int n, vector &res) { - // 当爬到第 n 阶时,方案数量加 1 - if (state == n) - res[0]++; - // 遍历所有选择 - for (auto &choice : choices) { - // 剪枝:不允许越过第 n 阶 - if (state + choice > n) - continue; - // 尝试:做出选择,更新状态 - backtrack(choices, state + choice, n, res); - // 回退 - } - } + [class]{}-[func]{backtrack} - /* 爬楼梯:回溯 */ - int climbingStairsBacktrack(int n) { - vector choices = {1, 2}; // 可选择向上爬 1 阶或 2 阶 - int state = 0; // 从第 0 阶开始爬 - vector res = {0}; // 使用 res[0] 记录方案数量 - backtrack(choices, state, n, res); - return res[0]; - } + [class]{}-[func]{climbingStairsBacktrack} ``` === "Java" ```java title="climbing_stairs_backtrack.java" - /* 回溯 */ + /* Backtracking */ void backtrack(List choices, int state, int n, List res) { - // 当爬到第 n 阶时,方案数量加 1 + // When climbing to the nth step, add 1 to the number of solutions if (state == n) res.set(0, res.get(0) + 1); - // 遍历所有选择 + // Traverse all choices for (Integer choice : choices) { - // 剪枝:不允许越过第 n 阶 + // Pruning: do not allow climbing beyond the nth step if (state + choice > n) continue; - // 尝试:做出选择,更新状态 + // Attempt: make a choice, update the state backtrack(choices, state + choice, n, res); - // 回退 + // Retract } } - /* 爬楼梯:回溯 */ + /* Climbing stairs: Backtracking */ int climbingStairsBacktrack(int n) { - List choices = Arrays.asList(1, 2); // 可选择向上爬 1 阶或 2 阶 - int state = 0; // 从第 0 阶开始爬 + List choices = Arrays.asList(1, 2); // Can choose to climb up 1 step or 2 steps + int state = 0; // Start climbing from the 0th step List res = new ArrayList<>(); - res.add(0); // 使用 res[0] 记录方案数量 + res.add(0); // Use res[0] to record the number of solutions backtrack(choices, state, n, res); return res.get(0); } @@ -108,288 +87,73 @@ The goal of this problem is to determine the number of ways, **considering using === "C#" ```csharp title="climbing_stairs_backtrack.cs" - /* 回溯 */ - void Backtrack(List choices, int state, int n, List res) { - // 当爬到第 n 阶时,方案数量加 1 - if (state == n) - res[0]++; - // 遍历所有选择 - foreach (int choice in choices) { - // 剪枝:不允许越过第 n 阶 - if (state + choice > n) - continue; - // 尝试:做出选择,更新状态 - Backtrack(choices, state + choice, n, res); - // 回退 - } - } + [class]{climbing_stairs_backtrack}-[func]{Backtrack} - /* 爬楼梯:回溯 */ - int ClimbingStairsBacktrack(int n) { - List choices = [1, 2]; // 可选择向上爬 1 阶或 2 阶 - int state = 0; // 从第 0 阶开始爬 - List res = [0]; // 使用 res[0] 记录方案数量 - Backtrack(choices, state, n, res); - return res[0]; - } + [class]{climbing_stairs_backtrack}-[func]{ClimbingStairsBacktrack} ``` === "Go" ```go title="climbing_stairs_backtrack.go" - /* 回溯 */ - func backtrack(choices []int, state, n int, res []int) { - // 当爬到第 n 阶时,方案数量加 1 - if state == n { - res[0] = res[0] + 1 - } - // 遍历所有选择 - for _, choice := range choices { - // 剪枝:不允许越过第 n 阶 - if state+choice > n { - continue - } - // 尝试:做出选择,更新状态 - backtrack(choices, state+choice, n, res) - // 回退 - } - } + [class]{}-[func]{backtrack} - /* 爬楼梯:回溯 */ - func climbingStairsBacktrack(n int) int { - // 可选择向上爬 1 阶或 2 阶 - choices := []int{1, 2} - // 从第 0 阶开始爬 - state := 0 - res := make([]int, 1) - // 使用 res[0] 记录方案数量 - res[0] = 0 - backtrack(choices, state, n, res) - return res[0] - } + [class]{}-[func]{climbingStairsBacktrack} ``` === "Swift" ```swift title="climbing_stairs_backtrack.swift" - /* 回溯 */ - func backtrack(choices: [Int], state: Int, n: Int, res: inout [Int]) { - // 当爬到第 n 阶时,方案数量加 1 - if state == n { - res[0] += 1 - } - // 遍历所有选择 - for choice in choices { - // 剪枝:不允许越过第 n 阶 - if state + choice > n { - continue - } - // 尝试:做出选择,更新状态 - backtrack(choices: choices, state: state + choice, n: n, res: &res) - // 回退 - } - } + [class]{}-[func]{backtrack} - /* 爬楼梯:回溯 */ - func climbingStairsBacktrack(n: Int) -> Int { - let choices = [1, 2] // 可选择向上爬 1 阶或 2 阶 - let state = 0 // 从第 0 阶开始爬 - var res: [Int] = [] - res.append(0) // 使用 res[0] 记录方案数量 - backtrack(choices: choices, state: state, n: n, res: &res) - return res[0] - } + [class]{}-[func]{climbingStairsBacktrack} ``` === "JS" ```javascript title="climbing_stairs_backtrack.js" - /* 回溯 */ - function backtrack(choices, state, n, res) { - // 当爬到第 n 阶时,方案数量加 1 - if (state === n) res.set(0, res.get(0) + 1); - // 遍历所有选择 - for (const choice of choices) { - // 剪枝:不允许越过第 n 阶 - if (state + choice > n) continue; - // 尝试:做出选择,更新状态 - backtrack(choices, state + choice, n, res); - // 回退 - } - } + [class]{}-[func]{backtrack} - /* 爬楼梯:回溯 */ - function climbingStairsBacktrack(n) { - const choices = [1, 2]; // 可选择向上爬 1 阶或 2 阶 - const state = 0; // 从第 0 阶开始爬 - const res = new Map(); - res.set(0, 0); // 使用 res[0] 记录方案数量 - backtrack(choices, state, n, res); - return res.get(0); - } + [class]{}-[func]{climbingStairsBacktrack} ``` === "TS" ```typescript title="climbing_stairs_backtrack.ts" - /* 回溯 */ - function backtrack( - choices: number[], - state: number, - n: number, - res: Map<0, any> - ): void { - // 当爬到第 n 阶时,方案数量加 1 - if (state === n) res.set(0, res.get(0) + 1); - // 遍历所有选择 - for (const choice of choices) { - // 剪枝:不允许越过第 n 阶 - if (state + choice > n) continue; - // 尝试:做出选择,更新状态 - backtrack(choices, state + choice, n, res); - // 回退 - } - } + [class]{}-[func]{backtrack} - /* 爬楼梯:回溯 */ - function climbingStairsBacktrack(n: number): number { - const choices = [1, 2]; // 可选择向上爬 1 阶或 2 阶 - const state = 0; // 从第 0 阶开始爬 - const res = new Map(); - res.set(0, 0); // 使用 res[0] 记录方案数量 - backtrack(choices, state, n, res); - return res.get(0); - } + [class]{}-[func]{climbingStairsBacktrack} ``` === "Dart" ```dart title="climbing_stairs_backtrack.dart" - /* 回溯 */ - void backtrack(List choices, int state, int n, List res) { - // 当爬到第 n 阶时,方案数量加 1 - if (state == n) { - res[0]++; - } - // 遍历所有选择 - for (int choice in choices) { - // 剪枝:不允许越过第 n 阶 - if (state + choice > n) continue; - // 尝试:做出选择,更新状态 - backtrack(choices, state + choice, n, res); - // 回退 - } - } + [class]{}-[func]{backtrack} - /* 爬楼梯:回溯 */ - int climbingStairsBacktrack(int n) { - List choices = [1, 2]; // 可选择向上爬 1 阶或 2 阶 - int state = 0; // 从第 0 阶开始爬 - List res = []; - res.add(0); // 使用 res[0] 记录方案数量 - backtrack(choices, state, n, res); - return res[0]; - } + [class]{}-[func]{climbingStairsBacktrack} ``` === "Rust" ```rust title="climbing_stairs_backtrack.rs" - /* 回溯 */ - fn backtrack(choices: &[i32], state: i32, n: i32, res: &mut [i32]) { - // 当爬到第 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); - // 回退 - } - } + [class]{}-[func]{backtrack} - /* 爬楼梯:回溯 */ - fn climbing_stairs_backtrack(n: usize) -> i32 { - let choices = vec![1, 2]; // 可选择向上爬 1 阶或 2 阶 - let state = 0; // 从第 0 阶开始爬 - let mut res = Vec::new(); - res.push(0); // 使用 res[0] 记录方案数量 - backtrack(&choices, state, n as i32, &mut res); - res[0] - } + [class]{}-[func]{climbing_stairs_backtrack} ``` === "C" ```c title="climbing_stairs_backtrack.c" - /* 回溯 */ - void backtrack(int *choices, int state, int n, int *res, int len) { - // 当爬到第 n 阶时,方案数量加 1 - if (state == n) - res[0]++; - // 遍历所有选择 - for (int i = 0; i < len; i++) { - int choice = choices[i]; - // 剪枝:不允许越过第 n 阶 - if (state + choice > n) - continue; - // 尝试:做出选择,更新状态 - backtrack(choices, state + choice, n, res, len); - // 回退 - } - } + [class]{}-[func]{backtrack} - /* 爬楼梯:回溯 */ - int climbingStairsBacktrack(int n) { - int choices[2] = {1, 2}; // 可选择向上爬 1 阶或 2 阶 - int state = 0; // 从第 0 阶开始爬 - int *res = (int *)malloc(sizeof(int)); - *res = 0; // 使用 res[0] 记录方案数量 - int len = sizeof(choices) / sizeof(int); - backtrack(choices, state, n, res, len); - int result = *res; - free(res); - return result; - } + [class]{}-[func]{climbingStairsBacktrack} ``` === "Kotlin" ```kotlin title="climbing_stairs_backtrack.kt" - /* 回溯 */ - fun backtrack( - choices: MutableList, - 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) - // 回退 - } - } + [class]{}-[func]{backtrack} - /* 爬楼梯:回溯 */ - fun climbingStairsBacktrack(n: Int): Int { - val choices = mutableListOf(1, 2) // 可选择向上爬 1 阶或 2 阶 - val state = 0 // 从第 0 阶开始爬 - val res = mutableListOf() - res.add(0) // 使用 res[0] 记录方案数量 - backtrack(choices, state, n, res) - return res[0] - } + [class]{}-[func]{climbingStairsBacktrack} ``` === "Ruby" @@ -403,41 +167,11 @@ The goal of this problem is to determine the number of ways, **considering using === "Zig" ```zig title="climbing_stairs_backtrack.zig" - // 回溯 - fn backtrack(choices: []i32, state: i32, n: i32, res: std.ArrayList(i32)) void { - // 当爬到第 n 阶时,方案数量加 1 - if (state == n) { - res.items[0] = res.items[0] + 1; - } - // 遍历所有选择 - for (choices) |choice| { - // 剪枝:不允许越过第 n 阶 - if (state + choice > n) { - continue; - } - // 尝试:做出选择,更新状态 - backtrack(choices, state + choice, n, res); - // 回退 - } - } + [class]{}-[func]{backtrack} - // 爬楼梯:回溯 - fn climbingStairsBacktrack(n: usize) !i32 { - var choices = [_]i32{ 1, 2 }; // 可选择向上爬 1 阶或 2 阶 - var state: i32 = 0; // 从第 0 阶开始爬 - var res = std.ArrayList(i32).init(std.heap.page_allocator); - defer res.deinit(); - try res.append(0); // 使用 res[0] 记录方案数量 - backtrack(&choices, state, @intCast(n), res); - return res.items[0]; - } + [class]{}-[func]{climbingStairsBacktrack} ``` -??? pythontutor "Code Visualization" - -
- - ## 14.1.1   Method 1: Brute force search Backtracking algorithms do not explicitly decompose the problem but treat solving the problem as a series of decision steps, searching for all possible solutions through exploration and pruning. @@ -470,8 +204,8 @@ Observe the following code, which, like standard backtracking code, belongs to d ```python title="climbing_stairs_dfs.py" def dfs(i: int) -> int: - """搜索""" - # 已知 dp[1] 和 dp[2] ,返回之 + """Search""" + # Known dp[1] and dp[2], return them if i == 1 or i == 2: return i # dp[i] = dp[i-1] + dp[i-2] @@ -479,35 +213,24 @@ Observe the following code, which, like standard backtracking code, belongs to d return count def climbing_stairs_dfs(n: int) -> int: - """爬楼梯:搜索""" + """Climbing stairs: Search""" return dfs(n) ``` === "C++" ```cpp title="climbing_stairs_dfs.cpp" - /* 搜索 */ - int dfs(int i) { - // 已知 dp[1] 和 dp[2] ,返回之 - if (i == 1 || i == 2) - return i; - // dp[i] = dp[i-1] + dp[i-2] - int count = dfs(i - 1) + dfs(i - 2); - return count; - } + [class]{}-[func]{dfs} - /* 爬楼梯:搜索 */ - int climbingStairsDFS(int n) { - return dfs(n); - } + [class]{}-[func]{climbingStairsDFS} ``` === "Java" ```java title="climbing_stairs_dfs.java" - /* 搜索 */ + /* Search */ int dfs(int i) { - // 已知 dp[1] 和 dp[2] ,返回之 + // Known dp[1] and dp[2], return them if (i == 1 || i == 2) return i; // dp[i] = dp[i-1] + dp[i-2] @@ -515,7 +238,7 @@ Observe the following code, which, like standard backtracking code, belongs to d return count; } - /* 爬楼梯:搜索 */ + /* Climbing stairs: Search */ int climbingStairsDFS(int n) { return dfs(n); } @@ -524,171 +247,73 @@ Observe the following code, which, like standard backtracking code, belongs to d === "C#" ```csharp title="climbing_stairs_dfs.cs" - /* 搜索 */ - int DFS(int i) { - // 已知 dp[1] 和 dp[2] ,返回之 - if (i == 1 || i == 2) - return i; - // dp[i] = dp[i-1] + dp[i-2] - int count = DFS(i - 1) + DFS(i - 2); - return count; - } + [class]{climbing_stairs_dfs}-[func]{DFS} - /* 爬楼梯:搜索 */ - int ClimbingStairsDFS(int n) { - return DFS(n); - } + [class]{climbing_stairs_dfs}-[func]{ClimbingStairsDFS} ``` === "Go" ```go title="climbing_stairs_dfs.go" - /* 搜索 */ - func dfs(i int) int { - // 已知 dp[1] 和 dp[2] ,返回之 - if i == 1 || i == 2 { - return i - } - // dp[i] = dp[i-1] + dp[i-2] - count := dfs(i-1) + dfs(i-2) - return count - } + [class]{}-[func]{dfs} - /* 爬楼梯:搜索 */ - func climbingStairsDFS(n int) int { - return dfs(n) - } + [class]{}-[func]{climbingStairsDFS} ``` === "Swift" ```swift title="climbing_stairs_dfs.swift" - /* 搜索 */ - func dfs(i: Int) -> Int { - // 已知 dp[1] 和 dp[2] ,返回之 - if i == 1 || i == 2 { - return i - } - // dp[i] = dp[i-1] + dp[i-2] - let count = dfs(i: i - 1) + dfs(i: i - 2) - return count - } + [class]{}-[func]{dfs} - /* 爬楼梯:搜索 */ - func climbingStairsDFS(n: Int) -> Int { - dfs(i: n) - } + [class]{}-[func]{climbingStairsDFS} ``` === "JS" ```javascript title="climbing_stairs_dfs.js" - /* 搜索 */ - function dfs(i) { - // 已知 dp[1] 和 dp[2] ,返回之 - if (i === 1 || i === 2) return i; - // dp[i] = dp[i-1] + dp[i-2] - const count = dfs(i - 1) + dfs(i - 2); - return count; - } + [class]{}-[func]{dfs} - /* 爬楼梯:搜索 */ - function climbingStairsDFS(n) { - return dfs(n); - } + [class]{}-[func]{climbingStairsDFS} ``` === "TS" ```typescript title="climbing_stairs_dfs.ts" - /* 搜索 */ - function dfs(i: number): number { - // 已知 dp[1] 和 dp[2] ,返回之 - if (i === 1 || i === 2) return i; - // dp[i] = dp[i-1] + dp[i-2] - const count = dfs(i - 1) + dfs(i - 2); - return count; - } + [class]{}-[func]{dfs} - /* 爬楼梯:搜索 */ - function climbingStairsDFS(n: number): number { - return dfs(n); - } + [class]{}-[func]{climbingStairsDFS} ``` === "Dart" ```dart title="climbing_stairs_dfs.dart" - /* 搜索 */ - int dfs(int i) { - // 已知 dp[1] 和 dp[2] ,返回之 - if (i == 1 || i == 2) return i; - // dp[i] = dp[i-1] + dp[i-2] - int count = dfs(i - 1) + dfs(i - 2); - return count; - } + [class]{}-[func]{dfs} - /* 爬楼梯:搜索 */ - int climbingStairsDFS(int n) { - return dfs(n); - } + [class]{}-[func]{climbingStairsDFS} ``` === "Rust" ```rust title="climbing_stairs_dfs.rs" - /* 搜索 */ - fn dfs(i: usize) -> i32 { - // 已知 dp[1] 和 dp[2] ,返回之 - if i == 1 || i == 2 { - return i as i32; - } - // dp[i] = dp[i-1] + dp[i-2] - let count = dfs(i - 1) + dfs(i - 2); - count - } + [class]{}-[func]{dfs} - /* 爬楼梯:搜索 */ - fn climbing_stairs_dfs(n: usize) -> i32 { - dfs(n) - } + [class]{}-[func]{climbing_stairs_dfs} ``` === "C" ```c title="climbing_stairs_dfs.c" - /* 搜索 */ - int dfs(int i) { - // 已知 dp[1] 和 dp[2] ,返回之 - if (i == 1 || i == 2) - return i; - // dp[i] = dp[i-1] + dp[i-2] - int count = dfs(i - 1) + dfs(i - 2); - return count; - } + [class]{}-[func]{dfs} - /* 爬楼梯:搜索 */ - int climbingStairsDFS(int n) { - return dfs(n); - } + [class]{}-[func]{climbingStairsDFS} ``` === "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 - } + [class]{}-[func]{dfs} - /* 爬楼梯:搜索 */ - fun climbingStairsDFS(n: Int): Int { - return dfs(n) - } + [class]{}-[func]{climbingStairsDFS} ``` === "Ruby" @@ -702,28 +327,11 @@ Observe the following code, which, like standard backtracking code, belongs to d === "Zig" ```zig title="climbing_stairs_dfs.zig" - // 搜索 - fn dfs(i: usize) i32 { - // 已知 dp[1] 和 dp[2] ,返回之 - if (i == 1 or i == 2) { - return @intCast(i); - } - // dp[i] = dp[i-1] + dp[i-2] - var count = dfs(i - 1) + dfs(i - 2); - return count; - } + [class]{}-[func]{dfs} - // 爬楼梯:搜索 - fn climbingStairsDFS(comptime n: usize) i32 { - return dfs(n); - } + [class]{}-[func]{climbingStairsDFS} ``` -??? pythontutor "Code Visualization" - -
- - Figure 14-3 shows the recursive tree formed by brute force search. For the problem $dp[n]$, the depth of its recursive tree is $n$, with a time complexity of $O(2^n)$. Exponential order represents explosive growth, and entering a long wait if a relatively large $n$ is input. ![Recursive tree for climbing stairs](intro_to_dynamic_programming.assets/climbing_stairs_dfs_tree.png){ class="animation-figure" } @@ -747,22 +355,22 @@ The code is as follows: ```python title="climbing_stairs_dfs_mem.py" def dfs(i: int, mem: list[int]) -> int: - """记忆化搜索""" - # 已知 dp[1] 和 dp[2] ,返回之 + """Memoized search""" + # Known dp[1] and dp[2], return them if i == 1 or i == 2: return i - # 若存在记录 dp[i] ,则直接返回之 + # If there is a record for dp[i], return it if mem[i] != -1: return mem[i] # dp[i] = dp[i-1] + dp[i-2] count = dfs(i - 1, mem) + dfs(i - 2, mem) - # 记录 dp[i] + # Record dp[i] mem[i] = count return count def climbing_stairs_dfs_mem(n: int) -> int: - """爬楼梯:记忆化搜索""" - # mem[i] 记录爬到第 i 阶的方案总数,-1 代表无记录 + """Climbing stairs: Memoized search""" + # mem[i] records the total number of solutions for climbing to the ith step, -1 means no record mem = [-1] * (n + 1) return dfs(n, mem) ``` @@ -770,50 +378,32 @@ The code is as follows: === "C++" ```cpp title="climbing_stairs_dfs_mem.cpp" - /* 记忆化搜索 */ - int dfs(int i, vector &mem) { - // 已知 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] - int count = dfs(i - 1, mem) + dfs(i - 2, mem); - // 记录 dp[i] - mem[i] = count; - return count; - } + [class]{}-[func]{dfs} - /* 爬楼梯:记忆化搜索 */ - int climbingStairsDFSMem(int n) { - // mem[i] 记录爬到第 i 阶的方案总数,-1 代表无记录 - vector mem(n + 1, -1); - return dfs(n, mem); - } + [class]{}-[func]{climbingStairsDFSMem} ``` === "Java" ```java title="climbing_stairs_dfs_mem.java" - /* 记忆化搜索 */ + /* Memoized search */ int dfs(int i, int[] mem) { - // 已知 dp[1] 和 dp[2] ,返回之 + // Known dp[1] and dp[2], return them if (i == 1 || i == 2) return i; - // 若存在记录 dp[i] ,则直接返回之 + // If there is a record for dp[i], return it if (mem[i] != -1) return mem[i]; // dp[i] = dp[i-1] + dp[i-2] int count = dfs(i - 1, mem) + dfs(i - 2, mem); - // 记录 dp[i] + // Record dp[i] mem[i] = count; return count; } - /* 爬楼梯:记忆化搜索 */ + /* Climbing stairs: Memoized search */ int climbingStairsDFSMem(int n) { - // mem[i] 记录爬到第 i 阶的方案总数,-1 代表无记录 + // mem[i] records the total number of solutions for climbing to the ith step, -1 means no record int[] mem = new int[n + 1]; Arrays.fill(mem, -1); return dfs(n, mem); @@ -823,243 +413,73 @@ The code is as follows: === "C#" ```csharp title="climbing_stairs_dfs_mem.cs" - /* 记忆化搜索 */ - int DFS(int i, int[] mem) { - // 已知 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] - int count = DFS(i - 1, mem) + DFS(i - 2, mem); - // 记录 dp[i] - mem[i] = count; - return count; - } + [class]{climbing_stairs_dfs_mem}-[func]{DFS} - /* 爬楼梯:记忆化搜索 */ - int ClimbingStairsDFSMem(int n) { - // mem[i] 记录爬到第 i 阶的方案总数,-1 代表无记录 - int[] mem = new int[n + 1]; - Array.Fill(mem, -1); - return DFS(n, mem); - } + [class]{climbing_stairs_dfs_mem}-[func]{ClimbingStairsDFSMem} ``` === "Go" ```go title="climbing_stairs_dfs_mem.go" - /* 记忆化搜索 */ - func dfsMem(i int, mem []int) 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] - count := dfsMem(i-1, mem) + dfsMem(i-2, mem) - // 记录 dp[i] - mem[i] = count - return count - } + [class]{}-[func]{dfsMem} - /* 爬楼梯:记忆化搜索 */ - func climbingStairsDFSMem(n int) int { - // mem[i] 记录爬到第 i 阶的方案总数,-1 代表无记录 - mem := make([]int, n+1) - for i := range mem { - mem[i] = -1 - } - return dfsMem(n, mem) - } + [class]{}-[func]{climbingStairsDFSMem} ``` === "Swift" ```swift title="climbing_stairs_dfs_mem.swift" - /* 记忆化搜索 */ - func dfs(i: Int, mem: inout [Int]) -> 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] - let count = dfs(i: i - 1, mem: &mem) + dfs(i: i - 2, mem: &mem) - // 记录 dp[i] - mem[i] = count - return count - } + [class]{}-[func]{dfs} - /* 爬楼梯:记忆化搜索 */ - func climbingStairsDFSMem(n: Int) -> Int { - // mem[i] 记录爬到第 i 阶的方案总数,-1 代表无记录 - var mem = Array(repeating: -1, count: n + 1) - return dfs(i: n, mem: &mem) - } + [class]{}-[func]{climbingStairsDFSMem} ``` === "JS" ```javascript title="climbing_stairs_dfs_mem.js" - /* 记忆化搜索 */ - function dfs(i, mem) { - // 已知 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] - const count = dfs(i - 1, mem) + dfs(i - 2, mem); - // 记录 dp[i] - mem[i] = count; - return count; - } + [class]{}-[func]{dfs} - /* 爬楼梯:记忆化搜索 */ - function climbingStairsDFSMem(n) { - // mem[i] 记录爬到第 i 阶的方案总数,-1 代表无记录 - const mem = new Array(n + 1).fill(-1); - return dfs(n, mem); - } + [class]{}-[func]{climbingStairsDFSMem} ``` === "TS" ```typescript title="climbing_stairs_dfs_mem.ts" - /* 记忆化搜索 */ - function dfs(i: number, mem: number[]): number { - // 已知 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] - const count = dfs(i - 1, mem) + dfs(i - 2, mem); - // 记录 dp[i] - mem[i] = count; - return count; - } + [class]{}-[func]{dfs} - /* 爬楼梯:记忆化搜索 */ - function climbingStairsDFSMem(n: number): number { - // mem[i] 记录爬到第 i 阶的方案总数,-1 代表无记录 - const mem = new Array(n + 1).fill(-1); - return dfs(n, mem); - } + [class]{}-[func]{climbingStairsDFSMem} ``` === "Dart" ```dart title="climbing_stairs_dfs_mem.dart" - /* 记忆化搜索 */ - int dfs(int i, List mem) { - // 已知 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] - int count = dfs(i - 1, mem) + dfs(i - 2, mem); - // 记录 dp[i] - mem[i] = count; - return count; - } + [class]{}-[func]{dfs} - /* 爬楼梯:记忆化搜索 */ - int climbingStairsDFSMem(int n) { - // mem[i] 记录爬到第 i 阶的方案总数,-1 代表无记录 - List mem = List.filled(n + 1, -1); - return dfs(n, mem); - } + [class]{}-[func]{climbingStairsDFSMem} ``` === "Rust" ```rust title="climbing_stairs_dfs_mem.rs" - /* 记忆化搜索 */ - fn dfs(i: usize, mem: &mut [i32]) -> i32 { - // 已知 dp[1] 和 dp[2] ,返回之 - if i == 1 || i == 2 { - return i as i32; - } - // 若存在记录 dp[i] ,则直接返回之 - if mem[i] != -1 { - return mem[i]; - } - // dp[i] = dp[i-1] + dp[i-2] - let count = dfs(i - 1, mem) + dfs(i - 2, mem); - // 记录 dp[i] - mem[i] = count; - count - } + [class]{}-[func]{dfs} - /* 爬楼梯:记忆化搜索 */ - fn climbing_stairs_dfs_mem(n: usize) -> i32 { - // mem[i] 记录爬到第 i 阶的方案总数,-1 代表无记录 - let mut mem = vec![-1; n + 1]; - dfs(n, &mut mem) - } + [class]{}-[func]{climbing_stairs_dfs_mem} ``` === "C" ```c title="climbing_stairs_dfs_mem.c" - /* 记忆化搜索 */ - int dfs(int i, int *mem) { - // 已知 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] - int count = dfs(i - 1, mem) + dfs(i - 2, mem); - // 记录 dp[i] - mem[i] = count; - return count; - } + [class]{}-[func]{dfs} - /* 爬楼梯:记忆化搜索 */ - int climbingStairsDFSMem(int n) { - // mem[i] 记录爬到第 i 阶的方案总数,-1 代表无记录 - int *mem = (int *)malloc((n + 1) * sizeof(int)); - for (int i = 0; i <= n; i++) { - mem[i] = -1; - } - int result = dfs(n, mem); - free(mem); - return result; - } + [class]{}-[func]{climbingStairsDFSMem} ``` === "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 - } + [class]{}-[func]{dfs} - /* 爬楼梯:记忆化搜索 */ - fun climbingStairsDFSMem(n: Int): Int { - // mem[i] 记录爬到第 i 阶的方案总数,-1 代表无记录 - val mem = IntArray(n + 1) - mem.fill(-1) - return dfs(n, mem) - } + [class]{}-[func]{climbingStairsDFSMem} ``` === "Ruby" @@ -1073,36 +493,11 @@ The code is as follows: === "Zig" ```zig title="climbing_stairs_dfs_mem.zig" - // 记忆化搜索 - fn dfs(i: usize, mem: []i32) i32 { - // 已知 dp[1] 和 dp[2] ,返回之 - if (i == 1 or i == 2) { - return @intCast(i); - } - // 若存在记录 dp[i] ,则直接返回之 - if (mem[i] != -1) { - return mem[i]; - } - // dp[i] = dp[i-1] + dp[i-2] - var count = dfs(i - 1, mem) + dfs(i - 2, mem); - // 记录 dp[i] - mem[i] = count; - return count; - } + [class]{}-[func]{dfs} - // 爬楼梯:记忆化搜索 - fn climbingStairsDFSMem(comptime n: usize) i32 { - // mem[i] 记录爬到第 i 阶的方案总数,-1 代表无记录 - var mem = [_]i32{ -1 } ** (n + 1); - return dfs(n, &mem); - } + [class]{}-[func]{climbingStairsDFSMem} ``` -??? pythontutor "Code Visualization" - -
- - Observe Figure 14-4, **after memoization, all overlapping subproblems need to be calculated only once, optimizing the time complexity to $O(n)$**, which is a significant leap. ![Recursive tree with memoized search](intro_to_dynamic_programming.assets/climbing_stairs_dfs_memo_tree.png){ class="animation-figure" } @@ -1121,14 +516,14 @@ Since dynamic programming does not include a backtracking process, it only requi ```python title="climbing_stairs_dp.py" def climbing_stairs_dp(n: int) -> int: - """爬楼梯:动态规划""" + """Climbing stairs: Dynamic programming""" if n == 1 or n == 2: return n - # 初始化 dp 表,用于存储子问题的解 + # Initialize dp table, used to store subproblem solutions dp = [0] * (n + 1) - # 初始状态:预设最小子问题的解 + # Initial state: preset the smallest subproblem solution dp[1], dp[2] = 1, 2 - # 状态转移:从较小子问题逐步求解较大子问题 + # State transition: gradually solve larger subproblems from smaller ones for i in range(3, n + 1): dp[i] = dp[i - 1] + dp[i - 2] return dp[n] @@ -1137,36 +532,22 @@ Since dynamic programming does not include a backtracking process, it only requi === "C++" ```cpp title="climbing_stairs_dp.cpp" - /* 爬楼梯:动态规划 */ - int climbingStairsDP(int n) { - if (n == 1 || n == 2) - return n; - // 初始化 dp 表,用于存储子问题的解 - vector dp(n + 1); - // 初始状态:预设最小子问题的解 - dp[1] = 1; - dp[2] = 2; - // 状态转移:从较小子问题逐步求解较大子问题 - for (int i = 3; i <= n; i++) { - dp[i] = dp[i - 1] + dp[i - 2]; - } - return dp[n]; - } + [class]{}-[func]{climbingStairsDP} ``` === "Java" ```java title="climbing_stairs_dp.java" - /* 爬楼梯:动态规划 */ + /* Climbing stairs: Dynamic programming */ int climbingStairsDP(int n) { if (n == 1 || n == 2) return n; - // 初始化 dp 表,用于存储子问题的解 + // Initialize dp table, used to store subproblem solutions int[] dp = new int[n + 1]; - // 初始状态:预设最小子问题的解 + // Initial state: preset the smallest subproblem solution dp[1] = 1; dp[2] = 2; - // 状态转移:从较小子问题逐步求解较大子问题 + // State transition: gradually solve larger subproblems from smaller ones for (int i = 3; i <= n; i++) { dp[i] = dp[i - 1] + dp[i - 2]; } @@ -1177,183 +558,55 @@ Since dynamic programming does not include a backtracking process, it only requi === "C#" ```csharp title="climbing_stairs_dp.cs" - /* 爬楼梯:动态规划 */ - int ClimbingStairsDP(int n) { - if (n == 1 || n == 2) - return n; - // 初始化 dp 表,用于存储子问题的解 - int[] dp = new int[n + 1]; - // 初始状态:预设最小子问题的解 - dp[1] = 1; - dp[2] = 2; - // 状态转移:从较小子问题逐步求解较大子问题 - for (int i = 3; i <= n; i++) { - dp[i] = dp[i - 1] + dp[i - 2]; - } - return dp[n]; - } + [class]{climbing_stairs_dp}-[func]{ClimbingStairsDP} ``` === "Go" ```go title="climbing_stairs_dp.go" - /* 爬楼梯:动态规划 */ - func climbingStairsDP(n int) int { - if n == 1 || n == 2 { - return n - } - // 初始化 dp 表,用于存储子问题的解 - dp := make([]int, n+1) - // 初始状态:预设最小子问题的解 - dp[1] = 1 - dp[2] = 2 - // 状态转移:从较小子问题逐步求解较大子问题 - for i := 3; i <= n; i++ { - dp[i] = dp[i-1] + dp[i-2] - } - return dp[n] - } + [class]{}-[func]{climbingStairsDP} ``` === "Swift" ```swift title="climbing_stairs_dp.swift" - /* 爬楼梯:动态规划 */ - func climbingStairsDP(n: Int) -> Int { - if n == 1 || n == 2 { - return n - } - // 初始化 dp 表,用于存储子问题的解 - var dp = Array(repeating: 0, count: n + 1) - // 初始状态:预设最小子问题的解 - dp[1] = 1 - dp[2] = 2 - // 状态转移:从较小子问题逐步求解较大子问题 - for i in 3 ... n { - dp[i] = dp[i - 1] + dp[i - 2] - } - return dp[n] - } + [class]{}-[func]{climbingStairsDP} ``` === "JS" ```javascript title="climbing_stairs_dp.js" - /* 爬楼梯:动态规划 */ - function climbingStairsDP(n) { - if (n === 1 || n === 2) return n; - // 初始化 dp 表,用于存储子问题的解 - const dp = new Array(n + 1).fill(-1); - // 初始状态:预设最小子问题的解 - dp[1] = 1; - dp[2] = 2; - // 状态转移:从较小子问题逐步求解较大子问题 - for (let i = 3; i <= n; i++) { - dp[i] = dp[i - 1] + dp[i - 2]; - } - return dp[n]; - } + [class]{}-[func]{climbingStairsDP} ``` === "TS" ```typescript title="climbing_stairs_dp.ts" - /* 爬楼梯:动态规划 */ - function climbingStairsDP(n: number): number { - if (n === 1 || n === 2) return n; - // 初始化 dp 表,用于存储子问题的解 - const dp = new Array(n + 1).fill(-1); - // 初始状态:预设最小子问题的解 - dp[1] = 1; - dp[2] = 2; - // 状态转移:从较小子问题逐步求解较大子问题 - for (let i = 3; i <= n; i++) { - dp[i] = dp[i - 1] + dp[i - 2]; - } - return dp[n]; - } + [class]{}-[func]{climbingStairsDP} ``` === "Dart" ```dart title="climbing_stairs_dp.dart" - /* 爬楼梯:动态规划 */ - int climbingStairsDP(int n) { - if (n == 1 || n == 2) return n; - // 初始化 dp 表,用于存储子问题的解 - List dp = List.filled(n + 1, 0); - // 初始状态:预设最小子问题的解 - dp[1] = 1; - dp[2] = 2; - // 状态转移:从较小子问题逐步求解较大子问题 - for (int i = 3; i <= n; i++) { - dp[i] = dp[i - 1] + dp[i - 2]; - } - return dp[n]; - } + [class]{}-[func]{climbingStairsDP} ``` === "Rust" ```rust title="climbing_stairs_dp.rs" - /* 爬楼梯:动态规划 */ - fn climbing_stairs_dp(n: usize) -> i32 { - // 已知 dp[1] 和 dp[2] ,返回之 - if n == 1 || n == 2 { - return n as i32; - } - // 初始化 dp 表,用于存储子问题的解 - let mut dp = vec![-1; n + 1]; - // 初始状态:预设最小子问题的解 - dp[1] = 1; - dp[2] = 2; - // 状态转移:从较小子问题逐步求解较大子问题 - for i in 3..=n { - dp[i] = dp[i - 1] + dp[i - 2]; - } - dp[n] - } + [class]{}-[func]{climbing_stairs_dp} ``` === "C" ```c title="climbing_stairs_dp.c" - /* 爬楼梯:动态规划 */ - int climbingStairsDP(int n) { - if (n == 1 || n == 2) - return n; - // 初始化 dp 表,用于存储子问题的解 - int *dp = (int *)malloc((n + 1) * sizeof(int)); - // 初始状态:预设最小子问题的解 - dp[1] = 1; - dp[2] = 2; - // 状态转移:从较小子问题逐步求解较大子问题 - for (int i = 3; i <= n; i++) { - dp[i] = dp[i - 1] + dp[i - 2]; - } - int result = dp[n]; - free(dp); - return result; - } + [class]{}-[func]{climbingStairsDP} ``` === "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] - } + [class]{}-[func]{climbingStairsDP} ``` === "Ruby" @@ -1365,30 +618,9 @@ Since dynamic programming does not include a backtracking process, it only requi === "Zig" ```zig title="climbing_stairs_dp.zig" - // 爬楼梯:动态规划 - fn climbingStairsDP(comptime n: usize) i32 { - // 已知 dp[1] 和 dp[2] ,返回之 - if (n == 1 or n == 2) { - return @intCast(n); - } - // 初始化 dp 表,用于存储子问题的解 - var dp = [_]i32{-1} ** (n + 1); - // 初始状态:预设最小子问题的解 - dp[1] = 1; - dp[2] = 2; - // 状态转移:从较小子问题逐步求解较大子问题 - for (3..n + 1) |i| { - dp[i] = dp[i - 1] + dp[i - 2]; - } - return dp[n]; - } + [class]{}-[func]{climbingStairsDP} ``` -??? pythontutor "Code Visualization" - -
- - Figure 14-5 simulates the execution process of the above code. ![Dynamic programming process for climbing stairs](intro_to_dynamic_programming.assets/climbing_stairs_dp.png){ class="animation-figure" } @@ -1411,7 +643,7 @@ Observant readers may have noticed that **since $dp[i]$ is only related to $dp[i ```python title="climbing_stairs_dp.py" def climbing_stairs_dp_comp(n: int) -> int: - """爬楼梯:空间优化后的动态规划""" + """Climbing stairs: Space-optimized dynamic programming""" if n == 1 or n == 2: return n a, b = 1, 2 @@ -1423,24 +655,13 @@ Observant readers may have noticed that **since $dp[i]$ is only related to $dp[i === "C++" ```cpp title="climbing_stairs_dp.cpp" - /* 爬楼梯:空间优化后的动态规划 */ - int climbingStairsDPComp(int n) { - if (n == 1 || n == 2) - return n; - int a = 1, b = 2; - for (int i = 3; i <= n; i++) { - int tmp = b; - b = a + b; - a = tmp; - } - return b; - } + [class]{}-[func]{climbingStairsDPComp} ``` === "Java" ```java title="climbing_stairs_dp.java" - /* 爬楼梯:空间优化后的动态规划 */ + /* Climbing stairs: Space-optimized dynamic programming */ int climbingStairsDPComp(int n) { if (n == 1 || n == 2) return n; @@ -1457,154 +678,55 @@ Observant readers may have noticed that **since $dp[i]$ is only related to $dp[i === "C#" ```csharp title="climbing_stairs_dp.cs" - /* 爬楼梯:空间优化后的动态规划 */ - int ClimbingStairsDPComp(int n) { - if (n == 1 || n == 2) - return n; - int a = 1, b = 2; - for (int i = 3; i <= n; i++) { - int tmp = b; - b = a + b; - a = tmp; - } - return b; - } + [class]{climbing_stairs_dp}-[func]{ClimbingStairsDPComp} ``` === "Go" ```go title="climbing_stairs_dp.go" - /* 爬楼梯:空间优化后的动态规划 */ - func climbingStairsDPComp(n int) int { - if n == 1 || n == 2 { - return n - } - a, b := 1, 2 - // 状态转移:从较小子问题逐步求解较大子问题 - for i := 3; i <= n; i++ { - a, b = b, a+b - } - return b - } + [class]{}-[func]{climbingStairsDPComp} ``` === "Swift" ```swift title="climbing_stairs_dp.swift" - /* 爬楼梯:空间优化后的动态规划 */ - func climbingStairsDPComp(n: Int) -> Int { - if n == 1 || n == 2 { - return n - } - var a = 1 - var b = 2 - for _ in 3 ... n { - (a, b) = (b, a + b) - } - return b - } + [class]{}-[func]{climbingStairsDPComp} ``` === "JS" ```javascript title="climbing_stairs_dp.js" - /* 爬楼梯:空间优化后的动态规划 */ - function climbingStairsDPComp(n) { - if (n === 1 || n === 2) return n; - let a = 1, - b = 2; - for (let i = 3; i <= n; i++) { - const tmp = b; - b = a + b; - a = tmp; - } - return b; - } + [class]{}-[func]{climbingStairsDPComp} ``` === "TS" ```typescript title="climbing_stairs_dp.ts" - /* 爬楼梯:空间优化后的动态规划 */ - function climbingStairsDPComp(n: number): number { - if (n === 1 || n === 2) return n; - let a = 1, - b = 2; - for (let i = 3; i <= n; i++) { - const tmp = b; - b = a + b; - a = tmp; - } - return b; - } + [class]{}-[func]{climbingStairsDPComp} ``` === "Dart" ```dart title="climbing_stairs_dp.dart" - /* 爬楼梯:空间优化后的动态规划 */ - int climbingStairsDPComp(int n) { - if (n == 1 || n == 2) return n; - int a = 1, b = 2; - for (int i = 3; i <= n; i++) { - int tmp = b; - b = a + b; - a = tmp; - } - return b; - } + [class]{}-[func]{climbingStairsDPComp} ``` === "Rust" ```rust title="climbing_stairs_dp.rs" - /* 爬楼梯:空间优化后的动态规划 */ - fn climbing_stairs_dp_comp(n: usize) -> i32 { - if n == 1 || n == 2 { - return n as i32; - } - let (mut a, mut b) = (1, 2); - for _ in 3..=n { - let tmp = b; - b = a + b; - a = tmp; - } - b - } + [class]{}-[func]{climbing_stairs_dp_comp} ``` === "C" ```c title="climbing_stairs_dp.c" - /* 爬楼梯:空间优化后的动态规划 */ - int climbingStairsDPComp(int n) { - if (n == 1 || n == 2) - return n; - int a = 1, b = 2; - for (int i = 3; i <= n; i++) { - int tmp = b; - b = a + b; - a = tmp; - } - return b; - } + [class]{}-[func]{climbingStairsDPComp} ``` === "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 temp = b - b += a - a = temp - } - return b - } + [class]{}-[func]{climbingStairsDPComp} ``` === "Ruby" @@ -1616,27 +738,9 @@ Observant readers may have noticed that **since $dp[i]$ is only related to $dp[i === "Zig" ```zig title="climbing_stairs_dp.zig" - // 爬楼梯:空间优化后的动态规划 - fn climbingStairsDPComp(comptime n: usize) i32 { - if (n == 1 or n == 2) { - return @intCast(n); - } - var a: i32 = 1; - var b: i32 = 2; - for (3..n + 1) |_| { - var tmp = b; - b = a + b; - a = tmp; - } - return b; - } + [class]{}-[func]{climbingStairsDPComp} ``` -??? pythontutor "Code Visualization" - -
- - Observing the above code, since the space occupied by the array `dp` is eliminated, the space complexity is reduced from $O(n)$ to $O(1)$. In dynamic programming problems, the current state is often only related to a limited number of previous states, allowing us to retain only the necessary states and save memory space by "dimension reduction". **This space optimization technique is known as 'rolling variable' or 'rolling array'**. diff --git a/en/docs/chapter_dynamic_programming/knapsack_problem.md b/en/docs/chapter_dynamic_programming/knapsack_problem.md index 73e4a0e3d..8d80be9b2 100644 --- a/en/docs/chapter_dynamic_programming/knapsack_problem.md +++ b/en/docs/chapter_dynamic_programming/knapsack_problem.md @@ -66,58 +66,43 @@ The search code includes the following elements. ```python title="knapsack.py" def knapsack_dfs(wgt: list[int], val: list[int], i: int, c: int) -> int: - """0-1 背包:暴力搜索""" - # 若已选完所有物品或背包无剩余容量,则返回价值 0 + """0-1 Knapsack: Brute force search""" + # If all items have been chosen or the knapsack has no remaining capacity, return value 0 if i == 0 or c == 0: return 0 - # 若超过背包容量,则只能选择不放入背包 + # If exceeding the knapsack capacity, can only choose not to put it in the knapsack if wgt[i - 1] > c: return knapsack_dfs(wgt, val, i - 1, c) - # 计算不放入和放入物品 i 的最大价值 + # Calculate the maximum value of not putting in and putting in item i no = knapsack_dfs(wgt, val, i - 1, c) yes = knapsack_dfs(wgt, val, i - 1, c - wgt[i - 1]) + val[i - 1] - # 返回两种方案中价值更大的那一个 + # Return the greater value of the two options return max(no, yes) ``` === "C++" ```cpp title="knapsack.cpp" - /* 0-1 背包:暴力搜索 */ - int knapsackDFS(vector &wgt, vector &val, int i, int c) { - // 若已选完所有物品或背包无剩余容量,则返回价值 0 - if (i == 0 || c == 0) { - return 0; - } - // 若超过背包容量,则只能选择不放入背包 - if (wgt[i - 1] > c) { - return knapsackDFS(wgt, val, i - 1, c); - } - // 计算不放入和放入物品 i 的最大价值 - int no = knapsackDFS(wgt, val, i - 1, c); - int yes = knapsackDFS(wgt, val, i - 1, c - wgt[i - 1]) + val[i - 1]; - // 返回两种方案中价值更大的那一个 - return max(no, yes); - } + [class]{}-[func]{knapsackDFS} ``` === "Java" ```java title="knapsack.java" - /* 0-1 背包:暴力搜索 */ + /* 0-1 Knapsack: Brute force search */ int knapsackDFS(int[] wgt, int[] val, int i, int c) { - // 若已选完所有物品或背包无剩余容量,则返回价值 0 + // If all items have been chosen or the knapsack has no remaining capacity, return value 0 if (i == 0 || c == 0) { return 0; } - // 若超过背包容量,则只能选择不放入背包 + // If exceeding the knapsack capacity, can only choose not to put it in the knapsack if (wgt[i - 1] > c) { return knapsackDFS(wgt, val, i - 1, c); } - // 计算不放入和放入物品 i 的最大价值 + // Calculate the maximum value of not putting in and putting in item i int no = knapsackDFS(wgt, val, i - 1, c); int yes = knapsackDFS(wgt, val, i - 1, c - wgt[i - 1]) + val[i - 1]; - // 返回两种方案中价值更大的那一个 + // Return the greater value of the two options return Math.max(no, yes); } ``` @@ -125,200 +110,55 @@ The search code includes the following elements. === "C#" ```csharp title="knapsack.cs" - /* 0-1 背包:暴力搜索 */ - int KnapsackDFS(int[] weight, int[] val, int i, int c) { - // 若已选完所有物品或背包无剩余容量,则返回价值 0 - if (i == 0 || c == 0) { - return 0; - } - // 若超过背包容量,则只能选择不放入背包 - if (weight[i - 1] > c) { - return KnapsackDFS(weight, val, i - 1, c); - } - // 计算不放入和放入物品 i 的最大价值 - int no = KnapsackDFS(weight, val, i - 1, c); - int yes = KnapsackDFS(weight, val, i - 1, c - weight[i - 1]) + val[i - 1]; - // 返回两种方案中价值更大的那一个 - return Math.Max(no, yes); - } + [class]{knapsack}-[func]{KnapsackDFS} ``` === "Go" ```go title="knapsack.go" - /* 0-1 背包:暴力搜索 */ - func knapsackDFS(wgt, val []int, i, c int) int { - // 若已选完所有物品或背包无剩余容量,则返回价值 0 - if i == 0 || c == 0 { - return 0 - } - // 若超过背包容量,则只能选择不放入背包 - if wgt[i-1] > c { - return knapsackDFS(wgt, val, i-1, c) - } - // 计算不放入和放入物品 i 的最大价值 - no := knapsackDFS(wgt, val, i-1, c) - yes := knapsackDFS(wgt, val, i-1, c-wgt[i-1]) + val[i-1] - // 返回两种方案中价值更大的那一个 - return int(math.Max(float64(no), float64(yes))) - } + [class]{}-[func]{knapsackDFS} ``` === "Swift" ```swift title="knapsack.swift" - /* 0-1 背包:暴力搜索 */ - func knapsackDFS(wgt: [Int], val: [Int], i: Int, c: Int) -> Int { - // 若已选完所有物品或背包无剩余容量,则返回价值 0 - if i == 0 || c == 0 { - return 0 - } - // 若超过背包容量,则只能选择不放入背包 - if wgt[i - 1] > c { - return knapsackDFS(wgt: wgt, val: val, i: i - 1, c: c) - } - // 计算不放入和放入物品 i 的最大价值 - let no = knapsackDFS(wgt: wgt, val: val, i: i - 1, c: c) - let yes = knapsackDFS(wgt: wgt, val: val, i: i - 1, c: c - wgt[i - 1]) + val[i - 1] - // 返回两种方案中价值更大的那一个 - return max(no, yes) - } + [class]{}-[func]{knapsackDFS} ``` === "JS" ```javascript title="knapsack.js" - /* 0-1 背包:暴力搜索 */ - function knapsackDFS(wgt, val, i, c) { - // 若已选完所有物品或背包无剩余容量,则返回价值 0 - if (i === 0 || c === 0) { - return 0; - } - // 若超过背包容量,则只能选择不放入背包 - if (wgt[i - 1] > c) { - return knapsackDFS(wgt, val, i - 1, c); - } - // 计算不放入和放入物品 i 的最大价值 - const no = knapsackDFS(wgt, val, i - 1, c); - const yes = knapsackDFS(wgt, val, i - 1, c - wgt[i - 1]) + val[i - 1]; - // 返回两种方案中价值更大的那一个 - return Math.max(no, yes); - } + [class]{}-[func]{knapsackDFS} ``` === "TS" ```typescript title="knapsack.ts" - /* 0-1 背包:暴力搜索 */ - function knapsackDFS( - wgt: Array, - val: Array, - i: number, - c: number - ): number { - // 若已选完所有物品或背包无剩余容量,则返回价值 0 - if (i === 0 || c === 0) { - return 0; - } - // 若超过背包容量,则只能选择不放入背包 - if (wgt[i - 1] > c) { - return knapsackDFS(wgt, val, i - 1, c); - } - // 计算不放入和放入物品 i 的最大价值 - const no = knapsackDFS(wgt, val, i - 1, c); - const yes = knapsackDFS(wgt, val, i - 1, c - wgt[i - 1]) + val[i - 1]; - // 返回两种方案中价值更大的那一个 - return Math.max(no, yes); - } + [class]{}-[func]{knapsackDFS} ``` === "Dart" ```dart title="knapsack.dart" - /* 0-1 背包:暴力搜索 */ - int knapsackDFS(List wgt, List val, int i, int c) { - // 若已选完所有物品或背包无剩余容量,则返回价值 0 - if (i == 0 || c == 0) { - return 0; - } - // 若超过背包容量,则只能选择不放入背包 - if (wgt[i - 1] > c) { - return knapsackDFS(wgt, val, i - 1, c); - } - // 计算不放入和放入物品 i 的最大价值 - int no = knapsackDFS(wgt, val, i - 1, c); - int yes = knapsackDFS(wgt, val, i - 1, c - wgt[i - 1]) + val[i - 1]; - // 返回两种方案中价值更大的那一个 - return max(no, yes); - } + [class]{}-[func]{knapsackDFS} ``` === "Rust" ```rust title="knapsack.rs" - /* 0-1 背包:暴力搜索 */ - fn knapsack_dfs(wgt: &[i32], val: &[i32], i: usize, c: usize) -> i32 { - // 若已选完所有物品或背包无剩余容量,则返回价值 0 - if i == 0 || c == 0 { - return 0; - } - // 若超过背包容量,则只能选择不放入背包 - if wgt[i - 1] > c as i32 { - return knapsack_dfs(wgt, val, i - 1, c); - } - // 计算不放入和放入物品 i 的最大价值 - let no = knapsack_dfs(wgt, val, i - 1, c); - let yes = knapsack_dfs(wgt, val, i - 1, c - wgt[i - 1] as usize) + val[i - 1]; - // 返回两种方案中价值更大的那一个 - std::cmp::max(no, yes) - } + [class]{}-[func]{knapsack_dfs} ``` === "C" ```c title="knapsack.c" - /* 0-1 背包:暴力搜索 */ - int knapsackDFS(int wgt[], int val[], int i, int c) { - // 若已选完所有物品或背包无剩余容量,则返回价值 0 - if (i == 0 || c == 0) { - return 0; - } - // 若超过背包容量,则只能选择不放入背包 - if (wgt[i - 1] > c) { - return knapsackDFS(wgt, val, i - 1, c); - } - // 计算不放入和放入物品 i 的最大价值 - int no = knapsackDFS(wgt, val, i - 1, c); - int yes = knapsackDFS(wgt, val, i - 1, c - wgt[i - 1]) + val[i - 1]; - // 返回两种方案中价值更大的那一个 - return myMax(no, yes); - } + [class]{}-[func]{knapsackDFS} ``` === "Kotlin" ```kotlin title="knapsack.kt" - /* 0-1 背包:暴力搜索 */ - fun knapsackDFS( - wgt: IntArray, - _val: IntArray, - i: Int, - c: Int - ): Int { - // 若已选完所有物品或背包无剩余容量,则返回价值 0 - if (i == 0 || c == 0) { - return 0 - } - // 若超过背包容量,则只能选择不放入背包 - if (wgt[i - 1] > c) { - return knapsackDFS(wgt, _val, i - 1, c) - } - // 计算不放入和放入物品 i 的最大价值 - val no = knapsackDFS(wgt, _val, i - 1, c) - val yes = knapsackDFS(wgt, _val, i - 1, c - wgt[i - 1]) + _val[i - 1] - // 返回两种方案中价值更大的那一个 - return max(no, yes) - } + [class]{}-[func]{knapsackDFS} ``` === "Ruby" @@ -330,29 +170,9 @@ The search code includes the following elements. === "Zig" ```zig title="knapsack.zig" - // 0-1 背包:暴力搜索 - fn knapsackDFS(wgt: []i32, val: []i32, i: usize, c: usize) i32 { - // 若已选完所有物品或背包无剩余容量,则返回价值 0 - if (i == 0 or c == 0) { - return 0; - } - // 若超过背包容量,则只能选择不放入背包 - if (wgt[i - 1] > c) { - return knapsackDFS(wgt, val, i - 1, c); - } - // 计算不放入和放入物品 i 的最大价值 - var no = knapsackDFS(wgt, val, i - 1, c); - var yes = knapsackDFS(wgt, val, i - 1, c - @as(usize, @intCast(wgt[i - 1]))) + val[i - 1]; - // 返回两种方案中价值更大的那一个 - return @max(no, yes); - } + [class]{}-[func]{knapsackDFS} ``` -??? pythontutor "Code Visualization" - -
- - As shown in Figure 14-18, since each item generates two search branches of not selecting and selecting, the time complexity is $O(2^n)$. Observing the recursive tree, it is easy to see that there are overlapping sub-problems, such as $dp[1, 10]$, etc. When there are many items and the knapsack capacity is large, especially when there are many items of the same weight, the number of overlapping sub-problems will increase significantly. @@ -373,20 +193,20 @@ After introducing memoization, **the time complexity depends on the number of su def knapsack_dfs_mem( wgt: list[int], val: list[int], mem: list[list[int]], i: int, c: int ) -> int: - """0-1 背包:记忆化搜索""" - # 若已选完所有物品或背包无剩余容量,则返回价值 0 + """0-1 Knapsack: Memoized search""" + # If all items have been chosen or the knapsack has no remaining capacity, return value 0 if i == 0 or c == 0: return 0 - # 若已有记录,则直接返回 + # If there is a record, return it if mem[i][c] != -1: return mem[i][c] - # 若超过背包容量,则只能选择不放入背包 + # If exceeding the knapsack capacity, can only choose not to put it in the knapsack if wgt[i - 1] > c: return knapsack_dfs_mem(wgt, val, mem, i - 1, c) - # 计算不放入和放入物品 i 的最大价值 + # Calculate the maximum value of not putting in and putting in item i no = knapsack_dfs_mem(wgt, val, mem, i - 1, c) yes = knapsack_dfs_mem(wgt, val, mem, i - 1, c - wgt[i - 1]) + val[i - 1] - # 记录并返回两种方案中价值更大的那一个 + # Record and return the greater value of the two options mem[i][c] = max(no, yes) return mem[i][c] ``` @@ -394,50 +214,30 @@ After introducing memoization, **the time complexity depends on the number of su === "C++" ```cpp title="knapsack.cpp" - /* 0-1 背包:记忆化搜索 */ - int knapsackDFSMem(vector &wgt, vector &val, vector> &mem, int i, int c) { - // 若已选完所有物品或背包无剩余容量,则返回价值 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, val, mem, i - 1, c); - } - // 计算不放入和放入物品 i 的最大价值 - int no = knapsackDFSMem(wgt, val, mem, i - 1, c); - int yes = knapsackDFSMem(wgt, val, mem, i - 1, c - wgt[i - 1]) + val[i - 1]; - // 记录并返回两种方案中价值更大的那一个 - mem[i][c] = max(no, yes); - return mem[i][c]; - } + [class]{}-[func]{knapsackDFSMem} ``` === "Java" ```java title="knapsack.java" - /* 0-1 背包:记忆化搜索 */ + /* 0-1 Knapsack: Memoized search */ int knapsackDFSMem(int[] wgt, int[] val, int[][] mem, int i, int c) { - // 若已选完所有物品或背包无剩余容量,则返回价值 0 + // If all items have been chosen or the knapsack has no remaining capacity, return value 0 if (i == 0 || c == 0) { return 0; } - // 若已有记录,则直接返回 + // If there is a record, return it if (mem[i][c] != -1) { return mem[i][c]; } - // 若超过背包容量,则只能选择不放入背包 + // If exceeding the knapsack capacity, can only choose not to put it in the knapsack if (wgt[i - 1] > c) { return knapsackDFSMem(wgt, val, mem, i - 1, c); } - // 计算不放入和放入物品 i 的最大价值 + // Calculate the maximum value of not putting in and putting in item i int no = knapsackDFSMem(wgt, val, mem, i - 1, c); int yes = knapsackDFSMem(wgt, val, mem, i - 1, c - wgt[i - 1]) + val[i - 1]; - // 记录并返回两种方案中价值更大的那一个 + // Record and return the greater value of the two options mem[i][c] = Math.max(no, yes); return mem[i][c]; } @@ -446,255 +246,55 @@ After introducing memoization, **the time complexity depends on the number of su === "C#" ```csharp title="knapsack.cs" - /* 0-1 背包:记忆化搜索 */ - int KnapsackDFSMem(int[] weight, int[] val, int[][] mem, int i, int c) { - // 若已选完所有物品或背包无剩余容量,则返回价值 0 - if (i == 0 || c == 0) { - return 0; - } - // 若已有记录,则直接返回 - if (mem[i][c] != -1) { - return mem[i][c]; - } - // 若超过背包容量,则只能选择不放入背包 - if (weight[i - 1] > c) { - return KnapsackDFSMem(weight, val, mem, i - 1, c); - } - // 计算不放入和放入物品 i 的最大价值 - int no = KnapsackDFSMem(weight, val, mem, i - 1, c); - int yes = KnapsackDFSMem(weight, val, mem, i - 1, c - weight[i - 1]) + val[i - 1]; - // 记录并返回两种方案中价值更大的那一个 - mem[i][c] = Math.Max(no, yes); - return mem[i][c]; - } + [class]{knapsack}-[func]{KnapsackDFSMem} ``` === "Go" ```go title="knapsack.go" - /* 0-1 背包:记忆化搜索 */ - func knapsackDFSMem(wgt, val []int, mem [][]int, i, 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, val, mem, i-1, c) - } - // 计算不放入和放入物品 i 的最大价值 - no := knapsackDFSMem(wgt, val, mem, i-1, c) - yes := knapsackDFSMem(wgt, val, mem, i-1, c-wgt[i-1]) + val[i-1] - // 返回两种方案中价值更大的那一个 - mem[i][c] = int(math.Max(float64(no), float64(yes))) - return mem[i][c] - } + [class]{}-[func]{knapsackDFSMem} ``` === "Swift" ```swift title="knapsack.swift" - /* 0-1 背包:记忆化搜索 */ - func knapsackDFSMem(wgt: [Int], val: [Int], mem: inout [[Int]], 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: wgt, val: val, mem: &mem, i: i - 1, c: c) - } - // 计算不放入和放入物品 i 的最大价值 - let no = knapsackDFSMem(wgt: wgt, val: val, mem: &mem, i: i - 1, c: c) - let yes = knapsackDFSMem(wgt: wgt, val: val, mem: &mem, i: i - 1, c: c - wgt[i - 1]) + val[i - 1] - // 记录并返回两种方案中价值更大的那一个 - mem[i][c] = max(no, yes) - return mem[i][c] - } + [class]{}-[func]{knapsackDFSMem} ``` === "JS" ```javascript title="knapsack.js" - /* 0-1 背包:记忆化搜索 */ - function knapsackDFSMem(wgt, val, mem, i, c) { - // 若已选完所有物品或背包无剩余容量,则返回价值 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, val, mem, i - 1, c); - } - // 计算不放入和放入物品 i 的最大价值 - const no = knapsackDFSMem(wgt, val, mem, i - 1, c); - const yes = - knapsackDFSMem(wgt, val, mem, i - 1, c - wgt[i - 1]) + val[i - 1]; - // 记录并返回两种方案中价值更大的那一个 - mem[i][c] = Math.max(no, yes); - return mem[i][c]; - } + [class]{}-[func]{knapsackDFSMem} ``` === "TS" ```typescript title="knapsack.ts" - /* 0-1 背包:记忆化搜索 */ - function knapsackDFSMem( - wgt: Array, - val: Array, - mem: Array>, - i: number, - c: number - ): number { - // 若已选完所有物品或背包无剩余容量,则返回价值 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, val, mem, i - 1, c); - } - // 计算不放入和放入物品 i 的最大价值 - const no = knapsackDFSMem(wgt, val, mem, i - 1, c); - const yes = - knapsackDFSMem(wgt, val, mem, i - 1, c - wgt[i - 1]) + val[i - 1]; - // 记录并返回两种方案中价值更大的那一个 - mem[i][c] = Math.max(no, yes); - return mem[i][c]; - } + [class]{}-[func]{knapsackDFSMem} ``` === "Dart" ```dart title="knapsack.dart" - /* 0-1 背包:记忆化搜索 */ - int knapsackDFSMem( - List wgt, - List val, - List> mem, - int i, - int c, - ) { - // 若已选完所有物品或背包无剩余容量,则返回价值 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, val, mem, i - 1, c); - } - // 计算不放入和放入物品 i 的最大价值 - int no = knapsackDFSMem(wgt, val, mem, i - 1, c); - int yes = knapsackDFSMem(wgt, val, mem, i - 1, c - wgt[i - 1]) + val[i - 1]; - // 记录并返回两种方案中价值更大的那一个 - mem[i][c] = max(no, yes); - return mem[i][c]; - } + [class]{}-[func]{knapsackDFSMem} ``` === "Rust" ```rust title="knapsack.rs" - /* 0-1 背包:记忆化搜索 */ - fn knapsack_dfs_mem(wgt: &[i32], val: &[i32], mem: &mut Vec>, i: usize, c: usize) -> i32 { - // 若已选完所有物品或背包无剩余容量,则返回价值 0 - if i == 0 || c == 0 { - return 0; - } - // 若已有记录,则直接返回 - if mem[i][c] != -1 { - return mem[i][c]; - } - // 若超过背包容量,则只能选择不放入背包 - if wgt[i - 1] > c as i32 { - return knapsack_dfs_mem(wgt, val, mem, i - 1, c); - } - // 计算不放入和放入物品 i 的最大价值 - let no = knapsack_dfs_mem(wgt, val, mem, i - 1, c); - let yes = knapsack_dfs_mem(wgt, val, mem, i - 1, c - wgt[i - 1] as usize) + val[i - 1]; - // 记录并返回两种方案中价值更大的那一个 - mem[i][c] = std::cmp::max(no, yes); - mem[i][c] - } + [class]{}-[func]{knapsack_dfs_mem} ``` === "C" ```c title="knapsack.c" - /* 0-1 背包:记忆化搜索 */ - int knapsackDFSMem(int wgt[], int val[], int memCols, int **mem, int i, int c) { - // 若已选完所有物品或背包无剩余容量,则返回价值 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, val, memCols, mem, i - 1, c); - } - // 计算不放入和放入物品 i 的最大价值 - int no = knapsackDFSMem(wgt, val, memCols, mem, i - 1, c); - int yes = knapsackDFSMem(wgt, val, memCols, mem, i - 1, c - wgt[i - 1]) + val[i - 1]; - // 记录并返回两种方案中价值更大的那一个 - mem[i][c] = myMax(no, yes); - return mem[i][c]; - } + [class]{}-[func]{knapsackDFSMem} ``` === "Kotlin" ```kotlin title="knapsack.kt" - /* 0-1 背包:记忆化搜索 */ - fun knapsackDFSMem( - wgt: IntArray, - _val: 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, _val, mem, i - 1, c) - } - // 计算不放入和放入物品 i 的最大价值 - val no = knapsackDFSMem(wgt, _val, mem, i - 1, c) - val yes = knapsackDFSMem(wgt, _val, mem, i - 1, c - wgt[i - 1]) + _val[i - 1] - // 记录并返回两种方案中价值更大的那一个 - mem[i][c] = max(no, yes) - return mem[i][c] - } + [class]{}-[func]{knapsackDFSMem} ``` === "Ruby" @@ -706,34 +306,9 @@ After introducing memoization, **the time complexity depends on the number of su === "Zig" ```zig title="knapsack.zig" - // 0-1 背包:记忆化搜索 - fn knapsackDFSMem(wgt: []i32, val: []i32, mem: anytype, i: usize, c: usize) i32 { - // 若已选完所有物品或背包无剩余容量,则返回价值 0 - if (i == 0 or c == 0) { - return 0; - } - // 若已有记录,则直接返回 - if (mem[i][c] != -1) { - return mem[i][c]; - } - // 若超过背包容量,则只能选择不放入背包 - if (wgt[i - 1] > c) { - return knapsackDFSMem(wgt, val, mem, i - 1, c); - } - // 计算不放入和放入物品 i 的最大价值 - var no = knapsackDFSMem(wgt, val, mem, i - 1, c); - var yes = knapsackDFSMem(wgt, val, mem, i - 1, c - @as(usize, @intCast(wgt[i - 1]))) + val[i - 1]; - // 记录并返回两种方案中价值更大的那一个 - mem[i][c] = @max(no, yes); - return mem[i][c]; - } + [class]{}-[func]{knapsackDFSMem} ``` -??? pythontutor "Code Visualization" - -
- - Figure 14-19 shows the search branches that are pruned in memoized search. ![The memoized search recursive tree of the 0-1 knapsack problem](knapsack_problem.assets/knapsack_dfs_mem.png){ class="animation-figure" } @@ -748,18 +323,18 @@ Dynamic programming essentially involves filling the $dp$ table during the state ```python title="knapsack.py" def knapsack_dp(wgt: list[int], val: list[int], cap: int) -> int: - """0-1 背包:动态规划""" + """0-1 Knapsack: Dynamic programming""" n = len(wgt) - # 初始化 dp 表 + # Initialize dp table dp = [[0] * (cap + 1) for _ in range(n + 1)] - # 状态转移 + # State transition for i in range(1, n + 1): for c in range(1, cap + 1): if wgt[i - 1] > c: - # 若超过背包容量,则不选物品 i + # If exceeding the knapsack capacity, do not choose item i dp[i][c] = dp[i - 1][c] else: - # 不选和选物品 i 这两种方案的较大值 + # The greater value between not choosing and choosing item i dp[i][c] = max(dp[i - 1][c], dp[i - 1][c - wgt[i - 1]] + val[i - 1]) return dp[n][cap] ``` @@ -767,43 +342,25 @@ Dynamic programming essentially involves filling the $dp$ table during the state === "C++" ```cpp title="knapsack.cpp" - /* 0-1 背包:动态规划 */ - int knapsackDP(vector &wgt, vector &val, int cap) { - int n = wgt.size(); - // 初始化 dp 表 - vector> dp(n + 1, vector(cap + 1, 0)); - // 状态转移 - for (int i = 1; i <= n; i++) { - for (int c = 1; c <= cap; c++) { - if (wgt[i - 1] > c) { - // 若超过背包容量,则不选物品 i - dp[i][c] = dp[i - 1][c]; - } else { - // 不选和选物品 i 这两种方案的较大值 - dp[i][c] = max(dp[i - 1][c], dp[i - 1][c - wgt[i - 1]] + val[i - 1]); - } - } - } - return dp[n][cap]; - } + [class]{}-[func]{knapsackDP} ``` === "Java" ```java title="knapsack.java" - /* 0-1 背包:动态规划 */ + /* 0-1 Knapsack: Dynamic programming */ int knapsackDP(int[] wgt, int[] val, int cap) { int n = wgt.length; - // 初始化 dp 表 + // Initialize dp table int[][] dp = new int[n + 1][cap + 1]; - // 状态转移 + // State transition for (int i = 1; i <= n; i++) { for (int c = 1; c <= cap; c++) { if (wgt[i - 1] > c) { - // 若超过背包容量,则不选物品 i + // If exceeding the knapsack capacity, do not choose item i dp[i][c] = dp[i - 1][c]; } else { - // 不选和选物品 i 这两种方案的较大值 + // The greater value between not choosing and choosing item i dp[i][c] = Math.max(dp[i - 1][c], dp[i - 1][c - wgt[i - 1]] + val[i - 1]); } } @@ -815,245 +372,55 @@ Dynamic programming essentially involves filling the $dp$ table during the state === "C#" ```csharp title="knapsack.cs" - /* 0-1 背包:动态规划 */ - int KnapsackDP(int[] weight, int[] val, int cap) { - int n = weight.Length; - // 初始化 dp 表 - int[,] dp = new int[n + 1, cap + 1]; - // 状态转移 - for (int i = 1; i <= n; i++) { - for (int c = 1; c <= cap; c++) { - if (weight[i - 1] > c) { - // 若超过背包容量,则不选物品 i - dp[i, c] = dp[i - 1, c]; - } else { - // 不选和选物品 i 这两种方案的较大值 - dp[i, c] = Math.Max(dp[i - 1, c - weight[i - 1]] + val[i - 1], dp[i - 1, c]); - } - } - } - return dp[n, cap]; - } + [class]{knapsack}-[func]{KnapsackDP} ``` === "Go" ```go title="knapsack.go" - /* 0-1 背包:动态规划 */ - func knapsackDP(wgt, val []int, cap int) int { - n := len(wgt) - // 初始化 dp 表 - dp := make([][]int, n+1) - for i := 0; i <= n; i++ { - dp[i] = make([]int, cap+1) - } - // 状态转移 - for i := 1; i <= n; i++ { - for c := 1; c <= cap; c++ { - if wgt[i-1] > c { - // 若超过背包容量,则不选物品 i - dp[i][c] = dp[i-1][c] - } else { - // 不选和选物品 i 这两种方案的较大值 - dp[i][c] = int(math.Max(float64(dp[i-1][c]), float64(dp[i-1][c-wgt[i-1]]+val[i-1]))) - } - } - } - return dp[n][cap] - } + [class]{}-[func]{knapsackDP} ``` === "Swift" ```swift title="knapsack.swift" - /* 0-1 背包:动态规划 */ - func knapsackDP(wgt: [Int], val: [Int], cap: Int) -> Int { - let n = wgt.count - // 初始化 dp 表 - var dp = Array(repeating: Array(repeating: 0, count: cap + 1), count: n + 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], dp[i - 1][c - wgt[i - 1]] + val[i - 1]) - } - } - } - return dp[n][cap] - } + [class]{}-[func]{knapsackDP} ``` === "JS" ```javascript title="knapsack.js" - /* 0-1 背包:动态规划 */ - function knapsackDP(wgt, val, cap) { - const n = wgt.length; - // 初始化 dp 表 - const dp = Array(n + 1) - .fill(0) - .map(() => Array(cap + 1).fill(0)); - // 状态转移 - for (let i = 1; i <= n; i++) { - for (let c = 1; c <= cap; c++) { - if (wgt[i - 1] > c) { - // 若超过背包容量,则不选物品 i - dp[i][c] = dp[i - 1][c]; - } else { - // 不选和选物品 i 这两种方案的较大值 - dp[i][c] = Math.max( - dp[i - 1][c], - dp[i - 1][c - wgt[i - 1]] + val[i - 1] - ); - } - } - } - return dp[n][cap]; - } + [class]{}-[func]{knapsackDP} ``` === "TS" ```typescript title="knapsack.ts" - /* 0-1 背包:动态规划 */ - function knapsackDP( - wgt: Array, - val: Array, - cap: number - ): number { - const n = wgt.length; - // 初始化 dp 表 - const dp = Array.from({ length: n + 1 }, () => - Array.from({ length: cap + 1 }, () => 0) - ); - // 状态转移 - for (let i = 1; i <= n; i++) { - for (let c = 1; c <= cap; c++) { - if (wgt[i - 1] > c) { - // 若超过背包容量,则不选物品 i - dp[i][c] = dp[i - 1][c]; - } else { - // 不选和选物品 i 这两种方案的较大值 - dp[i][c] = Math.max( - dp[i - 1][c], - dp[i - 1][c - wgt[i - 1]] + val[i - 1] - ); - } - } - } - return dp[n][cap]; - } + [class]{}-[func]{knapsackDP} ``` === "Dart" ```dart title="knapsack.dart" - /* 0-1 背包:动态规划 */ - int knapsackDP(List wgt, List val, int cap) { - int n = wgt.length; - // 初始化 dp 表 - List> dp = List.generate(n + 1, (index) => List.filled(cap + 1, 0)); - // 状态转移 - for (int i = 1; i <= n; i++) { - for (int c = 1; c <= cap; c++) { - if (wgt[i - 1] > c) { - // 若超过背包容量,则不选物品 i - dp[i][c] = dp[i - 1][c]; - } else { - // 不选和选物品 i 这两种方案的较大值 - dp[i][c] = max(dp[i - 1][c], dp[i - 1][c - wgt[i - 1]] + val[i - 1]); - } - } - } - return dp[n][cap]; - } + [class]{}-[func]{knapsackDP} ``` === "Rust" ```rust title="knapsack.rs" - /* 0-1 背包:动态规划 */ - fn knapsack_dp(wgt: &[i32], val: &[i32], cap: usize) -> i32 { - let n = wgt.len(); - // 初始化 dp 表 - let mut dp = vec![vec![0; cap + 1]; n + 1]; - // 状态转移 - for i in 1..=n { - for c in 1..=cap { - if wgt[i - 1] > c as i32 { - // 若超过背包容量,则不选物品 i - dp[i][c] = dp[i - 1][c]; - } else { - // 不选和选物品 i 这两种方案的较大值 - dp[i][c] = std::cmp::max( - dp[i - 1][c], - dp[i - 1][c - wgt[i - 1] as usize] + val[i - 1], - ); - } - } - } - dp[n][cap] - } + [class]{}-[func]{knapsack_dp} ``` === "C" ```c title="knapsack.c" - /* 0-1 背包:动态规划 */ - int knapsackDP(int wgt[], int val[], int cap, int wgtSize) { - int n = wgtSize; - // 初始化 dp 表 - int **dp = malloc((n + 1) * sizeof(int *)); - for (int i = 0; i <= n; i++) { - dp[i] = calloc(cap + 1, sizeof(int)); - } - // 状态转移 - for (int i = 1; i <= n; i++) { - for (int c = 1; c <= cap; c++) { - if (wgt[i - 1] > c) { - // 若超过背包容量,则不选物品 i - dp[i][c] = dp[i - 1][c]; - } else { - // 不选和选物品 i 这两种方案的较大值 - dp[i][c] = myMax(dp[i - 1][c], dp[i - 1][c - wgt[i - 1]] + val[i - 1]); - } - } - } - int res = dp[n][cap]; - // 释放内存 - for (int i = 0; i <= n; i++) { - free(dp[i]); - } - return res; - } + [class]{}-[func]{knapsackDP} ``` === "Kotlin" ```kotlin title="knapsack.kt" - /* 0-1 背包:动态规划 */ - fun knapsackDP(wgt: IntArray, _val: 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], dp[i - 1][c - wgt[i - 1]] + _val[i - 1]) - } - } - } - return dp[n][cap] - } + [class]{}-[func]{knapsackDP} ``` === "Ruby" @@ -1065,32 +432,9 @@ Dynamic programming essentially involves filling the $dp$ table during the state === "Zig" ```zig title="knapsack.zig" - // 0-1 背包:动态规划 - fn knapsackDP(comptime wgt: []i32, val: []i32, comptime cap: usize) i32 { - comptime var n = wgt.len; - // 初始化 dp 表 - var dp = [_][cap + 1]i32{[_]i32{0} ** (cap + 1)} ** (n + 1); - // 状态转移 - for (1..n + 1) |i| { - for (1..cap + 1) |c| { - if (wgt[i - 1] > c) { - // 若超过背包容量,则不选物品 i - dp[i][c] = dp[i - 1][c]; - } else { - // 不选和选物品 i 这两种方案的较大值 - dp[i][c] = @max(dp[i - 1][c], dp[i - 1][c - @as(usize, @intCast(wgt[i - 1]))] + val[i - 1]); - } - } - } - return dp[n][cap]; - } + [class]{}-[func]{knapsackDP} ``` -??? pythontutor "Code Visualization" - -
- - As shown in the figures below, both the time complexity and space complexity are determined by the size of the array `dp`, i.e., $O(n \times cap)$. === "<1>" @@ -1174,19 +518,19 @@ In the code implementation, we only need to delete the first dimension $i$ of th ```python title="knapsack.py" def knapsack_dp_comp(wgt: list[int], val: list[int], cap: int) -> int: - """0-1 背包:空间优化后的动态规划""" + """0-1 Knapsack: Space-optimized dynamic programming""" n = len(wgt) - # 初始化 dp 表 + # Initialize dp table dp = [0] * (cap + 1) - # 状态转移 + # State transition for i in range(1, n + 1): - # 倒序遍历 + # Traverse in reverse order for c in range(cap, 0, -1): if wgt[i - 1] > c: - # 若超过背包容量,则不选物品 i + # If exceeding the knapsack capacity, do not choose item i dp[c] = dp[c] else: - # 不选和选物品 i 这两种方案的较大值 + # The greater value between not choosing and choosing item i dp[c] = max(dp[c], dp[c - wgt[i - 1]] + val[i - 1]) return dp[cap] ``` @@ -1194,39 +538,23 @@ In the code implementation, we only need to delete the first dimension $i$ of th === "C++" ```cpp title="knapsack.cpp" - /* 0-1 背包:空间优化后的动态规划 */ - int knapsackDPComp(vector &wgt, vector &val, int cap) { - int n = wgt.size(); - // 初始化 dp 表 - vector dp(cap + 1, 0); - // 状态转移 - for (int i = 1; i <= n; i++) { - // 倒序遍历 - for (int c = cap; c >= 1; c--) { - if (wgt[i - 1] <= c) { - // 不选和选物品 i 这两种方案的较大值 - dp[c] = max(dp[c], dp[c - wgt[i - 1]] + val[i - 1]); - } - } - } - return dp[cap]; - } + [class]{}-[func]{knapsackDPComp} ``` === "Java" ```java title="knapsack.java" - /* 0-1 背包:空间优化后的动态规划 */ + /* 0-1 Knapsack: Space-optimized dynamic programming */ int knapsackDPComp(int[] wgt, int[] val, int cap) { int n = wgt.length; - // 初始化 dp 表 + // Initialize dp table int[] dp = new int[cap + 1]; - // 状态转移 + // State transition for (int i = 1; i <= n; i++) { - // 倒序遍历 + // Traverse in reverse order for (int c = cap; c >= 1; c--) { if (wgt[i - 1] <= c) { - // 不选和选物品 i 这两种方案的较大值 + // The greater value between not choosing and choosing item i dp[c] = Math.max(dp[c], dp[c - wgt[i - 1]] + val[i - 1]); } } @@ -1238,209 +566,55 @@ In the code implementation, we only need to delete the first dimension $i$ of th === "C#" ```csharp title="knapsack.cs" - /* 0-1 背包:空间优化后的动态规划 */ - int KnapsackDPComp(int[] weight, int[] val, int cap) { - int n = weight.Length; - // 初始化 dp 表 - int[] dp = new int[cap + 1]; - // 状态转移 - for (int i = 1; i <= n; i++) { - // 倒序遍历 - for (int c = cap; c > 0; c--) { - if (weight[i - 1] > c) { - // 若超过背包容量,则不选物品 i - dp[c] = dp[c]; - } else { - // 不选和选物品 i 这两种方案的较大值 - dp[c] = Math.Max(dp[c], dp[c - weight[i - 1]] + val[i - 1]); - } - } - } - return dp[cap]; - } + [class]{knapsack}-[func]{KnapsackDPComp} ``` === "Go" ```go title="knapsack.go" - /* 0-1 背包:空间优化后的动态规划 */ - func knapsackDPComp(wgt, val []int, cap int) int { - n := len(wgt) - // 初始化 dp 表 - dp := make([]int, cap+1) - // 状态转移 - for i := 1; i <= n; i++ { - // 倒序遍历 - for c := cap; c >= 1; c-- { - if wgt[i-1] <= c { - // 不选和选物品 i 这两种方案的较大值 - dp[c] = int(math.Max(float64(dp[c]), float64(dp[c-wgt[i-1]]+val[i-1]))) - } - } - } - return dp[cap] - } + [class]{}-[func]{knapsackDPComp} ``` === "Swift" ```swift title="knapsack.swift" - /* 0-1 背包:空间优化后的动态规划 */ - func knapsackDPComp(wgt: [Int], val: [Int], cap: Int) -> Int { - let n = wgt.count - // 初始化 dp 表 - var dp = Array(repeating: 0, count: cap + 1) - // 状态转移 - for i in 1 ... n { - // 倒序遍历 - for c in (1 ... cap).reversed() { - if wgt[i - 1] <= c { - // 不选和选物品 i 这两种方案的较大值 - dp[c] = max(dp[c], dp[c - wgt[i - 1]] + val[i - 1]) - } - } - } - return dp[cap] - } + [class]{}-[func]{knapsackDPComp} ``` === "JS" ```javascript title="knapsack.js" - /* 0-1 背包:状态压缩后的动态规划 */ - function knapsackDPComp(wgt, val, cap) { - const n = wgt.length; - // 初始化 dp 表 - const dp = Array(cap + 1).fill(0); - // 状态转移 - for (let i = 1; i <= n; i++) { - // 倒序遍历 - for (let c = cap; c >= 1; c--) { - if (wgt[i - 1] <= c) { - // 不选和选物品 i 这两种方案的较大值 - dp[c] = Math.max(dp[c], dp[c - wgt[i - 1]] + val[i - 1]); - } - } - } - return dp[cap]; - } + [class]{}-[func]{knapsackDPComp} ``` === "TS" ```typescript title="knapsack.ts" - /* 0-1 背包:状态压缩后的动态规划 */ - function knapsackDPComp( - wgt: Array, - val: Array, - cap: number - ): number { - const n = wgt.length; - // 初始化 dp 表 - const dp = Array(cap + 1).fill(0); - // 状态转移 - for (let i = 1; i <= n; i++) { - // 倒序遍历 - for (let c = cap; c >= 1; c--) { - if (wgt[i - 1] <= c) { - // 不选和选物品 i 这两种方案的较大值 - dp[c] = Math.max(dp[c], dp[c - wgt[i - 1]] + val[i - 1]); - } - } - } - return dp[cap]; - } + [class]{}-[func]{knapsackDPComp} ``` === "Dart" ```dart title="knapsack.dart" - /* 0-1 背包:空间优化后的动态规划 */ - int knapsackDPComp(List wgt, List val, int cap) { - int n = wgt.length; - // 初始化 dp 表 - List dp = List.filled(cap + 1, 0); - // 状态转移 - for (int i = 1; i <= n; i++) { - // 倒序遍历 - for (int c = cap; c >= 1; c--) { - if (wgt[i - 1] <= c) { - // 不选和选物品 i 这两种方案的较大值 - dp[c] = max(dp[c], dp[c - wgt[i - 1]] + val[i - 1]); - } - } - } - return dp[cap]; - } + [class]{}-[func]{knapsackDPComp} ``` === "Rust" ```rust title="knapsack.rs" - /* 0-1 背包:空间优化后的动态规划 */ - fn knapsack_dp_comp(wgt: &[i32], val: &[i32], cap: usize) -> i32 { - let n = wgt.len(); - // 初始化 dp 表 - let mut dp = vec![0; cap + 1]; - // 状态转移 - for i in 1..=n { - // 倒序遍历 - for c in (1..=cap).rev() { - if wgt[i - 1] <= c as i32 { - // 不选和选物品 i 这两种方案的较大值 - dp[c] = std::cmp::max(dp[c], dp[c - wgt[i - 1] as usize] + val[i - 1]); - } - } - } - dp[cap] - } + [class]{}-[func]{knapsack_dp_comp} ``` === "C" ```c title="knapsack.c" - /* 0-1 背包:空间优化后的动态规划 */ - int knapsackDPComp(int wgt[], int val[], int cap, int wgtSize) { - int n = wgtSize; - // 初始化 dp 表 - int *dp = calloc(cap + 1, sizeof(int)); - // 状态转移 - for (int i = 1; i <= n; i++) { - // 倒序遍历 - for (int c = cap; c >= 1; c--) { - if (wgt[i - 1] <= c) { - // 不选和选物品 i 这两种方案的较大值 - dp[c] = myMax(dp[c], dp[c - wgt[i - 1]] + val[i - 1]); - } - } - } - int res = dp[cap]; - // 释放内存 - free(dp); - return res; - } + [class]{}-[func]{knapsackDPComp} ``` === "Kotlin" ```kotlin title="knapsack.kt" - /* 0-1 背包:空间优化后的动态规划 */ - fun knapsackDPComp(wgt: IntArray, _val: 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], dp[c - wgt[i - 1]] + _val[i - 1]) - } - } - } - return dp[cap] - } + [class]{}-[func]{knapsackDPComp} ``` === "Ruby" @@ -1452,27 +626,5 @@ In the code implementation, we only need to delete the first dimension $i$ of th === "Zig" ```zig title="knapsack.zig" - // 0-1 背包:空间优化后的动态规划 - fn knapsackDPComp(wgt: []i32, val: []i32, comptime cap: usize) i32 { - var n = wgt.len; - // 初始化 dp 表 - var dp = [_]i32{0} ** (cap + 1); - // 状态转移 - for (1..n + 1) |i| { - // 倒序遍历 - var c = cap; - while (c > 0) : (c -= 1) { - if (wgt[i - 1] < c) { - // 不选和选物品 i 这两种方案的较大值 - dp[c] = @max(dp[c], dp[c - @as(usize, @intCast(wgt[i - 1]))] + val[i - 1]); - } - } - } - return dp[cap]; - } + [class]{}-[func]{knapsackDPComp} ``` - -??? pythontutor "Code Visualization" - -
- diff --git a/en/docs/chapter_dynamic_programming/unbounded_knapsack_problem.md b/en/docs/chapter_dynamic_programming/unbounded_knapsack_problem.md index 4113d2c4d..ea7a400fd 100644 --- a/en/docs/chapter_dynamic_programming/unbounded_knapsack_problem.md +++ b/en/docs/chapter_dynamic_programming/unbounded_knapsack_problem.md @@ -42,18 +42,18 @@ Comparing the code for the two problems, the state transition changes from $i-1$ ```python title="unbounded_knapsack.py" def unbounded_knapsack_dp(wgt: list[int], val: list[int], cap: int) -> int: - """完全背包:动态规划""" + """Complete knapsack: Dynamic programming""" n = len(wgt) - # 初始化 dp 表 + # Initialize dp table dp = [[0] * (cap + 1) for _ in range(n + 1)] - # 状态转移 + # State transition for i in range(1, n + 1): for c in range(1, cap + 1): if wgt[i - 1] > c: - # 若超过背包容量,则不选物品 i + # If exceeding the knapsack capacity, do not choose item i dp[i][c] = dp[i - 1][c] else: - # 不选和选物品 i 这两种方案的较大值 + # The greater value between not choosing and choosing item i dp[i][c] = max(dp[i - 1][c], dp[i][c - wgt[i - 1]] + val[i - 1]) return dp[n][cap] ``` @@ -61,43 +61,25 @@ Comparing the code for the two problems, the state transition changes from $i-1$ === "C++" ```cpp title="unbounded_knapsack.cpp" - /* 完全背包:动态规划 */ - int unboundedKnapsackDP(vector &wgt, vector &val, int cap) { - int n = wgt.size(); - // 初始化 dp 表 - vector> dp(n + 1, vector(cap + 1, 0)); - // 状态转移 - for (int i = 1; i <= n; i++) { - for (int c = 1; c <= cap; c++) { - if (wgt[i - 1] > c) { - // 若超过背包容量,则不选物品 i - dp[i][c] = dp[i - 1][c]; - } else { - // 不选和选物品 i 这两种方案的较大值 - dp[i][c] = max(dp[i - 1][c], dp[i][c - wgt[i - 1]] + val[i - 1]); - } - } - } - return dp[n][cap]; - } + [class]{}-[func]{unboundedKnapsackDP} ``` === "Java" ```java title="unbounded_knapsack.java" - /* 完全背包:动态规划 */ + /* Complete knapsack: Dynamic programming */ int unboundedKnapsackDP(int[] wgt, int[] val, int cap) { int n = wgt.length; - // 初始化 dp 表 + // Initialize dp table int[][] dp = new int[n + 1][cap + 1]; - // 状态转移 + // State transition for (int i = 1; i <= n; i++) { for (int c = 1; c <= cap; c++) { if (wgt[i - 1] > c) { - // 若超过背包容量,则不选物品 i + // If exceeding the knapsack capacity, do not choose item i dp[i][c] = dp[i - 1][c]; } else { - // 不选和选物品 i 这两种方案的较大值 + // The greater value between not choosing and choosing item i dp[i][c] = Math.max(dp[i - 1][c], dp[i][c - wgt[i - 1]] + val[i - 1]); } } @@ -109,242 +91,55 @@ Comparing the code for the two problems, the state transition changes from $i-1$ === "C#" ```csharp title="unbounded_knapsack.cs" - /* 完全背包:动态规划 */ - int UnboundedKnapsackDP(int[] wgt, int[] val, int cap) { - int n = wgt.Length; - // 初始化 dp 表 - int[,] dp = new int[n + 1, cap + 1]; - // 状态转移 - for (int i = 1; i <= n; i++) { - for (int c = 1; c <= cap; c++) { - if (wgt[i - 1] > c) { - // 若超过背包容量,则不选物品 i - dp[i, c] = dp[i - 1, c]; - } else { - // 不选和选物品 i 这两种方案的较大值 - dp[i, c] = Math.Max(dp[i - 1, c], dp[i, c - wgt[i - 1]] + val[i - 1]); - } - } - } - return dp[n, cap]; - } + [class]{unbounded_knapsack}-[func]{UnboundedKnapsackDP} ``` === "Go" ```go title="unbounded_knapsack.go" - /* 完全背包:动态规划 */ - func unboundedKnapsackDP(wgt, val []int, cap int) int { - n := len(wgt) - // 初始化 dp 表 - dp := make([][]int, n+1) - for i := 0; i <= n; i++ { - dp[i] = make([]int, cap+1) - } - // 状态转移 - for i := 1; i <= n; i++ { - for c := 1; c <= cap; c++ { - if wgt[i-1] > c { - // 若超过背包容量,则不选物品 i - dp[i][c] = dp[i-1][c] - } else { - // 不选和选物品 i 这两种方案的较大值 - dp[i][c] = int(math.Max(float64(dp[i-1][c]), float64(dp[i][c-wgt[i-1]]+val[i-1]))) - } - } - } - return dp[n][cap] - } + [class]{}-[func]{unboundedKnapsackDP} ``` === "Swift" ```swift title="unbounded_knapsack.swift" - /* 完全背包:动态规划 */ - func unboundedKnapsackDP(wgt: [Int], val: [Int], cap: Int) -> Int { - let n = wgt.count - // 初始化 dp 表 - var dp = Array(repeating: Array(repeating: 0, count: cap + 1), count: n + 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], dp[i][c - wgt[i - 1]] + val[i - 1]) - } - } - } - return dp[n][cap] - } + [class]{}-[func]{unboundedKnapsackDP} ``` === "JS" ```javascript title="unbounded_knapsack.js" - /* 完全背包:动态规划 */ - function unboundedKnapsackDP(wgt, val, cap) { - const n = wgt.length; - // 初始化 dp 表 - const dp = Array.from({ length: n + 1 }, () => - Array.from({ length: cap + 1 }, () => 0) - ); - // 状态转移 - for (let i = 1; i <= n; i++) { - for (let c = 1; c <= cap; c++) { - if (wgt[i - 1] > c) { - // 若超过背包容量,则不选物品 i - dp[i][c] = dp[i - 1][c]; - } else { - // 不选和选物品 i 这两种方案的较大值 - dp[i][c] = Math.max( - dp[i - 1][c], - dp[i][c - wgt[i - 1]] + val[i - 1] - ); - } - } - } - return dp[n][cap]; - } + [class]{}-[func]{unboundedKnapsackDP} ``` === "TS" ```typescript title="unbounded_knapsack.ts" - /* 完全背包:动态规划 */ - function unboundedKnapsackDP( - wgt: Array, - val: Array, - cap: number - ): number { - const n = wgt.length; - // 初始化 dp 表 - const dp = Array.from({ length: n + 1 }, () => - Array.from({ length: cap + 1 }, () => 0) - ); - // 状态转移 - for (let i = 1; i <= n; i++) { - for (let c = 1; c <= cap; c++) { - if (wgt[i - 1] > c) { - // 若超过背包容量,则不选物品 i - dp[i][c] = dp[i - 1][c]; - } else { - // 不选和选物品 i 这两种方案的较大值 - dp[i][c] = Math.max( - dp[i - 1][c], - dp[i][c - wgt[i - 1]] + val[i - 1] - ); - } - } - } - return dp[n][cap]; - } + [class]{}-[func]{unboundedKnapsackDP} ``` === "Dart" ```dart title="unbounded_knapsack.dart" - /* 完全背包:动态规划 */ - int unboundedKnapsackDP(List wgt, List val, int cap) { - int n = wgt.length; - // 初始化 dp 表 - List> dp = List.generate(n + 1, (index) => List.filled(cap + 1, 0)); - // 状态转移 - for (int i = 1; i <= n; i++) { - for (int c = 1; c <= cap; c++) { - if (wgt[i - 1] > c) { - // 若超过背包容量,则不选物品 i - dp[i][c] = dp[i - 1][c]; - } else { - // 不选和选物品 i 这两种方案的较大值 - dp[i][c] = max(dp[i - 1][c], dp[i][c - wgt[i - 1]] + val[i - 1]); - } - } - } - return dp[n][cap]; - } + [class]{}-[func]{unboundedKnapsackDP} ``` === "Rust" ```rust title="unbounded_knapsack.rs" - /* 完全背包:动态规划 */ - fn unbounded_knapsack_dp(wgt: &[i32], val: &[i32], cap: usize) -> i32 { - let n = wgt.len(); - // 初始化 dp 表 - let mut dp = vec![vec![0; cap + 1]; n + 1]; - // 状态转移 - for i in 1..=n { - for c in 1..=cap { - if wgt[i - 1] > c as i32 { - // 若超过背包容量,则不选物品 i - dp[i][c] = dp[i - 1][c]; - } else { - // 不选和选物品 i 这两种方案的较大值 - dp[i][c] = std::cmp::max(dp[i - 1][c], dp[i][c - wgt[i - 1] as usize] + val[i - 1]); - } - } - } - return dp[n][cap]; - } + [class]{}-[func]{unbounded_knapsack_dp} ``` === "C" ```c title="unbounded_knapsack.c" - /* 完全背包:动态规划 */ - int unboundedKnapsackDP(int wgt[], int val[], int cap, int wgtSize) { - int n = wgtSize; - // 初始化 dp 表 - int **dp = malloc((n + 1) * sizeof(int *)); - for (int i = 0; i <= n; i++) { - dp[i] = calloc(cap + 1, sizeof(int)); - } - // 状态转移 - for (int i = 1; i <= n; i++) { - for (int c = 1; c <= cap; c++) { - if (wgt[i - 1] > c) { - // 若超过背包容量,则不选物品 i - dp[i][c] = dp[i - 1][c]; - } else { - // 不选和选物品 i 这两种方案的较大值 - dp[i][c] = myMax(dp[i - 1][c], dp[i][c - wgt[i - 1]] + val[i - 1]); - } - } - } - int res = dp[n][cap]; - // 释放内存 - for (int i = 0; i <= n; i++) { - free(dp[i]); - } - return res; - } + [class]{}-[func]{unboundedKnapsackDP} ``` === "Kotlin" ```kotlin title="unbounded_knapsack.kt" - /* 完全背包:动态规划 */ - fun unboundedKnapsackDP(wgt: IntArray, _val: 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], dp[i][c - wgt[i - 1]] + _val[i - 1]) - } - } - } - return dp[n][cap] - } + [class]{}-[func]{unboundedKnapsackDP} ``` === "Ruby" @@ -356,32 +151,9 @@ Comparing the code for the two problems, the state transition changes from $i-1$ === "Zig" ```zig title="unbounded_knapsack.zig" - // 完全背包:动态规划 - fn unboundedKnapsackDP(comptime wgt: []i32, val: []i32, comptime cap: usize) i32 { - comptime var n = wgt.len; - // 初始化 dp 表 - var dp = [_][cap + 1]i32{[_]i32{0} ** (cap + 1)} ** (n + 1); - // 状态转移 - for (1..n + 1) |i| { - for (1..cap + 1) |c| { - if (wgt[i - 1] > c) { - // 若超过背包容量,则不选物品 i - dp[i][c] = dp[i - 1][c]; - } else { - // 不选和选物品 i 这两种方案的较大值 - dp[i][c] = @max(dp[i - 1][c], dp[i][c - @as(usize, @intCast(wgt[i - 1]))] + val[i - 1]); - } - } - } - return dp[n][cap]; - } + [class]{}-[func]{unboundedKnapsackDP} ``` -??? pythontutor "Code Visualization" - -
- - ### 3.   Space optimization Since the current state comes from the state to the left and above, **the space-optimized solution should perform a forward traversal for each row in the $dp$ table**. @@ -414,19 +186,19 @@ The code implementation is quite simple, just remove the first dimension of the ```python title="unbounded_knapsack.py" def unbounded_knapsack_dp_comp(wgt: list[int], val: list[int], cap: int) -> int: - """完全背包:空间优化后的动态规划""" + """Complete knapsack: Space-optimized dynamic programming""" n = len(wgt) - # 初始化 dp 表 + # Initialize dp table dp = [0] * (cap + 1) - # 状态转移 + # State transition for i in range(1, n + 1): - # 正序遍历 + # Traverse in order for c in range(1, cap + 1): if wgt[i - 1] > c: - # 若超过背包容量,则不选物品 i + # If exceeding the knapsack capacity, do not choose item i dp[c] = dp[c] else: - # 不选和选物品 i 这两种方案的较大值 + # The greater value between not choosing and choosing item i dp[c] = max(dp[c], dp[c - wgt[i - 1]] + val[i - 1]) return dp[cap] ``` @@ -434,43 +206,25 @@ The code implementation is quite simple, just remove the first dimension of the === "C++" ```cpp title="unbounded_knapsack.cpp" - /* 完全背包:空间优化后的动态规划 */ - int unboundedKnapsackDPComp(vector &wgt, vector &val, int cap) { - int n = wgt.size(); - // 初始化 dp 表 - vector dp(cap + 1, 0); - // 状态转移 - for (int i = 1; i <= n; i++) { - for (int c = 1; c <= cap; c++) { - if (wgt[i - 1] > c) { - // 若超过背包容量,则不选物品 i - dp[c] = dp[c]; - } else { - // 不选和选物品 i 这两种方案的较大值 - dp[c] = max(dp[c], dp[c - wgt[i - 1]] + val[i - 1]); - } - } - } - return dp[cap]; - } + [class]{}-[func]{unboundedKnapsackDPComp} ``` === "Java" ```java title="unbounded_knapsack.java" - /* 完全背包:空间优化后的动态规划 */ + /* Complete knapsack: Space-optimized dynamic programming */ int unboundedKnapsackDPComp(int[] wgt, int[] val, int cap) { int n = wgt.length; - // 初始化 dp 表 + // Initialize dp table int[] dp = new int[cap + 1]; - // 状态转移 + // State transition for (int i = 1; i <= n; i++) { for (int c = 1; c <= cap; c++) { if (wgt[i - 1] > c) { - // 若超过背包容量,则不选物品 i + // If exceeding the knapsack capacity, do not choose item i dp[c] = dp[c]; } else { - // 不选和选物品 i 这两种方案的较大值 + // The greater value between not choosing and choosing item i dp[c] = Math.max(dp[c], dp[c - wgt[i - 1]] + val[i - 1]); } } @@ -482,228 +236,55 @@ The code implementation is quite simple, just remove the first dimension of the === "C#" ```csharp title="unbounded_knapsack.cs" - /* 完全背包:空间优化后的动态规划 */ - int UnboundedKnapsackDPComp(int[] wgt, int[] val, int cap) { - int n = wgt.Length; - // 初始化 dp 表 - int[] dp = new int[cap + 1]; - // 状态转移 - for (int i = 1; i <= n; i++) { - for (int c = 1; c <= cap; c++) { - if (wgt[i - 1] > c) { - // 若超过背包容量,则不选物品 i - dp[c] = dp[c]; - } else { - // 不选和选物品 i 这两种方案的较大值 - dp[c] = Math.Max(dp[c], dp[c - wgt[i - 1]] + val[i - 1]); - } - } - } - return dp[cap]; - } + [class]{unbounded_knapsack}-[func]{UnboundedKnapsackDPComp} ``` === "Go" ```go title="unbounded_knapsack.go" - /* 完全背包:空间优化后的动态规划 */ - func unboundedKnapsackDPComp(wgt, val []int, cap int) int { - n := len(wgt) - // 初始化 dp 表 - dp := make([]int, cap+1) - // 状态转移 - for i := 1; i <= n; i++ { - for c := 1; c <= cap; c++ { - if wgt[i-1] > c { - // 若超过背包容量,则不选物品 i - dp[c] = dp[c] - } else { - // 不选和选物品 i 这两种方案的较大值 - dp[c] = int(math.Max(float64(dp[c]), float64(dp[c-wgt[i-1]]+val[i-1]))) - } - } - } - return dp[cap] - } + [class]{}-[func]{unboundedKnapsackDPComp} ``` === "Swift" ```swift title="unbounded_knapsack.swift" - /* 完全背包:空间优化后的动态规划 */ - func unboundedKnapsackDPComp(wgt: [Int], val: [Int], cap: Int) -> Int { - let n = wgt.count - // 初始化 dp 表 - var dp = Array(repeating: 0, count: 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], dp[c - wgt[i - 1]] + val[i - 1]) - } - } - } - return dp[cap] - } + [class]{}-[func]{unboundedKnapsackDPComp} ``` === "JS" ```javascript title="unbounded_knapsack.js" - /* 完全背包:状态压缩后的动态规划 */ - function unboundedKnapsackDPComp(wgt, val, cap) { - const n = wgt.length; - // 初始化 dp 表 - const dp = Array.from({ length: cap + 1 }, () => 0); - // 状态转移 - for (let i = 1; i <= n; i++) { - for (let c = 1; c <= cap; c++) { - if (wgt[i - 1] > c) { - // 若超过背包容量,则不选物品 i - dp[c] = dp[c]; - } else { - // 不选和选物品 i 这两种方案的较大值 - dp[c] = Math.max(dp[c], dp[c - wgt[i - 1]] + val[i - 1]); - } - } - } - return dp[cap]; - } + [class]{}-[func]{unboundedKnapsackDPComp} ``` === "TS" ```typescript title="unbounded_knapsack.ts" - /* 完全背包:状态压缩后的动态规划 */ - function unboundedKnapsackDPComp( - wgt: Array, - val: Array, - cap: number - ): number { - const n = wgt.length; - // 初始化 dp 表 - const dp = Array.from({ length: cap + 1 }, () => 0); - // 状态转移 - for (let i = 1; i <= n; i++) { - for (let c = 1; c <= cap; c++) { - if (wgt[i - 1] > c) { - // 若超过背包容量,则不选物品 i - dp[c] = dp[c]; - } else { - // 不选和选物品 i 这两种方案的较大值 - dp[c] = Math.max(dp[c], dp[c - wgt[i - 1]] + val[i - 1]); - } - } - } - return dp[cap]; - } + [class]{}-[func]{unboundedKnapsackDPComp} ``` === "Dart" ```dart title="unbounded_knapsack.dart" - /* 完全背包:空间优化后的动态规划 */ - int unboundedKnapsackDPComp(List wgt, List val, int cap) { - int n = wgt.length; - // 初始化 dp 表 - List dp = List.filled(cap + 1, 0); - // 状态转移 - for (int i = 1; i <= n; i++) { - for (int c = 1; c <= cap; c++) { - if (wgt[i - 1] > c) { - // 若超过背包容量,则不选物品 i - dp[c] = dp[c]; - } else { - // 不选和选物品 i 这两种方案的较大值 - dp[c] = max(dp[c], dp[c - wgt[i - 1]] + val[i - 1]); - } - } - } - return dp[cap]; - } + [class]{}-[func]{unboundedKnapsackDPComp} ``` === "Rust" ```rust title="unbounded_knapsack.rs" - /* 完全背包:空间优化后的动态规划 */ - fn unbounded_knapsack_dp_comp(wgt: &[i32], val: &[i32], cap: usize) -> i32 { - let n = wgt.len(); - // 初始化 dp 表 - let mut dp = vec![0; cap + 1]; - // 状态转移 - for i in 1..=n { - for c in 1..=cap { - if wgt[i - 1] > c as i32 { - // 若超过背包容量,则不选物品 i - dp[c] = dp[c]; - } else { - // 不选和选物品 i 这两种方案的较大值 - dp[c] = std::cmp::max(dp[c], dp[c - wgt[i - 1] as usize] + val[i - 1]); - } - } - } - dp[cap] - } + [class]{}-[func]{unbounded_knapsack_dp_comp} ``` === "C" ```c title="unbounded_knapsack.c" - /* 完全背包:空间优化后的动态规划 */ - int unboundedKnapsackDPComp(int wgt[], int val[], int cap, int wgtSize) { - int n = wgtSize; - // 初始化 dp 表 - int *dp = calloc(cap + 1, sizeof(int)); - // 状态转移 - for (int i = 1; i <= n; i++) { - for (int c = 1; c <= cap; c++) { - if (wgt[i - 1] > c) { - // 若超过背包容量,则不选物品 i - dp[c] = dp[c]; - } else { - // 不选和选物品 i 这两种方案的较大值 - dp[c] = myMax(dp[c], dp[c - wgt[i - 1]] + val[i - 1]); - } - } - } - int res = dp[cap]; - // 释放内存 - free(dp); - return res; - } + [class]{}-[func]{unboundedKnapsackDPComp} ``` === "Kotlin" ```kotlin title="unbounded_knapsack.kt" - /* 完全背包:空间优化后的动态规划 */ - fun unboundedKnapsackDPComp( - wgt: IntArray, - _val: 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], dp[c - wgt[i - 1]] + _val[i - 1]) - } - } - } - return dp[cap] - } + [class]{}-[func]{unboundedKnapsackDPComp} ``` === "Ruby" @@ -715,32 +296,9 @@ The code implementation is quite simple, just remove the first dimension of the === "Zig" ```zig title="unbounded_knapsack.zig" - // 完全背包:空间优化后的动态规划 - fn unboundedKnapsackDPComp(comptime wgt: []i32, val: []i32, comptime cap: usize) i32 { - comptime var n = wgt.len; - // 初始化 dp 表 - var dp = [_]i32{0} ** (cap + 1); - // 状态转移 - for (1..n + 1) |i| { - for (1..cap + 1) |c| { - if (wgt[i - 1] > c) { - // 若超过背包容量,则不选物品 i - dp[c] = dp[c]; - } else { - // 不选和选物品 i 这两种方案的较大值 - dp[c] = @max(dp[c], dp[c - @as(usize, @intCast(wgt[i - 1]))] + val[i - 1]); - } - } - } - return dp[cap]; - } + [class]{}-[func]{unboundedKnapsackDPComp} ``` -??? pythontutor "Code Visualization" - -
- - ## 14.5.2   Coin change problem The knapsack problem is a representative of a large class of dynamic programming problems and has many variants, such as the coin change problem. @@ -794,22 +352,22 @@ For this reason, we use the number $amt + 1$ to represent an invalid solution, b ```python title="coin_change.py" def coin_change_dp(coins: list[int], amt: int) -> int: - """零钱兑换:动态规划""" + """Coin change: Dynamic programming""" n = len(coins) MAX = amt + 1 - # 初始化 dp 表 + # Initialize dp table dp = [[0] * (amt + 1) for _ in range(n + 1)] - # 状态转移:首行首列 + # State transition: first row and first column for a in range(1, amt + 1): dp[0][a] = MAX - # 状态转移:其余行和列 + # State transition: the rest of the rows and columns for i in range(1, n + 1): for a in range(1, amt + 1): if coins[i - 1] > a: - # 若超过目标金额,则不选硬币 i + # If exceeding the target amount, do not choose coin i dp[i][a] = dp[i - 1][a] else: - # 不选和选硬币 i 这两种方案的较小值 + # The smaller value between not choosing and choosing coin i dp[i][a] = min(dp[i - 1][a], dp[i][a - coins[i - 1]] + 1) return dp[n][amt] if dp[n][amt] != MAX else -1 ``` @@ -817,53 +375,30 @@ For this reason, we use the number $amt + 1$ to represent an invalid solution, b === "C++" ```cpp title="coin_change.cpp" - /* 零钱兑换:动态规划 */ - int coinChangeDP(vector &coins, int amt) { - int n = coins.size(); - int MAX = amt + 1; - // 初始化 dp 表 - vector> dp(n + 1, vector(amt + 1, 0)); - // 状态转移:首行首列 - for (int a = 1; a <= amt; a++) { - dp[0][a] = MAX; - } - // 状态转移:其余行和列 - for (int i = 1; i <= n; i++) { - for (int a = 1; a <= amt; a++) { - if (coins[i - 1] > a) { - // 若超过目标金额,则不选硬币 i - dp[i][a] = dp[i - 1][a]; - } else { - // 不选和选硬币 i 这两种方案的较小值 - dp[i][a] = min(dp[i - 1][a], dp[i][a - coins[i - 1]] + 1); - } - } - } - return dp[n][amt] != MAX ? dp[n][amt] : -1; - } + [class]{}-[func]{coinChangeDP} ``` === "Java" ```java title="coin_change.java" - /* 零钱兑换:动态规划 */ + /* Coin change: Dynamic programming */ int coinChangeDP(int[] coins, int amt) { int n = coins.length; int MAX = amt + 1; - // 初始化 dp 表 + // Initialize dp table int[][] dp = new int[n + 1][amt + 1]; - // 状态转移:首行首列 + // State transition: first row and first column for (int a = 1; a <= amt; a++) { dp[0][a] = MAX; } - // 状态转移:其余行和列 + // State transition: the rest of the rows and columns for (int i = 1; i <= n; i++) { for (int a = 1; a <= amt; a++) { if (coins[i - 1] > a) { - // 若超过目标金额,则不选硬币 i + // If exceeding the target amount, do not choose coin i dp[i][a] = dp[i - 1][a]; } else { - // 不选和选硬币 i 这两种方案的较小值 + // The smaller value between not choosing and choosing coin i dp[i][a] = Math.min(dp[i - 1][a], dp[i][a - coins[i - 1]] + 1); } } @@ -875,285 +410,55 @@ For this reason, we use the number $amt + 1$ to represent an invalid solution, b === "C#" ```csharp title="coin_change.cs" - /* 零钱兑换:动态规划 */ - int CoinChangeDP(int[] coins, int amt) { - int n = coins.Length; - int MAX = amt + 1; - // 初始化 dp 表 - int[,] dp = new int[n + 1, amt + 1]; - // 状态转移:首行首列 - for (int a = 1; a <= amt; a++) { - dp[0, a] = MAX; - } - // 状态转移:其余行和列 - for (int i = 1; i <= n; i++) { - for (int a = 1; a <= amt; a++) { - if (coins[i - 1] > a) { - // 若超过目标金额,则不选硬币 i - dp[i, a] = dp[i - 1, a]; - } else { - // 不选和选硬币 i 这两种方案的较小值 - dp[i, a] = Math.Min(dp[i - 1, a], dp[i, a - coins[i - 1]] + 1); - } - } - } - return dp[n, amt] != MAX ? dp[n, amt] : -1; - } + [class]{coin_change}-[func]{CoinChangeDP} ``` === "Go" ```go title="coin_change.go" - /* 零钱兑换:动态规划 */ - func coinChangeDP(coins []int, amt int) int { - n := len(coins) - max := amt + 1 - // 初始化 dp 表 - dp := make([][]int, n+1) - for i := 0; i <= n; i++ { - dp[i] = make([]int, amt+1) - } - // 状态转移:首行首列 - for a := 1; a <= amt; a++ { - dp[0][a] = max - } - // 状态转移:其余行和列 - for i := 1; i <= n; i++ { - for a := 1; a <= amt; a++ { - if coins[i-1] > a { - // 若超过目标金额,则不选硬币 i - dp[i][a] = dp[i-1][a] - } else { - // 不选和选硬币 i 这两种方案的较小值 - dp[i][a] = int(math.Min(float64(dp[i-1][a]), float64(dp[i][a-coins[i-1]]+1))) - } - } - } - if dp[n][amt] != max { - return dp[n][amt] - } - return -1 - } + [class]{}-[func]{coinChangeDP} ``` === "Swift" ```swift title="coin_change.swift" - /* 零钱兑换:动态规划 */ - func coinChangeDP(coins: [Int], amt: Int) -> Int { - let n = coins.count - let MAX = amt + 1 - // 初始化 dp 表 - var dp = Array(repeating: Array(repeating: 0, count: amt + 1), count: n + 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], dp[i][a - coins[i - 1]] + 1) - } - } - } - return dp[n][amt] != MAX ? dp[n][amt] : -1 - } + [class]{}-[func]{coinChangeDP} ``` === "JS" ```javascript title="coin_change.js" - /* 零钱兑换:动态规划 */ - function coinChangeDP(coins, amt) { - const n = coins.length; - const MAX = amt + 1; - // 初始化 dp 表 - const dp = Array.from({ length: n + 1 }, () => - Array.from({ length: amt + 1 }, () => 0) - ); - // 状态转移:首行首列 - for (let a = 1; a <= amt; a++) { - dp[0][a] = MAX; - } - // 状态转移:其余行和列 - for (let i = 1; i <= n; i++) { - for (let a = 1; a <= amt; a++) { - if (coins[i - 1] > a) { - // 若超过目标金额,则不选硬币 i - dp[i][a] = dp[i - 1][a]; - } else { - // 不选和选硬币 i 这两种方案的较小值 - dp[i][a] = Math.min(dp[i - 1][a], dp[i][a - coins[i - 1]] + 1); - } - } - } - return dp[n][amt] !== MAX ? dp[n][amt] : -1; - } + [class]{}-[func]{coinChangeDP} ``` === "TS" ```typescript title="coin_change.ts" - /* 零钱兑换:动态规划 */ - function coinChangeDP(coins: Array, amt: number): number { - const n = coins.length; - const MAX = amt + 1; - // 初始化 dp 表 - const dp = Array.from({ length: n + 1 }, () => - Array.from({ length: amt + 1 }, () => 0) - ); - // 状态转移:首行首列 - for (let a = 1; a <= amt; a++) { - dp[0][a] = MAX; - } - // 状态转移:其余行和列 - for (let i = 1; i <= n; i++) { - for (let a = 1; a <= amt; a++) { - if (coins[i - 1] > a) { - // 若超过目标金额,则不选硬币 i - dp[i][a] = dp[i - 1][a]; - } else { - // 不选和选硬币 i 这两种方案的较小值 - dp[i][a] = Math.min(dp[i - 1][a], dp[i][a - coins[i - 1]] + 1); - } - } - } - return dp[n][amt] !== MAX ? dp[n][amt] : -1; - } + [class]{}-[func]{coinChangeDP} ``` === "Dart" ```dart title="coin_change.dart" - /* 零钱兑换:动态规划 */ - int coinChangeDP(List coins, int amt) { - int n = coins.length; - int MAX = amt + 1; - // 初始化 dp 表 - List> dp = List.generate(n + 1, (index) => List.filled(amt + 1, 0)); - // 状态转移:首行首列 - for (int a = 1; a <= amt; a++) { - dp[0][a] = MAX; - } - // 状态转移:其余行和列 - for (int i = 1; i <= n; i++) { - for (int a = 1; a <= amt; a++) { - if (coins[i - 1] > a) { - // 若超过目标金额,则不选硬币 i - dp[i][a] = dp[i - 1][a]; - } else { - // 不选和选硬币 i 这两种方案的较小值 - dp[i][a] = min(dp[i - 1][a], dp[i][a - coins[i - 1]] + 1); - } - } - } - return dp[n][amt] != MAX ? dp[n][amt] : -1; - } + [class]{}-[func]{coinChangeDP} ``` === "Rust" ```rust title="coin_change.rs" - /* 零钱兑换:动态规划 */ - fn coin_change_dp(coins: &[i32], amt: usize) -> i32 { - let n = coins.len(); - let max = amt + 1; - // 初始化 dp 表 - let mut dp = vec![vec![0; amt + 1]; n + 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 as i32 { - // 若超过目标金额,则不选硬币 i - dp[i][a] = dp[i - 1][a]; - } else { - // 不选和选硬币 i 这两种方案的较小值 - dp[i][a] = std::cmp::min(dp[i - 1][a], dp[i][a - coins[i - 1] as usize] + 1); - } - } - } - if dp[n][amt] != max { - return dp[n][amt] as i32; - } else { - -1 - } - } + [class]{}-[func]{coin_change_dp} ``` === "C" ```c title="coin_change.c" - /* 零钱兑换:动态规划 */ - int coinChangeDP(int coins[], int amt, int coinsSize) { - int n = coinsSize; - int MAX = amt + 1; - // 初始化 dp 表 - int **dp = malloc((n + 1) * sizeof(int *)); - for (int i = 0; i <= n; i++) { - dp[i] = calloc(amt + 1, sizeof(int)); - } - // 状态转移:首行首列 - for (int a = 1; a <= amt; a++) { - dp[0][a] = MAX; - } - // 状态转移:其余行和列 - for (int i = 1; i <= n; i++) { - for (int a = 1; a <= amt; a++) { - if (coins[i - 1] > a) { - // 若超过目标金额,则不选硬币 i - dp[i][a] = dp[i - 1][a]; - } else { - // 不选和选硬币 i 这两种方案的较小值 - dp[i][a] = myMin(dp[i - 1][a], dp[i][a - coins[i - 1]] + 1); - } - } - } - int res = dp[n][amt] != MAX ? dp[n][amt] : -1; - // 释放内存 - for (int i = 0; i <= n; i++) { - free(dp[i]); - } - free(dp); - return res; - } + [class]{}-[func]{coinChangeDP} ``` === "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], dp[i][a - coins[i - 1]] + 1) - } - } - } - return if (dp[n][amt] != MAX) dp[n][amt] else -1 - } + [class]{}-[func]{coinChangeDP} ``` === "Ruby" @@ -1165,41 +470,9 @@ For this reason, we use the number $amt + 1$ to represent an invalid solution, b === "Zig" ```zig title="coin_change.zig" - // 零钱兑换:动态规划 - fn coinChangeDP(comptime coins: []i32, comptime amt: usize) i32 { - comptime var n = coins.len; - comptime var max = amt + 1; - // 初始化 dp 表 - var dp = [_][amt + 1]i32{[_]i32{0} ** (amt + 1)} ** (n + 1); - // 状态转移:首行首列 - for (1..amt + 1) |a| { - dp[0][a] = max; - } - // 状态转移:其余行和列 - for (1..n + 1) |i| { - for (1..amt + 1) |a| { - if (coins[i - 1] > @as(i32, @intCast(a))) { - // 若超过目标金额,则不选硬币 i - dp[i][a] = dp[i - 1][a]; - } else { - // 不选和选硬币 i 这两种方案的较小值 - dp[i][a] = @min(dp[i - 1][a], dp[i][a - @as(usize, @intCast(coins[i - 1]))] + 1); - } - } - } - if (dp[n][amt] != max) { - return @intCast(dp[n][amt]); - } else { - return -1; - } - } + [class]{}-[func]{coinChangeDP} ``` -??? pythontutor "Code Visualization" - -
- - Figure 14-25 show the dynamic programming process for the coin change problem, which is very similar to the unbounded knapsack problem. === "<1>" @@ -1257,21 +530,21 @@ The space optimization for the coin change problem is handled in the same way as ```python title="coin_change.py" def coin_change_dp_comp(coins: list[int], amt: int) -> int: - """零钱兑换:空间优化后的动态规划""" + """Coin change: Space-optimized dynamic programming""" n = len(coins) MAX = amt + 1 - # 初始化 dp 表 + # Initialize dp table dp = [MAX] * (amt + 1) dp[0] = 0 - # 状态转移 + # State transition for i in range(1, n + 1): - # 正序遍历 + # Traverse in order for a in range(1, amt + 1): if coins[i - 1] > a: - # 若超过目标金额,则不选硬币 i + # If exceeding the target amount, do not choose coin i dp[a] = dp[a] else: - # 不选和选硬币 i 这两种方案的较小值 + # The smaller value between not choosing and choosing coin i dp[a] = min(dp[a], dp[a - coins[i - 1]] + 1) return dp[amt] if dp[amt] != MAX else -1 ``` @@ -1279,48 +552,28 @@ The space optimization for the coin change problem is handled in the same way as === "C++" ```cpp title="coin_change.cpp" - /* 零钱兑换:空间优化后的动态规划 */ - int coinChangeDPComp(vector &coins, int amt) { - int n = coins.size(); - int MAX = amt + 1; - // 初始化 dp 表 - vector dp(amt + 1, MAX); - dp[0] = 0; - // 状态转移 - for (int i = 1; i <= n; i++) { - for (int a = 1; a <= amt; a++) { - if (coins[i - 1] > a) { - // 若超过目标金额,则不选硬币 i - dp[a] = dp[a]; - } else { - // 不选和选硬币 i 这两种方案的较小值 - dp[a] = min(dp[a], dp[a - coins[i - 1]] + 1); - } - } - } - return dp[amt] != MAX ? dp[amt] : -1; - } + [class]{}-[func]{coinChangeDPComp} ``` === "Java" ```java title="coin_change.java" - /* 零钱兑换:空间优化后的动态规划 */ + /* Coin change: Space-optimized dynamic programming */ int coinChangeDPComp(int[] coins, int amt) { int n = coins.length; int MAX = amt + 1; - // 初始化 dp 表 + // Initialize dp table int[] dp = new int[amt + 1]; Arrays.fill(dp, MAX); dp[0] = 0; - // 状态转移 + // State transition for (int i = 1; i <= n; i++) { for (int a = 1; a <= amt; a++) { if (coins[i - 1] > a) { - // 若超过目标金额,则不选硬币 i + // If exceeding the target amount, do not choose coin i dp[a] = dp[a]; } else { - // 不选和选硬币 i 这两种方案的较小值 + // The smaller value between not choosing and choosing coin i dp[a] = Math.min(dp[a], dp[a - coins[i - 1]] + 1); } } @@ -1332,255 +585,55 @@ The space optimization for the coin change problem is handled in the same way as === "C#" ```csharp title="coin_change.cs" - /* 零钱兑换:空间优化后的动态规划 */ - int CoinChangeDPComp(int[] coins, int amt) { - int n = coins.Length; - int MAX = amt + 1; - // 初始化 dp 表 - int[] dp = new int[amt + 1]; - Array.Fill(dp, MAX); - dp[0] = 0; - // 状态转移 - for (int i = 1; i <= n; i++) { - for (int a = 1; a <= amt; a++) { - if (coins[i - 1] > a) { - // 若超过目标金额,则不选硬币 i - dp[a] = dp[a]; - } else { - // 不选和选硬币 i 这两种方案的较小值 - dp[a] = Math.Min(dp[a], dp[a - coins[i - 1]] + 1); - } - } - } - return dp[amt] != MAX ? dp[amt] : -1; - } + [class]{coin_change}-[func]{CoinChangeDPComp} ``` === "Go" ```go title="coin_change.go" - /* 零钱兑换:动态规划 */ - func coinChangeDPComp(coins []int, amt int) int { - n := len(coins) - max := amt + 1 - // 初始化 dp 表 - dp := make([]int, amt+1) - for i := 1; i <= amt; i++ { - dp[i] = max - } - // 状态转移 - for i := 1; i <= n; i++ { - // 正序遍历 - for a := 1; a <= amt; a++ { - if coins[i-1] > a { - // 若超过目标金额,则不选硬币 i - dp[a] = dp[a] - } else { - // 不选和选硬币 i 这两种方案的较小值 - dp[a] = int(math.Min(float64(dp[a]), float64(dp[a-coins[i-1]]+1))) - } - } - } - if dp[amt] != max { - return dp[amt] - } - return -1 - } + [class]{}-[func]{coinChangeDPComp} ``` === "Swift" ```swift title="coin_change.swift" - /* 零钱兑换:空间优化后的动态规划 */ - func coinChangeDPComp(coins: [Int], amt: Int) -> Int { - let n = coins.count - let MAX = amt + 1 - // 初始化 dp 表 - var dp = Array(repeating: MAX, count: amt + 1) - 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], dp[a - coins[i - 1]] + 1) - } - } - } - return dp[amt] != MAX ? dp[amt] : -1 - } + [class]{}-[func]{coinChangeDPComp} ``` === "JS" ```javascript title="coin_change.js" - /* 零钱兑换:状态压缩后的动态规划 */ - function coinChangeDPComp(coins, amt) { - const n = coins.length; - const MAX = amt + 1; - // 初始化 dp 表 - const dp = Array.from({ length: amt + 1 }, () => MAX); - dp[0] = 0; - // 状态转移 - for (let i = 1; i <= n; i++) { - for (let a = 1; a <= amt; a++) { - if (coins[i - 1] > a) { - // 若超过目标金额,则不选硬币 i - dp[a] = dp[a]; - } else { - // 不选和选硬币 i 这两种方案的较小值 - dp[a] = Math.min(dp[a], dp[a - coins[i - 1]] + 1); - } - } - } - return dp[amt] !== MAX ? dp[amt] : -1; - } + [class]{}-[func]{coinChangeDPComp} ``` === "TS" ```typescript title="coin_change.ts" - /* 零钱兑换:状态压缩后的动态规划 */ - function coinChangeDPComp(coins: Array, amt: number): number { - const n = coins.length; - const MAX = amt + 1; - // 初始化 dp 表 - const dp = Array.from({ length: amt + 1 }, () => MAX); - dp[0] = 0; - // 状态转移 - for (let i = 1; i <= n; i++) { - for (let a = 1; a <= amt; a++) { - if (coins[i - 1] > a) { - // 若超过目标金额,则不选硬币 i - dp[a] = dp[a]; - } else { - // 不选和选硬币 i 这两种方案的较小值 - dp[a] = Math.min(dp[a], dp[a - coins[i - 1]] + 1); - } - } - } - return dp[amt] !== MAX ? dp[amt] : -1; - } + [class]{}-[func]{coinChangeDPComp} ``` === "Dart" ```dart title="coin_change.dart" - /* 零钱兑换:空间优化后的动态规划 */ - int coinChangeDPComp(List coins, int amt) { - int n = coins.length; - int MAX = amt + 1; - // 初始化 dp 表 - List dp = List.filled(amt + 1, MAX); - dp[0] = 0; - // 状态转移 - for (int i = 1; i <= n; i++) { - for (int a = 1; a <= amt; a++) { - if (coins[i - 1] > a) { - // 若超过目标金额,则不选硬币 i - dp[a] = dp[a]; - } else { - // 不选和选硬币 i 这两种方案的较小值 - dp[a] = min(dp[a], dp[a - coins[i - 1]] + 1); - } - } - } - return dp[amt] != MAX ? dp[amt] : -1; - } + [class]{}-[func]{coinChangeDPComp} ``` === "Rust" ```rust title="coin_change.rs" - /* 零钱兑换:空间优化后的动态规划 */ - fn coin_change_dp_comp(coins: &[i32], amt: usize) -> i32 { - let n = coins.len(); - let max = amt + 1; - // 初始化 dp 表 - let mut dp = vec![0; amt + 1]; - dp.fill(max); - dp[0] = 0; - // 状态转移 - for i in 1..=n { - for a in 1..=amt { - if coins[i - 1] > a as i32 { - // 若超过目标金额,则不选硬币 i - dp[a] = dp[a]; - } else { - // 不选和选硬币 i 这两种方案的较小值 - dp[a] = std::cmp::min(dp[a], dp[a - coins[i - 1] as usize] + 1); - } - } - } - if dp[amt] != max { - return dp[amt] as i32; - } else { - -1 - } - } + [class]{}-[func]{coin_change_dp_comp} ``` === "C" ```c title="coin_change.c" - /* 零钱兑换:空间优化后的动态规划 */ - int coinChangeDPComp(int coins[], int amt, int coinsSize) { - int n = coinsSize; - int MAX = amt + 1; - // 初始化 dp 表 - int *dp = malloc((amt + 1) * sizeof(int)); - for (int j = 1; j <= amt; j++) { - dp[j] = MAX; - } - dp[0] = 0; - - // 状态转移 - for (int i = 1; i <= n; i++) { - for (int a = 1; a <= amt; a++) { - if (coins[i - 1] > a) { - // 若超过目标金额,则不选硬币 i - dp[a] = dp[a]; - } else { - // 不选和选硬币 i 这两种方案的较小值 - dp[a] = myMin(dp[a], dp[a - coins[i - 1]] + 1); - } - } - } - int res = dp[amt] != MAX ? dp[amt] : -1; - // 释放内存 - free(dp); - return res; - } + [class]{}-[func]{coinChangeDPComp} ``` === "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) - dp.fill(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], dp[a - coins[i - 1]] + 1) - } - } - } - return if (dp[amt] != MAX) dp[amt] else -1 - } + [class]{}-[func]{coinChangeDPComp} ``` === "Ruby" @@ -1592,39 +645,9 @@ The space optimization for the coin change problem is handled in the same way as === "Zig" ```zig title="coin_change.zig" - // 零钱兑换:空间优化后的动态规划 - fn coinChangeDPComp(comptime coins: []i32, comptime amt: usize) i32 { - comptime var n = coins.len; - comptime var max = amt + 1; - // 初始化 dp 表 - var dp = [_]i32{0} ** (amt + 1); - @memset(&dp, max); - dp[0] = 0; - // 状态转移 - for (1..n + 1) |i| { - for (1..amt + 1) |a| { - if (coins[i - 1] > @as(i32, @intCast(a))) { - // 若超过目标金额,则不选硬币 i - dp[a] = dp[a]; - } else { - // 不选和选硬币 i 这两种方案的较小值 - dp[a] = @min(dp[a], dp[a - @as(usize, @intCast(coins[i - 1]))] + 1); - } - } - } - if (dp[amt] != max) { - return @intCast(dp[amt]); - } else { - return -1; - } - } + [class]{}-[func]{coinChangeDPComp} ``` -??? pythontutor "Code Visualization" - -
- - ## 14.5.3   Coin change problem II !!! question @@ -1653,21 +676,21 @@ When the target amount is $0$, no coins are needed to make up the target amount, ```python title="coin_change_ii.py" def coin_change_ii_dp(coins: list[int], amt: int) -> int: - """零钱兑换 II:动态规划""" + """Coin change II: Dynamic programming""" n = len(coins) - # 初始化 dp 表 + # Initialize dp table dp = [[0] * (amt + 1) for _ in range(n + 1)] - # 初始化首列 + # Initialize first column for i in range(n + 1): dp[i][0] = 1 - # 状态转移 + # State transition for i in range(1, n + 1): for a in range(1, amt + 1): if coins[i - 1] > a: - # 若超过目标金额,则不选硬币 i + # If exceeding the target amount, do not choose coin i dp[i][a] = dp[i - 1][a] else: - # 不选和选硬币 i 这两种方案之和 + # The sum of the two options of not choosing and choosing coin i dp[i][a] = dp[i - 1][a] + dp[i][a - coins[i - 1]] return dp[n][amt] ``` @@ -1675,51 +698,29 @@ When the target amount is $0$, no coins are needed to make up the target amount, === "C++" ```cpp title="coin_change_ii.cpp" - /* 零钱兑换 II:动态规划 */ - int coinChangeIIDP(vector &coins, int amt) { - int n = coins.size(); - // 初始化 dp 表 - vector> dp(n + 1, vector(amt + 1, 0)); - // 初始化首列 - for (int i = 0; i <= n; i++) { - dp[i][0] = 1; - } - // 状态转移 - for (int i = 1; i <= n; i++) { - for (int a = 1; a <= amt; a++) { - 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]; - } + [class]{}-[func]{coinChangeIIDP} ``` === "Java" ```java title="coin_change_ii.java" - /* 零钱兑换 II:动态规划 */ + /* Coin change II: Dynamic programming */ int coinChangeIIDP(int[] coins, int amt) { int n = coins.length; - // 初始化 dp 表 + // Initialize dp table int[][] dp = new int[n + 1][amt + 1]; - // 初始化首列 + // Initialize first column for (int i = 0; i <= n; i++) { dp[i][0] = 1; } - // 状态转移 + // State transition for (int i = 1; i <= n; i++) { for (int a = 1; a <= amt; a++) { if (coins[i - 1] > a) { - // 若超过目标金额,则不选硬币 i + // If exceeding the target amount, do not choose coin i dp[i][a] = dp[i - 1][a]; } else { - // 不选和选硬币 i 这两种方案之和 + // The sum of the two options of not choosing and choosing coin i dp[i][a] = dp[i - 1][a] + dp[i][a - coins[i - 1]]; } } @@ -1731,269 +732,55 @@ When the target amount is $0$, no coins are needed to make up the target amount, === "C#" ```csharp title="coin_change_ii.cs" - /* 零钱兑换 II:动态规划 */ - int CoinChangeIIDP(int[] coins, int amt) { - int n = coins.Length; - // 初始化 dp 表 - int[,] dp = new int[n + 1, amt + 1]; - // 初始化首列 - for (int i = 0; i <= n; i++) { - dp[i, 0] = 1; - } - // 状态转移 - for (int i = 1; i <= n; i++) { - for (int a = 1; a <= amt; a++) { - 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]; - } + [class]{coin_change_ii}-[func]{CoinChangeIIDP} ``` === "Go" ```go title="coin_change_ii.go" - /* 零钱兑换 II:动态规划 */ - func coinChangeIIDP(coins []int, amt int) int { - n := len(coins) - // 初始化 dp 表 - dp := make([][]int, n+1) - for i := 0; i <= n; i++ { - dp[i] = make([]int, amt+1) - } - // 初始化首列 - for i := 0; i <= n; i++ { - dp[i][0] = 1 - } - // 状态转移:其余行和列 - for i := 1; i <= n; i++ { - for a := 1; a <= amt; a++ { - 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] - } + [class]{}-[func]{coinChangeIIDP} ``` === "Swift" ```swift title="coin_change_ii.swift" - /* 零钱兑换 II:动态规划 */ - func coinChangeIIDP(coins: [Int], amt: Int) -> Int { - let n = coins.count - // 初始化 dp 表 - var dp = Array(repeating: Array(repeating: 0, count: amt + 1), count: n + 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] - } + [class]{}-[func]{coinChangeIIDP} ``` === "JS" ```javascript title="coin_change_ii.js" - /* 零钱兑换 II:动态规划 */ - function coinChangeIIDP(coins, amt) { - const n = coins.length; - // 初始化 dp 表 - const dp = Array.from({ length: n + 1 }, () => - Array.from({ length: amt + 1 }, () => 0) - ); - // 初始化首列 - for (let i = 0; i <= n; i++) { - dp[i][0] = 1; - } - // 状态转移 - for (let i = 1; i <= n; i++) { - for (let a = 1; a <= amt; a++) { - 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]; - } + [class]{}-[func]{coinChangeIIDP} ``` === "TS" ```typescript title="coin_change_ii.ts" - /* 零钱兑换 II:动态规划 */ - function coinChangeIIDP(coins: Array, amt: number): number { - const n = coins.length; - // 初始化 dp 表 - const dp = Array.from({ length: n + 1 }, () => - Array.from({ length: amt + 1 }, () => 0) - ); - // 初始化首列 - for (let i = 0; i <= n; i++) { - dp[i][0] = 1; - } - // 状态转移 - for (let i = 1; i <= n; i++) { - for (let a = 1; a <= amt; a++) { - 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]; - } + [class]{}-[func]{coinChangeIIDP} ``` === "Dart" ```dart title="coin_change_ii.dart" - /* 零钱兑换 II:动态规划 */ - int coinChangeIIDP(List coins, int amt) { - int n = coins.length; - // 初始化 dp 表 - List> dp = List.generate(n + 1, (index) => List.filled(amt + 1, 0)); - // 初始化首列 - for (int i = 0; i <= n; i++) { - dp[i][0] = 1; - } - // 状态转移 - for (int i = 1; i <= n; i++) { - for (int a = 1; a <= amt; a++) { - 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]; - } + [class]{}-[func]{coinChangeIIDP} ``` === "Rust" ```rust title="coin_change_ii.rs" - /* 零钱兑换 II:动态规划 */ - fn coin_change_ii_dp(coins: &[i32], amt: usize) -> i32 { - let n = coins.len(); - // 初始化 dp 表 - let mut dp = vec![vec![0; amt + 1]; n + 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 as i32 { - // 若超过目标金额,则不选硬币 i - dp[i][a] = dp[i - 1][a]; - } else { - // 不选和选硬币 i 这两种方案之和 - dp[i][a] = dp[i - 1][a] + dp[i][a - coins[i - 1] as usize]; - } - } - } - dp[n][amt] - } + [class]{}-[func]{coin_change_ii_dp} ``` === "C" ```c title="coin_change_ii.c" - /* 零钱兑换 II:动态规划 */ - int coinChangeIIDP(int coins[], int amt, int coinsSize) { - int n = coinsSize; - // 初始化 dp 表 - int **dp = malloc((n + 1) * sizeof(int *)); - for (int i = 0; i <= n; i++) { - dp[i] = calloc(amt + 1, sizeof(int)); - } - // 初始化首列 - for (int i = 0; i <= n; i++) { - dp[i][0] = 1; - } - // 状态转移 - for (int i = 1; i <= n; i++) { - for (int a = 1; a <= amt; a++) { - 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]]; - } - } - } - int res = dp[n][amt]; - // 释放内存 - for (int i = 0; i <= n; i++) { - free(dp[i]); - } - free(dp); - return res; - } + [class]{}-[func]{coinChangeIIDP} ``` === "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] - } + [class]{}-[func]{coinChangeIIDP} ``` === "Ruby" @@ -2005,36 +792,9 @@ When the target amount is $0$, no coins are needed to make up the target amount, === "Zig" ```zig title="coin_change_ii.zig" - // 零钱兑换 II:动态规划 - fn coinChangeIIDP(comptime coins: []i32, comptime amt: usize) i32 { - comptime var n = coins.len; - // 初始化 dp 表 - var dp = [_][amt + 1]i32{[_]i32{0} ** (amt + 1)} ** (n + 1); - // 初始化首列 - for (0..n + 1) |i| { - dp[i][0] = 1; - } - // 状态转移 - for (1..n + 1) |i| { - for (1..amt + 1) |a| { - if (coins[i - 1] > @as(i32, @intCast(a))) { - // 若超过目标金额,则不选硬币 i - dp[i][a] = dp[i - 1][a]; - } else { - // 不选和选硬币 i 这两种方案的较小值 - dp[i][a] = dp[i - 1][a] + dp[i][a - @as(usize, @intCast(coins[i - 1]))]; - } - } - } - return dp[n][amt]; - } + [class]{}-[func]{coinChangeIIDP} ``` -??? pythontutor "Code Visualization" - -
- - ### 3.   Space optimization The space optimization approach is the same, just remove the coin dimension: @@ -2043,20 +803,20 @@ The space optimization approach is the same, just remove the coin dimension: ```python title="coin_change_ii.py" def coin_change_ii_dp_comp(coins: list[int], amt: int) -> int: - """零钱兑换 II:空间优化后的动态规划""" + """Coin change II: Space-optimized dynamic programming""" n = len(coins) - # 初始化 dp 表 + # Initialize dp table dp = [0] * (amt + 1) dp[0] = 1 - # 状态转移 + # State transition for i in range(1, n + 1): - # 正序遍历 + # Traverse in order for a in range(1, amt + 1): if coins[i - 1] > a: - # 若超过目标金额,则不选硬币 i + # If exceeding the target amount, do not choose coin i dp[a] = dp[a] else: - # 不选和选硬币 i 这两种方案之和 + # The sum of the two options of not choosing and choosing coin i dp[a] = dp[a] + dp[a - coins[i - 1]] return dp[amt] ``` @@ -2064,45 +824,26 @@ The space optimization approach is the same, just remove the coin dimension: === "C++" ```cpp title="coin_change_ii.cpp" - /* 零钱兑换 II:空间优化后的动态规划 */ - int coinChangeIIDPComp(vector &coins, int amt) { - int n = coins.size(); - // 初始化 dp 表 - vector dp(amt + 1, 0); - dp[0] = 1; - // 状态转移 - for (int i = 1; i <= n; i++) { - for (int a = 1; a <= amt; a++) { - if (coins[i - 1] > a) { - // 若超过目标金额,则不选硬币 i - dp[a] = dp[a]; - } else { - // 不选和选硬币 i 这两种方案之和 - dp[a] = dp[a] + dp[a - coins[i - 1]]; - } - } - } - return dp[amt]; - } + [class]{}-[func]{coinChangeIIDPComp} ``` === "Java" ```java title="coin_change_ii.java" - /* 零钱兑换 II:空间优化后的动态规划 */ + /* Coin change II: Space-optimized dynamic programming */ int coinChangeIIDPComp(int[] coins, int amt) { int n = coins.length; - // 初始化 dp 表 + // Initialize dp table int[] dp = new int[amt + 1]; dp[0] = 1; - // 状态转移 + // State transition for (int i = 1; i <= n; i++) { for (int a = 1; a <= amt; a++) { if (coins[i - 1] > a) { - // 若超过目标金额,则不选硬币 i + // If exceeding the target amount, do not choose coin i dp[a] = dp[a]; } else { - // 不选和选硬币 i 这两种方案之和 + // The sum of the two options of not choosing and choosing coin i dp[a] = dp[a] + dp[a - coins[i - 1]]; } } @@ -2114,230 +855,55 @@ The space optimization approach is the same, just remove the coin dimension: === "C#" ```csharp title="coin_change_ii.cs" - /* 零钱兑换 II:空间优化后的动态规划 */ - int CoinChangeIIDPComp(int[] coins, int amt) { - int n = coins.Length; - // 初始化 dp 表 - int[] dp = new int[amt + 1]; - dp[0] = 1; - // 状态转移 - for (int i = 1; i <= n; i++) { - for (int a = 1; a <= amt; a++) { - if (coins[i - 1] > a) { - // 若超过目标金额,则不选硬币 i - dp[a] = dp[a]; - } else { - // 不选和选硬币 i 这两种方案之和 - dp[a] = dp[a] + dp[a - coins[i - 1]]; - } - } - } - return dp[amt]; - } + [class]{coin_change_ii}-[func]{CoinChangeIIDPComp} ``` === "Go" ```go title="coin_change_ii.go" - /* 零钱兑换 II:空间优化后的动态规划 */ - func coinChangeIIDPComp(coins []int, amt int) int { - n := len(coins) - // 初始化 dp 表 - dp := make([]int, amt+1) - dp[0] = 1 - // 状态转移 - for i := 1; i <= n; i++ { - // 正序遍历 - for a := 1; a <= amt; a++ { - if coins[i-1] > a { - // 若超过目标金额,则不选硬币 i - dp[a] = dp[a] - } else { - // 不选和选硬币 i 这两种方案之和 - dp[a] = dp[a] + dp[a-coins[i-1]] - } - } - } - return dp[amt] - } + [class]{}-[func]{coinChangeIIDPComp} ``` === "Swift" ```swift title="coin_change_ii.swift" - /* 零钱兑换 II:空间优化后的动态规划 */ - func coinChangeIIDPComp(coins: [Int], amt: Int) -> Int { - let n = coins.count - // 初始化 dp 表 - var dp = Array(repeating: 0, count: 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] - } + [class]{}-[func]{coinChangeIIDPComp} ``` === "JS" ```javascript title="coin_change_ii.js" - /* 零钱兑换 II:状态压缩后的动态规划 */ - function coinChangeIIDPComp(coins, amt) { - const n = coins.length; - // 初始化 dp 表 - const dp = Array.from({ length: amt + 1 }, () => 0); - dp[0] = 1; - // 状态转移 - for (let i = 1; i <= n; i++) { - for (let a = 1; a <= amt; a++) { - if (coins[i - 1] > a) { - // 若超过目标金额,则不选硬币 i - dp[a] = dp[a]; - } else { - // 不选和选硬币 i 这两种方案之和 - dp[a] = dp[a] + dp[a - coins[i - 1]]; - } - } - } - return dp[amt]; - } + [class]{}-[func]{coinChangeIIDPComp} ``` === "TS" ```typescript title="coin_change_ii.ts" - /* 零钱兑换 II:状态压缩后的动态规划 */ - function coinChangeIIDPComp(coins: Array, amt: number): number { - const n = coins.length; - // 初始化 dp 表 - const dp = Array.from({ length: amt + 1 }, () => 0); - dp[0] = 1; - // 状态转移 - for (let i = 1; i <= n; i++) { - for (let a = 1; a <= amt; a++) { - if (coins[i - 1] > a) { - // 若超过目标金额,则不选硬币 i - dp[a] = dp[a]; - } else { - // 不选和选硬币 i 这两种方案之和 - dp[a] = dp[a] + dp[a - coins[i - 1]]; - } - } - } - return dp[amt]; - } + [class]{}-[func]{coinChangeIIDPComp} ``` === "Dart" ```dart title="coin_change_ii.dart" - /* 零钱兑换 II:空间优化后的动态规划 */ - int coinChangeIIDPComp(List coins, int amt) { - int n = coins.length; - // 初始化 dp 表 - List dp = List.filled(amt + 1, 0); - dp[0] = 1; - // 状态转移 - for (int i = 1; i <= n; i++) { - for (int a = 1; a <= amt; a++) { - if (coins[i - 1] > a) { - // 若超过目标金额,则不选硬币 i - dp[a] = dp[a]; - } else { - // 不选和选硬币 i 这两种方案之和 - dp[a] = dp[a] + dp[a - coins[i - 1]]; - } - } - } - return dp[amt]; - } + [class]{}-[func]{coinChangeIIDPComp} ``` === "Rust" ```rust title="coin_change_ii.rs" - /* 零钱兑换 II:空间优化后的动态规划 */ - fn coin_change_ii_dp_comp(coins: &[i32], amt: usize) -> i32 { - let n = coins.len(); - // 初始化 dp 表 - let mut dp = vec![0; amt + 1]; - dp[0] = 1; - // 状态转移 - for i in 1..=n { - for a in 1..=amt { - if coins[i - 1] > a as i32 { - // 若超过目标金额,则不选硬币 i - dp[a] = dp[a]; - } else { - // 不选和选硬币 i 这两种方案之和 - dp[a] = dp[a] + dp[a - coins[i - 1] as usize]; - } - } - } - dp[amt] - } + [class]{}-[func]{coin_change_ii_dp_comp} ``` === "C" ```c title="coin_change_ii.c" - /* 零钱兑换 II:空间优化后的动态规划 */ - int coinChangeIIDPComp(int coins[], int amt, int coinsSize) { - int n = coinsSize; - // 初始化 dp 表 - int *dp = calloc(amt + 1, sizeof(int)); - dp[0] = 1; - // 状态转移 - for (int i = 1; i <= n; i++) { - for (int a = 1; a <= amt; a++) { - if (coins[i - 1] > a) { - // 若超过目标金额,则不选硬币 i - dp[a] = dp[a]; - } else { - // 不选和选硬币 i 这两种方案之和 - dp[a] = dp[a] + dp[a - coins[i - 1]]; - } - } - } - int res = dp[amt]; - // 释放内存 - free(dp); - return res; - } + [class]{}-[func]{coinChangeIIDPComp} ``` === "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] - } + [class]{}-[func]{coinChangeIIDPComp} ``` === "Ruby" @@ -2349,29 +915,5 @@ The space optimization approach is the same, just remove the coin dimension: === "Zig" ```zig title="coin_change_ii.zig" - // 零钱兑换 II:空间优化后的动态规划 - fn coinChangeIIDPComp(comptime coins: []i32, comptime amt: usize) i32 { - comptime var n = coins.len; - // 初始化 dp 表 - var dp = [_]i32{0} ** (amt + 1); - dp[0] = 1; - // 状态转移 - for (1..n + 1) |i| { - for (1..amt + 1) |a| { - if (coins[i - 1] > @as(i32, @intCast(a))) { - // 若超过目标金额,则不选硬币 i - dp[a] = dp[a]; - } else { - // 不选和选硬币 i 这两种方案的较小值 - dp[a] = dp[a] + dp[a - @as(usize, @intCast(coins[i - 1]))]; - } - } - } - return dp[amt]; - } + [class]{}-[func]{coinChangeIIDPComp} ``` - -??? pythontutor "Code Visualization" - -
- diff --git a/en/docs/chapter_graph/graph_operations.md b/en/docs/chapter_graph/graph_operations.md index c618f4c2e..266868ece 100644 --- a/en/docs/chapter_graph/graph_operations.md +++ b/en/docs/chapter_graph/graph_operations.md @@ -38,249 +38,167 @@ Below is the implementation code for graphs represented using an adjacency matri ```python title="graph_adjacency_matrix.py" class GraphAdjMat: - """基于邻接矩阵实现的无向图类""" + """Undirected graph class based on adjacency matrix""" def __init__(self, vertices: list[int], edges: list[list[int]]): - """构造方法""" - # 顶点列表,元素代表“顶点值”,索引代表“顶点索引” + """Constructor""" + # Vertex list, elements represent "vertex value", index represents "vertex index" self.vertices: list[int] = [] - # 邻接矩阵,行列索引对应“顶点索引” + # Adjacency matrix, row and column indices correspond to "vertex index" self.adj_mat: list[list[int]] = [] - # 添加顶点 + # Add vertex for val in vertices: self.add_vertex(val) - # 添加边 - # 请注意,edges 元素代表顶点索引,即对应 vertices 元素索引 + # Add edge + # Please note, edges elements represent vertex indices, corresponding to vertices elements indices for e in edges: self.add_edge(e[0], e[1]) def size(self) -> int: - """获取顶点数量""" + """Get the number of vertices""" return len(self.vertices) def add_vertex(self, val: int): - """添加顶点""" + """Add vertex""" n = self.size() - # 向顶点列表中添加新顶点的值 + # Add new vertex value to the vertex list self.vertices.append(val) - # 在邻接矩阵中添加一行 + # Add a row to the adjacency matrix new_row = [0] * n self.adj_mat.append(new_row) - # 在邻接矩阵中添加一列 + # Add a column to the adjacency matrix for row in self.adj_mat: row.append(0) def remove_vertex(self, index: int): - """删除顶点""" + """Remove vertex""" if index >= self.size(): raise IndexError() - # 在顶点列表中移除索引 index 的顶点 + # Remove vertex at `index` from the vertex list self.vertices.pop(index) - # 在邻接矩阵中删除索引 index 的行 + # Remove the row at `index` from the adjacency matrix self.adj_mat.pop(index) - # 在邻接矩阵中删除索引 index 的列 + # Remove the column at `index` from the adjacency matrix for row in self.adj_mat: row.pop(index) def add_edge(self, i: int, j: int): - """添加边""" - # 参数 i, j 对应 vertices 元素索引 - # 索引越界与相等处理 + """Add edge""" + # Parameters i, j correspond to vertices element indices + # Handle index out of bounds and equality if i < 0 or j < 0 or i >= self.size() or j >= self.size() or i == j: raise IndexError() - # 在无向图中,邻接矩阵关于主对角线对称,即满足 (i, j) == (j, i) + # In an undirected graph, the adjacency matrix is symmetric about the main diagonal, i.e., satisfies (i, j) == (j, i) self.adj_mat[i][j] = 1 self.adj_mat[j][i] = 1 def remove_edge(self, i: int, j: int): - """删除边""" - # 参数 i, j 对应 vertices 元素索引 - # 索引越界与相等处理 + """Remove edge""" + # Parameters i, j correspond to vertices element indices + # Handle index out of bounds and equality if i < 0 or j < 0 or i >= self.size() or j >= self.size() or i == j: raise IndexError() self.adj_mat[i][j] = 0 self.adj_mat[j][i] = 0 def print(self): - """打印邻接矩阵""" - print("顶点列表 =", self.vertices) - print("邻接矩阵 =") + """Print adjacency matrix""" + print("Vertex list =", self.vertices) + print("Adjacency matrix =") print_matrix(self.adj_mat) ``` === "C++" ```cpp title="graph_adjacency_matrix.cpp" - /* 基于邻接矩阵实现的无向图类 */ - class GraphAdjMat { - vector vertices; // 顶点列表,元素代表“顶点值”,索引代表“顶点索引” - vector> adjMat; // 邻接矩阵,行列索引对应“顶点索引” - - public: - /* 构造方法 */ - GraphAdjMat(const vector &vertices, const vector> &edges) { - // 添加顶点 - for (int val : vertices) { - addVertex(val); - } - // 添加边 - // 请注意,edges 元素代表顶点索引,即对应 vertices 元素索引 - for (const vector &edge : edges) { - addEdge(edge[0], edge[1]); - } - } - - /* 获取顶点数量 */ - int size() const { - return vertices.size(); - } - - /* 添加顶点 */ - void addVertex(int val) { - int n = size(); - // 向顶点列表中添加新顶点的值 - vertices.push_back(val); - // 在邻接矩阵中添加一行 - adjMat.emplace_back(vector(n, 0)); - // 在邻接矩阵中添加一列 - for (vector &row : adjMat) { - row.push_back(0); - } - } - - /* 删除顶点 */ - void removeVertex(int index) { - if (index >= size()) { - throw out_of_range("顶点不存在"); - } - // 在顶点列表中移除索引 index 的顶点 - vertices.erase(vertices.begin() + index); - // 在邻接矩阵中删除索引 index 的行 - adjMat.erase(adjMat.begin() + index); - // 在邻接矩阵中删除索引 index 的列 - for (vector &row : adjMat) { - row.erase(row.begin() + index); - } - } - - /* 添加边 */ - // 参数 i, j 对应 vertices 元素索引 - void addEdge(int i, int j) { - // 索引越界与相等处理 - if (i < 0 || j < 0 || i >= size() || j >= size() || i == j) { - throw out_of_range("顶点不存在"); - } - // 在无向图中,邻接矩阵关于主对角线对称,即满足 (i, j) == (j, i) - adjMat[i][j] = 1; - adjMat[j][i] = 1; - } - - /* 删除边 */ - // 参数 i, j 对应 vertices 元素索引 - void removeEdge(int i, int j) { - // 索引越界与相等处理 - if (i < 0 || j < 0 || i >= size() || j >= size() || i == j) { - throw out_of_range("顶点不存在"); - } - adjMat[i][j] = 0; - adjMat[j][i] = 0; - } - - /* 打印邻接矩阵 */ - void print() { - cout << "顶点列表 = "; - printVector(vertices); - cout << "邻接矩阵 =" << endl; - printVectorMatrix(adjMat); - } - }; + [class]{GraphAdjMat}-[func]{} ``` === "Java" ```java title="graph_adjacency_matrix.java" - /* 基于邻接矩阵实现的无向图类 */ + /* Undirected graph class based on adjacency matrix */ class GraphAdjMat { - List vertices; // 顶点列表,元素代表“顶点值”,索引代表“顶点索引” - List> adjMat; // 邻接矩阵,行列索引对应“顶点索引” + List vertices; // Vertex list, elements represent "vertex value", index represents "vertex index" + List> adjMat; // Adjacency matrix, row and column indices correspond to "vertex index" - /* 构造方法 */ + /* Constructor */ public GraphAdjMat(int[] vertices, int[][] edges) { this.vertices = new ArrayList<>(); this.adjMat = new ArrayList<>(); - // 添加顶点 + // Add vertex for (int val : vertices) { addVertex(val); } - // 添加边 - // 请注意,edges 元素代表顶点索引,即对应 vertices 元素索引 + // Add edge + // Please note, edges elements represent vertex indices, corresponding to vertices elements indices for (int[] e : edges) { addEdge(e[0], e[1]); } } - /* 获取顶点数量 */ + /* Get the number of vertices */ public int size() { return vertices.size(); } - /* 添加顶点 */ + /* Add vertex */ public void addVertex(int val) { int n = size(); - // 向顶点列表中添加新顶点的值 + // Add new vertex value to the vertex list vertices.add(val); - // 在邻接矩阵中添加一行 + // Add a row to the adjacency matrix List newRow = new ArrayList<>(n); for (int j = 0; j < n; j++) { newRow.add(0); } adjMat.add(newRow); - // 在邻接矩阵中添加一列 + // Add a column to the adjacency matrix for (List row : adjMat) { row.add(0); } } - /* 删除顶点 */ + /* Remove vertex */ public void removeVertex(int index) { if (index >= size()) throw new IndexOutOfBoundsException(); - // 在顶点列表中移除索引 index 的顶点 + // Remove vertex at `index` from the vertex list vertices.remove(index); - // 在邻接矩阵中删除索引 index 的行 + // Remove the row at `index` from the adjacency matrix adjMat.remove(index); - // 在邻接矩阵中删除索引 index 的列 + // Remove the column at `index` from the adjacency matrix for (List row : adjMat) { row.remove(index); } } - /* 添加边 */ - // 参数 i, j 对应 vertices 元素索引 + /* Add edge */ + // Parameters i, j correspond to vertices element indices public void addEdge(int i, int j) { - // 索引越界与相等处理 + // Handle index out of bounds and equality if (i < 0 || j < 0 || i >= size() || j >= size() || i == j) throw new IndexOutOfBoundsException(); - // 在无向图中,邻接矩阵关于主对角线对称,即满足 (i, j) == (j, i) + // In an undirected graph, the adjacency matrix is symmetric about the main diagonal, i.e., satisfies (i, j) == (j, i) adjMat.get(i).set(j, 1); adjMat.get(j).set(i, 1); } - /* 删除边 */ - // 参数 i, j 对应 vertices 元素索引 + /* Remove edge */ + // Parameters i, j correspond to vertices element indices public void removeEdge(int i, int j) { - // 索引越界与相等处理 + // Handle index out of bounds and equality if (i < 0 || j < 0 || i >= size() || j >= size() || i == j) throw new IndexOutOfBoundsException(); adjMat.get(i).set(j, 0); adjMat.get(j).set(i, 0); } - /* 打印邻接矩阵 */ + /* Print adjacency matrix */ public void print() { - System.out.print("顶点列表 = "); + System.out.print("Vertex list = "); System.out.println(vertices); - System.out.println("邻接矩阵 ="); + System.out.println("Adjacency matrix ="); PrintUtil.printMatrix(adjMat); } } @@ -289,921 +207,61 @@ Below is the implementation code for graphs represented using an adjacency matri === "C#" ```csharp title="graph_adjacency_matrix.cs" - /* 基于邻接矩阵实现的无向图类 */ - class GraphAdjMat { - List vertices; // 顶点列表,元素代表“顶点值”,索引代表“顶点索引” - List> adjMat; // 邻接矩阵,行列索引对应“顶点索引” - - /* 构造函数 */ - public GraphAdjMat(int[] vertices, int[][] edges) { - this.vertices = []; - this.adjMat = []; - // 添加顶点 - foreach (int val in vertices) { - AddVertex(val); - } - // 添加边 - // 请注意,edges 元素代表顶点索引,即对应 vertices 元素索引 - foreach (int[] e in edges) { - AddEdge(e[0], e[1]); - } - } - - /* 获取顶点数量 */ - int Size() { - return vertices.Count; - } - - /* 添加顶点 */ - public void AddVertex(int val) { - int n = Size(); - // 向顶点列表中添加新顶点的值 - vertices.Add(val); - // 在邻接矩阵中添加一行 - List newRow = new(n); - for (int j = 0; j < n; j++) { - newRow.Add(0); - } - adjMat.Add(newRow); - // 在邻接矩阵中添加一列 - foreach (List row in adjMat) { - row.Add(0); - } - } - - /* 删除顶点 */ - public void RemoveVertex(int index) { - if (index >= Size()) - throw new IndexOutOfRangeException(); - // 在顶点列表中移除索引 index 的顶点 - vertices.RemoveAt(index); - // 在邻接矩阵中删除索引 index 的行 - adjMat.RemoveAt(index); - // 在邻接矩阵中删除索引 index 的列 - foreach (List row in adjMat) { - row.RemoveAt(index); - } - } - - /* 添加边 */ - // 参数 i, j 对应 vertices 元素索引 - public void AddEdge(int i, int j) { - // 索引越界与相等处理 - if (i < 0 || j < 0 || i >= Size() || j >= Size() || i == j) - throw new IndexOutOfRangeException(); - // 在无向图中,邻接矩阵关于主对角线对称,即满足 (i, j) == (j, i) - adjMat[i][j] = 1; - adjMat[j][i] = 1; - } - - /* 删除边 */ - // 参数 i, j 对应 vertices 元素索引 - public void RemoveEdge(int i, int j) { - // 索引越界与相等处理 - if (i < 0 || j < 0 || i >= Size() || j >= Size() || i == j) - throw new IndexOutOfRangeException(); - adjMat[i][j] = 0; - adjMat[j][i] = 0; - } - - /* 打印邻接矩阵 */ - public void Print() { - Console.Write("顶点列表 = "); - PrintUtil.PrintList(vertices); - Console.WriteLine("邻接矩阵 ="); - PrintUtil.PrintMatrix(adjMat); - } - } + [class]{GraphAdjMat}-[func]{} ``` === "Go" ```go title="graph_adjacency_matrix.go" - /* 基于邻接矩阵实现的无向图类 */ - type graphAdjMat struct { - // 顶点列表,元素代表“顶点值”,索引代表“顶点索引” - vertices []int - // 邻接矩阵,行列索引对应“顶点索引” - adjMat [][]int - } - - /* 构造函数 */ - func newGraphAdjMat(vertices []int, edges [][]int) *graphAdjMat { - // 添加顶点 - n := len(vertices) - adjMat := make([][]int, n) - for i := range adjMat { - adjMat[i] = make([]int, n) - } - // 初始化图 - g := &graphAdjMat{ - vertices: vertices, - adjMat: adjMat, - } - // 添加边 - // 请注意,edges 元素代表顶点索引,即对应 vertices 元素索引 - for i := range edges { - g.addEdge(edges[i][0], edges[i][1]) - } - return g - } - - /* 获取顶点数量 */ - func (g *graphAdjMat) size() int { - return len(g.vertices) - } - - /* 添加顶点 */ - func (g *graphAdjMat) addVertex(val int) { - n := g.size() - // 向顶点列表中添加新顶点的值 - g.vertices = append(g.vertices, val) - // 在邻接矩阵中添加一行 - newRow := make([]int, n) - g.adjMat = append(g.adjMat, newRow) - // 在邻接矩阵中添加一列 - for i := range g.adjMat { - g.adjMat[i] = append(g.adjMat[i], 0) - } - } - - /* 删除顶点 */ - func (g *graphAdjMat) removeVertex(index int) { - if index >= g.size() { - return - } - // 在顶点列表中移除索引 index 的顶点 - g.vertices = append(g.vertices[:index], g.vertices[index+1:]...) - // 在邻接矩阵中删除索引 index 的行 - g.adjMat = append(g.adjMat[:index], g.adjMat[index+1:]...) - // 在邻接矩阵中删除索引 index 的列 - for i := range g.adjMat { - g.adjMat[i] = append(g.adjMat[i][:index], g.adjMat[i][index+1:]...) - } - } - - /* 添加边 */ - // 参数 i, j 对应 vertices 元素索引 - func (g *graphAdjMat) addEdge(i, j int) { - // 索引越界与相等处理 - if i < 0 || j < 0 || i >= g.size() || j >= g.size() || i == j { - fmt.Errorf("%s", "Index Out Of Bounds Exception") - } - // 在无向图中,邻接矩阵关于主对角线对称,即满足 (i, j) == (j, i) - g.adjMat[i][j] = 1 - g.adjMat[j][i] = 1 - } - - /* 删除边 */ - // 参数 i, j 对应 vertices 元素索引 - func (g *graphAdjMat) removeEdge(i, j int) { - // 索引越界与相等处理 - if i < 0 || j < 0 || i >= g.size() || j >= g.size() || i == j { - fmt.Errorf("%s", "Index Out Of Bounds Exception") - } - g.adjMat[i][j] = 0 - g.adjMat[j][i] = 0 - } - - /* 打印邻接矩阵 */ - func (g *graphAdjMat) print() { - fmt.Printf("\t顶点列表 = %v\n", g.vertices) - fmt.Printf("\t邻接矩阵 = \n") - for i := range g.adjMat { - fmt.Printf("\t\t\t%v\n", g.adjMat[i]) - } - } + [class]{graphAdjMat}-[func]{} ``` === "Swift" ```swift title="graph_adjacency_matrix.swift" - /* 基于邻接矩阵实现的无向图类 */ - class GraphAdjMat { - private var vertices: [Int] // 顶点列表,元素代表“顶点值”,索引代表“顶点索引” - private var adjMat: [[Int]] // 邻接矩阵,行列索引对应“顶点索引” - - /* 构造方法 */ - init(vertices: [Int], edges: [[Int]]) { - self.vertices = [] - adjMat = [] - // 添加顶点 - for val in vertices { - addVertex(val: val) - } - // 添加边 - // 请注意,edges 元素代表顶点索引,即对应 vertices 元素索引 - for e in edges { - addEdge(i: e[0], j: e[1]) - } - } - - /* 获取顶点数量 */ - func size() -> Int { - vertices.count - } - - /* 添加顶点 */ - func addVertex(val: Int) { - let n = size() - // 向顶点列表中添加新顶点的值 - vertices.append(val) - // 在邻接矩阵中添加一行 - let newRow = Array(repeating: 0, count: n) - adjMat.append(newRow) - // 在邻接矩阵中添加一列 - for i in adjMat.indices { - adjMat[i].append(0) - } - } - - /* 删除顶点 */ - func removeVertex(index: Int) { - if index >= size() { - fatalError("越界") - } - // 在顶点列表中移除索引 index 的顶点 - vertices.remove(at: index) - // 在邻接矩阵中删除索引 index 的行 - adjMat.remove(at: index) - // 在邻接矩阵中删除索引 index 的列 - for i in adjMat.indices { - adjMat[i].remove(at: index) - } - } - - /* 添加边 */ - // 参数 i, j 对应 vertices 元素索引 - func addEdge(i: Int, j: Int) { - // 索引越界与相等处理 - if i < 0 || j < 0 || i >= size() || j >= size() || i == j { - fatalError("越界") - } - // 在无向图中,邻接矩阵关于主对角线对称,即满足 (i, j) == (j, i) - adjMat[i][j] = 1 - adjMat[j][i] = 1 - } - - /* 删除边 */ - // 参数 i, j 对应 vertices 元素索引 - func removeEdge(i: Int, j: Int) { - // 索引越界与相等处理 - if i < 0 || j < 0 || i >= size() || j >= size() || i == j { - fatalError("越界") - } - adjMat[i][j] = 0 - adjMat[j][i] = 0 - } - - /* 打印邻接矩阵 */ - func print() { - Swift.print("顶点列表 = ", terminator: "") - Swift.print(vertices) - Swift.print("邻接矩阵 =") - PrintUtil.printMatrix(matrix: adjMat) - } - } + [class]{GraphAdjMat}-[func]{} ``` === "JS" ```javascript title="graph_adjacency_matrix.js" - /* 基于邻接矩阵实现的无向图类 */ - class GraphAdjMat { - vertices; // 顶点列表,元素代表“顶点值”,索引代表“顶点索引” - adjMat; // 邻接矩阵,行列索引对应“顶点索引” - - /* 构造函数 */ - constructor(vertices, edges) { - this.vertices = []; - this.adjMat = []; - // 添加顶点 - for (const val of vertices) { - this.addVertex(val); - } - // 添加边 - // 请注意,edges 元素代表顶点索引,即对应 vertices 元素索引 - for (const e of edges) { - this.addEdge(e[0], e[1]); - } - } - - /* 获取顶点数量 */ - size() { - return this.vertices.length; - } - - /* 添加顶点 */ - addVertex(val) { - const n = this.size(); - // 向顶点列表中添加新顶点的值 - this.vertices.push(val); - // 在邻接矩阵中添加一行 - const newRow = []; - for (let j = 0; j < n; j++) { - newRow.push(0); - } - this.adjMat.push(newRow); - // 在邻接矩阵中添加一列 - for (const row of this.adjMat) { - row.push(0); - } - } - - /* 删除顶点 */ - removeVertex(index) { - if (index >= this.size()) { - throw new RangeError('Index Out Of Bounds Exception'); - } - // 在顶点列表中移除索引 index 的顶点 - this.vertices.splice(index, 1); - - // 在邻接矩阵中删除索引 index 的行 - this.adjMat.splice(index, 1); - // 在邻接矩阵中删除索引 index 的列 - for (const row of this.adjMat) { - row.splice(index, 1); - } - } - - /* 添加边 */ - // 参数 i, j 对应 vertices 元素索引 - addEdge(i, j) { - // 索引越界与相等处理 - if (i < 0 || j < 0 || i >= this.size() || j >= this.size() || i === j) { - throw new RangeError('Index Out Of Bounds Exception'); - } - // 在无向图中,邻接矩阵关于主对角线对称,即满足 (i, j) === (j, i) - this.adjMat[i][j] = 1; - this.adjMat[j][i] = 1; - } - - /* 删除边 */ - // 参数 i, j 对应 vertices 元素索引 - removeEdge(i, j) { - // 索引越界与相等处理 - if (i < 0 || j < 0 || i >= this.size() || j >= this.size() || i === j) { - throw new RangeError('Index Out Of Bounds Exception'); - } - this.adjMat[i][j] = 0; - this.adjMat[j][i] = 0; - } - - /* 打印邻接矩阵 */ - print() { - console.log('顶点列表 = ', this.vertices); - console.log('邻接矩阵 =', this.adjMat); - } - } + [class]{GraphAdjMat}-[func]{} ``` === "TS" ```typescript title="graph_adjacency_matrix.ts" - /* 基于邻接矩阵实现的无向图类 */ - class GraphAdjMat { - vertices: number[]; // 顶点列表,元素代表“顶点值”,索引代表“顶点索引” - adjMat: number[][]; // 邻接矩阵,行列索引对应“顶点索引” - - /* 构造函数 */ - constructor(vertices: number[], edges: number[][]) { - this.vertices = []; - this.adjMat = []; - // 添加顶点 - for (const val of vertices) { - this.addVertex(val); - } - // 添加边 - // 请注意,edges 元素代表顶点索引,即对应 vertices 元素索引 - for (const e of edges) { - this.addEdge(e[0], e[1]); - } - } - - /* 获取顶点数量 */ - size(): number { - return this.vertices.length; - } - - /* 添加顶点 */ - addVertex(val: number): void { - const n: number = this.size(); - // 向顶点列表中添加新顶点的值 - this.vertices.push(val); - // 在邻接矩阵中添加一行 - const newRow: number[] = []; - for (let j: number = 0; j < n; j++) { - newRow.push(0); - } - this.adjMat.push(newRow); - // 在邻接矩阵中添加一列 - for (const row of this.adjMat) { - row.push(0); - } - } - - /* 删除顶点 */ - removeVertex(index: number): void { - if (index >= this.size()) { - throw new RangeError('Index Out Of Bounds Exception'); - } - // 在顶点列表中移除索引 index 的顶点 - this.vertices.splice(index, 1); - - // 在邻接矩阵中删除索引 index 的行 - this.adjMat.splice(index, 1); - // 在邻接矩阵中删除索引 index 的列 - for (const row of this.adjMat) { - row.splice(index, 1); - } - } - - /* 添加边 */ - // 参数 i, j 对应 vertices 元素索引 - addEdge(i: number, j: number): void { - // 索引越界与相等处理 - if (i < 0 || j < 0 || i >= this.size() || j >= this.size() || i === j) { - throw new RangeError('Index Out Of Bounds Exception'); - } - // 在无向图中,邻接矩阵关于主对角线对称,即满足 (i, j) === (j, i) - this.adjMat[i][j] = 1; - this.adjMat[j][i] = 1; - } - - /* 删除边 */ - // 参数 i, j 对应 vertices 元素索引 - removeEdge(i: number, j: number): void { - // 索引越界与相等处理 - if (i < 0 || j < 0 || i >= this.size() || j >= this.size() || i === j) { - throw new RangeError('Index Out Of Bounds Exception'); - } - this.adjMat[i][j] = 0; - this.adjMat[j][i] = 0; - } - - /* 打印邻接矩阵 */ - print(): void { - console.log('顶点列表 = ', this.vertices); - console.log('邻接矩阵 =', this.adjMat); - } - } + [class]{GraphAdjMat}-[func]{} ``` === "Dart" ```dart title="graph_adjacency_matrix.dart" - /* 基于邻接矩阵实现的无向图类 */ - class GraphAdjMat { - List vertices = []; // 顶点元素,元素代表“顶点值”,索引代表“顶点索引” - List> adjMat = []; //邻接矩阵,行列索引对应“顶点索引” - - /* 构造方法 */ - GraphAdjMat(List vertices, List> edges) { - this.vertices = []; - this.adjMat = []; - // 添加顶点 - for (int val in vertices) { - addVertex(val); - } - // 添加边 - // 请注意,edges 元素代表顶点索引,即对应 vertices 元素索引 - for (List e in edges) { - addEdge(e[0], e[1]); - } - } - - /* 获取顶点数量 */ - int size() { - return vertices.length; - } - - /* 添加顶点 */ - void addVertex(int val) { - int n = size(); - // 向顶点列表中添加新顶点的值 - vertices.add(val); - // 在邻接矩阵中添加一行 - List newRow = List.filled(n, 0, growable: true); - adjMat.add(newRow); - // 在邻接矩阵中添加一列 - for (List row in adjMat) { - row.add(0); - } - } - - /* 删除顶点 */ - void removeVertex(int index) { - if (index >= size()) { - throw IndexError; - } - // 在顶点列表中移除索引 index 的顶点 - vertices.removeAt(index); - // 在邻接矩阵中删除索引 index 的行 - adjMat.removeAt(index); - // 在邻接矩阵中删除索引 index 的列 - for (List row in adjMat) { - row.removeAt(index); - } - } - - /* 添加边 */ - // 参数 i, j 对应 vertices 元素索引 - void addEdge(int i, int j) { - // 索引越界与相等处理 - if (i < 0 || j < 0 || i >= size() || j >= size() || i == j) { - throw IndexError; - } - // 在无向图中,邻接矩阵关于主对角线对称,即满足 (i, j) == (j, i) - adjMat[i][j] = 1; - adjMat[j][i] = 1; - } - - /* 删除边 */ - // 参数 i, j 对应 vertices 元素索引 - void removeEdge(int i, int j) { - // 索引越界与相等处理 - if (i < 0 || j < 0 || i >= size() || j >= size() || i == j) { - throw IndexError; - } - adjMat[i][j] = 0; - adjMat[j][i] = 0; - } - - /* 打印邻接矩阵 */ - void printAdjMat() { - print("顶点列表 = $vertices"); - print("邻接矩阵 = "); - printMatrix(adjMat); - } - } + [class]{GraphAdjMat}-[func]{} ``` === "Rust" ```rust title="graph_adjacency_matrix.rs" - /* 基于邻接矩阵实现的无向图类型 */ - pub struct GraphAdjMat { - // 顶点列表,元素代表“顶点值”,索引代表“顶点索引” - pub vertices: Vec, - // 邻接矩阵,行列索引对应“顶点索引” - pub adj_mat: Vec>, - } - - impl GraphAdjMat { - /* 构造方法 */ - pub fn new(vertices: Vec, edges: Vec<[usize; 2]>) -> Self { - let mut graph = GraphAdjMat { - vertices: vec![], - adj_mat: vec![], - }; - // 添加顶点 - for val in vertices { - graph.add_vertex(val); - } - // 添加边 - // 请注意,edges 元素代表顶点索引,即对应 vertices 元素索引 - for edge in edges { - graph.add_edge(edge[0], edge[1]) - } - - graph - } - - /* 获取顶点数量 */ - pub fn size(&self) -> usize { - self.vertices.len() - } - - /* 添加顶点 */ - pub fn add_vertex(&mut self, val: i32) { - let n = self.size(); - // 向顶点列表中添加新顶点的值 - self.vertices.push(val); - // 在邻接矩阵中添加一行 - self.adj_mat.push(vec![0; n]); - // 在邻接矩阵中添加一列 - for row in &mut self.adj_mat { - row.push(0); - } - } - - /* 删除顶点 */ - pub fn remove_vertex(&mut self, index: usize) { - if index >= self.size() { - panic!("index error") - } - // 在顶点列表中移除索引 index 的顶点 - self.vertices.remove(index); - // 在邻接矩阵中删除索引 index 的行 - self.adj_mat.remove(index); - // 在邻接矩阵中删除索引 index 的列 - for row in &mut self.adj_mat { - row.remove(index); - } - } - - /* 添加边 */ - pub fn add_edge(&mut self, i: usize, j: usize) { - // 参数 i, j 对应 vertices 元素索引 - // 索引越界与相等处理 - if i >= self.size() || j >= self.size() || i == j { - panic!("index error") - } - // 在无向图中,邻接矩阵关于主对角线对称,即满足 (i, j) == (j, i) - self.adj_mat[i][j] = 1; - self.adj_mat[j][i] = 1; - } - - /* 删除边 */ - // 参数 i, j 对应 vertices 元素索引 - pub fn remove_edge(&mut self, i: usize, j: usize) { - // 参数 i, j 对应 vertices 元素索引 - // 索引越界与相等处理 - if i >= self.size() || j >= self.size() || i == j { - panic!("index error") - } - self.adj_mat[i][j] = 0; - self.adj_mat[j][i] = 0; - } - - /* 打印邻接矩阵 */ - pub fn print(&self) { - println!("顶点列表 = {:?}", self.vertices); - println!("邻接矩阵 ="); - println!("["); - for row in &self.adj_mat { - println!(" {:?},", row); - } - println!("]") - } - } + [class]{GraphAdjMat}-[func]{} ``` === "C" ```c title="graph_adjacency_matrix.c" - /* 基于邻接矩阵实现的无向图结构体 */ - typedef struct { - int vertices[MAX_SIZE]; - int adjMat[MAX_SIZE][MAX_SIZE]; - int size; - } GraphAdjMat; - - /* 构造函数 */ - GraphAdjMat *newGraphAdjMat() { - GraphAdjMat *graph = (GraphAdjMat *)malloc(sizeof(GraphAdjMat)); - graph->size = 0; - for (int i = 0; i < MAX_SIZE; i++) { - for (int j = 0; j < MAX_SIZE; j++) { - graph->adjMat[i][j] = 0; - } - } - return graph; - } - - /* 析构函数 */ - void delGraphAdjMat(GraphAdjMat *graph) { - free(graph); - } - - /* 添加顶点 */ - void addVertex(GraphAdjMat *graph, int val) { - if (graph->size == MAX_SIZE) { - fprintf(stderr, "图的顶点数量已达最大值\n"); - return; - } - // 添加第 n 个顶点,并将第 n 行和列置零 - int n = graph->size; - graph->vertices[n] = val; - for (int i = 0; i <= n; i++) { - graph->adjMat[n][i] = graph->adjMat[i][n] = 0; - } - graph->size++; - } - - /* 删除顶点 */ - void removeVertex(GraphAdjMat *graph, int index) { - if (index < 0 || index >= graph->size) { - fprintf(stderr, "顶点索引越界\n"); - return; - } - // 在顶点列表中移除索引 index 的顶点 - for (int i = index; i < graph->size - 1; i++) { - graph->vertices[i] = graph->vertices[i + 1]; - } - // 在邻接矩阵中删除索引 index 的行 - for (int i = index; i < graph->size - 1; i++) { - for (int j = 0; j < graph->size; j++) { - graph->adjMat[i][j] = graph->adjMat[i + 1][j]; - } - } - // 在邻接矩阵中删除索引 index 的列 - for (int i = 0; i < graph->size; i++) { - for (int j = index; j < graph->size - 1; j++) { - graph->adjMat[i][j] = graph->adjMat[i][j + 1]; - } - } - graph->size--; - } - - /* 添加边 */ - // 参数 i, j 对应 vertices 元素索引 - void addEdge(GraphAdjMat *graph, int i, int j) { - if (i < 0 || j < 0 || i >= graph->size || j >= graph->size || i == j) { - fprintf(stderr, "边索引越界或相等\n"); - return; - } - graph->adjMat[i][j] = 1; - graph->adjMat[j][i] = 1; - } - - /* 删除边 */ - // 参数 i, j 对应 vertices 元素索引 - void removeEdge(GraphAdjMat *graph, int i, int j) { - if (i < 0 || j < 0 || i >= graph->size || j >= graph->size || i == j) { - fprintf(stderr, "边索引越界或相等\n"); - return; - } - graph->adjMat[i][j] = 0; - graph->adjMat[j][i] = 0; - } - - /* 打印邻接矩阵 */ - void printGraphAdjMat(GraphAdjMat *graph) { - printf("顶点列表 = "); - printArray(graph->vertices, graph->size); - printf("邻接矩阵 =\n"); - for (int i = 0; i < graph->size; i++) { - printArray(graph->adjMat[i], graph->size); - } - } + [class]{GraphAdjMat}-[func]{} ``` === "Kotlin" ```kotlin title="graph_adjacency_matrix.kt" - /* 基于邻接矩阵实现的无向图类 */ - class GraphAdjMat(vertices: IntArray, edges: Array) { - val vertices = mutableListOf() // 顶点列表,元素代表“顶点值”,索引代表“顶点索引” - val adjMat = mutableListOf>() // 邻接矩阵,行列索引对应“顶点索引” - - /* 构造方法 */ - 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(_val: Int) { - val n = size() - // 向顶点列表中添加新顶点的值 - vertices.add(_val) - // 在邻接矩阵中添加一行 - val newRow = 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 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 IndexOutOfBoundsException() - adjMat[i][j] = 0 - adjMat[j][i] = 0 - } - - /* 打印邻接矩阵 */ - fun print() { - print("顶点列表 = ") - println(vertices) - println("邻接矩阵 =") - printMatrix(adjMat) - } - } + [class]{GraphAdjMat}-[func]{} ``` === "Ruby" ```ruby title="graph_adjacency_matrix.rb" - ### 基于邻接矩阵实现的无向图类 ### - class GraphAdjMat - def initialize(vertices, edges) - ### 构造方法 ### - # 顶点列表,元素代表“顶点值”,索引代表“顶点索引” - @vertices = [] - # 邻接矩阵,行列索引对应“顶点索引” - @adj_mat = [] - # 添加顶点 - vertices.each { |val| add_vertex(val) } - # 添加边 - # 请注意,edges 元素代表顶点索引,即对应 vertices 元素索引 - edges.each { |e| add_edge(e[0], e[1]) } - end - - ### 获取顶点数量 ### - def size - @vertices.length - end - - ### 添加顶点 ### - def add_vertex(val) - n = size - # 向顶点列表中添加新顶点的值 - @vertices << val - # 在邻接矩阵中添加一行 - new_row = Array.new(n, 0) - @adj_mat << new_row - # 在邻接矩阵中添加一列 - @adj_mat.each { |row| row << 0 } - end - - ### 删除顶点 ### - def remove_vertex(index) - raise IndexError if index >= size - - # 在顶点列表中移除索引 index 的顶点 - @vertices.delete_at(index) - # 在邻接矩阵中删除索引 index 的行 - @adj_mat.delete_at(index) - # 在邻接矩阵中删除索引 index 的列 - @adj_mat.each { |row| row.delete_at(index) } - end - - ### 添加边 ### - def add_edge(i, j) - # 参数 i, j 对应 vertices 元素索引 - # 索引越界与相等处理 - if i < 0 || j < 0 || i >= size || j >= size || i == j - raise IndexError - end - # 在无向图中,邻接矩阵关于主对角线对称,即满足 (i, j) == (j, i) - @adj_mat[i][j] = 1 - @adj_mat[j][i] = 1 - end - - ### 删除边 ### - def remove_edge(i, j) - # 参数 i, j 对应 vertices 元素索引 - # 索引越界与相等处理 - if i < 0 || j < 0 || i >= size || j >= size || i == j - raise IndexError - end - @adj_mat[i][j] = 0 - @adj_mat[j][i] = 0 - end - - ### 打印邻接矩阵 ### - def __print__ - puts "顶点列表 = #{@vertices}" - puts '邻接矩阵 =' - print_matrix(@adj_mat) - end - end + [class]{GraphAdjMat}-[func]{} ``` === "Zig" @@ -1212,11 +270,6 @@ Below is the implementation code for graphs represented using an adjacency matri [class]{GraphAdjMat}-[func]{} ``` -??? pythontutor "Code Visualization" - -
- - ## 9.2.2   Implementation based on adjacency list Given an undirected graph with a total of $n$ vertices and $m$ edges, the various operations can be implemented as shown in Figure 9-8. @@ -1255,59 +308,59 @@ Additionally, we use the `Vertex` class to represent vertices in the adjacency l ```python title="graph_adjacency_list.py" class GraphAdjList: - """基于邻接表实现的无向图类""" + """Undirected graph class based on adjacency list""" def __init__(self, edges: list[list[Vertex]]): - """构造方法""" - # 邻接表,key:顶点,value:该顶点的所有邻接顶点 + """Constructor""" + # Adjacency list, key: vertex, value: all adjacent vertices of that vertex self.adj_list = dict[Vertex, list[Vertex]]() - # 添加所有顶点和边 + # Add all vertices and edges for edge in edges: self.add_vertex(edge[0]) self.add_vertex(edge[1]) self.add_edge(edge[0], edge[1]) def size(self) -> int: - """获取顶点数量""" + """Get the number of vertices""" return len(self.adj_list) def add_edge(self, vet1: Vertex, vet2: Vertex): - """添加边""" + """Add edge""" if vet1 not in self.adj_list or vet2 not in self.adj_list or vet1 == vet2: raise ValueError() - # 添加边 vet1 - vet2 + # Add edge vet1 - vet2 self.adj_list[vet1].append(vet2) self.adj_list[vet2].append(vet1) def remove_edge(self, vet1: Vertex, vet2: Vertex): - """删除边""" + """Remove edge""" if vet1 not in self.adj_list or vet2 not in self.adj_list or vet1 == vet2: raise ValueError() - # 删除边 vet1 - vet2 + # Remove edge vet1 - vet2 self.adj_list[vet1].remove(vet2) self.adj_list[vet2].remove(vet1) def add_vertex(self, vet: Vertex): - """添加顶点""" + """Add vertex""" if vet in self.adj_list: return - # 在邻接表中添加一个新链表 + # Add a new linked list to the adjacency list self.adj_list[vet] = [] def remove_vertex(self, vet: Vertex): - """删除顶点""" + """Remove vertex""" if vet not in self.adj_list: raise ValueError() - # 在邻接表中删除顶点 vet 对应的链表 + # Remove the vertex vet's corresponding linked list from the adjacency list self.adj_list.pop(vet) - # 遍历其他顶点的链表,删除所有包含 vet 的边 + # Traverse other vertices' linked lists, removing all edges containing vet for vertex in self.adj_list: if vet in self.adj_list[vertex]: self.adj_list[vertex].remove(vet) def print(self): - """打印邻接表""" - print("邻接表 =") + """Print the adjacency list""" + print("Adjacency list =") for vertex in self.adj_list: tmp = [v.val for v in self.adj_list[vertex]] print(f"{vertex.val}: {tmp},") @@ -1316,100 +369,21 @@ Additionally, we use the `Vertex` class to represent vertices in the adjacency l === "C++" ```cpp title="graph_adjacency_list.cpp" - /* 基于邻接表实现的无向图类 */ - class GraphAdjList { - public: - // 邻接表,key:顶点,value:该顶点的所有邻接顶点 - unordered_map> adjList; - - /* 在 vector 中删除指定节点 */ - void remove(vector &vec, Vertex *vet) { - for (int i = 0; i < vec.size(); i++) { - if (vec[i] == vet) { - vec.erase(vec.begin() + i); - break; - } - } - } - - /* 构造方法 */ - GraphAdjList(const vector> &edges) { - // 添加所有顶点和边 - for (const vector &edge : edges) { - addVertex(edge[0]); - addVertex(edge[1]); - addEdge(edge[0], edge[1]); - } - } - - /* 获取顶点数量 */ - int size() { - return adjList.size(); - } - - /* 添加边 */ - void addEdge(Vertex *vet1, Vertex *vet2) { - if (!adjList.count(vet1) || !adjList.count(vet2) || vet1 == vet2) - throw invalid_argument("不存在顶点"); - // 添加边 vet1 - vet2 - adjList[vet1].push_back(vet2); - adjList[vet2].push_back(vet1); - } - - /* 删除边 */ - void removeEdge(Vertex *vet1, Vertex *vet2) { - if (!adjList.count(vet1) || !adjList.count(vet2) || vet1 == vet2) - throw invalid_argument("不存在顶点"); - // 删除边 vet1 - vet2 - remove(adjList[vet1], vet2); - remove(adjList[vet2], vet1); - } - - /* 添加顶点 */ - void addVertex(Vertex *vet) { - if (adjList.count(vet)) - return; - // 在邻接表中添加一个新链表 - adjList[vet] = vector(); - } - - /* 删除顶点 */ - void removeVertex(Vertex *vet) { - if (!adjList.count(vet)) - throw invalid_argument("不存在顶点"); - // 在邻接表中删除顶点 vet 对应的链表 - adjList.erase(vet); - // 遍历其他顶点的链表,删除所有包含 vet 的边 - for (auto &adj : adjList) { - remove(adj.second, vet); - } - } - - /* 打印邻接表 */ - void print() { - cout << "邻接表 =" << endl; - for (auto &adj : adjList) { - const auto &key = adj.first; - const auto &vec = adj.second; - cout << key->val << ": "; - printVector(vetsToVals(vec)); - } - } - }; + [class]{GraphAdjList}-[func]{} ``` === "Java" ```java title="graph_adjacency_list.java" - /* 基于邻接表实现的无向图类 */ + /* Undirected graph class based on adjacency list */ class GraphAdjList { - // 邻接表,key:顶点,value:该顶点的所有邻接顶点 + // Adjacency list, key: vertex, value: all adjacent vertices of that vertex Map> adjList; - /* 构造方法 */ + /* Constructor */ public GraphAdjList(Vertex[][] edges) { this.adjList = new HashMap<>(); - // 添加所有顶点和边 + // Add all vertices and edges for (Vertex[] edge : edges) { addVertex(edge[0]); addVertex(edge[1]); @@ -1417,52 +391,52 @@ Additionally, we use the `Vertex` class to represent vertices in the adjacency l } } - /* 获取顶点数量 */ + /* Get the number of vertices */ public int size() { return adjList.size(); } - /* 添加边 */ + /* Add edge */ public void addEdge(Vertex vet1, Vertex vet2) { if (!adjList.containsKey(vet1) || !adjList.containsKey(vet2) || vet1 == vet2) throw new IllegalArgumentException(); - // 添加边 vet1 - vet2 + // Add edge vet1 - vet2 adjList.get(vet1).add(vet2); adjList.get(vet2).add(vet1); } - /* 删除边 */ + /* Remove edge */ public void removeEdge(Vertex vet1, Vertex vet2) { if (!adjList.containsKey(vet1) || !adjList.containsKey(vet2) || vet1 == vet2) throw new IllegalArgumentException(); - // 删除边 vet1 - vet2 + // Remove edge vet1 - vet2 adjList.get(vet1).remove(vet2); adjList.get(vet2).remove(vet1); } - /* 添加顶点 */ + /* Add vertex */ public void addVertex(Vertex vet) { if (adjList.containsKey(vet)) return; - // 在邻接表中添加一个新链表 + // Add a new linked list to the adjacency list adjList.put(vet, new ArrayList<>()); } - /* 删除顶点 */ + /* Remove vertex */ public void removeVertex(Vertex vet) { if (!adjList.containsKey(vet)) throw new IllegalArgumentException(); - // 在邻接表中删除顶点 vet 对应的链表 + // Remove the vertex vet's corresponding linked list from the adjacency list adjList.remove(vet); - // 遍历其他顶点的链表,删除所有包含 vet 的边 + // Traverse other vertices' linked lists, removing all edges containing vet for (List list : adjList.values()) { list.remove(vet); } } - /* 打印邻接表 */ + /* Print the adjacency list */ public void print() { - System.out.println("邻接表 ="); + System.out.println("Adjacency list ="); for (Map.Entry> pair : adjList.entrySet()) { List tmp = new ArrayList<>(); for (Vertex vertex : pair.getValue()) @@ -1476,903 +450,63 @@ Additionally, we use the `Vertex` class to represent vertices in the adjacency l === "C#" ```csharp title="graph_adjacency_list.cs" - /* 基于邻接表实现的无向图类 */ - class GraphAdjList { - // 邻接表,key:顶点,value:该顶点的所有邻接顶点 - public Dictionary> adjList; - - /* 构造函数 */ - public GraphAdjList(Vertex[][] edges) { - adjList = []; - // 添加所有顶点和边 - foreach (Vertex[] edge in edges) { - AddVertex(edge[0]); - AddVertex(edge[1]); - AddEdge(edge[0], edge[1]); - } - } - - /* 获取顶点数量 */ - int Size() { - return adjList.Count; - } - - /* 添加边 */ - public void AddEdge(Vertex vet1, Vertex vet2) { - if (!adjList.ContainsKey(vet1) || !adjList.ContainsKey(vet2) || vet1 == vet2) - throw new InvalidOperationException(); - // 添加边 vet1 - vet2 - adjList[vet1].Add(vet2); - adjList[vet2].Add(vet1); - } - - /* 删除边 */ - public void RemoveEdge(Vertex vet1, Vertex vet2) { - if (!adjList.ContainsKey(vet1) || !adjList.ContainsKey(vet2) || vet1 == vet2) - throw new InvalidOperationException(); - // 删除边 vet1 - vet2 - adjList[vet1].Remove(vet2); - adjList[vet2].Remove(vet1); - } - - /* 添加顶点 */ - public void AddVertex(Vertex vet) { - if (adjList.ContainsKey(vet)) - return; - // 在邻接表中添加一个新链表 - adjList.Add(vet, []); - } - - /* 删除顶点 */ - public void RemoveVertex(Vertex vet) { - if (!adjList.ContainsKey(vet)) - throw new InvalidOperationException(); - // 在邻接表中删除顶点 vet 对应的链表 - adjList.Remove(vet); - // 遍历其他顶点的链表,删除所有包含 vet 的边 - foreach (List list in adjList.Values) { - list.Remove(vet); - } - } - - /* 打印邻接表 */ - public void Print() { - Console.WriteLine("邻接表 ="); - foreach (KeyValuePair> pair in adjList) { - List tmp = []; - foreach (Vertex vertex in pair.Value) - tmp.Add(vertex.val); - Console.WriteLine(pair.Key.val + ": [" + string.Join(", ", tmp) + "],"); - } - } - } + [class]{GraphAdjList}-[func]{} ``` === "Go" ```go title="graph_adjacency_list.go" - /* 基于邻接表实现的无向图类 */ - type graphAdjList struct { - // 邻接表,key:顶点,value:该顶点的所有邻接顶点 - adjList map[Vertex][]Vertex - } - - /* 构造函数 */ - func newGraphAdjList(edges [][]Vertex) *graphAdjList { - g := &graphAdjList{ - adjList: make(map[Vertex][]Vertex), - } - // 添加所有顶点和边 - for _, edge := range edges { - g.addVertex(edge[0]) - g.addVertex(edge[1]) - g.addEdge(edge[0], edge[1]) - } - return g - } - - /* 获取顶点数量 */ - func (g *graphAdjList) size() int { - return len(g.adjList) - } - - /* 添加边 */ - func (g *graphAdjList) addEdge(vet1 Vertex, vet2 Vertex) { - _, ok1 := g.adjList[vet1] - _, ok2 := g.adjList[vet2] - if !ok1 || !ok2 || vet1 == vet2 { - panic("error") - } - // 添加边 vet1 - vet2, 添加匿名 struct{}, - g.adjList[vet1] = append(g.adjList[vet1], vet2) - g.adjList[vet2] = append(g.adjList[vet2], vet1) - } - - /* 删除边 */ - func (g *graphAdjList) removeEdge(vet1 Vertex, vet2 Vertex) { - _, ok1 := g.adjList[vet1] - _, ok2 := g.adjList[vet2] - if !ok1 || !ok2 || vet1 == vet2 { - panic("error") - } - // 删除边 vet1 - vet2 - g.adjList[vet1] = DeleteSliceElms(g.adjList[vet1], vet2) - g.adjList[vet2] = DeleteSliceElms(g.adjList[vet2], vet1) - } - - /* 添加顶点 */ - func (g *graphAdjList) addVertex(vet Vertex) { - _, ok := g.adjList[vet] - if ok { - return - } - // 在邻接表中添加一个新链表 - g.adjList[vet] = make([]Vertex, 0) - } - - /* 删除顶点 */ - func (g *graphAdjList) removeVertex(vet Vertex) { - _, ok := g.adjList[vet] - if !ok { - panic("error") - } - // 在邻接表中删除顶点 vet 对应的链表 - delete(g.adjList, vet) - // 遍历其他顶点的链表,删除所有包含 vet 的边 - for v, list := range g.adjList { - g.adjList[v] = DeleteSliceElms(list, vet) - } - } - - /* 打印邻接表 */ - func (g *graphAdjList) print() { - var builder strings.Builder - fmt.Printf("邻接表 = \n") - for k, v := range g.adjList { - builder.WriteString("\t\t" + strconv.Itoa(k.Val) + ": ") - for _, vet := range v { - builder.WriteString(strconv.Itoa(vet.Val) + " ") - } - fmt.Println(builder.String()) - builder.Reset() - } - } + [class]{graphAdjList}-[func]{} ``` === "Swift" ```swift title="graph_adjacency_list.swift" - /* 基于邻接表实现的无向图类 */ - class GraphAdjList { - // 邻接表,key:顶点,value:该顶点的所有邻接顶点 - public private(set) var adjList: [Vertex: [Vertex]] - - /* 构造方法 */ - public init(edges: [[Vertex]]) { - adjList = [:] - // 添加所有顶点和边 - for edge in edges { - addVertex(vet: edge[0]) - addVertex(vet: edge[1]) - addEdge(vet1: edge[0], vet2: edge[1]) - } - } - - /* 获取顶点数量 */ - public func size() -> Int { - adjList.count - } - - /* 添加边 */ - public func addEdge(vet1: Vertex, vet2: Vertex) { - if adjList[vet1] == nil || adjList[vet2] == nil || vet1 == vet2 { - fatalError("参数错误") - } - // 添加边 vet1 - vet2 - adjList[vet1]?.append(vet2) - adjList[vet2]?.append(vet1) - } - - /* 删除边 */ - public func removeEdge(vet1: Vertex, vet2: Vertex) { - if adjList[vet1] == nil || adjList[vet2] == nil || vet1 == vet2 { - fatalError("参数错误") - } - // 删除边 vet1 - vet2 - adjList[vet1]?.removeAll { $0 == vet2 } - adjList[vet2]?.removeAll { $0 == vet1 } - } - - /* 添加顶点 */ - public func addVertex(vet: Vertex) { - if adjList[vet] != nil { - return - } - // 在邻接表中添加一个新链表 - adjList[vet] = [] - } - - /* 删除顶点 */ - public func removeVertex(vet: Vertex) { - if adjList[vet] == nil { - fatalError("参数错误") - } - // 在邻接表中删除顶点 vet 对应的链表 - adjList.removeValue(forKey: vet) - // 遍历其他顶点的链表,删除所有包含 vet 的边 - for key in adjList.keys { - adjList[key]?.removeAll { $0 == vet } - } - } - - /* 打印邻接表 */ - public func print() { - Swift.print("邻接表 =") - for (vertex, list) in adjList { - let list = list.map { $0.val } - Swift.print("\(vertex.val): \(list),") - } - } - } + [class]{GraphAdjList}-[func]{} ``` === "JS" ```javascript title="graph_adjacency_list.js" - /* 基于邻接表实现的无向图类 */ - class GraphAdjList { - // 邻接表,key:顶点,value:该顶点的所有邻接顶点 - adjList; - - /* 构造方法 */ - constructor(edges) { - this.adjList = new Map(); - // 添加所有顶点和边 - for (const edge of edges) { - this.addVertex(edge[0]); - this.addVertex(edge[1]); - this.addEdge(edge[0], edge[1]); - } - } - - /* 获取顶点数量 */ - size() { - return this.adjList.size; - } - - /* 添加边 */ - addEdge(vet1, vet2) { - if ( - !this.adjList.has(vet1) || - !this.adjList.has(vet2) || - vet1 === vet2 - ) { - throw new Error('Illegal Argument Exception'); - } - // 添加边 vet1 - vet2 - this.adjList.get(vet1).push(vet2); - this.adjList.get(vet2).push(vet1); - } - - /* 删除边 */ - removeEdge(vet1, vet2) { - if ( - !this.adjList.has(vet1) || - !this.adjList.has(vet2) || - vet1 === vet2 - ) { - throw new Error('Illegal Argument Exception'); - } - // 删除边 vet1 - vet2 - this.adjList.get(vet1).splice(this.adjList.get(vet1).indexOf(vet2), 1); - this.adjList.get(vet2).splice(this.adjList.get(vet2).indexOf(vet1), 1); - } - - /* 添加顶点 */ - addVertex(vet) { - if (this.adjList.has(vet)) return; - // 在邻接表中添加一个新链表 - this.adjList.set(vet, []); - } - - /* 删除顶点 */ - removeVertex(vet) { - if (!this.adjList.has(vet)) { - throw new Error('Illegal Argument Exception'); - } - // 在邻接表中删除顶点 vet 对应的链表 - this.adjList.delete(vet); - // 遍历其他顶点的链表,删除所有包含 vet 的边 - for (const set of this.adjList.values()) { - const index = set.indexOf(vet); - if (index > -1) { - set.splice(index, 1); - } - } - } - - /* 打印邻接表 */ - print() { - console.log('邻接表 ='); - for (const [key, value] of this.adjList) { - const tmp = []; - for (const vertex of value) { - tmp.push(vertex.val); - } - console.log(key.val + ': ' + tmp.join()); - } - } - } + [class]{GraphAdjList}-[func]{} ``` === "TS" ```typescript title="graph_adjacency_list.ts" - /* 基于邻接表实现的无向图类 */ - class GraphAdjList { - // 邻接表,key:顶点,value:该顶点的所有邻接顶点 - adjList: Map; - - /* 构造方法 */ - constructor(edges: Vertex[][]) { - this.adjList = new Map(); - // 添加所有顶点和边 - for (const edge of edges) { - this.addVertex(edge[0]); - this.addVertex(edge[1]); - this.addEdge(edge[0], edge[1]); - } - } - - /* 获取顶点数量 */ - size(): number { - return this.adjList.size; - } - - /* 添加边 */ - addEdge(vet1: Vertex, vet2: Vertex): void { - if ( - !this.adjList.has(vet1) || - !this.adjList.has(vet2) || - vet1 === vet2 - ) { - throw new Error('Illegal Argument Exception'); - } - // 添加边 vet1 - vet2 - this.adjList.get(vet1).push(vet2); - this.adjList.get(vet2).push(vet1); - } - - /* 删除边 */ - removeEdge(vet1: Vertex, vet2: Vertex): void { - if ( - !this.adjList.has(vet1) || - !this.adjList.has(vet2) || - vet1 === vet2 - ) { - throw new Error('Illegal Argument Exception'); - } - // 删除边 vet1 - vet2 - this.adjList.get(vet1).splice(this.adjList.get(vet1).indexOf(vet2), 1); - this.adjList.get(vet2).splice(this.adjList.get(vet2).indexOf(vet1), 1); - } - - /* 添加顶点 */ - addVertex(vet: Vertex): void { - if (this.adjList.has(vet)) return; - // 在邻接表中添加一个新链表 - this.adjList.set(vet, []); - } - - /* 删除顶点 */ - removeVertex(vet: Vertex): void { - if (!this.adjList.has(vet)) { - throw new Error('Illegal Argument Exception'); - } - // 在邻接表中删除顶点 vet 对应的链表 - this.adjList.delete(vet); - // 遍历其他顶点的链表,删除所有包含 vet 的边 - for (const set of this.adjList.values()) { - const index: number = set.indexOf(vet); - if (index > -1) { - set.splice(index, 1); - } - } - } - - /* 打印邻接表 */ - print(): void { - console.log('邻接表 ='); - for (const [key, value] of this.adjList.entries()) { - const tmp = []; - for (const vertex of value) { - tmp.push(vertex.val); - } - console.log(key.val + ': ' + tmp.join()); - } - } - } + [class]{GraphAdjList}-[func]{} ``` === "Dart" ```dart title="graph_adjacency_list.dart" - /* 基于邻接表实现的无向图类 */ - class GraphAdjList { - // 邻接表,key:顶点,value:该顶点的所有邻接顶点 - Map> adjList = {}; - - /* 构造方法 */ - GraphAdjList(List> edges) { - for (List edge in edges) { - addVertex(edge[0]); - addVertex(edge[1]); - addEdge(edge[0], edge[1]); - } - } - - /* 获取顶点数量 */ - int size() { - return adjList.length; - } - - /* 添加边 */ - void addEdge(Vertex vet1, Vertex vet2) { - if (!adjList.containsKey(vet1) || - !adjList.containsKey(vet2) || - vet1 == vet2) { - throw ArgumentError; - } - // 添加边 vet1 - vet2 - adjList[vet1]!.add(vet2); - adjList[vet2]!.add(vet1); - } - - /* 删除边 */ - void removeEdge(Vertex vet1, Vertex vet2) { - if (!adjList.containsKey(vet1) || - !adjList.containsKey(vet2) || - vet1 == vet2) { - throw ArgumentError; - } - // 删除边 vet1 - vet2 - adjList[vet1]!.remove(vet2); - adjList[vet2]!.remove(vet1); - } - - /* 添加顶点 */ - void addVertex(Vertex vet) { - if (adjList.containsKey(vet)) return; - // 在邻接表中添加一个新链表 - adjList[vet] = []; - } - - /* 删除顶点 */ - void removeVertex(Vertex vet) { - if (!adjList.containsKey(vet)) { - throw ArgumentError; - } - // 在邻接表中删除顶点 vet 对应的链表 - adjList.remove(vet); - // 遍历其他顶点的链表,删除所有包含 vet 的边 - adjList.forEach((key, value) { - value.remove(vet); - }); - } - - /* 打印邻接表 */ - void printAdjList() { - print("邻接表 ="); - adjList.forEach((key, value) { - List tmp = []; - for (Vertex vertex in value) { - tmp.add(vertex.val); - } - print("${key.val}: $tmp,"); - }); - } - } + [class]{GraphAdjList}-[func]{} ``` === "Rust" ```rust title="graph_adjacency_list.rs" - /* 基于邻接表实现的无向图类型 */ - pub struct GraphAdjList { - // 邻接表,key:顶点,value:该顶点的所有邻接顶点 - pub adj_list: HashMap>, - } - - impl GraphAdjList { - /* 构造方法 */ - pub fn new(edges: Vec<[Vertex; 2]>) -> Self { - let mut graph = GraphAdjList { - adj_list: HashMap::new(), - }; - // 添加所有顶点和边 - for edge in edges { - graph.add_vertex(edge[0]); - graph.add_vertex(edge[1]); - graph.add_edge(edge[0], edge[1]); - } - - graph - } - - /* 获取顶点数量 */ - #[allow(unused)] - pub fn size(&self) -> usize { - self.adj_list.len() - } - - /* 添加边 */ - pub fn add_edge(&mut self, vet1: Vertex, vet2: Vertex) { - if !self.adj_list.contains_key(&vet1) || !self.adj_list.contains_key(&vet2) || vet1 == vet2 - { - panic!("value error"); - } - // 添加边 vet1 - vet2 - self.adj_list.get_mut(&vet1).unwrap().push(vet2); - self.adj_list.get_mut(&vet2).unwrap().push(vet1); - } - - /* 删除边 */ - #[allow(unused)] - pub fn remove_edge(&mut self, vet1: Vertex, vet2: Vertex) { - if !self.adj_list.contains_key(&vet1) || !self.adj_list.contains_key(&vet2) || vet1 == vet2 - { - panic!("value error"); - } - // 删除边 vet1 - vet2 - self.adj_list - .get_mut(&vet1) - .unwrap() - .retain(|&vet| vet != vet2); - self.adj_list - .get_mut(&vet2) - .unwrap() - .retain(|&vet| vet != vet1); - } - - /* 添加顶点 */ - pub fn add_vertex(&mut self, vet: Vertex) { - if self.adj_list.contains_key(&vet) { - return; - } - // 在邻接表中添加一个新链表 - self.adj_list.insert(vet, vec![]); - } - - /* 删除顶点 */ - #[allow(unused)] - pub fn remove_vertex(&mut self, vet: Vertex) { - if !self.adj_list.contains_key(&vet) { - panic!("value error"); - } - // 在邻接表中删除顶点 vet 对应的链表 - self.adj_list.remove(&vet); - // 遍历其他顶点的链表,删除所有包含 vet 的边 - for list in self.adj_list.values_mut() { - list.retain(|&v| v != vet); - } - } - - /* 打印邻接表 */ - pub fn print(&self) { - println!("邻接表 ="); - for (vertex, list) in &self.adj_list { - let list = list.iter().map(|vertex| vertex.val).collect::>(); - println!("{}: {:?},", vertex.val, list); - } - } - } + [class]{GraphAdjList}-[func]{} ``` === "C" ```c title="graph_adjacency_list.c" - /* 节点结构体 */ - typedef struct AdjListNode { - Vertex *vertex; // 顶点 - struct AdjListNode *next; // 后继节点 - } AdjListNode; + [class]{AdjListNode}-[func]{} - /* 查找顶点对应的节点 */ - AdjListNode *findNode(GraphAdjList *graph, Vertex *vet) { - for (int i = 0; i < graph->size; i++) { - if (graph->heads[i]->vertex == vet) { - return graph->heads[i]; - } - } - return NULL; - } - - /* 添加边辅助函数 */ - void addEdgeHelper(AdjListNode *head, Vertex *vet) { - AdjListNode *node = (AdjListNode *)malloc(sizeof(AdjListNode)); - node->vertex = vet; - // 头插法 - node->next = head->next; - head->next = node; - } - - /* 删除边辅助函数 */ - void removeEdgeHelper(AdjListNode *head, Vertex *vet) { - AdjListNode *pre = head; - AdjListNode *cur = head->next; - // 在链表中搜索 vet 对应节点 - while (cur != NULL && cur->vertex != vet) { - pre = cur; - cur = cur->next; - } - if (cur == NULL) - return; - // 将 vet 对应节点从链表中删除 - pre->next = cur->next; - // 释放内存 - free(cur); - } - - /* 基于邻接表实现的无向图类 */ - typedef struct { - AdjListNode *heads[MAX_SIZE]; // 节点数组 - int size; // 节点数量 - } GraphAdjList; - - /* 构造函数 */ - GraphAdjList *newGraphAdjList() { - GraphAdjList *graph = (GraphAdjList *)malloc(sizeof(GraphAdjList)); - if (!graph) { - return NULL; - } - graph->size = 0; - for (int i = 0; i < MAX_SIZE; i++) { - graph->heads[i] = NULL; - } - return graph; - } - - /* 析构函数 */ - void delGraphAdjList(GraphAdjList *graph) { - for (int i = 0; i < graph->size; i++) { - AdjListNode *cur = graph->heads[i]; - while (cur != NULL) { - AdjListNode *next = cur->next; - if (cur != graph->heads[i]) { - free(cur); - } - cur = next; - } - free(graph->heads[i]->vertex); - free(graph->heads[i]); - } - free(graph); - } - - /* 查找顶点对应的节点 */ - AdjListNode *findNode(GraphAdjList *graph, Vertex *vet) { - for (int i = 0; i < graph->size; i++) { - if (graph->heads[i]->vertex == vet) { - return graph->heads[i]; - } - } - return NULL; - } - - /* 添加边 */ - void addEdge(GraphAdjList *graph, Vertex *vet1, Vertex *vet2) { - AdjListNode *head1 = findNode(graph, vet1); - AdjListNode *head2 = findNode(graph, vet2); - assert(head1 != NULL && head2 != NULL && head1 != head2); - // 添加边 vet1 - vet2 - addEdgeHelper(head1, vet2); - addEdgeHelper(head2, vet1); - } - - /* 删除边 */ - void removeEdge(GraphAdjList *graph, Vertex *vet1, Vertex *vet2) { - AdjListNode *head1 = findNode(graph, vet1); - AdjListNode *head2 = findNode(graph, vet2); - assert(head1 != NULL && head2 != NULL); - // 删除边 vet1 - vet2 - removeEdgeHelper(head1, head2->vertex); - removeEdgeHelper(head2, head1->vertex); - } - - /* 添加顶点 */ - void addVertex(GraphAdjList *graph, Vertex *vet) { - assert(graph != NULL && graph->size < MAX_SIZE); - AdjListNode *head = (AdjListNode *)malloc(sizeof(AdjListNode)); - head->vertex = vet; - head->next = NULL; - // 在邻接表中添加一个新链表 - graph->heads[graph->size++] = head; - } - - /* 删除顶点 */ - void removeVertex(GraphAdjList *graph, Vertex *vet) { - AdjListNode *node = findNode(graph, vet); - assert(node != NULL); - // 在邻接表中删除顶点 vet 对应的链表 - AdjListNode *cur = node, *pre = NULL; - while (cur) { - pre = cur; - cur = cur->next; - free(pre); - } - // 遍历其他顶点的链表,删除所有包含 vet 的边 - for (int i = 0; i < graph->size; i++) { - cur = graph->heads[i]; - pre = NULL; - while (cur) { - pre = cur; - cur = cur->next; - if (cur && cur->vertex == vet) { - pre->next = cur->next; - free(cur); - break; - } - } - } - // 将该顶点之后的顶点向前移动,以填补空缺 - int i; - for (i = 0; i < graph->size; i++) { - if (graph->heads[i] == node) - break; - } - for (int j = i; j < graph->size - 1; j++) { - graph->heads[j] = graph->heads[j + 1]; - } - graph->size--; - free(vet); - } + [class]{GraphAdjList}-[func]{} ``` === "Kotlin" ```kotlin title="graph_adjacency_list.kt" - /* 基于邻接表实现的无向图类 */ - class GraphAdjList(edges: Array>) { - // 邻接表,key:顶点,value:该顶点的所有邻接顶点 - val adjList = 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 = mutableListOf() - for (vertex in pair.value) { - tmp.add(vertex._val) - } - println("${pair.key._val}: $tmp,") - } - } - } + [class]{GraphAdjList}-[func]{} ``` === "Ruby" ```ruby title="graph_adjacency_list.rb" - ### 基于邻接表实现的无向图类 ### - class GraphAdjList - attr_reader :adj_list - - ### 构造方法 ### - def initialize(edges) - # 邻接表,key:顶点,value:该顶点的所有邻接顶点 - @adj_list = {} - # 添加所有顶点和边 - for edge in edges - add_vertex(edge[0]) - add_vertex(edge[1]) - add_edge(edge[0], edge[1]) - end - end - - ### 获取顶点数量 ### - def size - @adj_list.length - end - - ### 添加边 ### - def add_edge(vet1, vet2) - raise ArgumentError if !@adj_list.include?(vet1) || !@adj_list.include?(vet2) - - @adj_list[vet1] << vet2 - @adj_list[vet2] << vet1 - end - - ### 删除边 ### - def remove_edge(vet1, vet2) - raise ArgumentError if !@adj_list.include?(vet1) || !@adj_list.include?(vet2) - - # 删除边 vet1 - vet2 - @adj_list[vet1].delete(vet2) - @adj_list[vet2].delete(vet1) - end - - ### 添加顶点 ### - def add_vertex(vet) - return if @adj_list.include?(vet) - - # 在邻接表中添加一个新链表 - @adj_list[vet] = [] - end - - ### 删除顶点 ### - def remove_vertex(vet) - raise ArgumentError unless @adj_list.include?(vet) - - # 在邻接表中删除顶点 vet 对应的链表 - @adj_list.delete(vet) - # 遍历其他顶点的链表,删除所有包含 vet 的边 - for vertex in @adj_list - @adj_list[vertex.first].delete(vet) if @adj_list[vertex.first].include?(vet) - end - end - - ### 打印邻接表 ### - def __print__ - puts '邻接表 =' - for vertex in @adj_list - tmp = @adj_list[vertex.first].map { |v| v.val } - puts "#{vertex.first.val}: #{tmp}," - end - end - end + [class]{GraphAdjList}-[func]{} ``` === "Zig" @@ -2381,11 +515,6 @@ Additionally, we use the `Vertex` class to represent vertices in the adjacency l [class]{GraphAdjList}-[func]{} ``` -??? pythontutor "Code Visualization" - -
- - ## 9.2.3   Efficiency comparison Assuming there are $n$ vertices and $m$ edges in the graph, Table 9-2 compares the time efficiency and space efficiency of the adjacency matrix and adjacency list. diff --git a/en/docs/chapter_graph/graph_traversal.md b/en/docs/chapter_graph/graph_traversal.md index d352446aa..3e38419f8 100644 --- a/en/docs/chapter_graph/graph_traversal.md +++ b/en/docs/chapter_graph/graph_traversal.md @@ -30,86 +30,61 @@ To prevent revisiting vertices, we use a hash set `visited` to record which node ```python title="graph_bfs.py" def graph_bfs(graph: GraphAdjList, start_vet: Vertex) -> list[Vertex]: - """广度优先遍历""" - # 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 - # 顶点遍历序列 + """Breadth-first traversal""" + # Use adjacency list to represent the graph, to obtain all adjacent vertices of a specified vertex + # Vertex traversal sequence res = [] - # 哈希集合,用于记录已被访问过的顶点 + # Hash set, used to record visited vertices visited = set[Vertex]([start_vet]) - # 队列用于实现 BFS + # Queue used to implement BFS que = deque[Vertex]([start_vet]) - # 以顶点 vet 为起点,循环直至访问完所有顶点 + # Starting from vertex vet, loop until all vertices are visited while len(que) > 0: - vet = que.popleft() # 队首顶点出队 - res.append(vet) # 记录访问顶点 - # 遍历该顶点的所有邻接顶点 + vet = que.popleft() # Dequeue the vertex at the head of the queue + res.append(vet) # Record visited vertex + # Traverse all adjacent vertices of that vertex for adj_vet in graph.adj_list[vet]: if adj_vet in visited: - continue # 跳过已被访问的顶点 - que.append(adj_vet) # 只入队未访问的顶点 - visited.add(adj_vet) # 标记该顶点已被访问 - # 返回顶点遍历序列 + continue # Skip already visited vertices + que.append(adj_vet) # Only enqueue unvisited vertices + visited.add(adj_vet) # Mark the vertex as visited + # Return the vertex traversal sequence return res ``` === "C++" ```cpp title="graph_bfs.cpp" - /* 广度优先遍历 */ - // 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 - vector graphBFS(GraphAdjList &graph, Vertex *startVet) { - // 顶点遍历序列 - vector res; - // 哈希集合,用于记录已被访问过的顶点 - unordered_set visited = {startVet}; - // 队列用于实现 BFS - queue que; - que.push(startVet); - // 以顶点 vet 为起点,循环直至访问完所有顶点 - while (!que.empty()) { - Vertex *vet = que.front(); - que.pop(); // 队首顶点出队 - res.push_back(vet); // 记录访问顶点 - // 遍历该顶点的所有邻接顶点 - for (auto adjVet : graph.adjList[vet]) { - if (visited.count(adjVet)) - continue; // 跳过已被访问的顶点 - que.push(adjVet); // 只入队未访问的顶点 - visited.emplace(adjVet); // 标记该顶点已被访问 - } - } - // 返回顶点遍历序列 - return res; - } + [class]{}-[func]{graphBFS} ``` === "Java" ```java title="graph_bfs.java" - /* 广度优先遍历 */ - // 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 + /* Breadth-first traversal */ + // Use adjacency list to represent the graph, to obtain all adjacent vertices of a specified vertex List graphBFS(GraphAdjList graph, Vertex startVet) { - // 顶点遍历序列 + // Vertex traversal sequence List res = new ArrayList<>(); - // 哈希集合,用于记录已被访问过的顶点 + // Hash set, used to record visited vertices Set visited = new HashSet<>(); visited.add(startVet); - // 队列用于实现 BFS + // Queue used to implement BFS Queue que = new LinkedList<>(); que.offer(startVet); - // 以顶点 vet 为起点,循环直至访问完所有顶点 + // Starting from vertex vet, loop until all vertices are visited while (!que.isEmpty()) { - Vertex vet = que.poll(); // 队首顶点出队 - res.add(vet); // 记录访问顶点 - // 遍历该顶点的所有邻接顶点 + Vertex vet = que.poll(); // Dequeue the vertex at the head of the queue + res.add(vet); // Record visited vertex + // Traverse all adjacent vertices of that vertex for (Vertex adjVet : graph.adjList.get(vet)) { if (visited.contains(adjVet)) - continue; // 跳过已被访问的顶点 - que.offer(adjVet); // 只入队未访问的顶点 - visited.add(adjVet); // 标记该顶点已被访问 + continue; // Skip already visited vertices + que.offer(adjVet); // Only enqueue unvisited vertices + visited.add(adjVet); // Mark the vertex as visited } } - // 返回顶点遍历序列 + // Return the vertex traversal sequence return res; } ``` @@ -117,359 +92,65 @@ To prevent revisiting vertices, we use a hash set `visited` to record which node === "C#" ```csharp title="graph_bfs.cs" - /* 广度优先遍历 */ - // 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 - List GraphBFS(GraphAdjList graph, Vertex startVet) { - // 顶点遍历序列 - List res = []; - // 哈希集合,用于记录已被访问过的顶点 - HashSet visited = [startVet]; - // 队列用于实现 BFS - Queue que = new(); - que.Enqueue(startVet); - // 以顶点 vet 为起点,循环直至访问完所有顶点 - while (que.Count > 0) { - Vertex vet = que.Dequeue(); // 队首顶点出队 - res.Add(vet); // 记录访问顶点 - foreach (Vertex adjVet in graph.adjList[vet]) { - if (visited.Contains(adjVet)) { - continue; // 跳过已被访问的顶点 - } - que.Enqueue(adjVet); // 只入队未访问的顶点 - visited.Add(adjVet); // 标记该顶点已被访问 - } - } - - // 返回顶点遍历序列 - return res; - } + [class]{graph_bfs}-[func]{GraphBFS} ``` === "Go" ```go title="graph_bfs.go" - /* 广度优先遍历 */ - // 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 - func graphBFS(g *graphAdjList, startVet Vertex) []Vertex { - // 顶点遍历序列 - res := make([]Vertex, 0) - // 哈希集合,用于记录已被访问过的顶点 - visited := make(map[Vertex]struct{}) - visited[startVet] = struct{}{} - // 队列用于实现 BFS, 使用切片模拟队列 - queue := make([]Vertex, 0) - queue = append(queue, startVet) - // 以顶点 vet 为起点,循环直至访问完所有顶点 - for len(queue) > 0 { - // 队首顶点出队 - vet := queue[0] - queue = queue[1:] - // 记录访问顶点 - res = append(res, vet) - // 遍历该顶点的所有邻接顶点 - for _, adjVet := range g.adjList[vet] { - _, isExist := visited[adjVet] - // 只入队未访问的顶点 - if !isExist { - queue = append(queue, adjVet) - visited[adjVet] = struct{}{} - } - } - } - // 返回顶点遍历序列 - return res - } + [class]{}-[func]{graphBFS} ``` === "Swift" ```swift title="graph_bfs.swift" - /* 广度优先遍历 */ - // 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 - func graphBFS(graph: GraphAdjList, startVet: Vertex) -> [Vertex] { - // 顶点遍历序列 - var res: [Vertex] = [] - // 哈希集合,用于记录已被访问过的顶点 - var visited: Set = [startVet] - // 队列用于实现 BFS - var que: [Vertex] = [startVet] - // 以顶点 vet 为起点,循环直至访问完所有顶点 - while !que.isEmpty { - let vet = que.removeFirst() // 队首顶点出队 - res.append(vet) // 记录访问顶点 - // 遍历该顶点的所有邻接顶点 - for adjVet in graph.adjList[vet] ?? [] { - if visited.contains(adjVet) { - continue // 跳过已被访问的顶点 - } - que.append(adjVet) // 只入队未访问的顶点 - visited.insert(adjVet) // 标记该顶点已被访问 - } - } - // 返回顶点遍历序列 - return res - } + [class]{}-[func]{graphBFS} ``` === "JS" ```javascript title="graph_bfs.js" - /* 广度优先遍历 */ - // 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 - function graphBFS(graph, startVet) { - // 顶点遍历序列 - const res = []; - // 哈希集合,用于记录已被访问过的顶点 - const visited = new Set(); - visited.add(startVet); - // 队列用于实现 BFS - const que = [startVet]; - // 以顶点 vet 为起点,循环直至访问完所有顶点 - while (que.length) { - const vet = que.shift(); // 队首顶点出队 - res.push(vet); // 记录访问顶点 - // 遍历该顶点的所有邻接顶点 - for (const adjVet of graph.adjList.get(vet) ?? []) { - if (visited.has(adjVet)) { - continue; // 跳过已被访问的顶点 - } - que.push(adjVet); // 只入队未访问的顶点 - visited.add(adjVet); // 标记该顶点已被访问 - } - } - // 返回顶点遍历序列 - return res; - } + [class]{}-[func]{graphBFS} ``` === "TS" ```typescript title="graph_bfs.ts" - /* 广度优先遍历 */ - // 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 - function graphBFS(graph: GraphAdjList, startVet: Vertex): Vertex[] { - // 顶点遍历序列 - const res: Vertex[] = []; - // 哈希集合,用于记录已被访问过的顶点 - const visited: Set = new Set(); - visited.add(startVet); - // 队列用于实现 BFS - const que = [startVet]; - // 以顶点 vet 为起点,循环直至访问完所有顶点 - while (que.length) { - const vet = que.shift(); // 队首顶点出队 - res.push(vet); // 记录访问顶点 - // 遍历该顶点的所有邻接顶点 - for (const adjVet of graph.adjList.get(vet) ?? []) { - if (visited.has(adjVet)) { - continue; // 跳过已被访问的顶点 - } - que.push(adjVet); // 只入队未访问 - visited.add(adjVet); // 标记该顶点已被访问 - } - } - // 返回顶点遍历序列 - return res; - } + [class]{}-[func]{graphBFS} ``` === "Dart" ```dart title="graph_bfs.dart" - /* 广度优先遍历 */ - List graphBFS(GraphAdjList graph, Vertex startVet) { - // 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 - // 顶点遍历序列 - List res = []; - // 哈希集合,用于记录已被访问过的顶点 - Set visited = {}; - visited.add(startVet); - // 队列用于实现 BFS - Queue que = Queue(); - que.add(startVet); - // 以顶点 vet 为起点,循环直至访问完所有顶点 - while (que.isNotEmpty) { - Vertex vet = que.removeFirst(); // 队首顶点出队 - res.add(vet); // 记录访问顶点 - // 遍历该顶点的所有邻接顶点 - for (Vertex adjVet in graph.adjList[vet]!) { - if (visited.contains(adjVet)) { - continue; // 跳过已被访问的顶点 - } - que.add(adjVet); // 只入队未访问的顶点 - visited.add(adjVet); // 标记该顶点已被访问 - } - } - // 返回顶点遍历序列 - return res; - } + [class]{}-[func]{graphBFS} ``` === "Rust" ```rust title="graph_bfs.rs" - /* 广度优先遍历 */ - // 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 - fn graph_bfs(graph: GraphAdjList, start_vet: Vertex) -> Vec { - // 顶点遍历序列 - let mut res = vec![]; - // 哈希集合,用于记录已被访问过的顶点 - let mut visited = HashSet::new(); - visited.insert(start_vet); - // 队列用于实现 BFS - let mut que = VecDeque::new(); - que.push_back(start_vet); - // 以顶点 vet 为起点,循环直至访问完所有顶点 - while !que.is_empty() { - let vet = que.pop_front().unwrap(); // 队首顶点出队 - res.push(vet); // 记录访问顶点 - - // 遍历该顶点的所有邻接顶点 - if let Some(adj_vets) = graph.adj_list.get(&vet) { - for &adj_vet in adj_vets { - if visited.contains(&adj_vet) { - continue; // 跳过已被访问的顶点 - } - que.push_back(adj_vet); // 只入队未访问的顶点 - visited.insert(adj_vet); // 标记该顶点已被访问 - } - } - } - // 返回顶点遍历序列 - res - } + [class]{}-[func]{graph_bfs} ``` === "C" ```c title="graph_bfs.c" - /* 节点队列结构体 */ - typedef struct { - Vertex *vertices[MAX_SIZE]; - int front, rear, size; - } Queue; + [class]{Queue}-[func]{} - /* 构造函数 */ - Queue *newQueue() { - Queue *q = (Queue *)malloc(sizeof(Queue)); - q->front = q->rear = q->size = 0; - return q; - } + [class]{}-[func]{isVisited} - /* 判断队列是否为空 */ - int isEmpty(Queue *q) { - return q->size == 0; - } - - /* 入队操作 */ - void enqueue(Queue *q, Vertex *vet) { - q->vertices[q->rear] = vet; - q->rear = (q->rear + 1) % MAX_SIZE; - q->size++; - } - - /* 出队操作 */ - Vertex *dequeue(Queue *q) { - Vertex *vet = q->vertices[q->front]; - q->front = (q->front + 1) % MAX_SIZE; - q->size--; - return vet; - } - - /* 检查顶点是否已被访问 */ - int isVisited(Vertex **visited, int size, Vertex *vet) { - // 遍历查找节点,使用 O(n) 时间 - for (int i = 0; i < size; i++) { - if (visited[i] == vet) - return 1; - } - return 0; - } - - /* 广度优先遍历 */ - // 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 - void graphBFS(GraphAdjList *graph, Vertex *startVet, Vertex **res, int *resSize, Vertex **visited, int *visitedSize) { - // 队列用于实现 BFS - Queue *queue = newQueue(); - enqueue(queue, startVet); - visited[(*visitedSize)++] = startVet; - // 以顶点 vet 为起点,循环直至访问完所有顶点 - while (!isEmpty(queue)) { - Vertex *vet = dequeue(queue); // 队首顶点出队 - res[(*resSize)++] = vet; // 记录访问顶点 - // 遍历该顶点的所有邻接顶点 - AdjListNode *node = findNode(graph, vet); - while (node != NULL) { - // 跳过已被访问的顶点 - if (!isVisited(visited, *visitedSize, node->vertex)) { - enqueue(queue, node->vertex); // 只入队未访问的顶点 - visited[(*visitedSize)++] = node->vertex; // 标记该顶点已被访问 - } - node = node->next; - } - } - // 释放内存 - free(queue); - } + [class]{}-[func]{graphBFS} ``` === "Kotlin" ```kotlin title="graph_bfs.kt" - /* 广度优先遍历 */ - // 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 - fun graphBFS(graph: GraphAdjList, startVet: Vertex): MutableList { - // 顶点遍历序列 - val res = mutableListOf() - // 哈希集合,用于记录已被访问过的顶点 - val visited = HashSet() - visited.add(startVet) - // 队列用于实现 BFS - val que = 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 - } + [class]{}-[func]{graphBFS} ``` === "Ruby" ```ruby title="graph_bfs.rb" - ### 广度优先遍历 ### - def graph_bfs(graph, start_vet) - # 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 - # 顶点遍历序列 - res = [] - # 哈希集合,用于记录已被访问过的顶点 - visited = Set.new([start_vet]) - # 队列用于实现 BFS - que = [start_vet] - # 以顶点 vet 为起点,循环直至访问完所有顶点 - while que.length > 0 - vet = que.shift # 队首顶点出队 - res << vet # 记录访问顶点 - # 遍历该顶点的所有邻接顶点 - for adj_vet in graph.adj_list[vet] - next if visited.include?(adj_vet) # 跳过已被访问的顶点 - que << adj_vet # 只入队未访问的顶点 - visited.add(adj_vet) # 标记该顶点已被访问 - end - end - # 返回顶点遍历序列 - res - end + [class]{}-[func]{graph_bfs} ``` === "Zig" @@ -478,11 +159,6 @@ To prevent revisiting vertices, we use a hash set `visited` to record which node [class]{}-[func]{graphBFS} ``` -??? pythontutor "Code Visualization" - -
- - The code is relatively abstract, it is suggested to compare with Figure 9-10 to deepen the understanding. === "<1>" @@ -546,22 +222,22 @@ This "go as far as possible and then return" algorithm paradigm is usually imple ```python title="graph_dfs.py" def dfs(graph: GraphAdjList, visited: set[Vertex], res: list[Vertex], vet: Vertex): - """深度优先遍历辅助函数""" - res.append(vet) # 记录访问顶点 - visited.add(vet) # 标记该顶点已被访问 - # 遍历该顶点的所有邻接顶点 + """Depth-first traversal helper function""" + res.append(vet) # Record visited vertex + visited.add(vet) # Mark the vertex as visited + # Traverse all adjacent vertices of that vertex for adjVet in graph.adj_list[vet]: if adjVet in visited: - continue # 跳过已被访问的顶点 - # 递归访问邻接顶点 + continue # Skip already visited vertices + # Recursively visit adjacent vertices dfs(graph, visited, res, adjVet) def graph_dfs(graph: GraphAdjList, start_vet: Vertex) -> list[Vertex]: - """深度优先遍历""" - # 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 - # 顶点遍历序列 + """Depth-first traversal""" + # Use adjacency list to represent the graph, to obtain all adjacent vertices of a specified vertex + # Vertex traversal sequence res = [] - # 哈希集合,用于记录已被访问过的顶点 + # Hash set, used to record visited vertices visited = set[Vertex]() dfs(graph, visited, res, start_vet) return res @@ -570,53 +246,33 @@ This "go as far as possible and then return" algorithm paradigm is usually imple === "C++" ```cpp title="graph_dfs.cpp" - /* 深度优先遍历辅助函数 */ - void dfs(GraphAdjList &graph, unordered_set &visited, vector &res, Vertex *vet) { - res.push_back(vet); // 记录访问顶点 - visited.emplace(vet); // 标记该顶点已被访问 - // 遍历该顶点的所有邻接顶点 - for (Vertex *adjVet : graph.adjList[vet]) { - if (visited.count(adjVet)) - continue; // 跳过已被访问的顶点 - // 递归访问邻接顶点 - dfs(graph, visited, res, adjVet); - } - } + [class]{}-[func]{dfs} - /* 深度优先遍历 */ - // 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 - vector graphDFS(GraphAdjList &graph, Vertex *startVet) { - // 顶点遍历序列 - vector res; - // 哈希集合,用于记录已被访问过的顶点 - unordered_set visited; - dfs(graph, visited, res, startVet); - return res; - } + [class]{}-[func]{graphDFS} ``` === "Java" ```java title="graph_dfs.java" - /* 深度优先遍历辅助函数 */ + /* Depth-first traversal helper function */ void dfs(GraphAdjList graph, Set visited, List res, Vertex vet) { - res.add(vet); // 记录访问顶点 - visited.add(vet); // 标记该顶点已被访问 - // 遍历该顶点的所有邻接顶点 + res.add(vet); // Record visited vertex + visited.add(vet); // Mark the vertex as visited + // Traverse all adjacent vertices of that vertex for (Vertex adjVet : graph.adjList.get(vet)) { if (visited.contains(adjVet)) - continue; // 跳过已被访问的顶点 - // 递归访问邻接顶点 + continue; // Skip already visited vertices + // Recursively visit adjacent vertices dfs(graph, visited, res, adjVet); } } - /* 深度优先遍历 */ - // 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 + /* Depth-first traversal */ + // Use adjacency list to represent the graph, to obtain all adjacent vertices of a specified vertex List graphDFS(GraphAdjList graph, Vertex startVet) { - // 顶点遍历序列 + // Vertex traversal sequence List res = new ArrayList<>(); - // 哈希集合,用于记录已被访问过的顶点 + // Hash set, used to record visited vertices Set visited = new HashSet<>(); dfs(graph, visited, res, startVet); return res; @@ -626,316 +282,83 @@ This "go as far as possible and then return" algorithm paradigm is usually imple === "C#" ```csharp title="graph_dfs.cs" - /* 深度优先遍历辅助函数 */ - void DFS(GraphAdjList graph, HashSet visited, List res, Vertex vet) { - res.Add(vet); // 记录访问顶点 - visited.Add(vet); // 标记该顶点已被访问 - // 遍历该顶点的所有邻接顶点 - foreach (Vertex adjVet in graph.adjList[vet]) { - if (visited.Contains(adjVet)) { - continue; // 跳过已被访问的顶点 - } - // 递归访问邻接顶点 - DFS(graph, visited, res, adjVet); - } - } + [class]{graph_dfs}-[func]{DFS} - /* 深度优先遍历 */ - // 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 - List GraphDFS(GraphAdjList graph, Vertex startVet) { - // 顶点遍历序列 - List res = []; - // 哈希集合,用于记录已被访问过的顶点 - HashSet visited = []; - DFS(graph, visited, res, startVet); - return res; - } + [class]{graph_dfs}-[func]{GraphDFS} ``` === "Go" ```go title="graph_dfs.go" - /* 深度优先遍历辅助函数 */ - func dfs(g *graphAdjList, visited map[Vertex]struct{}, res *[]Vertex, vet Vertex) { - // append 操作会返回新的的引用,必须让原引用重新赋值为新slice的引用 - *res = append(*res, vet) - visited[vet] = struct{}{} - // 遍历该顶点的所有邻接顶点 - for _, adjVet := range g.adjList[vet] { - _, isExist := visited[adjVet] - // 递归访问邻接顶点 - if !isExist { - dfs(g, visited, res, adjVet) - } - } - } + [class]{}-[func]{dfs} - /* 深度优先遍历 */ - // 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 - func graphDFS(g *graphAdjList, startVet Vertex) []Vertex { - // 顶点遍历序列 - res := make([]Vertex, 0) - // 哈希集合,用于记录已被访问过的顶点 - visited := make(map[Vertex]struct{}) - dfs(g, visited, &res, startVet) - // 返回顶点遍历序列 - return res - } + [class]{}-[func]{graphDFS} ``` === "Swift" ```swift title="graph_dfs.swift" - /* 深度优先遍历辅助函数 */ - func dfs(graph: GraphAdjList, visited: inout Set, res: inout [Vertex], vet: Vertex) { - res.append(vet) // 记录访问顶点 - visited.insert(vet) // 标记该顶点已被访问 - // 遍历该顶点的所有邻接顶点 - for adjVet in graph.adjList[vet] ?? [] { - if visited.contains(adjVet) { - continue // 跳过已被访问的顶点 - } - // 递归访问邻接顶点 - dfs(graph: graph, visited: &visited, res: &res, vet: adjVet) - } - } + [class]{}-[func]{dfs} - /* 深度优先遍历 */ - // 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 - func graphDFS(graph: GraphAdjList, startVet: Vertex) -> [Vertex] { - // 顶点遍历序列 - var res: [Vertex] = [] - // 哈希集合,用于记录已被访问过的顶点 - var visited: Set = [] - dfs(graph: graph, visited: &visited, res: &res, vet: startVet) - return res - } + [class]{}-[func]{graphDFS} ``` === "JS" ```javascript title="graph_dfs.js" - /* 深度优先遍历 */ - // 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 - function dfs(graph, visited, res, vet) { - res.push(vet); // 记录访问顶点 - visited.add(vet); // 标记该顶点已被访问 - // 遍历该顶点的所有邻接顶点 - for (const adjVet of graph.adjList.get(vet)) { - if (visited.has(adjVet)) { - continue; // 跳过已被访问的顶点 - } - // 递归访问邻接顶点 - dfs(graph, visited, res, adjVet); - } - } + [class]{}-[func]{dfs} - /* 深度优先遍历 */ - // 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 - function graphDFS(graph, startVet) { - // 顶点遍历序列 - const res = []; - // 哈希集合,用于记录已被访问过的顶点 - const visited = new Set(); - dfs(graph, visited, res, startVet); - return res; - } + [class]{}-[func]{graphDFS} ``` === "TS" ```typescript title="graph_dfs.ts" - /* 深度优先遍历辅助函数 */ - function dfs( - graph: GraphAdjList, - visited: Set, - res: Vertex[], - vet: Vertex - ): void { - res.push(vet); // 记录访问顶点 - visited.add(vet); // 标记该顶点已被访问 - // 遍历该顶点的所有邻接顶点 - for (const adjVet of graph.adjList.get(vet)) { - if (visited.has(adjVet)) { - continue; // 跳过已被访问的顶点 - } - // 递归访问邻接顶点 - dfs(graph, visited, res, adjVet); - } - } + [class]{}-[func]{dfs} - /* 深度优先遍历 */ - // 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 - function graphDFS(graph: GraphAdjList, startVet: Vertex): Vertex[] { - // 顶点遍历序列 - const res: Vertex[] = []; - // 哈希集合,用于记录已被访问过的顶点 - const visited: Set = new Set(); - dfs(graph, visited, res, startVet); - return res; - } + [class]{}-[func]{graphDFS} ``` === "Dart" ```dart title="graph_dfs.dart" - /* 深度优先遍历辅助函数 */ - void dfs( - GraphAdjList graph, - Set visited, - List res, - Vertex vet, - ) { - res.add(vet); // 记录访问顶点 - visited.add(vet); // 标记该顶点已被访问 - // 遍历该顶点的所有邻接顶点 - for (Vertex adjVet in graph.adjList[vet]!) { - if (visited.contains(adjVet)) { - continue; // 跳过已被访问的顶点 - } - // 递归访问邻接顶点 - dfs(graph, visited, res, adjVet); - } - } + [class]{}-[func]{dfs} - /* 深度优先遍历 */ - List graphDFS(GraphAdjList graph, Vertex startVet) { - // 顶点遍历序列 - List res = []; - // 哈希集合,用于记录已被访问过的顶点 - Set visited = {}; - dfs(graph, visited, res, startVet); - return res; - } + [class]{}-[func]{graphDFS} ``` === "Rust" ```rust title="graph_dfs.rs" - /* 深度优先遍历辅助函数 */ - fn dfs(graph: &GraphAdjList, visited: &mut HashSet, res: &mut Vec, vet: Vertex) { - res.push(vet); // 记录访问顶点 - visited.insert(vet); // 标记该顶点已被访问 - // 遍历该顶点的所有邻接顶点 - if let Some(adj_vets) = graph.adj_list.get(&vet) { - for &adj_vet in adj_vets { - if visited.contains(&adj_vet) { - continue; // 跳过已被访问的顶点 - } - // 递归访问邻接顶点 - dfs(graph, visited, res, adj_vet); - } - } - } + [class]{}-[func]{dfs} - /* 深度优先遍历 */ - // 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 - fn graph_dfs(graph: GraphAdjList, start_vet: Vertex) -> Vec { - // 顶点遍历序列 - let mut res = vec![]; - // 哈希集合,用于记录已被访问过的顶点 - let mut visited = HashSet::new(); - dfs(&graph, &mut visited, &mut res, start_vet); - - res - } + [class]{}-[func]{graph_dfs} ``` === "C" ```c title="graph_dfs.c" - /* 检查顶点是否已被访问 */ - int isVisited(Vertex **res, int size, Vertex *vet) { - // 遍历查找节点,使用 O(n) 时间 - for (int i = 0; i < size; i++) { - if (res[i] == vet) { - return 1; - } - } - return 0; - } + [class]{}-[func]{isVisited} - /* 深度优先遍历辅助函数 */ - void dfs(GraphAdjList *graph, Vertex **res, int *resSize, Vertex *vet) { - // 记录访问顶点 - res[(*resSize)++] = vet; - // 遍历该顶点的所有邻接顶点 - AdjListNode *node = findNode(graph, vet); - while (node != NULL) { - // 跳过已被访问的顶点 - if (!isVisited(res, *resSize, node->vertex)) { - // 递归访问邻接顶点 - dfs(graph, res, resSize, node->vertex); - } - node = node->next; - } - } + [class]{}-[func]{dfs} - /* 深度优先遍历 */ - // 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 - void graphDFS(GraphAdjList *graph, Vertex *startVet, Vertex **res, int *resSize) { - dfs(graph, res, resSize, startVet); - } + [class]{}-[func]{graphDFS} ``` === "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) - } - } + [class]{}-[func]{dfs} - /* 深度优先遍历 */ - // 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 - fun graphDFS(graph: GraphAdjList, startVet: Vertex?): MutableList { - // 顶点遍历序列 - val res = mutableListOf() - // 哈希集合,用于记录已被访问过的顶点 - val visited = HashSet() - dfs(graph, visited, res, startVet) - return res - } + [class]{}-[func]{graphDFS} ``` === "Ruby" ```ruby title="graph_dfs.rb" - ### 深度优先遍历辅助函数 ### - def dfs(graph, visited, res, vet) - res << vet # 记录访问顶点 - visited.add(vet) # 标记该顶点已被访问 - # 遍历该顶点的所有邻接顶点 - for adj_vet in graph.adj_list[vet] - next if visited.include?(adj_vet) # 跳过已被访问的顶点 - # 递归访问邻接顶点 - dfs(graph, visited, res, adj_vet) - end - end + [class]{}-[func]{dfs} - ### 深度优先遍历 ### - def graph_dfs(graph, start_vet) - # 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 - # 顶点遍历序列 - res = [] - # 哈希集合,用于记录已被访问过的顶点 - visited = Set.new - dfs(graph, visited, res, start_vet) - res - end + [class]{}-[func]{graph_dfs} ``` === "Zig" @@ -946,11 +369,6 @@ This "go as far as possible and then return" algorithm paradigm is usually imple [class]{}-[func]{graphDFS} ``` -??? pythontutor "Code Visualization" - -
- - The algorithm process of depth-first search is shown in Figure 9-12. - **Dashed lines represent downward recursion**, indicating that a new recursive method has been initiated to visit a new vertex. diff --git a/en/docs/chapter_greedy/fractional_knapsack_problem.md b/en/docs/chapter_greedy/fractional_knapsack_problem.md index 7cb641185..b950b7870 100644 --- a/en/docs/chapter_greedy/fractional_knapsack_problem.md +++ b/en/docs/chapter_greedy/fractional_knapsack_problem.md @@ -43,29 +43,29 @@ We have created an `Item` class in order to sort the items by their unit value. ```python title="fractional_knapsack.py" class Item: - """物品""" + """Item""" def __init__(self, w: int, v: int): - self.w = w # 物品重量 - self.v = v # 物品价值 + self.w = w # Item weight + self.v = v # Item value def fractional_knapsack(wgt: list[int], val: list[int], cap: int) -> int: - """分数背包:贪心""" - # 创建物品列表,包含两个属性:重量、价值 + """Fractional knapsack: Greedy""" + # Create an item list, containing two properties: weight, value items = [Item(w, v) for w, v in zip(wgt, val)] - # 按照单位价值 item.v / item.w 从高到低进行排序 + # Sort by unit value item.v / item.w from high to low items.sort(key=lambda item: item.v / item.w, reverse=True) - # 循环贪心选择 + # Loop for greedy selection res = 0 for item in items: if item.w <= cap: - # 若剩余容量充足,则将当前物品整个装进背包 + # If the remaining capacity is sufficient, put the entire item into the knapsack res += item.v cap -= item.w else: - # 若剩余容量不足,则将当前物品的一部分装进背包 + # If the remaining capacity is insufficient, put part of the item into the knapsack res += (item.v / item.w) * cap - # 已无剩余容量,因此跳出循环 + # No remaining capacity left, thus break the loop break return res ``` @@ -73,50 +73,18 @@ We have created an `Item` class in order to sort the items by their unit value. === "C++" ```cpp title="fractional_knapsack.cpp" - /* 物品 */ - class Item { - public: - int w; // 物品重量 - int v; // 物品价值 + [class]{Item}-[func]{} - Item(int w, int v) : w(w), v(v) { - } - }; - - /* 分数背包:贪心 */ - double fractionalKnapsack(vector &wgt, vector &val, int cap) { - // 创建物品列表,包含两个属性:重量、价值 - vector items; - for (int i = 0; i < wgt.size(); i++) { - items.push_back(Item(wgt[i], val[i])); - } - // 按照单位价值 item.v / item.w 从高到低进行排序 - sort(items.begin(), items.end(), [](Item &a, Item &b) { return (double)a.v / a.w > (double)b.v / b.w; }); - // 循环贪心选择 - double res = 0; - for (auto &item : items) { - if (item.w <= cap) { - // 若剩余容量充足,则将当前物品整个装进背包 - res += item.v; - cap -= item.w; - } else { - // 若剩余容量不足,则将当前物品的一部分装进背包 - res += (double)item.v / item.w * cap; - // 已无剩余容量,因此跳出循环 - break; - } - } - return res; - } + [class]{}-[func]{fractionalKnapsack} ``` === "Java" ```java title="fractional_knapsack.java" - /* 物品 */ + /* Item */ class Item { - int w; // 物品重量 - int v; // 物品价值 + int w; // Item weight + int v; // Item value public Item(int w, int v) { this.w = w; @@ -124,26 +92,26 @@ We have created an `Item` class in order to sort the items by their unit value. } } - /* 分数背包:贪心 */ + /* Fractional knapsack: Greedy */ double fractionalKnapsack(int[] wgt, int[] val, int cap) { - // 创建物品列表,包含两个属性:重量、价值 + // Create an item list, containing two properties: weight, value Item[] items = new Item[wgt.length]; for (int i = 0; i < wgt.length; i++) { items[i] = new Item(wgt[i], val[i]); } - // 按照单位价值 item.v / item.w 从高到低进行排序 + // Sort by unit value item.v / item.w from high to low Arrays.sort(items, Comparator.comparingDouble(item -> -((double) item.v / item.w))); - // 循环贪心选择 + // Loop for greedy selection double res = 0; for (Item item : items) { if (item.w <= cap) { - // 若剩余容量充足,则将当前物品整个装进背包 + // If the remaining capacity is sufficient, put the entire item into the knapsack res += item.v; cap -= item.w; } else { - // 若剩余容量不足,则将当前物品的一部分装进背包 + // If the remaining capacity is insufficient, put part of the item into the knapsack res += (double) item.v / item.w * cap; - // 已无剩余容量,因此跳出循环 + // No remaining capacity left, thus break the loop break; } } @@ -154,343 +122,73 @@ We have created an `Item` class in order to sort the items by their unit value. === "C#" ```csharp title="fractional_knapsack.cs" - /* 物品 */ - class Item(int w, int v) { - public int w = w; // 物品重量 - public int v = v; // 物品价值 - } + [class]{Item}-[func]{} - /* 分数背包:贪心 */ - double FractionalKnapsack(int[] wgt, int[] val, int cap) { - // 创建物品列表,包含两个属性:重量、价值 - Item[] items = new Item[wgt.Length]; - for (int i = 0; i < wgt.Length; i++) { - items[i] = new Item(wgt[i], val[i]); - } - // 按照单位价值 item.v / item.w 从高到低进行排序 - Array.Sort(items, (x, y) => (y.v / y.w).CompareTo(x.v / x.w)); - // 循环贪心选择 - double res = 0; - foreach (Item item in items) { - if (item.w <= cap) { - // 若剩余容量充足,则将当前物品整个装进背包 - res += item.v; - cap -= item.w; - } else { - // 若剩余容量不足,则将当前物品的一部分装进背包 - res += (double)item.v / item.w * cap; - // 已无剩余容量,因此跳出循环 - break; - } - } - return res; - } + [class]{fractional_knapsack}-[func]{FractionalKnapsack} ``` === "Go" ```go title="fractional_knapsack.go" - /* 物品 */ - type Item struct { - w int // 物品重量 - v int // 物品价值 - } + [class]{Item}-[func]{} - /* 分数背包:贪心 */ - func fractionalKnapsack(wgt []int, val []int, cap int) float64 { - // 创建物品列表,包含两个属性:重量、价值 - items := make([]Item, len(wgt)) - for i := 0; i < len(wgt); i++ { - items[i] = Item{wgt[i], val[i]} - } - // 按照单位价值 item.v / item.w 从高到低进行排序 - sort.Slice(items, func(i, j int) bool { - return float64(items[i].v)/float64(items[i].w) > float64(items[j].v)/float64(items[j].w) - }) - // 循环贪心选择 - res := 0.0 - for _, item := range items { - if item.w <= cap { - // 若剩余容量充足,则将当前物品整个装进背包 - res += float64(item.v) - cap -= item.w - } else { - // 若剩余容量不足,则将当前物品的一部分装进背包 - res += float64(item.v) / float64(item.w) * float64(cap) - // 已无剩余容量,因此跳出循环 - break - } - } - return res - } + [class]{}-[func]{fractionalKnapsack} ``` === "Swift" ```swift title="fractional_knapsack.swift" - /* 物品 */ - class Item { - var w: Int // 物品重量 - var v: Int // 物品价值 + [class]{Item}-[func]{} - init(w: Int, v: Int) { - self.w = w - self.v = v - } - } - - /* 分数背包:贪心 */ - func fractionalKnapsack(wgt: [Int], val: [Int], cap: Int) -> Double { - // 创建物品列表,包含两个属性:重量、价值 - var items = zip(wgt, val).map { Item(w: $0, v: $1) } - // 按照单位价值 item.v / item.w 从高到低进行排序 - items.sort { -(Double($0.v) / Double($0.w)) < -(Double($1.v) / Double($1.w)) } - // 循环贪心选择 - var res = 0.0 - var cap = cap - for item in items { - if item.w <= cap { - // 若剩余容量充足,则将当前物品整个装进背包 - res += Double(item.v) - cap -= item.w - } else { - // 若剩余容量不足,则将当前物品的一部分装进背包 - res += Double(item.v) / Double(item.w) * Double(cap) - // 已无剩余容量,因此跳出循环 - break - } - } - return res - } + [class]{}-[func]{fractionalKnapsack} ``` === "JS" ```javascript title="fractional_knapsack.js" - /* 物品 */ - class Item { - constructor(w, v) { - this.w = w; // 物品重量 - this.v = v; // 物品价值 - } - } + [class]{Item}-[func]{} - /* 分数背包:贪心 */ - function fractionalKnapsack(wgt, val, cap) { - // 创建物品列表,包含两个属性:重量、价值 - const items = wgt.map((w, i) => new Item(w, val[i])); - // 按照单位价值 item.v / item.w 从高到低进行排序 - items.sort((a, b) => b.v / b.w - a.v / a.w); - // 循环贪心选择 - let res = 0; - for (const item of items) { - if (item.w <= cap) { - // 若剩余容量充足,则将当前物品整个装进背包 - res += item.v; - cap -= item.w; - } else { - // 若剩余容量不足,则将当前物品的一部分装进背包 - res += (item.v / item.w) * cap; - // 已无剩余容量,因此跳出循环 - break; - } - } - return res; - } + [class]{}-[func]{fractionalKnapsack} ``` === "TS" ```typescript title="fractional_knapsack.ts" - /* 物品 */ - class Item { - w: number; // 物品重量 - v: number; // 物品价值 + [class]{Item}-[func]{} - constructor(w: number, v: number) { - this.w = w; - this.v = v; - } - } - - /* 分数背包:贪心 */ - function fractionalKnapsack(wgt: number[], val: number[], cap: number): number { - // 创建物品列表,包含两个属性:重量、价值 - const items: Item[] = wgt.map((w, i) => new Item(w, val[i])); - // 按照单位价值 item.v / item.w 从高到低进行排序 - items.sort((a, b) => b.v / b.w - a.v / a.w); - // 循环贪心选择 - let res = 0; - for (const item of items) { - if (item.w <= cap) { - // 若剩余容量充足,则将当前物品整个装进背包 - res += item.v; - cap -= item.w; - } else { - // 若剩余容量不足,则将当前物品的一部分装进背包 - res += (item.v / item.w) * cap; - // 已无剩余容量,因此跳出循环 - break; - } - } - return res; - } + [class]{}-[func]{fractionalKnapsack} ``` === "Dart" ```dart title="fractional_knapsack.dart" - /* 物品 */ - class Item { - int w; // 物品重量 - int v; // 物品价值 + [class]{Item}-[func]{} - Item(this.w, this.v); - } - - /* 分数背包:贪心 */ - double fractionalKnapsack(List wgt, List val, int cap) { - // 创建物品列表,包含两个属性:重量、价值 - List items = List.generate(wgt.length, (i) => Item(wgt[i], val[i])); - // 按照单位价值 item.v / item.w 从高到低进行排序 - items.sort((a, b) => (b.v / b.w).compareTo(a.v / a.w)); - // 循环贪心选择 - double res = 0; - for (Item item in items) { - if (item.w <= cap) { - // 若剩余容量充足,则将当前物品整个装进背包 - res += item.v; - cap -= item.w; - } else { - // 若剩余容量不足,则将当前物品的一部分装进背包 - res += item.v / item.w * cap; - // 已无剩余容量,因此跳出循环 - break; - } - } - return res; - } + [class]{}-[func]{fractionalKnapsack} ``` === "Rust" ```rust title="fractional_knapsack.rs" - /* 物品 */ - struct Item { - w: i32, // 物品重量 - v: i32, // 物品价值 - } + [class]{Item}-[func]{} - impl Item { - fn new(w: i32, v: i32) -> Self { - Self { w, v } - } - } - - /* 分数背包:贪心 */ - fn fractional_knapsack(wgt: &[i32], val: &[i32], mut cap: i32) -> f64 { - // 创建物品列表,包含两个属性:重量、价值 - let mut items = wgt - .iter() - .zip(val.iter()) - .map(|(&w, &v)| Item::new(w, v)) - .collect::>(); - // 按照单位价值 item.v / item.w 从高到低进行排序 - items.sort_by(|a, b| { - (b.v as f64 / b.w as f64) - .partial_cmp(&(a.v as f64 / a.w as f64)) - .unwrap() - }); - // 循环贪心选择 - let mut res = 0.0; - for item in &items { - if item.w <= cap { - // 若剩余容量充足,则将当前物品整个装进背包 - res += item.v as f64; - cap -= item.w; - } else { - // 若剩余容量不足,则将当前物品的一部分装进背包 - res += item.v as f64 / item.w as f64 * cap as f64; - // 已无剩余容量,因此跳出循环 - break; - } - } - res - } + [class]{}-[func]{fractional_knapsack} ``` === "C" ```c title="fractional_knapsack.c" - /* 物品 */ - typedef struct { - int w; // 物品重量 - int v; // 物品价值 - } Item; + [class]{Item}-[func]{} - /* 分数背包:贪心 */ - float fractionalKnapsack(int wgt[], int val[], int itemCount, int cap) { - // 创建物品列表,包含两个属性:重量、价值 - Item *items = malloc(sizeof(Item) * itemCount); - for (int i = 0; i < itemCount; i++) { - items[i] = (Item){.w = wgt[i], .v = val[i]}; - } - // 按照单位价值 item.v / item.w 从高到低进行排序 - qsort(items, (size_t)itemCount, sizeof(Item), sortByValueDensity); - // 循环贪心选择 - float res = 0.0; - for (int i = 0; i < itemCount; i++) { - if (items[i].w <= cap) { - // 若剩余容量充足,则将当前物品整个装进背包 - res += items[i].v; - cap -= items[i].w; - } else { - // 若剩余容量不足,则将当前物品的一部分装进背包 - res += (float)cap / items[i].w * items[i].v; - cap = 0; - break; - } - } - free(items); - return res; - } + [class]{}-[func]{fractionalKnapsack} ``` === "Kotlin" ```kotlin title="fractional_knapsack.kt" - /* 物品 */ - class Item( - val w: Int, // 物品 - val v: Int // 物品价值 - ) + [class]{Item}-[func]{} - /* 分数背包:贪心 */ - fun fractionalKnapsack(wgt: IntArray, _val: IntArray, c: Int): Double { - // 创建物品列表,包含两个属性:重量、价值 - var cap = c - val items = arrayOfNulls(wgt.size) - for (i in wgt.indices) { - items[i] = Item(wgt[i], _val[i]) - } - // 按照单位价值 item.v / item.w 从高到低进行排序 - items.sortBy { item: Item? -> -(item!!.v.toDouble() / item.w) } - // 循环贪心选择 - var res = 0.0 - for (item in items) { - if (item!!.w <= cap) { - // 若剩余容量充足,则将当前物品整个装进背包 - res += item.v - cap -= item.w - } else { - // 若剩余容量不足,则将当前物品的一部分装进背包 - res += item.v.toDouble() / item.w * cap - // 已无剩余容量,因此跳出循环 - break - } - } - return res - } + [class]{}-[func]{fractionalKnapsack} ``` === "Ruby" @@ -509,11 +207,6 @@ We have created an `Item` class in order to sort the items by their unit value. [class]{}-[func]{fractionalKnapsack} ``` -??? pythontutor "Code Visualization" - -
- - Apart from sorting, in the worst case, the entire list of items needs to be traversed, **hence the time complexity is $O(n)$**, where $n$ is the number of items. Since an `Item` object list is initialized, **the space complexity is $O(n)$**. diff --git a/en/docs/chapter_greedy/greedy_algorithm.md b/en/docs/chapter_greedy/greedy_algorithm.md index 2d5b2504f..e7ba3e9f3 100644 --- a/en/docs/chapter_greedy/greedy_algorithm.md +++ b/en/docs/chapter_greedy/greedy_algorithm.md @@ -29,64 +29,47 @@ The implementation code is as follows: ```python title="coin_change_greedy.py" def coin_change_greedy(coins: list[int], amt: int) -> int: - """零钱兑换:贪心""" - # 假设 coins 列表有序 + """Coin change: Greedy""" + # Assume coins list is ordered i = len(coins) - 1 count = 0 - # 循环进行贪心选择,直到无剩余金额 + # Loop for greedy selection until no remaining amount while amt > 0: - # 找到小于且最接近剩余金额的硬币 + # Find the smallest coin close to and less than the remaining amount while i > 0 and coins[i] > amt: i -= 1 - # 选择 coins[i] + # Choose coins[i] amt -= coins[i] count += 1 - # 若未找到可行方案,则返回 -1 + # If no feasible solution is found, return -1 return count if amt == 0 else -1 ``` === "C++" ```cpp title="coin_change_greedy.cpp" - /* 零钱兑换:贪心 */ - int coinChangeGreedy(vector &coins, int amt) { - // 假设 coins 列表有序 - int i = coins.size() - 1; - int count = 0; - // 循环进行贪心选择,直到无剩余金额 - while (amt > 0) { - // 找到小于且最接近剩余金额的硬币 - while (i > 0 && coins[i] > amt) { - i--; - } - // 选择 coins[i] - amt -= coins[i]; - count++; - } - // 若未找到可行方案,则返回 -1 - return amt == 0 ? count : -1; - } + [class]{}-[func]{coinChangeGreedy} ``` === "Java" ```java title="coin_change_greedy.java" - /* 零钱兑换:贪心 */ + /* Coin change: Greedy */ int coinChangeGreedy(int[] coins, int amt) { - // 假设 coins 列表有序 + // Assume coins list is ordered int i = coins.length - 1; int count = 0; - // 循环进行贪心选择,直到无剩余金额 + // Loop for greedy selection until no remaining amount while (amt > 0) { - // 找到小于且最接近剩余金额的硬币 + // Find the smallest coin close to and less than the remaining amount while (i > 0 && coins[i] > amt) { i--; } - // 选择 coins[i] + // Choose coins[i] amt -= coins[i]; count++; } - // 若未找到可行方案,则返回 -1 + // If no feasible solution is found, return -1 return amt == 0 ? count : -1; } ``` @@ -94,217 +77,55 @@ The implementation code is as follows: === "C#" ```csharp title="coin_change_greedy.cs" - /* 零钱兑换:贪心 */ - int CoinChangeGreedy(int[] coins, int amt) { - // 假设 coins 列表有序 - int i = coins.Length - 1; - int count = 0; - // 循环进行贪心选择,直到无剩余金额 - while (amt > 0) { - // 找到小于且最接近剩余金额的硬币 - while (i > 0 && coins[i] > amt) { - i--; - } - // 选择 coins[i] - amt -= coins[i]; - count++; - } - // 若未找到可行方案,则返回 -1 - return amt == 0 ? count : -1; - } + [class]{coin_change_greedy}-[func]{CoinChangeGreedy} ``` === "Go" ```go title="coin_change_greedy.go" - /* 零钱兑换:贪心 */ - func coinChangeGreedy(coins []int, amt int) int { - // 假设 coins 列表有序 - i := len(coins) - 1 - count := 0 - // 循环进行贪心选择,直到无剩余金额 - for amt > 0 { - // 找到小于且最接近剩余金额的硬币 - for i > 0 && coins[i] > amt { - i-- - } - // 选择 coins[i] - amt -= coins[i] - count++ - } - // 若未找到可行方案,则返回 -1 - if amt != 0 { - return -1 - } - return count - } + [class]{}-[func]{coinChangeGreedy} ``` === "Swift" ```swift title="coin_change_greedy.swift" - /* 零钱兑换:贪心 */ - func coinChangeGreedy(coins: [Int], amt: Int) -> Int { - // 假设 coins 列表有序 - var i = coins.count - 1 - var count = 0 - var amt = amt - // 循环进行贪心选择,直到无剩余金额 - while amt > 0 { - // 找到小于且最接近剩余金额的硬币 - while i > 0 && coins[i] > amt { - i -= 1 - } - // 选择 coins[i] - amt -= coins[i] - count += 1 - } - // 若未找到可行方案,则返回 -1 - return amt == 0 ? count : -1 - } + [class]{}-[func]{coinChangeGreedy} ``` === "JS" ```javascript title="coin_change_greedy.js" - /* 零钱兑换:贪心 */ - function coinChangeGreedy(coins, amt) { - // 假设 coins 数组有序 - let i = coins.length - 1; - let count = 0; - // 循环进行贪心选择,直到无剩余金额 - while (amt > 0) { - // 找到小于且最接近剩余金额的硬币 - while (i > 0 && coins[i] > amt) { - i--; - } - // 选择 coins[i] - amt -= coins[i]; - count++; - } - // 若未找到可行方案,则返回 -1 - return amt === 0 ? count : -1; - } + [class]{}-[func]{coinChangeGreedy} ``` === "TS" ```typescript title="coin_change_greedy.ts" - /* 零钱兑换:贪心 */ - function coinChangeGreedy(coins: number[], amt: number): number { - // 假设 coins 数组有序 - let i = coins.length - 1; - let count = 0; - // 循环进行贪心选择,直到无剩余金额 - while (amt > 0) { - // 找到小于且最接近剩余金额的硬币 - while (i > 0 && coins[i] > amt) { - i--; - } - // 选择 coins[i] - amt -= coins[i]; - count++; - } - // 若未找到可行方案,则返回 -1 - return amt === 0 ? count : -1; - } + [class]{}-[func]{coinChangeGreedy} ``` === "Dart" ```dart title="coin_change_greedy.dart" - /* 零钱兑换:贪心 */ - int coinChangeGreedy(List coins, int amt) { - // 假设 coins 列表有序 - int i = coins.length - 1; - int count = 0; - // 循环进行贪心选择,直到无剩余金额 - while (amt > 0) { - // 找到小于且最接近剩余金额的硬币 - while (i > 0 && coins[i] > amt) { - i--; - } - // 选择 coins[i] - amt -= coins[i]; - count++; - } - // 若未找到可行方案,则返回 -1 - return amt == 0 ? count : -1; - } + [class]{}-[func]{coinChangeGreedy} ``` === "Rust" ```rust title="coin_change_greedy.rs" - /* 零钱兑换:贪心 */ - fn coin_change_greedy(coins: &[i32], mut amt: i32) -> i32 { - // 假设 coins 列表有序 - let mut i = coins.len() - 1; - let mut count = 0; - // 循环进行贪心选择,直到无剩余金额 - while amt > 0 { - // 找到小于且最接近剩余金额的硬币 - while i > 0 && coins[i] > amt { - i -= 1; - } - // 选择 coins[i] - amt -= coins[i]; - count += 1; - } - // 若未找到可行方案,则返回 -1 - if amt == 0 { - count - } else { - -1 - } - } + [class]{}-[func]{coin_change_greedy} ``` === "C" ```c title="coin_change_greedy.c" - /* 零钱兑换:贪心 */ - int coinChangeGreedy(int *coins, int size, int amt) { - // 假设 coins 列表有序 - int i = size - 1; - int count = 0; - // 循环进行贪心选择,直到无剩余金额 - while (amt > 0) { - // 找到小于且最接近剩余金额的硬币 - while (i > 0 && coins[i] > amt) { - i--; - } - // 选择 coins[i] - amt -= coins[i]; - count++; - } - // 若未找到可行方案,则返回 -1 - return amt == 0 ? count : -1; - } + [class]{}-[func]{coinChangeGreedy} ``` === "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 - } + [class]{}-[func]{coinChangeGreedy} ``` === "Ruby" @@ -319,11 +140,6 @@ The implementation code is as follows: [class]{}-[func]{coinChangeGreedy} ``` -??? pythontutor "Code Visualization" - -
- - You might exclaim: So clean! The greedy algorithm solves the coin change problem in about ten lines of code. ## 15.1.1   Advantages and limitations of greedy algorithms diff --git a/en/docs/chapter_greedy/max_capacity_problem.md b/en/docs/chapter_greedy/max_capacity_problem.md index c4c5af400..436d23ac1 100644 --- a/en/docs/chapter_greedy/max_capacity_problem.md +++ b/en/docs/chapter_greedy/max_capacity_problem.md @@ -96,17 +96,17 @@ The variables $i$, $j$, and $res$ use a constant amount of extra space, **thus t ```python title="max_capacity.py" def max_capacity(ht: list[int]) -> int: - """最大容量:贪心""" - # 初始化 i, j,使其分列数组两端 + """Maximum capacity: Greedy""" + # Initialize i, j, making them split the array at both ends i, j = 0, len(ht) - 1 - # 初始最大容量为 0 + # Initial maximum capacity is 0 res = 0 - # 循环贪心选择,直至两板相遇 + # Loop for greedy selection until the two boards meet while i < j: - # 更新最大容量 + # Update maximum capacity cap = min(ht[i], ht[j]) * (j - i) res = max(res, cap) - # 向内移动短板 + # Move the shorter board inward if ht[i] < ht[j]: i += 1 else: @@ -117,43 +117,24 @@ The variables $i$, $j$, and $res$ use a constant amount of extra space, **thus t === "C++" ```cpp title="max_capacity.cpp" - /* 最大容量:贪心 */ - int maxCapacity(vector &ht) { - // 初始化 i, j,使其分列数组两端 - int i = 0, j = ht.size() - 1; - // 初始最大容量为 0 - int res = 0; - // 循环贪心选择,直至两板相遇 - while (i < j) { - // 更新最大容量 - int cap = min(ht[i], ht[j]) * (j - i); - res = max(res, cap); - // 向内移动短板 - if (ht[i] < ht[j]) { - i++; - } else { - j--; - } - } - return res; - } + [class]{}-[func]{maxCapacity} ``` === "Java" ```java title="max_capacity.java" - /* 最大容量:贪心 */ + /* Maximum capacity: Greedy */ int maxCapacity(int[] ht) { - // 初始化 i, j,使其分列数组两端 + // Initialize i, j, making them split the array at both ends int i = 0, j = ht.length - 1; - // 初始最大容量为 0 + // Initial maximum capacity is 0 int res = 0; - // 循环贪心选择,直至两板相遇 + // Loop for greedy selection until the two boards meet while (i < j) { - // 更新最大容量 + // Update maximum capacity int cap = Math.min(ht[i], ht[j]) * (j - i); res = Math.max(res, cap); - // 向内移动短板 + // Move the shorter board inward if (ht[i] < ht[j]) { i++; } else { @@ -167,231 +148,55 @@ The variables $i$, $j$, and $res$ use a constant amount of extra space, **thus t === "C#" ```csharp title="max_capacity.cs" - /* 最大容量:贪心 */ - int MaxCapacity(int[] ht) { - // 初始化 i, j,使其分列数组两端 - int i = 0, j = ht.Length - 1; - // 初始最大容量为 0 - int res = 0; - // 循环贪心选择,直至两板相遇 - while (i < j) { - // 更新最大容量 - int cap = Math.Min(ht[i], ht[j]) * (j - i); - res = Math.Max(res, cap); - // 向内移动短板 - if (ht[i] < ht[j]) { - i++; - } else { - j--; - } - } - return res; - } + [class]{max_capacity}-[func]{MaxCapacity} ``` === "Go" ```go title="max_capacity.go" - /* 最大容量:贪心 */ - func maxCapacity(ht []int) int { - // 初始化 i, j,使其分列数组两端 - i, j := 0, len(ht)-1 - // 初始最大容量为 0 - res := 0 - // 循环贪心选择,直至两板相遇 - for i < j { - // 更新最大容量 - capacity := int(math.Min(float64(ht[i]), float64(ht[j]))) * (j - i) - res = int(math.Max(float64(res), float64(capacity))) - // 向内移动短板 - if ht[i] < ht[j] { - i++ - } else { - j-- - } - } - return res - } + [class]{}-[func]{maxCapacity} ``` === "Swift" ```swift title="max_capacity.swift" - /* 最大容量:贪心 */ - func maxCapacity(ht: [Int]) -> Int { - // 初始化 i, j,使其分列数组两端 - var i = ht.startIndex, j = ht.endIndex - 1 - // 初始最大容量为 0 - var res = 0 - // 循环贪心选择,直至两板相遇 - while i < j { - // 更新最大容量 - let cap = min(ht[i], ht[j]) * (j - i) - res = max(res, cap) - // 向内移动短板 - if ht[i] < ht[j] { - i += 1 - } else { - j -= 1 - } - } - return res - } + [class]{}-[func]{maxCapacity} ``` === "JS" ```javascript title="max_capacity.js" - /* 最大容量:贪心 */ - function maxCapacity(ht) { - // 初始化 i, j,使其分列数组两端 - let i = 0, - j = ht.length - 1; - // 初始最大容量为 0 - let res = 0; - // 循环贪心选择,直至两板相遇 - while (i < j) { - // 更新最大容量 - const cap = Math.min(ht[i], ht[j]) * (j - i); - res = Math.max(res, cap); - // 向内移动短板 - if (ht[i] < ht[j]) { - i += 1; - } else { - j -= 1; - } - } - return res; - } + [class]{}-[func]{maxCapacity} ``` === "TS" ```typescript title="max_capacity.ts" - /* 最大容量:贪心 */ - function maxCapacity(ht: number[]): number { - // 初始化 i, j,使其分列数组两端 - let i = 0, - j = ht.length - 1; - // 初始最大容量为 0 - let res = 0; - // 循环贪心选择,直至两板相遇 - while (i < j) { - // 更新最大容量 - const cap: number = Math.min(ht[i], ht[j]) * (j - i); - res = Math.max(res, cap); - // 向内移动短板 - if (ht[i] < ht[j]) { - i += 1; - } else { - j -= 1; - } - } - return res; - } + [class]{}-[func]{maxCapacity} ``` === "Dart" ```dart title="max_capacity.dart" - /* 最大容量:贪心 */ - int maxCapacity(List ht) { - // 初始化 i, j,使其分列数组两端 - int i = 0, j = ht.length - 1; - // 初始最大容量为 0 - int res = 0; - // 循环贪心选择,直至两板相遇 - while (i < j) { - // 更新最大容量 - int cap = min(ht[i], ht[j]) * (j - i); - res = max(res, cap); - // 向内移动短板 - if (ht[i] < ht[j]) { - i++; - } else { - j--; - } - } - return res; - } + [class]{}-[func]{maxCapacity} ``` === "Rust" ```rust title="max_capacity.rs" - /* 最大容量:贪心 */ - fn max_capacity(ht: &[i32]) -> i32 { - // 初始化 i, j,使其分列数组两端 - let mut i = 0; - let mut j = ht.len() - 1; - // 初始最大容量为 0 - let mut res = 0; - // 循环贪心选择,直至两板相遇 - while i < j { - // 更新最大容量 - let cap = std::cmp::min(ht[i], ht[j]) * (j - i) as i32; - res = std::cmp::max(res, cap); - // 向内移动短板 - if ht[i] < ht[j] { - i += 1; - } else { - j -= 1; - } - } - res - } + [class]{}-[func]{max_capacity} ``` === "C" ```c title="max_capacity.c" - /* 最大容量:贪心 */ - int maxCapacity(int ht[], int htLength) { - // 初始化 i, j,使其分列数组两端 - int i = 0; - int j = htLength - 1; - // 初始最大容量为 0 - int res = 0; - // 循环贪心选择,直至两板相遇 - while (i < j) { - // 更新最大容量 - int capacity = myMin(ht[i], ht[j]) * (j - i); - res = myMax(res, capacity); - // 向内移动短板 - if (ht[i] < ht[j]) { - i++; - } else { - j--; - } - } - return res; - } + [class]{}-[func]{maxCapacity} ``` === "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], ht[j]) * (j - i) - res = max(res, cap) - // 向内移动短板 - if (ht[i] < ht[j]) { - i++ - } else { - j-- - } - } - return res - } + [class]{}-[func]{maxCapacity} ``` === "Ruby" @@ -406,11 +211,6 @@ The variables $i$, $j$, and $res$ use a constant amount of extra space, **thus t [class]{}-[func]{maxCapacity} ``` -??? pythontutor "Code Visualization" - -
- - ### 3.   Proof of correctness The reason why the greedy method is faster than enumeration is that each round of greedy selection "skips" some states. diff --git a/en/docs/chapter_greedy/max_product_cutting_problem.md b/en/docs/chapter_greedy/max_product_cutting_problem.md index bca43f684..16ef9dcdd 100644 --- a/en/docs/chapter_greedy/max_product_cutting_problem.md +++ b/en/docs/chapter_greedy/max_product_cutting_problem.md @@ -77,68 +77,49 @@ Please note, for the boundary case where $n \leq 3$, a $1$ must be split out, wi ```python title="max_product_cutting.py" def max_product_cutting(n: int) -> int: - """最大切分乘积:贪心""" - # 当 n <= 3 时,必须切分出一个 1 + """Maximum product of cutting: Greedy""" + # When n <= 3, must cut out a 1 if n <= 3: return 1 * (n - 1) - # 贪心地切分出 3 ,a 为 3 的个数,b 为余数 + # Greedy cut out 3s, a is the number of 3s, b is the remainder a, b = n // 3, n % 3 if b == 1: - # 当余数为 1 时,将一对 1 * 3 转化为 2 * 2 + # When the remainder is 1, convert a pair of 1 * 3 into 2 * 2 return int(math.pow(3, a - 1)) * 2 * 2 if b == 2: - # 当余数为 2 时,不做处理 + # When the remainder is 2, do nothing return int(math.pow(3, a)) * 2 - # 当余数为 0 时,不做处理 + # When the remainder is 0, do nothing return int(math.pow(3, a)) ``` === "C++" ```cpp title="max_product_cutting.cpp" - /* 最大切分乘积:贪心 */ - int maxProductCutting(int n) { - // 当 n <= 3 时,必须切分出一个 1 - if (n <= 3) { - return 1 * (n - 1); - } - // 贪心地切分出 3 ,a 为 3 的个数,b 为余数 - int a = n / 3; - int b = n % 3; - if (b == 1) { - // 当余数为 1 时,将一对 1 * 3 转化为 2 * 2 - return (int)pow(3, a - 1) * 2 * 2; - } - if (b == 2) { - // 当余数为 2 时,不做处理 - return (int)pow(3, a) * 2; - } - // 当余数为 0 时,不做处理 - return (int)pow(3, a); - } + [class]{}-[func]{maxProductCutting} ``` === "Java" ```java title="max_product_cutting.java" - /* 最大切分乘积:贪心 */ + /* Maximum product of cutting: Greedy */ int maxProductCutting(int n) { - // 当 n <= 3 时,必须切分出一个 1 + // When n <= 3, must cut out a 1 if (n <= 3) { return 1 * (n - 1); } - // 贪心地切分出 3 ,a 为 3 的个数,b 为余数 + // Greedy cut out 3s, a is the number of 3s, b is the remainder int a = n / 3; int b = n % 3; if (b == 1) { - // 当余数为 1 时,将一对 1 * 3 转化为 2 * 2 + // When the remainder is 1, convert a pair of 1 * 3 into 2 * 2 return (int) Math.pow(3, a - 1) * 2 * 2; } if (b == 2) { - // 当余数为 2 时,不做处理 + // When the remainder is 2, do nothing return (int) Math.pow(3, a) * 2; } - // 当余数为 0 时,不做处理 + // When the remainder is 0, do nothing return (int) Math.pow(3, a); } ``` @@ -146,226 +127,55 @@ Please note, for the boundary case where $n \leq 3$, a $1$ must be split out, wi === "C#" ```csharp title="max_product_cutting.cs" - /* 最大切分乘积:贪心 */ - int MaxProductCutting(int n) { - // 当 n <= 3 时,必须切分出一个 1 - if (n <= 3) { - return 1 * (n - 1); - } - // 贪心地切分出 3 ,a 为 3 的个数,b 为余数 - int a = n / 3; - int b = n % 3; - if (b == 1) { - // 当余数为 1 时,将一对 1 * 3 转化为 2 * 2 - return (int)Math.Pow(3, a - 1) * 2 * 2; - } - if (b == 2) { - // 当余数为 2 时,不做处理 - return (int)Math.Pow(3, a) * 2; - } - // 当余数为 0 时,不做处理 - return (int)Math.Pow(3, a); - } + [class]{max_product_cutting}-[func]{MaxProductCutting} ``` === "Go" ```go title="max_product_cutting.go" - /* 最大切分乘积:贪心 */ - func maxProductCutting(n int) int { - // 当 n <= 3 时,必须切分出一个 1 - if n <= 3 { - return 1 * (n - 1) - } - // 贪心地切分出 3 ,a 为 3 的个数,b 为余数 - a := n / 3 - b := n % 3 - if b == 1 { - // 当余数为 1 时,将一对 1 * 3 转化为 2 * 2 - return int(math.Pow(3, float64(a-1))) * 2 * 2 - } - if b == 2 { - // 当余数为 2 时,不做处理 - return int(math.Pow(3, float64(a))) * 2 - } - // 当余数为 0 时,不做处理 - return int(math.Pow(3, float64(a))) - } + [class]{}-[func]{maxProductCutting} ``` === "Swift" ```swift title="max_product_cutting.swift" - /* 最大切分乘积:贪心 */ - func maxProductCutting(n: Int) -> Int { - // 当 n <= 3 时,必须切分出一个 1 - if n <= 3 { - return 1 * (n - 1) - } - // 贪心地切分出 3 ,a 为 3 的个数,b 为余数 - let a = n / 3 - let b = n % 3 - if b == 1 { - // 当余数为 1 时,将一对 1 * 3 转化为 2 * 2 - return pow(3, a - 1) * 2 * 2 - } - if b == 2 { - // 当余数为 2 时,不做处理 - return pow(3, a) * 2 - } - // 当余数为 0 时,不做处理 - return pow(3, a) - } + [class]{}-[func]{maxProductCutting} ``` === "JS" ```javascript title="max_product_cutting.js" - /* 最大切分乘积:贪心 */ - function maxProductCutting(n) { - // 当 n <= 3 时,必须切分出一个 1 - if (n <= 3) { - return 1 * (n - 1); - } - // 贪心地切分出 3 ,a 为 3 的个数,b 为余数 - let a = Math.floor(n / 3); - let b = n % 3; - if (b === 1) { - // 当余数为 1 时,将一对 1 * 3 转化为 2 * 2 - return Math.pow(3, a - 1) * 2 * 2; - } - if (b === 2) { - // 当余数为 2 时,不做处理 - return Math.pow(3, a) * 2; - } - // 当余数为 0 时,不做处理 - return Math.pow(3, a); - } + [class]{}-[func]{maxProductCutting} ``` === "TS" ```typescript title="max_product_cutting.ts" - /* 最大切分乘积:贪心 */ - function maxProductCutting(n: number): number { - // 当 n <= 3 时,必须切分出一个 1 - if (n <= 3) { - return 1 * (n - 1); - } - // 贪心地切分出 3 ,a 为 3 的个数,b 为余数 - let a: number = Math.floor(n / 3); - let b: number = n % 3; - if (b === 1) { - // 当余数为 1 时,将一对 1 * 3 转化为 2 * 2 - return Math.pow(3, a - 1) * 2 * 2; - } - if (b === 2) { - // 当余数为 2 时,不做处理 - return Math.pow(3, a) * 2; - } - // 当余数为 0 时,不做处理 - return Math.pow(3, a); - } + [class]{}-[func]{maxProductCutting} ``` === "Dart" ```dart title="max_product_cutting.dart" - /* 最大切分乘积:贪心 */ - int maxProductCutting(int n) { - // 当 n <= 3 时,必须切分出一个 1 - if (n <= 3) { - return 1 * (n - 1); - } - // 贪心地切分出 3 ,a 为 3 的个数,b 为余数 - int a = n ~/ 3; - int b = n % 3; - if (b == 1) { - // 当余数为 1 时,将一对 1 * 3 转化为 2 * 2 - return (pow(3, a - 1) * 2 * 2).toInt(); - } - if (b == 2) { - // 当余数为 2 时,不做处理 - return (pow(3, a) * 2).toInt(); - } - // 当余数为 0 时,不做处理 - return pow(3, a).toInt(); - } + [class]{}-[func]{maxProductCutting} ``` === "Rust" ```rust title="max_product_cutting.rs" - /* 最大切分乘积:贪心 */ - fn max_product_cutting(n: i32) -> i32 { - // 当 n <= 3 时,必须切分出一个 1 - if n <= 3 { - return 1 * (n - 1); - } - // 贪心地切分出 3 ,a 为 3 的个数,b 为余数 - let a = n / 3; - let b = n % 3; - if b == 1 { - // 当余数为 1 时,将一对 1 * 3 转化为 2 * 2 - 3_i32.pow(a as u32 - 1) * 2 * 2 - } else if b == 2 { - // 当余数为 2 时,不做处理 - 3_i32.pow(a as u32) * 2 - } else { - // 当余数为 0 时,不做处理 - 3_i32.pow(a as u32) - } - } + [class]{}-[func]{max_product_cutting} ``` === "C" ```c title="max_product_cutting.c" - /* 最大切分乘积:贪心 */ - int maxProductCutting(int n) { - // 当 n <= 3 时,必须切分出一个 1 - if (n <= 3) { - return 1 * (n - 1); - } - // 贪心地切分出 3 ,a 为 3 的个数,b 为余数 - int a = n / 3; - int b = n % 3; - if (b == 1) { - // 当余数为 1 时,将一对 1 * 3 转化为 2 * 2 - return pow(3, a - 1) * 2 * 2; - } - if (b == 2) { - // 当余数为 2 时,不做处理 - return pow(3, a) * 2; - } - // 当余数为 0 时,不做处理 - return pow(3, a); - } + [class]{}-[func]{maxProductCutting} ``` === "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)).toInt() * 2 * 2 - } - if (b == 2) { - // 当余数为 2 时,不做处理 - return 3.0.pow(a).toInt() * 2 * 2 - } - // 当余数为 0 时,不做处理 - return 3.0.pow(a).toInt() - } + [class]{}-[func]{maxProductCutting} ``` === "Ruby" @@ -380,11 +190,6 @@ Please note, for the boundary case where $n \leq 3$, a $1$ must be split out, wi [class]{}-[func]{maxProductCutting} ``` -??? pythontutor "Code Visualization" - -
- - ![Calculation method of the maximum product after cutting](max_product_cutting_problem.assets/max_product_cutting_greedy_calculation.png){ class="animation-figure" }

Figure 15-16   Calculation method of the maximum product after cutting

diff --git a/en/docs/chapter_hashing/hash_algorithm.md b/en/docs/chapter_hashing/hash_algorithm.md index b580e9d59..cda803595 100644 --- a/en/docs/chapter_hashing/hash_algorithm.md +++ b/en/docs/chapter_hashing/hash_algorithm.md @@ -56,7 +56,7 @@ The design of hash algorithms is a complex issue that requires consideration of ```python title="simple_hash.py" def add_hash(key: str) -> int: - """加法哈希""" + """Additive hash""" hash = 0 modulus = 1000000007 for c in key: @@ -64,7 +64,7 @@ The design of hash algorithms is a complex issue that requires consideration of return hash % modulus def mul_hash(key: str) -> int: - """乘法哈希""" + """Multiplicative hash""" hash = 0 modulus = 1000000007 for c in key: @@ -72,7 +72,7 @@ The design of hash algorithms is a complex issue that requires consideration of return hash % modulus def xor_hash(key: str) -> int: - """异或哈希""" + """XOR hash""" hash = 0 modulus = 1000000007 for c in key: @@ -80,7 +80,7 @@ The design of hash algorithms is a complex issue that requires consideration of return hash % modulus def rot_hash(key: str) -> int: - """旋转哈希""" + """Rotational hash""" hash = 0 modulus = 1000000007 for c in key: @@ -91,51 +91,19 @@ The design of hash algorithms is a complex issue that requires consideration of === "C++" ```cpp title="simple_hash.cpp" - /* 加法哈希 */ - int addHash(string key) { - long long hash = 0; - const int MODULUS = 1000000007; - for (unsigned char c : key) { - hash = (hash + (int)c) % MODULUS; - } - return (int)hash; - } + [class]{}-[func]{addHash} - /* 乘法哈希 */ - int mulHash(string key) { - long long hash = 0; - const int MODULUS = 1000000007; - for (unsigned char c : key) { - hash = (31 * hash + (int)c) % MODULUS; - } - return (int)hash; - } + [class]{}-[func]{mulHash} - /* 异或哈希 */ - int xorHash(string key) { - int hash = 0; - const int MODULUS = 1000000007; - for (unsigned char c : key) { - hash ^= (int)c; - } - return hash & MODULUS; - } + [class]{}-[func]{xorHash} - /* 旋转哈希 */ - int rotHash(string key) { - long long hash = 0; - const int MODULUS = 1000000007; - for (unsigned char c : key) { - hash = ((hash << 4) ^ (hash >> 28) ^ (int)c) % MODULUS; - } - return (int)hash; - } + [class]{}-[func]{rotHash} ``` === "Java" ```java title="simple_hash.java" - /* 加法哈希 */ + /* Additive hash */ int addHash(String key) { long hash = 0; final int MODULUS = 1000000007; @@ -145,7 +113,7 @@ The design of hash algorithms is a complex issue that requires consideration of return (int) hash; } - /* 乘法哈希 */ + /* Multiplicative hash */ int mulHash(String key) { long hash = 0; final int MODULUS = 1000000007; @@ -155,7 +123,7 @@ The design of hash algorithms is a complex issue that requires consideration of return (int) hash; } - /* 异或哈希 */ + /* XOR hash */ int xorHash(String key) { int hash = 0; final int MODULUS = 1000000007; @@ -165,7 +133,7 @@ The design of hash algorithms is a complex issue that requires consideration of return hash & MODULUS; } - /* 旋转哈希 */ + /* Rotational hash */ int rotHash(String key) { long hash = 0; final int MODULUS = 1000000007; @@ -179,467 +147,121 @@ The design of hash algorithms is a complex issue that requires consideration of === "C#" ```csharp title="simple_hash.cs" - /* 加法哈希 */ - int AddHash(string key) { - long hash = 0; - const int MODULUS = 1000000007; - foreach (char c in key) { - hash = (hash + c) % MODULUS; - } - return (int)hash; - } + [class]{simple_hash}-[func]{AddHash} - /* 乘法哈希 */ - int MulHash(string key) { - long hash = 0; - const int MODULUS = 1000000007; - foreach (char c in key) { - hash = (31 * hash + c) % MODULUS; - } - return (int)hash; - } + [class]{simple_hash}-[func]{MulHash} - /* 异或哈希 */ - int XorHash(string key) { - int hash = 0; - const int MODULUS = 1000000007; - foreach (char c in key) { - hash ^= c; - } - return hash & MODULUS; - } + [class]{simple_hash}-[func]{XorHash} - /* 旋转哈希 */ - int RotHash(string key) { - long hash = 0; - const int MODULUS = 1000000007; - foreach (char c in key) { - hash = ((hash << 4) ^ (hash >> 28) ^ c) % MODULUS; - } - return (int)hash; - } + [class]{simple_hash}-[func]{RotHash} ``` === "Go" ```go title="simple_hash.go" - /* 加法哈希 */ - func addHash(key string) int { - var hash int64 - var modulus int64 + [class]{}-[func]{addHash} - modulus = 1000000007 - for _, b := range []byte(key) { - hash = (hash + int64(b)) % modulus - } - return int(hash) - } + [class]{}-[func]{mulHash} - /* 乘法哈希 */ - func mulHash(key string) int { - var hash int64 - var modulus int64 + [class]{}-[func]{xorHash} - modulus = 1000000007 - for _, b := range []byte(key) { - hash = (31*hash + int64(b)) % modulus - } - return int(hash) - } - - /* 异或哈希 */ - func xorHash(key string) int { - hash := 0 - modulus := 1000000007 - for _, b := range []byte(key) { - fmt.Println(int(b)) - hash ^= int(b) - hash = (31*hash + int(b)) % modulus - } - return hash & modulus - } - - /* 旋转哈希 */ - func rotHash(key string) int { - var hash int64 - var modulus int64 - - modulus = 1000000007 - for _, b := range []byte(key) { - hash = ((hash << 4) ^ (hash >> 28) ^ int64(b)) % modulus - } - return int(hash) - } + [class]{}-[func]{rotHash} ``` === "Swift" ```swift title="simple_hash.swift" - /* 加法哈希 */ - func addHash(key: String) -> Int { - var hash = 0 - let MODULUS = 1_000_000_007 - for c in key { - for scalar in c.unicodeScalars { - hash = (hash + Int(scalar.value)) % MODULUS - } - } - return hash - } + [class]{}-[func]{addHash} - /* 乘法哈希 */ - func mulHash(key: String) -> Int { - var hash = 0 - let MODULUS = 1_000_000_007 - for c in key { - for scalar in c.unicodeScalars { - hash = (31 * hash + Int(scalar.value)) % MODULUS - } - } - return hash - } + [class]{}-[func]{mulHash} - /* 异或哈希 */ - func xorHash(key: String) -> Int { - var hash = 0 - let MODULUS = 1_000_000_007 - for c in key { - for scalar in c.unicodeScalars { - hash ^= Int(scalar.value) - } - } - return hash & MODULUS - } + [class]{}-[func]{xorHash} - /* 旋转哈希 */ - func rotHash(key: String) -> Int { - var hash = 0 - let MODULUS = 1_000_000_007 - for c in key { - for scalar in c.unicodeScalars { - hash = ((hash << 4) ^ (hash >> 28) ^ Int(scalar.value)) % MODULUS - } - } - return hash - } + [class]{}-[func]{rotHash} ``` === "JS" ```javascript title="simple_hash.js" - /* 加法哈希 */ - function addHash(key) { - let hash = 0; - const MODULUS = 1000000007; - for (const c of key) { - hash = (hash + c.charCodeAt(0)) % MODULUS; - } - return hash; - } + [class]{}-[func]{addHash} - /* 乘法哈希 */ - function mulHash(key) { - let hash = 0; - const MODULUS = 1000000007; - for (const c of key) { - hash = (31 * hash + c.charCodeAt(0)) % MODULUS; - } - return hash; - } + [class]{}-[func]{mulHash} - /* 异或哈希 */ - function xorHash(key) { - let hash = 0; - const MODULUS = 1000000007; - for (const c of key) { - hash ^= c.charCodeAt(0); - } - return hash & MODULUS; - } + [class]{}-[func]{xorHash} - /* 旋转哈希 */ - function rotHash(key) { - let hash = 0; - const MODULUS = 1000000007; - for (const c of key) { - hash = ((hash << 4) ^ (hash >> 28) ^ c.charCodeAt(0)) % MODULUS; - } - return hash; - } + [class]{}-[func]{rotHash} ``` === "TS" ```typescript title="simple_hash.ts" - /* 加法哈希 */ - function addHash(key: string): number { - let hash = 0; - const MODULUS = 1000000007; - for (const c of key) { - hash = (hash + c.charCodeAt(0)) % MODULUS; - } - return hash; - } + [class]{}-[func]{addHash} - /* 乘法哈希 */ - function mulHash(key: string): number { - let hash = 0; - const MODULUS = 1000000007; - for (const c of key) { - hash = (31 * hash + c.charCodeAt(0)) % MODULUS; - } - return hash; - } + [class]{}-[func]{mulHash} - /* 异或哈希 */ - function xorHash(key: string): number { - let hash = 0; - const MODULUS = 1000000007; - for (const c of key) { - hash ^= c.charCodeAt(0); - } - return hash & MODULUS; - } + [class]{}-[func]{xorHash} - /* 旋转哈希 */ - function rotHash(key: string): number { - let hash = 0; - const MODULUS = 1000000007; - for (const c of key) { - hash = ((hash << 4) ^ (hash >> 28) ^ c.charCodeAt(0)) % MODULUS; - } - return hash; - } + [class]{}-[func]{rotHash} ``` === "Dart" ```dart title="simple_hash.dart" - /* 加法哈希 */ - int addHash(String key) { - int hash = 0; - final int MODULUS = 1000000007; - for (int i = 0; i < key.length; i++) { - hash = (hash + key.codeUnitAt(i)) % MODULUS; - } - return hash; - } + [class]{}-[func]{addHash} - /* 乘法哈希 */ - int mulHash(String key) { - int hash = 0; - final int MODULUS = 1000000007; - for (int i = 0; i < key.length; i++) { - hash = (31 * hash + key.codeUnitAt(i)) % MODULUS; - } - return hash; - } + [class]{}-[func]{mulHash} - /* 异或哈希 */ - int xorHash(String key) { - int hash = 0; - final int MODULUS = 1000000007; - for (int i = 0; i < key.length; i++) { - hash ^= key.codeUnitAt(i); - } - return hash & MODULUS; - } + [class]{}-[func]{xorHash} - /* 旋转哈希 */ - int rotHash(String key) { - int hash = 0; - final int MODULUS = 1000000007; - for (int i = 0; i < key.length; i++) { - hash = ((hash << 4) ^ (hash >> 28) ^ key.codeUnitAt(i)) % MODULUS; - } - return hash; - } + [class]{}-[func]{rotHash} ``` === "Rust" ```rust title="simple_hash.rs" - /* 加法哈希 */ - fn add_hash(key: &str) -> i32 { - let mut hash = 0_i64; - const MODULUS: i64 = 1000000007; + [class]{}-[func]{add_hash} - for c in key.chars() { - hash = (hash + c as i64) % MODULUS; - } + [class]{}-[func]{mul_hash} - hash as i32 - } + [class]{}-[func]{xor_hash} - /* 乘法哈希 */ - fn mul_hash(key: &str) -> i32 { - let mut hash = 0_i64; - const MODULUS: i64 = 1000000007; - - for c in key.chars() { - hash = (31 * hash + c as i64) % MODULUS; - } - - hash as i32 - } - - /* 异或哈希 */ - fn xor_hash(key: &str) -> i32 { - let mut hash = 0_i64; - const MODULUS: i64 = 1000000007; - - for c in key.chars() { - hash ^= c as i64; - } - - (hash & MODULUS) as i32 - } - - /* 旋转哈希 */ - fn rot_hash(key: &str) -> i32 { - let mut hash = 0_i64; - const MODULUS: i64 = 1000000007; - - for c in key.chars() { - hash = ((hash << 4) ^ (hash >> 28) ^ c as i64) % MODULUS; - } - - hash as i32 - } + [class]{}-[func]{rot_hash} ``` === "C" ```c title="simple_hash.c" - /* 加法哈希 */ - int addHash(char *key) { - long long hash = 0; - const int MODULUS = 1000000007; - for (int i = 0; i < strlen(key); i++) { - hash = (hash + (unsigned char)key[i]) % MODULUS; - } - return (int)hash; - } + [class]{}-[func]{addHash} - /* 乘法哈希 */ - int mulHash(char *key) { - long long hash = 0; - const int MODULUS = 1000000007; - for (int i = 0; i < strlen(key); i++) { - hash = (31 * hash + (unsigned char)key[i]) % MODULUS; - } - return (int)hash; - } + [class]{}-[func]{mulHash} - /* 异或哈希 */ - int xorHash(char *key) { - int hash = 0; - const int MODULUS = 1000000007; + [class]{}-[func]{xorHash} - for (int i = 0; i < strlen(key); i++) { - hash ^= (unsigned char)key[i]; - } - return hash & MODULUS; - } - - /* 旋转哈希 */ - int rotHash(char *key) { - long long hash = 0; - const int MODULUS = 1000000007; - for (int i = 0; i < strlen(key); i++) { - hash = ((hash << 4) ^ (hash >> 28) ^ (unsigned char)key[i]) % MODULUS; - } - - return (int)hash; - } + [class]{}-[func]{rotHash} ``` === "Kotlin" ```kotlin title="simple_hash.kt" - /* 加法哈希 */ - fun addHash(key: String): Int { - var hash = 0L - val MODULUS = 1000000007 - for (c in key.toCharArray()) { - hash = (hash + c.code) % MODULUS - } - return hash.toInt() - } + [class]{}-[func]{addHash} - /* 乘法哈希 */ - fun mulHash(key: String): Int { - var hash = 0L - val MODULUS = 1000000007 - for (c in key.toCharArray()) { - hash = (31 * hash + c.code) % MODULUS - } - return hash.toInt() - } + [class]{}-[func]{mulHash} - /* 异或哈希 */ - fun xorHash(key: String): Int { - var hash = 0 - val MODULUS = 1000000007 - for (c in key.toCharArray()) { - hash = hash xor c.code - } - return hash and MODULUS - } + [class]{}-[func]{xorHash} - /* 旋转哈希 */ - fun rotHash(key: String): Int { - var hash = 0L - val MODULUS = 1000000007 - for (c in key.toCharArray()) { - hash = ((hash shl 4) xor (hash shr 28) xor c.code.toLong()) % MODULUS - } - return hash.toInt() - } + [class]{}-[func]{rotHash} ``` === "Ruby" ```ruby title="simple_hash.rb" - ### 加法哈希 ### - def add_hash(key) - hash = 0 - modulus = 1_000_000_007 + [class]{}-[func]{add_hash} - key.each_char { |c| hash += c.ord } + [class]{}-[func]{mul_hash} - hash % modulus - end + [class]{}-[func]{xor_hash} - ### 乘法哈希 ### - def mul_hash(key) - hash = 0 - modulus = 1_000_000_007 - - key.each_char { |c| hash = 31 * hash + c.ord } - - hash % modulus - end - - ### 异或哈希 ### - def xor_hash(key) - hash = 0 - modulus = 1_000_000_007 - - key.each_char { |c| hash ^= c.ord } - - hash % modulus - end - - ### 旋转哈希 ### - def rot_hash(key) - hash = 0 - modulus = 1_000_000_007 - - key.each_char { |c| hash = (hash << 4) ^ (hash >> 28) ^ c.ord } - - hash % modulus - end + [class]{}-[func]{rot_hash} ``` === "Zig" @@ -654,11 +276,6 @@ The design of hash algorithms is a complex issue that requires consideration of [class]{}-[func]{rotHash} ``` -??? pythontutor "Code Visualization" - -
- - It is observed that the last step of each hash algorithm is to take the modulus of the large prime number $1000000007$ to ensure that the hash value is within an appropriate range. It is worth pondering why emphasis is placed on modulo a prime number, or what are the disadvantages of modulo a composite number? This is an interesting question. To conclude: **Using a large prime number as the modulus can maximize the uniform distribution of hash values**. Since a prime number does not share common factors with other numbers, it can reduce the periodic patterns caused by the modulo operation, thus avoiding hash collisions. diff --git a/en/docs/chapter_hashing/hash_collision.md b/en/docs/chapter_hashing/hash_collision.md index fa5ad8ef5..9c12daf86 100644 --- a/en/docs/chapter_hashing/hash_collision.md +++ b/en/docs/chapter_hashing/hash_collision.md @@ -41,57 +41,57 @@ The code below provides a simple implementation of a separate chaining hash tabl ```python title="hash_map_chaining.py" class HashMapChaining: - """链式地址哈希表""" + """Chained address hash table""" def __init__(self): - """构造方法""" - self.size = 0 # 键值对数量 - self.capacity = 4 # 哈希表容量 - self.load_thres = 2.0 / 3.0 # 触发扩容的负载因子阈值 - self.extend_ratio = 2 # 扩容倍数 - self.buckets = [[] for _ in range(self.capacity)] # 桶数组 + """Constructor""" + self.size = 0 # Number of key-value pairs + self.capacity = 4 # Hash table capacity + self.load_thres = 2.0 / 3.0 # Load factor threshold for triggering expansion + self.extend_ratio = 2 # Expansion multiplier + self.buckets = [[] for _ in range(self.capacity)] # Bucket array def hash_func(self, key: int) -> int: - """哈希函数""" + """Hash function""" return key % self.capacity def load_factor(self) -> float: - """负载因子""" + """Load factor""" return self.size / self.capacity def get(self, key: int) -> str | None: - """查询操作""" + """Query operation""" index = self.hash_func(key) bucket = self.buckets[index] - # 遍历桶,若找到 key ,则返回对应 val + # Traverse the bucket, if the key is found, return the corresponding val for pair in bucket: if pair.key == key: return pair.val - # 若未找到 key ,则返回 None + # If the key is not found, return None return None def put(self, key: int, val: str): - """添加操作""" - # 当负载因子超过阈值时,执行扩容 + """Add operation""" + # When the load factor exceeds the threshold, perform expansion if self.load_factor() > self.load_thres: self.extend() index = self.hash_func(key) bucket = self.buckets[index] - # 遍历桶,若遇到指定 key ,则更新对应 val 并返回 + # Traverse the bucket, if the specified key is encountered, update the corresponding val and return for pair in bucket: if pair.key == key: pair.val = val return - # 若无该 key ,则将键值对添加至尾部 + # If the key is not found, add the key-value pair to the end pair = Pair(key, val) bucket.append(pair) self.size += 1 def remove(self, key: int): - """删除操作""" + """Remove operation""" index = self.hash_func(key) bucket = self.buckets[index] - # 遍历桶,从中删除键值对 + # Traverse the bucket, remove the key-value pair from it for pair in bucket: if pair.key == key: bucket.remove(pair) @@ -99,20 +99,20 @@ The code below provides a simple implementation of a separate chaining hash tabl break def extend(self): - """扩容哈希表""" - # 暂存原哈希表 + """Extend hash table""" + # Temporarily store the original hash table buckets = self.buckets - # 初始化扩容后的新哈希表 + # Initialize the extended new hash table self.capacity *= self.extend_ratio self.buckets = [[] for _ in range(self.capacity)] self.size = 0 - # 将键值对从原哈希表搬运至新哈希表 + # Move key-value pairs from the original hash table to the new hash table for bucket in buckets: for pair in bucket: self.put(pair.key, pair.val) def print(self): - """打印哈希表""" + """Print hash table""" for bucket in self.buckets: res = [] for pair in bucket: @@ -123,133 +123,21 @@ The code below provides a simple implementation of a separate chaining hash tabl === "C++" ```cpp title="hash_map_chaining.cpp" - /* 链式地址哈希表 */ - class HashMapChaining { - private: - int size; // 键值对数量 - int capacity; // 哈希表容量 - double loadThres; // 触发扩容的负载因子阈值 - int extendRatio; // 扩容倍数 - vector> buckets; // 桶数组 - - public: - /* 构造方法 */ - HashMapChaining() : size(0), capacity(4), loadThres(2.0 / 3.0), extendRatio(2) { - buckets.resize(capacity); - } - - /* 析构方法 */ - ~HashMapChaining() { - for (auto &bucket : buckets) { - for (Pair *pair : bucket) { - // 释放内存 - delete pair; - } - } - } - - /* 哈希函数 */ - int hashFunc(int key) { - return key % capacity; - } - - /* 负载因子 */ - double loadFactor() { - return (double)size / (double)capacity; - } - - /* 查询操作 */ - string get(int key) { - int index = hashFunc(key); - // 遍历桶,若找到 key ,则返回对应 val - for (Pair *pair : buckets[index]) { - if (pair->key == key) { - return pair->val; - } - } - // 若未找到 key ,则返回空字符串 - return ""; - } - - /* 添加操作 */ - void put(int key, string val) { - // 当负载因子超过阈值时,执行扩容 - if (loadFactor() > loadThres) { - extend(); - } - int index = hashFunc(key); - // 遍历桶,若遇到指定 key ,则更新对应 val 并返回 - for (Pair *pair : buckets[index]) { - if (pair->key == key) { - pair->val = val; - return; - } - } - // 若无该 key ,则将键值对添加至尾部 - buckets[index].push_back(new Pair(key, val)); - size++; - } - - /* 删除操作 */ - void remove(int key) { - int index = hashFunc(key); - auto &bucket = buckets[index]; - // 遍历桶,从中删除键值对 - for (int i = 0; i < bucket.size(); i++) { - if (bucket[i]->key == key) { - Pair *tmp = bucket[i]; - bucket.erase(bucket.begin() + i); // 从中删除键值对 - delete tmp; // 释放内存 - size--; - return; - } - } - } - - /* 扩容哈希表 */ - void extend() { - // 暂存原哈希表 - vector> bucketsTmp = buckets; - // 初始化扩容后的新哈希表 - capacity *= extendRatio; - buckets.clear(); - buckets.resize(capacity); - size = 0; - // 将键值对从原哈希表搬运至新哈希表 - for (auto &bucket : bucketsTmp) { - for (Pair *pair : bucket) { - put(pair->key, pair->val); - // 释放内存 - delete pair; - } - } - } - - /* 打印哈希表 */ - void print() { - for (auto &bucket : buckets) { - cout << "["; - for (Pair *pair : bucket) { - cout << pair->key << " -> " << pair->val << ", "; - } - cout << "]\n"; - } - } - }; + [class]{HashMapChaining}-[func]{} ``` === "Java" ```java title="hash_map_chaining.java" - /* 链式地址哈希表 */ + /* Chained address hash table */ class HashMapChaining { - int size; // 键值对数量 - int capacity; // 哈希表容量 - double loadThres; // 触发扩容的负载因子阈值 - int extendRatio; // 扩容倍数 - List> buckets; // 桶数组 + int size; // Number of key-value pairs + int capacity; // Hash table capacity + double loadThres; // Load factor threshold for triggering expansion + int extendRatio; // Expansion multiplier + List> buckets; // Bucket array - /* 构造方法 */ + /* Constructor */ public HashMapChaining() { size = 0; capacity = 4; @@ -261,56 +149,56 @@ The code below provides a simple implementation of a separate chaining hash tabl } } - /* 哈希函数 */ + /* Hash function */ int hashFunc(int key) { return key % capacity; } - /* 负载因子 */ + /* Load factor */ double loadFactor() { return (double) size / capacity; } - /* 查询操作 */ + /* Query operation */ String get(int key) { int index = hashFunc(key); List bucket = buckets.get(index); - // 遍历桶,若找到 key ,则返回对应 val + // Traverse the bucket, if the key is found, return the corresponding val for (Pair pair : bucket) { if (pair.key == key) { return pair.val; } } - // 若未找到 key ,则返回 null + // If key is not found, return null return null; } - /* 添加操作 */ + /* Add operation */ void put(int key, String val) { - // 当负载因子超过阈值时,执行扩容 + // When the load factor exceeds the threshold, perform expansion if (loadFactor() > loadThres) { extend(); } int index = hashFunc(key); List bucket = buckets.get(index); - // 遍历桶,若遇到指定 key ,则更新对应 val 并返回 + // Traverse the bucket, if the specified key is encountered, update the corresponding val and return for (Pair pair : bucket) { if (pair.key == key) { pair.val = val; return; } } - // 若无该 key ,则将键值对添加至尾部 + // If the key is not found, add the key-value pair to the end Pair pair = new Pair(key, val); bucket.add(pair); size++; } - /* 删除操作 */ + /* Remove operation */ void remove(int key) { int index = hashFunc(key); List bucket = buckets.get(index); - // 遍历桶,从中删除键值对 + // Traverse the bucket, remove the key-value pair from it for (Pair pair : bucket) { if (pair.key == key) { bucket.remove(pair); @@ -320,18 +208,18 @@ The code below provides a simple implementation of a separate chaining hash tabl } } - /* 扩容哈希表 */ + /* Extend hash table */ void extend() { - // 暂存原哈希表 + // Temporarily store the original hash table List> bucketsTmp = buckets; - // 初始化扩容后的新哈希表 + // Initialize the extended new hash table capacity *= extendRatio; buckets = new ArrayList<>(capacity); for (int i = 0; i < capacity; i++) { buckets.add(new ArrayList<>()); } size = 0; - // 将键值对从原哈希表搬运至新哈希表 + // Move key-value pairs from the original hash table to the new hash table for (List bucket : bucketsTmp) { for (Pair pair : bucket) { put(pair.key, pair.val); @@ -339,7 +227,7 @@ The code below provides a simple implementation of a separate chaining hash tabl } } - /* 打印哈希表 */ + /* Print hash table */ void print() { for (List bucket : buckets) { List res = new ArrayList<>(); @@ -355,1170 +243,63 @@ The code below provides a simple implementation of a separate chaining hash tabl === "C#" ```csharp title="hash_map_chaining.cs" - /* 链式地址哈希表 */ - class HashMapChaining { - int size; // 键值对数量 - int capacity; // 哈希表容量 - double loadThres; // 触发扩容的负载因子阈值 - int extendRatio; // 扩容倍数 - List> buckets; // 桶数组 - - /* 构造方法 */ - public HashMapChaining() { - size = 0; - capacity = 4; - loadThres = 2.0 / 3.0; - extendRatio = 2; - buckets = new List>(capacity); - for (int i = 0; i < capacity; i++) { - buckets.Add([]); - } - } - - /* 哈希函数 */ - int HashFunc(int key) { - return key % capacity; - } - - /* 负载因子 */ - double LoadFactor() { - return (double)size / capacity; - } - - /* 查询操作 */ - public string? Get(int key) { - int index = HashFunc(key); - // 遍历桶,若找到 key ,则返回对应 val - foreach (Pair pair in buckets[index]) { - if (pair.key == key) { - return pair.val; - } - } - // 若未找到 key ,则返回 null - return null; - } - - /* 添加操作 */ - public void Put(int key, string val) { - // 当负载因子超过阈值时,执行扩容 - if (LoadFactor() > loadThres) { - Extend(); - } - int index = HashFunc(key); - // 遍历桶,若遇到指定 key ,则更新对应 val 并返回 - foreach (Pair pair in buckets[index]) { - if (pair.key == key) { - pair.val = val; - return; - } - } - // 若无该 key ,则将键值对添加至尾部 - buckets[index].Add(new Pair(key, val)); - size++; - } - - /* 删除操作 */ - public void Remove(int key) { - int index = HashFunc(key); - // 遍历桶,从中删除键值对 - foreach (Pair pair in buckets[index].ToList()) { - if (pair.key == key) { - buckets[index].Remove(pair); - size--; - break; - } - } - } - - /* 扩容哈希表 */ - void Extend() { - // 暂存原哈希表 - List> bucketsTmp = buckets; - // 初始化扩容后的新哈希表 - capacity *= extendRatio; - buckets = new List>(capacity); - for (int i = 0; i < capacity; i++) { - buckets.Add([]); - } - size = 0; - // 将键值对从原哈希表搬运至新哈希表 - foreach (List bucket in bucketsTmp) { - foreach (Pair pair in bucket) { - Put(pair.key, pair.val); - } - } - } - - /* 打印哈希表 */ - public void Print() { - foreach (List bucket in buckets) { - List res = []; - foreach (Pair pair in bucket) { - res.Add(pair.key + " -> " + pair.val); - } - foreach (string kv in res) { - Console.WriteLine(kv); - } - } - } - } + [class]{HashMapChaining}-[func]{} ``` === "Go" ```go title="hash_map_chaining.go" - /* 链式地址哈希表 */ - type hashMapChaining struct { - size int // 键值对数量 - capacity int // 哈希表容量 - loadThres float64 // 触发扩容的负载因子阈值 - extendRatio int // 扩容倍数 - buckets [][]pair // 桶数组 - } - - /* 构造方法 */ - func newHashMapChaining() *hashMapChaining { - buckets := make([][]pair, 4) - for i := 0; i < 4; i++ { - buckets[i] = make([]pair, 0) - } - return &hashMapChaining{ - size: 0, - capacity: 4, - loadThres: 2.0 / 3.0, - extendRatio: 2, - buckets: buckets, - } - } - - /* 哈希函数 */ - func (m *hashMapChaining) hashFunc(key int) int { - return key % m.capacity - } - - /* 负载因子 */ - func (m *hashMapChaining) loadFactor() float64 { - return float64(m.size) / float64(m.capacity) - } - - /* 查询操作 */ - func (m *hashMapChaining) get(key int) string { - idx := m.hashFunc(key) - bucket := m.buckets[idx] - // 遍历桶,若找到 key ,则返回对应 val - for _, p := range bucket { - if p.key == key { - return p.val - } - } - // 若未找到 key ,则返回空字符串 - return "" - } - - /* 添加操作 */ - func (m *hashMapChaining) put(key int, val string) { - // 当负载因子超过阈值时,执行扩容 - if m.loadFactor() > m.loadThres { - m.extend() - } - idx := m.hashFunc(key) - // 遍历桶,若遇到指定 key ,则更新对应 val 并返回 - for i := range m.buckets[idx] { - if m.buckets[idx][i].key == key { - m.buckets[idx][i].val = val - return - } - } - // 若无该 key ,则将键值对添加至尾部 - p := pair{ - key: key, - val: val, - } - m.buckets[idx] = append(m.buckets[idx], p) - m.size += 1 - } - - /* 删除操作 */ - func (m *hashMapChaining) remove(key int) { - idx := m.hashFunc(key) - // 遍历桶,从中删除键值对 - for i, p := range m.buckets[idx] { - if p.key == key { - // 切片删除 - m.buckets[idx] = append(m.buckets[idx][:i], m.buckets[idx][i+1:]...) - m.size -= 1 - break - } - } - } - - /* 扩容哈希表 */ - func (m *hashMapChaining) extend() { - // 暂存原哈希表 - tmpBuckets := make([][]pair, len(m.buckets)) - for i := 0; i < len(m.buckets); i++ { - tmpBuckets[i] = make([]pair, len(m.buckets[i])) - copy(tmpBuckets[i], m.buckets[i]) - } - // 初始化扩容后的新哈希表 - m.capacity *= m.extendRatio - m.buckets = make([][]pair, m.capacity) - for i := 0; i < m.capacity; i++ { - m.buckets[i] = make([]pair, 0) - } - m.size = 0 - // 将键值对从原哈希表搬运至新哈希表 - for _, bucket := range tmpBuckets { - for _, p := range bucket { - m.put(p.key, p.val) - } - } - } - - /* 打印哈希表 */ - func (m *hashMapChaining) print() { - var builder strings.Builder - - for _, bucket := range m.buckets { - builder.WriteString("[") - for _, p := range bucket { - builder.WriteString(strconv.Itoa(p.key) + " -> " + p.val + " ") - } - builder.WriteString("]") - fmt.Println(builder.String()) - builder.Reset() - } - } + [class]{hashMapChaining}-[func]{} ``` === "Swift" ```swift title="hash_map_chaining.swift" - /* 链式地址哈希表 */ - class HashMapChaining { - var size: Int // 键值对数量 - var capacity: Int // 哈希表容量 - var loadThres: Double // 触发扩容的负载因子阈值 - var extendRatio: Int // 扩容倍数 - var buckets: [[Pair]] // 桶数组 - - /* 构造方法 */ - init() { - size = 0 - capacity = 4 - loadThres = 2.0 / 3.0 - extendRatio = 2 - buckets = Array(repeating: [], count: capacity) - } - - /* 哈希函数 */ - func hashFunc(key: Int) -> Int { - key % capacity - } - - /* 负载因子 */ - func loadFactor() -> Double { - Double(size) / Double(capacity) - } - - /* 查询操作 */ - func get(key: Int) -> String? { - let index = hashFunc(key: key) - let bucket = buckets[index] - // 遍历桶,若找到 key ,则返回对应 val - for pair in bucket { - if pair.key == key { - return pair.val - } - } - // 若未找到 key ,则返回 nil - return nil - } - - /* 添加操作 */ - func put(key: Int, val: String) { - // 当负载因子超过阈值时,执行扩容 - if loadFactor() > loadThres { - extend() - } - let index = hashFunc(key: key) - let bucket = buckets[index] - // 遍历桶,若遇到指定 key ,则更新对应 val 并返回 - for pair in bucket { - if pair.key == key { - pair.val = val - return - } - } - // 若无该 key ,则将键值对添加至尾部 - let pair = Pair(key: key, val: val) - buckets[index].append(pair) - size += 1 - } - - /* 删除操作 */ - func remove(key: Int) { - let index = hashFunc(key: key) - let bucket = buckets[index] - // 遍历桶,从中删除键值对 - for (pairIndex, pair) in bucket.enumerated() { - if pair.key == key { - buckets[index].remove(at: pairIndex) - size -= 1 - break - } - } - } - - /* 扩容哈希表 */ - func extend() { - // 暂存原哈希表 - let bucketsTmp = buckets - // 初始化扩容后的新哈希表 - capacity *= extendRatio - buckets = Array(repeating: [], count: capacity) - size = 0 - // 将键值对从原哈希表搬运至新哈希表 - for bucket in bucketsTmp { - for pair in bucket { - put(key: pair.key, val: pair.val) - } - } - } - - /* 打印哈希表 */ - func print() { - for bucket in buckets { - let res = bucket.map { "\($0.key) -> \($0.val)" } - Swift.print(res) - } - } - } + [class]{HashMapChaining}-[func]{} ``` === "JS" ```javascript title="hash_map_chaining.js" - /* 链式地址哈希表 */ - class HashMapChaining { - #size; // 键值对数量 - #capacity; // 哈希表容量 - #loadThres; // 触发扩容的负载因子阈值 - #extendRatio; // 扩容倍数 - #buckets; // 桶数组 - - /* 构造方法 */ - constructor() { - this.#size = 0; - this.#capacity = 4; - this.#loadThres = 2.0 / 3.0; - this.#extendRatio = 2; - this.#buckets = new Array(this.#capacity).fill(null).map((x) => []); - } - - /* 哈希函数 */ - #hashFunc(key) { - return key % this.#capacity; - } - - /* 负载因子 */ - #loadFactor() { - return this.#size / this.#capacity; - } - - /* 查询操作 */ - get(key) { - const index = this.#hashFunc(key); - const bucket = this.#buckets[index]; - // 遍历桶,若找到 key ,则返回对应 val - for (const pair of bucket) { - if (pair.key === key) { - return pair.val; - } - } - // 若未找到 key ,则返回 null - return null; - } - - /* 添加操作 */ - put(key, val) { - // 当负载因子超过阈值时,执行扩容 - if (this.#loadFactor() > this.#loadThres) { - this.#extend(); - } - const index = this.#hashFunc(key); - const bucket = this.#buckets[index]; - // 遍历桶,若遇到指定 key ,则更新对应 val 并返回 - for (const pair of bucket) { - if (pair.key === key) { - pair.val = val; - return; - } - } - // 若无该 key ,则将键值对添加至尾部 - const pair = new Pair(key, val); - bucket.push(pair); - this.#size++; - } - - /* 删除操作 */ - remove(key) { - const index = this.#hashFunc(key); - let bucket = this.#buckets[index]; - // 遍历桶,从中删除键值对 - for (let i = 0; i < bucket.length; i++) { - if (bucket[i].key === key) { - bucket.splice(i, 1); - this.#size--; - break; - } - } - } - - /* 扩容哈希表 */ - #extend() { - // 暂存原哈希表 - const bucketsTmp = this.#buckets; - // 初始化扩容后的新哈希表 - this.#capacity *= this.#extendRatio; - this.#buckets = new Array(this.#capacity).fill(null).map((x) => []); - this.#size = 0; - // 将键值对从原哈希表搬运至新哈希表 - for (const bucket of bucketsTmp) { - for (const pair of bucket) { - this.put(pair.key, pair.val); - } - } - } - - /* 打印哈希表 */ - print() { - for (const bucket of this.#buckets) { - let res = []; - for (const pair of bucket) { - res.push(pair.key + ' -> ' + pair.val); - } - console.log(res); - } - } - } + [class]{HashMapChaining}-[func]{} ``` === "TS" ```typescript title="hash_map_chaining.ts" - /* 链式地址哈希表 */ - class HashMapChaining { - #size: number; // 键值对数量 - #capacity: number; // 哈希表容量 - #loadThres: number; // 触发扩容的负载因子阈值 - #extendRatio: number; // 扩容倍数 - #buckets: Pair[][]; // 桶数组 - - /* 构造方法 */ - constructor() { - this.#size = 0; - this.#capacity = 4; - this.#loadThres = 2.0 / 3.0; - this.#extendRatio = 2; - this.#buckets = new Array(this.#capacity).fill(null).map((x) => []); - } - - /* 哈希函数 */ - #hashFunc(key: number): number { - return key % this.#capacity; - } - - /* 负载因子 */ - #loadFactor(): number { - return this.#size / this.#capacity; - } - - /* 查询操作 */ - get(key: number): string | null { - const index = this.#hashFunc(key); - const bucket = this.#buckets[index]; - // 遍历桶,若找到 key ,则返回对应 val - for (const pair of bucket) { - if (pair.key === key) { - return pair.val; - } - } - // 若未找到 key ,则返回 null - return null; - } - - /* 添加操作 */ - put(key: number, val: string): void { - // 当负载因子超过阈值时,执行扩容 - if (this.#loadFactor() > this.#loadThres) { - this.#extend(); - } - const index = this.#hashFunc(key); - const bucket = this.#buckets[index]; - // 遍历桶,若遇到指定 key ,则更新对应 val 并返回 - for (const pair of bucket) { - if (pair.key === key) { - pair.val = val; - return; - } - } - // 若无该 key ,则将键值对添加至尾部 - const pair = new Pair(key, val); - bucket.push(pair); - this.#size++; - } - - /* 删除操作 */ - remove(key: number): void { - const index = this.#hashFunc(key); - let bucket = this.#buckets[index]; - // 遍历桶,从中删除键值对 - for (let i = 0; i < bucket.length; i++) { - if (bucket[i].key === key) { - bucket.splice(i, 1); - this.#size--; - break; - } - } - } - - /* 扩容哈希表 */ - #extend(): void { - // 暂存原哈希表 - const bucketsTmp = this.#buckets; - // 初始化扩容后的新哈希表 - this.#capacity *= this.#extendRatio; - this.#buckets = new Array(this.#capacity).fill(null).map((x) => []); - this.#size = 0; - // 将键值对从原哈希表搬运至新哈希表 - for (const bucket of bucketsTmp) { - for (const pair of bucket) { - this.put(pair.key, pair.val); - } - } - } - - /* 打印哈希表 */ - print(): void { - for (const bucket of this.#buckets) { - let res = []; - for (const pair of bucket) { - res.push(pair.key + ' -> ' + pair.val); - } - console.log(res); - } - } - } + [class]{HashMapChaining}-[func]{} ``` === "Dart" ```dart title="hash_map_chaining.dart" - /* 链式地址哈希表 */ - class HashMapChaining { - late int size; // 键值对数量 - late int capacity; // 哈希表容量 - late double loadThres; // 触发扩容的负载因子阈值 - late int extendRatio; // 扩容倍数 - late List> buckets; // 桶数组 - - /* 构造方法 */ - HashMapChaining() { - size = 0; - capacity = 4; - loadThres = 2.0 / 3.0; - extendRatio = 2; - buckets = List.generate(capacity, (_) => []); - } - - /* 哈希函数 */ - int hashFunc(int key) { - return key % capacity; - } - - /* 负载因子 */ - double loadFactor() { - return size / capacity; - } - - /* 查询操作 */ - String? get(int key) { - int index = hashFunc(key); - List bucket = buckets[index]; - // 遍历桶,若找到 key ,则返回对应 val - for (Pair pair in bucket) { - if (pair.key == key) { - return pair.val; - } - } - // 若未找到 key ,则返回 null - return null; - } - - /* 添加操作 */ - void put(int key, String val) { - // 当负载因子超过阈值时,执行扩容 - if (loadFactor() > loadThres) { - extend(); - } - int index = hashFunc(key); - List bucket = buckets[index]; - // 遍历桶,若遇到指定 key ,则更新对应 val 并返回 - for (Pair pair in bucket) { - if (pair.key == key) { - pair.val = val; - return; - } - } - // 若无该 key ,则将键值对添加至尾部 - Pair pair = Pair(key, val); - bucket.add(pair); - size++; - } - - /* 删除操作 */ - void remove(int key) { - int index = hashFunc(key); - List bucket = buckets[index]; - // 遍历桶,从中删除键值对 - for (Pair pair in bucket) { - if (pair.key == key) { - bucket.remove(pair); - size--; - break; - } - } - } - - /* 扩容哈希表 */ - void extend() { - // 暂存原哈希表 - List> bucketsTmp = buckets; - // 初始化扩容后的新哈希表 - capacity *= extendRatio; - buckets = List.generate(capacity, (_) => []); - size = 0; - // 将键值对从原哈希表搬运至新哈希表 - for (List bucket in bucketsTmp) { - for (Pair pair in bucket) { - put(pair.key, pair.val); - } - } - } - - /* 打印哈希表 */ - void printHashMap() { - for (List bucket in buckets) { - List res = []; - for (Pair pair in bucket) { - res.add("${pair.key} -> ${pair.val}"); - } - print(res); - } - } - } + [class]{HashMapChaining}-[func]{} ``` === "Rust" ```rust title="hash_map_chaining.rs" - /* 链式地址哈希表 */ - struct HashMapChaining { - size: i32, - capacity: i32, - load_thres: f32, - extend_ratio: i32, - buckets: Vec>, - } - - impl HashMapChaining { - /* 构造方法 */ - fn new() -> Self { - Self { - size: 0, - capacity: 4, - load_thres: 2.0 / 3.0, - extend_ratio: 2, - buckets: vec![vec![]; 4], - } - } - - /* 哈希函数 */ - fn hash_func(&self, key: i32) -> usize { - key as usize % self.capacity as usize - } - - /* 负载因子 */ - fn load_factor(&self) -> f32 { - self.size as f32 / self.capacity as f32 - } - - /* 删除操作 */ - fn remove(&mut self, key: i32) -> Option { - let index = self.hash_func(key); - let bucket = &mut self.buckets[index]; - - // 遍历桶,从中删除键值对 - for i in 0..bucket.len() { - if bucket[i].key == key { - let pair = bucket.remove(i); - self.size -= 1; - return Some(pair.val); - } - } - - // 若未找到 key ,则返回 None - None - } - - /* 扩容哈希表 */ - fn extend(&mut self) { - // 暂存原哈希表 - let buckets_tmp = std::mem::replace(&mut self.buckets, vec![]); - - // 初始化扩容后的新哈希表 - self.capacity *= self.extend_ratio; - self.buckets = vec![Vec::new(); self.capacity as usize]; - self.size = 0; - - // 将键值对从原哈希表搬运至新哈希表 - for bucket in buckets_tmp { - for pair in bucket { - self.put(pair.key, pair.val); - } - } - } - - /* 打印哈希表 */ - fn print(&self) { - for bucket in &self.buckets { - let mut res = Vec::new(); - for pair in bucket { - res.push(format!("{} -> {}", pair.key, pair.val)); - } - println!("{:?}", res); - } - } - - /* 添加操作 */ - fn put(&mut self, key: i32, val: String) { - // 当负载因子超过阈值时,执行扩容 - if self.load_factor() > self.load_thres { - self.extend(); - } - - let index = self.hash_func(key); - let bucket = &mut self.buckets[index]; - - // 遍历桶,若遇到指定 key ,则更新对应 val 并返回 - for pair in bucket { - if pair.key == key { - pair.val = val; - return; - } - } - let bucket = &mut self.buckets[index]; - - // 若无该 key ,则将键值对添加至尾部 - let pair = Pair { key, val }; - bucket.push(pair); - self.size += 1; - } - - /* 查询操作 */ - fn get(&self, key: i32) -> Option<&str> { - let index = self.hash_func(key); - let bucket = &self.buckets[index]; - - // 遍历桶,若找到 key ,则返回对应 val - for pair in bucket { - if pair.key == key { - return Some(&pair.val); - } - } - - // 若未找到 key ,则返回 None - None - } - } + [class]{HashMapChaining}-[func]{} ``` === "C" ```c title="hash_map_chaining.c" - /* 链表节点 */ - typedef struct Node { - Pair *pair; - struct Node *next; - } Node; + [class]{Node}-[func]{} - /* 链式地址哈希表 */ - typedef struct { - int size; // 键值对数量 - int capacity; // 哈希表容量 - double loadThres; // 触发扩容的负载因子阈值 - int extendRatio; // 扩容倍数 - Node **buckets; // 桶数组 - } HashMapChaining; - - /* 构造函数 */ - HashMapChaining *newHashMapChaining() { - HashMapChaining *hashMap = (HashMapChaining *)malloc(sizeof(HashMapChaining)); - hashMap->size = 0; - hashMap->capacity = 4; - hashMap->loadThres = 2.0 / 3.0; - hashMap->extendRatio = 2; - hashMap->buckets = (Node **)malloc(hashMap->capacity * sizeof(Node *)); - for (int i = 0; i < hashMap->capacity; i++) { - hashMap->buckets[i] = NULL; - } - return hashMap; - } - - /* 析构函数 */ - void delHashMapChaining(HashMapChaining *hashMap) { - for (int i = 0; i < hashMap->capacity; i++) { - Node *cur = hashMap->buckets[i]; - while (cur) { - Node *tmp = cur; - cur = cur->next; - free(tmp->pair); - free(tmp); - } - } - free(hashMap->buckets); - free(hashMap); - } - - /* 哈希函数 */ - int hashFunc(HashMapChaining *hashMap, int key) { - return key % hashMap->capacity; - } - - /* 负载因子 */ - double loadFactor(HashMapChaining *hashMap) { - return (double)hashMap->size / (double)hashMap->capacity; - } - - /* 查询操作 */ - char *get(HashMapChaining *hashMap, int key) { - int index = hashFunc(hashMap, key); - // 遍历桶,若找到 key ,则返回对应 val - Node *cur = hashMap->buckets[index]; - while (cur) { - if (cur->pair->key == key) { - return cur->pair->val; - } - cur = cur->next; - } - return ""; // 若未找到 key ,则返回空字符串 - } - - /* 添加操作 */ - void put(HashMapChaining *hashMap, int key, const char *val) { - // 当负载因子超过阈值时,执行扩容 - if (loadFactor(hashMap) > hashMap->loadThres) { - extend(hashMap); - } - int index = hashFunc(hashMap, key); - // 遍历桶,若遇到指定 key ,则更新对应 val 并返回 - Node *cur = hashMap->buckets[index]; - while (cur) { - if (cur->pair->key == key) { - strcpy(cur->pair->val, val); // 若遇到指定 key ,则更新对应 val 并返回 - return; - } - cur = cur->next; - } - // 若无该 key ,则将键值对添加至链表头部 - Pair *newPair = (Pair *)malloc(sizeof(Pair)); - newPair->key = key; - strcpy(newPair->val, val); - Node *newNode = (Node *)malloc(sizeof(Node)); - newNode->pair = newPair; - newNode->next = hashMap->buckets[index]; - hashMap->buckets[index] = newNode; - hashMap->size++; - } - - /* 扩容哈希表 */ - void extend(HashMapChaining *hashMap) { - // 暂存原哈希表 - int oldCapacity = hashMap->capacity; - Node **oldBuckets = hashMap->buckets; - // 初始化扩容后的新哈希表 - hashMap->capacity *= hashMap->extendRatio; - hashMap->buckets = (Node **)malloc(hashMap->capacity * sizeof(Node *)); - for (int i = 0; i < hashMap->capacity; i++) { - hashMap->buckets[i] = NULL; - } - hashMap->size = 0; - // 将键值对从原哈希表搬运至新哈希表 - for (int i = 0; i < oldCapacity; i++) { - Node *cur = oldBuckets[i]; - while (cur) { - put(hashMap, cur->pair->key, cur->pair->val); - Node *temp = cur; - cur = cur->next; - // 释放内存 - free(temp->pair); - free(temp); - } - } - - free(oldBuckets); - } - - /* 删除操作 */ - void removeItem(HashMapChaining *hashMap, int key) { - int index = hashFunc(hashMap, key); - Node *cur = hashMap->buckets[index]; - Node *pre = NULL; - while (cur) { - if (cur->pair->key == key) { - // 从中删除键值对 - if (pre) { - pre->next = cur->next; - } else { - hashMap->buckets[index] = cur->next; - } - // 释放内存 - free(cur->pair); - free(cur); - hashMap->size--; - return; - } - pre = cur; - cur = cur->next; - } - } - - /* 打印哈希表 */ - void print(HashMapChaining *hashMap) { - for (int i = 0; i < hashMap->capacity; i++) { - Node *cur = hashMap->buckets[i]; - printf("["); - while (cur) { - printf("%d -> %s, ", cur->pair->key, cur->pair->val); - cur = cur->next; - } - printf("]\n"); - } - } + [class]{HashMapChaining}-[func]{} ``` === "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 = mutableListOf() - 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._val = _val - return - } - } - // 若无该 key ,则将键值对添加至尾部 - val pair = Pair(key, _val) - 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._val - res.add("$k -> $v") - } - println(res) - } - } - } + [class]{HashMapChaining}-[func]{} ``` === "Ruby" ```ruby title="hash_map_chaining.rb" - ### 键式地址哈希表 ### - class HashMapChaining - ### 构造方法 ### - def initialize - @size = 0 # 键值对数量 - @capacity = 4 # 哈希表容量 - @load_thres = 2.0 / 3.0 # 触发扩容的负载因子阈值 - @extend_ratio = 2 # 扩容倍数 - @buckets = Array.new(@capacity) { [] } # 桶数组 - end - - ### 哈希函数 ### - def hash_func(key) - key % @capacity - end - - ### 负载因子 ### - def load_factor - @size / @capacity - end - - ### 查询操作 ### - def get(key) - index = hash_func(key) - bucket = @buckets[index] - # 遍历桶,若找到 key ,则返回对应 val - for pair in bucket - return pair.val if pair.key == key - end - # 若未找到 key , 则返回 nil - nil - end - - ### 添加操作 ### - def put(key, val) - # 当负载因子超过阈值时,执行扩容 - extend if load_factor > @load_thres - index = hash_func(key) - bucket = @buckets[index] - # 遍历桶,若遇到指定 key ,则更新对应 val 并返回 - for pair in bucket - if pair.key == key - pair.val = val - return - end - end - # 若无该 key ,则将键值对添加至尾部 - pair = Pair.new(key, val) - bucket << pair - @size += 1 - end - - ### 删除操作 ### - def remove(key) - index = hash_func(key) - bucket = @buckets[index] - # 遍历桶,从中删除键值对 - for pair in bucket - if pair.key == key - bucket.delete(pair) - @size -= 1 - break - end - end - end - - ### 扩容哈希表 ### - def extend - # 暫存原哈希表 - buckets = @buckets - # 初始化扩容后的新哈希表 - @capacity *= @extend_ratio - @buckets = Array.new(@capacity) { [] } - @size = 0 - # 将键值对从原哈希表搬运至新哈希表 - for bucket in buckets - for pair in bucket - put(pair.key, pair.val) - end - end - end - - ### 打印哈希表 ### - def print - for bucket in @buckets - res = [] - for pair in bucket - res << "#{pair.key} -> #{pair.val}" - end - pp res - end - end - end + [class]{HashMapChaining}-[func]{} ``` === "Zig" @@ -1527,11 +308,6 @@ The code below provides a simple implementation of a separate chaining hash tabl [class]{HashMapChaining}-[func]{} ``` -??? pythontutor "Code Visualization" - -
- - It's worth noting that when the linked list is very long, the query efficiency $O(n)$ is poor. **In this case, the list can be converted to an "AVL tree" or "Red-Black tree"** to optimize the time complexity of the query operation to $O(\log n)$. ## 6.2.2   Open addressing @@ -1573,96 +349,96 @@ The code below implements an open addressing (linear probing) hash table with la ```python title="hash_map_open_addressing.py" class HashMapOpenAddressing: - """开放寻址哈希表""" + """Open addressing hash table""" def __init__(self): - """构造方法""" - self.size = 0 # 键值对数量 - self.capacity = 4 # 哈希表容量 - self.load_thres = 2.0 / 3.0 # 触发扩容的负载因子阈值 - self.extend_ratio = 2 # 扩容倍数 - self.buckets: list[Pair | None] = [None] * self.capacity # 桶数组 - self.TOMBSTONE = Pair(-1, "-1") # 删除标记 + """Constructor""" + self.size = 0 # Number of key-value pairs + self.capacity = 4 # Hash table capacity + self.load_thres = 2.0 / 3.0 # Load factor threshold for triggering expansion + self.extend_ratio = 2 # Expansion multiplier + self.buckets: list[Pair | None] = [None] * self.capacity # Bucket array + self.TOMBSTONE = Pair(-1, "-1") # Removal mark def hash_func(self, key: int) -> int: - """哈希函数""" + """Hash function""" return key % self.capacity def load_factor(self) -> float: - """负载因子""" + """Load factor""" return self.size / self.capacity def find_bucket(self, key: int) -> int: - """搜索 key 对应的桶索引""" + """Search for the bucket index corresponding to key""" index = self.hash_func(key) first_tombstone = -1 - # 线性探测,当遇到空桶时跳出 + # Linear probing, break when encountering an empty bucket while self.buckets[index] is not None: - # 若遇到 key ,返回对应的桶索引 + # If the key is encountered, return the corresponding bucket index if self.buckets[index].key == key: - # 若之前遇到了删除标记,则将键值对移动至该索引处 + # If a removal mark was encountered earlier, move the key-value pair to that index if first_tombstone != -1: self.buckets[first_tombstone] = self.buckets[index] self.buckets[index] = self.TOMBSTONE - return first_tombstone # 返回移动后的桶索引 - return index # 返回桶索引 - # 记录遇到的首个删除标记 + return first_tombstone # Return the moved bucket index + return index # Return bucket index + # Record the first encountered removal mark if first_tombstone == -1 and self.buckets[index] is self.TOMBSTONE: first_tombstone = index - # 计算桶索引,越过尾部则返回头部 + # Calculate the bucket index, return to the head if exceeding the tail index = (index + 1) % self.capacity - # 若 key 不存在,则返回添加点的索引 + # If the key does not exist, return the index of the insertion point return index if first_tombstone == -1 else first_tombstone def get(self, key: int) -> str: - """查询操作""" - # 搜索 key 对应的桶索引 + """Query operation""" + # Search for the bucket index corresponding to key index = self.find_bucket(key) - # 若找到键值对,则返回对应 val + # If the key-value pair is found, return the corresponding val if self.buckets[index] not in [None, self.TOMBSTONE]: return self.buckets[index].val - # 若键值对不存在,则返回 None + # If the key-value pair does not exist, return None return None def put(self, key: int, val: str): - """添加操作""" - # 当负载因子超过阈值时,执行扩容 + """Add operation""" + # When the load factor exceeds the threshold, perform expansion if self.load_factor() > self.load_thres: self.extend() - # 搜索 key 对应的桶索引 + # Search for the bucket index corresponding to key index = self.find_bucket(key) - # 若找到键值对,则覆盖 val 并返回 + # If the key-value pair is found, overwrite val and return if self.buckets[index] not in [None, self.TOMBSTONE]: self.buckets[index].val = val return - # 若键值对不存在,则添加该键值对 + # If the key-value pair does not exist, add the key-value pair self.buckets[index] = Pair(key, val) self.size += 1 def remove(self, key: int): - """删除操作""" - # 搜索 key 对应的桶索引 + """Remove operation""" + # Search for the bucket index corresponding to key index = self.find_bucket(key) - # 若找到键值对,则用删除标记覆盖它 + # If the key-value pair is found, cover it with a removal mark if self.buckets[index] not in [None, self.TOMBSTONE]: self.buckets[index] = self.TOMBSTONE self.size -= 1 def extend(self): - """扩容哈希表""" - # 暂存原哈希表 + """Extend hash table""" + # Temporarily store the original hash table buckets_tmp = self.buckets - # 初始化扩容后的新哈希表 + # Initialize the extended new hash table self.capacity *= self.extend_ratio self.buckets = [None] * self.capacity self.size = 0 - # 将键值对从原哈希表搬运至新哈希表 + # Move key-value pairs from the original hash table to the new hash table for pair in buckets_tmp: if pair not in [None, self.TOMBSTONE]: self.put(pair.key, pair.val) def print(self): - """打印哈希表""" + """Print hash table""" for pair in self.buckets: if pair is None: print("None") @@ -1675,247 +451,114 @@ The code below implements an open addressing (linear probing) hash table with la === "C++" ```cpp title="hash_map_open_addressing.cpp" - /* 开放寻址哈希表 */ - class HashMapOpenAddressing { - private: - int size; // 键值对数量 - int capacity = 4; // 哈希表容量 - const double loadThres = 2.0 / 3.0; // 触发扩容的负载因子阈值 - const int extendRatio = 2; // 扩容倍数 - vector buckets; // 桶数组 - Pair *TOMBSTONE = new Pair(-1, "-1"); // 删除标记 - - public: - /* 构造方法 */ - HashMapOpenAddressing() : size(0), buckets(capacity, nullptr) { - } - - /* 析构方法 */ - ~HashMapOpenAddressing() { - for (Pair *pair : buckets) { - if (pair != nullptr && pair != TOMBSTONE) { - delete pair; - } - } - delete TOMBSTONE; - } - - /* 哈希函数 */ - int hashFunc(int key) { - return key % capacity; - } - - /* 负载因子 */ - double loadFactor() { - return (double)size / capacity; - } - - /* 搜索 key 对应的桶索引 */ - int findBucket(int key) { - int index = hashFunc(key); - int firstTombstone = -1; - // 线性探测,当遇到空桶时跳出 - while (buckets[index] != nullptr) { - // 若遇到 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 firstTombstone == -1 ? index : firstTombstone; - } - - /* 查询操作 */ - string get(int key) { - // 搜索 key 对应的桶索引 - int index = findBucket(key); - // 若找到键值对,则返回对应 val - if (buckets[index] != nullptr && buckets[index] != TOMBSTONE) { - return buckets[index]->val; - } - // 若键值对不存在,则返回空字符串 - return ""; - } - - /* 添加操作 */ - void put(int key, string val) { - // 当负载因子超过阈值时,执行扩容 - if (loadFactor() > loadThres) { - extend(); - } - // 搜索 key 对应的桶索引 - int index = findBucket(key); - // 若找到键值对,则覆盖 val 并返回 - if (buckets[index] != nullptr && buckets[index] != TOMBSTONE) { - buckets[index]->val = val; - return; - } - // 若键值对不存在,则添加该键值对 - buckets[index] = new Pair(key, val); - size++; - } - - /* 删除操作 */ - void remove(int key) { - // 搜索 key 对应的桶索引 - int index = findBucket(key); - // 若找到键值对,则用删除标记覆盖它 - if (buckets[index] != nullptr && buckets[index] != TOMBSTONE) { - delete buckets[index]; - buckets[index] = TOMBSTONE; - size--; - } - } - - /* 扩容哈希表 */ - void extend() { - // 暂存原哈希表 - vector bucketsTmp = buckets; - // 初始化扩容后的新哈希表 - capacity *= extendRatio; - buckets = vector(capacity, nullptr); - size = 0; - // 将键值对从原哈希表搬运至新哈希表 - for (Pair *pair : bucketsTmp) { - if (pair != nullptr && pair != TOMBSTONE) { - put(pair->key, pair->val); - delete pair; - } - } - } - - /* 打印哈希表 */ - void print() { - for (Pair *pair : buckets) { - if (pair == nullptr) { - cout << "nullptr" << endl; - } else if (pair == TOMBSTONE) { - cout << "TOMBSTONE" << endl; - } else { - cout << pair->key << " -> " << pair->val << endl; - } - } - } - }; + [class]{HashMapOpenAddressing}-[func]{} ``` === "Java" ```java title="hash_map_open_addressing.java" - /* 开放寻址哈希表 */ + /* Open addressing hash table */ class HashMapOpenAddressing { - private int size; // 键值对数量 - private int capacity = 4; // 哈希表容量 - private final double loadThres = 2.0 / 3.0; // 触发扩容的负载因子阈值 - private final int extendRatio = 2; // 扩容倍数 - private Pair[] buckets; // 桶数组 - private final Pair TOMBSTONE = new Pair(-1, "-1"); // 删除标记 + private int size; // Number of key-value pairs + private int capacity = 4; // Hash table capacity + private final double loadThres = 2.0 / 3.0; // Load factor threshold for triggering expansion + private final int extendRatio = 2; // Expansion multiplier + private Pair[] buckets; // Bucket array + private final Pair TOMBSTONE = new Pair(-1, "-1"); // Removal mark - /* 构造方法 */ + /* Constructor */ public HashMapOpenAddressing() { size = 0; buckets = new Pair[capacity]; } - /* 哈希函数 */ + /* Hash function */ private int hashFunc(int key) { return key % capacity; } - /* 负载因子 */ + /* Load factor */ private double loadFactor() { return (double) size / capacity; } - /* 搜索 key 对应的桶索引 */ + /* Search for the bucket index corresponding to key */ private int findBucket(int key) { int index = hashFunc(key); int firstTombstone = -1; - // 线性探测,当遇到空桶时跳出 + // Linear probing, break when encountering an empty bucket while (buckets[index] != null) { - // 若遇到 key ,返回对应的桶索引 + // If the key is encountered, return the corresponding bucket index if (buckets[index].key == key) { - // 若之前遇到了删除标记,则将键值对移动至该索引处 + // If a removal mark was encountered earlier, move the key-value pair to that index if (firstTombstone != -1) { buckets[firstTombstone] = buckets[index]; buckets[index] = TOMBSTONE; - return firstTombstone; // 返回移动后的桶索引 + return firstTombstone; // Return the moved bucket index } - return index; // 返回桶索引 + return index; // Return bucket index } - // 记录遇到的首个删除标记 + // Record the first encountered removal mark if (firstTombstone == -1 && buckets[index] == TOMBSTONE) { firstTombstone = index; } - // 计算桶索引,越过尾部则返回头部 + // Calculate the bucket index, return to the head if exceeding the tail index = (index + 1) % capacity; } - // 若 key 不存在,则返回添加点的索引 + // If the key does not exist, return the index of the insertion point return firstTombstone == -1 ? index : firstTombstone; } - /* 查询操作 */ + /* Query operation */ public String get(int key) { - // 搜索 key 对应的桶索引 + // Search for the bucket index corresponding to key int index = findBucket(key); - // 若找到键值对,则返回对应 val + // If the key-value pair is found, return the corresponding val if (buckets[index] != null && buckets[index] != TOMBSTONE) { return buckets[index].val; } - // 若键值对不存在,则返回 null + // If the key-value pair does not exist, return null return null; } - /* 添加操作 */ + /* Add operation */ public void put(int key, String val) { - // 当负载因子超过阈值时,执行扩容 + // When the load factor exceeds the threshold, perform expansion if (loadFactor() > loadThres) { extend(); } - // 搜索 key 对应的桶索引 + // Search for the bucket index corresponding to key int index = findBucket(key); - // 若找到键值对,则覆盖 val 并返回 + // If the key-value pair is found, overwrite val and return if (buckets[index] != null && buckets[index] != TOMBSTONE) { buckets[index].val = val; return; } - // 若键值对不存在,则添加该键值对 + // If the key-value pair does not exist, add the key-value pair buckets[index] = new Pair(key, val); size++; } - /* 删除操作 */ + /* Remove operation */ public void remove(int key) { - // 搜索 key 对应的桶索引 + // Search for the bucket index corresponding to key int index = findBucket(key); - // 若找到键值对,则用删除标记覆盖它 + // If the key-value pair is found, cover it with a removal mark if (buckets[index] != null && buckets[index] != TOMBSTONE) { buckets[index] = TOMBSTONE; size--; } } - /* 扩容哈希表 */ + /* Extend hash table */ private void extend() { - // 暂存原哈希表 + // Temporarily store the original hash table Pair[] bucketsTmp = buckets; - // 初始化扩容后的新哈希表 + // Initialize the extended new hash table capacity *= extendRatio; buckets = new Pair[capacity]; size = 0; - // 将键值对从原哈希表搬运至新哈希表 + // Move key-value pairs from the original hash table to the new hash table for (Pair pair : bucketsTmp) { if (pair != null && pair != TOMBSTONE) { put(pair.key, pair.val); @@ -1923,7 +566,7 @@ The code below implements an open addressing (linear probing) hash table with la } } - /* 打印哈希表 */ + /* Print hash table */ public void print() { for (Pair pair : buckets) { if (pair == null) { @@ -1941,1355 +584,61 @@ The code below implements an open addressing (linear probing) hash table with la === "C#" ```csharp title="hash_map_open_addressing.cs" - /* 开放寻址哈希表 */ - class HashMapOpenAddressing { - int size; // 键值对数量 - int capacity = 4; // 哈希表容量 - double loadThres = 2.0 / 3.0; // 触发扩容的负载因子阈值 - int extendRatio = 2; // 扩容倍数 - Pair[] buckets; // 桶数组 - Pair TOMBSTONE = new(-1, "-1"); // 删除标记 - - /* 构造方法 */ - public HashMapOpenAddressing() { - size = 0; - buckets = new Pair[capacity]; - } - - /* 哈希函数 */ - int HashFunc(int key) { - return key % capacity; - } - - /* 负载因子 */ - double LoadFactor() { - return (double)size / capacity; - } - - /* 搜索 key 对应的桶索引 */ - int FindBucket(int key) { - int index = HashFunc(key); - int 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 firstTombstone == -1 ? index : firstTombstone; - } - - /* 查询操作 */ - public string? Get(int key) { - // 搜索 key 对应的桶索引 - int index = FindBucket(key); - // 若找到键值对,则返回对应 val - if (buckets[index] != null && buckets[index] != TOMBSTONE) { - return buckets[index].val; - } - // 若键值对不存在,则返回 null - return null; - } - - /* 添加操作 */ - public void Put(int key, string val) { - // 当负载因子超过阈值时,执行扩容 - if (LoadFactor() > loadThres) { - Extend(); - } - // 搜索 key 对应的桶索引 - int index = FindBucket(key); - // 若找到键值对,则覆盖 val 并返回 - if (buckets[index] != null && buckets[index] != TOMBSTONE) { - buckets[index].val = val; - return; - } - // 若键值对不存在,则添加该键值对 - buckets[index] = new Pair(key, val); - size++; - } - - /* 删除操作 */ - public void Remove(int key) { - // 搜索 key 对应的桶索引 - int index = FindBucket(key); - // 若找到键值对,则用删除标记覆盖它 - if (buckets[index] != null && buckets[index] != TOMBSTONE) { - buckets[index] = TOMBSTONE; - size--; - } - } - - /* 扩容哈希表 */ - void Extend() { - // 暂存原哈希表 - Pair[] bucketsTmp = buckets; - // 初始化扩容后的新哈希表 - capacity *= extendRatio; - buckets = new Pair[capacity]; - size = 0; - // 将键值对从原哈希表搬运至新哈希表 - foreach (Pair pair in bucketsTmp) { - if (pair != null && pair != TOMBSTONE) { - Put(pair.key, pair.val); - } - } - } - - /* 打印哈希表 */ - public void Print() { - foreach (Pair pair in buckets) { - if (pair == null) { - Console.WriteLine("null"); - } else if (pair == TOMBSTONE) { - Console.WriteLine("TOMBSTONE"); - } else { - Console.WriteLine(pair.key + " -> " + pair.val); - } - } - } - } + [class]{HashMapOpenAddressing}-[func]{} ``` === "Go" ```go title="hash_map_open_addressing.go" - /* 开放寻址哈希表 */ - type hashMapOpenAddressing struct { - size int // 键值对数量 - capacity int // 哈希表容量 - loadThres float64 // 触发扩容的负载因子阈值 - extendRatio int // 扩容倍数 - buckets []*pair // 桶数组 - TOMBSTONE *pair // 删除标记 - } - - /* 构造方法 */ - func newHashMapOpenAddressing() *hashMapOpenAddressing { - return &hashMapOpenAddressing{ - size: 0, - capacity: 4, - loadThres: 2.0 / 3.0, - extendRatio: 2, - buckets: make([]*pair, 4), - TOMBSTONE: &pair{-1, "-1"}, - } - } - - /* 哈希函数 */ - func (h *hashMapOpenAddressing) hashFunc(key int) int { - return key % h.capacity // 根据键计算哈希值 - } - - /* 负载因子 */ - func (h *hashMapOpenAddressing) loadFactor() float64 { - return float64(h.size) / float64(h.capacity) // 计算当前负载因子 - } - - /* 搜索 key 对应的桶索引 */ - func (h *hashMapOpenAddressing) findBucket(key int) int { - index := h.hashFunc(key) // 获取初始索引 - firstTombstone := -1 // 记录遇到的第一个TOMBSTONE的位置 - for h.buckets[index] != nil { - if h.buckets[index].key == key { - if firstTombstone != -1 { - // 若之前遇到了删除标记,则将键值对移动至该索引处 - h.buckets[firstTombstone] = h.buckets[index] - h.buckets[index] = h.TOMBSTONE - return firstTombstone // 返回移动后的桶索引 - } - return index // 返回找到的索引 - } - if firstTombstone == -1 && h.buckets[index] == h.TOMBSTONE { - firstTombstone = index // 记录遇到的首个删除标记的位置 - } - index = (index + 1) % h.capacity // 线性探测,越过尾部则返回头部 - } - // 若 key 不存在,则返回添加点的索引 - if firstTombstone != -1 { - return firstTombstone - } - return index - } - - /* 查询操作 */ - func (h *hashMapOpenAddressing) get(key int) string { - index := h.findBucket(key) // 搜索 key 对应的桶索引 - if h.buckets[index] != nil && h.buckets[index] != h.TOMBSTONE { - return h.buckets[index].val // 若找到键值对,则返回对应 val - } - return "" // 若键值对不存在,则返回 "" - } - - /* 添加操作 */ - func (h *hashMapOpenAddressing) put(key int, val string) { - if h.loadFactor() > h.loadThres { - h.extend() // 当负载因子超过阈值时,执行扩容 - } - index := h.findBucket(key) // 搜索 key 对应的桶索引 - if h.buckets[index] == nil || h.buckets[index] == h.TOMBSTONE { - h.buckets[index] = &pair{key, val} // 若键值对不存在,则添加该键值对 - h.size++ - } else { - h.buckets[index].val = val // 若找到键值对,则覆盖 val - } - } - - /* 删除操作 */ - func (h *hashMapOpenAddressing) remove(key int) { - index := h.findBucket(key) // 搜索 key 对应的桶索引 - if h.buckets[index] != nil && h.buckets[index] != h.TOMBSTONE { - h.buckets[index] = h.TOMBSTONE // 若找到键值对,则用删除标记覆盖它 - h.size-- - } - } - - /* 扩容哈希表 */ - func (h *hashMapOpenAddressing) extend() { - oldBuckets := h.buckets // 暂存原哈希表 - h.capacity *= h.extendRatio // 更新容量 - h.buckets = make([]*pair, h.capacity) // 初始化扩容后的新哈希表 - h.size = 0 // 重置大小 - // 将键值对从原哈希表搬运至新哈希表 - for _, pair := range oldBuckets { - if pair != nil && pair != h.TOMBSTONE { - h.put(pair.key, pair.val) - } - } - } - - /* 打印哈希表 */ - func (h *hashMapOpenAddressing) print() { - for _, pair := range h.buckets { - if pair == nil { - fmt.Println("nil") - } else if pair == h.TOMBSTONE { - fmt.Println("TOMBSTONE") - } else { - fmt.Printf("%d -> %s\n", pair.key, pair.val) - } - } - } + [class]{hashMapOpenAddressing}-[func]{} ``` === "Swift" ```swift title="hash_map_open_addressing.swift" - /* 开放寻址哈希表 */ - class HashMapOpenAddressing { - var size: Int // 键值对数量 - var capacity: Int // 哈希表容量 - var loadThres: Double // 触发扩容的负载因子阈值 - var extendRatio: Int // 扩容倍数 - var buckets: [Pair?] // 桶数组 - var TOMBSTONE: Pair // 删除标记 - - /* 构造方法 */ - init() { - size = 0 - capacity = 4 - loadThres = 2.0 / 3.0 - extendRatio = 2 - buckets = Array(repeating: nil, count: capacity) - TOMBSTONE = Pair(key: -1, val: "-1") - } - - /* 哈希函数 */ - func hashFunc(key: Int) -> Int { - key % capacity - } - - /* 负载因子 */ - func loadFactor() -> Double { - Double(size) / Double(capacity) - } - - /* 搜索 key 对应的桶索引 */ - func findBucket(key: Int) -> Int { - var index = hashFunc(key: key) - var firstTombstone = -1 - // 线性探测,当遇到空桶时跳出 - while buckets[index] != nil { - // 若遇到 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 firstTombstone == -1 ? index : firstTombstone - } - - /* 查询操作 */ - func get(key: Int) -> String? { - // 搜索 key 对应的桶索引 - let index = findBucket(key: key) - // 若找到键值对,则返回对应 val - if buckets[index] != nil, buckets[index] != TOMBSTONE { - return buckets[index]!.val - } - // 若键值对不存在,则返回 null - return nil - } - - /* 添加操作 */ - func put(key: Int, val: String) { - // 当负载因子超过阈值时,执行扩容 - if loadFactor() > loadThres { - extend() - } - // 搜索 key 对应的桶索引 - let index = findBucket(key: key) - // 若找到键值对,则覆盖 val 并返回 - if buckets[index] != nil, buckets[index] != TOMBSTONE { - buckets[index]!.val = val - return - } - // 若键值对不存在,则添加该键值对 - buckets[index] = Pair(key: key, val: val) - size += 1 - } - - /* 删除操作 */ - func remove(key: Int) { - // 搜索 key 对应的桶索引 - let index = findBucket(key: key) - // 若找到键值对,则用删除标记覆盖它 - if buckets[index] != nil, buckets[index] != TOMBSTONE { - buckets[index] = TOMBSTONE - size -= 1 - } - } - - /* 扩容哈希表 */ - func extend() { - // 暂存原哈希表 - let bucketsTmp = buckets - // 初始化扩容后的新哈希表 - capacity *= extendRatio - buckets = Array(repeating: nil, count: capacity) - size = 0 - // 将键值对从原哈希表搬运至新哈希表 - for pair in bucketsTmp { - if let pair, pair != TOMBSTONE { - put(key: pair.key, val: pair.val) - } - } - } - - /* 打印哈希表 */ - func print() { - for pair in buckets { - if pair == nil { - Swift.print("null") - } else if pair == TOMBSTONE { - Swift.print("TOMBSTONE") - } else { - Swift.print("\(pair!.key) -> \(pair!.val)") - } - } - } - } + [class]{HashMapOpenAddressing}-[func]{} ``` === "JS" ```javascript title="hash_map_open_addressing.js" - /* 开放寻址哈希表 */ - class HashMapOpenAddressing { - #size; // 键值对数量 - #capacity; // 哈希表容量 - #loadThres; // 触发扩容的负载因子阈值 - #extendRatio; // 扩容倍数 - #buckets; // 桶数组 - #TOMBSTONE; // 删除标记 - - /* 构造方法 */ - constructor() { - this.#size = 0; // 键值对数量 - this.#capacity = 4; // 哈希表容量 - this.#loadThres = 2.0 / 3.0; // 触发扩容的负载因子阈值 - this.#extendRatio = 2; // 扩容倍数 - this.#buckets = Array(this.#capacity).fill(null); // 桶数组 - this.#TOMBSTONE = new Pair(-1, '-1'); // 删除标记 - } - - /* 哈希函数 */ - #hashFunc(key) { - return key % this.#capacity; - } - - /* 负载因子 */ - #loadFactor() { - return this.#size / this.#capacity; - } - - /* 搜索 key 对应的桶索引 */ - #findBucket(key) { - let index = this.#hashFunc(key); - let firstTombstone = -1; - // 线性探测,当遇到空桶时跳出 - while (this.#buckets[index] !== null) { - // 若遇到 key ,返回对应的桶索引 - if (this.#buckets[index].key === key) { - // 若之前遇到了删除标记,则将键值对移动至该索引处 - if (firstTombstone !== -1) { - this.#buckets[firstTombstone] = this.#buckets[index]; - this.#buckets[index] = this.#TOMBSTONE; - return firstTombstone; // 返回移动后的桶索引 - } - return index; // 返回桶索引 - } - // 记录遇到的首个删除标记 - if ( - firstTombstone === -1 && - this.#buckets[index] === this.#TOMBSTONE - ) { - firstTombstone = index; - } - // 计算桶索引,越过尾部则返回头部 - index = (index + 1) % this.#capacity; - } - // 若 key 不存在,则返回添加点的索引 - return firstTombstone === -1 ? index : firstTombstone; - } - - /* 查询操作 */ - get(key) { - // 搜索 key 对应的桶索引 - const index = this.#findBucket(key); - // 若找到键值对,则返回对应 val - if ( - this.#buckets[index] !== null && - this.#buckets[index] !== this.#TOMBSTONE - ) { - return this.#buckets[index].val; - } - // 若键值对不存在,则返回 null - return null; - } - - /* 添加操作 */ - put(key, val) { - // 当负载因子超过阈值时,执行扩容 - if (this.#loadFactor() > this.#loadThres) { - this.#extend(); - } - // 搜索 key 对应的桶索引 - const index = this.#findBucket(key); - // 若找到键值对,则覆盖 val 并返回 - if ( - this.#buckets[index] !== null && - this.#buckets[index] !== this.#TOMBSTONE - ) { - this.#buckets[index].val = val; - return; - } - // 若键值对不存在,则添加该键值对 - this.#buckets[index] = new Pair(key, val); - this.#size++; - } - - /* 删除操作 */ - remove(key) { - // 搜索 key 对应的桶索引 - const index = this.#findBucket(key); - // 若找到键值对,则用删除标记覆盖它 - if ( - this.#buckets[index] !== null && - this.#buckets[index] !== this.#TOMBSTONE - ) { - this.#buckets[index] = this.#TOMBSTONE; - this.#size--; - } - } - - /* 扩容哈希表 */ - #extend() { - // 暂存原哈希表 - const bucketsTmp = this.#buckets; - // 初始化扩容后的新哈希表 - this.#capacity *= this.#extendRatio; - this.#buckets = Array(this.#capacity).fill(null); - this.#size = 0; - // 将键值对从原哈希表搬运至新哈希表 - for (const pair of bucketsTmp) { - if (pair !== null && pair !== this.#TOMBSTONE) { - this.put(pair.key, pair.val); - } - } - } - - /* 打印哈希表 */ - print() { - for (const pair of this.#buckets) { - if (pair === null) { - console.log('null'); - } else if (pair === this.#TOMBSTONE) { - console.log('TOMBSTONE'); - } else { - console.log(pair.key + ' -> ' + pair.val); - } - } - } - } + [class]{HashMapOpenAddressing}-[func]{} ``` === "TS" ```typescript title="hash_map_open_addressing.ts" - /* 开放寻址哈希表 */ - class HashMapOpenAddressing { - private size: number; // 键值对数量 - private capacity: number; // 哈希表容量 - private loadThres: number; // 触发扩容的负载因子阈值 - private extendRatio: number; // 扩容倍数 - private buckets: Array; // 桶数组 - private TOMBSTONE: Pair; // 删除标记 - - /* 构造方法 */ - constructor() { - this.size = 0; // 键值对数量 - this.capacity = 4; // 哈希表容量 - this.loadThres = 2.0 / 3.0; // 触发扩容的负载因子阈值 - this.extendRatio = 2; // 扩容倍数 - this.buckets = Array(this.capacity).fill(null); // 桶数组 - this.TOMBSTONE = new Pair(-1, '-1'); // 删除标记 - } - - /* 哈希函数 */ - private hashFunc(key: number): number { - return key % this.capacity; - } - - /* 负载因子 */ - private loadFactor(): number { - return this.size / this.capacity; - } - - /* 搜索 key 对应的桶索引 */ - private findBucket(key: number): number { - let index = this.hashFunc(key); - let firstTombstone = -1; - // 线性探测,当遇到空桶时跳出 - while (this.buckets[index] !== null) { - // 若遇到 key ,返回对应的桶索引 - if (this.buckets[index]!.key === key) { - // 若之前遇到了删除标记,则将键值对移动至该索引处 - if (firstTombstone !== -1) { - this.buckets[firstTombstone] = this.buckets[index]; - this.buckets[index] = this.TOMBSTONE; - return firstTombstone; // 返回移动后的桶索引 - } - return index; // 返回桶索引 - } - // 记录遇到的首个删除标记 - if ( - firstTombstone === -1 && - this.buckets[index] === this.TOMBSTONE - ) { - firstTombstone = index; - } - // 计算桶索引,越过尾部则返回头部 - index = (index + 1) % this.capacity; - } - // 若 key 不存在,则返回添加点的索引 - return firstTombstone === -1 ? index : firstTombstone; - } - - /* 查询操作 */ - get(key: number): string | null { - // 搜索 key 对应的桶索引 - const index = this.findBucket(key); - // 若找到键值对,则返回对应 val - if ( - this.buckets[index] !== null && - this.buckets[index] !== this.TOMBSTONE - ) { - return this.buckets[index]!.val; - } - // 若键值对不存在,则返回 null - return null; - } - - /* 添加操作 */ - put(key: number, val: string): void { - // 当负载因子超过阈值时,执行扩容 - if (this.loadFactor() > this.loadThres) { - this.extend(); - } - // 搜索 key 对应的桶索引 - const index = this.findBucket(key); - // 若找到键值对,则覆盖 val 并返回 - if ( - this.buckets[index] !== null && - this.buckets[index] !== this.TOMBSTONE - ) { - this.buckets[index]!.val = val; - return; - } - // 若键值对不存在,则添加该键值对 - this.buckets[index] = new Pair(key, val); - this.size++; - } - - /* 删除操作 */ - remove(key: number): void { - // 搜索 key 对应的桶索引 - const index = this.findBucket(key); - // 若找到键值对,则用删除标记覆盖它 - if ( - this.buckets[index] !== null && - this.buckets[index] !== this.TOMBSTONE - ) { - this.buckets[index] = this.TOMBSTONE; - this.size--; - } - } - - /* 扩容哈希表 */ - private extend(): void { - // 暂存原哈希表 - const bucketsTmp = this.buckets; - // 初始化扩容后的新哈希表 - this.capacity *= this.extendRatio; - this.buckets = Array(this.capacity).fill(null); - this.size = 0; - // 将键值对从原哈希表搬运至新哈希表 - for (const pair of bucketsTmp) { - if (pair !== null && pair !== this.TOMBSTONE) { - this.put(pair.key, pair.val); - } - } - } - - /* 打印哈希表 */ - print(): void { - for (const pair of this.buckets) { - if (pair === null) { - console.log('null'); - } else if (pair === this.TOMBSTONE) { - console.log('TOMBSTONE'); - } else { - console.log(pair.key + ' -> ' + pair.val); - } - } - } - } + [class]{HashMapOpenAddressing}-[func]{} ``` === "Dart" ```dart title="hash_map_open_addressing.dart" - /* 开放寻址哈希表 */ - class HashMapOpenAddressing { - late int _size; // 键值对数量 - int _capacity = 4; // 哈希表容量 - double _loadThres = 2.0 / 3.0; // 触发扩容的负载因子阈值 - int _extendRatio = 2; // 扩容倍数 - late List _buckets; // 桶数组 - Pair _TOMBSTONE = Pair(-1, "-1"); // 删除标记 - - /* 构造方法 */ - HashMapOpenAddressing() { - _size = 0; - _buckets = List.generate(_capacity, (index) => null); - } - - /* 哈希函数 */ - int hashFunc(int key) { - return key % _capacity; - } - - /* 负载因子 */ - double loadFactor() { - return _size / _capacity; - } - - /* 搜索 key 对应的桶索引 */ - int findBucket(int key) { - int index = hashFunc(key); - int 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 firstTombstone == -1 ? index : firstTombstone; - } - - /* 查询操作 */ - String? get(int key) { - // 搜索 key 对应的桶索引 - int index = findBucket(key); - // 若找到键值对,则返回对应 val - if (_buckets[index] != null && _buckets[index] != _TOMBSTONE) { - return _buckets[index]!.val; - } - // 若键值对不存在,则返回 null - return null; - } - - /* 添加操作 */ - void put(int key, String val) { - // 当负载因子超过阈值时,执行扩容 - if (loadFactor() > _loadThres) { - extend(); - } - // 搜索 key 对应的桶索引 - int index = findBucket(key); - // 若找到键值对,则覆盖 val 并返回 - if (_buckets[index] != null && _buckets[index] != _TOMBSTONE) { - _buckets[index]!.val = val; - return; - } - // 若键值对不存在,则添加该键值对 - _buckets[index] = new Pair(key, val); - _size++; - } - - /* 删除操作 */ - void remove(int key) { - // 搜索 key 对应的桶索引 - int index = findBucket(key); - // 若找到键值对,则用删除标记覆盖它 - if (_buckets[index] != null && _buckets[index] != _TOMBSTONE) { - _buckets[index] = _TOMBSTONE; - _size--; - } - } - - /* 扩容哈希表 */ - void extend() { - // 暂存原哈希表 - List bucketsTmp = _buckets; - // 初始化扩容后的新哈希表 - _capacity *= _extendRatio; - _buckets = List.generate(_capacity, (index) => null); - _size = 0; - // 将键值对从原哈希表搬运至新哈希表 - for (Pair? pair in bucketsTmp) { - if (pair != null && pair != _TOMBSTONE) { - put(pair.key, pair.val); - } - } - } - - /* 打印哈希表 */ - void printHashMap() { - for (Pair? pair in _buckets) { - if (pair == null) { - print("null"); - } else if (pair == _TOMBSTONE) { - print("TOMBSTONE"); - } else { - print("${pair.key} -> ${pair.val}"); - } - } - } - } + [class]{HashMapOpenAddressing}-[func]{} ``` === "Rust" ```rust title="hash_map_open_addressing.rs" - /* 开放寻址哈希表 */ - struct HashMapOpenAddressing { - size: usize, // 键值对数量 - capacity: usize, // 哈希表容量 - load_thres: f64, // 触发扩容的负载因子阈值 - extend_ratio: usize, // 扩容倍数 - buckets: Vec>, // 桶数组 - TOMBSTONE: Option, // 删除标记 - } - - impl HashMapOpenAddressing { - /* 构造方法 */ - fn new() -> Self { - Self { - size: 0, - capacity: 4, - load_thres: 2.0 / 3.0, - extend_ratio: 2, - buckets: vec![None; 4], - TOMBSTONE: Some(Pair { - key: -1, - val: "-1".to_string(), - }), - } - } - - /* 哈希函数 */ - fn hash_func(&self, key: i32) -> usize { - (key % self.capacity as i32) as usize - } - - /* 负载因子 */ - fn load_factor(&self) -> f64 { - self.size as f64 / self.capacity as f64 - } - - /* 搜索 key 对应的桶索引 */ - fn find_bucket(&mut self, key: i32) -> usize { - let mut index = self.hash_func(key); - let mut first_tombstone = -1; - // 线性探测,当遇到空桶时跳出 - while self.buckets[index].is_some() { - // 若遇到 key,返回对应的桶索引 - if self.buckets[index].as_ref().unwrap().key == key { - // 若之前遇到了删除标记,则将建值对移动至该索引 - if first_tombstone != -1 { - self.buckets[first_tombstone as usize] = self.buckets[index].take(); - self.buckets[index] = self.TOMBSTONE.clone(); - return first_tombstone as usize; // 返回移动后的桶索引 - } - return index; // 返回桶索引 - } - // 记录遇到的首个删除标记 - if first_tombstone == -1 && self.buckets[index] == self.TOMBSTONE { - first_tombstone = index as i32; - } - // 计算桶索引,越过尾部则返回头部 - index = (index + 1) % self.capacity; - } - // 若 key 不存在,则返回添加点的索引 - if first_tombstone == -1 { - index - } else { - first_tombstone as usize - } - } - - /* 查询操作 */ - fn get(&mut self, key: i32) -> Option<&str> { - // 搜索 key 对应的桶索引 - let index = self.find_bucket(key); - // 若找到键值对,则返回对应 val - if self.buckets[index].is_some() && self.buckets[index] != self.TOMBSTONE { - return self.buckets[index].as_ref().map(|pair| &pair.val as &str); - } - // 若键值对不存在,则返回 null - None - } - - /* 添加操作 */ - fn put(&mut self, key: i32, val: String) { - // 当负载因子超过阈值时,执行扩容 - if self.load_factor() > self.load_thres { - self.extend(); - } - // 搜索 key 对应的桶索引 - let index = self.find_bucket(key); - // 若找到键值对,则覆盖 val 并返回 - if self.buckets[index].is_some() && self.buckets[index] != self.TOMBSTONE { - self.buckets[index].as_mut().unwrap().val = val; - return; - } - // 若键值对不存在,则添加该键值对 - self.buckets[index] = Some(Pair { key, val }); - self.size += 1; - } - - /* 删除操作 */ - fn remove(&mut self, key: i32) { - // 搜索 key 对应的桶索引 - let index = self.find_bucket(key); - // 若找到键值对,则用删除标记覆盖它 - if self.buckets[index].is_some() && self.buckets[index] != self.TOMBSTONE { - self.buckets[index] = self.TOMBSTONE.clone(); - self.size -= 1; - } - } - - /* 扩容哈希表 */ - fn extend(&mut self) { - // 暂存原哈希表 - let buckets_tmp = self.buckets.clone(); - // 初始化扩容后的新哈希表 - self.capacity *= self.extend_ratio; - self.buckets = vec![None; self.capacity]; - self.size = 0; - - // 将键值对从原哈希表搬运至新哈希表 - for pair in buckets_tmp { - if pair.is_none() || pair == self.TOMBSTONE { - continue; - } - let pair = pair.unwrap(); - - self.put(pair.key, pair.val); - } - } - /* 打印哈希表 */ - fn print(&self) { - for pair in &self.buckets { - if pair.is_none() { - println!("null"); - } else if pair == &self.TOMBSTONE { - println!("TOMBSTONE"); - } else { - let pair = pair.as_ref().unwrap(); - println!("{} -> {}", pair.key, pair.val); - } - } - } - } + [class]{HashMapOpenAddressing}-[func]{} ``` === "C" ```c title="hash_map_open_addressing.c" - /* 开放寻址哈希表 */ - typedef struct { - int size; // 键值对数量 - int capacity; // 哈希表容量 - double loadThres; // 触发扩容的负载因子阈值 - int extendRatio; // 扩容倍数 - Pair **buckets; // 桶数组 - Pair *TOMBSTONE; // 删除标记 - } HashMapOpenAddressing; - - /* 构造函数 */ - HashMapOpenAddressing *newHashMapOpenAddressing() { - HashMapOpenAddressing *hashMap = (HashMapOpenAddressing *)malloc(sizeof(HashMapOpenAddressing)); - hashMap->size = 0; - hashMap->capacity = 4; - hashMap->loadThres = 2.0 / 3.0; - hashMap->extendRatio = 2; - hashMap->buckets = (Pair **)malloc(sizeof(Pair *) * hashMap->capacity); - hashMap->TOMBSTONE = (Pair *)malloc(sizeof(Pair)); - hashMap->TOMBSTONE->key = -1; - hashMap->TOMBSTONE->val = "-1"; - - return hashMap; - } - - /* 析构函数 */ - void delHashMapOpenAddressing(HashMapOpenAddressing *hashMap) { - for (int i = 0; i < hashMap->capacity; i++) { - Pair *pair = hashMap->buckets[i]; - if (pair != NULL && pair != hashMap->TOMBSTONE) { - free(pair->val); - free(pair); - } - } - free(hashMap->buckets); - free(hashMap->TOMBSTONE); - free(hashMap); - } - - /* 哈希函数 */ - int hashFunc(HashMapOpenAddressing *hashMap, int key) { - return key % hashMap->capacity; - } - - /* 负载因子 */ - double loadFactor(HashMapOpenAddressing *hashMap) { - return (double)hashMap->size / (double)hashMap->capacity; - } - - /* 搜索 key 对应的桶索引 */ - int findBucket(HashMapOpenAddressing *hashMap, int key) { - int index = hashFunc(hashMap, key); - int firstTombstone = -1; - // 线性探测,当遇到空桶时跳出 - while (hashMap->buckets[index] != NULL) { - // 若遇到 key ,返回对应的桶索引 - if (hashMap->buckets[index]->key == key) { - // 若之前遇到了删除标记,则将键值对移动至该索引处 - if (firstTombstone != -1) { - hashMap->buckets[firstTombstone] = hashMap->buckets[index]; - hashMap->buckets[index] = hashMap->TOMBSTONE; - return firstTombstone; // 返回移动后的桶索引 - } - return index; // 返回桶索引 - } - // 记录遇到的首个删除标记 - if (firstTombstone == -1 && hashMap->buckets[index] == hashMap->TOMBSTONE) { - firstTombstone = index; - } - // 计算桶索引,越过尾部则返回头部 - index = (index + 1) % hashMap->capacity; - } - // 若 key 不存在,则返回添加点的索引 - return firstTombstone == -1 ? index : firstTombstone; - } - - /* 查询操作 */ - char *get(HashMapOpenAddressing *hashMap, int key) { - // 搜索 key 对应的桶索引 - int index = findBucket(hashMap, key); - // 若找到键值对,则返回对应 val - if (hashMap->buckets[index] != NULL && hashMap->buckets[index] != hashMap->TOMBSTONE) { - return hashMap->buckets[index]->val; - } - // 若键值对不存在,则返回空字符串 - return ""; - } - - /* 添加操作 */ - void put(HashMapOpenAddressing *hashMap, int key, char *val) { - // 当负载因子超过阈值时,执行扩容 - if (loadFactor(hashMap) > hashMap->loadThres) { - extend(hashMap); - } - // 搜索 key 对应的桶索引 - int index = findBucket(hashMap, key); - // 若找到键值对,则覆盖 val 并返回 - if (hashMap->buckets[index] != NULL && hashMap->buckets[index] != hashMap->TOMBSTONE) { - free(hashMap->buckets[index]->val); - hashMap->buckets[index]->val = (char *)malloc(sizeof(strlen(val) + 1)); - strcpy(hashMap->buckets[index]->val, val); - hashMap->buckets[index]->val[strlen(val)] = '\0'; - return; - } - // 若键值对不存在,则添加该键值对 - Pair *pair = (Pair *)malloc(sizeof(Pair)); - pair->key = key; - pair->val = (char *)malloc(sizeof(strlen(val) + 1)); - strcpy(pair->val, val); - pair->val[strlen(val)] = '\0'; - - hashMap->buckets[index] = pair; - hashMap->size++; - } - - /* 删除操作 */ - void removeItem(HashMapOpenAddressing *hashMap, int key) { - // 搜索 key 对应的桶索引 - int index = findBucket(hashMap, key); - // 若找到键值对,则用删除标记覆盖它 - if (hashMap->buckets[index] != NULL && hashMap->buckets[index] != hashMap->TOMBSTONE) { - Pair *pair = hashMap->buckets[index]; - free(pair->val); - free(pair); - hashMap->buckets[index] = hashMap->TOMBSTONE; - hashMap->size--; - } - } - - /* 扩容哈希表 */ - void extend(HashMapOpenAddressing *hashMap) { - // 暂存原哈希表 - Pair **bucketsTmp = hashMap->buckets; - int oldCapacity = hashMap->capacity; - // 初始化扩容后的新哈希表 - hashMap->capacity *= hashMap->extendRatio; - hashMap->buckets = (Pair **)malloc(sizeof(Pair *) * hashMap->capacity); - hashMap->size = 0; - // 将键值对从原哈希表搬运至新哈希表 - for (int i = 0; i < oldCapacity; i++) { - Pair *pair = bucketsTmp[i]; - if (pair != NULL && pair != hashMap->TOMBSTONE) { - put(hashMap, pair->key, pair->val); - free(pair->val); - free(pair); - } - } - free(bucketsTmp); - } - - /* 打印哈希表 */ - void print(HashMapOpenAddressing *hashMap) { - for (int i = 0; i < hashMap->capacity; i++) { - Pair *pair = hashMap->buckets[i]; - if (pair == NULL) { - printf("NULL\n"); - } else if (pair == hashMap->TOMBSTONE) { - printf("TOMBSTONE\n"); - } else { - printf("%d -> %s\n", pair->key, pair->val); - } - } - } + [class]{HashMapOpenAddressing}-[func]{} ``` === "Kotlin" ```kotlin title="hash_map_open_addressing.kt" - /* 开放寻址哈希表 */ - class HashMapOpenAddressing { - private var size: Int // 键值对数量 - private var capacity: Int // 哈希表容量 - private val loadThres: Double // 触发扩容的负载因子阈值 - private val extendRatio: Int // 扩容倍数 - private var buckets: Array // 桶数组 - private val TOMBSTONE: Pair // 删除标记 - - /* 构造方法 */ - init { - size = 0 - capacity = 4 - loadThres = 2.0 / 3.0 - extendRatio = 2 - buckets = arrayOfNulls(capacity) - TOMBSTONE = Pair(-1, "-1") - } - - /* 哈希函数 */ - 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]?._val - } - // 若键值对不存在,则返回 null - return null - } - - /* 添加操作 */ - fun put(key: Int, _val: String) { - // 当负载因子超过阈值时,执行扩容 - if (loadFactor() > loadThres) { - extend() - } - // 搜索 key 对应的桶索引 - val index = findBucket(key) - // 若找到键值对,则覆盖 val 并返回 - if (buckets[index] != null && buckets[index] != TOMBSTONE) { - buckets[index]!!._val = _val - return - } - // 若键值对不存在,则添加该键值对 - buckets[index] = Pair(key, _val) - 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._val) - } - } - } - - /* 打印哈希表 */ - fun print() { - for (pair in buckets) { - if (pair == null) { - println("null") - } else if (pair == TOMBSTONE) { - println("TOMESTOME") - } else { - println("${pair.key} -> ${pair._val}") - } - } - } - } + [class]{HashMapOpenAddressing}-[func]{} ``` === "Ruby" ```ruby title="hash_map_open_addressing.rb" - ### 开放寻址哈希表 ### - class HashMapOpenAddressing - TOMBSTONE = Pair.new(-1, '-1') # 删除标记 - - ### 构造方法 ### - def initialize - @size = 0 # 键值对数量 - @capacity = 4 # 哈希表容量 - @load_thres = 2.0 / 3.0 # 触发扩容的负载因子阈值 - @extend_ratio = 2 # 扩容倍数 - @buckets = Array.new(@capacity) # 桶数组 - end - - ### 哈希函数 ### - def hash_func(key) - key % @capacity - end - - ### 负载因子 ### - def load_factor - @size / @capacity - end - - ### 搜索 key 对应的桶索引 ### - def find_bucket(key) - index = hash_func(key) - first_tombstone = -1 - # 线性探测,当遇到空桶时跳出 - while !@buckets[index].nil? - # 若遇到 key ,返回对应的桶索引 - if @buckets[index].key == key - # 若之前遇到了删除标记,则将键值对移动至该索引处 - if first_tombstone != -1 - @buckets[first_tombstone] = @buckets[index] - @buckets[index] = TOMBSTONE - return first_tombstone # 返回移动后的桶索引 - end - return index # 返回桶索引 - end - # 记录遇到的首个删除标记 - first_tombstone = index if first_tombstone == -1 && @buckets[index] == TOMBSTONE - # 计算桶索引,越过尾部则返回头部 - index = (index + 1) % @capacity - end - # 若 key 不存在,则返回添加点的索引 - first_tombstone == -1 ? index : first_tombstone - end - - ### 查询操作 ### - def get(key) - # 搜索 key 对应的桶索引 - index = find_bucket(key) - # 若找到键值对,则返回对应 val - return @buckets[index].val unless [nil, TOMBSTONE].include?(@buckets[index]) - # 若键值对不存在,则返回 nil - nil - end - - ### 添加操作 ### - def put(key, val) - # 当负载因子超过阈值时,执行扩容 - extend if load_factor > @load_thres - # 搜索 key 对应的桶索引 - index = find_bucket(key) - # 若找到键值对,则覆盖 val 开返回 - unless [nil, TOMBSTONE].include?(@buckets[index]) - @buckets[index].val = val - return - end - # 若键值对不存在,则添加该键值对 - @buckets[index] = Pair.new(key, val) - @size += 1 - end - - ### 删除操作 ### - def remove(key) - # 搜索 key 对应的桶索引 - index = find_bucket(key) - # 若找到键值对,则用删除标记覆盖它 - unless [nil, TOMBSTONE].include?(@buckets[index]) - @buckets[index] = TOMBSTONE - @size -= 1 - end - end - - ### 扩容哈希表 ### - def extend - # 暂存原哈希表 - buckets_tmp = @buckets - # 初始化扩容后的新哈希表 - @capacity *= @extend_ratio - @buckets = Array.new(@capacity) - @size = 0 - # 将键值对从原哈希表搬运至新哈希表 - for pair in buckets_tmp - put(pair.key, pair.val) unless [nil, TOMBSTONE].include?(pair) - end - end - - ### 打印哈希表 ### - def print - for pair in @buckets - if pair.nil? - puts "Nil" - elsif pair == TOMBSTONE - puts "TOMBSTONE" - else - puts "#{pair.key} -> #{pair.val}" - end - end - end - end + [class]{HashMapOpenAddressing}-[func]{} ``` === "Zig" diff --git a/en/docs/chapter_hashing/hash_map.md b/en/docs/chapter_hashing/hash_map.md index c00ca5226..0e247d300 100755 --- a/en/docs/chapter_hashing/hash_map.md +++ b/en/docs/chapter_hashing/hash_map.md @@ -525,27 +525,27 @@ The following code implements a simple hash table. Here, we encapsulate `key` an ```python title="array_hash_map.py" class Pair: - """键值对""" + """Key-value pair""" def __init__(self, key: int, val: str): self.key = key self.val = val class ArrayHashMap: - """基于数组实现的哈希表""" + """Hash table based on array implementation""" def __init__(self): - """构造方法""" - # 初始化数组,包含 100 个桶 + """Constructor""" + # Initialize an array, containing 100 buckets self.buckets: list[Pair | None] = [None] * 100 def hash_func(self, key: int) -> int: - """哈希函数""" + """Hash function""" index = key % 100 return index def get(self, key: int) -> str: - """查询操作""" + """Query operation""" index: int = self.hash_func(key) pair: Pair = self.buckets[index] if pair is None: @@ -553,19 +553,19 @@ The following code implements a simple hash table. Here, we encapsulate `key` an return pair.val def put(self, key: int, val: str): - """添加操作""" + """Add operation""" pair = Pair(key, val) index: int = self.hash_func(key) self.buckets[index] = pair def remove(self, key: int): - """删除操作""" + """Remove operation""" index: int = self.hash_func(key) - # 置为 None ,代表删除 + # Set to None, representing removal self.buckets[index] = None def entry_set(self) -> list[Pair]: - """获取所有键值对""" + """Get all key-value pairs""" result: list[Pair] = [] for pair in self.buckets: if pair is not None: @@ -573,7 +573,7 @@ The following code implements a simple hash table. Here, we encapsulate `key` an return result def key_set(self) -> list[int]: - """获取所有键""" + """Get all keys""" result = [] for pair in self.buckets: if pair is not None: @@ -581,7 +581,7 @@ The following code implements a simple hash table. Here, we encapsulate `key` an return result def value_set(self) -> list[str]: - """获取所有值""" + """Get all values""" result = [] for pair in self.buckets: if pair is not None: @@ -589,7 +589,7 @@ The following code implements a simple hash table. Here, we encapsulate `key` an return result def print(self): - """打印哈希表""" + """Print hash table""" for pair in self.buckets: if pair is not None: print(pair.key, "->", pair.val) @@ -598,112 +598,15 @@ The following code implements a simple hash table. Here, we encapsulate `key` an === "C++" ```cpp title="array_hash_map.cpp" - /* 键值对 */ - struct Pair { - public: - int key; - string val; - Pair(int key, string val) { - this->key = key; - this->val = val; - } - }; + [class]{Pair}-[func]{} - /* 基于数组实现的哈希表 */ - class ArrayHashMap { - private: - vector buckets; - - public: - ArrayHashMap() { - // 初始化数组,包含 100 个桶 - buckets = vector(100); - } - - ~ArrayHashMap() { - // 释放内存 - for (const auto &bucket : buckets) { - delete bucket; - } - buckets.clear(); - } - - /* 哈希函数 */ - int hashFunc(int key) { - int index = key % 100; - return index; - } - - /* 查询操作 */ - string get(int key) { - int index = hashFunc(key); - Pair *pair = buckets[index]; - if (pair == nullptr) - return ""; - return pair->val; - } - - /* 添加操作 */ - void put(int key, string val) { - Pair *pair = new Pair(key, val); - int index = hashFunc(key); - buckets[index] = pair; - } - - /* 删除操作 */ - void remove(int key) { - int index = hashFunc(key); - // 释放内存并置为 nullptr - delete buckets[index]; - buckets[index] = nullptr; - } - - /* 获取所有键值对 */ - vector pairSet() { - vector pairSet; - for (Pair *pair : buckets) { - if (pair != nullptr) { - pairSet.push_back(pair); - } - } - return pairSet; - } - - /* 获取所有键 */ - vector keySet() { - vector keySet; - for (Pair *pair : buckets) { - if (pair != nullptr) { - keySet.push_back(pair->key); - } - } - return keySet; - } - - /* 获取所有值 */ - vector valueSet() { - vector valueSet; - for (Pair *pair : buckets) { - if (pair != nullptr) { - valueSet.push_back(pair->val); - } - } - return valueSet; - } - - /* 打印哈希表 */ - void print() { - for (Pair *kv : pairSet()) { - cout << kv->key << " -> " << kv->val << endl; - } - } - }; + [class]{ArrayHashMap}-[func]{} ``` === "Java" ```java title="array_hash_map.java" - /* 键值对 */ + /* Key-value pair */ class Pair { public int key; public String val; @@ -714,25 +617,25 @@ The following code implements a simple hash table. Here, we encapsulate `key` an } } - /* 基于数组实现的哈希表 */ + /* Hash table based on array implementation */ class ArrayHashMap { private List buckets; public ArrayHashMap() { - // 初始化数组,包含 100 个桶 + // Initialize an array, containing 100 buckets buckets = new ArrayList<>(); for (int i = 0; i < 100; i++) { buckets.add(null); } } - /* 哈希函数 */ + /* Hash function */ private int hashFunc(int key) { int index = key % 100; return index; } - /* 查询操作 */ + /* Query operation */ public String get(int key) { int index = hashFunc(key); Pair pair = buckets.get(index); @@ -741,21 +644,21 @@ The following code implements a simple hash table. Here, we encapsulate `key` an return pair.val; } - /* 添加操作 */ + /* Add operation */ public void put(int key, String val) { Pair pair = new Pair(key, val); int index = hashFunc(key); buckets.set(index, pair); } - /* 删除操作 */ + /* Remove operation */ public void remove(int key) { int index = hashFunc(key); - // 置为 null ,代表删除 + // Set to null, indicating removal buckets.set(index, null); } - /* 获取所有键值对 */ + /* Get all key-value pairs */ public List pairSet() { List pairSet = new ArrayList<>(); for (Pair pair : buckets) { @@ -765,7 +668,7 @@ The following code implements a simple hash table. Here, we encapsulate `key` an return pairSet; } - /* 获取所有键 */ + /* Get all keys */ public List keySet() { List keySet = new ArrayList<>(); for (Pair pair : buckets) { @@ -775,7 +678,7 @@ The following code implements a simple hash table. Here, we encapsulate `key` an return keySet; } - /* 获取所有值 */ + /* Get all values */ public List valueSet() { List valueSet = new ArrayList<>(); for (Pair pair : buckets) { @@ -785,7 +688,7 @@ The following code implements a simple hash table. Here, we encapsulate `key` an return valueSet; } - /* 打印哈希表 */ + /* Print hash table */ public void print() { for (Pair kv : pairSet()) { System.out.println(kv.key + " -> " + kv.val); @@ -797,1022 +700,91 @@ The following code implements a simple hash table. Here, we encapsulate `key` an === "C#" ```csharp title="array_hash_map.cs" - /* 键值对 int->string */ - class Pair(int key, string val) { - public int key = key; - public string val = val; - } + [class]{Pair}-[func]{} - /* 基于数组实现的哈希表 */ - class ArrayHashMap { - List buckets; - public ArrayHashMap() { - // 初始化数组,包含 100 个桶 - buckets = []; - for (int i = 0; i < 100; i++) { - buckets.Add(null); - } - } - - /* 哈希函数 */ - int HashFunc(int key) { - int index = key % 100; - return index; - } - - /* 查询操作 */ - public string? Get(int key) { - int index = HashFunc(key); - Pair? pair = buckets[index]; - if (pair == null) return null; - return pair.val; - } - - /* 添加操作 */ - public void Put(int key, string val) { - Pair pair = new(key, val); - int index = HashFunc(key); - buckets[index] = pair; - } - - /* 删除操作 */ - public void Remove(int key) { - int index = HashFunc(key); - // 置为 null ,代表删除 - buckets[index] = null; - } - - /* 获取所有键值对 */ - public List PairSet() { - List pairSet = []; - foreach (Pair? pair in buckets) { - if (pair != null) - pairSet.Add(pair); - } - return pairSet; - } - - /* 获取所有键 */ - public List KeySet() { - List keySet = []; - foreach (Pair? pair in buckets) { - if (pair != null) - keySet.Add(pair.key); - } - return keySet; - } - - /* 获取所有值 */ - public List ValueSet() { - List valueSet = []; - foreach (Pair? pair in buckets) { - if (pair != null) - valueSet.Add(pair.val); - } - return valueSet; - } - - /* 打印哈希表 */ - public void Print() { - foreach (Pair kv in PairSet()) { - Console.WriteLine(kv.key + " -> " + kv.val); - } - } - } + [class]{ArrayHashMap}-[func]{} ``` === "Go" ```go title="array_hash_map.go" - /* 键值对 */ - type pair struct { - key int - val string - } + [class]{pair}-[func]{} - /* 基于数组实现的哈希表 */ - type arrayHashMap struct { - buckets []*pair - } - - /* 初始化哈希表 */ - func newArrayHashMap() *arrayHashMap { - // 初始化数组,包含 100 个桶 - buckets := make([]*pair, 100) - return &arrayHashMap{buckets: buckets} - } - - /* 哈希函数 */ - func (a *arrayHashMap) hashFunc(key int) int { - index := key % 100 - return index - } - - /* 查询操作 */ - func (a *arrayHashMap) get(key int) string { - index := a.hashFunc(key) - pair := a.buckets[index] - if pair == nil { - return "Not Found" - } - return pair.val - } - - /* 添加操作 */ - func (a *arrayHashMap) put(key int, val string) { - pair := &pair{key: key, val: val} - index := a.hashFunc(key) - a.buckets[index] = pair - } - - /* 删除操作 */ - func (a *arrayHashMap) remove(key int) { - index := a.hashFunc(key) - // 置为 nil ,代表删除 - a.buckets[index] = nil - } - - /* 获取所有键对 */ - func (a *arrayHashMap) pairSet() []*pair { - var pairs []*pair - for _, pair := range a.buckets { - if pair != nil { - pairs = append(pairs, pair) - } - } - return pairs - } - - /* 获取所有键 */ - func (a *arrayHashMap) keySet() []int { - var keys []int - for _, pair := range a.buckets { - if pair != nil { - keys = append(keys, pair.key) - } - } - return keys - } - - /* 获取所有值 */ - func (a *arrayHashMap) valueSet() []string { - var values []string - for _, pair := range a.buckets { - if pair != nil { - values = append(values, pair.val) - } - } - return values - } - - /* 打印哈希表 */ - func (a *arrayHashMap) print() { - for _, pair := range a.buckets { - if pair != nil { - fmt.Println(pair.key, "->", pair.val) - } - } - } + [class]{arrayHashMap}-[func]{} ``` === "Swift" ```swift title="array_hash_map.swift" - /* 键值对 */ - class Pair: Equatable { - public var key: Int - public var val: String + [file]{utils/pair.swift}-[class]{Pair}-[func]{} - public init(key: Int, val: String) { - self.key = key - self.val = val - } - - public static func == (lhs: Pair, rhs: Pair) -> Bool { - lhs.key == rhs.key && lhs.val == rhs.val - } - } - - /* 基于数组实现的哈希表 */ - class ArrayHashMap { - private var buckets: [Pair?] - - init() { - // 初始化数组,包含 100 个桶 - buckets = Array(repeating: nil, count: 100) - } - - /* 哈希函数 */ - private func hashFunc(key: Int) -> Int { - let index = key % 100 - return index - } - - /* 查询操作 */ - func get(key: Int) -> String? { - let index = hashFunc(key: key) - let pair = buckets[index] - return pair?.val - } - - /* 添加操作 */ - func put(key: Int, val: String) { - let pair = Pair(key: key, val: val) - let index = hashFunc(key: key) - buckets[index] = pair - } - - /* 删除操作 */ - func remove(key: Int) { - let index = hashFunc(key: key) - // 置为 nil ,代表删除 - buckets[index] = nil - } - - /* 获取所有键值对 */ - func pairSet() -> [Pair] { - buckets.compactMap { $0 } - } - - /* 获取所有键 */ - func keySet() -> [Int] { - buckets.compactMap { $0?.key } - } - - /* 获取所有值 */ - func valueSet() -> [String] { - buckets.compactMap { $0?.val } - } - - /* 打印哈希表 */ - func print() { - for pair in pairSet() { - Swift.print("\(pair.key) -> \(pair.val)") - } - } - } + [class]{ArrayHashMap}-[func]{} ``` === "JS" ```javascript title="array_hash_map.js" - /* 键值对 Number -> String */ - class Pair { - constructor(key, val) { - this.key = key; - this.val = val; - } - } + [class]{Pair}-[func]{} - /* 基于数组实现的哈希表 */ - class ArrayHashMap { - #buckets; - constructor() { - // 初始化数组,包含 100 个桶 - this.#buckets = new Array(100).fill(null); - } - - /* 哈希函数 */ - #hashFunc(key) { - return key % 100; - } - - /* 查询操作 */ - get(key) { - let index = this.#hashFunc(key); - let pair = this.#buckets[index]; - if (pair === null) return null; - return pair.val; - } - - /* 添加操作 */ - set(key, val) { - let index = this.#hashFunc(key); - this.#buckets[index] = new Pair(key, val); - } - - /* 删除操作 */ - delete(key) { - let index = this.#hashFunc(key); - // 置为 null ,代表删除 - this.#buckets[index] = null; - } - - /* 获取所有键值对 */ - entries() { - let arr = []; - for (let i = 0; i < this.#buckets.length; i++) { - if (this.#buckets[i]) { - arr.push(this.#buckets[i]); - } - } - return arr; - } - - /* 获取所有键 */ - keys() { - let arr = []; - for (let i = 0; i < this.#buckets.length; i++) { - if (this.#buckets[i]) { - arr.push(this.#buckets[i].key); - } - } - return arr; - } - - /* 获取所有值 */ - values() { - let arr = []; - for (let i = 0; i < this.#buckets.length; i++) { - if (this.#buckets[i]) { - arr.push(this.#buckets[i].val); - } - } - return arr; - } - - /* 打印哈希表 */ - print() { - let pairSet = this.entries(); - for (const pair of pairSet) { - console.info(`${pair.key} -> ${pair.val}`); - } - } - } + [class]{ArrayHashMap}-[func]{} ``` === "TS" ```typescript title="array_hash_map.ts" - /* 键值对 Number -> String */ - class Pair { - public key: number; - public val: string; + [class]{Pair}-[func]{} - constructor(key: number, val: string) { - this.key = key; - this.val = val; - } - } - - /* 基于数组实现的哈希表 */ - class ArrayHashMap { - private readonly buckets: (Pair | null)[]; - - constructor() { - // 初始化数组,包含 100 个桶 - this.buckets = new Array(100).fill(null); - } - - /* 哈希函数 */ - private hashFunc(key: number): number { - return key % 100; - } - - /* 查询操作 */ - public get(key: number): string | null { - let index = this.hashFunc(key); - let pair = this.buckets[index]; - if (pair === null) return null; - return pair.val; - } - - /* 添加操作 */ - public set(key: number, val: string) { - let index = this.hashFunc(key); - this.buckets[index] = new Pair(key, val); - } - - /* 删除操作 */ - public delete(key: number) { - let index = this.hashFunc(key); - // 置为 null ,代表删除 - this.buckets[index] = null; - } - - /* 获取所有键值对 */ - public entries(): (Pair | null)[] { - let arr: (Pair | null)[] = []; - for (let i = 0; i < this.buckets.length; i++) { - if (this.buckets[i]) { - arr.push(this.buckets[i]); - } - } - return arr; - } - - /* 获取所有键 */ - public keys(): (number | undefined)[] { - let arr: (number | undefined)[] = []; - for (let i = 0; i < this.buckets.length; i++) { - if (this.buckets[i]) { - arr.push(this.buckets[i].key); - } - } - return arr; - } - - /* 获取所有值 */ - public values(): (string | undefined)[] { - let arr: (string | undefined)[] = []; - for (let i = 0; i < this.buckets.length; i++) { - if (this.buckets[i]) { - arr.push(this.buckets[i].val); - } - } - return arr; - } - - /* 打印哈希表 */ - public print() { - let pairSet = this.entries(); - for (const pair of pairSet) { - console.info(`${pair.key} -> ${pair.val}`); - } - } - } + [class]{ArrayHashMap}-[func]{} ``` === "Dart" ```dart title="array_hash_map.dart" - /* 键值对 */ - class Pair { - int key; - String val; - Pair(this.key, this.val); - } + [class]{Pair}-[func]{} - /* 基于数组实现的哈希表 */ - class ArrayHashMap { - late List _buckets; - - ArrayHashMap() { - // 初始化数组,包含 100 个桶 - _buckets = List.filled(100, null); - } - - /* 哈希函数 */ - int _hashFunc(int key) { - final int index = key % 100; - return index; - } - - /* 查询操作 */ - String? get(int key) { - final int index = _hashFunc(key); - final Pair? pair = _buckets[index]; - if (pair == null) { - return null; - } - return pair.val; - } - - /* 添加操作 */ - void put(int key, String val) { - final Pair pair = Pair(key, val); - final int index = _hashFunc(key); - _buckets[index] = pair; - } - - /* 删除操作 */ - void remove(int key) { - final int index = _hashFunc(key); - _buckets[index] = null; - } - - /* 获取所有键值对 */ - List pairSet() { - List pairSet = []; - for (final Pair? pair in _buckets) { - if (pair != null) { - pairSet.add(pair); - } - } - return pairSet; - } - - /* 获取所有键 */ - List keySet() { - List keySet = []; - for (final Pair? pair in _buckets) { - if (pair != null) { - keySet.add(pair.key); - } - } - return keySet; - } - - /* 获取所有值 */ - List values() { - List valueSet = []; - for (final Pair? pair in _buckets) { - if (pair != null) { - valueSet.add(pair.val); - } - } - return valueSet; - } - - /* 打印哈希表 */ - void printHashMap() { - for (final Pair kv in pairSet()) { - print("${kv.key} -> ${kv.val}"); - } - } - } + [class]{ArrayHashMap}-[func]{} ``` === "Rust" ```rust title="array_hash_map.rs" - /* 键值对 */ - #[derive(Debug, Clone, PartialEq)] - pub struct Pair { - pub key: i32, - pub val: String, - } + [class]{Pair}-[func]{} - /* 基于数组实现的哈希表 */ - pub struct ArrayHashMap { - buckets: Vec>, - } - - impl ArrayHashMap { - pub fn new() -> ArrayHashMap { - // 初始化数组,包含 100 个桶 - Self { - buckets: vec![None; 100], - } - } - - /* 哈希函数 */ - fn hash_func(&self, key: i32) -> usize { - key as usize % 100 - } - - /* 查询操作 */ - pub fn get(&self, key: i32) -> Option<&String> { - let index = self.hash_func(key); - self.buckets[index].as_ref().map(|pair| &pair.val) - } - - /* 添加操作 */ - pub fn put(&mut self, key: i32, val: &str) { - let index = self.hash_func(key); - self.buckets[index] = Some(Pair { - key, - val: val.to_string(), - }); - } - - /* 删除操作 */ - pub fn remove(&mut self, key: i32) { - let index = self.hash_func(key); - // 置为 None ,代表删除 - self.buckets[index] = None; - } - - /* 获取所有键值对 */ - pub fn entry_set(&self) -> Vec<&Pair> { - self.buckets - .iter() - .filter_map(|pair| pair.as_ref()) - .collect() - } - - /* 获取所有键 */ - pub fn key_set(&self) -> Vec<&i32> { - self.buckets - .iter() - .filter_map(|pair| pair.as_ref().map(|pair| &pair.key)) - .collect() - } - - /* 获取所有值 */ - pub fn value_set(&self) -> Vec<&String> { - self.buckets - .iter() - .filter_map(|pair| pair.as_ref().map(|pair| &pair.val)) - .collect() - } - - /* 打印哈希表 */ - pub fn print(&self) { - for pair in self.entry_set() { - println!("{} -> {}", pair.key, pair.val); - } - } - } + [class]{ArrayHashMap}-[func]{} ``` === "C" ```c title="array_hash_map.c" - /* 键值对 int->string */ - typedef struct { - int key; - char *val; - } Pair; + [class]{Pair}-[func]{} - /* 基于数组实现的哈希表 */ - typedef struct { - Pair *buckets[MAX_SIZE]; - } ArrayHashMap; - - /* 构造函数 */ - ArrayHashMap *newArrayHashMap() { - ArrayHashMap *hmap = malloc(sizeof(ArrayHashMap)); - for (int i=0; i < MAX_SIZE; i++) { - hmap->buckets[i] = NULL; - } - return hmap; - } - - /* 析构函数 */ - void delArrayHashMap(ArrayHashMap *hmap) { - for (int i = 0; i < MAX_SIZE; i++) { - if (hmap->buckets[i] != NULL) { - free(hmap->buckets[i]->val); - free(hmap->buckets[i]); - } - } - free(hmap); - } - - /* 添加操作 */ - void put(ArrayHashMap *hmap, const int key, const char *val) { - Pair *Pair = malloc(sizeof(Pair)); - Pair->key = key; - Pair->val = malloc(strlen(val) + 1); - strcpy(Pair->val, val); - - int index = hashFunc(key); - hmap->buckets[index] = Pair; - } - - /* 删除操作 */ - void removeItem(ArrayHashMap *hmap, const int key) { - int index = hashFunc(key); - free(hmap->buckets[index]->val); - free(hmap->buckets[index]); - hmap->buckets[index] = NULL; - } - - /* 获取所有键值对 */ - void pairSet(ArrayHashMap *hmap, MapSet *set) { - Pair *entries; - int i = 0, index = 0; - int total = 0; - /* 统计有效键值对数量 */ - for (i = 0; i < MAX_SIZE; i++) { - if (hmap->buckets[i] != NULL) { - total++; - } - } - entries = malloc(sizeof(Pair) * total); - for (i = 0; i < MAX_SIZE; i++) { - if (hmap->buckets[i] != NULL) { - entries[index].key = hmap->buckets[i]->key; - entries[index].val = malloc(strlen(hmap->buckets[i]->val) + 1); - strcpy(entries[index].val, hmap->buckets[i]->val); - index++; - } - } - set->set = entries; - set->len = total; - } - - /* 获取所有键 */ - void keySet(ArrayHashMap *hmap, MapSet *set) { - int *keys; - int i = 0, index = 0; - int total = 0; - /* 统计有效键值对数量 */ - for (i = 0; i < MAX_SIZE; i++) { - if (hmap->buckets[i] != NULL) { - total++; - } - } - keys = malloc(total * sizeof(int)); - for (i = 0; i < MAX_SIZE; i++) { - if (hmap->buckets[i] != NULL) { - keys[index] = hmap->buckets[i]->key; - index++; - } - } - set->set = keys; - set->len = total; - } - - /* 获取所有值 */ - void valueSet(ArrayHashMap *hmap, MapSet *set) { - char **vals; - int i = 0, index = 0; - int total = 0; - /* 统计有效键值对数量 */ - for (i = 0; i < MAX_SIZE; i++) { - if (hmap->buckets[i] != NULL) { - total++; - } - } - vals = malloc(total * sizeof(char *)); - for (i = 0; i < MAX_SIZE; i++) { - if (hmap->buckets[i] != NULL) { - vals[index] = hmap->buckets[i]->val; - index++; - } - } - set->set = vals; - set->len = total; - } - - /* 打印哈希表 */ - void print(ArrayHashMap *hmap) { - int i; - MapSet set; - pairSet(hmap, &set); - Pair *entries = (Pair *)set.set; - for (i = 0; i < set.len; i++) { - printf("%d -> %s\n", entries[i].key, entries[i].val); - } - free(set.set); - } + [class]{ArrayHashMap}-[func]{} ``` === "Kotlin" ```kotlin title="array_hash_map.kt" - /* 键值对 */ - class Pair( - var key: Int, - var _val: String - ) + [class]{Pair}-[func]{} - /* 基于数组实现的哈希表 */ - class ArrayHashMap { - // 初始化数组,包含 100 个桶 - private val buckets = arrayOfNulls(100) - - /* 哈希函数 */ - 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._val - } - - /* 添加操作 */ - fun put(key: Int, _val: String) { - val pair = Pair(key, _val) - val index = hashFunc(key) - buckets[index] = pair - } - - /* 删除操作 */ - fun remove(key: Int) { - val index = hashFunc(key) - // 置为 null ,代表删除 - buckets[index] = null - } - - /* 获取所有键值对 */ - fun pairSet(): MutableList { - val pairSet = mutableListOf() - for (pair in buckets) { - if (pair != null) - pairSet.add(pair) - } - return pairSet - } - - /* 获取所有键 */ - fun keySet(): MutableList { - val keySet = mutableListOf() - for (pair in buckets) { - if (pair != null) - keySet.add(pair.key) - } - return keySet - } - - /* 获取所有值 */ - fun valueSet(): MutableList { - val valueSet = mutableListOf() - for (pair in buckets) { - if (pair != null) - valueSet.add(pair._val) - } - return valueSet - } - - /* 打印哈希表 */ - fun print() { - for (kv in pairSet()) { - val key = kv.key - val _val = kv._val - println("$key -> $_val") - } - } - } + [class]{ArrayHashMap}-[func]{} ``` === "Ruby" ```ruby title="array_hash_map.rb" - ### 键值对 ### - class Pair - attr_accessor :key, :val + [class]{Pair}-[func]{} - def initialize(key, val) - @key = key - @val = val - end - end - - ### 基于数组实现的哈希表 ### - class ArrayHashMap - ### 构造方法 ### - def initialize - # 初始化数组,包含 100 个桶 - @buckets = Array.new(100) - end - - ### 哈希函数 ### - def hash_func(key) - index = key % 100 - end - - ### 查询操作 ### - def get(key) - index = hash_func(key) - pair = @buckets[index] - - return if pair.nil? - pair.val - end - - ### 添加操作 ### - def put(key, val) - pair = Pair.new(key, val) - index = hash_func(key) - @buckets[index] = pair - end - - ### 删除操作 ### - def remove(key) - index = hash_func(key) - # 置为 nil ,代表删除 - @buckets[index] = nil - end - - ### 获取所有键值对 ### - def entry_set - result = [] - @buckets.each { |pair| result << pair unless pair.nil? } - result - end - - ### 获取所有键 ### - def key_set - result = [] - @buckets.each { |pair| result << pair.key unless pair.nil? } - result - end - - ### 获取所有值 ### - def value_set - result = [] - @buckets.each { |pair| result << pair.val unless pair.nil? } - result - end - - ### 打印哈希表 ### - def print - @buckets.each { |pair| puts "#{pair.key} -> #{pair.val}" unless pair.nil? } - end - end + [class]{ArrayHashMap}-[func]{} ``` === "Zig" ```zig title="array_hash_map.zig" - // 键值对 - const Pair = struct { - key: usize = undefined, - val: []const u8 = undefined, + [class]{Pair}-[func]{} - pub fn init(key: usize, val: []const u8) Pair { - return Pair { - .key = key, - .val = val, - }; - } - }; - - // 基于数组实现的哈希表 - fn ArrayHashMap(comptime T: type) type { - return struct { - bucket: ?std.ArrayList(?T) = null, - mem_allocator: std.mem.Allocator = undefined, - - const Self = @This(); - - // 构造函数 - pub fn init(self: *Self, allocator: std.mem.Allocator) !void { - self.mem_allocator = allocator; - // 初始化一个长度为 100 的桶(数组) - self.bucket = std.ArrayList(?T).init(self.mem_allocator); - var i: i32 = 0; - while (i < 100) : (i += 1) { - try self.bucket.?.append(null); - } - } - - // 析构函数 - pub fn deinit(self: *Self) void { - if (self.bucket != null) self.bucket.?.deinit(); - } - - // 哈希函数 - fn hashFunc(key: usize) usize { - var index = key % 100; - return index; - } - - // 查询操作 - pub fn get(self: *Self, key: usize) []const u8 { - var index = hashFunc(key); - var pair = self.bucket.?.items[index]; - return pair.?.val; - } - - // 添加操作 - pub fn put(self: *Self, key: usize, val: []const u8) !void { - var pair = Pair.init(key, val); - var index = hashFunc(key); - self.bucket.?.items[index] = pair; - } - - // 删除操作 - pub fn remove(self: *Self, key: usize) !void { - var index = hashFunc(key); - // 置为 null ,代表删除 - self.bucket.?.items[index] = null; - } - - // 获取所有键值对 - pub fn pairSet(self: *Self) !std.ArrayList(T) { - var entry_set = std.ArrayList(T).init(self.mem_allocator); - for (self.bucket.?.items) |item| { - if (item == null) continue; - try entry_set.append(item.?); - } - return entry_set; - } - - // 获取所有键 - pub fn keySet(self: *Self) !std.ArrayList(usize) { - var key_set = std.ArrayList(usize).init(self.mem_allocator); - for (self.bucket.?.items) |item| { - if (item == null) continue; - try key_set.append(item.?.key); - } - return key_set; - } - - // 获取所有值 - pub fn valueSet(self: *Self) !std.ArrayList([]const u8) { - var value_set = std.ArrayList([]const u8).init(self.mem_allocator); - for (self.bucket.?.items) |item| { - if (item == null) continue; - try value_set.append(item.?.val); - } - return value_set; - } - - // 打印哈希表 - pub fn print(self: *Self) !void { - var entry_set = try self.pairSet(); - defer entry_set.deinit(); - for (entry_set.items) |item| { - std.debug.print("{} -> {s}\n", .{item.key, item.val}); - } - } - }; - } + [class]{ArrayHashMap}-[func]{} ``` -??? pythontutor "Code Visualization" - -
- - ## 6.1.3   Hash collision and resizing Fundamentally, the role of the hash function is to map the entire input space of all keys to the output space of all array indices. However, the input space is often much larger than the output space. Therefore, **theoretically, there must be situations where "multiple inputs correspond to the same output"**. diff --git a/en/docs/chapter_heap/build_heap.md b/en/docs/chapter_heap/build_heap.md index b6b9d3103..bea3ca370 100644 --- a/en/docs/chapter_heap/build_heap.md +++ b/en/docs/chapter_heap/build_heap.md @@ -31,10 +31,10 @@ It's worth mentioning that **since leaf nodes have no children, they naturally f ```python title="my_heap.py" def __init__(self, nums: list[int]): - """构造方法,根据输入列表建堆""" - # 将列表元素原封不动添加进堆 + """Constructor, build heap based on input list""" + # Add all list elements into the heap self.max_heap = nums - # 堆化除叶节点以外的其他所有节点 + # Heapify all nodes except leaves for i in range(self.parent(self.size() - 1), -1, -1): self.sift_down(i) ``` @@ -42,25 +42,17 @@ It's worth mentioning that **since leaf nodes have no children, they naturally f === "C++" ```cpp title="my_heap.cpp" - /* 构造方法,根据输入列表建堆 */ - MaxHeap(vector nums) { - // 将列表元素原封不动添加进堆 - maxHeap = nums; - // 堆化除叶节点以外的其他所有节点 - for (int i = parent(size() - 1); i >= 0; i--) { - siftDown(i); - } - } + [class]{MaxHeap}-[func]{MaxHeap} ``` === "Java" ```java title="my_heap.java" - /* 构造方法,根据输入列表建堆 */ + /* Constructor, build heap based on input list */ MaxHeap(List nums) { - // 将列表元素原封不动添加进堆 + // Add all list elements into the heap maxHeap = new ArrayList<>(nums); - // 堆化除叶节点以外的其他所有节点 + // Heapify all nodes except leaves for (int i = parent(size() - 1); i >= 0; i--) { siftDown(i); } @@ -70,242 +62,55 @@ It's worth mentioning that **since leaf nodes have no children, they naturally f === "C#" ```csharp title="my_heap.cs" - /* 构造函数,根据输入列表建堆 */ - MaxHeap(IEnumerable nums) { - // 将列表元素原封不动添加进堆 - maxHeap = new List(nums); - // 堆化除叶节点以外的其他所有节点 - var size = Parent(this.Size() - 1); - for (int i = size; i >= 0; i--) { - SiftDown(i); - } - } + [class]{MaxHeap}-[func]{MaxHeap} ``` === "Go" ```go title="my_heap.go" - /* 构造函数,根据切片建堆 */ - func newMaxHeap(nums []any) *maxHeap { - // 将列表元素原封不动添加进堆 - h := &maxHeap{data: nums} - for i := h.parent(len(h.data) - 1); i >= 0; i-- { - // 堆化除叶节点以外的其他所有节点 - h.siftDown(i) - } - return h - } + [class]{maxHeap}-[func]{newMaxHeap} ``` === "Swift" ```swift title="my_heap.swift" - /* 构造方法,根据输入列表建堆 */ - init(nums: [Int]) { - // 将列表元素原封不动添加进堆 - maxHeap = nums - // 堆化除叶节点以外的其他所有节点 - for i in (0 ... parent(i: size() - 1)).reversed() { - siftDown(i: i) - } - } + [class]{MaxHeap}-[func]{init} ``` === "JS" ```javascript title="my_heap.js" - /* 构造方法,建立空堆或根据输入列表建堆 */ - constructor(nums) { - // 将列表元素原封不动添加进堆 - this.#maxHeap = nums === undefined ? [] : [...nums]; - // 堆化除叶节点以外的其他所有节点 - for (let i = this.#parent(this.size() - 1); i >= 0; i--) { - this.#siftDown(i); - } - } + [class]{MaxHeap}-[func]{constructor} ``` === "TS" ```typescript title="my_heap.ts" - /* 构造方法,建立空堆或根据输入列表建堆 */ - constructor(nums?: number[]) { - // 将列表元素原封不动添加进堆 - this.maxHeap = nums === undefined ? [] : [...nums]; - // 堆化除叶节点以外的其他所有节点 - for (let i = this.parent(this.size() - 1); i >= 0; i--) { - this.siftDown(i); - } - } + [class]{MaxHeap}-[func]{constructor} ``` === "Dart" ```dart title="my_heap.dart" - /* 构造方法,根据输入列表建堆 */ - MaxHeap(List nums) { - // 将列表元素原封不动添加进堆 - _maxHeap = nums; - // 堆化除叶节点以外的其他所有节点 - for (int i = _parent(size() - 1); i >= 0; i--) { - siftDown(i); - } - } + [class]{MaxHeap}-[func]{MaxHeap} ``` === "Rust" ```rust title="my_heap.rs" - /* 构造方法,根据输入列表建堆 */ - fn new(nums: Vec) -> Self { - // 将列表元素原封不动添加进堆 - let mut heap = MaxHeap { max_heap: nums }; - // 堆化除叶节点以外的其他所有节点 - for i in (0..=Self::parent(heap.size() - 1)).rev() { - heap.sift_down(i); - } - heap - } + [class]{MaxHeap}-[func]{new} ``` === "C" ```c title="my_heap.c" - /* 构造函数,根据切片建堆 */ - MaxHeap *newMaxHeap(int nums[], int size) { - // 所有元素入堆 - MaxHeap *maxHeap = (MaxHeap *)malloc(sizeof(MaxHeap)); - maxHeap->size = size; - memcpy(maxHeap->data, nums, size * sizeof(int)); - for (int i = parent(maxHeap, size - 1); i >= 0; i--) { - // 堆化除叶节点以外的其他所有节点 - siftDown(maxHeap, i); - } - return maxHeap; - } + [class]{MaxHeap}-[func]{newMaxHeap} ``` === "Kotlin" ```kotlin title="my_heap.kt" - /* 大顶堆 */ - class MaxHeap(nums: MutableList?) { - // 使用列表而非数组,这样无须考虑扩容问题 - private val maxHeap = mutableListOf() - - /* 构造方法,根据输入列表建堆 */ - init { - // 将列表元素原封不动添加进堆 - maxHeap.addAll(nums!!) - // 堆化除叶节点以外的其他所有节点 - 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) { - val temp = maxHeap[i] - maxHeap[i] = maxHeap[j] - maxHeap[j] = temp - } - - /* 获取堆大小 */ - fun size(): Int { - return maxHeap.size - } - - /* 判断堆是否为空 */ - fun isEmpty(): Boolean { - /* 判断堆是否为空 */ - return size() == 0 - } - - /* 访问堆顶元素 */ - fun peek(): Int { - return maxHeap[0] - } - - /* 元素入堆 */ - fun push(_val: Int) { - // 添加节点 - maxHeap.add(_val) - // 从底至顶堆化 - 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 _val = maxHeap.removeAt(size() - 1) - // 从顶至底堆化 - siftDown(0) - // 返回堆顶元素 - return _val - } - - /* 从节点 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) - } - } + [class]{MaxHeap}-[func]{} ``` === "Ruby" @@ -317,25 +122,9 @@ It's worth mentioning that **since leaf nodes have no children, they naturally f === "Zig" ```zig title="my_heap.zig" - // 构造方法,根据输入列表建堆 - fn init(self: *Self, allocator: std.mem.Allocator, nums: []const T) !void { - if (self.max_heap != null) return; - self.max_heap = std.ArrayList(T).init(allocator); - // 将列表元素原封不动添加进堆 - try self.max_heap.?.appendSlice(nums); - // 堆化除叶节点以外的其他所有节点 - var i: usize = parent(self.size() - 1) + 1; - while (i > 0) : (i -= 1) { - try self.siftDown(i - 1); - } - } + [class]{MaxHeap}-[func]{init} ``` -??? pythontutor "Code Visualization" - -
- - ## 8.2.3   Complexity analysis Next, let's attempt to calculate the time complexity of this second method of heap construction. diff --git a/en/docs/chapter_heap/heap.md b/en/docs/chapter_heap/heap.md index cd388148c..62d1da0d9 100644 --- a/en/docs/chapter_heap/heap.md +++ b/en/docs/chapter_heap/heap.md @@ -450,265 +450,155 @@ We can encapsulate the index mapping formula into functions for convenient later ```python title="my_heap.py" def left(self, i: int) -> int: - """获取左子节点的索引""" + """Get index of left child node""" return 2 * i + 1 def right(self, i: int) -> int: - """获取右子节点的索引""" + """Get index of right child node""" return 2 * i + 2 def parent(self, i: int) -> int: - """获取父节点的索引""" - return (i - 1) // 2 # 向下整除 + """Get index of parent node""" + return (i - 1) // 2 # Integer division down ``` === "C++" ```cpp title="my_heap.cpp" - /* 获取左子节点的索引 */ - int left(int i) { - return 2 * i + 1; - } + [class]{MaxHeap}-[func]{left} - /* 获取右子节点的索引 */ - int right(int i) { - return 2 * i + 2; - } + [class]{MaxHeap}-[func]{right} - /* 获取父节点的索引 */ - int parent(int i) { - return (i - 1) / 2; // 向下整除 - } + [class]{MaxHeap}-[func]{parent} ``` === "Java" ```java title="my_heap.java" - /* 获取左子节点的索引 */ + /* Get index of left child node */ int left(int i) { return 2 * i + 1; } - /* 获取右子节点的索引 */ + /* Get index of right child node */ int right(int i) { return 2 * i + 2; } - /* 获取父节点的索引 */ + /* Get index of parent node */ int parent(int i) { - return (i - 1) / 2; // 向下整除 + return (i - 1) / 2; // Integer division down } ``` === "C#" ```csharp title="my_heap.cs" - /* 获取左子节点的索引 */ - int Left(int i) { - return 2 * i + 1; - } + [class]{MaxHeap}-[func]{Left} - /* 获取右子节点的索引 */ - int Right(int i) { - return 2 * i + 2; - } + [class]{MaxHeap}-[func]{Right} - /* 获取父节点的索引 */ - int Parent(int i) { - return (i - 1) / 2; // 向下整除 - } + [class]{MaxHeap}-[func]{Parent} ``` === "Go" ```go title="my_heap.go" - /* 获取左子节点的索引 */ - func (h *maxHeap) left(i int) int { - return 2*i + 1 - } + [class]{maxHeap}-[func]{left} - /* 获取右子节点的索引 */ - func (h *maxHeap) right(i int) int { - return 2*i + 2 - } + [class]{maxHeap}-[func]{right} - /* 获取父节点的索引 */ - func (h *maxHeap) parent(i int) int { - // 向下整除 - return (i - 1) / 2 - } + [class]{maxHeap}-[func]{parent} ``` === "Swift" ```swift title="my_heap.swift" - /* 获取左子节点的索引 */ - func left(i: Int) -> Int { - 2 * i + 1 - } + [class]{MaxHeap}-[func]{left} - /* 获取右子节点的索引 */ - func right(i: Int) -> Int { - 2 * i + 2 - } + [class]{MaxHeap}-[func]{right} - /* 获取父节点的索引 */ - func parent(i: Int) -> Int { - (i - 1) / 2 // 向下整除 - } + [class]{MaxHeap}-[func]{parent} ``` === "JS" ```javascript title="my_heap.js" - /* 获取左子节点的索引 */ - #left(i) { - return 2 * i + 1; - } + [class]{MaxHeap}-[func]{left} - /* 获取右子节点的索引 */ - #right(i) { - return 2 * i + 2; - } + [class]{MaxHeap}-[func]{right} - /* 获取父节点的索引 */ - #parent(i) { - return Math.floor((i - 1) / 2); // 向下整除 - } + [class]{MaxHeap}-[func]{parent} ``` === "TS" ```typescript title="my_heap.ts" - /* 获取左子节点的索引 */ - left(i: number): number { - return 2 * i + 1; - } + [class]{MaxHeap}-[func]{left} - /* 获取右子节点的索引 */ - right(i: number): number { - return 2 * i + 2; - } + [class]{MaxHeap}-[func]{right} - /* 获取父节点的索引 */ - parent(i: number): number { - return Math.floor((i - 1) / 2); // 向下整除 - } + [class]{MaxHeap}-[func]{parent} ``` === "Dart" ```dart title="my_heap.dart" - /* 获取左子节点的索引 */ - int _left(int i) { - return 2 * i + 1; - } + [class]{MaxHeap}-[func]{_left} - /* 获取右子节点的索引 */ - int _right(int i) { - return 2 * i + 2; - } + [class]{MaxHeap}-[func]{_right} - /* 获取父节点的索引 */ - int _parent(int i) { - return (i - 1) ~/ 2; // 向下整除 - } + [class]{MaxHeap}-[func]{_parent} ``` === "Rust" ```rust title="my_heap.rs" - /* 获取左子节点的索引 */ - fn left(i: usize) -> usize { - 2 * i + 1 - } + [class]{MaxHeap}-[func]{left} - /* 获取右子节点的索引 */ - fn right(i: usize) -> usize { - 2 * i + 2 - } + [class]{MaxHeap}-[func]{right} - /* 获取父节点的索引 */ - fn parent(i: usize) -> usize { - (i - 1) / 2 // 向下整除 - } + [class]{MaxHeap}-[func]{parent} ``` === "C" ```c title="my_heap.c" - /* 获取左子节点的索引 */ - int left(MaxHeap *maxHeap, int i) { - return 2 * i + 1; - } + [class]{MaxHeap}-[func]{left} - /* 获取右子节点的索引 */ - int right(MaxHeap *maxHeap, int i) { - return 2 * i + 2; - } + [class]{MaxHeap}-[func]{right} - /* 获取父节点的索引 */ - int parent(MaxHeap *maxHeap, int i) { - return (i - 1) / 2; // 向下取整 - } + [class]{MaxHeap}-[func]{parent} ``` === "Kotlin" ```kotlin title="my_heap.kt" - /* 获取左子节点的索引 */ - fun left(i: Int): Int { - return 2 * i + 1 - } + [class]{MaxHeap}-[func]{left} - /* 获取右子节点的索引 */ - fun right(i: Int): Int { - return 2 * i + 2 - } + [class]{MaxHeap}-[func]{right} - /* 获取父节点的索引 */ - fun parent(i: Int): Int { - return (i - 1) / 2 // 向下整除 - } + [class]{MaxHeap}-[func]{parent} ``` === "Ruby" ```ruby title="my_heap.rb" - ### 获取左子节点的索引 ### - def left(i) - 2 * i + 1 - end + [class]{MaxHeap}-[func]{left} - ### 获取右子节点的索引 ### - def right(i) - 2 * i + 2 - end + [class]{MaxHeap}-[func]{right} - ### 获取父节点的索引 ### - def parent(i) - (i - 1) / 2 # 向下整除 - end + [class]{MaxHeap}-[func]{parent} ``` === "Zig" ```zig title="my_heap.zig" - // 获取左子节点的索引 - fn left(i: usize) usize { - return 2 * i + 1; - } + [class]{MaxHeap}-[func]{left} - // 获取右子节点的索引 - fn right(i: usize) usize { - return 2 * i + 2; - } + [class]{MaxHeap}-[func]{right} - // 获取父节点的索引 - fn parent(i: usize) usize { - // return (i - 1) / 2; // 向下整除 - return @divFloor(i - 1, 2); - } + [class]{MaxHeap}-[func]{parent} ``` ### 2.   Accessing the top element of the heap @@ -719,23 +609,20 @@ The top element of the heap is the root node of the binary tree, which is also t ```python title="my_heap.py" def peek(self) -> int: - """访问堆顶元素""" + """Access heap top element""" return self.max_heap[0] ``` === "C++" ```cpp title="my_heap.cpp" - /* 访问堆顶元素 */ - int peek() { - return maxHeap[0]; - } + [class]{MaxHeap}-[func]{peek} ``` === "Java" ```java title="my_heap.java" - /* 访问堆顶元素 */ + /* Access heap top element */ int peek() { return maxHeap.get(0); } @@ -744,107 +631,69 @@ The top element of the heap is the root node of the binary tree, which is also t === "C#" ```csharp title="my_heap.cs" - /* 访问堆顶元素 */ - int Peek() { - return maxHeap[0]; - } + [class]{MaxHeap}-[func]{Peek} ``` === "Go" ```go title="my_heap.go" - /* 访问堆顶元素 */ - func (h *maxHeap) peek() any { - return h.data[0] - } + [class]{maxHeap}-[func]{peek} ``` === "Swift" ```swift title="my_heap.swift" - /* 访问堆顶元素 */ - func peek() -> Int { - maxHeap[0] - } + [class]{MaxHeap}-[func]{peek} ``` === "JS" ```javascript title="my_heap.js" - /* 访问堆顶元素 */ - peek() { - return this.#maxHeap[0]; - } + [class]{MaxHeap}-[func]{peek} ``` === "TS" ```typescript title="my_heap.ts" - /* 访问堆顶元素 */ - peek(): number { - return this.maxHeap[0]; - } + [class]{MaxHeap}-[func]{peek} ``` === "Dart" ```dart title="my_heap.dart" - /* 访问堆顶元素 */ - int peek() { - return _maxHeap[0]; - } + [class]{MaxHeap}-[func]{peek} ``` === "Rust" ```rust title="my_heap.rs" - /* 访问堆顶元素 */ - fn peek(&self) -> Option { - self.max_heap.first().copied() - } + [class]{MaxHeap}-[func]{peek} ``` === "C" ```c title="my_heap.c" - /* 访问堆顶元素 */ - int peek(MaxHeap *maxHeap) { - return maxHeap->data[0]; - } + [class]{MaxHeap}-[func]{peek} ``` === "Kotlin" ```kotlin title="my_heap.kt" - /* 访问堆顶元素 */ - fun peek(): Int { - return maxHeap[0] - } + [class]{MaxHeap}-[func]{peek} ``` === "Ruby" ```ruby title="my_heap.rb" - ### 访问堆顶元素 ### - def peek - @max_heap[0] - end + [class]{MaxHeap}-[func]{peek} ``` === "Zig" ```zig title="my_heap.zig" - // 访问堆顶元素 - fn peek(self: *Self) T { - return self.max_heap.?.items[0]; - } + [class]{MaxHeap}-[func]{peek} ``` -??? pythontutor "Code Visualization" - -
- - ### 3.   Inserting an element into the heap Given an element `val`, we first add it to the bottom of the heap. After addition, since `val` may be larger than other elements in the heap, the heap's integrity might be compromised, **thus it's necessary to repair the path from the inserted node to the root node**. This operation is called heapifying. @@ -886,75 +735,56 @@ Given a total of $n$ nodes, the height of the tree is $O(\log n)$. Hence, the lo ```python title="my_heap.py" def push(self, val: int): - """元素入堆""" - # 添加节点 + """Push the element into heap""" + # Add node self.max_heap.append(val) - # 从底至顶堆化 + # Heapify from bottom to top self.sift_up(self.size() - 1) def sift_up(self, i: int): - """从节点 i 开始,从底至顶堆化""" + """Start heapifying node i, from bottom to top""" while True: - # 获取节点 i 的父节点 + # Get parent node of node i p = self.parent(i) - # 当“越过根节点”或“节点无须修复”时,结束堆化 + # When "crossing the root node" or "node does not need repair", end heapification if p < 0 or self.max_heap[i] <= self.max_heap[p]: break - # 交换两节点 + # Swap two nodes self.swap(i, p) - # 循环向上堆化 + # Loop upwards heapification i = p ``` === "C++" ```cpp title="my_heap.cpp" - /* 元素入堆 */ - void push(int val) { - // 添加节点 - maxHeap.push_back(val); - // 从底至顶堆化 - siftUp(size() - 1); - } + [class]{MaxHeap}-[func]{push} - /* 从节点 i 开始,从底至顶堆化 */ - void siftUp(int i) { - while (true) { - // 获取节点 i 的父节点 - int p = parent(i); - // 当“越过根节点”或“节点无须修复”时,结束堆化 - if (p < 0 || maxHeap[i] <= maxHeap[p]) - break; - // 交换两节点 - swap(maxHeap[i], maxHeap[p]); - // 循环向上堆化 - i = p; - } - } + [class]{MaxHeap}-[func]{siftUp} ``` === "Java" ```java title="my_heap.java" - /* 元素入堆 */ + /* Push the element into heap */ void push(int val) { - // 添加节点 + // Add node maxHeap.add(val); - // 从底至顶堆化 + // Heapify from bottom to top siftUp(size() - 1); } - /* 从节点 i 开始,从底至顶堆化 */ + /* Start heapifying node i, from bottom to top */ void siftUp(int i) { while (true) { - // 获取节点 i 的父节点 + // Get parent node of node i int p = parent(i); - // 当“越过根节点”或“节点无须修复”时,结束堆化 + // When "crossing the root node" or "node does not need repair", end heapification if (p < 0 || maxHeap.get(i) <= maxHeap.get(p)) break; - // 交换两节点 + // Swap two nodes swap(i, p); - // 循环向上堆化 + // Loop upwards heapification i = p; } } @@ -963,320 +793,91 @@ Given a total of $n$ nodes, the height of the tree is $O(\log n)$. Hence, the lo === "C#" ```csharp title="my_heap.cs" - /* 元素入堆 */ - void Push(int val) { - // 添加节点 - maxHeap.Add(val); - // 从底至顶堆化 - SiftUp(Size() - 1); - } + [class]{MaxHeap}-[func]{Push} - /* 从节点 i 开始,从底至顶堆化 */ - void SiftUp(int i) { - while (true) { - // 获取节点 i 的父节点 - int p = Parent(i); - // 若“越过根节点”或“节点无须修复”,则结束堆化 - if (p < 0 || maxHeap[i] <= maxHeap[p]) - break; - // 交换两节点 - Swap(i, p); - // 循环向上堆化 - i = p; - } - } + [class]{MaxHeap}-[func]{SiftUp} ``` === "Go" ```go title="my_heap.go" - /* 元素入堆 */ - func (h *maxHeap) push(val any) { - // 添加节点 - h.data = append(h.data, val) - // 从底至顶堆化 - h.siftUp(len(h.data) - 1) - } + [class]{maxHeap}-[func]{push} - /* 从节点 i 开始,从底至顶堆化 */ - func (h *maxHeap) siftUp(i int) { - for true { - // 获取节点 i 的父节点 - p := h.parent(i) - // 当“越过根节点”或“节点无须修复”时,结束堆化 - if p < 0 || h.data[i].(int) <= h.data[p].(int) { - break - } - // 交换两节点 - h.swap(i, p) - // 循环向上堆化 - i = p - } - } + [class]{maxHeap}-[func]{siftUp} ``` === "Swift" ```swift title="my_heap.swift" - /* 元素入堆 */ - func push(val: Int) { - // 添加节点 - maxHeap.append(val) - // 从底至顶堆化 - siftUp(i: size() - 1) - } + [class]{MaxHeap}-[func]{push} - /* 从节点 i 开始,从底至顶堆化 */ - func siftUp(i: Int) { - var i = i - while true { - // 获取节点 i 的父节点 - let p = parent(i: i) - // 当“越过根节点”或“节点无须修复”时,结束堆化 - if p < 0 || maxHeap[i] <= maxHeap[p] { - break - } - // 交换两节点 - swap(i: i, j: p) - // 循环向上堆化 - i = p - } - } + [class]{MaxHeap}-[func]{siftUp} ``` === "JS" ```javascript title="my_heap.js" - /* 元素入堆 */ - push(val) { - // 添加节点 - this.#maxHeap.push(val); - // 从底至顶堆化 - this.#siftUp(this.size() - 1); - } + [class]{MaxHeap}-[func]{push} - /* 从节点 i 开始,从底至顶堆化 */ - #siftUp(i) { - while (true) { - // 获取节点 i 的父节点 - const p = this.#parent(i); - // 当“越过根节点”或“节点无须修复”时,结束堆化 - if (p < 0 || this.#maxHeap[i] <= this.#maxHeap[p]) break; - // 交换两节点 - this.#swap(i, p); - // 循环向上堆化 - i = p; - } - } + [class]{MaxHeap}-[func]{siftUp} ``` === "TS" ```typescript title="my_heap.ts" - /* 元素入堆 */ - push(val: number): void { - // 添加节点 - this.maxHeap.push(val); - // 从底至顶堆化 - this.siftUp(this.size() - 1); - } + [class]{MaxHeap}-[func]{push} - /* 从节点 i 开始,从底至顶堆化 */ - siftUp(i: number): void { - while (true) { - // 获取节点 i 的父节点 - const p = this.parent(i); - // 当“越过根节点”或“节点无须修复”时,结束堆化 - if (p < 0 || this.maxHeap[i] <= this.maxHeap[p]) break; - // 交换两节点 - this.swap(i, p); - // 循环向上堆化 - i = p; - } - } + [class]{MaxHeap}-[func]{siftUp} ``` === "Dart" ```dart title="my_heap.dart" - /* 元素入堆 */ - void push(int val) { - // 添加节点 - _maxHeap.add(val); - // 从底至顶堆化 - siftUp(size() - 1); - } + [class]{MaxHeap}-[func]{push} - /* 从节点 i 开始,从底至顶堆化 */ - void siftUp(int i) { - while (true) { - // 获取节点 i 的父节点 - int p = _parent(i); - // 当“越过根节点”或“节点无须修复”时,结束堆化 - if (p < 0 || _maxHeap[i] <= _maxHeap[p]) { - break; - } - // 交换两节点 - _swap(i, p); - // 循环向上堆化 - i = p; - } - } + [class]{MaxHeap}-[func]{siftUp} ``` === "Rust" ```rust title="my_heap.rs" - /* 元素入堆 */ - fn push(&mut self, val: i32) { - // 添加节点 - self.max_heap.push(val); - // 从底至顶堆化 - self.sift_up(self.size() - 1); - } + [class]{MaxHeap}-[func]{push} - /* 从节点 i 开始,从底至顶堆化 */ - fn sift_up(&mut self, mut i: usize) { - loop { - // 节点 i 已经是堆顶节点了,结束堆化 - if i == 0 { - break; - } - // 获取节点 i 的父节点 - let p = Self::parent(i); - // 当“节点无须修复”时,结束堆化 - if self.max_heap[i] <= self.max_heap[p] { - break; - } - // 交换两节点 - self.swap(i, p); - // 循环向上堆化 - i = p; - } - } + [class]{MaxHeap}-[func]{sift_up} ``` === "C" ```c title="my_heap.c" - /* 元素入堆 */ - void push(MaxHeap *maxHeap, int val) { - // 默认情况下,不应该添加这么多节点 - if (maxHeap->size == MAX_SIZE) { - printf("heap is full!"); - return; - } - // 添加节点 - maxHeap->data[maxHeap->size] = val; - maxHeap->size++; + [class]{MaxHeap}-[func]{push} - // 从底至顶堆化 - siftUp(maxHeap, maxHeap->size - 1); - } - - /* 从节点 i 开始,从底至顶堆化 */ - void siftUp(MaxHeap *maxHeap, int i) { - while (true) { - // 获取节点 i 的父节点 - int p = parent(maxHeap, i); - // 当“越过根节点”或“节点无须修复”时,结束堆化 - if (p < 0 || maxHeap->data[i] <= maxHeap->data[p]) { - break; - } - // 交换两节点 - swap(maxHeap, i, p); - // 循环向上堆化 - i = p; - } - } + [class]{MaxHeap}-[func]{siftUp} ``` === "Kotlin" ```kotlin title="my_heap.kt" - /* 元素入堆 */ - fun push(_val: Int) { - // 添加节点 - maxHeap.add(_val) - // 从底至顶堆化 - siftUp(size() - 1) - } + [class]{MaxHeap}-[func]{push} - /* 从节点 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 - } - } + [class]{MaxHeap}-[func]{siftUp} ``` === "Ruby" ```ruby title="my_heap.rb" - ### 元素入堆 ### - def push(val) - # 添加节点 - @max_heap << val - # 从底至顶堆化 - sift_up(size - 1) - end + [class]{MaxHeap}-[func]{push} - ### 从节点 i 开始,从底至顶堆化 ### - def sift_up(i) - loop do - # 获取节点 i 的父节点 - p = parent(i) - # 当“越过根节点”或“节点无须修复”时,结束堆化 - break if p < 0 || @max_heap[i] <= @max_heap[p] - # 交换两节点 - swap(i, p) - # 循环向上堆化 - i = p - end - end + [class]{MaxHeap}-[func]{sift_up} ``` === "Zig" ```zig title="my_heap.zig" - // 元素入堆 - fn push(self: *Self, val: T) !void { - // 添加节点 - try self.max_heap.?.append(val); - // 从底至顶堆化 - try self.siftUp(self.size() - 1); - } + [class]{MaxHeap}-[func]{push} - // 从节点 i 开始,从底至顶堆化 - fn siftUp(self: *Self, i_: usize) !void { - var i = i_; - while (true) { - // 获取节点 i 的父节点 - var p = parent(i); - // 当“越过根节点”或“节点无须修复”时,结束堆化 - if (p < 0 or self.max_heap.?.items[i] <= self.max_heap.?.items[p]) break; - // 交换两节点 - try self.swap(i, p); - // 循环向上堆化 - i = p; - } - } + [class]{MaxHeap}-[func]{siftUp} ``` -??? pythontutor "Code Visualization" - -
- - ### 4.   Removing the top element from the heap The top element of the heap is the root node of the binary tree, that is, the first element of the list. If we directly remove the first element from the list, all node indexes in the binary tree would change, making it difficult to use heapify for repairs subsequently. To minimize changes in element indexes, we use the following steps. @@ -1325,106 +926,78 @@ Similar to the element insertion operation, the time complexity of the top eleme ```python title="my_heap.py" def pop(self) -> int: - """元素出堆""" - # 判空处理 + """Element exits heap""" + # Empty handling if self.is_empty(): - raise IndexError("堆为空") - # 交换根节点与最右叶节点(交换首元素与尾元素) + raise IndexError("Heap is empty") + # Swap the root node with the rightmost leaf node (swap the first element with the last element) self.swap(0, self.size() - 1) - # 删除节点 + # Remove node val = self.max_heap.pop() - # 从顶至底堆化 + # Heapify from top to bottom self.sift_down(0) - # 返回堆顶元素 + # Return heap top element return val def sift_down(self, i: int): - """从节点 i 开始,从顶至底堆化""" + """Start heapifying node i, from top to bottom""" while True: - # 判断节点 i, l, r 中值最大的节点,记为 ma + # Determine the largest node among i, l, r, noted as ma l, r, ma = self.left(i), self.right(i), i if l < self.size() and self.max_heap[l] > self.max_heap[ma]: ma = l if r < self.size() and self.max_heap[r] > self.max_heap[ma]: ma = r - # 若节点 i 最大或索引 l, r 越界,则无须继续堆化,跳出 + # If node i is the largest or indices l, r are out of bounds, no further heapification needed, break if ma == i: break - # 交换两节点 + # Swap two nodes self.swap(i, ma) - # 循环向下堆化 + # Loop downwards heapification i = ma ``` === "C++" ```cpp title="my_heap.cpp" - /* 元素出堆 */ - void pop() { - // 判空处理 - if (isEmpty()) { - throw out_of_range("堆为空"); - } - // 交换根节点与最右叶节点(交换首元素与尾元素) - swap(maxHeap[0], maxHeap[size() - 1]); - // 删除节点 - maxHeap.pop_back(); - // 从顶至底堆化 - siftDown(0); - } + [class]{MaxHeap}-[func]{pop} - /* 从节点 i 开始,从顶至底堆化 */ - void siftDown(int i) { - while (true) { - // 判断节点 i, l, r 中值最大的节点,记为 ma - int l = left(i), r = right(i), 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(maxHeap[i], maxHeap[ma]); - // 循环向下堆化 - i = ma; - } - } + [class]{MaxHeap}-[func]{siftDown} ``` === "Java" ```java title="my_heap.java" - /* 元素出堆 */ + /* Element exits heap */ int pop() { - // 判空处理 + // Empty handling if (isEmpty()) throw new IndexOutOfBoundsException(); - // 交换根节点与最右叶节点(交换首元素与尾元素) + // Swap the root node with the rightmost leaf node (swap the first element with the last element) swap(0, size() - 1); - // 删除节点 + // Remove node int val = maxHeap.remove(size() - 1); - // 从顶至底堆化 + // Heapify from top to bottom siftDown(0); - // 返回堆顶元素 + // Return heap top element return val; } - /* 从节点 i 开始,从顶至底堆化 */ + /* Start heapifying node i, from top to bottom */ void siftDown(int i) { while (true) { - // 判断节点 i, l, r 中值最大的节点,记为 ma + // Determine the largest node among i, l, r, noted as ma int l = left(i), r = right(i), ma = i; if (l < size() && maxHeap.get(l) > maxHeap.get(ma)) ma = l; if (r < size() && maxHeap.get(r) > maxHeap.get(ma)) ma = r; - // 若节点 i 最大或索引 l, r 越界,则无须继续堆化,跳出 + // If node i is the largest or indices l, r are out of bounds, no further heapification needed, break if (ma == i) break; - // 交换两节点 + // Swap two nodes swap(i, ma); - // 循环向下堆化 + // Loop downwards heapification i = ma; } } @@ -1433,444 +1006,91 @@ Similar to the element insertion operation, the time complexity of the top eleme === "C#" ```csharp title="my_heap.cs" - /* 元素出堆 */ - int Pop() { - // 判空处理 - if (IsEmpty()) - throw new IndexOutOfRangeException(); - // 交换根节点与最右叶节点(交换首元素与尾元素) - Swap(0, Size() - 1); - // 删除节点 - int val = maxHeap.Last(); - maxHeap.RemoveAt(Size() - 1); - // 从顶至底堆化 - SiftDown(0); - // 返回堆顶元素 - return val; - } + [class]{MaxHeap}-[func]{Pop} - /* 从节点 i 开始,从顶至底堆化 */ - void SiftDown(int i) { - while (true) { - // 判断节点 i, l, r 中值最大的节点,记为 ma - int l = Left(i), r = Right(i), ma = i; - if (l < Size() && maxHeap[l] > maxHeap[ma]) - ma = l; - if (r < Size() && maxHeap[r] > maxHeap[ma]) - ma = r; - // 若“节点 i 最大”或“越过叶节点”,则结束堆化 - if (ma == i) break; - // 交换两节点 - Swap(i, ma); - // 循环向下堆化 - i = ma; - } - } + [class]{MaxHeap}-[func]{SiftDown} ``` === "Go" ```go title="my_heap.go" - /* 元素出堆 */ - func (h *maxHeap) pop() any { - // 判空处理 - if h.isEmpty() { - fmt.Println("error") - return nil - } - // 交换根节点与最右叶节点(交换首元素与尾元素) - h.swap(0, h.size()-1) - // 删除节点 - val := h.data[len(h.data)-1] - h.data = h.data[:len(h.data)-1] - // 从顶至底堆化 - h.siftDown(0) + [class]{maxHeap}-[func]{pop} - // 返回堆顶元素 - return val - } - - /* 从节点 i 开始,从顶至底堆化 */ - func (h *maxHeap) siftDown(i int) { - for true { - // 判断节点 i, l, r 中值最大的节点,记为 max - l, r, max := h.left(i), h.right(i), i - if l < h.size() && h.data[l].(int) > h.data[max].(int) { - max = l - } - if r < h.size() && h.data[r].(int) > h.data[max].(int) { - max = r - } - // 若节点 i 最大或索引 l, r 越界,则无须继续堆化,跳出 - if max == i { - break - } - // 交换两节点 - h.swap(i, max) - // 循环向下堆化 - i = max - } - } + [class]{maxHeap}-[func]{siftDown} ``` === "Swift" ```swift title="my_heap.swift" - /* 元素出堆 */ - func pop() -> Int { - // 判空处理 - if isEmpty() { - fatalError("堆为空") - } - // 交换根节点与最右叶节点(交换首元素与尾元素) - swap(i: 0, j: size() - 1) - // 删除节点 - let val = maxHeap.remove(at: size() - 1) - // 从顶至底堆化 - siftDown(i: 0) - // 返回堆顶元素 - return val - } + [class]{MaxHeap}-[func]{pop} - /* 从节点 i 开始,从顶至底堆化 */ - func siftDown(i: Int) { - var i = i - while true { - // 判断节点 i, l, r 中值最大的节点,记为 ma - let l = left(i: i) - let r = right(i: 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: i, j: ma) - // 循环向下堆化 - i = ma - } - } + [class]{MaxHeap}-[func]{siftDown} ``` === "JS" ```javascript title="my_heap.js" - /* 元素出堆 */ - pop() { - // 判空处理 - if (this.isEmpty()) throw new Error('堆为空'); - // 交换根节点与最右叶节点(交换首元素与尾元素) - this.#swap(0, this.size() - 1); - // 删除节点 - const val = this.#maxHeap.pop(); - // 从顶至底堆化 - this.#siftDown(0); - // 返回堆顶元素 - return val; - } + [class]{MaxHeap}-[func]{pop} - /* 从节点 i 开始,从顶至底堆化 */ - #siftDown(i) { - while (true) { - // 判断节点 i, l, r 中值最大的节点,记为 ma - const l = this.#left(i), - r = this.#right(i); - let ma = i; - if (l < this.size() && this.#maxHeap[l] > this.#maxHeap[ma]) ma = l; - if (r < this.size() && this.#maxHeap[r] > this.#maxHeap[ma]) ma = r; - // 若节点 i 最大或索引 l, r 越界,则无须继续堆化,跳出 - if (ma === i) break; - // 交换两节点 - this.#swap(i, ma); - // 循环向下堆化 - i = ma; - } - } + [class]{MaxHeap}-[func]{siftDown} ``` === "TS" ```typescript title="my_heap.ts" - /* 元素出堆 */ - pop(): number { - // 判空处理 - if (this.isEmpty()) throw new RangeError('Heap is empty.'); - // 交换根节点与最右叶节点(交换首元素与尾元素) - this.swap(0, this.size() - 1); - // 删除节点 - const val = this.maxHeap.pop(); - // 从顶至底堆化 - this.siftDown(0); - // 返回堆顶元素 - return val; - } + [class]{MaxHeap}-[func]{pop} - /* 从节点 i 开始,从顶至底堆化 */ - siftDown(i: number): void { - while (true) { - // 判断节点 i, l, r 中值最大的节点,记为 ma - const l = this.left(i), - r = this.right(i); - let ma = i; - if (l < this.size() && this.maxHeap[l] > this.maxHeap[ma]) ma = l; - if (r < this.size() && this.maxHeap[r] > this.maxHeap[ma]) ma = r; - // 若节点 i 最大或索引 l, r 越界,则无须继续堆化,跳出 - if (ma === i) break; - // 交换两节点 - this.swap(i, ma); - // 循环向下堆化 - i = ma; - } - } + [class]{MaxHeap}-[func]{siftDown} ``` === "Dart" ```dart title="my_heap.dart" - /* 元素出堆 */ - int pop() { - // 判空处理 - if (isEmpty()) throw Exception('堆为空'); - // 交换根节点与最右叶节点(交换首元素与尾元素) - _swap(0, size() - 1); - // 删除节点 - int val = _maxHeap.removeLast(); - // 从顶至底堆化 - siftDown(0); - // 返回堆顶元素 - return val; - } + [class]{MaxHeap}-[func]{pop} - /* 从节点 i 开始,从顶至底堆化 */ - void siftDown(int i) { - while (true) { - // 判断节点 i, l, r 中值最大的节点,记为 ma - int l = _left(i); - int r = _right(i); - int 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; - } - } + [class]{MaxHeap}-[func]{siftDown} ``` === "Rust" ```rust title="my_heap.rs" - /* 元素出堆 */ - fn pop(&mut self) -> i32 { - // 判空处理 - if self.is_empty() { - panic!("index out of bounds"); - } - // 交换根节点与最右叶节点(交换首元素与尾元素) - self.swap(0, self.size() - 1); - // 删除节点 - let val = self.max_heap.pop().unwrap(); - // 从顶至底堆化 - self.sift_down(0); - // 返回堆顶元素 - val - } + [class]{MaxHeap}-[func]{pop} - /* 从节点 i 开始,从顶至底堆化 */ - fn sift_down(&mut self, mut i: usize) { - loop { - // 判断节点 i, l, r 中值最大的节点,记为 ma - let (l, r, mut ma) = (Self::left(i), Self::right(i), i); - if l < self.size() && self.max_heap[l] > self.max_heap[ma] { - ma = l; - } - if r < self.size() && self.max_heap[r] > self.max_heap[ma] { - ma = r; - } - // 若节点 i 最大或索引 l, r 越界,则无须继续堆化,跳出 - if ma == i { - break; - } - // 交换两节点 - self.swap(i, ma); - // 循环向下堆化 - i = ma; - } - } + [class]{MaxHeap}-[func]{sift_down} ``` === "C" ```c title="my_heap.c" - /* 元素出堆 */ - int pop(MaxHeap *maxHeap) { - // 判空处理 - if (isEmpty(maxHeap)) { - printf("heap is empty!"); - return INT_MAX; - } - // 交换根节点与最右叶节点(交换首元素与尾元素) - swap(maxHeap, 0, size(maxHeap) - 1); - // 删除节点 - int val = maxHeap->data[maxHeap->size - 1]; - maxHeap->size--; - // 从顶至底堆化 - siftDown(maxHeap, 0); + [class]{MaxHeap}-[func]{pop} - // 返回堆顶元素 - return val; - } - - /* 从节点 i 开始,从顶至底堆化 */ - void siftDown(MaxHeap *maxHeap, int i) { - while (true) { - // 判断节点 i, l, r 中值最大的节点,记为 max - int l = left(maxHeap, i); - int r = right(maxHeap, i); - int max = i; - if (l < size(maxHeap) && maxHeap->data[l] > maxHeap->data[max]) { - max = l; - } - if (r < size(maxHeap) && maxHeap->data[r] > maxHeap->data[max]) { - max = r; - } - // 若节点 i 最大或索引 l, r 越界,则无须继续堆化,跳出 - if (max == i) { - break; - } - // 交换两节点 - swap(maxHeap, i, max); - // 循环向下堆化 - i = max; - } - } + [class]{MaxHeap}-[func]{siftDown} ``` === "Kotlin" ```kotlin title="my_heap.kt" - /* 元素出堆 */ - fun pop(): Int { - // 判空处理 - if (isEmpty()) throw IndexOutOfBoundsException() - // 交换根节点与最右叶节点(交换首元素与尾元素) - swap(0, size() - 1) - // 删除节点 - val _val = maxHeap.removeAt(size() - 1) - // 从顶至底堆化 - siftDown(0) - // 返回堆顶元素 - return _val - } + [class]{MaxHeap}-[func]{pop} - /* 从节点 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 - } - } + [class]{MaxHeap}-[func]{siftDown} ``` === "Ruby" ```ruby title="my_heap.rb" - ### 元素出堆 ### - def pop - # 判空处理 - raise IndexError, "堆为空" if is_empty? - # 交换根节点与最右叶节点(交换首元素与尾元素) - swap(0, size - 1) - # 删除节点 - val = @max_heap.pop - # 从顶至底堆化 - sift_down(0) - # 返回堆顶元素 - val - end + [class]{MaxHeap}-[func]{pop} - ### 从节点 i 开始,从顶至底堆化 ### - def sift_down(i) - loop do - # 判断节点 i, l, r 中值最大的节点,记为 ma - l, r, ma = left(i), right(i), i - ma = l if l < size && @max_heap[l] > @max_heap[ma] - ma = r if r < size && @max_heap[r] > @max_heap[ma] - - # 若节点 i 最大或索引 l, r 越界,则无须继续堆化,跳出 - break if ma == i - - # 交换两节点 - swap(i, ma) - # 循环向下堆化 - i = ma - end - end + [class]{MaxHeap}-[func]{sift_down} ``` === "Zig" ```zig title="my_heap.zig" - // 元素出堆 - fn pop(self: *Self) !T { - // 判断处理 - if (self.isEmpty()) unreachable; - // 交换根节点与最右叶节点(交换首元素与尾元素) - try self.swap(0, self.size() - 1); - // 删除节点 - var val = self.max_heap.?.pop(); - // 从顶至底堆化 - try self.siftDown(0); - // 返回堆顶元素 - return val; - } + [class]{MaxHeap}-[func]{pop} - // 从节点 i 开始,从顶至底堆化 - fn siftDown(self: *Self, i_: usize) !void { - var i = i_; - while (true) { - // 判断节点 i, l, r 中值最大的节点,记为 ma - var l = left(i); - var r = right(i); - var ma = i; - if (l < self.size() and self.max_heap.?.items[l] > self.max_heap.?.items[ma]) ma = l; - if (r < self.size() and self.max_heap.?.items[r] > self.max_heap.?.items[ma]) ma = r; - // 若节点 i 最大或索引 l, r 越界,则无须继续堆化,跳出 - if (ma == i) break; - // 交换两节点 - try self.swap(i, ma); - // 循环向下堆化 - i = ma; - } - } + [class]{MaxHeap}-[func]{siftDown} ``` -??? pythontutor "Code Visualization" - -
- - ## 8.1.3   Common applications of heaps - **Priority Queue**: Heaps are often the preferred data structure for implementing priority queues, with both enqueue and dequeue operations having a time complexity of $O(\log n)$, and building a queue having a time complexity of $O(n)$, all of which are very efficient. diff --git a/en/docs/chapter_heap/top_k.md b/en/docs/chapter_heap/top_k.md index b88fd6bd5..2876ece37 100644 --- a/en/docs/chapter_heap/top_k.md +++ b/en/docs/chapter_heap/top_k.md @@ -78,15 +78,15 @@ Example code is as follows: ```python title="top_k.py" def top_k_heap(nums: list[int], k: int) -> list[int]: - """基于堆查找数组中最大的 k 个元素""" - # 初始化小顶堆 + """Using heap to find the largest k elements in an array""" + # Initialize min-heap heap = [] - # 将数组的前 k 个元素入堆 + # Enter the first k elements of the array into the heap for i in range(k): heapq.heappush(heap, nums[i]) - # 从第 k+1 个元素开始,保持堆的长度为 k + # From the k+1th element, keep the heap length as k for i in range(k, len(nums)): - # 若当前元素大于堆顶元素,则将堆顶元素出堆、当前元素入堆 + # If the current element is larger than the heap top element, remove the heap top element and enter the current element into the heap if nums[i] > heap[0]: heapq.heappop(heap) heapq.heappush(heap, nums[i]) @@ -96,40 +96,23 @@ Example code is as follows: === "C++" ```cpp title="top_k.cpp" - /* 基于堆查找数组中最大的 k 个元素 */ - priority_queue, greater> topKHeap(vector &nums, int k) { - // 初始化小顶堆 - priority_queue, greater> heap; - // 将数组的前 k 个元素入堆 - for (int i = 0; i < k; i++) { - heap.push(nums[i]); - } - // 从第 k+1 个元素开始,保持堆的长度为 k - for (int i = k; i < nums.size(); i++) { - // 若当前元素大于堆顶元素,则将堆顶元素出堆、当前元素入堆 - if (nums[i] > heap.top()) { - heap.pop(); - heap.push(nums[i]); - } - } - return heap; - } + [class]{}-[func]{topKHeap} ``` === "Java" ```java title="top_k.java" - /* 基于堆查找数组中最大的 k 个元素 */ + /* Using heap to find the largest k elements in an array */ Queue topKHeap(int[] nums, int k) { - // 初始化小顶堆 + // Initialize min-heap Queue heap = new PriorityQueue(); - // 将数组的前 k 个元素入堆 + // Enter the first k elements of the array into the heap for (int i = 0; i < k; i++) { heap.offer(nums[i]); } - // 从第 k+1 个元素开始,保持堆的长度为 k + // From the k+1th element, keep the heap length as k for (int i = k; i < nums.length; i++) { - // 若当前元素大于堆顶元素,则将堆顶元素出堆、当前元素入堆 + // If the current element is larger than the heap top element, remove the heap top element and enter the current element into the heap if (nums[i] > heap.peek()) { heap.poll(); heap.offer(nums[i]); @@ -142,323 +125,85 @@ Example code is as follows: === "C#" ```csharp title="top_k.cs" - /* 基于堆查找数组中最大的 k 个元素 */ - PriorityQueue TopKHeap(int[] nums, int k) { - // 初始化小顶堆 - PriorityQueue heap = new(); - // 将数组的前 k 个元素入堆 - for (int i = 0; i < k; i++) { - heap.Enqueue(nums[i], nums[i]); - } - // 从第 k+1 个元素开始,保持堆的长度为 k - for (int i = k; i < nums.Length; i++) { - // 若当前元素大于堆顶元素,则将堆顶元素出堆、当前元素入堆 - if (nums[i] > heap.Peek()) { - heap.Dequeue(); - heap.Enqueue(nums[i], nums[i]); - } - } - return heap; - } + [class]{top_k}-[func]{TopKHeap} ``` === "Go" ```go title="top_k.go" - /* 基于堆查找数组中最大的 k 个元素 */ - func topKHeap(nums []int, k int) *minHeap { - // 初始化小顶堆 - h := &minHeap{} - heap.Init(h) - // 将数组的前 k 个元素入堆 - for i := 0; i < k; i++ { - heap.Push(h, nums[i]) - } - // 从第 k+1 个元素开始,保持堆的长度为 k - for i := k; i < len(nums); i++ { - // 若当前元素大于堆顶元素,则将堆顶元素出堆、当前元素入堆 - if nums[i] > h.Top().(int) { - heap.Pop(h) - heap.Push(h, nums[i]) - } - } - return h - } + [class]{}-[func]{topKHeap} ``` === "Swift" ```swift title="top_k.swift" - /* 基于堆查找数组中最大的 k 个元素 */ - func topKHeap(nums: [Int], k: Int) -> [Int] { - // 初始化一个小顶堆,并将前 k 个元素建堆 - var heap = Heap(nums.prefix(k)) - // 从第 k+1 个元素开始,保持堆的长度为 k - for i in nums.indices.dropFirst(k) { - // 若当前元素大于堆顶元素,则将堆顶元素出堆、当前元素入堆 - if nums[i] > heap.min()! { - _ = heap.removeMin() - heap.insert(nums[i]) - } - } - return heap.unordered - } + [class]{}-[func]{topKHeap} ``` === "JS" ```javascript title="top_k.js" - /* 元素入堆 */ - function pushMinHeap(maxHeap, val) { - // 元素取反 - maxHeap.push(-val); - } + [class]{}-[func]{pushMinHeap} - /* 元素出堆 */ - function popMinHeap(maxHeap) { - // 元素取反 - return -maxHeap.pop(); - } + [class]{}-[func]{popMinHeap} - /* 访问堆顶元素 */ - function peekMinHeap(maxHeap) { - // 元素取反 - return -maxHeap.peek(); - } + [class]{}-[func]{peekMinHeap} - /* 取出堆中元素 */ - function getMinHeap(maxHeap) { - // 元素取反 - return maxHeap.getMaxHeap().map((num) => -num); - } + [class]{}-[func]{getMinHeap} - /* 基于堆查找数组中最大的 k 个元素 */ - function topKHeap(nums, k) { - // 初始化小顶堆 - // 请注意:我们将堆中所有元素取反,从而用大顶堆来模拟小顶堆 - const maxHeap = new MaxHeap([]); - // 将数组的前 k 个元素入堆 - for (let i = 0; i < k; i++) { - pushMinHeap(maxHeap, nums[i]); - } - // 从第 k+1 个元素开始,保持堆的长度为 k - for (let i = k; i < nums.length; i++) { - // 若当前元素大于堆顶元素,则将堆顶元素出堆、当前元素入堆 - if (nums[i] > peekMinHeap(maxHeap)) { - popMinHeap(maxHeap); - pushMinHeap(maxHeap, nums[i]); - } - } - // 返回堆中元素 - return getMinHeap(maxHeap); - } + [class]{}-[func]{topKHeap} ``` === "TS" ```typescript title="top_k.ts" - /* 元素入堆 */ - function pushMinHeap(maxHeap: MaxHeap, val: number): void { - // 元素取反 - maxHeap.push(-val); - } + [class]{}-[func]{pushMinHeap} - /* 元素出堆 */ - function popMinHeap(maxHeap: MaxHeap): number { - // 元素取反 - return -maxHeap.pop(); - } + [class]{}-[func]{popMinHeap} - /* 访问堆顶元素 */ - function peekMinHeap(maxHeap: MaxHeap): number { - // 元素取反 - return -maxHeap.peek(); - } + [class]{}-[func]{peekMinHeap} - /* 取出堆中元素 */ - function getMinHeap(maxHeap: MaxHeap): number[] { - // 元素取反 - return maxHeap.getMaxHeap().map((num: number) => -num); - } + [class]{}-[func]{getMinHeap} - /* 基于堆查找数组中最大的 k 个元素 */ - function topKHeap(nums: number[], k: number): number[] { - // 初始化小顶堆 - // 请注意:我们将堆中所有元素取反,从而用大顶堆来模拟小顶堆 - const maxHeap = new MaxHeap([]); - // 将数组的前 k 个元素入堆 - for (let i = 0; i < k; i++) { - pushMinHeap(maxHeap, nums[i]); - } - // 从第 k+1 个元素开始,保持堆的长度为 k - for (let i = k; i < nums.length; i++) { - // 若当前元素大于堆顶元素,则将堆顶元素出堆、当前元素入堆 - if (nums[i] > peekMinHeap(maxHeap)) { - popMinHeap(maxHeap); - pushMinHeap(maxHeap, nums[i]); - } - } - // 返回堆中元素 - return getMinHeap(maxHeap); - } + [class]{}-[func]{topKHeap} ``` === "Dart" ```dart title="top_k.dart" - /* 基于堆查找数组中最大的 k 个元素 */ - MinHeap topKHeap(List nums, int k) { - // 初始化小顶堆,将数组的前 k 个元素入堆 - MinHeap heap = MinHeap(nums.sublist(0, k)); - // 从第 k+1 个元素开始,保持堆的长度为 k - for (int i = k; i < nums.length; i++) { - // 若当前元素大于堆顶元素,则将堆顶元素出堆、当前元素入堆 - if (nums[i] > heap.peek()) { - heap.pop(); - heap.push(nums[i]); - } - } - return heap; - } + [class]{}-[func]{topKHeap} ``` === "Rust" ```rust title="top_k.rs" - /* 基于堆查找数组中最大的 k 个元素 */ - fn top_k_heap(nums: Vec, k: usize) -> BinaryHeap> { - // BinaryHeap 是大顶堆,使用 Reverse 将元素取反,从而实现小顶堆 - let mut heap = BinaryHeap::>::new(); - // 将数组的前 k 个元素入堆 - for &num in nums.iter().take(k) { - heap.push(Reverse(num)); - } - // 从第 k+1 个元素开始,保持堆的长度为 k - for &num in nums.iter().skip(k) { - // 若当前元素大于堆顶元素,则将堆顶元素出堆、当前元素入堆 - if num > heap.peek().unwrap().0 { - heap.pop(); - heap.push(Reverse(num)); - } - } - heap - } + [class]{}-[func]{top_k_heap} ``` === "C" ```c title="top_k.c" - /* 元素入堆 */ - void pushMinHeap(MaxHeap *maxHeap, int val) { - // 元素取反 - push(maxHeap, -val); - } + [class]{}-[func]{pushMinHeap} - /* 元素出堆 */ - int popMinHeap(MaxHeap *maxHeap) { - // 元素取反 - return -pop(maxHeap); - } + [class]{}-[func]{popMinHeap} - /* 访问堆顶元素 */ - int peekMinHeap(MaxHeap *maxHeap) { - // 元素取反 - return -peek(maxHeap); - } + [class]{}-[func]{peekMinHeap} - /* 取出堆中元素 */ - int *getMinHeap(MaxHeap *maxHeap) { - // 将堆中所有元素取反并存入 res 数组 - int *res = (int *)malloc(maxHeap->size * sizeof(int)); - for (int i = 0; i < maxHeap->size; i++) { - res[i] = -maxHeap->data[i]; - } - return res; - } + [class]{}-[func]{getMinHeap} - /* 取出堆中元素 */ - int *getMinHeap(MaxHeap *maxHeap) { - // 将堆中所有元素取反并存入 res 数组 - int *res = (int *)malloc(maxHeap->size * sizeof(int)); - for (int i = 0; i < maxHeap->size; i++) { - res[i] = -maxHeap->data[i]; - } - return res; - } - - // 基于堆查找数组中最大的 k 个元素的函数 - int *topKHeap(int *nums, int sizeNums, int k) { - // 初始化小顶堆 - // 请注意:我们将堆中所有元素取反,从而用大顶堆来模拟小顶堆 - int *empty = (int *)malloc(0); - MaxHeap *maxHeap = newMaxHeap(empty, 0); - // 将数组的前 k 个元素入堆 - for (int i = 0; i < k; i++) { - pushMinHeap(maxHeap, nums[i]); - } - // 从第 k+1 个元素开始,保持堆的长度为 k - for (int i = k; i < sizeNums; i++) { - // 若当前元素大于堆顶元素,则将堆顶元素出堆、当前元素入堆 - if (nums[i] > peekMinHeap(maxHeap)) { - popMinHeap(maxHeap); - pushMinHeap(maxHeap, nums[i]); - } - } - int *res = getMinHeap(maxHeap); - // 释放内存 - delMaxHeap(maxHeap); - return res; - } + [class]{}-[func]{topKHeap} ``` === "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 - } + [class]{}-[func]{topKHeap} ``` === "Ruby" ```ruby title="top_k.rb" - ### 基于堆查找数组中最大的 k 个元素 ### - def top_k_heap(nums, k) - # 初始化小顶堆 - # 请注意:我们将堆中所有元素取反,从而用大顶堆来模拟小顶堆 - max_heap = MaxHeap.new([]) - - # 将数组的前 k 个元素入堆 - for i in 0...k - push_min_heap(max_heap, nums[i]) - end - - # 从第 k+1 个元素开始,保持堆的长度为 k - for i in k...nums.length - # 若当前元素大于堆顶元素,则将堆顶元素出堆、当前元素入堆 - if nums[i] > peek_min_heap(max_heap) - pop_min_heap(max_heap) - push_min_heap(max_heap, nums[i]) - end - end - - get_min_heap(max_heap) - end + [class]{}-[func]{top_k_heap} ``` === "Zig" @@ -467,11 +212,6 @@ Example code is as follows: [class]{}-[func]{topKHeap} ``` -??? pythontutor "Code Visualization" - -
- - A total of $n$ rounds of heap insertions and deletions are performed, with the maximum heap size being $k$, hence the time complexity is $O(n \log k)$. This method is very efficient; when $k$ is small, the time complexity tends towards $O(n)$; when $k$ is large, the time complexity will not exceed $O(n \log n)$. Additionally, this method is suitable for scenarios with dynamic data streams. By continuously adding data, we can maintain the elements within the heap, thereby achieving dynamic updates of the largest $k$ elements. diff --git a/en/docs/chapter_searching/binary_search.md b/en/docs/chapter_searching/binary_search.md index 6f61d102e..aa803d1c8 100644 --- a/en/docs/chapter_searching/binary_search.md +++ b/en/docs/chapter_searching/binary_search.md @@ -57,62 +57,46 @@ The code is as follows: ```python title="binary_search.py" def binary_search(nums: list[int], target: int) -> int: - """二分查找(双闭区间)""" - # 初始化双闭区间 [0, n-1] ,即 i, j 分别指向数组首元素、尾元素 + """Binary search (double closed interval)""" + # Initialize double closed interval [0, n-1], i.e., i, j point to the first element and last element of the array respectively i, j = 0, len(nums) - 1 - # 循环,当搜索区间为空时跳出(当 i > j 时为空) + # Loop until the search interval is empty (when i > j, it is empty) while i <= j: - # 理论上 Python 的数字可以无限大(取决于内存大小),无须考虑大数越界问题 - m = (i + j) // 2 # 计算中点索引 m + # Theoretically, Python's numbers can be infinitely large (depending on memory size), so there is no need to consider large number overflow + m = (i + j) // 2 # Calculate midpoint index m if nums[m] < target: - i = m + 1 # 此情况说明 target 在区间 [m+1, j] 中 + i = m + 1 # This situation indicates that target is in the interval [m+1, j] elif nums[m] > target: - j = m - 1 # 此情况说明 target 在区间 [i, m-1] 中 + j = m - 1 # This situation indicates that target is in the interval [i, m-1] else: - return m # 找到目标元素,返回其索引 - return -1 # 未找到目标元素,返回 -1 + return m # Found the target element, thus return its index + return -1 # Did not find the target element, thus return -1 ``` === "C++" ```cpp title="binary_search.cpp" - /* 二分查找(双闭区间) */ - int binarySearch(vector &nums, int target) { - // 初始化双闭区间 [0, n-1] ,即 i, j 分别指向数组首元素、尾元素 - int i = 0, j = nums.size() - 1; - // 循环,当搜索区间为空时跳出(当 i > j 时为空) - while (i <= j) { - int 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; - } + [class]{}-[func]{binarySearch} ``` === "Java" ```java title="binary_search.java" - /* 二分查找(双闭区间) */ + /* Binary search (double closed interval) */ int binarySearch(int[] nums, int target) { - // 初始化双闭区间 [0, n-1] ,即 i, j 分别指向数组首元素、尾元素 + // Initialize double closed interval [0, n-1], i.e., i, j point to the first element and last element of the array respectively int i = 0, j = nums.length - 1; - // 循环,当搜索区间为空时跳出(当 i > j 时为空) + // Loop until the search interval is empty (when i > j, it is empty) while (i <= j) { - int m = i + (j - i) / 2; // 计算中点索引 m - if (nums[m] < target) // 此情况说明 target 在区间 [m+1, j] 中 + int m = i + (j - i) / 2; // Calculate midpoint index m + if (nums[m] < target) // This situation indicates that target is in the interval [m+1, j] i = m + 1; - else if (nums[m] > target) // 此情况说明 target 在区间 [i, m-1] 中 + else if (nums[m] > target) // This situation indicates that target is in the interval [i, m-1] j = m - 1; - else // 找到目标元素,返回其索引 + else // Found the target element, thus return its index return m; } - // 未找到目标元素,返回 -1 + // Did not find the target element, thus return -1 return -1; } ``` @@ -120,277 +104,69 @@ The code is as follows: === "C#" ```csharp title="binary_search.cs" - /* 二分查找(双闭区间) */ - int BinarySearch(int[] nums, int target) { - // 初始化双闭区间 [0, n-1] ,即 i, j 分别指向数组首元素、尾元素 - int i = 0, j = nums.Length - 1; - // 循环,当搜索区间为空时跳出(当 i > j 时为空) - while (i <= j) { - int 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; - } + [class]{binary_search}-[func]{BinarySearch} ``` === "Go" ```go title="binary_search.go" - /* 二分查找(双闭区间) */ - func binarySearch(nums []int, target int) int { - // 初始化双闭区间 [0, n-1] ,即 i, j 分别指向数组首元素、尾元素 - i, j := 0, len(nums)-1 - // 循环,当搜索区间为空时跳出(当 i > j 时为空) - for i <= j { - 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 - } + [class]{}-[func]{binarySearch} ``` === "Swift" ```swift title="binary_search.swift" - /* 二分查找(双闭区间) */ - func binarySearch(nums: [Int], target: Int) -> Int { - // 初始化双闭区间 [0, n-1] ,即 i, j 分别指向数组首元素、尾元素 - var i = nums.startIndex - var j = nums.endIndex - 1 - // 循环,当搜索区间为空时跳出(当 i > j 时为空) - while i <= j { - let 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 - } + [class]{}-[func]{binarySearch} ``` === "JS" ```javascript title="binary_search.js" - /* 二分查找(双闭区间) */ - function binarySearch(nums, target) { - // 初始化双闭区间 [0, n-1] ,即 i, j 分别指向数组首元素、尾元素 - let i = 0, - j = nums.length - 1; - // 循环,当搜索区间为空时跳出(当 i > j 时为空) - while (i <= j) { - // 计算中点索引 m ,使用 parseInt() 向下取整 - const m = parseInt(i + (j - i) / 2); - 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; - } + [class]{}-[func]{binarySearch} ``` === "TS" ```typescript title="binary_search.ts" - /* 二分查找(双闭区间) */ - function binarySearch(nums: number[], target: number): number { - // 初始化双闭区间 [0, n-1] ,即 i, j 分别指向数组首元素、尾元素 - let i = 0, - j = nums.length - 1; - // 循环,当搜索区间为空时跳出(当 i > j 时为空) - while (i <= j) { - // 计算中点索引 m - const m = Math.floor(i + (j - i) / 2); - 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; - } - } - return -1; // 未找到目标元素,返回 -1 - } + [class]{}-[func]{binarySearch} ``` === "Dart" ```dart title="binary_search.dart" - /* 二分查找(双闭区间) */ - int binarySearch(List nums, int target) { - // 初始化双闭区间 [0, n-1] ,即 i, j 分别指向数组首元素、尾元素 - int i = 0, j = nums.length - 1; - // 循环,当搜索区间为空时跳出(当 i > j 时为空) - while (i <= j) { - int 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; - } + [class]{}-[func]{binarySearch} ``` === "Rust" ```rust title="binary_search.rs" - /* 二分查找(双闭区间) */ - fn binary_search(nums: &[i32], target: i32) -> i32 { - // 初始化双闭区间 [0, n-1] ,即 i, j 分别指向数组首元素、尾元素 - let mut i = 0; - let mut j = nums.len() as i32 - 1; - // 循环,当搜索区间为空时跳出(当 i > j 时为空) - while i <= j { - let m = i + (j - i) / 2; // 计算中点索引 m - if nums[m as usize] < target { - // 此情况说明 target 在区间 [m+1, j] 中 - i = m + 1; - } else if nums[m as usize] > target { - // 此情况说明 target 在区间 [i, m-1] 中 - j = m - 1; - } else { - // 找到目标元素,返回其索引 - return m; - } - } - // 未找到目标元素,返回 -1 - return -1; - } + [class]{}-[func]{binary_search} ``` === "C" ```c title="binary_search.c" - /* 二分查找(双闭区间) */ - int binarySearch(int *nums, int len, int target) { - // 初始化双闭区间 [0, n-1] ,即 i, j 分别指向数组首元素、尾元素 - int i = 0, j = len - 1; - // 循环,当搜索区间为空时跳出(当 i > j 时为空) - while (i <= j) { - int 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; - } + [class]{}-[func]{binarySearch} ``` === "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 - } + [class]{}-[func]{binarySearch} ``` === "Ruby" ```ruby title="binary_search.rb" - ### 二分查找(双闭区间) ### - def binary_search(nums, target) - # 初始化双闭区间 [0, n-1] ,即 i, j 分别指向数组首元素、尾元素 - i, j = 0, nums.length - 1 - - # 循环,当搜索区间为空时跳出(当 i > j 时为空) - while i <= j - # 理论上 Ruby 的数字可以无限大(取决于内存大小),无须考虑大数越界问题 - m = (i + j) / 2 # 计算中点索引 m - - if nums[m] < target - i = m + 1 # 此情况说明 target 在区间 [m+1, j] 中 - elsif nums[m] > target - j = m - 1 # 此情况说明 target 在区间 [i, m-1] 中 - else - return m # 找到目标元素,返回其索引 - end - end - - -1 # 未找到目标元素,返回 -1 - end + [class]{}-[func]{binary_search} ``` === "Zig" ```zig title="binary_search.zig" - // 二分查找(双闭区间) - fn binarySearch(comptime T: type, nums: std.ArrayList(T), target: T) T { - // 初始化双闭区间 [0, n-1] ,即 i, j 分别指向数组首元素、尾元素 - var i: usize = 0; - var j: usize = nums.items.len - 1; - // 循环,当搜索区间为空时跳出(当 i > j 时为空) - while (i <= j) { - var m = i + (j - i) / 2; // 计算中点索引 m - if (nums.items[m] < target) { // 此情况说明 target 在区间 [m+1, j] 中 - i = m + 1; - } else if (nums.items[m] > target) { // 此情况说明 target 在区间 [i, m-1] 中 - j = m - 1; - } else { // 找到目标元素,返回其索引 - return @intCast(m); - } - } - // 未找到目标元素,返回 -1 - return -1; - } + [class]{}-[func]{binarySearch} ``` -??? pythontutor "Code Visualization" - -
- - **Time complexity is $O(\log n)$** : In the binary loop, the interval reduces by half each round, hence the number of iterations is $\log_2 n$. **Space complexity is $O(1)$** : Pointers $i$ and $j$ use constant size space. @@ -405,61 +181,45 @@ We can implement a binary search algorithm with the same functionality based on ```python title="binary_search.py" def binary_search_lcro(nums: list[int], target: int) -> int: - """二分查找(左闭右开区间)""" - # 初始化左闭右开区间 [0, n) ,即 i, j 分别指向数组首元素、尾元素+1 + """Binary search (left closed right open interval)""" + # Initialize left closed right open interval [0, n), i.e., i, j point to the first element and the last element +1 of the array respectively i, j = 0, len(nums) - # 循环,当搜索区间为空时跳出(当 i = j 时为空) + # Loop until the search interval is empty (when i = j, it is empty) while i < j: - m = (i + j) // 2 # 计算中点索引 m + m = (i + j) // 2 # Calculate midpoint index m if nums[m] < target: - i = m + 1 # 此情况说明 target 在区间 [m+1, j) 中 + i = m + 1 # This situation indicates that target is in the interval [m+1, j) elif nums[m] > target: - j = m # 此情况说明 target 在区间 [i, m) 中 + j = m # This situation indicates that target is in the interval [i, m) else: - return m # 找到目标元素,返回其索引 - return -1 # 未找到目标元素,返回 -1 + return m # Found the target element, thus return its index + return -1 # Did not find the target element, thus return -1 ``` === "C++" ```cpp title="binary_search.cpp" - /* 二分查找(左闭右开区间) */ - int binarySearchLCRO(vector &nums, int target) { - // 初始化左闭右开区间 [0, n) ,即 i, j 分别指向数组首元素、尾元素+1 - int i = 0, j = nums.size(); - // 循环,当搜索区间为空时跳出(当 i = j 时为空) - while (i < j) { - int 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; - } + [class]{}-[func]{binarySearchLCRO} ``` === "Java" ```java title="binary_search.java" - /* 二分查找(左闭右开区间) */ + /* Binary search (left closed right open interval) */ int binarySearchLCRO(int[] nums, int target) { - // 初始化左闭右开区间 [0, n) ,即 i, j 分别指向数组首元素、尾元素+1 + // Initialize left closed right open interval [0, n), i.e., i, j point to the first element and the last element +1 of the array respectively int i = 0, j = nums.length; - // 循环,当搜索区间为空时跳出(当 i = j 时为空) + // Loop until the search interval is empty (when i = j, it is empty) while (i < j) { - int m = i + (j - i) / 2; // 计算中点索引 m - if (nums[m] < target) // 此情况说明 target 在区间 [m+1, j) 中 + int m = i + (j - i) / 2; // Calculate midpoint index m + if (nums[m] < target) // This situation indicates that target is in the interval [m+1, j) i = m + 1; - else if (nums[m] > target) // 此情况说明 target 在区间 [i, m) 中 + else if (nums[m] > target) // This situation indicates that target is in the interval [i, m) j = m; - else // 找到目标元素,返回其索引 + else // Found the target element, thus return its index return m; } - // 未找到目标元素,返回 -1 + // Did not find the target element, thus return -1 return -1; } ``` @@ -467,278 +227,69 @@ We can implement a binary search algorithm with the same functionality based on === "C#" ```csharp title="binary_search.cs" - /* 二分查找(左闭右开区间) */ - int BinarySearchLCRO(int[] nums, int target) { - // 初始化左闭右开区间 [0, n) ,即 i, j 分别指向数组首元素、尾元素+1 - int i = 0, j = nums.Length; - // 循环,当搜索区间为空时跳出(当 i = j 时为空) - while (i < j) { - int 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; - } + [class]{binary_search}-[func]{BinarySearchLCRO} ``` === "Go" ```go title="binary_search.go" - /* 二分查找(左闭右开区间) */ - func binarySearchLCRO(nums []int, target int) int { - // 初始化左闭右开区间 [0, n) ,即 i, j 分别指向数组首元素、尾元素+1 - i, j := 0, len(nums) - // 循环,当搜索区间为空时跳出(当 i = j 时为空) - for i < j { - 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 - } + [class]{}-[func]{binarySearchLCRO} ``` === "Swift" ```swift title="binary_search.swift" - /* 二分查找(左闭右开区间) */ - func binarySearchLCRO(nums: [Int], target: Int) -> Int { - // 初始化左闭右开区间 [0, n) ,即 i, j 分别指向数组首元素、尾元素+1 - var i = nums.startIndex - var j = nums.endIndex - // 循环,当搜索区间为空时跳出(当 i = j 时为空) - while i < j { - let 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 - } + [class]{}-[func]{binarySearchLCRO} ``` === "JS" ```javascript title="binary_search.js" - /* 二分查找(左闭右开区间) */ - function binarySearchLCRO(nums, target) { - // 初始化左闭右开区间 [0, n) ,即 i, j 分别指向数组首元素、尾元素+1 - let i = 0, - j = nums.length; - // 循环,当搜索区间为空时跳出(当 i = j 时为空) - while (i < j) { - // 计算中点索引 m ,使用 parseInt() 向下取整 - const m = parseInt(i + (j - i) / 2); - 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; - } + [class]{}-[func]{binarySearchLCRO} ``` === "TS" ```typescript title="binary_search.ts" - /* 二分查找(左闭右开区间) */ - function binarySearchLCRO(nums: number[], target: number): number { - // 初始化左闭右开区间 [0, n) ,即 i, j 分别指向数组首元素、尾元素+1 - let i = 0, - j = nums.length; - // 循环,当搜索区间为空时跳出(当 i = j 时为空) - while (i < j) { - // 计算中点索引 m - const m = Math.floor(i + (j - i) / 2); - if (nums[m] < target) { - // 此情况说明 target 在区间 [m+1, j) 中 - i = m + 1; - } else if (nums[m] > target) { - // 此情况说明 target 在区间 [i, m) 中 - j = m; - } else { - // 找到目标元素,返回其索引 - return m; - } - } - return -1; // 未找到目标元素,返回 -1 - } + [class]{}-[func]{binarySearchLCRO} ``` === "Dart" ```dart title="binary_search.dart" - /* 二分查找(左闭右开区间) */ - int binarySearchLCRO(List nums, int target) { - // 初始化左闭右开区间 [0, n) ,即 i, j 分别指向数组首元素、尾元素+1 - int i = 0, j = nums.length; - // 循环,当搜索区间为空时跳出(当 i = j 时为空) - while (i < j) { - int 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; - } + [class]{}-[func]{binarySearchLCRO} ``` === "Rust" ```rust title="binary_search.rs" - /* 二分查找(左闭右开区间) */ - fn binary_search_lcro(nums: &[i32], target: i32) -> i32 { - // 初始化左闭右开区间 [0, n) ,即 i, j 分别指向数组首元素、尾元素+1 - let mut i = 0; - let mut j = nums.len() as i32; - // 循环,当搜索区间为空时跳出(当 i = j 时为空) - while i < j { - let m = i + (j - i) / 2; // 计算中点索引 m - if nums[m as usize] < target { - // 此情况说明 target 在区间 [m+1, j) 中 - i = m + 1; - } else if nums[m as usize] > target { - // 此情况说明 target 在区间 [i, m) 中 - j = m; - } else { - // 找到目标元素,返回其索引 - return m; - } - } - // 未找到目标元素,返回 -1 - return -1; - } + [class]{}-[func]{binary_search_lcro} ``` === "C" ```c title="binary_search.c" - /* 二分查找(左闭右开区间) */ - int binarySearchLCRO(int *nums, int len, int target) { - // 初始化左闭右开区间 [0, n) ,即 i, j 分别指向数组首元素、尾元素+1 - int i = 0, j = len; - // 循环,当搜索区间为空时跳出(当 i = j 时为空) - while (i < j) { - int 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; - } + [class]{}-[func]{binarySearchLCRO} ``` === "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 - } + [class]{}-[func]{binarySearchLCRO} ``` === "Ruby" ```ruby title="binary_search.rb" - ### 二分查找(左闭右开区间) ### - def binary_search_lcro(nums, target) - # 初始化左闭右开区间 [0, n) ,即 i, j 分别指向数组首元素、尾元素+1 - i, j = 0, nums.length - - # 循环,当搜索区间为空时跳出(当 i = j 时为空) - while i < j - # 计算中点索引 m - m = (i + j) / 2 - - if nums[m] < target - i = m + 1 # 此情况说明 target 在区间 [m+1, j) 中 - elsif nums[m] > target - j = m - 1 # 此情况说明 target 在区间 [i, m) 中 - else - return m # 找到目标元素,返回其索引 - end - end - - -1 # 未找到目标元素,返回 -1 - end + [class]{}-[func]{binary_search_lcro} ``` === "Zig" ```zig title="binary_search.zig" - // 二分查找(左闭右开区间) - fn binarySearchLCRO(comptime T: type, nums: std.ArrayList(T), target: T) T { - // 初始化左闭右开区间 [0, n) ,即 i, j 分别指向数组首元素、尾元素+1 - var i: usize = 0; - var j: usize = nums.items.len; - // 循环,当搜索区间为空时跳出(当 i = j 时为空) - while (i <= j) { - var m = i + (j - i) / 2; // 计算中点索引 m - if (nums.items[m] < target) { // 此情况说明 target 在区间 [m+1, j) 中 - i = m + 1; - } else if (nums.items[m] > target) { // 此情况说明 target 在区间 [i, m) 中 - j = m; - } else { // 找到目标元素,返回其索引 - return @intCast(m); - } - } - // 未找到目标元素,返回 -1 - return -1; - } + [class]{}-[func]{binarySearchLCRO} ``` -??? pythontutor "Code Visualization" - -
- - As shown in Figure 10-3, in the two types of interval representations, the initialization of the binary search algorithm, the loop condition, and the narrowing interval operation are different. Since both boundaries in the "closed interval" representation are defined as closed, the operations to narrow the interval through pointers $i$ and $j$ are also symmetrical. This makes it less prone to errors, **therefore, it is generally recommended to use the "closed interval" approach**. diff --git a/en/docs/chapter_searching/binary_search_edge.md b/en/docs/chapter_searching/binary_search_edge.md index b5687f202..16af6fb90 100644 --- a/en/docs/chapter_searching/binary_search_edge.md +++ b/en/docs/chapter_searching/binary_search_edge.md @@ -23,44 +23,34 @@ In these cases, simply return $-1$. The code is as follows: ```python title="binary_search_edge.py" def binary_search_left_edge(nums: list[int], target: int) -> int: - """二分查找最左一个 target""" - # 等价于查找 target 的插入点 + """Binary search for the leftmost target""" + # Equivalent to finding the insertion point of target i = binary_search_insertion(nums, target) - # 未找到 target ,返回 -1 + # Did not find target, thus return -1 if i == len(nums) or nums[i] != target: return -1 - # 找到 target ,返回索引 i + # Found target, return index i return i ``` === "C++" ```cpp title="binary_search_edge.cpp" - /* 二分查找最左一个 target */ - int binarySearchLeftEdge(vector &nums, int target) { - // 等价于查找 target 的插入点 - int i = binarySearchInsertion(nums, target); - // 未找到 target ,返回 -1 - if (i == nums.size() || nums[i] != target) { - return -1; - } - // 找到 target ,返回索引 i - return i; - } + [class]{}-[func]{binarySearchLeftEdge} ``` === "Java" ```java title="binary_search_edge.java" - /* 二分查找最左一个 target */ + /* Binary search for the leftmost target */ int binarySearchLeftEdge(int[] nums, int target) { - // 等价于查找 target 的插入点 + // Equivalent to finding the insertion point of target int i = binary_search_insertion.binarySearchInsertion(nums, target); - // 未找到 target ,返回 -1 + // Did not find target, thus return -1 if (i == nums.length || nums[i] != target) { return -1; } - // 找到 target ,返回索引 i + // Found target, return index i return i; } ``` @@ -68,160 +58,61 @@ In these cases, simply return $-1$. The code is as follows: === "C#" ```csharp title="binary_search_edge.cs" - /* 二分查找最左一个 target */ - int BinarySearchLeftEdge(int[] nums, int target) { - // 等价于查找 target 的插入点 - int i = binary_search_insertion.BinarySearchInsertion(nums, target); - // 未找到 target ,返回 -1 - if (i == nums.Length || nums[i] != target) { - return -1; - } - // 找到 target ,返回索引 i - return i; - } + [class]{binary_search_edge}-[func]{BinarySearchLeftEdge} ``` === "Go" ```go title="binary_search_edge.go" - /* 二分查找最左一个 target */ - func binarySearchLeftEdge(nums []int, target int) int { - // 等价于查找 target 的插入点 - i := binarySearchInsertion(nums, target) - // 未找到 target ,返回 -1 - if i == len(nums) || nums[i] != target { - return -1 - } - // 找到 target ,返回索引 i - return i - } + [class]{}-[func]{binarySearchLeftEdge} ``` === "Swift" ```swift title="binary_search_edge.swift" - /* 二分查找最左一个 target */ - func binarySearchLeftEdge(nums: [Int], target: Int) -> Int { - // 等价于查找 target 的插入点 - let i = binarySearchInsertion(nums: nums, target: target) - // 未找到 target ,返回 -1 - if i == nums.endIndex || nums[i] != target { - return -1 - } - // 找到 target ,返回索引 i - return i - } + [class]{}-[func]{binarySearchLeftEdge} ``` === "JS" ```javascript title="binary_search_edge.js" - /* 二分查找最左一个 target */ - function binarySearchLeftEdge(nums, target) { - // 等价于查找 target 的插入点 - const i = binarySearchInsertion(nums, target); - // 未找到 target ,返回 -1 - if (i === nums.length || nums[i] !== target) { - return -1; - } - // 找到 target ,返回索引 i - return i; - } + [class]{}-[func]{binarySearchLeftEdge} ``` === "TS" ```typescript title="binary_search_edge.ts" - /* 二分查找最左一个 target */ - function binarySearchLeftEdge(nums: Array, target: number): number { - // 等价于查找 target 的插入点 - const i = binarySearchInsertion(nums, target); - // 未找到 target ,返回 -1 - if (i === nums.length || nums[i] !== target) { - return -1; - } - // 找到 target ,返回索引 i - return i; - } + [class]{}-[func]{binarySearchLeftEdge} ``` === "Dart" ```dart title="binary_search_edge.dart" - /* 二分查找最左一个 target */ - int binarySearchLeftEdge(List nums, int target) { - // 等价于查找 target 的插入点 - int i = binarySearchInsertion(nums, target); - // 未找到 target ,返回 -1 - if (i == nums.length || nums[i] != target) { - return -1; - } - // 找到 target ,返回索引 i - return i; - } + [class]{}-[func]{binarySearchLeftEdge} ``` === "Rust" ```rust title="binary_search_edge.rs" - /* 二分查找最左一个 target */ - fn binary_search_left_edge(nums: &[i32], target: i32) -> i32 { - // 等价于查找 target 的插入点 - let i = binary_search_insertion(nums, target); - // 未找到 target ,返回 -1 - if i == nums.len() as i32 || nums[i as usize] != target { - return -1; - } - // 找到 target ,返回索引 i - i - } + [class]{}-[func]{binary_search_left_edge} ``` === "C" ```c title="binary_search_edge.c" - /* 二分查找最左一个 target */ - int binarySearchLeftEdge(int *nums, int numSize, int target) { - // 等价于查找 target 的插入点 - int i = binarySearchInsertion(nums, numSize, target); - // 未找到 target ,返回 -1 - if (i == numSize || nums[i] != target) { - return -1; - } - // 找到 target ,返回索引 i - return i; - } + [class]{}-[func]{binarySearchLeftEdge} ``` === "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 - } + [class]{}-[func]{binarySearchLeftEdge} ``` === "Ruby" ```ruby title="binary_search_edge.rb" - ### 二分查找最左一个 target ### - def binary_search_left_edge(nums, target) - # 等价于查找 target 的插入点 - i = binary_search_insertion(nums, target) - - # 未找到 target ,返回 -1 - return -1 if i == nums.length || nums[i] != target - - i # 找到 target ,返回索引 i - end + [class]{}-[func]{binary_search_left_edge} ``` === "Zig" @@ -230,11 +121,6 @@ In these cases, simply return $-1$. The code is as follows: [class]{}-[func]{binarySearchLeftEdge} ``` -??? pythontutor "Code Visualization" - -
- - ## 10.3.2   Find the right boundary So how do we find the rightmost `target`? The most straightforward way is to modify the code, replacing the pointer contraction operation in the case of `nums[m] == target`. The code is omitted here, but interested readers can implement it on their own. @@ -257,50 +143,38 @@ Please note, the insertion point returned is $i$, therefore, it should be subtra ```python title="binary_search_edge.py" def binary_search_right_edge(nums: list[int], target: int) -> int: - """二分查找最右一个 target""" - # 转化为查找最左一个 target + 1 + """Binary search for the rightmost target""" + # Convert to finding the leftmost target + 1 i = binary_search_insertion(nums, target + 1) - # j 指向最右一个 target ,i 指向首个大于 target 的元素 + # j points to the rightmost target, i points to the first element greater than target j = i - 1 - # 未找到 target ,返回 -1 + # Did not find target, thus return -1 if j == -1 or nums[j] != target: return -1 - # 找到 target ,返回索引 j + # Found target, return index j return j ``` === "C++" ```cpp title="binary_search_edge.cpp" - /* 二分查找最右一个 target */ - int binarySearchRightEdge(vector &nums, int target) { - // 转化为查找最左一个 target + 1 - int i = binarySearchInsertion(nums, target + 1); - // j 指向最右一个 target ,i 指向首个大于 target 的元素 - int j = i - 1; - // 未找到 target ,返回 -1 - if (j == -1 || nums[j] != target) { - return -1; - } - // 找到 target ,返回索引 j - return j; - } + [class]{}-[func]{binarySearchRightEdge} ``` === "Java" ```java title="binary_search_edge.java" - /* 二分查找最右一个 target */ + /* Binary search for the rightmost target */ int binarySearchRightEdge(int[] nums, int target) { - // 转化为查找最左一个 target + 1 + // Convert to finding the leftmost target + 1 int i = binary_search_insertion.binarySearchInsertion(nums, target + 1); - // j 指向最右一个 target ,i 指向首个大于 target 的元素 + // j points to the rightmost target, i points to the first element greater than target int j = i - 1; - // 未找到 target ,返回 -1 + // Did not find target, thus return -1 if (j == -1 || nums[j] != target) { return -1; } - // 找到 target ,返回索引 j + // Found target, return index j return j; } ``` @@ -308,181 +182,61 @@ Please note, the insertion point returned is $i$, therefore, it should be subtra === "C#" ```csharp title="binary_search_edge.cs" - /* 二分查找最右一个 target */ - int BinarySearchRightEdge(int[] nums, int target) { - // 转化为查找最左一个 target + 1 - int i = binary_search_insertion.BinarySearchInsertion(nums, target + 1); - // j 指向最右一个 target ,i 指向首个大于 target 的元素 - int j = i - 1; - // 未找到 target ,返回 -1 - if (j == -1 || nums[j] != target) { - return -1; - } - // 找到 target ,返回索引 j - return j; - } + [class]{binary_search_edge}-[func]{BinarySearchRightEdge} ``` === "Go" ```go title="binary_search_edge.go" - /* 二分查找最右一个 target */ - func binarySearchRightEdge(nums []int, target int) int { - // 转化为查找最左一个 target + 1 - i := binarySearchInsertion(nums, target+1) - // j 指向最右一个 target ,i 指向首个大于 target 的元素 - j := i - 1 - // 未找到 target ,返回 -1 - if j == -1 || nums[j] != target { - return -1 - } - // 找到 target ,返回索引 j - return j - } + [class]{}-[func]{binarySearchRightEdge} ``` === "Swift" ```swift title="binary_search_edge.swift" - /* 二分查找最右一个 target */ - func binarySearchRightEdge(nums: [Int], target: Int) -> Int { - // 转化为查找最左一个 target + 1 - let i = binarySearchInsertion(nums: nums, target: target + 1) - // j 指向最右一个 target ,i 指向首个大于 target 的元素 - let j = i - 1 - // 未找到 target ,返回 -1 - if j == -1 || nums[j] != target { - return -1 - } - // 找到 target ,返回索引 j - return j - } + [class]{}-[func]{binarySearchRightEdge} ``` === "JS" ```javascript title="binary_search_edge.js" - /* 二分查找最右一个 target */ - function binarySearchRightEdge(nums, target) { - // 转化为查找最左一个 target + 1 - const i = binarySearchInsertion(nums, target + 1); - // j 指向最右一个 target ,i 指向首个大于 target 的元素 - const j = i - 1; - // 未找到 target ,返回 -1 - if (j === -1 || nums[j] !== target) { - return -1; - } - // 找到 target ,返回索引 j - return j; - } + [class]{}-[func]{binarySearchRightEdge} ``` === "TS" ```typescript title="binary_search_edge.ts" - /* 二分查找最右一个 target */ - function binarySearchRightEdge(nums: Array, target: number): number { - // 转化为查找最左一个 target + 1 - const i = binarySearchInsertion(nums, target + 1); - // j 指向最右一个 target ,i 指向首个大于 target 的元素 - const j = i - 1; - // 未找到 target ,返回 -1 - if (j === -1 || nums[j] !== target) { - return -1; - } - // 找到 target ,返回索引 j - return j; - } + [class]{}-[func]{binarySearchRightEdge} ``` === "Dart" ```dart title="binary_search_edge.dart" - /* 二分查找最右一个 target */ - int binarySearchRightEdge(List nums, int target) { - // 转化为查找最左一个 target + 1 - int i = binarySearchInsertion(nums, target + 1); - // j 指向最右一个 target ,i 指向首个大于 target 的元素 - int j = i - 1; - // 未找到 target ,返回 -1 - if (j == -1 || nums[j] != target) { - return -1; - } - // 找到 target ,返回索引 j - return j; - } + [class]{}-[func]{binarySearchRightEdge} ``` === "Rust" ```rust title="binary_search_edge.rs" - /* 二分查找最右一个 target */ - fn binary_search_right_edge(nums: &[i32], target: i32) -> i32 { - // 转化为查找最左一个 target + 1 - let i = binary_search_insertion(nums, target + 1); - // j 指向最右一个 target ,i 指向首个大于 target 的元素 - let j = i - 1; - // 未找到 target ,返回 -1 - if j == -1 || nums[j as usize] != target { - return -1; - } - // 找到 target ,返回索引 j - j - } + [class]{}-[func]{binary_search_right_edge} ``` === "C" ```c title="binary_search_edge.c" - /* 二分查找最右一个 target */ - int binarySearchRightEdge(int *nums, int numSize, int target) { - // 转化为查找最左一个 target + 1 - int i = binarySearchInsertion(nums, numSize, target + 1); - // j 指向最右一个 target ,i 指向首个大于 target 的元素 - int j = i - 1; - // 未找到 target ,返回 -1 - if (j == -1 || nums[j] != target) { - return -1; - } - // 找到 target ,返回索引 j - return j; - } + [class]{}-[func]{binarySearchRightEdge} ``` === "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 - } + [class]{}-[func]{binarySearchRightEdge} ``` === "Ruby" ```ruby title="binary_search_edge.rb" - ### 二分查找最右一个 target ### - def binary_search_right_edge(nums, target) - # 转化为查找最左一个 target + 1 - i = binary_search_insertion(nums, target + 1) - - # j 指向最右一个 target ,i 指向首个大于 target 的元素 - j = i - 1 - - # 未找到 target ,返回 -1 - return -1 if j == -1 || nums[j] != target - - j # 找到 target ,返回索引 j - end + [class]{}-[func]{binary_search_right_edge} ``` === "Zig" @@ -491,11 +245,6 @@ Please note, the insertion point returned is $i$, therefore, it should be subtra [class]{}-[func]{binarySearchRightEdge} ``` -??? pythontutor "Code Visualization" - -
- - ### 2.   Transforming into an element search We know that when the array does not contain `target`, $i$ and $j$ will eventually point to the first element greater and smaller than `target` respectively. diff --git a/en/docs/chapter_searching/binary_search_insertion.md b/en/docs/chapter_searching/binary_search_insertion.md index 3f8470b3c..ef6f19f0d 100644 --- a/en/docs/chapter_searching/binary_search_insertion.md +++ b/en/docs/chapter_searching/binary_search_insertion.md @@ -32,58 +32,43 @@ Therefore, at the end of the binary, it is certain that: $i$ points to the first ```python title="binary_search_insertion.py" def binary_search_insertion_simple(nums: list[int], target: int) -> int: - """二分查找插入点(无重复元素)""" - i, j = 0, len(nums) - 1 # 初始化双闭区间 [0, n-1] + """Binary search for insertion point (no duplicate elements)""" + i, j = 0, len(nums) - 1 # Initialize double closed interval [0, n-1] while i <= j: - m = (i + j) // 2 # 计算中点索引 m + m = (i + j) // 2 # Calculate midpoint index m if nums[m] < target: - i = m + 1 # target 在区间 [m+1, j] 中 + i = m + 1 # Target is in interval [m+1, j] elif nums[m] > target: - j = m - 1 # target 在区间 [i, m-1] 中 + j = m - 1 # Target is in interval [i, m-1] else: - return m # 找到 target ,返回插入点 m - # 未找到 target ,返回插入点 i + return m # Found target, return insertion point m + # Did not find target, return insertion point i return i ``` === "C++" ```cpp title="binary_search_insertion.cpp" - /* 二分查找插入点(无重复元素) */ - int binarySearchInsertionSimple(vector &nums, int target) { - int i = 0, j = nums.size() - 1; // 初始化双闭区间 [0, n-1] - while (i <= j) { - int 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; - } + [class]{}-[func]{binarySearchInsertionSimple} ``` === "Java" ```java title="binary_search_insertion.java" - /* 二分查找插入点(无重复元素) */ + /* Binary search for insertion point (no duplicate elements) */ int binarySearchInsertionSimple(int[] nums, int target) { - int i = 0, j = nums.length - 1; // 初始化双闭区间 [0, n-1] + int i = 0, j = nums.length - 1; // Initialize double closed interval [0, n-1] while (i <= j) { - int m = i + (j - i) / 2; // 计算中点索引 m + int m = i + (j - i) / 2; // Calculate midpoint index m if (nums[m] < target) { - i = m + 1; // target 在区间 [m+1, j] 中 + i = m + 1; // Target is in interval [m+1, j] } else if (nums[m] > target) { - j = m - 1; // target 在区间 [i, m-1] 中 + j = m - 1; // Target is in interval [i, m-1] } else { - return m; // 找到 target ,返回插入点 m + return m; // Found target, return insertion point m } } - // 未找到 target ,返回插入点 i + // Did not find target, return insertion point i return i; } ``` @@ -91,228 +76,61 @@ Therefore, at the end of the binary, it is certain that: $i$ points to the first === "C#" ```csharp title="binary_search_insertion.cs" - /* 二分查找插入点(无重复元素) */ - int BinarySearchInsertionSimple(int[] nums, int target) { - int i = 0, j = nums.Length - 1; // 初始化双闭区间 [0, n-1] - while (i <= j) { - int 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; - } + [class]{binary_search_insertion}-[func]{BinarySearchInsertionSimple} ``` === "Go" ```go title="binary_search_insertion.go" - /* 二分查找插入点(无重复元素) */ - func binarySearchInsertionSimple(nums []int, target int) int { - // 初始化双闭区间 [0, n-1] - i, j := 0, len(nums)-1 - for i <= j { - // 计算中点索引 m - m := i + (j-i)/2 - if nums[m] < target { - // target 在区间 [m+1, j] 中 - i = m + 1 - } else if nums[m] > target { - // target 在区间 [i, m-1] 中 - j = m - 1 - } else { - // 找到 target ,返回插入点 m - return m - } - } - // 未找到 target ,返回插入点 i - return i - } + [class]{}-[func]{binarySearchInsertionSimple} ``` === "Swift" ```swift title="binary_search_insertion.swift" - /* 二分查找插入点(无重复元素) */ - func binarySearchInsertionSimple(nums: [Int], target: Int) -> Int { - // 初始化双闭区间 [0, n-1] - var i = nums.startIndex - var j = nums.endIndex - 1 - while i <= j { - let 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 - } + [class]{}-[func]{binarySearchInsertionSimple} ``` === "JS" ```javascript title="binary_search_insertion.js" - /* 二分查找插入点(无重复元素) */ - function binarySearchInsertionSimple(nums, target) { - let i = 0, - j = nums.length - 1; // 初始化双闭区间 [0, n-1] - while (i <= j) { - const m = Math.floor(i + (j - i) / 2); // 计算中点索引 m, 使用 Math.floor() 向下取整 - 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; - } + [class]{}-[func]{binarySearchInsertionSimple} ``` === "TS" ```typescript title="binary_search_insertion.ts" - /* 二分查找插入点(无重复元素) */ - function binarySearchInsertionSimple( - nums: Array, - target: number - ): number { - let i = 0, - j = nums.length - 1; // 初始化双闭区间 [0, n-1] - while (i <= j) { - const m = Math.floor(i + (j - i) / 2); // 计算中点索引 m, 使用 Math.floor() 向下取整 - 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; - } + [class]{}-[func]{binarySearchInsertionSimple} ``` === "Dart" ```dart title="binary_search_insertion.dart" - /* 二分查找插入点(无重复元素) */ - int binarySearchInsertionSimple(List nums, int target) { - int i = 0, j = nums.length - 1; // 初始化双闭区间 [0, n-1] - while (i <= j) { - int 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; - } + [class]{}-[func]{binarySearchInsertionSimple} ``` === "Rust" ```rust title="binary_search_insertion.rs" - /* 二分查找插入点(无重复元素) */ - fn binary_search_insertion_simple(nums: &[i32], target: i32) -> i32 { - let (mut i, mut j) = (0, nums.len() as i32 - 1); // 初始化双闭区间 [0, n-1] - while i <= j { - let m = i + (j - i) / 2; // 计算中点索引 m - if nums[m as usize] < target { - i = m + 1; // target 在区间 [m+1, j] 中 - } else if nums[m as usize] > target { - j = m - 1; // target 在区间 [i, m-1] 中 - } else { - return m; - } - } - // 未找到 target ,返回插入点 i - i - } + [class]{}-[func]{binary_search_insertion_simple} ``` === "C" ```c title="binary_search_insertion.c" - /* 二分查找插入点(无重复元素) */ - int binarySearchInsertionSimple(int *nums, int numSize, int target) { - int i = 0, j = numSize - 1; // 初始化双闭区间 [0, n-1] - while (i <= j) { - int 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; - } + [class]{}-[func]{binarySearchInsertionSimple} ``` === "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 - } + [class]{}-[func]{binarySearchInsertionSimple} ``` === "Ruby" ```ruby title="binary_search_insertion.rb" - ### 二分查找插入点(无重复元素) ### - def binary_search_insertion_simple(nums, target) - # 初始化双闭区间 [0, n-1] - i, j = 0, nums.length - 1 - - while i <= j - # 计算中点索引 m - m = (i + j) / 2 - - if nums[m] < target - i = m + 1 # target 在区间 [m+1, j] 中 - elsif nums[m] > target - j = m - 1 # target 在区间 [i, m-1] 中 - else - return m # 找到 target ,返回插入点 m - end - end - - i # 未找到 target ,返回插入点 i - end + [class]{}-[func]{binary_search_insertion_simple} ``` === "Zig" @@ -321,11 +139,6 @@ Therefore, at the end of the binary, it is certain that: $i$ points to the first [class]{}-[func]{binarySearchInsertionSimple} ``` -??? pythontutor "Code Visualization" - -
- - ## 10.2.2   Case with duplicate elements !!! question @@ -386,58 +199,43 @@ Even so, we can still keep the conditions expanded, as their logic is clearer an ```python title="binary_search_insertion.py" def binary_search_insertion(nums: list[int], target: int) -> int: - """二分查找插入点(存在重复元素)""" - i, j = 0, len(nums) - 1 # 初始化双闭区间 [0, n-1] + """Binary search for insertion point (with duplicate elements)""" + i, j = 0, len(nums) - 1 # Initialize double closed interval [0, n-1] while i <= j: - m = (i + j) // 2 # 计算中点索引 m + m = (i + j) // 2 # Calculate midpoint index m if nums[m] < target: - i = m + 1 # target 在区间 [m+1, j] 中 + i = m + 1 # Target is in interval [m+1, j] elif nums[m] > target: - j = m - 1 # target 在区间 [i, m-1] 中 + j = m - 1 # Target is in interval [i, m-1] else: - j = m - 1 # 首个小于 target 的元素在区间 [i, m-1] 中 - # 返回插入点 i + j = m - 1 # First element less than target is in interval [i, m-1] + # Return insertion point i return i ``` === "C++" ```cpp title="binary_search_insertion.cpp" - /* 二分查找插入点(存在重复元素) */ - int binarySearchInsertion(vector &nums, int target) { - int i = 0, j = nums.size() - 1; // 初始化双闭区间 [0, n-1] - while (i <= j) { - int 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; - } + [class]{}-[func]{binarySearchInsertion} ``` === "Java" ```java title="binary_search_insertion.java" - /* 二分查找插入点(存在重复元素) */ + /* Binary search for insertion point (with duplicate elements) */ int binarySearchInsertion(int[] nums, int target) { - int i = 0, j = nums.length - 1; // 初始化双闭区间 [0, n-1] + int i = 0, j = nums.length - 1; // Initialize double closed interval [0, n-1] while (i <= j) { - int m = i + (j - i) / 2; // 计算中点索引 m + int m = i + (j - i) / 2; // Calculate midpoint index m if (nums[m] < target) { - i = m + 1; // target 在区间 [m+1, j] 中 + i = m + 1; // Target is in interval [m+1, j] } else if (nums[m] > target) { - j = m - 1; // target 在区间 [i, m-1] 中 + j = m - 1; // Target is in interval [i, m-1] } else { - j = m - 1; // 首个小于 target 的元素在区间 [i, m-1] 中 + j = m - 1; // First element less than target is in interval [i, m-1] } } - // 返回插入点 i + // Return insertion point i return i; } ``` @@ -445,225 +243,61 @@ Even so, we can still keep the conditions expanded, as their logic is clearer an === "C#" ```csharp title="binary_search_insertion.cs" - /* 二分查找插入点(存在重复元素) */ - int BinarySearchInsertion(int[] nums, int target) { - int i = 0, j = nums.Length - 1; // 初始化双闭区间 [0, n-1] - while (i <= j) { - int 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; - } + [class]{binary_search_insertion}-[func]{BinarySearchInsertion} ``` === "Go" ```go title="binary_search_insertion.go" - /* 二分查找插入点(存在重复元素) */ - func binarySearchInsertion(nums []int, target int) int { - // 初始化双闭区间 [0, n-1] - i, j := 0, len(nums)-1 - for i <= j { - // 计算中点索引 m - m := i + (j-i)/2 - if nums[m] < target { - // target 在区间 [m+1, j] 中 - i = m + 1 - } else if nums[m] > target { - // target 在区间 [i, m-1] 中 - j = m - 1 - } else { - // 首个小于 target 的元素在区间 [i, m-1] 中 - j = m - 1 - } - } - // 返回插入点 i - return i - } + [class]{}-[func]{binarySearchInsertion} ``` === "Swift" ```swift title="binary_search_insertion.swift" - /* 二分查找插入点(存在重复元素) */ - func binarySearchInsertion(nums: [Int], target: Int) -> Int { - // 初始化双闭区间 [0, n-1] - var i = nums.startIndex - var j = nums.endIndex - 1 - while i <= j { - let 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 - } + [class]{}-[func]{binarySearchInsertion} ``` === "JS" ```javascript title="binary_search_insertion.js" - /* 二分查找插入点(存在重复元素) */ - function binarySearchInsertion(nums, target) { - let i = 0, - j = nums.length - 1; // 初始化双闭区间 [0, n-1] - while (i <= j) { - const m = Math.floor(i + (j - i) / 2); // 计算中点索引 m, 使用 Math.floor() 向下取整 - 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; - } + [class]{}-[func]{binarySearchInsertion} ``` === "TS" ```typescript title="binary_search_insertion.ts" - /* 二分查找插入点(存在重复元素) */ - function binarySearchInsertion(nums: Array, target: number): number { - let i = 0, - j = nums.length - 1; // 初始化双闭区间 [0, n-1] - while (i <= j) { - const m = Math.floor(i + (j - i) / 2); // 计算中点索引 m, 使用 Math.floor() 向下取整 - 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; - } + [class]{}-[func]{binarySearchInsertion} ``` === "Dart" ```dart title="binary_search_insertion.dart" - /* 二分查找插入点(存在重复元素) */ - int binarySearchInsertion(List nums, int target) { - int i = 0, j = nums.length - 1; // 初始化双闭区间 [0, n-1] - while (i <= j) { - int 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; - } + [class]{}-[func]{binarySearchInsertion} ``` === "Rust" ```rust title="binary_search_insertion.rs" - /* 二分查找插入点(存在重复元素) */ - pub fn binary_search_insertion(nums: &[i32], target: i32) -> i32 { - let (mut i, mut j) = (0, nums.len() as i32 - 1); // 初始化双闭区间 [0, n-1] - while i <= j { - let m = i + (j - i) / 2; // 计算中点索引 m - if nums[m as usize] < target { - i = m + 1; // target 在区间 [m+1, j] 中 - } else if nums[m as usize] > target { - j = m - 1; // target 在区间 [i, m-1] 中 - } else { - j = m - 1; // 首个小于 target 的元素在区间 [i, m-1] 中 - } - } - // 返回插入点 i - i - } + [class]{}-[func]{binary_search_insertion} ``` === "C" ```c title="binary_search_insertion.c" - /* 二分查找插入点(存在重复元素) */ - int binarySearchInsertion(int *nums, int numSize, int target) { - int i = 0, j = numSize - 1; // 初始化双闭区间 [0, n-1] - while (i <= j) { - int 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; - } + [class]{}-[func]{binarySearchInsertion} ``` === "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 - } + [class]{}-[func]{binarySearchInsertion} ``` === "Ruby" ```ruby title="binary_search_insertion.rb" - ### 二分查找插入点(存在重复元素) ### - def binary_search_insertion(nums, target) - # 初始化双闭区间 [0, n-1] - i, j = 0, nums.length - 1 - - while i <= j - # 计算中点索引 m - m = (i + j) / 2 - - if nums[m] < target - i = m + 1 # target 在区间 [m+1, j] 中 - elsif nums[m] > target - j = m - 1 # target 在区间 [i, m-1] 中 - else - j = m - 1 # 首个小于 target 的元素在区间 [i, m-1] 中 - end - end - - i # 返回插入点 i - end + [class]{}-[func]{binary_search_insertion} ``` === "Zig" @@ -672,11 +306,6 @@ Even so, we can still keep the conditions expanded, as their logic is clearer an [class]{}-[func]{binarySearchInsertion} ``` -??? pythontutor "Code Visualization" - -
- - !!! tip The code in this section uses "closed intervals". Readers interested can implement the "left-closed right-open" method themselves. diff --git a/en/docs/chapter_searching/replace_linear_by_hashing.md b/en/docs/chapter_searching/replace_linear_by_hashing.md index 731648368..2a37ac94b 100644 --- a/en/docs/chapter_searching/replace_linear_by_hashing.md +++ b/en/docs/chapter_searching/replace_linear_by_hashing.md @@ -24,8 +24,8 @@ The code is shown below: ```python title="two_sum.py" def two_sum_brute_force(nums: list[int], target: int) -> list[int]: - """方法一:暴力枚举""" - # 两层循环,时间复杂度为 O(n^2) + """Method one: Brute force enumeration""" + # Two-layer loop, time complexity is O(n^2) for i in range(len(nums) - 1): for j in range(i + 1, len(nums)): if nums[i] + nums[j] == target: @@ -36,27 +36,16 @@ The code is shown below: === "C++" ```cpp title="two_sum.cpp" - /* 方法一:暴力枚举 */ - vector twoSumBruteForce(vector &nums, int target) { - int size = nums.size(); - // 两层循环,时间复杂度为 O(n^2) - for (int i = 0; i < size - 1; i++) { - for (int j = i + 1; j < size; j++) { - if (nums[i] + nums[j] == target) - return {i, j}; - } - } - return {}; - } + [class]{}-[func]{twoSumBruteForce} ``` === "Java" ```java title="two_sum.java" - /* 方法一:暴力枚举 */ + /* Method one: Brute force enumeration */ int[] twoSumBruteForce(int[] nums, int target) { int size = nums.length; - // 两层循环,时间复杂度为 O(n^2) + // Two-layer loop, time complexity is O(n^2) for (int i = 0; i < size - 1; i++) { for (int j = i + 1; j < size; j++) { if (nums[i] + nums[j] == target) @@ -70,202 +59,69 @@ The code is shown below: === "C#" ```csharp title="two_sum.cs" - /* 方法一:暴力枚举 */ - int[] TwoSumBruteForce(int[] nums, int target) { - int size = nums.Length; - // 两层循环,时间复杂度为 O(n^2) - for (int i = 0; i < size - 1; i++) { - for (int j = i + 1; j < size; j++) { - if (nums[i] + nums[j] == target) - return [i, j]; - } - } - return []; - } + [class]{two_sum}-[func]{TwoSumBruteForce} ``` === "Go" ```go title="two_sum.go" - /* 方法一:暴力枚举 */ - func twoSumBruteForce(nums []int, target int) []int { - size := len(nums) - // 两层循环,时间复杂度为 O(n^2) - for i := 0; i < size-1; i++ { - for j := i + 1; j < size; j++ { - if nums[i]+nums[j] == target { - return []int{i, j} - } - } - } - return nil - } + [class]{}-[func]{twoSumBruteForce} ``` === "Swift" ```swift title="two_sum.swift" - /* 方法一:暴力枚举 */ - func twoSumBruteForce(nums: [Int], target: Int) -> [Int] { - // 两层循环,时间复杂度为 O(n^2) - for i in nums.indices.dropLast() { - for j in nums.indices.dropFirst(i + 1) { - if nums[i] + nums[j] == target { - return [i, j] - } - } - } - return [0] - } + [class]{}-[func]{twoSumBruteForce} ``` === "JS" ```javascript title="two_sum.js" - /* 方法一:暴力枚举 */ - function twoSumBruteForce(nums, target) { - const n = nums.length; - // 两层循环,时间复杂度为 O(n^2) - for (let i = 0; i < n; i++) { - for (let j = i + 1; j < n; j++) { - if (nums[i] + nums[j] === target) { - return [i, j]; - } - } - } - return []; - } + [class]{}-[func]{twoSumBruteForce} ``` === "TS" ```typescript title="two_sum.ts" - /* 方法一:暴力枚举 */ - function twoSumBruteForce(nums: number[], target: number): number[] { - const n = nums.length; - // 两层循环,时间复杂度为 O(n^2) - for (let i = 0; i < n; i++) { - for (let j = i + 1; j < n; j++) { - if (nums[i] + nums[j] === target) { - return [i, j]; - } - } - } - return []; - } + [class]{}-[func]{twoSumBruteForce} ``` === "Dart" ```dart title="two_sum.dart" - /* 方法一: 暴力枚举 */ - List twoSumBruteForce(List nums, int target) { - int size = nums.length; - // 两层循环,时间复杂度为 O(n^2) - for (var i = 0; i < size - 1; i++) { - for (var j = i + 1; j < size; j++) { - if (nums[i] + nums[j] == target) return [i, j]; - } - } - return [0]; - } + [class]{}-[func]{twoSumBruteForce} ``` === "Rust" ```rust title="two_sum.rs" - /* 方法一:暴力枚举 */ - pub fn two_sum_brute_force(nums: &Vec, target: i32) -> Option> { - let size = nums.len(); - // 两层循环,时间复杂度为 O(n^2) - for i in 0..size - 1 { - for j in i + 1..size { - if nums[i] + nums[j] == target { - return Some(vec![i as i32, j as i32]); - } - } - } - None - } + [class]{}-[func]{two_sum_brute_force} ``` === "C" ```c title="two_sum.c" - /* 方法一:暴力枚举 */ - int *twoSumBruteForce(int *nums, int numsSize, int target, int *returnSize) { - for (int i = 0; i < numsSize; ++i) { - for (int j = i + 1; j < numsSize; ++j) { - if (nums[i] + nums[j] == target) { - int *res = malloc(sizeof(int) * 2); - res[0] = i, res[1] = j; - *returnSize = 2; - return res; - } - } - } - *returnSize = 0; - return NULL; - } + [class]{}-[func]{twoSumBruteForce} ``` === "Kotlin" ```kotlin title="two_sum.kt" - /* 方法一:暴力枚举 */ - fun twoSumBruteForce(nums: IntArray, target: Int): IntArray { - val size = nums.size - // 两层循环,时间复杂度为 O(n^2) - for (i in 0.. - - This method has a time complexity of $O(n^2)$ and a space complexity of $O(1)$, which is very time-consuming with large data volumes. ## 10.4.2   Hash search: trading space for time @@ -292,10 +148,10 @@ The implementation code is shown below, requiring only a single loop: ```python title="two_sum.py" def two_sum_hash_table(nums: list[int], target: int) -> list[int]: - """方法二:辅助哈希表""" - # 辅助哈希表,空间复杂度为 O(n) + """Method two: Auxiliary hash table""" + # Auxiliary hash table, space complexity is O(n) dic = {} - # 单层循环,时间复杂度为 O(n) + # Single-layer loop, time complexity is O(n) for i in range(len(nums)): if target - nums[i] in dic: return [dic[target - nums[i]], i] @@ -306,31 +162,18 @@ The implementation code is shown below, requiring only a single loop: === "C++" ```cpp title="two_sum.cpp" - /* 方法二:辅助哈希表 */ - vector twoSumHashTable(vector &nums, int target) { - int size = nums.size(); - // 辅助哈希表,空间复杂度为 O(n) - unordered_map dic; - // 单层循环,时间复杂度为 O(n) - for (int i = 0; i < size; i++) { - if (dic.find(target - nums[i]) != dic.end()) { - return {dic[target - nums[i]], i}; - } - dic.emplace(nums[i], i); - } - return {}; - } + [class]{}-[func]{twoSumHashTable} ``` === "Java" ```java title="two_sum.java" - /* 方法二:辅助哈希表 */ + /* Method two: Auxiliary hash table */ int[] twoSumHashTable(int[] nums, int target) { int size = nums.length; - // 辅助哈希表,空间复杂度为 O(n) + // Auxiliary hash table, space complexity is O(n) Map dic = new HashMap<>(); - // 单层循环,时间复杂度为 O(n) + // Single-layer loop, time complexity is O(n) for (int i = 0; i < size; i++) { if (dic.containsKey(target - nums[i])) { return new int[] { dic.get(target - nums[i]), i }; @@ -344,244 +187,71 @@ The implementation code is shown below, requiring only a single loop: === "C#" ```csharp title="two_sum.cs" - /* 方法二:辅助哈希表 */ - int[] TwoSumHashTable(int[] nums, int target) { - int size = nums.Length; - // 辅助哈希表,空间复杂度为 O(n) - Dictionary dic = []; - // 单层循环,时间复杂度为 O(n) - for (int i = 0; i < size; i++) { - if (dic.ContainsKey(target - nums[i])) { - return [dic[target - nums[i]], i]; - } - dic.Add(nums[i], i); - } - return []; - } + [class]{two_sum}-[func]{TwoSumHashTable} ``` === "Go" ```go title="two_sum.go" - /* 方法二:辅助哈希表 */ - func twoSumHashTable(nums []int, target int) []int { - // 辅助哈希表,空间复杂度为 O(n) - hashTable := map[int]int{} - // 单层循环,时间复杂度为 O(n) - for idx, val := range nums { - if preIdx, ok := hashTable[target-val]; ok { - return []int{preIdx, idx} - } - hashTable[val] = idx - } - return nil - } + [class]{}-[func]{twoSumHashTable} ``` === "Swift" ```swift title="two_sum.swift" - /* 方法二:辅助哈希表 */ - func twoSumHashTable(nums: [Int], target: Int) -> [Int] { - // 辅助哈希表,空间复杂度为 O(n) - var dic: [Int: Int] = [:] - // 单层循环,时间复杂度为 O(n) - for i in nums.indices { - if let j = dic[target - nums[i]] { - return [j, i] - } - dic[nums[i]] = i - } - return [0] - } + [class]{}-[func]{twoSumHashTable} ``` === "JS" ```javascript title="two_sum.js" - /* 方法二:辅助哈希表 */ - function twoSumHashTable(nums, target) { - // 辅助哈希表,空间复杂度为 O(n) - let m = {}; - // 单层循环,时间复杂度为 O(n) - for (let i = 0; i < nums.length; i++) { - if (m[target - nums[i]] !== undefined) { - return [m[target - nums[i]], i]; - } else { - m[nums[i]] = i; - } - } - return []; - } + [class]{}-[func]{twoSumHashTable} ``` === "TS" ```typescript title="two_sum.ts" - /* 方法二:辅助哈希表 */ - function twoSumHashTable(nums: number[], target: number): number[] { - // 辅助哈希表,空间复杂度为 O(n) - let m: Map = new Map(); - // 单层循环,时间复杂度为 O(n) - for (let i = 0; i < nums.length; i++) { - let index = m.get(target - nums[i]); - if (index !== undefined) { - return [index, i]; - } else { - m.set(nums[i], i); - } - } - return []; - } + [class]{}-[func]{twoSumHashTable} ``` === "Dart" ```dart title="two_sum.dart" - /* 方法二: 辅助哈希表 */ - List twoSumHashTable(List nums, int target) { - int size = nums.length; - // 辅助哈希表,空间复杂度为 O(n) - Map dic = HashMap(); - // 单层循环,时间复杂度为 O(n) - for (var i = 0; i < size; i++) { - if (dic.containsKey(target - nums[i])) { - return [dic[target - nums[i]]!, i]; - } - dic.putIfAbsent(nums[i], () => i); - } - return [0]; - } + [class]{}-[func]{twoSumHashTable} ``` === "Rust" ```rust title="two_sum.rs" - /* 方法二:辅助哈希表 */ - pub fn two_sum_hash_table(nums: &Vec, target: i32) -> Option> { - // 辅助哈希表,空间复杂度为 O(n) - let mut dic = HashMap::new(); - // 单层循环,时间复杂度为 O(n) - for (i, num) in nums.iter().enumerate() { - match dic.get(&(target - num)) { - Some(v) => return Some(vec![*v as i32, i as i32]), - None => dic.insert(num, i as i32), - }; - } - None - } + [class]{}-[func]{two_sum_hash_table} ``` === "C" ```c title="two_sum.c" - /* 哈希表 */ - typedef struct { - int key; - int val; - UT_hash_handle hh; // 基于 uthash.h 实现 - } HashTable; + [class]{HashTable}-[func]{} - /* 哈希表查询 */ - HashTable *find(HashTable *h, int key) { - HashTable *tmp; - HASH_FIND_INT(h, &key, tmp); - return tmp; - } - - /* 哈希表元素插入 */ - void insert(HashTable *h, int key, int val) { - HashTable *t = find(h, key); - if (t == NULL) { - HashTable *tmp = malloc(sizeof(HashTable)); - tmp->key = key, tmp->val = val; - HASH_ADD_INT(h, key, tmp); - } else { - t->val = val; - } - } - - /* 方法二:辅助哈希表 */ - int *twoSumHashTable(int *nums, int numsSize, int target, int *returnSize) { - HashTable *hashtable = NULL; - for (int i = 0; i < numsSize; i++) { - HashTable *t = find(hashtable, target - nums[i]); - if (t != NULL) { - int *res = malloc(sizeof(int) * 2); - res[0] = t->val, res[1] = i; - *returnSize = 2; - return res; - } - insert(hashtable, nums[i], i); - } - *returnSize = 0; - return NULL; - } + [class]{}-[func]{twoSumHashTable} ``` === "Kotlin" ```kotlin title="two_sum.kt" - /* 方法二:辅助哈希表 */ - fun twoSumHashTable(nums: IntArray, target: Int): IntArray { - val size = nums.size - // 辅助哈希表,空间复杂度为 O(n) - val dic = HashMap() - // 单层循环,时间复杂度为 O(n) - for (i in 0.. - - This method reduces the time complexity from $O(n^2)$ to $O(n)$ by using hash search, greatly improving the running efficiency. As it requires maintaining an additional hash table, the space complexity is $O(n)$. **Nevertheless, this method has a more balanced time-space efficiency overall, making it the optimal solution for this problem**. diff --git a/en/docs/chapter_sorting/bubble_sort.md b/en/docs/chapter_sorting/bubble_sort.md index 3854212e0..4bdde9601 100644 --- a/en/docs/chapter_sorting/bubble_sort.md +++ b/en/docs/chapter_sorting/bubble_sort.md @@ -50,47 +50,34 @@ Example code is as follows: ```python title="bubble_sort.py" def bubble_sort(nums: list[int]): - """冒泡排序""" + """Bubble sort""" n = len(nums) - # 外循环:未排序区间为 [0, i] + # Outer loop: unsorted range is [0, i] for i in range(n - 1, 0, -1): - # 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端 + # Inner loop: swap the largest element in the unsorted range [0, i] to the right end of the range for j in range(i): if nums[j] > nums[j + 1]: - # 交换 nums[j] 与 nums[j + 1] + # Swap nums[j] and nums[j + 1] nums[j], nums[j + 1] = nums[j + 1], nums[j] ``` === "C++" ```cpp title="bubble_sort.cpp" - /* 冒泡排序 */ - void bubbleSort(vector &nums) { - // 外循环:未排序区间为 [0, i] - for (int i = nums.size() - 1; i > 0; i--) { - // 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端 - for (int j = 0; j < i; j++) { - if (nums[j] > nums[j + 1]) { - // 交换 nums[j] 与 nums[j + 1] - // 这里使用了 std::swap() 函数 - swap(nums[j], nums[j + 1]); - } - } - } - } + [class]{}-[func]{bubbleSort} ``` === "Java" ```java title="bubble_sort.java" - /* 冒泡排序 */ + /* Bubble sort */ void bubbleSort(int[] nums) { - // 外循环:未排序区间为 [0, i] + // Outer loop: unsorted range is [0, i] for (int i = nums.length - 1; i > 0; i--) { - // 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端 + // Inner loop: swap the largest element in the unsorted range [0, i] to the right end of the range for (int j = 0; j < i; j++) { if (nums[j] > nums[j + 1]) { - // 交换 nums[j] 与 nums[j + 1] + // Swap nums[j] and nums[j + 1] int tmp = nums[j]; nums[j] = nums[j + 1]; nums[j + 1] = tmp; @@ -103,222 +90,69 @@ Example code is as follows: === "C#" ```csharp title="bubble_sort.cs" - /* 冒泡排序 */ - void BubbleSort(int[] nums) { - // 外循环:未排序区间为 [0, i] - for (int i = nums.Length - 1; i > 0; i--) { - // 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端 - for (int j = 0; j < i; j++) { - if (nums[j] > nums[j + 1]) { - // 交换 nums[j] 与 nums[j + 1] - (nums[j + 1], nums[j]) = (nums[j], nums[j + 1]); - } - } - } - } + [class]{bubble_sort}-[func]{BubbleSort} ``` === "Go" ```go title="bubble_sort.go" - /* 冒泡排序 */ - func bubbleSort(nums []int) { - // 外循环:未排序区间为 [0, i] - for i := len(nums) - 1; i > 0; i-- { - // 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端 - for j := 0; j < i; j++ { - if nums[j] > nums[j+1] { - // 交换 nums[j] 与 nums[j + 1] - nums[j], nums[j+1] = nums[j+1], nums[j] - } - } - } - } + [class]{}-[func]{bubbleSort} ``` === "Swift" ```swift title="bubble_sort.swift" - /* 冒泡排序 */ - func bubbleSort(nums: inout [Int]) { - // 外循环:未排序区间为 [0, i] - for i in nums.indices.dropFirst().reversed() { - // 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端 - for j in 0 ..< i { - if nums[j] > nums[j + 1] { - // 交换 nums[j] 与 nums[j + 1] - nums.swapAt(j, j + 1) - } - } - } - } + [class]{}-[func]{bubbleSort} ``` === "JS" ```javascript title="bubble_sort.js" - /* 冒泡排序 */ - function bubbleSort(nums) { - // 外循环:未排序区间为 [0, i] - for (let i = nums.length - 1; i > 0; i--) { - // 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端 - for (let j = 0; j < i; j++) { - if (nums[j] > nums[j + 1]) { - // 交换 nums[j] 与 nums[j + 1] - let tmp = nums[j]; - nums[j] = nums[j + 1]; - nums[j + 1] = tmp; - } - } - } - } + [class]{}-[func]{bubbleSort} ``` === "TS" ```typescript title="bubble_sort.ts" - /* 冒泡排序 */ - function bubbleSort(nums: number[]): void { - // 外循环:未排序区间为 [0, i] - for (let i = nums.length - 1; i > 0; i--) { - // 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端 - for (let j = 0; j < i; j++) { - if (nums[j] > nums[j + 1]) { - // 交换 nums[j] 与 nums[j + 1] - let tmp = nums[j]; - nums[j] = nums[j + 1]; - nums[j + 1] = tmp; - } - } - } - } + [class]{}-[func]{bubbleSort} ``` === "Dart" ```dart title="bubble_sort.dart" - /* 冒泡排序 */ - void bubbleSort(List nums) { - // 外循环:未排序区间为 [0, i] - for (int i = nums.length - 1; i > 0; i--) { - // 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端 - for (int j = 0; j < i; j++) { - if (nums[j] > nums[j + 1]) { - // 交换 nums[j] 与 nums[j + 1] - int tmp = nums[j]; - nums[j] = nums[j + 1]; - nums[j + 1] = tmp; - } - } - } - } + [class]{}-[func]{bubbleSort} ``` === "Rust" ```rust title="bubble_sort.rs" - /* 冒泡排序 */ - fn bubble_sort(nums: &mut [i32]) { - // 外循环:未排序区间为 [0, i] - for i in (1..nums.len()).rev() { - // 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端 - for j in 0..i { - if nums[j] > nums[j + 1] { - // 交换 nums[j] 与 nums[j + 1] - let tmp = nums[j]; - nums[j] = nums[j + 1]; - nums[j + 1] = tmp; - } - } - } - } + [class]{}-[func]{bubble_sort} ``` === "C" ```c title="bubble_sort.c" - /* 冒泡排序 */ - void bubbleSort(int nums[], int size) { - // 外循环:未排序区间为 [0, i] - for (int i = size - 1; i > 0; i--) { - // 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端 - for (int j = 0; j < i; j++) { - if (nums[j] > nums[j + 1]) { - int temp = nums[j]; - nums[j] = nums[j + 1]; - nums[j + 1] = temp; - } - } - } - } + [class]{}-[func]{bubbleSort} ``` === "Kotlin" ```kotlin title="bubble_sort.kt" - /* 冒泡排序 */ - fun bubbleSort(nums: IntArray) { - // 外循环:未排序区间为 [0, i] - for (i in nums.size - 1 downTo 1) { - // 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端 - for (j in 0.. nums[j + 1]) { - // 交换 nums[j] 与 nums[j + 1] - val temp = nums[j] - nums[j] = nums[j + 1] - nums[j + 1] = temp - } - } - } - } + [class]{}-[func]{bubbleSort} ``` === "Ruby" ```ruby title="bubble_sort.rb" - ### 冒泡排序 ### - def bubble_sort(nums) - n = nums.length - # 外循环:未排序区间为 [0, i] - for i in (n - 1).downto(1) - # 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端 - for j in 0...i - if nums[j] > nums[j + 1] - # 交换 nums[j] 与 nums[j + 1] - nums[j], nums[j + 1] = nums[j + 1], nums[j] - end - end - end - end + [class]{}-[func]{bubble_sort} ``` === "Zig" ```zig title="bubble_sort.zig" - // 冒泡排序 - fn bubbleSort(nums: []i32) void { - // 外循环:未排序区间为 [0, i] - var i: usize = nums.len - 1; - while (i > 0) : (i -= 1) { - var j: usize = 0; - // 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端 - while (j < i) : (j += 1) { - if (nums[j] > nums[j + 1]) { - // 交换 nums[j] 与 nums[j + 1] - var tmp = nums[j]; - nums[j] = nums[j + 1]; - nums[j + 1] = tmp; - } - } - } - } + [class]{}-[func]{bubbleSort} ``` -??? pythontutor "Code Visualization" - -
- - ## 11.3.2   Efficiency optimization We find that if no swaps are performed in a round of "bubbling," the array is already sorted, and we can return the result immediately. Thus, we can add a flag `flag` to monitor this situation and return immediately when it occurs. @@ -329,64 +163,47 @@ Even after optimization, the worst-case time complexity and average time complex ```python title="bubble_sort.py" def bubble_sort_with_flag(nums: list[int]): - """冒泡排序(标志优化)""" + """Bubble sort (optimized with flag)""" n = len(nums) - # 外循环:未排序区间为 [0, i] + # Outer loop: unsorted range is [0, i] for i in range(n - 1, 0, -1): - flag = False # 初始化标志位 - # 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端 + flag = False # Initialize flag + # Inner loop: swap the largest element in the unsorted range [0, i] to the right end of the range for j in range(i): if nums[j] > nums[j + 1]: - # 交换 nums[j] 与 nums[j + 1] + # Swap nums[j] and nums[j + 1] nums[j], nums[j + 1] = nums[j + 1], nums[j] - flag = True # 记录交换元素 + flag = True # Record swapped elements if not flag: - break # 此轮“冒泡”未交换任何元素,直接跳出 + break # If no elements were swapped in this round of "bubbling", exit ``` === "C++" ```cpp title="bubble_sort.cpp" - /* 冒泡排序(标志优化)*/ - void bubbleSortWithFlag(vector &nums) { - // 外循环:未排序区间为 [0, i] - for (int i = nums.size() - 1; i > 0; i--) { - bool flag = false; // 初始化标志位 - // 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端 - for (int j = 0; j < i; j++) { - if (nums[j] > nums[j + 1]) { - // 交换 nums[j] 与 nums[j + 1] - // 这里使用了 std::swap() 函数 - swap(nums[j], nums[j + 1]); - flag = true; // 记录交换元素 - } - } - if (!flag) - break; // 此轮“冒泡”未交换任何元素,直接跳出 - } - } + [class]{}-[func]{bubbleSortWithFlag} ``` === "Java" ```java title="bubble_sort.java" - /* 冒泡排序(标志优化) */ + /* Bubble sort (optimized with flag) */ void bubbleSortWithFlag(int[] nums) { - // 外循环:未排序区间为 [0, i] + // Outer loop: unsorted range is [0, i] for (int i = nums.length - 1; i > 0; i--) { - boolean flag = false; // 初始化标志位 - // 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端 + boolean flag = false; // Initialize flag + // Inner loop: swap the largest element in the unsorted range [0, i] to the right end of the range for (int j = 0; j < i; j++) { if (nums[j] > nums[j + 1]) { - // 交换 nums[j] 与 nums[j + 1] + // Swap nums[j] and nums[j + 1] int tmp = nums[j]; nums[j] = nums[j + 1]; nums[j + 1] = tmp; - flag = true; // 记录交换元素 + flag = true; // Record swapped elements } } if (!flag) - break; // 此轮“冒泡”未交换任何元素,直接跳出 + break; // If no elements were swapped in this round of "bubbling", exit } } ``` @@ -394,263 +211,69 @@ Even after optimization, the worst-case time complexity and average time complex === "C#" ```csharp title="bubble_sort.cs" - /* 冒泡排序(标志优化)*/ - void BubbleSortWithFlag(int[] nums) { - // 外循环:未排序区间为 [0, i] - for (int i = nums.Length - 1; i > 0; i--) { - bool flag = false; // 初始化标志位 - // 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端 - for (int j = 0; j < i; j++) { - if (nums[j] > nums[j + 1]) { - // 交换 nums[j] 与 nums[j + 1] - (nums[j + 1], nums[j]) = (nums[j], nums[j + 1]); - flag = true; // 记录交换元素 - } - } - if (!flag) break; // 此轮“冒泡”未交换任何元素,直接跳出 - } - } + [class]{bubble_sort}-[func]{BubbleSortWithFlag} ``` === "Go" ```go title="bubble_sort.go" - /* 冒泡排序(标志优化)*/ - func bubbleSortWithFlag(nums []int) { - // 外循环:未排序区间为 [0, i] - for i := len(nums) - 1; i > 0; i-- { - flag := false // 初始化标志位 - // 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端 - for j := 0; j < i; j++ { - if nums[j] > nums[j+1] { - // 交换 nums[j] 与 nums[j + 1] - nums[j], nums[j+1] = nums[j+1], nums[j] - flag = true // 记录交换元素 - } - } - if flag == false { // 此轮“冒泡”未交换任何元素,直接跳出 - break - } - } - } + [class]{}-[func]{bubbleSortWithFlag} ``` === "Swift" ```swift title="bubble_sort.swift" - /* 冒泡排序(标志优化)*/ - func bubbleSortWithFlag(nums: inout [Int]) { - // 外循环:未排序区间为 [0, i] - for i in nums.indices.dropFirst().reversed() { - var flag = false // 初始化标志位 - for j in 0 ..< i { - if nums[j] > nums[j + 1] { - // 交换 nums[j] 与 nums[j + 1] - nums.swapAt(j, j + 1) - flag = true // 记录交换元素 - } - } - if !flag { // 此轮“冒泡”未交换任何元素,直接跳出 - break - } - } - } + [class]{}-[func]{bubbleSortWithFlag} ``` === "JS" ```javascript title="bubble_sort.js" - /* 冒泡排序(标志优化)*/ - function bubbleSortWithFlag(nums) { - // 外循环:未排序区间为 [0, i] - for (let i = nums.length - 1; i > 0; i--) { - let flag = false; // 初始化标志位 - // 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端 - for (let j = 0; j < i; j++) { - if (nums[j] > nums[j + 1]) { - // 交换 nums[j] 与 nums[j + 1] - let tmp = nums[j]; - nums[j] = nums[j + 1]; - nums[j + 1] = tmp; - flag = true; // 记录交换元素 - } - } - if (!flag) break; // 此轮“冒泡”未交换任何元素,直接跳出 - } - } + [class]{}-[func]{bubbleSortWithFlag} ``` === "TS" ```typescript title="bubble_sort.ts" - /* 冒泡排序(标志优化)*/ - function bubbleSortWithFlag(nums: number[]): void { - // 外循环:未排序区间为 [0, i] - for (let i = nums.length - 1; i > 0; i--) { - let flag = false; // 初始化标志位 - // 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端 - for (let j = 0; j < i; j++) { - if (nums[j] > nums[j + 1]) { - // 交换 nums[j] 与 nums[j + 1] - let tmp = nums[j]; - nums[j] = nums[j + 1]; - nums[j + 1] = tmp; - flag = true; // 记录交换元素 - } - } - if (!flag) break; // 此轮“冒泡”未交换任何元素,直接跳出 - } - } + [class]{}-[func]{bubbleSortWithFlag} ``` === "Dart" ```dart title="bubble_sort.dart" - /* 冒泡排序(标志优化)*/ - void bubbleSortWithFlag(List nums) { - // 外循环:未排序区间为 [0, i] - for (int i = nums.length - 1; i > 0; i--) { - bool flag = false; // 初始化标志位 - // 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端 - for (int j = 0; j < i; j++) { - if (nums[j] > nums[j + 1]) { - // 交换 nums[j] 与 nums[j + 1] - int tmp = nums[j]; - nums[j] = nums[j + 1]; - nums[j + 1] = tmp; - flag = true; // 记录交换元素 - } - } - if (!flag) break; // 此轮“冒泡”未交换任何元素,直接跳出 - } - } + [class]{}-[func]{bubbleSortWithFlag} ``` === "Rust" ```rust title="bubble_sort.rs" - /* 冒泡排序(标志优化) */ - fn bubble_sort_with_flag(nums: &mut [i32]) { - // 外循环:未排序区间为 [0, i] - for i in (1..nums.len()).rev() { - let mut flag = false; // 初始化标志位 - // 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端 - for j in 0..i { - if nums[j] > nums[j + 1] { - // 交换 nums[j] 与 nums[j + 1] - let tmp = nums[j]; - nums[j] = nums[j + 1]; - nums[j + 1] = tmp; - flag = true; // 记录交换元素 - } - } - if !flag { - break; // 此轮“冒泡”未交换任何元素,直接跳出 - }; - } - } + [class]{}-[func]{bubble_sort_with_flag} ``` === "C" ```c title="bubble_sort.c" - /* 冒泡排序(标志优化)*/ - void bubbleSortWithFlag(int nums[], int size) { - // 外循环:未排序区间为 [0, i] - for (int i = size - 1; i > 0; i--) { - bool flag = false; - // 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端 - for (int j = 0; j < i; j++) { - if (nums[j] > nums[j + 1]) { - int temp = nums[j]; - nums[j] = nums[j + 1]; - nums[j + 1] = temp; - flag = true; - } - } - if (!flag) - break; - } - } + [class]{}-[func]{bubbleSortWithFlag} ``` === "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] - val temp = nums[j] - nums[j] = nums[j + 1] - nums[j + 1] = temp - flag = true // 记录交换元素 - } - } - if (!flag) break // 此轮“冒泡”未交换任何元素,直接跳出 - } - } + [class]{}-[func]{bubbleSortWithFlag} ``` === "Ruby" ```ruby title="bubble_sort.rb" - ### 冒泡排序(标志优化)### - def bubble_sort_with_flag(nums) - n = nums.length - # 外循环:未排序区间为 [0, i] - for i in (n - 1).downto(1) - flag = false # 初始化标志位 - - # 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端 - for j in 0...i - if nums[j] > nums[j + 1] - # 交换 nums[j] 与 nums[j + 1] - nums[j], nums[j + 1] = nums[j + 1], nums[j] - flag = true # 记录交换元素 - end - end - - break unless flag # 此轮“冒泡”未交换任何元素,直接跳出 - end - end + [class]{}-[func]{bubble_sort_with_flag} ``` === "Zig" ```zig title="bubble_sort.zig" - // 冒泡排序(标志优化) - fn bubbleSortWithFlag(nums: []i32) void { - // 外循环:未排序区间为 [0, i] - var i: usize = nums.len - 1; - while (i > 0) : (i -= 1) { - var flag = false; // 初始化标志位 - var j: usize = 0; - // 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端 - while (j < i) : (j += 1) { - if (nums[j] > nums[j + 1]) { - // 交换 nums[j] 与 nums[j + 1] - var tmp = nums[j]; - nums[j] = nums[j + 1]; - nums[j + 1] = tmp; - flag = true; - } - } - if (!flag) break; // 此轮“冒泡”未交换任何元素,直接跳出 - } - } + [class]{}-[func]{bubbleSortWithFlag} ``` -??? pythontutor "Code Visualization" - -
- - ## 11.3.3   Algorithm characteristics - **Time complexity of $O(n^2)$, adaptive sorting**: The length of the array traversed in each round of "bubbling" decreases sequentially from $n - 1$, $n - 2$, $\dots$, $2$, $1$, totaling $(n - 1) n / 2$. With the introduction of `flag` optimization, the best time complexity can reach $O(n)$. diff --git a/en/docs/chapter_sorting/bucket_sort.md b/en/docs/chapter_sorting/bucket_sort.md index f737683ca..deee84690 100644 --- a/en/docs/chapter_sorting/bucket_sort.md +++ b/en/docs/chapter_sorting/bucket_sort.md @@ -26,21 +26,21 @@ The code is shown as follows: ```python title="bucket_sort.py" def bucket_sort(nums: list[float]): - """桶排序""" - # 初始化 k = n/2 个桶,预期向每个桶分配 2 个元素 + """Bucket sort""" + # Initialize k = n/2 buckets, expected to allocate 2 elements per bucket k = len(nums) // 2 buckets = [[] for _ in range(k)] - # 1. 将数组元素分配到各个桶中 + # 1. Distribute array elements into various buckets for num in nums: - # 输入数据范围为 [0, 1),使用 num * k 映射到索引范围 [0, k-1] + # Input data range is [0, 1), use num * k to map to index range [0, k-1] i = int(num * k) - # 将 num 添加进桶 i + # Add num to bucket i buckets[i].append(num) - # 2. 对各个桶执行排序 + # 2. Sort each bucket for bucket in buckets: - # 使用内置排序函数,也可以替换成其他排序算法 + # Use built-in sorting function, can also replace with other sorting algorithms bucket.sort() - # 3. 遍历桶合并结果 + # 3. Traverse buckets to merge results i = 0 for bucket in buckets: for num in bucket: @@ -51,57 +51,33 @@ The code is shown as follows: === "C++" ```cpp title="bucket_sort.cpp" - /* 桶排序 */ - void bucketSort(vector &nums) { - // 初始化 k = n/2 个桶,预期向每个桶分配 2 个元素 - int k = nums.size() / 2; - vector> buckets(k); - // 1. 将数组元素分配到各个桶中 - for (float num : nums) { - // 输入数据范围为 [0, 1),使用 num * k 映射到索引范围 [0, k-1] - int i = num * k; - // 将 num 添加进桶 bucket_idx - buckets[i].push_back(num); - } - // 2. 对各个桶执行排序 - for (vector &bucket : buckets) { - // 使用内置排序函数,也可以替换成其他排序算法 - sort(bucket.begin(), bucket.end()); - } - // 3. 遍历桶合并结果 - int i = 0; - for (vector &bucket : buckets) { - for (float num : bucket) { - nums[i++] = num; - } - } - } + [class]{}-[func]{bucketSort} ``` === "Java" ```java title="bucket_sort.java" - /* 桶排序 */ + /* Bucket sort */ void bucketSort(float[] nums) { - // 初始化 k = n/2 个桶,预期向每个桶分配 2 个元素 + // Initialize k = n/2 buckets, expected to allocate 2 elements per bucket int k = nums.length / 2; List> buckets = new ArrayList<>(); for (int i = 0; i < k; i++) { buckets.add(new ArrayList<>()); } - // 1. 将数组元素分配到各个桶中 + // 1. Distribute array elements into various buckets for (float num : nums) { - // 输入数据范围为 [0, 1),使用 num * k 映射到索引范围 [0, k-1] + // Input data range is [0, 1), use num * k to map to index range [0, k-1] int i = (int) (num * k); - // 将 num 添加进桶 i + // Add num to bucket i buckets.get(i).add(num); } - // 2. 对各个桶执行排序 + // 2. Sort each bucket for (List bucket : buckets) { - // 使用内置排序函数,也可以替换成其他排序算法 + // Use built-in sorting function, can also replace with other sorting algorithms Collections.sort(bucket); } - // 3. 遍历桶合并结果 + // 3. Traverse buckets to merge results int i = 0; for (List bucket : buckets) { for (float num : bucket) { @@ -114,327 +90,61 @@ The code is shown as follows: === "C#" ```csharp title="bucket_sort.cs" - /* 桶排序 */ - void BucketSort(float[] nums) { - // 初始化 k = n/2 个桶,预期向每个桶分配 2 个元素 - int k = nums.Length / 2; - List> buckets = []; - for (int i = 0; i < k; i++) { - buckets.Add([]); - } - // 1. 将数组元素分配到各个桶中 - foreach (float num in nums) { - // 输入数据范围为 [0, 1),使用 num * k 映射到索引范围 [0, k-1] - int i = (int)(num * k); - // 将 num 添加进桶 i - buckets[i].Add(num); - } - // 2. 对各个桶执行排序 - foreach (List bucket in buckets) { - // 使用内置排序函数,也可以替换成其他排序算法 - bucket.Sort(); - } - // 3. 遍历桶合并结果 - int j = 0; - foreach (List bucket in buckets) { - foreach (float num in bucket) { - nums[j++] = num; - } - } - } + [class]{bucket_sort}-[func]{BucketSort} ``` === "Go" ```go title="bucket_sort.go" - /* 桶排序 */ - func bucketSort(nums []float64) { - // 初始化 k = n/2 个桶,预期向每个桶分配 2 个元素 - k := len(nums) / 2 - buckets := make([][]float64, k) - for i := 0; i < k; i++ { - buckets[i] = make([]float64, 0) - } - // 1. 将数组元素分配到各个桶中 - for _, num := range nums { - // 输入数据范围为 [0, 1),使用 num * k 映射到索引范围 [0, k-1] - i := int(num * float64(k)) - // 将 num 添加进桶 i - buckets[i] = append(buckets[i], num) - } - // 2. 对各个桶执行排序 - for i := 0; i < k; i++ { - // 使用内置切片排序函数,也可以替换成其他排序算法 - sort.Float64s(buckets[i]) - } - // 3. 遍历桶合并结果 - i := 0 - for _, bucket := range buckets { - for _, num := range bucket { - nums[i] = num - i++ - } - } - } + [class]{}-[func]{bucketSort} ``` === "Swift" ```swift title="bucket_sort.swift" - /* 桶排序 */ - func bucketSort(nums: inout [Double]) { - // 初始化 k = n/2 个桶,预期向每个桶分配 2 个元素 - let k = nums.count / 2 - var buckets = (0 ..< k).map { _ in [Double]() } - // 1. 将数组元素分配到各个桶中 - for num in nums { - // 输入数据范围为 [0, 1),使用 num * k 映射到索引范围 [0, k-1] - let i = Int(num * Double(k)) - // 将 num 添加进桶 i - buckets[i].append(num) - } - // 2. 对各个桶执行排序 - for i in buckets.indices { - // 使用内置排序函数,也可以替换成其他排序算法 - buckets[i].sort() - } - // 3. 遍历桶合并结果 - var i = nums.startIndex - for bucket in buckets { - for num in bucket { - nums[i] = num - i += 1 - } - } - } + [class]{}-[func]{bucketSort} ``` === "JS" ```javascript title="bucket_sort.js" - /* 桶排序 */ - function bucketSort(nums) { - // 初始化 k = n/2 个桶,预期向每个桶分配 2 个元素 - const k = nums.length / 2; - const buckets = []; - for (let i = 0; i < k; i++) { - buckets.push([]); - } - // 1. 将数组元素分配到各个桶中 - for (const num of nums) { - // 输入数据范围为 [0, 1),使用 num * k 映射到索引范围 [0, k-1] - const i = Math.floor(num * k); - // 将 num 添加进桶 i - buckets[i].push(num); - } - // 2. 对各个桶执行排序 - for (const bucket of buckets) { - // 使用内置排序函数,也可以替换成其他排序算法 - bucket.sort((a, b) => a - b); - } - // 3. 遍历桶合并结果 - let i = 0; - for (const bucket of buckets) { - for (const num of bucket) { - nums[i++] = num; - } - } - } + [class]{}-[func]{bucketSort} ``` === "TS" ```typescript title="bucket_sort.ts" - /* 桶排序 */ - function bucketSort(nums: number[]): void { - // 初始化 k = n/2 个桶,预期向每个桶分配 2 个元素 - const k = nums.length / 2; - const buckets: number[][] = []; - for (let i = 0; i < k; i++) { - buckets.push([]); - } - // 1. 将数组元素分配到各个桶中 - for (const num of nums) { - // 输入数据范围为 [0, 1),使用 num * k 映射到索引范围 [0, k-1] - const i = Math.floor(num * k); - // 将 num 添加进桶 i - buckets[i].push(num); - } - // 2. 对各个桶执行排序 - for (const bucket of buckets) { - // 使用内置排序函数,也可以替换成其他排序算法 - bucket.sort((a, b) => a - b); - } - // 3. 遍历桶合并结果 - let i = 0; - for (const bucket of buckets) { - for (const num of bucket) { - nums[i++] = num; - } - } - } + [class]{}-[func]{bucketSort} ``` === "Dart" ```dart title="bucket_sort.dart" - /* 桶排序 */ - void bucketSort(List nums) { - // 初始化 k = n/2 个桶,预期向每个桶分配 2 个元素 - int k = nums.length ~/ 2; - List> buckets = List.generate(k, (index) => []); - - // 1. 将数组元素分配到各个桶中 - for (double _num in nums) { - // 输入数据范围为 [0, 1),使用 _num * k 映射到索引范围 [0, k-1] - int i = (_num * k).toInt(); - // 将 _num 添加进桶 bucket_idx - buckets[i].add(_num); - } - // 2. 对各个桶执行排序 - for (List bucket in buckets) { - bucket.sort(); - } - // 3. 遍历桶合并结果 - int i = 0; - for (List bucket in buckets) { - for (double _num in bucket) { - nums[i++] = _num; - } - } - } + [class]{}-[func]{bucketSort} ``` === "Rust" ```rust title="bucket_sort.rs" - /* 桶排序 */ - fn bucket_sort(nums: &mut [f64]) { - // 初始化 k = n/2 个桶,预期向每个桶分配 2 个元素 - let k = nums.len() / 2; - let mut buckets = vec![vec![]; k]; - // 1. 将数组元素分配到各个桶中 - for &mut num in &mut *nums { - // 输入数据范围为 [0, 1),使用 num * k 映射到索引范围 [0, k-1] - let i = (num * k as f64) as usize; - // 将 num 添加进桶 i - buckets[i].push(num); - } - // 2. 对各个桶执行排序 - for bucket in &mut buckets { - // 使用内置排序函数,也可以替换成其他排序算法 - bucket.sort_by(|a, b| a.partial_cmp(b).unwrap()); - } - // 3. 遍历桶合并结果 - let mut i = 0; - for bucket in &mut buckets { - for &mut num in bucket { - nums[i] = num; - i += 1; - } - } - } + [class]{}-[func]{bucket_sort} ``` === "C" ```c title="bucket_sort.c" - /* 桶排序 */ - void bucketSort(float nums[], int n) { - int k = n / 2; // 初始化 k = n/2 个桶 - int *sizes = malloc(k * sizeof(int)); // 记录每个桶的大小 - float **buckets = malloc(k * sizeof(float *)); // 动态数组的数组(桶) - // 为每个桶预分配足够的空间 - for (int i = 0; i < k; ++i) { - buckets[i] = (float *)malloc(n * sizeof(float)); - sizes[i] = 0; - } - // 1. 将数组元素分配到各个桶中 - for (int i = 0; i < n; ++i) { - int idx = (int)(nums[i] * k); - buckets[idx][sizes[idx]++] = nums[i]; - } - // 2. 对各个桶执行排序 - for (int i = 0; i < k; ++i) { - qsort(buckets[i], sizes[i], sizeof(float), compare); - } - // 3. 合并排序后的桶 - int idx = 0; - for (int i = 0; i < k; ++i) { - for (int j = 0; j < sizes[i]; ++j) { - nums[idx++] = buckets[i][j]; - } - // 释放内存 - free(buckets[i]); - } - } + [class]{}-[func]{bucketSort} ``` === "Kotlin" ```kotlin title="bucket_sort.kt" - /* 桶排序 */ - fun bucketSort(nums: FloatArray) { - // 初始化 k = n/2 个桶,预期向每个桶分配 2 个元素 - val k = nums.size / 2 - val buckets = mutableListOf>() - for (i in 0.. - - ## 11.8.2   Algorithm characteristics Bucket sort is suitable for handling very large data sets. For example, if the input data includes 1 million elements, and system memory limitations prevent loading all the data at once, you can divide the data into 1,000 buckets and sort each bucket separately before merging the results. diff --git a/en/docs/chapter_sorting/counting_sort.md b/en/docs/chapter_sorting/counting_sort.md index 6afd7e756..60b4c45e6 100644 --- a/en/docs/chapter_sorting/counting_sort.md +++ b/en/docs/chapter_sorting/counting_sort.md @@ -24,18 +24,18 @@ The code is shown below: ```python title="counting_sort.py" def counting_sort_naive(nums: list[int]): - """计数排序""" - # 简单实现,无法用于排序对象 - # 1. 统计数组最大元素 m + """Counting sort""" + # Simple implementation, cannot be used for sorting objects + # 1. Count the maximum element m in the array m = 0 for num in nums: m = max(m, num) - # 2. 统计各数字的出现次数 - # counter[num] 代表 num 的出现次数 + # 2. Count the occurrence of each digit + # counter[num] represents the occurrence of num counter = [0] * (m + 1) for num in nums: counter[num] += 1 - # 3. 遍历 counter ,将各元素填入原数组 nums + # 3. Traverse counter, filling each element back into the original array nums i = 0 for num in range(m + 1): for _ in range(counter[num]): @@ -46,48 +46,27 @@ The code is shown below: === "C++" ```cpp title="counting_sort.cpp" - /* 计数排序 */ - // 简单实现,无法用于排序对象 - void countingSortNaive(vector &nums) { - // 1. 统计数组最大元素 m - int m = 0; - for (int num : nums) { - m = max(m, num); - } - // 2. 统计各数字的出现次数 - // counter[num] 代表 num 的出现次数 - vector counter(m + 1, 0); - for (int num : nums) { - counter[num]++; - } - // 3. 遍历 counter ,将各元素填入原数组 nums - int i = 0; - for (int num = 0; num < m + 1; num++) { - for (int j = 0; j < counter[num]; j++, i++) { - nums[i] = num; - } - } - } + [class]{}-[func]{countingSortNaive} ``` === "Java" ```java title="counting_sort.java" - /* 计数排序 */ - // 简单实现,无法用于排序对象 + /* Counting sort */ + // Simple implementation, cannot be used for sorting objects void countingSortNaive(int[] nums) { - // 1. 统计数组最大元素 m + // 1. Count the maximum element m in the array int m = 0; for (int num : nums) { m = Math.max(m, num); } - // 2. 统计各数字的出现次数 - // counter[num] 代表 num 的出现次数 + // 2. Count the occurrence of each digit + // counter[num] represents the occurrence of num int[] counter = new int[m + 1]; for (int num : nums) { counter[num]++; } - // 3. 遍历 counter ,将各元素填入原数组 nums + // 3. Traverse counter, filling each element back into the original array nums int i = 0; for (int num = 0; num < m + 1; num++) { for (int j = 0; j < counter[num]; j++, i++) { @@ -100,273 +79,61 @@ The code is shown below: === "C#" ```csharp title="counting_sort.cs" - /* 计数排序 */ - // 简单实现,无法用于排序对象 - void CountingSortNaive(int[] nums) { - // 1. 统计数组最大元素 m - int m = 0; - foreach (int num in nums) { - m = Math.Max(m, num); - } - // 2. 统计各数字的出现次数 - // counter[num] 代表 num 的出现次数 - int[] counter = new int[m + 1]; - foreach (int num in nums) { - counter[num]++; - } - // 3. 遍历 counter ,将各元素填入原数组 nums - int i = 0; - for (int num = 0; num < m + 1; num++) { - for (int j = 0; j < counter[num]; j++, i++) { - nums[i] = num; - } - } - } + [class]{counting_sort}-[func]{CountingSortNaive} ``` === "Go" ```go title="counting_sort.go" - /* 计数排序 */ - // 简单实现,无法用于排序对象 - func countingSortNaive(nums []int) { - // 1. 统计数组最大元素 m - m := 0 - for _, num := range nums { - if num > m { - m = num - } - } - // 2. 统计各数字的出现次数 - // counter[num] 代表 num 的出现次数 - counter := make([]int, m+1) - for _, num := range nums { - counter[num]++ - } - // 3. 遍历 counter ,将各元素填入原数组 nums - for i, num := 0, 0; num < m+1; num++ { - for j := 0; j < counter[num]; j++ { - nums[i] = num - i++ - } - } - } + [class]{}-[func]{countingSortNaive} ``` === "Swift" ```swift title="counting_sort.swift" - /* 计数排序 */ - // 简单实现,无法用于排序对象 - func countingSortNaive(nums: inout [Int]) { - // 1. 统计数组最大元素 m - let m = nums.max()! - // 2. 统计各数字的出现次数 - // counter[num] 代表 num 的出现次数 - var counter = Array(repeating: 0, count: m + 1) - for num in nums { - counter[num] += 1 - } - // 3. 遍历 counter ,将各元素填入原数组 nums - var i = 0 - for num in 0 ..< m + 1 { - for _ in 0 ..< counter[num] { - nums[i] = num - i += 1 - } - } - } + [class]{}-[func]{countingSortNaive} ``` === "JS" ```javascript title="counting_sort.js" - /* 计数排序 */ - // 简单实现,无法用于排序对象 - function countingSortNaive(nums) { - // 1. 统计数组最大元素 m - let m = 0; - for (const num of nums) { - m = Math.max(m, num); - } - // 2. 统计各数字的出现次数 - // counter[num] 代表 num 的出现次数 - const counter = new Array(m + 1).fill(0); - for (const num of nums) { - counter[num]++; - } - // 3. 遍历 counter ,将各元素填入原数组 nums - let i = 0; - for (let num = 0; num < m + 1; num++) { - for (let j = 0; j < counter[num]; j++, i++) { - nums[i] = num; - } - } - } + [class]{}-[func]{countingSortNaive} ``` === "TS" ```typescript title="counting_sort.ts" - /* 计数排序 */ - // 简单实现,无法用于排序对象 - function countingSortNaive(nums: number[]): void { - // 1. 统计数组最大元素 m - let m = 0; - for (const num of nums) { - m = Math.max(m, num); - } - // 2. 统计各数字的出现次数 - // counter[num] 代表 num 的出现次数 - const counter: number[] = new Array(m + 1).fill(0); - for (const num of nums) { - counter[num]++; - } - // 3. 遍历 counter ,将各元素填入原数组 nums - let i = 0; - for (let num = 0; num < m + 1; num++) { - for (let j = 0; j < counter[num]; j++, i++) { - nums[i] = num; - } - } - } + [class]{}-[func]{countingSortNaive} ``` === "Dart" ```dart title="counting_sort.dart" - /* 计数排序 */ - // 简单实现,无法用于排序对象 - void countingSortNaive(List nums) { - // 1. 统计数组最大元素 m - int m = 0; - for (int _num in nums) { - m = max(m, _num); - } - // 2. 统计各数字的出现次数 - // counter[_num] 代表 _num 的出现次数 - List counter = List.filled(m + 1, 0); - for (int _num in nums) { - counter[_num]++; - } - // 3. 遍历 counter ,将各元素填入原数组 nums - int i = 0; - for (int _num = 0; _num < m + 1; _num++) { - for (int j = 0; j < counter[_num]; j++, i++) { - nums[i] = _num; - } - } - } + [class]{}-[func]{countingSortNaive} ``` === "Rust" ```rust title="counting_sort.rs" - /* 计数排序 */ - // 简单实现,无法用于排序对象 - fn counting_sort_naive(nums: &mut [i32]) { - // 1. 统计数组最大元素 m - let m = *nums.into_iter().max().unwrap(); - // 2. 统计各数字的出现次数 - // counter[num] 代表 num 的出现次数 - let mut counter = vec![0; m as usize + 1]; - for &num in &*nums { - counter[num as usize] += 1; - } - // 3. 遍历 counter ,将各元素填入原数组 nums - let mut i = 0; - for num in 0..m + 1 { - for _ in 0..counter[num as usize] { - nums[i] = num; - i += 1; - } - } - } + [class]{}-[func]{counting_sort_naive} ``` === "C" ```c title="counting_sort.c" - /* 计数排序 */ - // 简单实现,无法用于排序对象 - void countingSortNaive(int nums[], int size) { - // 1. 统计数组最大元素 m - int m = 0; - for (int i = 0; i < size; i++) { - if (nums[i] > m) { - m = nums[i]; - } - } - // 2. 统计各数字的出现次数 - // counter[num] 代表 num 的出现次数 - int *counter = calloc(m + 1, sizeof(int)); - for (int i = 0; i < size; i++) { - counter[nums[i]]++; - } - // 3. 遍历 counter ,将各元素填入原数组 nums - int i = 0; - for (int num = 0; num < m + 1; num++) { - for (int j = 0; j < counter[num]; j++, i++) { - nums[i] = num; - } - } - // 4. 释放内存 - free(counter); - } + [class]{}-[func]{countingSortNaive} ``` === "Kotlin" ```kotlin title="counting_sort.kt" - /* 计数排序 */ - // 简单实现,无法用于排序对象 - fun countingSortNaive(nums: IntArray) { - // 1. 统计数组最大元素 m - var m = 0 - for (num in nums) { - m = max(m, num) - } - // 2. 统计各数字的出现次数 - // counter[num] 代表 num 的出现次数 - val counter = IntArray(m + 1) - for (num in nums) { - counter[num]++ - } - // 3. 遍历 counter ,将各元素填入原数组 nums - var i = 0 - for (num in 0.. - - !!! note "Connection between counting sort and bucket sort" From the perspective of bucket sort, we can consider each index of the counting array `counter` in counting sort as a bucket, and the process of counting as distributing elements into the corresponding buckets. Essentially, counting sort is a special case of bucket sort for integer data. @@ -433,28 +195,28 @@ The implementation code of counting sort is shown below: ```python title="counting_sort.py" def counting_sort(nums: list[int]): - """计数排序""" - # 完整实现,可排序对象,并且是稳定排序 - # 1. 统计数组最大元素 m + """Counting sort""" + # Complete implementation, can sort objects and is a stable sort + # 1. Count the maximum element m in the array m = max(nums) - # 2. 统计各数字的出现次数 - # counter[num] 代表 num 的出现次数 + # 2. Count the occurrence of each digit + # counter[num] represents the occurrence of num counter = [0] * (m + 1) for num in nums: counter[num] += 1 - # 3. 求 counter 的前缀和,将“出现次数”转换为“尾索引” - # 即 counter[num]-1 是 num 在 res 中最后一次出现的索引 + # 3. Calculate the prefix sum of counter, converting "occurrence count" to "tail index" + # counter[num]-1 is the last index where num appears in res for i in range(m): counter[i + 1] += counter[i] - # 4. 倒序遍历 nums ,将各元素填入结果数组 res - # 初始化数组 res 用于记录结果 + # 4. Traverse nums in reverse order, placing each element into the result array res + # Initialize the array res to record results n = len(nums) res = [0] * n for i in range(n - 1, -1, -1): num = nums[i] - res[counter[num] - 1] = num # 将 num 放置到对应索引处 - counter[num] -= 1 # 令前缀和自减 1 ,得到下次放置 num 的索引 - # 使用结果数组 res 覆盖原数组 nums + res[counter[num] - 1] = num # Place num at the corresponding index + counter[num] -= 1 # Decrement the prefix sum by 1, getting the next index to place num + # Use result array res to overwrite the original array nums for i in range(n): nums[i] = res[i] ``` @@ -462,71 +224,41 @@ The implementation code of counting sort is shown below: === "C++" ```cpp title="counting_sort.cpp" - /* 计数排序 */ - // 完整实现,可排序对象,并且是稳定排序 - void countingSort(vector &nums) { - // 1. 统计数组最大元素 m - int m = 0; - for (int num : nums) { - m = max(m, num); - } - // 2. 统计各数字的出现次数 - // counter[num] 代表 num 的出现次数 - vector counter(m + 1, 0); - for (int num : nums) { - counter[num]++; - } - // 3. 求 counter 的前缀和,将“出现次数”转换为“尾索引” - // 即 counter[num]-1 是 num 在 res 中最后一次出现的索引 - for (int i = 0; i < m; i++) { - counter[i + 1] += counter[i]; - } - // 4. 倒序遍历 nums ,将各元素填入结果数组 res - // 初始化数组 res 用于记录结果 - int n = nums.size(); - vector res(n); - for (int i = n - 1; i >= 0; i--) { - int num = nums[i]; - res[counter[num] - 1] = num; // 将 num 放置到对应索引处 - counter[num]--; // 令前缀和自减 1 ,得到下次放置 num 的索引 - } - // 使用结果数组 res 覆盖原数组 nums - nums = res; - } + [class]{}-[func]{countingSort} ``` === "Java" ```java title="counting_sort.java" - /* 计数排序 */ - // 完整实现,可排序对象,并且是稳定排序 + /* Counting sort */ + // Complete implementation, can sort objects and is a stable sort void countingSort(int[] nums) { - // 1. 统计数组最大元素 m + // 1. Count the maximum element m in the array int m = 0; for (int num : nums) { m = Math.max(m, num); } - // 2. 统计各数字的出现次数 - // counter[num] 代表 num 的出现次数 + // 2. Count the occurrence of each digit + // counter[num] represents the occurrence of num int[] counter = new int[m + 1]; for (int num : nums) { counter[num]++; } - // 3. 求 counter 的前缀和,将“出现次数”转换为“尾索引” - // 即 counter[num]-1 是 num 在 res 中最后一次出现的索引 + // 3. Calculate the prefix sum of counter, converting "occurrence count" to "tail index" + // counter[num]-1 is the last index where num appears in res for (int i = 0; i < m; i++) { counter[i + 1] += counter[i]; } - // 4. 倒序遍历 nums ,将各元素填入结果数组 res - // 初始化数组 res 用于记录结果 + // 4. Traverse nums in reverse order, placing each element into the result array res + // Initialize the array res to record results int n = nums.length; int[] res = new int[n]; for (int i = n - 1; i >= 0; i--) { int num = nums[i]; - res[counter[num] - 1] = num; // 将 num 放置到对应索引处 - counter[num]--; // 令前缀和自减 1 ,得到下次放置 num 的索引 + res[counter[num] - 1] = num; // Place num at the corresponding index + counter[num]--; // Decrement the prefix sum by 1, getting the next index to place num } - // 使用结果数组 res 覆盖原数组 nums + // Use result array res to overwrite the original array nums for (int i = 0; i < n; i++) { nums[i] = res[i]; } @@ -536,366 +268,61 @@ The implementation code of counting sort is shown below: === "C#" ```csharp title="counting_sort.cs" - /* 计数排序 */ - // 完整实现,可排序对象,并且是稳定排序 - void CountingSort(int[] nums) { - // 1. 统计数组最大元素 m - int m = 0; - foreach (int num in nums) { - m = Math.Max(m, num); - } - // 2. 统计各数字的出现次数 - // counter[num] 代表 num 的出现次数 - int[] counter = new int[m + 1]; - foreach (int num in nums) { - counter[num]++; - } - // 3. 求 counter 的前缀和,将“出现次数”转换为“尾索引” - // 即 counter[num]-1 是 num 在 res 中最后一次出现的索引 - for (int i = 0; i < m; i++) { - counter[i + 1] += counter[i]; - } - // 4. 倒序遍历 nums ,将各元素填入结果数组 res - // 初始化数组 res 用于记录结果 - int n = nums.Length; - int[] res = new int[n]; - for (int i = n - 1; i >= 0; i--) { - int num = nums[i]; - res[counter[num] - 1] = num; // 将 num 放置到对应索引处 - counter[num]--; // 令前缀和自减 1 ,得到下次放置 num 的索引 - } - // 使用结果数组 res 覆盖原数组 nums - for (int i = 0; i < n; i++) { - nums[i] = res[i]; - } - } + [class]{counting_sort}-[func]{CountingSort} ``` === "Go" ```go title="counting_sort.go" - /* 计数排序 */ - // 完整实现,可排序对象,并且是稳定排序 - func countingSort(nums []int) { - // 1. 统计数组最大元素 m - m := 0 - for _, num := range nums { - if num > m { - m = num - } - } - // 2. 统计各数字的出现次数 - // counter[num] 代表 num 的出现次数 - counter := make([]int, m+1) - for _, num := range nums { - counter[num]++ - } - // 3. 求 counter 的前缀和,将“出现次数”转换为“尾索引” - // 即 counter[num]-1 是 num 在 res 中最后一次出现的索引 - for i := 0; i < m; i++ { - counter[i+1] += counter[i] - } - // 4. 倒序遍历 nums ,将各元素填入结果数组 res - // 初始化数组 res 用于记录结果 - n := len(nums) - res := make([]int, n) - for i := n - 1; i >= 0; i-- { - num := nums[i] - // 将 num 放置到对应索引处 - res[counter[num]-1] = num - // 令前缀和自减 1 ,得到下次放置 num 的索引 - counter[num]-- - } - // 使用结果数组 res 覆盖原数组 nums - copy(nums, res) - } + [class]{}-[func]{countingSort} ``` === "Swift" ```swift title="counting_sort.swift" - /* 计数排序 */ - // 完整实现,可排序对象,并且是稳定排序 - func countingSort(nums: inout [Int]) { - // 1. 统计数组最大元素 m - let m = nums.max()! - // 2. 统计各数字的出现次数 - // counter[num] 代表 num 的出现次数 - var counter = Array(repeating: 0, count: m + 1) - for num in nums { - counter[num] += 1 - } - // 3. 求 counter 的前缀和,将“出现次数”转换为“尾索引” - // 即 counter[num]-1 是 num 在 res 中最后一次出现的索引 - for i in 0 ..< m { - counter[i + 1] += counter[i] - } - // 4. 倒序遍历 nums ,将各元素填入结果数组 res - // 初始化数组 res 用于记录结果 - var res = Array(repeating: 0, count: nums.count) - for i in nums.indices.reversed() { - let num = nums[i] - res[counter[num] - 1] = num // 将 num 放置到对应索引处 - counter[num] -= 1 // 令前缀和自减 1 ,得到下次放置 num 的索引 - } - // 使用结果数组 res 覆盖原数组 nums - for i in nums.indices { - nums[i] = res[i] - } - } + [class]{}-[func]{countingSort} ``` === "JS" ```javascript title="counting_sort.js" - /* 计数排序 */ - // 完整实现,可排序对象,并且是稳定排序 - function countingSort(nums) { - // 1. 统计数组最大元素 m - let m = 0; - for (const num of nums) { - m = Math.max(m, num); - } - // 2. 统计各数字的出现次数 - // counter[num] 代表 num 的出现次数 - const counter = new Array(m + 1).fill(0); - for (const num of nums) { - counter[num]++; - } - // 3. 求 counter 的前缀和,将“出现次数”转换为“尾索引” - // 即 counter[num]-1 是 num 在 res 中最后一次出现的索引 - for (let i = 0; i < m; i++) { - counter[i + 1] += counter[i]; - } - // 4. 倒序遍历 nums ,将各元素填入结果数组 res - // 初始化数组 res 用于记录结果 - const n = nums.length; - const res = new Array(n); - for (let i = n - 1; i >= 0; i--) { - const num = nums[i]; - res[counter[num] - 1] = num; // 将 num 放置到对应索引处 - counter[num]--; // 令前缀和自减 1 ,得到下次放置 num 的索引 - } - // 使用结果数组 res 覆盖原数组 nums - for (let i = 0; i < n; i++) { - nums[i] = res[i]; - } - } + [class]{}-[func]{countingSort} ``` === "TS" ```typescript title="counting_sort.ts" - /* 计数排序 */ - // 完整实现,可排序对象,并且是稳定排序 - function countingSort(nums: number[]): void { - // 1. 统计数组最大元素 m - let m = 0; - for (const num of nums) { - m = Math.max(m, num); - } - // 2. 统计各数字的出现次数 - // counter[num] 代表 num 的出现次数 - const counter: number[] = new Array(m + 1).fill(0); - for (const num of nums) { - counter[num]++; - } - // 3. 求 counter 的前缀和,将“出现次数”转换为“尾索引” - // 即 counter[num]-1 是 num 在 res 中最后一次出现的索引 - for (let i = 0; i < m; i++) { - counter[i + 1] += counter[i]; - } - // 4. 倒序遍历 nums ,将各元素填入结果数组 res - // 初始化数组 res 用于记录结果 - const n = nums.length; - const res: number[] = new Array(n); - for (let i = n - 1; i >= 0; i--) { - const num = nums[i]; - res[counter[num] - 1] = num; // 将 num 放置到对应索引处 - counter[num]--; // 令前缀和自减 1 ,得到下次放置 num 的索引 - } - // 使用结果数组 res 覆盖原数组 nums - for (let i = 0; i < n; i++) { - nums[i] = res[i]; - } - } + [class]{}-[func]{countingSort} ``` === "Dart" ```dart title="counting_sort.dart" - /* 计数排序 */ - // 完整实现,可排序对象,并且是稳定排序 - void countingSort(List nums) { - // 1. 统计数组最大元素 m - int m = 0; - for (int _num in nums) { - m = max(m, _num); - } - // 2. 统计各数字的出现次数 - // counter[_num] 代表 _num 的出现次数 - List counter = List.filled(m + 1, 0); - for (int _num in nums) { - counter[_num]++; - } - // 3. 求 counter 的前缀和,将“出现次数”转换为“尾索引” - // 即 counter[_num]-1 是 _num 在 res 中最后一次出现的索引 - for (int i = 0; i < m; i++) { - counter[i + 1] += counter[i]; - } - // 4. 倒序遍历 nums ,将各元素填入结果数组 res - // 初始化数组 res 用于记录结果 - int n = nums.length; - List res = List.filled(n, 0); - for (int i = n - 1; i >= 0; i--) { - int _num = nums[i]; - res[counter[_num] - 1] = _num; // 将 _num 放置到对应索引处 - counter[_num]--; // 令前缀和自减 1 ,得到下次放置 _num 的索引 - } - // 使用结果数组 res 覆盖原数组 nums - nums.setAll(0, res); - } + [class]{}-[func]{countingSort} ``` === "Rust" ```rust title="counting_sort.rs" - /* 计数排序 */ - // 完整实现,可排序对象,并且是稳定排序 - fn counting_sort(nums: &mut [i32]) { - // 1. 统计数组最大元素 m - let m = *nums.into_iter().max().unwrap(); - // 2. 统计各数字的出现次数 - // counter[num] 代表 num 的出现次数 - let mut counter = vec![0; m as usize + 1]; - for &num in &*nums { - counter[num as usize] += 1; - } - // 3. 求 counter 的前缀和,将“出现次数”转换为“尾索引” - // 即 counter[num]-1 是 num 在 res 中最后一次出现的索引 - for i in 0..m as usize { - counter[i + 1] += counter[i]; - } - // 4. 倒序遍历 nums ,将各元素填入结果数组 res - // 初始化数组 res 用于记录结果 - let n = nums.len(); - let mut res = vec![0; n]; - for i in (0..n).rev() { - let num = nums[i]; - res[counter[num as usize] - 1] = num; // 将 num 放置到对应索引处 - counter[num as usize] -= 1; // 令前缀和自减 1 ,得到下次放置 num 的索引 - } - // 使用结果数组 res 覆盖原数组 nums - for i in 0..n { - nums[i] = res[i]; - } - } + [class]{}-[func]{counting_sort} ``` === "C" ```c title="counting_sort.c" - /* 计数排序 */ - // 完整实现,可排序对象,并且是稳定排序 - void countingSort(int nums[], int size) { - // 1. 统计数组最大元素 m - int m = 0; - for (int i = 0; i < size; i++) { - if (nums[i] > m) { - m = nums[i]; - } - } - // 2. 统计各数字的出现次数 - // counter[num] 代表 num 的出现次数 - int *counter = calloc(m, sizeof(int)); - for (int i = 0; i < size; i++) { - counter[nums[i]]++; - } - // 3. 求 counter 的前缀和,将“出现次数”转换为“尾索引” - // 即 counter[num]-1 是 num 在 res 中最后一次出现的索引 - for (int i = 0; i < m; i++) { - counter[i + 1] += counter[i]; - } - // 4. 倒序遍历 nums ,将各元素填入结果数组 res - // 初始化数组 res 用于记录结果 - int *res = malloc(sizeof(int) * size); - for (int i = size - 1; i >= 0; i--) { - int num = nums[i]; - res[counter[num] - 1] = num; // 将 num 放置到对应索引处 - counter[num]--; // 令前缀和自减 1 ,得到下次放置 num 的索引 - } - // 使用结果数组 res 覆盖原数组 nums - memcpy(nums, res, size * sizeof(int)); - // 5. 释放内存 - free(counter); - } + [class]{}-[func]{countingSort} ``` === "Kotlin" ```kotlin title="counting_sort.kt" - /* 计数排序 */ - // 完整实现,可排序对象,并且是稳定排序 - fun countingSort(nums: IntArray) { - // 1. 统计数组最大元素 m - var m = 0 - for (num in nums) { - m = max(m, num) - } - // 2. 统计各数字的出现次数 - // counter[num] 代表 num 的出现次数 - val counter = IntArray(m + 1) - for (num in nums) { - counter[num]++ - } - // 3. 求 counter 的前缀和,将“出现次数”转换为“尾索引” - // 即 counter[num]-1 是 num 在 res 中最后一次出现的索引 - for (i in 0.. - - ## 11.9.3   Algorithm characteristics - **Time complexity is $O(n + m)$, non-adaptive sort**: Involves traversing `nums` and `counter`, both using linear time. Generally, $n \gg m$, and the time complexity tends towards $O(n)$. diff --git a/en/docs/chapter_sorting/heap_sort.md b/en/docs/chapter_sorting/heap_sort.md index 4ddab17f4..d018a72a0 100644 --- a/en/docs/chapter_sorting/heap_sort.md +++ b/en/docs/chapter_sorting/heap_sort.md @@ -72,9 +72,9 @@ In the code implementation, we used the sift-down function `sift_down()` from th ```python title="heap_sort.py" def sift_down(nums: list[int], n: int, i: int): - """堆的长度为 n ,从节点 i 开始,从顶至底堆化""" + """Heap length is n, start heapifying node i, from top to bottom""" while True: - # 判断节点 i, l, r 中值最大的节点,记为 ma + # Determine the largest node among i, l, r, noted as ma l = 2 * i + 1 r = 2 * i + 2 ma = i @@ -82,75 +82,42 @@ In the code implementation, we used the sift-down function `sift_down()` from th ma = l if r < n and nums[r] > nums[ma]: ma = r - # 若节点 i 最大或索引 l, r 越界,则无须继续堆化,跳出 + # If node i is the largest or indices l, r are out of bounds, no further heapification needed, break if ma == i: break - # 交换两节点 + # Swap two nodes nums[i], nums[ma] = nums[ma], nums[i] - # 循环向下堆化 + # Loop downwards heapification i = ma def heap_sort(nums: list[int]): - """堆排序""" - # 建堆操作:堆化除叶节点以外的其他所有节点 + """Heap sort""" + # Build heap operation: heapify all nodes except leaves for i in range(len(nums) // 2 - 1, -1, -1): sift_down(nums, len(nums), i) - # 从堆中提取最大元素,循环 n-1 轮 + # Extract the largest element from the heap and repeat for n-1 rounds for i in range(len(nums) - 1, 0, -1): - # 交换根节点与最右叶节点(交换首元素与尾元素) + # Swap the root node with the rightmost leaf node (swap the first element with the last element) nums[0], nums[i] = nums[i], nums[0] - # 以根节点为起点,从顶至底进行堆化 + # Start heapifying the root node, from top to bottom sift_down(nums, i, 0) ``` === "C++" ```cpp title="heap_sort.cpp" - /* 堆的长度为 n ,从节点 i 开始,从顶至底堆化 */ - void siftDown(vector &nums, int n, int i) { - while (true) { - // 判断节点 i, l, r 中值最大的节点,记为 ma - int l = 2 * i + 1; - int r = 2 * i + 2; - int ma = i; - if (l < n && nums[l] > nums[ma]) - ma = l; - if (r < n && nums[r] > nums[ma]) - ma = r; - // 若节点 i 最大或索引 l, r 越界,则无须继续堆化,跳出 - if (ma == i) { - break; - } - // 交换两节点 - swap(nums[i], nums[ma]); - // 循环向下堆化 - i = ma; - } - } + [class]{}-[func]{siftDown} - /* 堆排序 */ - void heapSort(vector &nums) { - // 建堆操作:堆化除叶节点以外的其他所有节点 - for (int i = nums.size() / 2 - 1; i >= 0; --i) { - siftDown(nums, nums.size(), i); - } - // 从堆中提取最大元素,循环 n-1 轮 - for (int i = nums.size() - 1; i > 0; --i) { - // 交换根节点与最右叶节点(交换首元素与尾元素) - swap(nums[0], nums[i]); - // 以根节点为起点,从顶至底进行堆化 - siftDown(nums, i, 0); - } - } + [class]{}-[func]{heapSort} ``` === "Java" ```java title="heap_sort.java" - /* 堆的长度为 n ,从节点 i 开始,从顶至底堆化 */ + /* Heap length is n, start heapifying node i, from top to bottom */ void siftDown(int[] nums, int n, int i) { while (true) { - // 判断节点 i, l, r 中值最大的节点,记为 ma + // Determine the largest node among i, l, r, noted as ma int l = 2 * i + 1; int r = 2 * i + 2; int ma = i; @@ -158,31 +125,31 @@ In the code implementation, we used the sift-down function `sift_down()` from th ma = l; if (r < n && nums[r] > nums[ma]) ma = r; - // 若节点 i 最大或索引 l, r 越界,则无须继续堆化,跳出 + // If node i is the largest or indices l, r are out of bounds, no further heapification needed, break if (ma == i) break; - // 交换两节点 + // Swap two nodes int temp = nums[i]; nums[i] = nums[ma]; nums[ma] = temp; - // 循环向下堆化 + // Loop downwards heapification i = ma; } } - /* 堆排序 */ + /* Heap sort */ void heapSort(int[] nums) { - // 建堆操作:堆化除叶节点以外的其他所有节点 + // Build heap operation: heapify all nodes except leaves for (int i = nums.length / 2 - 1; i >= 0; i--) { siftDown(nums, nums.length, i); } - // 从堆中提取最大元素,循环 n-1 轮 + // Extract the largest element from the heap and repeat for n-1 rounds for (int i = nums.length - 1; i > 0; i--) { - // 交换根节点与最右叶节点(交换首元素与尾元素) + // Swap the root node with the rightmost leaf node (swap the first element with the last element) int tmp = nums[0]; nums[0] = nums[i]; nums[i] = tmp; - // 以根节点为起点,从顶至底进行堆化 + // Start heapifying the root node, from top to bottom siftDown(nums, i, 0); } } @@ -191,429 +158,81 @@ In the code implementation, we used the sift-down function `sift_down()` from th === "C#" ```csharp title="heap_sort.cs" - /* 堆的长度为 n ,从节点 i 开始,从顶至底堆化 */ - void SiftDown(int[] nums, int n, int i) { - while (true) { - // 判断节点 i, l, r 中值最大的节点,记为 ma - int l = 2 * i + 1; - int r = 2 * i + 2; - int ma = i; - if (l < n && nums[l] > nums[ma]) - ma = l; - if (r < n && nums[r] > nums[ma]) - ma = r; - // 若节点 i 最大或索引 l, r 越界,则无须继续堆化,跳出 - if (ma == i) - break; - // 交换两节点 - (nums[ma], nums[i]) = (nums[i], nums[ma]); - // 循环向下堆化 - i = ma; - } - } + [class]{heap_sort}-[func]{SiftDown} - /* 堆排序 */ - void HeapSort(int[] nums) { - // 建堆操作:堆化除叶节点以外的其他所有节点 - for (int i = nums.Length / 2 - 1; i >= 0; i--) { - SiftDown(nums, nums.Length, i); - } - // 从堆中提取最大元素,循环 n-1 轮 - for (int i = nums.Length - 1; i > 0; i--) { - // 交换根节点与最右叶节点(交换首元素与尾元素) - (nums[i], nums[0]) = (nums[0], nums[i]); - // 以根节点为起点,从顶至底进行堆化 - SiftDown(nums, i, 0); - } - } + [class]{heap_sort}-[func]{HeapSort} ``` === "Go" ```go title="heap_sort.go" - /* 堆的长度为 n ,从节点 i 开始,从顶至底堆化 */ - func siftDown(nums *[]int, n, i int) { - for true { - // 判断节点 i, l, r 中值最大的节点,记为 ma - l := 2*i + 1 - r := 2*i + 2 - ma := i - if l < n && (*nums)[l] > (*nums)[ma] { - ma = l - } - if r < n && (*nums)[r] > (*nums)[ma] { - ma = r - } - // 若节点 i 最大或索引 l, r 越界,则无须继续堆化,跳出 - if ma == i { - break - } - // 交换两节点 - (*nums)[i], (*nums)[ma] = (*nums)[ma], (*nums)[i] - // 循环向下堆化 - i = ma - } - } + [class]{}-[func]{siftDown} - /* 堆排序 */ - func heapSort(nums *[]int) { - // 建堆操作:堆化除叶节点以外的其他所有节点 - for i := len(*nums)/2 - 1; i >= 0; i-- { - siftDown(nums, len(*nums), i) - } - // 从堆中提取最大元素,循环 n-1 轮 - for i := len(*nums) - 1; i > 0; i-- { - // 交换根节点与最右叶节点(交换首元素与尾元素) - (*nums)[0], (*nums)[i] = (*nums)[i], (*nums)[0] - // 以根节点为起点,从顶至底进行堆化 - siftDown(nums, i, 0) - } - } + [class]{}-[func]{heapSort} ``` === "Swift" ```swift title="heap_sort.swift" - /* 堆的长度为 n ,从节点 i 开始,从顶至底堆化 */ - func siftDown(nums: inout [Int], n: Int, i: Int) { - var i = i - while true { - // 判断节点 i, l, r 中值最大的节点,记为 ma - let l = 2 * i + 1 - let r = 2 * i + 2 - var ma = i - if l < n, nums[l] > nums[ma] { - ma = l - } - if r < n, nums[r] > nums[ma] { - ma = r - } - // 若节点 i 最大或索引 l, r 越界,则无须继续堆化,跳出 - if ma == i { - break - } - // 交换两节点 - nums.swapAt(i, ma) - // 循环向下堆化 - i = ma - } - } + [class]{}-[func]{siftDown} - /* 堆排序 */ - func heapSort(nums: inout [Int]) { - // 建堆操作:堆化除叶节点以外的其他所有节点 - for i in stride(from: nums.count / 2 - 1, through: 0, by: -1) { - siftDown(nums: &nums, n: nums.count, i: i) - } - // 从堆中提取最大元素,循环 n-1 轮 - for i in nums.indices.dropFirst().reversed() { - // 交换根节点与最右叶节点(交换首元素与尾元素) - nums.swapAt(0, i) - // 以根节点为起点,从顶至底进行堆化 - siftDown(nums: &nums, n: i, i: 0) - } - } + [class]{}-[func]{heapSort} ``` === "JS" ```javascript title="heap_sort.js" - /* 堆的长度为 n ,从节点 i 开始,从顶至底堆化 */ - function siftDown(nums, n, i) { - while (true) { - // 判断节点 i, l, r 中值最大的节点,记为 ma - let l = 2 * i + 1; - let r = 2 * i + 2; - let ma = i; - if (l < n && nums[l] > nums[ma]) { - ma = l; - } - if (r < n && nums[r] > nums[ma]) { - ma = r; - } - // 若节点 i 最大或索引 l, r 越界,则无须继续堆化,跳出 - if (ma === i) { - break; - } - // 交换两节点 - [nums[i], nums[ma]] = [nums[ma], nums[i]]; - // 循环向下堆化 - i = ma; - } - } + [class]{}-[func]{siftDown} - /* 堆排序 */ - function heapSort(nums) { - // 建堆操作:堆化除叶节点以外的其他所有节点 - for (let i = Math.floor(nums.length / 2) - 1; i >= 0; i--) { - siftDown(nums, nums.length, i); - } - // 从堆中提取最大元素,循环 n-1 轮 - for (let i = nums.length - 1; i > 0; i--) { - // 交换根节点与最右叶节点(交换首元素与尾元素) - [nums[0], nums[i]] = [nums[i], nums[0]]; - // 以根节点为起点,从顶至底进行堆化 - siftDown(nums, i, 0); - } - } + [class]{}-[func]{heapSort} ``` === "TS" ```typescript title="heap_sort.ts" - /* 堆的长度为 n ,从节点 i 开始,从顶至底堆化 */ - function siftDown(nums: number[], n: number, i: number): void { - while (true) { - // 判断节点 i, l, r 中值最大的节点,记为 ma - let l = 2 * i + 1; - let r = 2 * i + 2; - let ma = i; - if (l < n && nums[l] > nums[ma]) { - ma = l; - } - if (r < n && nums[r] > nums[ma]) { - ma = r; - } - // 若节点 i 最大或索引 l, r 越界,则无须继续堆化,跳出 - if (ma === i) { - break; - } - // 交换两节点 - [nums[i], nums[ma]] = [nums[ma], nums[i]]; - // 循环向下堆化 - i = ma; - } - } + [class]{}-[func]{siftDown} - /* 堆排序 */ - function heapSort(nums: number[]): void { - // 建堆操作:堆化除叶节点以外的其他所有节点 - for (let i = Math.floor(nums.length / 2) - 1; i >= 0; i--) { - siftDown(nums, nums.length, i); - } - // 从堆中提取最大元素,循环 n-1 轮 - for (let i = nums.length - 1; i > 0; i--) { - // 交换根节点与最右叶节点(交换首元素与尾元素) - [nums[0], nums[i]] = [nums[i], nums[0]]; - // 以根节点为起点,从顶至底进行堆化 - siftDown(nums, i, 0); - } - } + [class]{}-[func]{heapSort} ``` === "Dart" ```dart title="heap_sort.dart" - /* 堆的长度为 n ,从节点 i 开始,从顶至底堆化 */ - void siftDown(List nums, int n, int i) { - while (true) { - // 判断节点 i, l, r 中值最大的节点,记为 ma - int l = 2 * i + 1; - int r = 2 * i + 2; - int ma = i; - if (l < n && nums[l] > nums[ma]) ma = l; - if (r < n && nums[r] > nums[ma]) ma = r; - // 若节点 i 最大或索引 l, r 越界,则无须继续堆化,跳出 - if (ma == i) break; - // 交换两节点 - int temp = nums[i]; - nums[i] = nums[ma]; - nums[ma] = temp; - // 循环向下堆化 - i = ma; - } - } + [class]{}-[func]{siftDown} - /* 堆排序 */ - void heapSort(List nums) { - // 建堆操作:堆化除叶节点以外的其他所有节点 - for (int i = nums.length ~/ 2 - 1; i >= 0; i--) { - siftDown(nums, nums.length, i); - } - // 从堆中提取最大元素,循环 n-1 轮 - for (int i = nums.length - 1; i > 0; i--) { - // 交换根节点与最右叶节点(交换首元素与尾元素) - int tmp = nums[0]; - nums[0] = nums[i]; - nums[i] = tmp; - // 以根节点为起点,从顶至底进行堆化 - siftDown(nums, i, 0); - } - } + [class]{}-[func]{heapSort} ``` === "Rust" ```rust title="heap_sort.rs" - /* 堆的长度为 n ,从节点 i 开始,从顶至底堆化 */ - fn sift_down(nums: &mut [i32], n: usize, mut i: usize) { - loop { - // 判断节点 i, l, r 中值最大的节点,记为 ma - let l = 2 * i + 1; - let r = 2 * i + 2; - let mut ma = i; - if l < n && nums[l] > nums[ma] { - ma = l; - } - if r < n && nums[r] > nums[ma] { - ma = r; - } - // 若节点 i 最大或索引 l, r 越界,则无须继续堆化,跳出 - if ma == i { - break; - } - // 交换两节点 - let temp = nums[i]; - nums[i] = nums[ma]; - nums[ma] = temp; - // 循环向下堆化 - i = ma; - } - } + [class]{}-[func]{sift_down} - /* 堆排序 */ - fn heap_sort(nums: &mut [i32]) { - // 建堆操作:堆化除叶节点以外的其他所有节点 - for i in (0..=nums.len() / 2 - 1).rev() { - sift_down(nums, nums.len(), i); - } - // 从堆中提取最大元素,循环 n-1 轮 - for i in (1..=nums.len() - 1).rev() { - // 交换根节点与最右叶节点(交换首元素与尾元素) - let tmp = nums[0]; - nums[0] = nums[i]; - nums[i] = tmp; - // 以根节点为起点,从顶至底进行堆化 - sift_down(nums, i, 0); - } - } + [class]{}-[func]{heap_sort} ``` === "C" ```c title="heap_sort.c" - /* 堆的长度为 n ,从节点 i 开始,从顶至底堆化 */ - void siftDown(int nums[], int n, int i) { - while (1) { - // 判断节点 i, l, r 中值最大的节点,记为 ma - int l = 2 * i + 1; - int r = 2 * i + 2; - int ma = i; - if (l < n && nums[l] > nums[ma]) - ma = l; - if (r < n && nums[r] > nums[ma]) - ma = r; - // 若节点 i 最大或索引 l, r 越界,则无须继续堆化,跳出 - if (ma == i) { - break; - } - // 交换两节点 - int temp = nums[i]; - nums[i] = nums[ma]; - nums[ma] = temp; - // 循环向下堆化 - i = ma; - } - } + [class]{}-[func]{siftDown} - /* 堆排序 */ - void heapSort(int nums[], int n) { - // 建堆操作:堆化除叶节点以外的其他所有节点 - for (int i = n / 2 - 1; i >= 0; --i) { - siftDown(nums, n, i); - } - // 从堆中提取最大元素,循环 n-1 轮 - for (int i = n - 1; i > 0; --i) { - // 交换根节点与最右叶节点(交换首元素与尾元素) - int tmp = nums[0]; - nums[0] = nums[i]; - nums[i] = tmp; - // 以根节点为起点,从顶至底进行堆化 - siftDown(nums, i, 0); - } - } + [class]{}-[func]{heapSort} ``` === "Kotlin" ```kotlin title="heap_sort.kt" - /* 堆的长度为 n ,从节点 i 开始,从顶至底堆化 */ - fun siftDown(nums: IntArray, n: Int, li: Int) { - var i = li - while (true) { - // 判断节点 i, l, r 中值最大的节点,记为 ma - val l = 2 * i + 1 - val r = 2 * i + 2 - var ma = i - if (l < n && nums[l] > nums[ma]) - ma = l - if (r < n && nums[r] > nums[ma]) - ma = r - // 若节点 i 最大或索引 l, r 越界,则无须继续堆化,跳出 - if (ma == i) - break - // 交换两节点 - val temp = nums[i] - nums[i] = nums[ma] - nums[ma] = temp - // 循环向下堆化 - i = ma - } - } + [class]{}-[func]{siftDown} - /* 堆排序 */ - 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) { - // 交换根节点与最右叶节点(交换首元素与尾元素) - val temp = nums[0] - nums[0] = nums[i] - nums[i] = temp - // 以根节点为起点,从顶至底进行堆化 - siftDown(nums, i, 0) - } - } + [class]{}-[func]{heapSort} ``` === "Ruby" ```ruby title="heap_sort.rb" - ### 堆的长度为 n ,从节点 i 开始,从顶至底堆化 ### - def sift_down(nums, n, i) - while true - # 判断节点 i, l, r 中值最大的节点,记为 ma - l = 2 * i + 1 - r = 2 * i + 2 - ma = i - ma = l if l < n && nums[l] > nums[ma] - ma = r if r < n && nums[r] > nums[ma] - # 若节点 i 最大或索引 l, r 越界,则无须继续堆化,跳出 - break if ma == i - # 交换两节点 - nums[i], nums[ma] = nums[ma], nums[i] - # 循环向下堆化 - i = ma - end - end + [class]{}-[func]{sift_down} - ### 堆排序 ### - def heap_sort(nums) - # 建堆操作:堆化除叶节点以外的其他所有节点 - (nums.length / 2 - 1).downto(0) do |i| - sift_down(nums, nums.length, i) - end - # 从堆中提取最大元素,循环 n-1 轮 - (nums.length - 1).downto(1) do |i| - # 交换根节点与最右叶节点(交换首元素与尾元素) - nums[0], nums[i] = nums[i], nums[0] - # 以根节点为起点,从顶至底进行堆化 - sift_down(nums, i, 0) - end - end + [class]{}-[func]{heap_sort} ``` === "Zig" @@ -624,11 +243,6 @@ In the code implementation, we used the sift-down function `sift_down()` from th [class]{}-[func]{heapSort} ``` -??? pythontutor "Code Visualization" - -
- - ## 11.7.2   Algorithm characteristics - **Time complexity is $O(n \log n)$, non-adaptive sort**: The heap creation uses $O(n)$ time. Extracting the largest element from the heap takes $O(\log n)$ time, looping for $n - 1$ rounds. diff --git a/en/docs/chapter_sorting/insertion_sort.md b/en/docs/chapter_sorting/insertion_sort.md index 08e657b23..ef19a266c 100644 --- a/en/docs/chapter_sorting/insertion_sort.md +++ b/en/docs/chapter_sorting/insertion_sort.md @@ -33,50 +33,38 @@ Example code is as follows: ```python title="insertion_sort.py" def insertion_sort(nums: list[int]): - """插入排序""" - # 外循环:已排序区间为 [0, i-1] + """Insertion sort""" + # Outer loop: sorted range is [0, i-1] for i in range(1, len(nums)): base = nums[i] j = i - 1 - # 内循环:将 base 插入到已排序区间 [0, i-1] 中的正确位置 + # Inner loop: insert base into the correct position within the sorted range [0, i-1] while j >= 0 and nums[j] > base: - nums[j + 1] = nums[j] # 将 nums[j] 向右移动一位 + nums[j + 1] = nums[j] # Move nums[j] to the right by one position j -= 1 - nums[j + 1] = base # 将 base 赋值到正确位置 + nums[j + 1] = base # Assign base to the correct position ``` === "C++" ```cpp title="insertion_sort.cpp" - /* 插入排序 */ - void insertionSort(vector &nums) { - // 外循环:已排序区间为 [0, i-1] - for (int i = 1; i < nums.size(); i++) { - int base = nums[i], j = i - 1; - // 内循环:将 base 插入到已排序区间 [0, i-1] 中的正确位置 - while (j >= 0 && nums[j] > base) { - nums[j + 1] = nums[j]; // 将 nums[j] 向右移动一位 - j--; - } - nums[j + 1] = base; // 将 base 赋值到正确位置 - } - } + [class]{}-[func]{insertionSort} ``` === "Java" ```java title="insertion_sort.java" - /* 插入排序 */ + /* Insertion sort */ void insertionSort(int[] nums) { - // 外循环:已排序区间为 [0, i-1] + // Outer loop: sorted range is [0, i-1] for (int i = 1; i < nums.length; i++) { int base = nums[i], j = i - 1; - // 内循环:将 base 插入到已排序区间 [0, i-1] 中的正确位置 + // Inner loop: insert base into the correct position within the sorted range [0, i-1] while (j >= 0 && nums[j] > base) { - nums[j + 1] = nums[j]; // 将 nums[j] 向右移动一位 + nums[j + 1] = nums[j]; // Move nums[j] to the right by one position j--; } - nums[j + 1] = base; // 将 base 赋值到正确位置 + nums[j + 1] = base; // Assign base to the correct position } } ``` @@ -84,216 +72,69 @@ Example code is as follows: === "C#" ```csharp title="insertion_sort.cs" - /* 插入排序 */ - void InsertionSort(int[] nums) { - // 外循环:已排序区间为 [0, i-1] - for (int i = 1; i < nums.Length; i++) { - int bas = nums[i], j = i - 1; - // 内循环:将 base 插入到已排序区间 [0, i-1] 中的正确位置 - while (j >= 0 && nums[j] > bas) { - nums[j + 1] = nums[j]; // 将 nums[j] 向右移动一位 - j--; - } - nums[j + 1] = bas; // 将 base 赋值到正确位置 - } - } + [class]{insertion_sort}-[func]{InsertionSort} ``` === "Go" ```go title="insertion_sort.go" - /* 插入排序 */ - func insertionSort(nums []int) { - // 外循环:已排序区间为 [0, i-1] - for i := 1; i < len(nums); i++ { - base := nums[i] - j := i - 1 - // 内循环:将 base 插入到已排序区间 [0, i-1] 中的正确位置 - for j >= 0 && nums[j] > base { - nums[j+1] = nums[j] // 将 nums[j] 向右移动一位 - j-- - } - nums[j+1] = base // 将 base 赋值到正确位置 - } - } + [class]{}-[func]{insertionSort} ``` === "Swift" ```swift title="insertion_sort.swift" - /* 插入排序 */ - func insertionSort(nums: inout [Int]) { - // 外循环:已排序区间为 [0, i-1] - for i in nums.indices.dropFirst() { - let base = nums[i] - var j = i - 1 - // 内循环:将 base 插入到已排序区间 [0, i-1] 中的正确位置 - while j >= 0, nums[j] > base { - nums[j + 1] = nums[j] // 将 nums[j] 向右移动一位 - j -= 1 - } - nums[j + 1] = base // 将 base 赋值到正确位置 - } - } + [class]{}-[func]{insertionSort} ``` === "JS" ```javascript title="insertion_sort.js" - /* 插入排序 */ - function insertionSort(nums) { - // 外循环:已排序区间为 [0, i-1] - for (let i = 1; i < nums.length; i++) { - let base = nums[i], - j = i - 1; - // 内循环:将 base 插入到已排序区间 [0, i-1] 中的正确位置 - while (j >= 0 && nums[j] > base) { - nums[j + 1] = nums[j]; // 将 nums[j] 向右移动一位 - j--; - } - nums[j + 1] = base; // 将 base 赋值到正确位置 - } - } + [class]{}-[func]{insertionSort} ``` === "TS" ```typescript title="insertion_sort.ts" - /* 插入排序 */ - function insertionSort(nums: number[]): void { - // 外循环:已排序区间为 [0, i-1] - for (let i = 1; i < nums.length; i++) { - const base = nums[i]; - let j = i - 1; - // 内循环:将 base 插入到已排序区间 [0, i-1] 中的正确位置 - while (j >= 0 && nums[j] > base) { - nums[j + 1] = nums[j]; // 将 nums[j] 向右移动一位 - j--; - } - nums[j + 1] = base; // 将 base 赋值到正确位置 - } - } + [class]{}-[func]{insertionSort} ``` === "Dart" ```dart title="insertion_sort.dart" - /* 插入排序 */ - void insertionSort(List nums) { - // 外循环:已排序区间为 [0, i-1] - for (int i = 1; i < nums.length; i++) { - int base = nums[i], j = i - 1; - // 内循环:将 base 插入到已排序区间 [0, i-1] 中的正确位置 - while (j >= 0 && nums[j] > base) { - nums[j + 1] = nums[j]; // 将 nums[j] 向右移动一位 - j--; - } - nums[j + 1] = base; // 将 base 赋值到正确位置 - } - } + [class]{}-[func]{insertionSort} ``` === "Rust" ```rust title="insertion_sort.rs" - /* 插入排序 */ - fn insertion_sort(nums: &mut [i32]) { - // 外循环:已排序区间为 [0, i-1] - for i in 1..nums.len() { - let (base, mut j) = (nums[i], (i - 1) as i32); - // 内循环:将 base 插入到已排序区间 [0, i-1] 中的正确位置 - while j >= 0 && nums[j as usize] > base { - nums[(j + 1) as usize] = nums[j as usize]; // 将 nums[j] 向右移动一位 - j -= 1; - } - nums[(j + 1) as usize] = base; // 将 base 赋值到正确位置 - } - } + [class]{}-[func]{insertion_sort} ``` === "C" ```c title="insertion_sort.c" - /* 插入排序 */ - void insertionSort(int nums[], int size) { - // 外循环:已排序区间为 [0, i-1] - for (int i = 1; i < size; i++) { - int base = nums[i], j = i - 1; - // 内循环:将 base 插入到已排序区间 [0, i-1] 中的正确位置 - while (j >= 0 && nums[j] > base) { - // 将 nums[j] 向右移动一位 - nums[j + 1] = nums[j]; - j--; - } - // 将 base 赋值到正确位置 - nums[j + 1] = base; - } - } + [class]{}-[func]{insertionSort} ``` === "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 插入到已排序区间 [0, i-1] 中的正确位置 - while (j >= 0 && nums[j] > base) { - nums[j + 1] = nums[j] // 将 nums[j] 向右移动一位 - j-- - } - nums[j + 1] = base // 将 base 赋值到正确位置 - } - } + [class]{}-[func]{insertionSort} ``` === "Ruby" ```ruby title="insertion_sort.rb" - ### 插入排序 ### - def insertion_sort(nums) - n = nums.length - # 外循环:已排序区间为 [0, i-1] - for i in 1...n - base = nums[i] - j = i - 1 - # 内循环:将 base 插入到已排序区间 [0, i-1] 中的正确位置 - while j >= 0 && nums[j] > base - nums[j + 1] = nums[j] # 将 nums[j] 向右移动一位 - j -= 1 - end - nums[j + 1] = base # 将 base 赋值到正确位置 - end - end + [class]{}-[func]{insertion_sort} ``` === "Zig" ```zig title="insertion_sort.zig" - // 插入排序 - fn insertionSort(nums: []i32) void { - // 外循环:已排序区间为 [0, i-1] - var i: usize = 1; - while (i < nums.len) : (i += 1) { - var base = nums[i]; - var j: usize = i; - // 内循环:将 base 插入到已排序区间 [0, i-1] 中的正确位置 - while (j >= 1 and nums[j - 1] > base) : (j -= 1) { - nums[j] = nums[j - 1]; // 将 nums[j] 向右移动一位 - } - nums[j] = base; // 将 base 赋值到正确位置 - } - } + [class]{}-[func]{insertionSort} ``` -??? pythontutor "Code Visualization" - -
- - ## 11.4.2   Algorithm characteristics - **Time complexity is $O(n^2)$, adaptive sorting**: In the worst case, each insertion operation requires $n - 1$, $n-2$, ..., $2$, $1$ loops, summing up to $(n - 1) n / 2$, thus the time complexity is $O(n^2)$. In the case of ordered data, the insertion operation will terminate early. When the input array is completely ordered, insertion sort achieves the best time complexity of $O(n)$. diff --git a/en/docs/chapter_sorting/merge_sort.md b/en/docs/chapter_sorting/merge_sort.md index f37e78189..876464738 100644 --- a/en/docs/chapter_sorting/merge_sort.md +++ b/en/docs/chapter_sorting/merge_sort.md @@ -65,13 +65,13 @@ The implementation of merge sort is shown in the following code. Note that the i ```python title="merge_sort.py" def merge(nums: list[int], left: int, mid: int, right: int): - """合并左子数组和右子数组""" - # 左子数组区间为 [left, mid], 右子数组区间为 [mid+1, right] - # 创建一个临时数组 tmp ,用于存放合并后的结果 + """Merge left subarray and right subarray""" + # Left subarray interval is [left, mid], right subarray interval is [mid+1, right] + # Create a temporary array tmp to store the merged results tmp = [0] * (right - left + 1) - # 初始化左子数组和右子数组的起始索引 + # Initialize the start indices of the left and right subarrays i, j, k = left, mid + 1, 0 - # 当左右子数组都还有元素时,进行比较并将较小的元素复制到临时数组中 + # While both subarrays still have elements, compare and copy the smaller element into the temporary array while i <= mid and j <= right: if nums[i] <= nums[j]: tmp[k] = nums[i] @@ -80,7 +80,7 @@ The implementation of merge sort is shown in the following code. Note that the i tmp[k] = nums[j] j += 1 k += 1 - # 将左子数组和右子数组的剩余元素复制到临时数组中 + # Copy the remaining elements of the left and right subarrays into the temporary array while i <= mid: tmp[k] = nums[i] i += 1 @@ -89,107 +89,71 @@ The implementation of merge sort is shown in the following code. Note that the i tmp[k] = nums[j] j += 1 k += 1 - # 将临时数组 tmp 中的元素复制回原数组 nums 的对应区间 + # Copy the elements from the temporary array tmp back to the original array nums at the corresponding interval for k in range(0, len(tmp)): nums[left + k] = tmp[k] def merge_sort(nums: list[int], left: int, right: int): - """归并排序""" - # 终止条件 + """Merge sort""" + # Termination condition if left >= right: - return # 当子数组长度为 1 时终止递归 - # 划分阶段 - mid = (left + right) // 2 # 计算中点 - merge_sort(nums, left, mid) # 递归左子数组 - merge_sort(nums, mid + 1, right) # 递归右子数组 - # 合并阶段 + return # Terminate recursion when subarray length is 1 + # Partition stage + mid = (left + right) // 2 # Calculate midpoint + merge_sort(nums, left, mid) # Recursively process the left subarray + merge_sort(nums, mid + 1, right) # Recursively process the right subarray + # Merge stage merge(nums, left, mid, right) ``` === "C++" ```cpp title="merge_sort.cpp" - /* 合并左子数组和右子数组 */ - void merge(vector &nums, int left, int mid, int right) { - // 左子数组区间为 [left, mid], 右子数组区间为 [mid+1, right] - // 创建一个临时数组 tmp ,用于存放合并后的结果 - vector tmp(right - left + 1); - // 初始化左子数组和右子数组的起始索引 - int i = left, j = mid + 1, 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 (k = 0; k < tmp.size(); k++) { - nums[left + k] = tmp[k]; - } - } + [class]{}-[func]{merge} - /* 归并排序 */ - void mergeSort(vector &nums, int left, int right) { - // 终止条件 - if (left >= right) - return; // 当子数组长度为 1 时终止递归 - // 划分阶段 - int mid = (left + right) / 2; // 计算中点 - mergeSort(nums, left, mid); // 递归左子数组 - mergeSort(nums, mid + 1, right); // 递归右子数组 - // 合并阶段 - merge(nums, left, mid, right); - } + [class]{}-[func]{mergeSort} ``` === "Java" ```java title="merge_sort.java" - /* 合并左子数组和右子数组 */ + /* Merge left subarray and right subarray */ void merge(int[] nums, int left, int mid, int right) { - // 左子数组区间为 [left, mid], 右子数组区间为 [mid+1, right] - // 创建一个临时数组 tmp ,用于存放合并后的结果 + // Left subarray interval is [left, mid], right subarray interval is [mid+1, right] + // Create a temporary array tmp to store the merged results int[] tmp = new int[right - left + 1]; - // 初始化左子数组和右子数组的起始索引 + // Initialize the start indices of the left and right subarrays int i = left, j = mid + 1, k = 0; - // 当左右子数组都还有元素时,进行比较并将较小的元素复制到临时数组中 + // While both subarrays still have elements, compare and copy the smaller element into the temporary array while (i <= mid && j <= right) { if (nums[i] <= nums[j]) tmp[k++] = nums[i++]; else tmp[k++] = nums[j++]; } - // 将左子数组和右子数组的剩余元素复制到临时数组中 + // Copy the remaining elements of the left and right subarrays into the temporary array while (i <= mid) { tmp[k++] = nums[i++]; } while (j <= right) { tmp[k++] = nums[j++]; } - // 将临时数组 tmp 中的元素复制回原数组 nums 的对应区间 + // Copy the elements from the temporary array tmp back to the original array nums at the corresponding interval for (k = 0; k < tmp.length; k++) { nums[left + k] = tmp[k]; } } - /* 归并排序 */ + /* Merge sort */ void mergeSort(int[] nums, int left, int right) { - // 终止条件 + // Termination condition if (left >= right) - return; // 当子数组长度为 1 时终止递归 - // 划分阶段 - int mid = (left + right) / 2; // 计算中点 - mergeSort(nums, left, mid); // 递归左子数组 - mergeSort(nums, mid + 1, right); // 递归右子数组 - // 合并阶段 + return; // Terminate recursion when subarray length is 1 + // Partition stage + int mid = (left + right) / 2; // Calculate midpoint + mergeSort(nums, left, mid); // Recursively process the left subarray + mergeSort(nums, mid + 1, right); // Recursively process the right subarray + // Merge stage merge(nums, left, mid, right); } ``` @@ -197,547 +161,91 @@ The implementation of merge sort is shown in the following code. Note that the i === "C#" ```csharp title="merge_sort.cs" - /* 合并左子数组和右子数组 */ - void Merge(int[] nums, int left, int mid, int right) { - // 左子数组区间为 [left, mid], 右子数组区间为 [mid+1, right] - // 创建一个临时数组 tmp ,用于存放合并后的结果 - int[] tmp = new int[right - left + 1]; - // 初始化左子数组和右子数组的起始索引 - int i = left, j = mid + 1, 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 (k = 0; k < tmp.Length; ++k) { - nums[left + k] = tmp[k]; - } - } + [class]{merge_sort}-[func]{Merge} - /* 归并排序 */ - void MergeSort(int[] nums, int left, int right) { - // 终止条件 - if (left >= right) return; // 当子数组长度为 1 时终止递归 - // 划分阶段 - int mid = (left + right) / 2; // 计算中点 - MergeSort(nums, left, mid); // 递归左子数组 - MergeSort(nums, mid + 1, right); // 递归右子数组 - // 合并阶段 - Merge(nums, left, mid, right); - } + [class]{merge_sort}-[func]{MergeSort} ``` === "Go" ```go title="merge_sort.go" - /* 合并左子数组和右子数组 */ - func merge(nums []int, left, mid, right int) { - // 左子数组区间为 [left, mid], 右子数组区间为 [mid+1, right] - // 创建一个临时数组 tmp ,用于存放合并后的结果 - tmp := make([]int, right-left+1) - // 初始化左子数组和右子数组的起始索引 - i, j, k := left, mid+1, 0 - // 当左右子数组都还有元素时,进行比较并将较小的元素复制到临时数组中 - for i <= mid && j <= right { - if nums[i] <= nums[j] { - tmp[k] = nums[i] - i++ - } else { - tmp[k] = nums[j] - j++ - } - k++ - } - // 将左子数组和右子数组的剩余元素复制到临时数组中 - for i <= mid { - tmp[k] = nums[i] - i++ - k++ - } - for j <= right { - tmp[k] = nums[j] - j++ - k++ - } - // 将临时数组 tmp 中的元素复制回原数组 nums 的对应区间 - for k := 0; k < len(tmp); k++ { - nums[left+k] = tmp[k] - } - } + [class]{}-[func]{merge} - /* 归并排序 */ - func mergeSort(nums []int, left, right int) { - // 终止条件 - if left >= right { - return - } - // 划分阶段 - mid := (left + right) / 2 - mergeSort(nums, left, mid) - mergeSort(nums, mid+1, right) - // 合并阶段 - merge(nums, left, mid, right) - } + [class]{}-[func]{mergeSort} ``` === "Swift" ```swift title="merge_sort.swift" - /* 合并左子数组和右子数组 */ - func merge(nums: inout [Int], left: Int, mid: Int, right: Int) { - // 左子数组区间为 [left, mid], 右子数组区间为 [mid+1, right] - // 创建一个临时数组 tmp ,用于存放合并后的结果 - var tmp = Array(repeating: 0, count: right - left + 1) - // 初始化左子数组和右子数组的起始索引 - var i = left, j = mid + 1, k = 0 - // 当左右子数组都还有元素时,进行比较并将较小的元素复制到临时数组中 - while i <= mid, j <= right { - if nums[i] <= nums[j] { - tmp[k] = nums[i] - i += 1 - } else { - tmp[k] = nums[j] - j += 1 - } - k += 1 - } - // 将左子数组和右子数组的剩余元素复制到临时数组中 - while i <= mid { - tmp[k] = nums[i] - i += 1 - k += 1 - } - while j <= right { - tmp[k] = nums[j] - j += 1 - k += 1 - } - // 将临时数组 tmp 中的元素复制回原数组 nums 的对应区间 - for k in tmp.indices { - nums[left + k] = tmp[k] - } - } + [class]{}-[func]{merge} - /* 归并排序 */ - func mergeSort(nums: inout [Int], left: Int, right: Int) { - // 终止条件 - if left >= right { // 当子数组长度为 1 时终止递归 - return - } - // 划分阶段 - let mid = (left + right) / 2 // 计算中点 - mergeSort(nums: &nums, left: left, right: mid) // 递归左子数组 - mergeSort(nums: &nums, left: mid + 1, right: right) // 递归右子数组 - // 合并阶段 - merge(nums: &nums, left: left, mid: mid, right: right) - } + [class]{}-[func]{mergeSort} ``` === "JS" ```javascript title="merge_sort.js" - /* 合并左子数组和右子数组 */ - function merge(nums, left, mid, right) { - // 左子数组区间为 [left, mid], 右子数组区间为 [mid+1, right] - // 创建一个临时数组 tmp ,用于存放合并后的结果 - const tmp = new Array(right - left + 1); - // 初始化左子数组和右子数组的起始索引 - let i = left, - j = mid + 1, - 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 (k = 0; k < tmp.length; k++) { - nums[left + k] = tmp[k]; - } - } + [class]{}-[func]{merge} - /* 归并排序 */ - function mergeSort(nums, left, right) { - // 终止条件 - if (left >= right) return; // 当子数组长度为 1 时终止递归 - // 划分阶段 - let mid = Math.floor((left + right) / 2); // 计算中点 - mergeSort(nums, left, mid); // 递归左子数组 - mergeSort(nums, mid + 1, right); // 递归右子数组 - // 合并阶段 - merge(nums, left, mid, right); - } + [class]{}-[func]{mergeSort} ``` === "TS" ```typescript title="merge_sort.ts" - /* 合并左子数组和右子数组 */ - function merge(nums: number[], left: number, mid: number, right: number): void { - // 左子数组区间为 [left, mid], 右子数组区间为 [mid+1, right] - // 创建一个临时数组 tmp ,用于存放合并后的结果 - const tmp = new Array(right - left + 1); - // 初始化左子数组和右子数组的起始索引 - let i = left, - j = mid + 1, - 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 (k = 0; k < tmp.length; k++) { - nums[left + k] = tmp[k]; - } - } + [class]{}-[func]{merge} - /* 归并排序 */ - function mergeSort(nums: number[], left: number, right: number): void { - // 终止条件 - if (left >= right) return; // 当子数组长度为 1 时终止递归 - // 划分阶段 - let mid = Math.floor((left + right) / 2); // 计算中点 - mergeSort(nums, left, mid); // 递归左子数组 - mergeSort(nums, mid + 1, right); // 递归右子数组 - // 合并阶段 - merge(nums, left, mid, right); - } + [class]{}-[func]{mergeSort} ``` === "Dart" ```dart title="merge_sort.dart" - /* 合并左子数组和右子数组 */ - void merge(List nums, int left, int mid, int right) { - // 左子数组区间为 [left, mid], 右子数组区间为 [mid+1, right] - // 创建一个临时数组 tmp ,用于存放合并后的结果 - List tmp = List.filled(right - left + 1, 0); - // 初始化左子数组和右子数组的起始索引 - int i = left, j = mid + 1, 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 (k = 0; k < tmp.length; k++) { - nums[left + k] = tmp[k]; - } - } + [class]{}-[func]{merge} - /* 归并排序 */ - void mergeSort(List nums, int left, int right) { - // 终止条件 - if (left >= right) return; // 当子数组长度为 1 时终止递归 - // 划分阶段 - int mid = (left + right) ~/ 2; // 计算中点 - mergeSort(nums, left, mid); // 递归左子数组 - mergeSort(nums, mid + 1, right); // 递归右子数组 - // 合并阶段 - merge(nums, left, mid, right); - } + [class]{}-[func]{mergeSort} ``` === "Rust" ```rust title="merge_sort.rs" - /* 合并左子数组和右子数组 */ - fn merge(nums: &mut [i32], left: usize, mid: usize, right: usize) { - // 左子数组区间为 [left, mid], 右子数组区间为 [mid+1, right] - // 创建一个临时数组 tmp ,用于存放合并后的结果 - let tmp_size = right - left + 1; - let mut tmp = vec![0; tmp_size]; - // 初始化左子数组和右子数组的起始索引 - let (mut i, mut j, mut k) = (left, mid + 1, 0); - // 当左右子数组都还有元素时,进行比较并将较小的元素复制到临时数组中 - while i <= mid && j <= right { - if nums[i] <= nums[j] { - tmp[k] = nums[i]; - i += 1; - } else { - tmp[k] = nums[j]; - j += 1; - } - k += 1; - } - // 将左子数组和右子数组的剩余元素复制到临时数组中 - while i <= mid { - tmp[k] = nums[i]; - k += 1; - i += 1; - } - while j <= right { - tmp[k] = nums[j]; - k += 1; - j += 1; - } - // 将临时数组 tmp 中的元素复制回原数组 nums 的对应区间 - for k in 0..tmp_size { - nums[left + k] = tmp[k]; - } - } + [class]{}-[func]{merge} - /* 归并排序 */ - fn merge_sort(nums: &mut [i32], left: usize, right: usize) { - // 终止条件 - if left >= right { - return; // 当子数组长度为 1 时终止递归 - } - - // 划分阶段 - let mid = (left + right) / 2; // 计算中点 - merge_sort(nums, left, mid); // 递归左子数组 - merge_sort(nums, mid + 1, right); // 递归右子数组 - - // 合并阶段 - merge(nums, left, mid, right); - } + [class]{}-[func]{merge_sort} ``` === "C" ```c title="merge_sort.c" - /* 合并左子数组和右子数组 */ - void merge(int *nums, int left, int mid, int right) { - // 左子数组区间为 [left, mid], 右子数组区间为 [mid+1, right] - // 创建一个临时数组 tmp ,用于存放合并后的结果 - int tmpSize = right - left + 1; - int *tmp = (int *)malloc(tmpSize * sizeof(int)); - // 初始化左子数组和右子数组的起始索引 - int i = left, j = mid + 1, 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 (k = 0; k < tmpSize; ++k) { - nums[left + k] = tmp[k]; - } - // 释放内存 - free(tmp); - } + [class]{}-[func]{merge} - /* 归并排序 */ - void mergeSort(int *nums, int left, int right) { - // 终止条件 - if (left >= right) - return; // 当子数组长度为 1 时终止递归 - // 划分阶段 - int mid = (left + right) / 2; // 计算中点 - mergeSort(nums, left, mid); // 递归左子数组 - mergeSort(nums, mid + 1, right); // 递归右子数组 - // 合并阶段 - merge(nums, left, mid, right); - } + [class]{}-[func]{mergeSort} ``` === "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] - } - } + [class]{}-[func]{merge} - /* 归并排序 */ - 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) - } + [class]{}-[func]{mergeSort} ``` === "Ruby" ```ruby title="merge_sort.rb" - ### 合并左子数组和右子数组 ### - def merge(nums, left, mid, right) - # 左子数组区间为 [left, mid], 右子数组区间为 [mid+1, right] - # 创建一个临时数组 tmp,用于存放合并后的结果 - tmp = Array.new(right - left + 1, 0) - # 初始化左子数组和右子数组的起始索引 - i, j, k = left, mid + 1, 0 - # 当左右子数组都还有元素时,进行比较并将较小的元素复制到临时数组中 - while i <= mid && j <= right - if nums[i] <= nums[j] - tmp[k] = nums[i] - i += 1 - else - tmp[k] = nums[j] - j += 1 - end - k += 1 - end - # 将左子数组和右子数组的剩余元素复制到临时数组中 - while i <= mid - tmp[k] = nums[i] - i += 1 - k += 1 - end - while j <= right - tmp[k] = nums[j] - j += 1 - k += 1 - end - # 将临时数组 tmp 中的元素复制回原数组 nums 的对应区间 - (0...tmp.length).each do |k| - nums[left + k] = tmp[k] - end - end + [class]{}-[func]{merge} - ### 归并排序 ### - def merge_sort(nums, left, right) - # 终止条件 - # 当子数组长度为 1 时终止递归 - return if left >= right - # 划分阶段 - mid = (left + right) / 2 # 计算中点 - merge_sort(nums, left, mid) # 递归左子数组 - merge_sort(nums, mid + 1, right) # 递归右子数组 - # 合并阶段 - merge(nums, left, mid, right) - end + [class]{}-[func]{merge_sort} ``` === "Zig" ```zig title="merge_sort.zig" - // 合并左子数组和右子数组 - // 左子数组区间 [left, mid] - // 右子数组区间 [mid + 1, right] - fn merge(nums: []i32, left: usize, mid: usize, right: usize) !void { - // 初始化辅助数组 - var mem_arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); - defer mem_arena.deinit(); - const mem_allocator = mem_arena.allocator(); - var tmp = try mem_allocator.alloc(i32, right + 1 - left); - std.mem.copy(i32, tmp, nums[left..right+1]); - // 左子数组的起始索引和结束索引 - var leftStart = left - left; - var leftEnd = mid - left; - // 右子数组的起始索引和结束索引 - var rightStart = mid + 1 - left; - var rightEnd = right - left; - // i, j 分别指向左子数组、右子数组的首元素 - var i = leftStart; - var j = rightStart; - // 通过覆盖原数组 nums 来合并左子数组和右子数组 - var k = left; - while (k <= right) : (k += 1) { - // 若“左子数组已全部合并完”,则选取右子数组元素,并且 j++ - if (i > leftEnd) { - nums[k] = tmp[j]; - j += 1; - // 否则,若“右子数组已全部合并完”或“左子数组元素 <= 右子数组元素”,则选取左子数组元素,并且 i++ - } else if (j > rightEnd or tmp[i] <= tmp[j]) { - nums[k] = tmp[i]; - i += 1; - // 否则,若“左右子数组都未全部合并完”且“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++ - } else { - nums[k] = tmp[j]; - j += 1; - } - } - } + [class]{}-[func]{merge} - // 归并排序 - fn mergeSort(nums: []i32, left: usize, right: usize) !void { - // 终止条件 - if (left >= right) return; // 当子数组长度为 1 时终止递归 - // 划分阶段 - var mid = (left + right) / 2; // 计算中点 - try mergeSort(nums, left, mid); // 递归左子数组 - try mergeSort(nums, mid + 1, right); // 递归右子数组 - // 合并阶段 - try merge(nums, left, mid, right); - } + [class]{}-[func]{mergeSort} ``` -??? pythontutor "Code Visualization" - -
- - ## 11.6.2   Algorithm characteristics - **Time complexity of $O(n \log n)$, non-adaptive sort**: The division creates a recursion tree of height $\log n$, with each layer merging a total of $n$ operations, resulting in an overall time complexity of $O(n \log n)$. diff --git a/en/docs/chapter_sorting/quick_sort.md b/en/docs/chapter_sorting/quick_sort.md index becc87232..d8c800c96 100644 --- a/en/docs/chapter_sorting/quick_sort.md +++ b/en/docs/chapter_sorting/quick_sort.md @@ -51,358 +51,135 @@ After the pivot partitioning, the original array is divided into three parts: le ```python title="quick_sort.py" def partition(self, nums: list[int], left: int, right: int) -> int: - """哨兵划分""" - # 以 nums[left] 为基准数 + """Partition""" + # Use nums[left] as the pivot i, j = left, right while i < j: while i < j and nums[j] >= nums[left]: - j -= 1 # 从右向左找首个小于基准数的元素 + j -= 1 # Search from right to left for the first element smaller than the pivot while i < j and nums[i] <= nums[left]: - i += 1 # 从左向右找首个大于基准数的元素 - # 元素交换 + i += 1 # Search from left to right for the first element greater than the pivot + # Swap elements nums[i], nums[j] = nums[j], nums[i] - # 将基准数交换至两子数组的分界线 + # Swap the pivot to the boundary between the two subarrays nums[i], nums[left] = nums[left], nums[i] - return i # 返回基准数的索引 + return i # Return the index of the pivot ``` === "C++" ```cpp title="quick_sort.cpp" - /* 元素交换 */ - void swap(vector &nums, int i, int j) { - int tmp = nums[i]; - nums[i] = nums[j]; - nums[j] = tmp; - } + [class]{QuickSort}-[func]{swap} - /* 哨兵划分 */ - int partition(vector &nums, int left, int right) { - // 以 nums[left] 为基准数 - int i = left, 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; // 返回基准数的索引 - } + [class]{QuickSort}-[func]{partition} ``` === "Java" ```java title="quick_sort.java" - /* 元素交换 */ + /* Swap elements */ void swap(int[] nums, int i, int j) { int tmp = nums[i]; nums[i] = nums[j]; nums[j] = tmp; } - /* 哨兵划分 */ + /* Partition */ int partition(int[] nums, int left, int right) { - // 以 nums[left] 为基准数 + // Use nums[left] as the pivot int i = left, j = right; while (i < j) { while (i < j && nums[j] >= nums[left]) - j--; // 从右向左找首个小于基准数的元素 + j--; // Search from right to left for the first element smaller than the pivot while (i < j && nums[i] <= nums[left]) - i++; // 从左向右找首个大于基准数的元素 - swap(nums, i, j); // 交换这两个元素 + i++; // Search from left to right for the first element greater than the pivot + swap(nums, i, j); // Swap these two elements } - swap(nums, i, left); // 将基准数交换至两子数组的分界线 - return i; // 返回基准数的索引 + swap(nums, i, left); // Swap the pivot to the boundary between the two subarrays + return i; // Return the index of the pivot } ``` === "C#" ```csharp title="quick_sort.cs" - /* 元素交换 */ - void Swap(int[] nums, int i, int j) { - (nums[j], nums[i]) = (nums[i], nums[j]); - } + [class]{quickSort}-[func]{Swap} - /* 哨兵划分 */ - int Partition(int[] nums, int left, int right) { - // 以 nums[left] 为基准数 - int i = left, 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; // 返回基准数的索引 - } + [class]{quickSort}-[func]{Partition} ``` === "Go" ```go title="quick_sort.go" - /* 哨兵划分 */ - func (q *quickSort) partition(nums []int, left, right int) int { - // 以 nums[left] 为基准数 - i, j := left, right - for i < j { - for i < j && nums[j] >= nums[left] { - j-- // 从右向左找首个小于基准数的元素 - } - for i < j && nums[i] <= nums[left] { - i++ // 从左向右找首个大于基准数的元素 - } - // 元素交换 - nums[i], nums[j] = nums[j], nums[i] - } - // 将基准数交换至两子数组的分界线 - nums[i], nums[left] = nums[left], nums[i] - return i // 返回基准数的索引 - } + [class]{quickSort}-[func]{partition} ``` === "Swift" ```swift title="quick_sort.swift" - /* 哨兵划分 */ - func partition(nums: inout [Int], left: Int, right: Int) -> Int { - // 以 nums[left] 为基准数 - var i = left - var j = right - while i < j { - while i < j, nums[j] >= nums[left] { - j -= 1 // 从右向左找首个小于基准数的元素 - } - while i < j, nums[i] <= nums[left] { - i += 1 // 从左向右找首个大于基准数的元素 - } - nums.swapAt(i, j) // 交换这两个元素 - } - nums.swapAt(i, left) // 将基准数交换至两子数组的分界线 - return i // 返回基准数的索引 - } + [class]{}-[func]{partition} ``` === "JS" ```javascript title="quick_sort.js" - /* 元素交换 */ - swap(nums, i, j) { - let tmp = nums[i]; - nums[i] = nums[j]; - nums[j] = tmp; - } + [class]{QuickSort}-[func]{swap} - /* 哨兵划分 */ - partition(nums, left, right) { - // 以 nums[left] 为基准数 - let i = left, - j = right; - while (i < j) { - while (i < j && nums[j] >= nums[left]) { - j -= 1; // 从右向左找首个小于基准数的元素 - } - while (i < j && nums[i] <= nums[left]) { - i += 1; // 从左向右找首个大于基准数的元素 - } - // 元素交换 - this.swap(nums, i, j); // 交换这两个元素 - } - this.swap(nums, i, left); // 将基准数交换至两子数组的分界线 - return i; // 返回基准数的索引 - } + [class]{QuickSort}-[func]{partition} ``` === "TS" ```typescript title="quick_sort.ts" - /* 元素交换 */ - swap(nums: number[], i: number, j: number): void { - let tmp = nums[i]; - nums[i] = nums[j]; - nums[j] = tmp; - } + [class]{QuickSort}-[func]{swap} - /* 哨兵划分 */ - partition(nums: number[], left: number, right: number): number { - // 以 nums[left] 为基准数 - let i = left, - j = right; - while (i < j) { - while (i < j && nums[j] >= nums[left]) { - j -= 1; // 从右向左找首个小于基准数的元素 - } - while (i < j && nums[i] <= nums[left]) { - i += 1; // 从左向右找首个大于基准数的元素 - } - // 元素交换 - this.swap(nums, i, j); // 交换这两个元素 - } - this.swap(nums, i, left); // 将基准数交换至两子数组的分界线 - return i; // 返回基准数的索引 - } + [class]{QuickSort}-[func]{partition} ``` === "Dart" ```dart title="quick_sort.dart" - /* 元素交换 */ - void _swap(List nums, int i, int j) { - int tmp = nums[i]; - nums[i] = nums[j]; - nums[j] = tmp; - } + [class]{QuickSort}-[func]{_swap} - /* 哨兵划分 */ - int _partition(List nums, int left, int right) { - // 以 nums[left] 为基准数 - int i = left, 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; // 返回基准数的索引 - } + [class]{QuickSort}-[func]{_partition} ``` === "Rust" ```rust title="quick_sort.rs" - /* 哨兵划分 */ - fn partition(nums: &mut [i32], left: usize, right: usize) -> usize { - // 以 nums[left] 为基准数 - let (mut i, mut j) = (left, right); - while i < j { - while i < j && nums[j] >= nums[left] { - j -= 1; // 从右向左找首个小于基准数的元素 - } - while i < j && nums[i] <= nums[left] { - i += 1; // 从左向右找首个大于基准数的元素 - } - nums.swap(i, j); // 交换这两个元素 - } - nums.swap(i, left); // 将基准数交换至两子数组的分界线 - i // 返回基准数的索引 - } + [class]{QuickSort}-[func]{partition} ``` === "C" ```c title="quick_sort.c" - /* 元素交换 */ - void swap(int nums[], int i, int j) { - int tmp = nums[i]; - nums[i] = nums[j]; - nums[j] = tmp; - } + [class]{}-[func]{swap} - /* 哨兵划分 */ - int partition(int nums[], int left, int right) { - // 以 nums[left] 为基准数 - int i = left, 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; - } + [class]{}-[func]{partition} ``` === "Kotlin" ```kotlin title="quick_sort.kt" - /* 元素交换 */ - fun swap(nums: IntArray, i: Int, j: Int) { - val temp = nums[i] - nums[i] = nums[j] - nums[j] = temp - } + [class]{}-[func]{swap} - /* 哨兵划分 */ - 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 // 返回基准数的索引 - } + [class]{}-[func]{partition} ``` === "Ruby" ```ruby title="quick_sort.rb" - ### 哨兵划分 ### - def partition(nums, left, right) - # 以 nums[left] 为基准数 - i, j = left, right - while i < j - while i < j && nums[j] >= nums[left] - j -= 1 # 从右向左找首个小于基准数的元素 - end - while i < j && nums[i] <= nums[left] - i += 1 # 从左向右找首个大于基准数的元素 - end - # 元素交换 - nums[i], nums[j] = nums[j], nums[i] - end - # 将基准数交换至两子数组的分界线 - nums[i], nums[left] = nums[left], nums[i] - i # 返回基准数的索引 - end + [class]{QuickSort}-[func]{partition} ``` === "Zig" ```zig title="quick_sort.zig" - // 元素交换 - fn swap(nums: []i32, i: usize, j: usize) void { - var tmp = nums[i]; - nums[i] = nums[j]; - nums[j] = tmp; - } + [class]{QuickSort}-[func]{swap} - // 哨兵划分 - fn partition(nums: []i32, left: usize, right: usize) usize { - // 以 nums[left] 为基准数 - var i = left; - var j = right; - while (i < j) { - while (i < j and nums[j] >= nums[left]) j -= 1; // 从右向左找首个小于基准数的元素 - while (i < j and nums[i] <= nums[left]) i += 1; // 从左向右找首个大于基准数的元素 - swap(nums, i, j); // 交换这两个元素 - } - swap(nums, i, left); // 将基准数交换至两子数组的分界线 - return i; // 返回基准数的索引 - } + [class]{QuickSort}-[func]{partition} ``` -??? pythontutor "Code Visualization" - -
- - ## 11.5.1   Algorithm process The overall process of quick sort is shown in Figure 11-9. @@ -419,13 +196,13 @@ The overall process of quick sort is shown in Figure 11-9. ```python title="quick_sort.py" def quick_sort(self, nums: list[int], left: int, right: int): - """快速排序""" - # 子数组长度为 1 时终止递归 + """Quick sort""" + # Terminate recursion when subarray length is 1 if left >= right: return - # 哨兵划分 + # Partition pivot = self.partition(nums, left, right) - # 递归左子数组、右子数组 + # Recursively process the left subarray and right subarray self.quick_sort(nums, left, pivot - 1) self.quick_sort(nums, pivot + 1, right) ``` @@ -433,30 +210,20 @@ The overall process of quick sort is shown in Figure 11-9. === "C++" ```cpp title="quick_sort.cpp" - /* 快速排序 */ - void quickSort(vector &nums, int left, int right) { - // 子数组长度为 1 时终止递归 - if (left >= right) - return; - // 哨兵划分 - int pivot = partition(nums, left, right); - // 递归左子数组、右子数组 - quickSort(nums, left, pivot - 1); - quickSort(nums, pivot + 1, right); - } + [class]{QuickSort}-[func]{quickSort} ``` === "Java" ```java title="quick_sort.java" - /* 快速排序 */ + /* Quick sort */ void quickSort(int[] nums, int left, int right) { - // 子数组长度为 1 时终止递归 + // Terminate recursion when subarray length is 1 if (left >= right) return; - // 哨兵划分 + // Partition int pivot = partition(nums, left, right); - // 递归左子数组、右子数组 + // Recursively process the left subarray and right subarray quickSort(nums, left, pivot - 1); quickSort(nums, pivot + 1, right); } @@ -465,186 +232,69 @@ The overall process of quick sort is shown in Figure 11-9. === "C#" ```csharp title="quick_sort.cs" - /* 快速排序 */ - void QuickSort(int[] nums, int left, int right) { - // 子数组长度为 1 时终止递归 - if (left >= right) - return; - // 哨兵划分 - int pivot = Partition(nums, left, right); - // 递归左子数组、右子数组 - QuickSort(nums, left, pivot - 1); - QuickSort(nums, pivot + 1, right); - } + [class]{quickSort}-[func]{QuickSort} ``` === "Go" ```go title="quick_sort.go" - /* 快速排序 */ - func (q *quickSort) quickSort(nums []int, left, right int) { - // 子数组长度为 1 时终止递归 - if left >= right { - return - } - // 哨兵划分 - pivot := q.partition(nums, left, right) - // 递归左子数组、右子数组 - q.quickSort(nums, left, pivot-1) - q.quickSort(nums, pivot+1, right) - } + [class]{quickSort}-[func]{quickSort} ``` === "Swift" ```swift title="quick_sort.swift" - /* 快速排序 */ - func quickSort(nums: inout [Int], left: Int, right: Int) { - // 子数组长度为 1 时终止递归 - if left >= right { - return - } - // 哨兵划分 - let pivot = partition(nums: &nums, left: left, right: right) - // 递归左子数组、右子数组 - quickSort(nums: &nums, left: left, right: pivot - 1) - quickSort(nums: &nums, left: pivot + 1, right: right) - } + [class]{}-[func]{quickSort} ``` === "JS" ```javascript title="quick_sort.js" - /* 快速排序 */ - quickSort(nums, left, right) { - // 子数组长度为 1 时终止递归 - if (left >= right) return; - // 哨兵划分 - const pivot = this.partition(nums, left, right); - // 递归左子数组、右子数组 - this.quickSort(nums, left, pivot - 1); - this.quickSort(nums, pivot + 1, right); - } + [class]{QuickSort}-[func]{quickSort} ``` === "TS" ```typescript title="quick_sort.ts" - /* 快速排序 */ - quickSort(nums: number[], left: number, right: number): void { - // 子数组长度为 1 时终止递归 - if (left >= right) { - return; - } - // 哨兵划分 - const pivot = this.partition(nums, left, right); - // 递归左子数组、右子数组 - this.quickSort(nums, left, pivot - 1); - this.quickSort(nums, pivot + 1, right); - } + [class]{QuickSort}-[func]{quickSort} ``` === "Dart" ```dart title="quick_sort.dart" - /* 快速排序 */ - void quickSort(List nums, int left, int right) { - // 子数组长度为 1 时终止递归 - if (left >= right) return; - // 哨兵划分 - int pivot = _partition(nums, left, right); - // 递归左子数组、右子数组 - quickSort(nums, left, pivot - 1); - quickSort(nums, pivot + 1, right); - } + [class]{QuickSort}-[func]{quickSort} ``` === "Rust" ```rust title="quick_sort.rs" - /* 快速排序 */ - pub fn quick_sort(left: i32, right: i32, nums: &mut [i32]) { - // 子数组长度为 1 时终止递归 - if left >= right { - return; - } - // 哨兵划分 - let pivot = Self::partition(nums, left as usize, right as usize) as i32; - // 递归左子数组、右子数组 - Self::quick_sort(left, pivot - 1, nums); - Self::quick_sort(pivot + 1, right, nums); - } + [class]{QuickSort}-[func]{quick_sort} ``` === "C" ```c title="quick_sort.c" - /* 快速排序 */ - void quickSort(int nums[], int left, int right) { - // 子数组长度为 1 时终止递归 - if (left >= right) { - return; - } - // 哨兵划分 - int pivot = partition(nums, left, right); - // 递归左子数组、右子数组 - quickSort(nums, left, pivot - 1); - quickSort(nums, pivot + 1, right); - } + [class]{}-[func]{quickSort} ``` === "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) - } + [class]{}-[func]{quickSort} ``` === "Ruby" ```ruby title="quick_sort.rb" - ### 快速排序类 ### - def quick_sort(nums, left, right) - # 子数组长度不为 1 时递归 - if left < right - # 哨兵划分 - pivot = partition(nums, left, right) - # 递归左子数组、右子数组 - quick_sort(nums, left, pivot - 1) - quick_sort(nums, pivot + 1, right) - end - nums - end + [class]{QuickSort}-[func]{quick_sort} ``` === "Zig" ```zig title="quick_sort.zig" - // 快速排序 - fn quickSort(nums: []i32, left: usize, right: usize) void { - // 子数组长度为 1 时终止递归 - if (left >= right) return; - // 哨兵划分 - var pivot = partition(nums, left, right); - // 递归左子数组、右子数组 - quickSort(nums, left, pivot - 1); - quickSort(nums, pivot + 1, right); - } + [class]{QuickSort}-[func]{quickSort} ``` -??? pythontutor "Code Visualization" - -
- - ## 11.5.2   Algorithm features - **Time complexity of $O(n \log n)$, adaptive sorting**: In average cases, the recursive levels of pivot partitioning are $\log n$, and the total number of loops per level is $n$, using $O(n \log n)$ time overall. In the worst case, each round of pivot partitioning divides an array of length $n$ into two sub-arrays of lengths $0$ and $n - 1$, reaching $n$ recursive levels, and using $O(n^2)$ time overall. @@ -675,499 +325,163 @@ Sample code is as follows: ```python title="quick_sort.py" def median_three(self, nums: list[int], left: int, mid: int, right: int) -> int: - """选取三个候选元素的中位数""" + """Select the median of three candidate elements""" l, m, r = nums[left], nums[mid], nums[right] if (l <= m <= r) or (r <= m <= l): - return mid # m 在 l 和 r 之间 + return mid # m is between l and r if (m <= l <= r) or (r <= l <= m): - return left # l 在 m 和 r 之间 + return left # l is between m and r return right def partition(self, nums: list[int], left: int, right: int) -> int: - """哨兵划分(三数取中值)""" - # 以 nums[left] 为基准数 + """Partition (median of three)""" + # Use nums[left] as the pivot med = self.median_three(nums, left, (left + right) // 2, right) - # 将中位数交换至数组最左端 + # Swap the median to the array's leftmost position nums[left], nums[med] = nums[med], nums[left] - # 以 nums[left] 为基准数 + # Use nums[left] as the pivot i, j = left, right while i < j: while i < j and nums[j] >= nums[left]: - j -= 1 # 从右向左找首个小于基准数的元素 + j -= 1 # Search from right to left for the first element smaller than the pivot while i < j and nums[i] <= nums[left]: - i += 1 # 从左向右找首个大于基准数的元素 - # 元素交换 + i += 1 # Search from left to right for the first element greater than the pivot + # Swap elements nums[i], nums[j] = nums[j], nums[i] - # 将基准数交换至两子数组的分界线 + # Swap the pivot to the boundary between the two subarrays nums[i], nums[left] = nums[left], nums[i] - return i # 返回基准数的索引 + return i # Return the index of the pivot ``` === "C++" ```cpp title="quick_sort.cpp" - /* 选取三个候选元素的中位数 */ - int medianThree(vector &nums, int left, int mid, int right) { - int l = nums[left], m = nums[mid], r = nums[right]; - if ((l <= m && m <= r) || (r <= m && m <= l)) - return mid; // m 在 l 和 r 之间 - if ((m <= l && l <= r) || (r <= l && l <= m)) - return left; // l 在 m 和 r 之间 - return right; - } + [class]{QuickSortMedian}-[func]{medianThree} - /* 哨兵划分(三数取中值) */ - int partition(vector &nums, int left, int right) { - // 选取三个候选元素的中位数 - int med = medianThree(nums, left, (left + right) / 2, right); - // 将中位数交换至数组最左端 - swap(nums, left, med); - // 以 nums[left] 为基准数 - int i = left, 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; // 返回基准数的索引 - } + [class]{QuickSortMedian}-[func]{partition} ``` === "Java" ```java title="quick_sort.java" - /* 选取三个候选元素的中位数 */ + /* Select the median of three candidate elements */ int medianThree(int[] nums, int left, int mid, int right) { int l = nums[left], m = nums[mid], r = nums[right]; if ((l <= m && m <= r) || (r <= m && m <= l)) - return mid; // m 在 l 和 r 之间 + return mid; // m is between l and r if ((m <= l && l <= r) || (r <= l && l <= m)) - return left; // l 在 m 和 r 之间 + return left; // l is between m and r return right; } - /* 哨兵划分(三数取中值) */ + /* Partition (median of three) */ int partition(int[] nums, int left, int right) { - // 选取三个候选元素的中位数 + // Select the median of three candidate elements int med = medianThree(nums, left, (left + right) / 2, right); - // 将中位数交换至数组最左端 + // Swap the median to the array's leftmost position swap(nums, left, med); - // 以 nums[left] 为基准数 + // Use nums[left] as the pivot int i = left, j = right; while (i < j) { while (i < j && nums[j] >= nums[left]) - j--; // 从右向左找首个小于基准数的元素 + j--; // Search from right to left for the first element smaller than the pivot while (i < j && nums[i] <= nums[left]) - i++; // 从左向右找首个大于基准数的元素 - swap(nums, i, j); // 交换这两个元素 + i++; // Search from left to right for the first element greater than the pivot + swap(nums, i, j); // Swap these two elements } - swap(nums, i, left); // 将基准数交换至两子数组的分界线 - return i; // 返回基准数的索引 + swap(nums, i, left); // Swap the pivot to the boundary between the two subarrays + return i; // Return the index of the pivot } ``` === "C#" ```csharp title="quick_sort.cs" - /* 选取三个候选元素的中位数 */ - int MedianThree(int[] nums, int left, int mid, int right) { - int l = nums[left], m = nums[mid], r = nums[right]; - if ((l <= m && m <= r) || (r <= m && m <= l)) - return mid; // m 在 l 和 r 之间 - if ((m <= l && l <= r) || (r <= l && l <= m)) - return left; // l 在 m 和 r 之间 - return right; - } + [class]{QuickSortMedian}-[func]{MedianThree} - /* 哨兵划分(三数取中值) */ - int Partition(int[] nums, int left, int right) { - // 选取三个候选元素的中位数 - int med = MedianThree(nums, left, (left + right) / 2, right); - // 将中位数交换至数组最左端 - Swap(nums, left, med); - // 以 nums[left] 为基准数 - int i = left, 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; // 返回基准数的索引 - } + [class]{QuickSortMedian}-[func]{Partition} ``` === "Go" ```go title="quick_sort.go" - /* 选取三个候选元素的中位数 */ - func (q *quickSortMedian) medianThree(nums []int, left, mid, right int) int { - l, m, r := nums[left], nums[mid], nums[right] - if (l <= m && m <= r) || (r <= m && m <= l) { - return mid // m 在 l 和 r 之间 - } - if (m <= l && l <= r) || (r <= l && l <= m) { - return left // l 在 m 和 r 之间 - } - return right - } + [class]{quickSortMedian}-[func]{medianThree} - /* 哨兵划分(三数取中值)*/ - func (q *quickSortMedian) partition(nums []int, left, right int) int { - // 以 nums[left] 为基准数 - med := q.medianThree(nums, left, (left+right)/2, right) - // 将中位数交换至数组最左端 - nums[left], nums[med] = nums[med], nums[left] - // 以 nums[left] 为基准数 - i, j := left, right - for i < j { - for i < j && nums[j] >= nums[left] { - j-- //从右向左找首个小于基准数的元素 - } - for i < j && nums[i] <= nums[left] { - i++ //从左向右找首个大于基准数的元素 - } - //元素交换 - nums[i], nums[j] = nums[j], nums[i] - } - //将基准数交换至两子数组的分界线 - nums[i], nums[left] = nums[left], nums[i] - return i //返回基准数的索引 - } + [class]{quickSortMedian}-[func]{partition} ``` === "Swift" ```swift title="quick_sort.swift" - /* 选取三个候选元素的中位数 */ - func medianThree(nums: [Int], left: Int, mid: Int, right: Int) -> Int { - let l = nums[left] - let m = nums[mid] - let r = nums[right] - if (l <= m && m <= r) || (r <= m && m <= l) { - return mid // m 在 l 和 r 之间 - } - if (m <= l && l <= r) || (r <= l && l <= m) { - return left // l 在 m 和 r 之间 - } - return right - } + [class]{}-[func]{medianThree} - /* 哨兵划分(三数取中值) */ - func partitionMedian(nums: inout [Int], left: Int, right: Int) -> Int { - // 选取三个候选元素的中位数 - let med = medianThree(nums: nums, left: left, mid: (left + right) / 2, right: right) - // 将中位数交换至数组最左端 - nums.swapAt(left, med) - return partition(nums: &nums, left: left, right: right) - } + [class]{}-[func]{partitionMedian} ``` === "JS" ```javascript title="quick_sort.js" - /* 选取三个候选元素的中位数 */ - medianThree(nums, left, mid, right) { - let l = nums[left], - m = nums[mid], - r = nums[right]; - // m 在 l 和 r 之间 - if ((l <= m && m <= r) || (r <= m && m <= l)) return mid; - // l 在 m 和 r 之间 - if ((m <= l && l <= r) || (r <= l && l <= m)) return left; - return right; - } + [class]{QuickSortMedian}-[func]{medianThree} - /* 哨兵划分(三数取中值) */ - partition(nums, left, right) { - // 选取三个候选元素的中位数 - let med = this.medianThree( - nums, - left, - Math.floor((left + right) / 2), - right - ); - // 将中位数交换至数组最左端 - this.swap(nums, left, med); - // 以 nums[left] 为基准数 - let i = left, - j = right; - while (i < j) { - while (i < j && nums[j] >= nums[left]) j--; // 从右向左找首个小于基准数的元素 - while (i < j && nums[i] <= nums[left]) i++; // 从左向右找首个大于基准数的元素 - this.swap(nums, i, j); // 交换这两个元素 - } - this.swap(nums, i, left); // 将基准数交换至两子数组的分界线 - return i; // 返回基准数的索引 - } + [class]{QuickSortMedian}-[func]{partition} ``` === "TS" ```typescript title="quick_sort.ts" - /* 选取三个候选元素的中位数 */ - medianThree( - nums: number[], - left: number, - mid: number, - right: number - ): number { - let l = nums[left], - m = nums[mid], - r = nums[right]; - // m 在 l 和 r 之间 - if ((l <= m && m <= r) || (r <= m && m <= l)) return mid; - // l 在 m 和 r 之间 - if ((m <= l && l <= r) || (r <= l && l <= m)) return left; - return right; - } + [class]{QuickSortMedian}-[func]{medianThree} - /* 哨兵划分(三数取中值) */ - partition(nums: number[], left: number, right: number): number { - // 选取三个候选元素的中位数 - let med = this.medianThree( - nums, - left, - Math.floor((left + right) / 2), - right - ); - // 将中位数交换至数组最左端 - this.swap(nums, left, med); - // 以 nums[left] 为基准数 - let i = left, - j = right; - while (i < j) { - while (i < j && nums[j] >= nums[left]) { - j--; // 从右向左找首个小于基准数的元素 - } - while (i < j && nums[i] <= nums[left]) { - i++; // 从左向右找首个大于基准数的元素 - } - this.swap(nums, i, j); // 交换这两个元素 - } - this.swap(nums, i, left); // 将基准数交换至两子数组的分界线 - return i; // 返回基准数的索引 - } + [class]{QuickSortMedian}-[func]{partition} ``` === "Dart" ```dart title="quick_sort.dart" - /* 选取三个候选元素的中位数 */ - int _medianThree(List nums, int left, int mid, int right) { - int l = nums[left], m = nums[mid], r = nums[right]; - if ((l <= m && m <= r) || (r <= m && m <= l)) - return mid; // m 在 l 和 r 之间 - if ((m <= l && l <= r) || (r <= l && l <= m)) - return left; // l 在 m 和 r 之间 - return right; - } + [class]{QuickSortMedian}-[func]{_medianThree} - /* 哨兵划分(三数取中值) */ - int _partition(List nums, int left, int right) { - // 选取三个候选元素的中位数 - int med = _medianThree(nums, left, (left + right) ~/ 2, right); - // 将中位数交换至数组最左端 - _swap(nums, left, med); - // 以 nums[left] 为基准数 - int i = left, 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; // 返回基准数的索引 - } + [class]{QuickSortMedian}-[func]{_partition} ``` === "Rust" ```rust title="quick_sort.rs" - /* 选取三个候选元素的中位数 */ - fn median_three(nums: &mut [i32], left: usize, mid: usize, right: usize) -> usize { - let (l, m, r) = (nums[left], nums[mid], nums[right]); - if (l <= m && m <= r) || (r <= m && m <= l) { - return mid; // m 在 l 和 r 之间 - } - if (m <= l && l <= r) || (r <= l && l <= m) { - return left; // l 在 m 和 r 之间 - } - right - } + [class]{QuickSortMedian}-[func]{median_three} - /* 哨兵划分(三数取中值) */ - fn partition(nums: &mut [i32], left: usize, right: usize) -> usize { - // 选取三个候选元素的中位数 - let med = Self::median_three(nums, left, (left + right) / 2, right); - // 将中位数交换至数组最左端 - nums.swap(left, med); - // 以 nums[left] 为基准数 - let (mut i, mut j) = (left, right); - while i < j { - while i < j && nums[j] >= nums[left] { - j -= 1; // 从右向左找首个小于基准数的元素 - } - while i < j && nums[i] <= nums[left] { - i += 1; // 从左向右找首个大于基准数的元素 - } - nums.swap(i, j); // 交换这两个元素 - } - nums.swap(i, left); // 将基准数交换至两子数组的分界线 - i // 返回基准数的索引 - } + [class]{QuickSortMedian}-[func]{partition} ``` === "C" ```c title="quick_sort.c" - /* 选取三个候选元素的中位数 */ - int medianThree(int nums[], int left, int mid, int right) { - int l = nums[left], m = nums[mid], r = nums[right]; - if ((l <= m && m <= r) || (r <= m && m <= l)) - return mid; // m 在 l 和 r 之间 - if ((m <= l && l <= r) || (r <= l && l <= m)) - return left; // l 在 m 和 r 之间 - return right; - } + [class]{}-[func]{medianThree} - /* 哨兵划分(三数取中值) */ - int partitionMedian(int nums[], int left, int right) { - // 选取三个候选元素的中位数 - int med = medianThree(nums, left, (left + right) / 2, right); - // 将中位数交换至数组最左端 - swap(nums, left, med); - // 以 nums[left] 为基准数 - int i = left, 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; // 返回基准数的索引 - } + [class]{}-[func]{partitionMedian} ``` === "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 - } + [class]{}-[func]{medianThree} - /* 哨兵划分(三数取中值) */ - fun partitionMedian(nums: IntArray, left: Int, right: Int): Int { - // 选取三个候选元素的中位数 - val med = medianThree(nums, left, (left + right) / 2, right) - // 将中位数交换至数组最左端 - swap(nums, left, med) - // 以 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 // 返回基准数的索引 - } + [class]{}-[func]{partitionMedian} ``` === "Ruby" ```ruby title="quick_sort.rb" - ### 选取三个候选元素的中位数 ### - def median_three(nums, left, mid, right) - # 选取三个候选元素的中位数 - _l, _m, _r = nums[left], nums[mid], nums[right] - # m 在 l 和 r 之间 - return mid if (_l <= _m && _m <= _r) || (_r <= _m && _m <= _l) - # l 在 m 和 r 之间 - return left if (_m <= _l && _l <= _r) || (_r <= _l && _l <= _m) - return right - end + [class]{QuickSortMedian}-[func]{median_three} - ### 哨兵划分(三数取中值)### - def partition(nums, left, right) - ### 以 nums[left] 为基准数 - med = median_three(nums, left, (left + right) / 2, right) - # 将中位数交换至数组最左断 - nums[left], nums[med] = nums[med], nums[left] - i, j = left, right - while i < j - while i < j && nums[j] >= nums[left] - j -= 1 # 从右向左找首个小于基准数的元素 - end - while i < j && nums[i] <= nums[left] - i += 1 # 从左向右找首个大于基准数的元素 - end - # 元素交换 - nums[i], nums[j] = nums[j], nums[i] - end - # 将基准数交换至两子数组的分界线 - nums[i], nums[left] = nums[left], nums[i] - i # 返回基准数的索引 - end + [class]{QuickSortMedian}-[func]{partition} ``` === "Zig" ```zig title="quick_sort.zig" - // 选取三个候选元素的中位数 - fn medianThree(nums: []i32, left: usize, mid: usize, right: usize) usize { - var l = nums[left]; - var m = nums[mid]; - var r = nums[right]; - if ((l <= m && m <= r) || (r <= m && m <= l)) - return mid; // m 在 l 和 r 之间 - if ((m <= l && l <= r) || (r <= l && l <= m)) - return left; // l 在 m 和 r 之间 - return right; - } + [class]{QuickSortMedian}-[func]{medianThree} - // 哨兵划分(三数取中值) - fn partition(nums: []i32, left: usize, right: usize) usize { - // 选取三个候选元素的中位数 - var med = medianThree(nums, left, (left + right) / 2, right); - // 将中位数交换至数组最左端 - swap(nums, left, med); - // 以 nums[left] 为基准数 - var i = left; - var j = right; - while (i < j) { - while (i < j and nums[j] >= nums[left]) j -= 1; // 从右向左找首个小于基准数的元素 - while (i < j and nums[i] <= nums[left]) i += 1; // 从左向右找首个大于基准数的元素 - swap(nums, i, j); // 交换这两个元素 - } - swap(nums, i, left); // 将基准数交换至两子数组的分界线 - return i; // 返回基准数的索引 - } + [class]{QuickSortMedian}-[func]{partition} ``` -??? pythontutor "Code Visualization" - -
- - ## 11.5.5   Tail recursion optimization **Under certain inputs, quick sort may occupy more space**. For a completely ordered input array, assume the sub-array length in recursion is $m$, each round of pivot partitioning produces a left sub-array of length $0$ and a right sub-array of length $m - 1$, meaning the problem size reduced per recursive call is very small (only one element), and the height of the recursion tree can reach $n - 1$, requiring $O(n)$ stack frame space. @@ -1178,57 +492,42 @@ To prevent the accumulation of stack frame space, we can compare the lengths of ```python title="quick_sort.py" def quick_sort(self, nums: list[int], left: int, right: int): - """快速排序(尾递归优化)""" - # 子数组长度为 1 时终止 + """Quick sort (tail recursion optimization)""" + # Terminate when subarray length is 1 while left < right: - # 哨兵划分操作 + # Partition operation pivot = self.partition(nums, left, right) - # 对两个子数组中较短的那个执行快速排序 + # Perform quick sort on the shorter of the two subarrays if pivot - left < right - pivot: - self.quick_sort(nums, left, pivot - 1) # 递归排序左子数组 - left = pivot + 1 # 剩余未排序区间为 [pivot + 1, right] + self.quick_sort(nums, left, pivot - 1) # Recursively sort the left subarray + left = pivot + 1 # Remaining unsorted interval is [pivot + 1, right] else: - self.quick_sort(nums, pivot + 1, right) # 递归排序右子数组 - right = pivot - 1 # 剩余未排序区间为 [left, pivot - 1] + self.quick_sort(nums, pivot + 1, right) # Recursively sort the right subarray + right = pivot - 1 # Remaining unsorted interval is [left, pivot - 1] ``` === "C++" ```cpp title="quick_sort.cpp" - /* 快速排序(尾递归优化) */ - void quickSort(vector &nums, int left, int right) { - // 子数组长度为 1 时终止 - while (left < right) { - // 哨兵划分操作 - int pivot = partition(nums, left, right); - // 对两个子数组中较短的那个执行快速排序 - if (pivot - left < right - pivot) { - quickSort(nums, left, pivot - 1); // 递归排序左子数组 - left = pivot + 1; // 剩余未排序区间为 [pivot + 1, right] - } else { - quickSort(nums, pivot + 1, right); // 递归排序右子数组 - right = pivot - 1; // 剩余未排序区间为 [left, pivot - 1] - } - } - } + [class]{QuickSortTailCall}-[func]{quickSort} ``` === "Java" ```java title="quick_sort.java" - /* 快速排序(尾递归优化) */ + /* Quick sort (tail recursion optimization) */ void quickSort(int[] nums, int left, int right) { - // 子数组长度为 1 时终止 + // Terminate when subarray length is 1 while (left < right) { - // 哨兵划分操作 + // Partition operation int pivot = partition(nums, left, right); - // 对两个子数组中较短的那个执行快速排序 + // Perform quick sort on the shorter of the two subarrays if (pivot - left < right - pivot) { - quickSort(nums, left, pivot - 1); // 递归排序左子数组 - left = pivot + 1; // 剩余未排序区间为 [pivot + 1, right] + quickSort(nums, left, pivot - 1); // Recursively sort the left subarray + left = pivot + 1; // Remaining unsorted interval is [pivot + 1, right] } else { - quickSort(nums, pivot + 1, right); // 递归排序右子数组 - right = pivot - 1; // 剩余未排序区间为 [left, pivot - 1] + quickSort(nums, pivot + 1, right); // Recursively sort the right subarray + right = pivot - 1; // Remaining unsorted interval is [left, pivot - 1] } } } @@ -1237,245 +536,65 @@ To prevent the accumulation of stack frame space, we can compare the lengths of === "C#" ```csharp title="quick_sort.cs" - /* 快速排序(尾递归优化) */ - void QuickSort(int[] nums, int left, int right) { - // 子数组长度为 1 时终止 - while (left < right) { - // 哨兵划分操作 - int pivot = Partition(nums, left, right); - // 对两个子数组中较短的那个执行快速排序 - if (pivot - left < right - pivot) { - QuickSort(nums, left, pivot - 1); // 递归排序左子数组 - left = pivot + 1; // 剩余未排序区间为 [pivot + 1, right] - } else { - QuickSort(nums, pivot + 1, right); // 递归排序右子数组 - right = pivot - 1; // 剩余未排序区间为 [left, pivot - 1] - } - } - } + [class]{QuickSortTailCall}-[func]{QuickSort} ``` === "Go" ```go title="quick_sort.go" - /* 快速排序(尾递归优化)*/ - func (q *quickSortTailCall) quickSort(nums []int, left, right int) { - // 子数组长度为 1 时终止 - for left < right { - // 哨兵划分操作 - pivot := q.partition(nums, left, right) - // 对两个子数组中较短的那个执行快速排序 - if pivot-left < right-pivot { - q.quickSort(nums, left, pivot-1) // 递归排序左子数组 - left = pivot + 1 // 剩余未排序区间为 [pivot + 1, right] - } else { - q.quickSort(nums, pivot+1, right) // 递归排序右子数组 - right = pivot - 1 // 剩余未排序区间为 [left, pivot - 1] - } - } - } + [class]{quickSortTailCall}-[func]{quickSort} ``` === "Swift" ```swift title="quick_sort.swift" - /* 快速排序(尾递归优化) */ - func quickSortTailCall(nums: inout [Int], left: Int, right: Int) { - var left = left - var right = right - // 子数组长度为 1 时终止 - while left < right { - // 哨兵划分操作 - let pivot = partition(nums: &nums, left: left, right: right) - // 对两个子数组中较短的那个执行快速排序 - if (pivot - left) < (right - pivot) { - quickSortTailCall(nums: &nums, left: left, right: pivot - 1) // 递归排序左子数组 - left = pivot + 1 // 剩余未排序区间为 [pivot + 1, right] - } else { - quickSortTailCall(nums: &nums, left: pivot + 1, right: right) // 递归排序右子数组 - right = pivot - 1 // 剩余未排序区间为 [left, pivot - 1] - } - } - } + [class]{}-[func]{quickSortTailCall} ``` === "JS" ```javascript title="quick_sort.js" - /* 快速排序(尾递归优化) */ - quickSort(nums, left, right) { - // 子数组长度为 1 时终止 - while (left < right) { - // 哨兵划分操作 - let pivot = this.partition(nums, left, right); - // 对两个子数组中较短的那个执行快速排序 - if (pivot - left < right - pivot) { - this.quickSort(nums, left, pivot - 1); // 递归排序左子数组 - left = pivot + 1; // 剩余未排序区间为 [pivot + 1, right] - } else { - this.quickSort(nums, pivot + 1, right); // 递归排序右子数组 - right = pivot - 1; // 剩余未排序区间为 [left, pivot - 1] - } - } - } + [class]{QuickSortTailCall}-[func]{quickSort} ``` === "TS" ```typescript title="quick_sort.ts" - /* 快速排序(尾递归优化) */ - quickSort(nums: number[], left: number, right: number): void { - // 子数组长度为 1 时终止 - while (left < right) { - // 哨兵划分操作 - let pivot = this.partition(nums, left, right); - // 对两个子数组中较短的那个执行快速排序 - if (pivot - left < right - pivot) { - this.quickSort(nums, left, pivot - 1); // 递归排序左子数组 - left = pivot + 1; // 剩余未排序区间为 [pivot + 1, right] - } else { - this.quickSort(nums, pivot + 1, right); // 递归排序右子数组 - right = pivot - 1; // 剩余未排序区间为 [left, pivot - 1] - } - } - } + [class]{QuickSortTailCall}-[func]{quickSort} ``` === "Dart" ```dart title="quick_sort.dart" - /* 快速排序(尾递归优化) */ - void quickSort(List nums, int left, int right) { - // 子数组长度为 1 时终止 - while (left < right) { - // 哨兵划分操作 - int pivot = _partition(nums, left, right); - // 对两个子数组中较短的那个执行快速排序 - if (pivot - left < right - pivot) { - quickSort(nums, left, pivot - 1); // 递归排序左子数组 - left = pivot + 1; // 剩余未排序区间为 [pivot + 1, right] - } else { - quickSort(nums, pivot + 1, right); // 递归排序右子数组 - right = pivot - 1; // 剩余未排序区间为 [left, pivot - 1] - } - } - } + [class]{QuickSortTailCall}-[func]{quickSort} ``` === "Rust" ```rust title="quick_sort.rs" - /* 快速排序(尾递归优化) */ - pub fn quick_sort(mut left: i32, mut right: i32, nums: &mut [i32]) { - // 子数组长度为 1 时终止 - while left < right { - // 哨兵划分操作 - let pivot = Self::partition(nums, left as usize, right as usize) as i32; - // 对两个子数组中较短的那个执行快速排序 - if pivot - left < right - pivot { - Self::quick_sort(left, pivot - 1, nums); // 递归排序左子数组 - left = pivot + 1; // 剩余未排序区间为 [pivot + 1, right] - } else { - Self::quick_sort(pivot + 1, right, nums); // 递归排序右子数组 - right = pivot - 1; // 剩余未排序区间为 [left, pivot - 1] - } - } - } + [class]{QuickSortTailCall}-[func]{quick_sort} ``` === "C" ```c title="quick_sort.c" - /* 快速排序(尾递归优化) */ - void quickSortTailCall(int nums[], int left, int right) { - // 子数组长度为 1 时终止 - while (left < right) { - // 哨兵划分操作 - int pivot = partition(nums, left, right); - // 对两个子数组中较短的那个执行快速排序 - if (pivot - left < right - pivot) { - // 递归排序左子数组 - quickSortTailCall(nums, left, pivot - 1); - // 剩余未排序区间为 [pivot + 1, right] - left = pivot + 1; - } else { - // 递归排序右子数组 - quickSortTailCall(nums, pivot + 1, right); - // 剩余未排序区间为 [left, pivot - 1] - right = pivot - 1; - } - } - } + [class]{}-[func]{quickSortTailCall} ``` === "Kotlin" ```kotlin title="quick_sort.kt" - /* 快速排序(尾递归优化) */ - fun quickSortTailCall(nums: IntArray, left: Int, right: Int) { - // 子数组长度为 1 时终止 - var l = left - var r = right - while (l < r) { - // 哨兵划分操作 - val pivot = partition(nums, l, r) - // 对两个子数组中较短的那个执行快速排序 - if (pivot - l < r - pivot) { - quickSort(nums, l, pivot - 1) // 递归排序左子数组 - l = pivot + 1 // 剩余未排序区间为 [pivot + 1, right] - } else { - quickSort(nums, pivot + 1, r) // 递归排序右子数组 - r = pivot - 1 // 剩余未排序区间为 [left, pivot - 1] - } - } - } + [class]{}-[func]{quickSortTailCall} ``` === "Ruby" ```ruby title="quick_sort.rb" - ### 快速排序(尾递归优化)### - def quick_sort(nums, left, right) - # 子数组长度不为 1 时递归 - while left < right - # 哨兵划分 - pivot = partition(nums, left, right) - # 对两个子数组中较短的那个执行快速排序 - if pivot - left < right - pivot - quick_sort(nums, left, pivot - 1) - left = pivot + 1 # 剩余未排序区间为 [pivot + 1, right] - else - quick_sort(nums, pivot + 1, right) - right = pivot - 1 # 剩余未排序区间为 [left, pivot - 1] - end - end - end + [class]{QuickSortTailCall}-[func]{quick_sort} ``` === "Zig" ```zig title="quick_sort.zig" - // 快速排序(尾递归优化) - fn quickSort(nums: []i32, left_: usize, right_: usize) void { - var left = left_; - var right = right_; - // 子数组长度为 1 时终止递归 - while (left < right) { - // 哨兵划分操作 - var pivot = partition(nums, left, right); - // 对两个子数组中较短的那个执行快速排序 - if (pivot - left < right - pivot) { - quickSort(nums, left, pivot - 1); // 递归排序左子数组 - left = pivot + 1; // 剩余未排序区间为 [pivot + 1, right] - } else { - quickSort(nums, pivot + 1, right); // 递归排序右子数组 - right = pivot - 1; // 剩余未排序区间为 [left, pivot - 1] - } - } - } + [class]{QuickSortTailCall}-[func]{quickSort} ``` - -??? pythontutor "Code Visualization" - -
- diff --git a/en/docs/chapter_sorting/radix_sort.md b/en/docs/chapter_sorting/radix_sort.md index 35b9dfd6e..d5a27bed1 100644 --- a/en/docs/chapter_sorting/radix_sort.md +++ b/en/docs/chapter_sorting/radix_sort.md @@ -34,44 +34,44 @@ Additionally, we need to slightly modify the counting sort code to allow sorting ```python title="radix_sort.py" def digit(num: int, exp: int) -> int: - """获取元素 num 的第 k 位,其中 exp = 10^(k-1)""" - # 传入 exp 而非 k 可以避免在此重复执行昂贵的次方计算 + """Get the k-th digit of element num, where exp = 10^(k-1)""" + # Passing exp instead of k can avoid repeated expensive exponentiation here return (num // exp) % 10 def counting_sort_digit(nums: list[int], exp: int): - """计数排序(根据 nums 第 k 位排序)""" - # 十进制的位范围为 0~9 ,因此需要长度为 10 的桶数组 + """Counting sort (based on nums k-th digit)""" + # Decimal digit range is 0~9, therefore need a bucket array of length 10 counter = [0] * 10 n = len(nums) - # 统计 0~9 各数字的出现次数 + # Count the occurrence of digits 0~9 for i in range(n): - d = digit(nums[i], exp) # 获取 nums[i] 第 k 位,记为 d - counter[d] += 1 # 统计数字 d 的出现次数 - # 求前缀和,将“出现个数”转换为“数组索引” + d = digit(nums[i], exp) # Get the k-th digit of nums[i], noted as d + counter[d] += 1 # Count the occurrence of digit d + # Calculate prefix sum, converting "occurrence count" into "array index" for i in range(1, 10): counter[i] += counter[i - 1] - # 倒序遍历,根据桶内统计结果,将各元素填入 res + # Traverse in reverse, based on bucket statistics, place each element into res res = [0] * n for i in range(n - 1, -1, -1): d = digit(nums[i], exp) - j = counter[d] - 1 # 获取 d 在数组中的索引 j - res[j] = nums[i] # 将当前元素填入索引 j - counter[d] -= 1 # 将 d 的数量减 1 - # 使用结果覆盖原数组 nums + j = counter[d] - 1 # Get the index j for d in the array + res[j] = nums[i] # Place the current element at index j + counter[d] -= 1 # Decrease the count of d by 1 + # Use result to overwrite the original array nums for i in range(n): nums[i] = res[i] def radix_sort(nums: list[int]): - """基数排序""" - # 获取数组的最大元素,用于判断最大位数 + """Radix sort""" + # Get the maximum element of the array, used to determine the maximum number of digits m = max(nums) - # 按照从低位到高位的顺序遍历 + # Traverse from the lowest to the highest digit exp = 1 while exp <= m: - # 对数组元素的第 k 位执行计数排序 + # Perform counting sort on the k-th digit of array elements # k = 1 -> exp = 1 # k = 2 -> exp = 10 - # 即 exp = 10^(k-1) + # i.e., exp = 10^(k-1) counting_sort_digit(nums, exp) exp *= 10 ``` @@ -79,102 +79,62 @@ Additionally, we need to slightly modify the counting sort code to allow sorting === "C++" ```cpp title="radix_sort.cpp" - /* 获取元素 num 的第 k 位,其中 exp = 10^(k-1) */ - int digit(int num, int exp) { - // 传入 exp 而非 k 可以避免在此重复执行昂贵的次方计算 - return (num / exp) % 10; - } + [class]{}-[func]{digit} - /* 计数排序(根据 nums 第 k 位排序) */ - void countingSortDigit(vector &nums, int exp) { - // 十进制的位范围为 0~9 ,因此需要长度为 10 的桶数组 - vector counter(10, 0); - int n = nums.size(); - // 统计 0~9 各数字的出现次数 - for (int i = 0; i < n; i++) { - int d = digit(nums[i], exp); // 获取 nums[i] 第 k 位,记为 d - counter[d]++; // 统计数字 d 的出现次数 - } - // 求前缀和,将“出现个数”转换为“数组索引” - for (int i = 1; i < 10; i++) { - counter[i] += counter[i - 1]; - } - // 倒序遍历,根据桶内统计结果,将各元素填入 res - vector res(n, 0); - for (int i = n - 1; i >= 0; i--) { - int d = digit(nums[i], exp); - int j = counter[d] - 1; // 获取 d 在数组中的索引 j - res[j] = nums[i]; // 将当前元素填入索引 j - counter[d]--; // 将 d 的数量减 1 - } - // 使用结果覆盖原数组 nums - for (int i = 0; i < n; i++) - nums[i] = res[i]; - } + [class]{}-[func]{countingSortDigit} - /* 基数排序 */ - void radixSort(vector &nums) { - // 获取数组的最大元素,用于判断最大位数 - int m = *max_element(nums.begin(), nums.end()); - // 按照从低位到高位的顺序遍历 - for (int exp = 1; exp <= m; exp *= 10) - // 对数组元素的第 k 位执行计数排序 - // k = 1 -> exp = 1 - // k = 2 -> exp = 10 - // 即 exp = 10^(k-1) - countingSortDigit(nums, exp); - } + [class]{}-[func]{radixSort} ``` === "Java" ```java title="radix_sort.java" - /* 获取元素 num 的第 k 位,其中 exp = 10^(k-1) */ + /* Get the k-th digit of element num, where exp = 10^(k-1) */ int digit(int num, int exp) { - // 传入 exp 而非 k 可以避免在此重复执行昂贵的次方计算 + // Passing exp instead of k can avoid repeated expensive exponentiation here return (num / exp) % 10; } - /* 计数排序(根据 nums 第 k 位排序) */ + /* Counting sort (based on nums k-th digit) */ void countingSortDigit(int[] nums, int exp) { - // 十进制的位范围为 0~9 ,因此需要长度为 10 的桶数组 + // Decimal digit range is 0~9, therefore need a bucket array of length 10 int[] counter = new int[10]; int n = nums.length; - // 统计 0~9 各数字的出现次数 + // Count the occurrence of digits 0~9 for (int i = 0; i < n; i++) { - int d = digit(nums[i], exp); // 获取 nums[i] 第 k 位,记为 d - counter[d]++; // 统计数字 d 的出现次数 + int d = digit(nums[i], exp); // Get the k-th digit of nums[i], noted as d + counter[d]++; // Count the occurrence of digit d } - // 求前缀和,将“出现个数”转换为“数组索引” + // Calculate prefix sum, converting "occurrence count" into "array index" for (int i = 1; i < 10; i++) { counter[i] += counter[i - 1]; } - // 倒序遍历,根据桶内统计结果,将各元素填入 res + // Traverse in reverse, based on bucket statistics, place each element into res int[] res = new int[n]; for (int i = n - 1; i >= 0; i--) { int d = digit(nums[i], exp); - int j = counter[d] - 1; // 获取 d 在数组中的索引 j - res[j] = nums[i]; // 将当前元素填入索引 j - counter[d]--; // 将 d 的数量减 1 + int j = counter[d] - 1; // Get the index j for d in the array + res[j] = nums[i]; // Place the current element at index j + counter[d]--; // Decrease the count of d by 1 } - // 使用结果覆盖原数组 nums + // Use result to overwrite the original array nums for (int i = 0; i < n; i++) nums[i] = res[i]; } - /* 基数排序 */ + /* Radix sort */ void radixSort(int[] nums) { - // 获取数组的最大元素,用于判断最大位数 + // Get the maximum element of the array, used to determine the maximum number of digits int m = Integer.MIN_VALUE; for (int num : nums) if (num > m) m = num; - // 按照从低位到高位的顺序遍历 + // Traverse from the lowest to the highest digit for (int exp = 1; exp <= m; exp *= 10) { - // 对数组元素的第 k 位执行计数排序 + // Perform counting sort on the k-th digit of array elements // k = 1 -> exp = 1 // k = 2 -> exp = 10 - // 即 exp = 10^(k-1) + // i.e., exp = 10^(k-1) countingSortDigit(nums, exp); } } @@ -183,616 +143,113 @@ Additionally, we need to slightly modify the counting sort code to allow sorting === "C#" ```csharp title="radix_sort.cs" - /* 获取元素 num 的第 k 位,其中 exp = 10^(k-1) */ - int Digit(int num, int exp) { - // 传入 exp 而非 k 可以避免在此重复执行昂贵的次方计算 - return (num / exp) % 10; - } + [class]{radix_sort}-[func]{Digit} - /* 计数排序(根据 nums 第 k 位排序) */ - void CountingSortDigit(int[] nums, int exp) { - // 十进制的位范围为 0~9 ,因此需要长度为 10 的桶数组 - int[] counter = new int[10]; - int n = nums.Length; - // 统计 0~9 各数字的出现次数 - for (int i = 0; i < n; i++) { - int d = Digit(nums[i], exp); // 获取 nums[i] 第 k 位,记为 d - counter[d]++; // 统计数字 d 的出现次数 - } - // 求前缀和,将“出现个数”转换为“数组索引” - for (int i = 1; i < 10; i++) { - counter[i] += counter[i - 1]; - } - // 倒序遍历,根据桶内统计结果,将各元素填入 res - int[] res = new int[n]; - for (int i = n - 1; i >= 0; i--) { - int d = Digit(nums[i], exp); - int j = counter[d] - 1; // 获取 d 在数组中的索引 j - res[j] = nums[i]; // 将当前元素填入索引 j - counter[d]--; // 将 d 的数量减 1 - } - // 使用结果覆盖原数组 nums - for (int i = 0; i < n; i++) { - nums[i] = res[i]; - } - } + [class]{radix_sort}-[func]{CountingSortDigit} - /* 基数排序 */ - void RadixSort(int[] nums) { - // 获取数组的最大元素,用于判断最大位数 - int m = int.MinValue; - foreach (int num in nums) { - if (num > m) m = num; - } - // 按照从低位到高位的顺序遍历 - for (int exp = 1; exp <= m; exp *= 10) { - // 对数组元素的第 k 位执行计数排序 - // k = 1 -> exp = 1 - // k = 2 -> exp = 10 - // 即 exp = 10^(k-1) - CountingSortDigit(nums, exp); - } - } + [class]{radix_sort}-[func]{RadixSort} ``` === "Go" ```go title="radix_sort.go" - /* 获取元素 num 的第 k 位,其中 exp = 10^(k-1) */ - func digit(num, exp int) int { - // 传入 exp 而非 k 可以避免在此重复执行昂贵的次方计算 - return (num / exp) % 10 - } + [class]{}-[func]{digit} - /* 计数排序(根据 nums 第 k 位排序) */ - func countingSortDigit(nums []int, exp int) { - // 十进制的位范围为 0~9 ,因此需要长度为 10 的桶数组 - counter := make([]int, 10) - n := len(nums) - // 统计 0~9 各数字的出现次数 - for i := 0; i < n; i++ { - d := digit(nums[i], exp) // 获取 nums[i] 第 k 位,记为 d - counter[d]++ // 统计数字 d 的出现次数 - } - // 求前缀和,将“出现个数”转换为“数组索引” - for i := 1; i < 10; i++ { - counter[i] += counter[i-1] - } - // 倒序遍历,根据桶内统计结果,将各元素填入 res - res := make([]int, n) - for i := n - 1; i >= 0; i-- { - d := digit(nums[i], exp) - j := counter[d] - 1 // 获取 d 在数组中的索引 j - res[j] = nums[i] // 将当前元素填入索引 j - counter[d]-- // 将 d 的数量减 1 - } - // 使用结果覆盖原数组 nums - for i := 0; i < n; i++ { - nums[i] = res[i] - } - } + [class]{}-[func]{countingSortDigit} - /* 基数排序 */ - func radixSort(nums []int) { - // 获取数组的最大元素,用于判断最大位数 - max := math.MinInt - for _, num := range nums { - if num > max { - max = num - } - } - // 按照从低位到高位的顺序遍历 - for exp := 1; max >= exp; exp *= 10 { - // 对数组元素的第 k 位执行计数排序 - // k = 1 -> exp = 1 - // k = 2 -> exp = 10 - // 即 exp = 10^(k-1) - countingSortDigit(nums, exp) - } - } + [class]{}-[func]{radixSort} ``` === "Swift" ```swift title="radix_sort.swift" - /* 获取元素 num 的第 k 位,其中 exp = 10^(k-1) */ - func digit(num: Int, exp: Int) -> Int { - // 传入 exp 而非 k 可以避免在此重复执行昂贵的次方计算 - (num / exp) % 10 - } + [class]{}-[func]{digit} - /* 计数排序(根据 nums 第 k 位排序) */ - func countingSortDigit(nums: inout [Int], exp: Int) { - // 十进制的位范围为 0~9 ,因此需要长度为 10 的桶数组 - var counter = Array(repeating: 0, count: 10) - // 统计 0~9 各数字的出现次数 - for i in nums.indices { - let d = digit(num: nums[i], exp: exp) // 获取 nums[i] 第 k 位,记为 d - counter[d] += 1 // 统计数字 d 的出现次数 - } - // 求前缀和,将“出现个数”转换为“数组索引” - for i in 1 ..< 10 { - counter[i] += counter[i - 1] - } - // 倒序遍历,根据桶内统计结果,将各元素填入 res - var res = Array(repeating: 0, count: nums.count) - for i in nums.indices.reversed() { - let d = digit(num: nums[i], exp: exp) - let j = counter[d] - 1 // 获取 d 在数组中的索引 j - res[j] = nums[i] // 将当前元素填入索引 j - counter[d] -= 1 // 将 d 的数量减 1 - } - // 使用结果覆盖原数组 nums - for i in nums.indices { - nums[i] = res[i] - } - } + [class]{}-[func]{countingSortDigit} - /* 基数排序 */ - func radixSort(nums: inout [Int]) { - // 获取数组的最大元素,用于判断最大位数 - var m = Int.min - for num in nums { - if num > m { - m = num - } - } - // 按照从低位到高位的顺序遍历 - for exp in sequence(first: 1, next: { m >= ($0 * 10) ? $0 * 10 : nil }) { - // 对数组元素的第 k 位执行计数排序 - // k = 1 -> exp = 1 - // k = 2 -> exp = 10 - // 即 exp = 10^(k-1) - countingSortDigit(nums: &nums, exp: exp) - } - } + [class]{}-[func]{radixSort} ``` === "JS" ```javascript title="radix_sort.js" - /* 获取元素 num 的第 k 位,其中 exp = 10^(k-1) */ - function digit(num, exp) { - // 传入 exp 而非 k 可以避免在此重复执行昂贵的次方计算 - return Math.floor(num / exp) % 10; - } + [class]{}-[func]{digit} - /* 计数排序(根据 nums 第 k 位排序) */ - function countingSortDigit(nums, exp) { - // 十进制的位范围为 0~9 ,因此需要长度为 10 的桶数组 - const counter = new Array(10).fill(0); - const n = nums.length; - // 统计 0~9 各数字的出现次数 - for (let i = 0; i < n; i++) { - const d = digit(nums[i], exp); // 获取 nums[i] 第 k 位,记为 d - counter[d]++; // 统计数字 d 的出现次数 - } - // 求前缀和,将“出现个数”转换为“数组索引” - for (let i = 1; i < 10; i++) { - counter[i] += counter[i - 1]; - } - // 倒序遍历,根据桶内统计结果,将各元素填入 res - const res = new Array(n).fill(0); - for (let i = n - 1; i >= 0; i--) { - const d = digit(nums[i], exp); - const j = counter[d] - 1; // 获取 d 在数组中的索引 j - res[j] = nums[i]; // 将当前元素填入索引 j - counter[d]--; // 将 d 的数量减 1 - } - // 使用结果覆盖原数组 nums - for (let i = 0; i < n; i++) { - nums[i] = res[i]; - } - } + [class]{}-[func]{countingSortDigit} - /* 基数排序 */ - function radixSort(nums) { - // 获取数组的最大元素,用于判断最大位数 - let m = Number.MIN_VALUE; - for (const num of nums) { - if (num > m) { - m = num; - } - } - // 按照从低位到高位的顺序遍历 - for (let exp = 1; exp <= m; exp *= 10) { - // 对数组元素的第 k 位执行计数排序 - // k = 1 -> exp = 1 - // k = 2 -> exp = 10 - // 即 exp = 10^(k-1) - countingSortDigit(nums, exp); - } - } + [class]{}-[func]{radixSort} ``` === "TS" ```typescript title="radix_sort.ts" - /* 获取元素 num 的第 k 位,其中 exp = 10^(k-1) */ - function digit(num: number, exp: number): number { - // 传入 exp 而非 k 可以避免在此重复执行昂贵的次方计算 - return Math.floor(num / exp) % 10; - } + [class]{}-[func]{digit} - /* 计数排序(根据 nums 第 k 位排序) */ - function countingSortDigit(nums: number[], exp: number): void { - // 十进制的位范围为 0~9 ,因此需要长度为 10 的桶数组 - const counter = new Array(10).fill(0); - const n = nums.length; - // 统计 0~9 各数字的出现次数 - for (let i = 0; i < n; i++) { - const d = digit(nums[i], exp); // 获取 nums[i] 第 k 位,记为 d - counter[d]++; // 统计数字 d 的出现次数 - } - // 求前缀和,将“出现个数”转换为“数组索引” - for (let i = 1; i < 10; i++) { - counter[i] += counter[i - 1]; - } - // 倒序遍历,根据桶内统计结果,将各元素填入 res - const res = new Array(n).fill(0); - for (let i = n - 1; i >= 0; i--) { - const d = digit(nums[i], exp); - const j = counter[d] - 1; // 获取 d 在数组中的索引 j - res[j] = nums[i]; // 将当前元素填入索引 j - counter[d]--; // 将 d 的数量减 1 - } - // 使用结果覆盖原数组 nums - for (let i = 0; i < n; i++) { - nums[i] = res[i]; - } - } + [class]{}-[func]{countingSortDigit} - /* 基数排序 */ - function radixSort(nums: number[]): void { - // 获取数组的最大元素,用于判断最大位数 - let m = Number.MIN_VALUE; - for (const num of nums) { - if (num > m) { - m = num; - } - } - // 按照从低位到高位的顺序遍历 - for (let exp = 1; exp <= m; exp *= 10) { - // 对数组元素的第 k 位执行计数排序 - // k = 1 -> exp = 1 - // k = 2 -> exp = 10 - // 即 exp = 10^(k-1) - countingSortDigit(nums, exp); - } - } + [class]{}-[func]{radixSort} ``` === "Dart" ```dart title="radix_sort.dart" - /* 获取元素 _num 的第 k 位,其中 exp = 10^(k-1) */ - int digit(int _num, int exp) { - // 传入 exp 而非 k 可以避免在此重复执行昂贵的次方计算 - return (_num ~/ exp) % 10; - } + [class]{}-[func]{digit} - /* 计数排序(根据 nums 第 k 位排序) */ - void countingSortDigit(List nums, int exp) { - // 十进制的位范围为 0~9 ,因此需要长度为 10 的桶数组 - List counter = List.filled(10, 0); - int n = nums.length; - // 统计 0~9 各数字的出现次数 - for (int i = 0; i < n; i++) { - int d = digit(nums[i], exp); // 获取 nums[i] 第 k 位,记为 d - counter[d]++; // 统计数字 d 的出现次数 - } - // 求前缀和,将“出现个数”转换为“数组索引” - for (int i = 1; i < 10; i++) { - counter[i] += counter[i - 1]; - } - // 倒序遍历,根据桶内统计结果,将各元素填入 res - List res = List.filled(n, 0); - for (int i = n - 1; i >= 0; i--) { - int d = digit(nums[i], exp); - int j = counter[d] - 1; // 获取 d 在数组中的索引 j - res[j] = nums[i]; // 将当前元素填入索引 j - counter[d]--; // 将 d 的数量减 1 - } - // 使用结果覆盖原数组 nums - for (int i = 0; i < n; i++) nums[i] = res[i]; - } + [class]{}-[func]{countingSortDigit} - /* 基数排序 */ - void radixSort(List nums) { - // 获取数组的最大元素,用于判断最大位数 - // dart 中 int 的长度是 64 位的 - int m = -1 << 63; - for (int _num in nums) if (_num > m) m = _num; - // 按照从低位到高位的顺序遍历 - for (int exp = 1; exp <= m; exp *= 10) - // 对数组元素的第 k 位执行计数排序 - // k = 1 -> exp = 1 - // k = 2 -> exp = 10 - // 即 exp = 10^(k-1) - countingSortDigit(nums, exp); - } + [class]{}-[func]{radixSort} ``` === "Rust" ```rust title="radix_sort.rs" - /* 获取元素 num 的第 k 位,其中 exp = 10^(k-1) */ - fn digit(num: i32, exp: i32) -> usize { - // 传入 exp 而非 k 可以避免在此重复执行昂贵的次方计算 - return ((num / exp) % 10) as usize; - } + [class]{}-[func]{digit} - /* 计数排序(根据 nums 第 k 位排序) */ - fn counting_sort_digit(nums: &mut [i32], exp: i32) { - // 十进制的位范围为 0~9 ,因此需要长度为 10 的桶数组 - let mut counter = [0; 10]; - let n = nums.len(); - // 统计 0~9 各数字的出现次数 - for i in 0..n { - let d = digit(nums[i], exp); // 获取 nums[i] 第 k 位,记为 d - counter[d] += 1; // 统计数字 d 的出现次数 - } - // 求前缀和,将“出现个数”转换为“数组索引” - for i in 1..10 { - counter[i] += counter[i - 1]; - } - // 倒序遍历,根据桶内统计结果,将各元素填入 res - let mut res = vec![0; n]; - for i in (0..n).rev() { - let d = digit(nums[i], exp); - let j = counter[d] - 1; // 获取 d 在数组中的索引 j - res[j] = nums[i]; // 将当前元素填入索引 j - counter[d] -= 1; // 将 d 的数量减 1 - } - // 使用结果覆盖原数组 nums - for i in 0..n { - nums[i] = res[i]; - } - } + [class]{}-[func]{counting_sort_digit} - /* 基数排序 */ - fn radix_sort(nums: &mut [i32]) { - // 获取数组的最大元素,用于判断最大位数 - let m = *nums.into_iter().max().unwrap(); - // 按照从低位到高位的顺序遍历 - let mut exp = 1; - while exp <= m { - counting_sort_digit(nums, exp); - exp *= 10; - } - } + [class]{}-[func]{radix_sort} ``` === "C" ```c title="radix_sort.c" - /* 获取元素 num 的第 k 位,其中 exp = 10^(k-1) */ - int digit(int num, int exp) { - // 传入 exp 而非 k 可以避免在此重复执行昂贵的次方计算 - return (num / exp) % 10; - } + [class]{}-[func]{digit} - /* 计数排序(根据 nums 第 k 位排序) */ - void countingSortDigit(int nums[], int size, int exp) { - // 十进制的位范围为 0~9 ,因此需要长度为 10 的桶数组 - int *counter = (int *)malloc((sizeof(int) * 10)); - // 统计 0~9 各数字的出现次数 - for (int i = 0; i < size; i++) { - // 获取 nums[i] 第 k 位,记为 d - int d = digit(nums[i], exp); - // 统计数字 d 的出现次数 - counter[d]++; - } - // 求前缀和,将“出现个数”转换为“数组索引” - for (int i = 1; i < 10; i++) { - counter[i] += counter[i - 1]; - } - // 倒序遍历,根据桶内统计结果,将各元素填入 res - int *res = (int *)malloc(sizeof(int) * size); - for (int i = size - 1; i >= 0; i--) { - int d = digit(nums[i], exp); - int j = counter[d] - 1; // 获取 d 在数组中的索引 j - res[j] = nums[i]; // 将当前元素填入索引 j - counter[d]--; // 将 d 的数量减 1 - } - // 使用结果覆盖原数组 nums - for (int i = 0; i < size; i++) { - nums[i] = res[i]; - } - } + [class]{}-[func]{countingSortDigit} - /* 基数排序 */ - void radixSort(int nums[], int size) { - // 获取数组的最大元素,用于判断最大位数 - int max = INT32_MIN; - for (size_t i = 0; i < size - 1; i++) { - if (nums[i] > max) { - max = nums[i]; - } - } - // 按照从低位到高位的顺序遍历 - for (int exp = 1; max >= exp; exp *= 10) - // 对数组元素的第 k 位执行计数排序 - // k = 1 -> exp = 1 - // k = 2 -> exp = 10 - // 即 exp = 10^(k-1) - countingSortDigit(nums, size, exp); - } + [class]{}-[func]{radixSort} ``` === "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 - } + [class]{}-[func]{digit} - /* 计数排序(根据 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 - } - } + [class]{}-[func]{radixSort} ``` === "Ruby" ```ruby title="radix_sort.rb" - ### 获取元素 num 的第 k 位,其中 exp = 10^(k-1) ### - def digit(num, exp) - # 转入 exp 而非 k 可以避免在此重复执行昂贵的次方计算 - (num / exp) % 10 - end + [class]{}-[func]{digit} - ### 计数排序(根据 nums 第 k 位排序)### - def counting_sort_digit(nums, exp) - # 十进制的位范围为 0~9 ,因此需要长度为 10 的桶数组 - counter = Array.new(10, 0) - n = nums.length - # 统计 0~9 各数字的出现次数 - for i in 0...n - d = digit(nums[i], exp) # 获取 nums[i] 第 k 位,记为 d - counter[d] += 1 # 统计数字 d 的出现次数 - end - # 求前缀和,将“出现个数”转换为“数组索引” - (1...10).each { |i| counter[i] += counter[i - 1] } - # 倒序遍历,根据桶内统计结果,将各元素填入 res - res = Array.new(n, 0) - for i in (n - 1).downto(0) - d = digit(nums[i], exp) - j = counter[d] - 1 # 获取 d 在数组中的索引 j - res[j] = nums[i] # 将当前元素填入索引 j - counter[d] -= 1 # 将 d 的数量减 1 - end - # 使用结果覆盖原数组 nums - (0...n).each { |i| nums[i] = res[i] } - end + [class]{}-[func]{counting_sort_digit} - ### 基数排序 ### - def radix_sort(nums) - # 获取数组的最大元素,用于判断最大位数 - m = nums.max - # 按照从低位到高位的顺序遍历 - exp = 1 - while exp <= m - # 对数组元素的第 k 位执行计数排序 - # k = 1 -> exp = 1 - # k = 2 -> exp = 10 - # 即 exp = 10^(k-1) - counting_sort_digit(nums, exp) - exp *= 10 - end - end + [class]{}-[func]{radix_sort} ``` === "Zig" ```zig title="radix_sort.zig" - // 获取元素 num 的第 k 位,其中 exp = 10^(k-1) - fn digit(num: i32, exp: i32) i32 { - // 传入 exp 而非 k 可以避免在此重复执行昂贵的次方计算 - return @mod(@divFloor(num, exp), 10); - } + [class]{}-[func]{digit} - // 计数排序(根据 nums 第 k 位排序) - fn countingSortDigit(nums: []i32, exp: i32) !void { - // 十进制的位范围为 0~9 ,因此需要长度为 10 的桶数组 - var mem_arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); - // defer mem_arena.deinit(); - const mem_allocator = mem_arena.allocator(); - var counter = try mem_allocator.alloc(usize, 10); - @memset(counter, 0); - var n = nums.len; - // 统计 0~9 各数字的出现次数 - for (nums) |num| { - var d: u32 = @bitCast(digit(num, exp)); // 获取 nums[i] 第 k 位,记为 d - counter[d] += 1; // 统计数字 d 的出现次数 - } - // 求前缀和,将“出现个数”转换为“数组索引” - var i: usize = 1; - while (i < 10) : (i += 1) { - counter[i] += counter[i - 1]; - } - // 倒序遍历,根据桶内统计结果,将各元素填入 res - var res = try mem_allocator.alloc(i32, n); - i = n - 1; - while (i >= 0) : (i -= 1) { - var d: u32 = @bitCast(digit(nums[i], exp)); - var j = counter[d] - 1; // 获取 d 在数组中的索引 j - res[j] = nums[i]; // 将当前元素填入索引 j - counter[d] -= 1; // 将 d 的数量减 1 - if (i == 0) break; - } - // 使用结果覆盖原数组 nums - i = 0; - while (i < n) : (i += 1) { - nums[i] = res[i]; - } - } + [class]{}-[func]{countingSortDigit} - // 基数排序 - fn radixSort(nums: []i32) !void { - // 获取数组的最大元素,用于判断最大位数 - var m: i32 = std.math.minInt(i32); - for (nums) |num| { - if (num > m) m = num; - } - // 按照从低位到高位的顺序遍历 - var exp: i32 = 1; - while (exp <= m) : (exp *= 10) { - // 对数组元素的第 k 位执行计数排序 - // k = 1 -> exp = 1 - // k = 2 -> exp = 10 - // 即 exp = 10^(k-1) - try countingSortDigit(nums, exp); - } - } + [class]{}-[func]{radixSort} ``` -??? pythontutor "Code Visualization" - -
- - !!! question "Why start sorting from the least significant digit?" In consecutive sorting rounds, the result of a later round will override the result of an earlier round. For example, if the result of the first round is $a < b$ and the result of the second round is $a > b$, the result of the second round will replace the first round's result. Since the significance of higher digits is greater than that of lower digits, it makes sense to sort lower digits before higher digits. diff --git a/en/docs/chapter_sorting/selection_sort.md b/en/docs/chapter_sorting/selection_sort.md index 3af22ab6b..cf2a8237b 100644 --- a/en/docs/chapter_sorting/selection_sort.md +++ b/en/docs/chapter_sorting/selection_sort.md @@ -55,54 +55,40 @@ In the code, we use $k$ to record the smallest element within the unsorted inter ```python title="selection_sort.py" def selection_sort(nums: list[int]): - """选择排序""" + """Selection sort""" n = len(nums) - # 外循环:未排序区间为 [i, n-1] + # Outer loop: unsorted range is [i, n-1] for i in range(n - 1): - # 内循环:找到未排序区间内的最小元素 + # Inner loop: find the smallest element within the unsorted range k = i for j in range(i + 1, n): if nums[j] < nums[k]: - k = j # 记录最小元素的索引 - # 将该最小元素与未排序区间的首个元素交换 + k = j # Record the index of the smallest element + # Swap the smallest element with the first element of the unsorted range nums[i], nums[k] = nums[k], nums[i] ``` === "C++" ```cpp title="selection_sort.cpp" - /* 选择排序 */ - void selectionSort(vector &nums) { - int n = nums.size(); - // 外循环:未排序区间为 [i, n-1] - for (int i = 0; i < n - 1; i++) { - // 内循环:找到未排序区间内的最小元素 - int k = i; - for (int j = i + 1; j < n; j++) { - if (nums[j] < nums[k]) - k = j; // 记录最小元素的索引 - } - // 将该最小元素与未排序区间的首个元素交换 - swap(nums[i], nums[k]); - } - } + [class]{}-[func]{selectionSort} ``` === "Java" ```java title="selection_sort.java" - /* 选择排序 */ + /* Selection sort */ void selectionSort(int[] nums) { int n = nums.length; - // 外循环:未排序区间为 [i, n-1] + // Outer loop: unsorted range is [i, n-1] for (int i = 0; i < n - 1; i++) { - // 内循环:找到未排序区间内的最小元素 + // Inner loop: find the smallest element within the unsorted range int k = i; for (int j = i + 1; j < n; j++) { if (nums[j] < nums[k]) - k = j; // 记录最小元素的索引 + k = j; // Record the index of the smallest element } - // 将该最小元素与未排序区间的首个元素交换 + // Swap the smallest element with the first element of the unsorted range int temp = nums[i]; nums[i] = nums[k]; nums[k] = temp; @@ -113,215 +99,61 @@ In the code, we use $k$ to record the smallest element within the unsorted inter === "C#" ```csharp title="selection_sort.cs" - /* 选择排序 */ - void SelectionSort(int[] nums) { - int n = nums.Length; - // 外循环:未排序区间为 [i, n-1] - for (int i = 0; i < n - 1; i++) { - // 内循环:找到未排序区间内的最小元素 - int k = i; - for (int j = i + 1; j < n; j++) { - if (nums[j] < nums[k]) - k = j; // 记录最小元素的索引 - } - // 将该最小元素与未排序区间的首个元素交换 - (nums[k], nums[i]) = (nums[i], nums[k]); - } - } + [class]{selection_sort}-[func]{SelectionSort} ``` === "Go" ```go title="selection_sort.go" - /* 选择排序 */ - func selectionSort(nums []int) { - n := len(nums) - // 外循环:未排序区间为 [i, n-1] - for i := 0; i < n-1; i++ { - // 内循环:找到未排序区间内的最小元素 - k := i - for j := i + 1; j < n; j++ { - if nums[j] < nums[k] { - // 记录最小元素的索引 - k = j - } - } - // 将该最小元素与未排序区间的首个元素交换 - nums[i], nums[k] = nums[k], nums[i] - - } - } + [class]{}-[func]{selectionSort} ``` === "Swift" ```swift title="selection_sort.swift" - /* 选择排序 */ - func selectionSort(nums: inout [Int]) { - // 外循环:未排序区间为 [i, n-1] - for i in nums.indices.dropLast() { - // 内循环:找到未排序区间内的最小元素 - var k = i - for j in nums.indices.dropFirst(i + 1) { - if nums[j] < nums[k] { - k = j // 记录最小元素的索引 - } - } - // 将该最小元素与未排序区间的首个元素交换 - nums.swapAt(i, k) - } - } + [class]{}-[func]{selectionSort} ``` === "JS" ```javascript title="selection_sort.js" - /* 选择排序 */ - function selectionSort(nums) { - let n = nums.length; - // 外循环:未排序区间为 [i, n-1] - for (let i = 0; i < n - 1; i++) { - // 内循环:找到未排序区间内的最小元素 - let k = i; - for (let j = i + 1; j < n; j++) { - if (nums[j] < nums[k]) { - k = j; // 记录最小元素的索引 - } - } - // 将该最小元素与未排序区间的首个元素交换 - [nums[i], nums[k]] = [nums[k], nums[i]]; - } - } + [class]{}-[func]{selectionSort} ``` === "TS" ```typescript title="selection_sort.ts" - /* 选择排序 */ - function selectionSort(nums: number[]): void { - let n = nums.length; - // 外循环:未排序区间为 [i, n-1] - for (let i = 0; i < n - 1; i++) { - // 内循环:找到未排序区间内的最小元素 - let k = i; - for (let j = i + 1; j < n; j++) { - if (nums[j] < nums[k]) { - k = j; // 记录最小元素的索引 - } - } - // 将该最小元素与未排序区间的首个元素交换 - [nums[i], nums[k]] = [nums[k], nums[i]]; - } - } + [class]{}-[func]{selectionSort} ``` === "Dart" ```dart title="selection_sort.dart" - /* 选择排序 */ - void selectionSort(List nums) { - int n = nums.length; - // 外循环:未排序区间为 [i, n-1] - for (int i = 0; i < n - 1; i++) { - // 内循环:找到未排序区间内的最小元素 - int k = i; - for (int j = i + 1; j < n; j++) { - if (nums[j] < nums[k]) k = j; // 记录最小元素的索引 - } - // 将该最小元素与未排序区间的首个元素交换 - int temp = nums[i]; - nums[i] = nums[k]; - nums[k] = temp; - } - } + [class]{}-[func]{selectionSort} ``` === "Rust" ```rust title="selection_sort.rs" - /* 选择排序 */ - fn selection_sort(nums: &mut [i32]) { - if nums.is_empty() { - return; - } - let n = nums.len(); - // 外循环:未排序区间为 [i, n-1] - for i in 0..n - 1 { - // 内循环:找到未排序区间内的最小元素 - let mut k = i; - for j in i + 1..n { - if nums[j] < nums[k] { - k = j; // 记录最小元素的索引 - } - } - // 将该最小元素与未排序区间的首个元素交换 - nums.swap(i, k); - } - } + [class]{}-[func]{selection_sort} ``` === "C" ```c title="selection_sort.c" - /* 选择排序 */ - void selectionSort(int nums[], int n) { - // 外循环:未排序区间为 [i, n-1] - for (int i = 0; i < n - 1; i++) { - // 内循环:找到未排序区间内的最小元素 - int k = i; - for (int j = i + 1; j < n; j++) { - if (nums[j] < nums[k]) - k = j; // 记录最小元素的索引 - } - // 将该最小元素与未排序区间的首个元素交换 - int temp = nums[i]; - nums[i] = nums[k]; - nums[k] = temp; - } - } + [class]{}-[func]{selectionSort} ``` === "Kotlin" ```kotlin title="selection_sort.kt" - /* 选择排序 */ - fun selectionSort(nums: IntArray) { - val n = nums.size - // 外循环:未排序区间为 [i, n-1] - for (i in 0.. - - ## 11.2.1   Algorithm characteristics - **Time complexity of $O(n^2)$, non-adaptive sort**: There are $n - 1$ rounds in the outer loop, with the unsorted interval length starting at $n$ in the first round and decreasing to $2$ in the last round, i.e., the outer loops contain $n$, $n - 1$, $\dots$, $3$, $2$ inner loops respectively, summing up to $\frac{(n - 1)(n + 2)}{2}$. diff --git a/en/docs/chapter_stack_and_queue/deque.md b/en/docs/chapter_stack_and_queue/deque.md index 58055ee6c..3809c802a 100644 --- a/en/docs/chapter_stack_and_queue/deque.md +++ b/en/docs/chapter_stack_and_queue/deque.md @@ -385,106 +385,106 @@ The implementation code is as follows: ```python title="linkedlist_deque.py" class ListNode: - """双向链表节点""" + """Double-linked list node""" def __init__(self, val: int): - """构造方法""" + """Constructor""" self.val: int = val - self.next: ListNode | None = None # 后继节点引用 - self.prev: ListNode | None = None # 前驱节点引用 + self.next: ListNode | None = None # Reference to the next node + self.prev: ListNode | None = None # Reference to predecessor node class LinkedListDeque: - """基于双向链表实现的双向队列""" + """Double-ended queue class based on double-linked list""" def __init__(self): - """构造方法""" - self._front: ListNode | None = None # 头节点 front - self._rear: ListNode | None = None # 尾节点 rear - self._size: int = 0 # 双向队列的长度 + """Constructor""" + self._front: ListNode | None = None # Head node front + self._rear: ListNode | None = None # Tail node rear + self._size: int = 0 # Length of the double-ended queue def size(self) -> int: - """获取双向队列的长度""" + """Get the length of the double-ended queue""" return self._size def is_empty(self) -> bool: - """判断双向队列是否为空""" + """Determine if the double-ended queue is empty""" return self._size == 0 def push(self, num: int, is_front: bool): - """入队操作""" + """Enqueue operation""" node = ListNode(num) - # 若链表为空,则令 front 和 rear 都指向 node + # If the list is empty, make front and rear both point to node if self.is_empty(): self._front = self._rear = node - # 队首入队操作 + # Front enqueue operation elif is_front: - # 将 node 添加至链表头部 + # Add node to the head of the list self._front.prev = node node.next = self._front - self._front = node # 更新头节点 - # 队尾入队操作 + self._front = node # Update head node + # Rear enqueue operation else: - # 将 node 添加至链表尾部 + # Add node to the tail of the list self._rear.next = node node.prev = self._rear - self._rear = node # 更新尾节点 - self._size += 1 # 更新队列长度 + self._rear = node # Update tail node + self._size += 1 # Update queue length def push_first(self, num: int): - """队首入队""" + """Front enqueue""" self.push(num, True) def push_last(self, num: int): - """队尾入队""" + """Rear enqueue""" self.push(num, False) def pop(self, is_front: bool) -> int: - """出队操作""" + """Dequeue operation""" if self.is_empty(): - raise IndexError("双向队列为空") - # 队首出队操作 + raise IndexError("Double-ended queue is empty") + # Front dequeue operation if is_front: - val: int = self._front.val # 暂存头节点值 - # 删除头节点 + val: int = self._front.val # Temporarily store the head node value + # Remove head node fnext: ListNode | None = self._front.next if fnext != None: fnext.prev = None self._front.next = None - self._front = fnext # 更新头节点 - # 队尾出队操作 + self._front = fnext # Update head node + # Rear dequeue operation else: - val: int = self._rear.val # 暂存尾节点值 - # 删除尾节点 + val: int = self._rear.val # Temporarily store the tail node value + # Remove tail node rprev: ListNode | None = self._rear.prev if rprev != None: rprev.next = None self._rear.prev = None - self._rear = rprev # 更新尾节点 - self._size -= 1 # 更新队列长度 + self._rear = rprev # Update tail node + self._size -= 1 # Update queue length return val def pop_first(self) -> int: - """队首出队""" + """Front dequeue""" return self.pop(True) def pop_last(self) -> int: - """队尾出队""" + """Rear dequeue""" return self.pop(False) def peek_first(self) -> int: - """访问队首元素""" + """Access front element""" if self.is_empty(): - raise IndexError("双向队列为空") + raise IndexError("Double-ended queue is empty") return self._front.val def peek_last(self) -> int: - """访问队尾元素""" + """Access rear element""" if self.is_empty(): - raise IndexError("双向队列为空") + raise IndexError("Double-ended queue is empty") return self._rear.val def to_array(self) -> list[int]: - """返回数组用于打印""" + """Return array for printing""" node = self._front res = [0] * self.size() for i in range(self.size()): @@ -496,156 +496,19 @@ The implementation code is as follows: === "C++" ```cpp title="linkedlist_deque.cpp" - /* 双向链表节点 */ - struct DoublyListNode { - int val; // 节点值 - DoublyListNode *next; // 后继节点指针 - DoublyListNode *prev; // 前驱节点指针 - DoublyListNode(int val) : val(val), prev(nullptr), next(nullptr) { - } - }; + [class]{DoublyListNode}-[func]{} - /* 基于双向链表实现的双向队列 */ - class LinkedListDeque { - private: - DoublyListNode *front, *rear; // 头节点 front ,尾节点 rear - int queSize = 0; // 双向队列的长度 - - public: - /* 构造方法 */ - LinkedListDeque() : front(nullptr), rear(nullptr) { - } - - /* 析构方法 */ - ~LinkedListDeque() { - // 遍历链表删除节点,释放内存 - DoublyListNode *pre, *cur = front; - while (cur != nullptr) { - pre = cur; - cur = cur->next; - delete pre; - } - } - - /* 获取双向队列的长度 */ - int size() { - return queSize; - } - - /* 判断双向队列是否为空 */ - bool isEmpty() { - return size() == 0; - } - - /* 入队操作 */ - void push(int num, bool isFront) { - DoublyListNode *node = new DoublyListNode(num); - // 若链表为空,则令 front 和 rear 都指向 node - if (isEmpty()) - front = rear = node; - // 队首入队操作 - else if (isFront) { - // 将 node 添加至链表头部 - front->prev = node; - node->next = front; - front = node; // 更新头节点 - // 队尾入队操作 - } else { - // 将 node 添加至链表尾部 - rear->next = node; - node->prev = rear; - rear = node; // 更新尾节点 - } - queSize++; // 更新队列长度 - } - - /* 队首入队 */ - void pushFirst(int num) { - push(num, true); - } - - /* 队尾入队 */ - void pushLast(int num) { - push(num, false); - } - - /* 出队操作 */ - int pop(bool isFront) { - if (isEmpty()) - throw out_of_range("队列为空"); - int val; - // 队首出队操作 - if (isFront) { - val = front->val; // 暂存头节点值 - // 删除头节点 - DoublyListNode *fNext = front->next; - if (fNext != nullptr) { - fNext->prev = nullptr; - front->next = nullptr; - } - delete front; - front = fNext; // 更新头节点 - // 队尾出队操作 - } else { - val = rear->val; // 暂存尾节点值 - // 删除尾节点 - DoublyListNode *rPrev = rear->prev; - if (rPrev != nullptr) { - rPrev->next = nullptr; - rear->prev = nullptr; - } - delete rear; - rear = rPrev; // 更新尾节点 - } - queSize--; // 更新队列长度 - return val; - } - - /* 队首出队 */ - int popFirst() { - return pop(true); - } - - /* 队尾出队 */ - int popLast() { - return pop(false); - } - - /* 访问队首元素 */ - int peekFirst() { - if (isEmpty()) - throw out_of_range("双向队列为空"); - return front->val; - } - - /* 访问队尾元素 */ - int peekLast() { - if (isEmpty()) - throw out_of_range("双向队列为空"); - return rear->val; - } - - /* 返回数组用于打印 */ - vector toVector() { - DoublyListNode *node = front; - vector res(size()); - for (int i = 0; i < res.size(); i++) { - res[i] = node->val; - node = node->next; - } - return res; - } - }; + [class]{LinkedListDeque}-[func]{} ``` === "Java" ```java title="linkedlist_deque.java" - /* 双向链表节点 */ + /* Double-linked list node */ class ListNode { - int val; // 节点值 - ListNode next; // 后继节点引用 - ListNode prev; // 前驱节点引用 + int val; // Node value + ListNode next; // Reference to the next node + ListNode prev; // Reference to predecessor node ListNode(int val) { this.val = val; @@ -653,112 +516,112 @@ The implementation code is as follows: } } - /* 基于双向链表实现的双向队列 */ + /* Double-ended queue class based on double-linked list */ class LinkedListDeque { - private ListNode front, rear; // 头节点 front ,尾节点 rear - private int queSize = 0; // 双向队列的长度 + private ListNode front, rear; // Front node front, back node rear + private int queSize = 0; // Length of the double-ended queue public LinkedListDeque() { front = rear = null; } - /* 获取双向队列的长度 */ + /* Get the length of the double-ended queue */ public int size() { return queSize; } - /* 判断双向队列是否为空 */ + /* Determine if the double-ended queue is empty */ public boolean isEmpty() { return size() == 0; } - /* 入队操作 */ + /* Enqueue operation */ private void push(int num, boolean isFront) { ListNode node = new ListNode(num); - // 若链表为空,则令 front 和 rear 都指向 node + // If the list is empty, make front and rear both point to node if (isEmpty()) front = rear = node; - // 队首入队操作 + // Front enqueue operation else if (isFront) { - // 将 node 添加至链表头部 + // Add node to the head of the list front.prev = node; node.next = front; - front = node; // 更新头节点 - // 队尾入队操作 + front = node; // Update head node + // Rear enqueue operation } else { - // 将 node 添加至链表尾部 + // Add node to the tail of the list rear.next = node; node.prev = rear; - rear = node; // 更新尾节点 + rear = node; // Update tail node } - queSize++; // 更新队列长度 + queSize++; // Update queue length } - /* 队首入队 */ + /* Front enqueue */ public void pushFirst(int num) { push(num, true); } - /* 队尾入队 */ + /* Rear enqueue */ public void pushLast(int num) { push(num, false); } - /* 出队操作 */ + /* Dequeue operation */ private int pop(boolean isFront) { if (isEmpty()) throw new IndexOutOfBoundsException(); int val; - // 队首出队操作 + // Front dequeue operation if (isFront) { - val = front.val; // 暂存头节点值 - // 删除头节点 + val = front.val; // Temporarily store the head node value + // Remove head node ListNode fNext = front.next; if (fNext != null) { fNext.prev = null; front.next = null; } - front = fNext; // 更新头节点 - // 队尾出队操作 + front = fNext; // Update head node + // Rear dequeue operation } else { - val = rear.val; // 暂存尾节点值 - // 删除尾节点 + val = rear.val; // Temporarily store the tail node value + // Remove tail node ListNode rPrev = rear.prev; if (rPrev != null) { rPrev.next = null; rear.prev = null; } - rear = rPrev; // 更新尾节点 + rear = rPrev; // Update tail node } - queSize--; // 更新队列长度 + queSize--; // Update queue length return val; } - /* 队首出队 */ + /* Front dequeue */ public int popFirst() { return pop(true); } - /* 队尾出队 */ + /* Rear dequeue */ public int popLast() { return pop(false); } - /* 访问队首元素 */ + /* Access front element */ public int peekFirst() { if (isEmpty()) throw new IndexOutOfBoundsException(); return front.val; } - /* 访问队尾元素 */ + /* Access rear element */ public int peekLast() { if (isEmpty()) throw new IndexOutOfBoundsException(); return rear.val; } - /* 返回数组用于打印 */ + /* Return array for printing */ public int[] toArray() { ListNode node = front; int[] res = new int[size()]; @@ -774,1492 +637,87 @@ The implementation code is as follows: === "C#" ```csharp title="linkedlist_deque.cs" - /* 双向链表节点 */ - class ListNode(int val) { - public int val = val; // 节点值 - public ListNode? next = null; // 后继节点引用 - public ListNode? prev = null; // 前驱节点引用 - } + [class]{ListNode}-[func]{} - /* 基于双向链表实现的双向队列 */ - class LinkedListDeque { - ListNode? front, rear; // 头节点 front, 尾节点 rear - int queSize = 0; // 双向队列的长度 - - public LinkedListDeque() { - front = null; - rear = null; - } - - /* 获取双向队列的长度 */ - public int Size() { - return queSize; - } - - /* 判断双向队列是否为空 */ - public bool IsEmpty() { - return Size() == 0; - } - - /* 入队操作 */ - void Push(int num, bool isFront) { - ListNode node = new(num); - // 若链表为空,则令 front 和 rear 都指向 node - if (IsEmpty()) { - front = node; - rear = node; - } - // 队首入队操作 - else if (isFront) { - // 将 node 添加至链表头部 - front!.prev = node; - node.next = front; - front = node; // 更新头节点 - } - // 队尾入队操作 - else { - // 将 node 添加至链表尾部 - rear!.next = node; - node.prev = rear; - rear = node; // 更新尾节点 - } - - queSize++; // 更新队列长度 - } - - /* 队首入队 */ - public void PushFirst(int num) { - Push(num, true); - } - - /* 队尾入队 */ - public void PushLast(int num) { - Push(num, false); - } - - /* 出队操作 */ - int? Pop(bool isFront) { - if (IsEmpty()) - throw new Exception(); - int? val; - // 队首出队操作 - if (isFront) { - val = front?.val; // 暂存头节点值 - // 删除头节点 - ListNode? fNext = front?.next; - if (fNext != null) { - fNext.prev = null; - front!.next = null; - } - front = fNext; // 更新头节点 - } - // 队尾出队操作 - else { - val = rear?.val; // 暂存尾节点值 - // 删除尾节点 - ListNode? rPrev = rear?.prev; - if (rPrev != null) { - rPrev.next = null; - rear!.prev = null; - } - rear = rPrev; // 更新尾节点 - } - - queSize--; // 更新队列长度 - return val; - } - - /* 队首出队 */ - public int? PopFirst() { - return Pop(true); - } - - /* 队尾出队 */ - public int? PopLast() { - return Pop(false); - } - - /* 访问队首元素 */ - public int? PeekFirst() { - if (IsEmpty()) - throw new Exception(); - return front?.val; - } - - /* 访问队尾元素 */ - public int? PeekLast() { - if (IsEmpty()) - throw new Exception(); - return rear?.val; - } - - /* 返回数组用于打印 */ - public int?[] ToArray() { - ListNode? node = front; - int?[] res = new int?[Size()]; - for (int i = 0; i < res.Length; i++) { - res[i] = node?.val; - node = node?.next; - } - - return res; - } - } + [class]{LinkedListDeque}-[func]{} ``` === "Go" ```go title="linkedlist_deque.go" - /* 基于双向链表实现的双向队列 */ - type linkedListDeque struct { - // 使用内置包 list - data *list.List - } - - /* 初始化双端队列 */ - func newLinkedListDeque() *linkedListDeque { - return &linkedListDeque{ - data: list.New(), - } - } - - /* 队首元素入队 */ - func (s *linkedListDeque) pushFirst(value any) { - s.data.PushFront(value) - } - - /* 队尾元素入队 */ - func (s *linkedListDeque) pushLast(value any) { - s.data.PushBack(value) - } - - /* 队首元素出队 */ - func (s *linkedListDeque) popFirst() any { - if s.isEmpty() { - return nil - } - e := s.data.Front() - s.data.Remove(e) - return e.Value - } - - /* 队尾元素出队 */ - func (s *linkedListDeque) popLast() any { - if s.isEmpty() { - return nil - } - e := s.data.Back() - s.data.Remove(e) - return e.Value - } - - /* 访问队首元素 */ - func (s *linkedListDeque) peekFirst() any { - if s.isEmpty() { - return nil - } - e := s.data.Front() - return e.Value - } - - /* 访问队尾元素 */ - func (s *linkedListDeque) peekLast() any { - if s.isEmpty() { - return nil - } - e := s.data.Back() - return e.Value - } - - /* 获取队列的长度 */ - func (s *linkedListDeque) size() int { - return s.data.Len() - } - - /* 判断队列是否为空 */ - func (s *linkedListDeque) isEmpty() bool { - return s.data.Len() == 0 - } - - /* 获取 List 用于打印 */ - func (s *linkedListDeque) toList() *list.List { - return s.data - } + [class]{linkedListDeque}-[func]{} ``` === "Swift" ```swift title="linkedlist_deque.swift" - /* 双向链表节点 */ - class ListNode { - var val: Int // 节点值 - var next: ListNode? // 后继节点引用 - weak var prev: ListNode? // 前驱节点引用 + [class]{ListNode}-[func]{} - init(val: Int) { - self.val = val - } - } - - /* 基于双向链表实现的双向队列 */ - class LinkedListDeque { - private var front: ListNode? // 头节点 front - private var rear: ListNode? // 尾节点 rear - private var _size: Int // 双向队列的长度 - - init() { - _size = 0 - } - - /* 获取双向队列的长度 */ - func size() -> Int { - _size - } - - /* 判断双向队列是否为空 */ - func isEmpty() -> Bool { - size() == 0 - } - - /* 入队操作 */ - private func push(num: Int, isFront: Bool) { - let node = ListNode(val: num) - // 若链表为空,则令 front 和 rear 都指向 node - if isEmpty() { - front = node - rear = node - } - // 队首入队操作 - else if isFront { - // 将 node 添加至链表头部 - front?.prev = node - node.next = front - front = node // 更新头节点 - } - // 队尾入队操作 - else { - // 将 node 添加至链表尾部 - rear?.next = node - node.prev = rear - rear = node // 更新尾节点 - } - _size += 1 // 更新队列长度 - } - - /* 队首入队 */ - func pushFirst(num: Int) { - push(num: num, isFront: true) - } - - /* 队尾入队 */ - func pushLast(num: Int) { - push(num: num, isFront: false) - } - - /* 出队操作 */ - private func pop(isFront: Bool) -> Int { - if isEmpty() { - fatalError("双向队列为空") - } - let val: Int - // 队首出队操作 - if isFront { - val = front!.val // 暂存头节点值 - // 删除头节点 - let fNext = front?.next - if fNext != nil { - fNext?.prev = nil - front?.next = nil - } - front = fNext // 更新头节点 - } - // 队尾出队操作 - else { - val = rear!.val // 暂存尾节点值 - // 删除尾节点 - let rPrev = rear?.prev - if rPrev != nil { - rPrev?.next = nil - rear?.prev = nil - } - rear = rPrev // 更新尾节点 - } - _size -= 1 // 更新队列长度 - return val - } - - /* 队首出队 */ - func popFirst() -> Int { - pop(isFront: true) - } - - /* 队尾出队 */ - func popLast() -> Int { - pop(isFront: false) - } - - /* 访问队首元素 */ - func peekFirst() -> Int { - if isEmpty() { - fatalError("双向队列为空") - } - return front!.val - } - - /* 访问队尾元素 */ - func peekLast() -> Int { - if isEmpty() { - fatalError("双向队列为空") - } - return rear!.val - } - - /* 返回数组用于打印 */ - func toArray() -> [Int] { - var node = front - var res = Array(repeating: 0, count: size()) - for i in res.indices { - res[i] = node!.val - node = node?.next - } - return res - } - } + [class]{LinkedListDeque}-[func]{} ``` === "JS" ```javascript title="linkedlist_deque.js" - /* 双向链表节点 */ - class ListNode { - prev; // 前驱节点引用 (指针) - next; // 后继节点引用 (指针) - val; // 节点值 + [class]{ListNode}-[func]{} - constructor(val) { - this.val = val; - this.next = null; - this.prev = null; - } - } - - /* 基于双向链表实现的双向队列 */ - class LinkedListDeque { - #front; // 头节点 front - #rear; // 尾节点 rear - #queSize; // 双向队列的长度 - - constructor() { - this.#front = null; - this.#rear = null; - this.#queSize = 0; - } - - /* 队尾入队操作 */ - pushLast(val) { - const node = new ListNode(val); - // 若链表为空,则令 front 和 rear 都指向 node - if (this.#queSize === 0) { - this.#front = node; - this.#rear = node; - } else { - // 将 node 添加至链表尾部 - this.#rear.next = node; - node.prev = this.#rear; - this.#rear = node; // 更新尾节点 - } - this.#queSize++; - } - - /* 队首入队操作 */ - pushFirst(val) { - const node = new ListNode(val); - // 若链表为空,则令 front 和 rear 都指向 node - if (this.#queSize === 0) { - this.#front = node; - this.#rear = node; - } else { - // 将 node 添加至链表头部 - this.#front.prev = node; - node.next = this.#front; - this.#front = node; // 更新头节点 - } - this.#queSize++; - } - - /* 队尾出队操作 */ - popLast() { - if (this.#queSize === 0) { - return null; - } - const value = this.#rear.val; // 存储尾节点值 - // 删除尾节点 - let temp = this.#rear.prev; - if (temp !== null) { - temp.next = null; - this.#rear.prev = null; - } - this.#rear = temp; // 更新尾节点 - this.#queSize--; - return value; - } - - /* 队首出队操作 */ - popFirst() { - if (this.#queSize === 0) { - return null; - } - const value = this.#front.val; // 存储尾节点值 - // 删除头节点 - let temp = this.#front.next; - if (temp !== null) { - temp.prev = null; - this.#front.next = null; - } - this.#front = temp; // 更新头节点 - this.#queSize--; - return value; - } - - /* 访问队尾元素 */ - peekLast() { - return this.#queSize === 0 ? null : this.#rear.val; - } - - /* 访问队首元素 */ - peekFirst() { - return this.#queSize === 0 ? null : this.#front.val; - } - - /* 获取双向队列的长度 */ - size() { - return this.#queSize; - } - - /* 判断双向队列是否为空 */ - isEmpty() { - return this.#queSize === 0; - } - - /* 打印双向队列 */ - print() { - const arr = []; - let temp = this.#front; - while (temp !== null) { - arr.push(temp.val); - temp = temp.next; - } - console.log('[' + arr.join(', ') + ']'); - } - } + [class]{LinkedListDeque}-[func]{} ``` === "TS" ```typescript title="linkedlist_deque.ts" - /* 双向链表节点 */ - class ListNode { - prev: ListNode; // 前驱节点引用 (指针) - next: ListNode; // 后继节点引用 (指针) - val: number; // 节点值 + [class]{ListNode}-[func]{} - constructor(val: number) { - this.val = val; - this.next = null; - this.prev = null; - } - } - - /* 基于双向链表实现的双向队列 */ - class LinkedListDeque { - private front: ListNode; // 头节点 front - private rear: ListNode; // 尾节点 rear - private queSize: number; // 双向队列的长度 - - constructor() { - this.front = null; - this.rear = null; - this.queSize = 0; - } - - /* 队尾入队操作 */ - pushLast(val: number): void { - const node: ListNode = new ListNode(val); - // 若链表为空,则令 front 和 rear 都指向 node - if (this.queSize === 0) { - this.front = node; - this.rear = node; - } else { - // 将 node 添加至链表尾部 - this.rear.next = node; - node.prev = this.rear; - this.rear = node; // 更新尾节点 - } - this.queSize++; - } - - /* 队首入队操作 */ - pushFirst(val: number): void { - const node: ListNode = new ListNode(val); - // 若链表为空,则令 front 和 rear 都指向 node - if (this.queSize === 0) { - this.front = node; - this.rear = node; - } else { - // 将 node 添加至链表头部 - this.front.prev = node; - node.next = this.front; - this.front = node; // 更新头节点 - } - this.queSize++; - } - - /* 队尾出队操作 */ - popLast(): number { - if (this.queSize === 0) { - return null; - } - const value: number = this.rear.val; // 存储尾节点值 - // 删除尾节点 - let temp: ListNode = this.rear.prev; - if (temp !== null) { - temp.next = null; - this.rear.prev = null; - } - this.rear = temp; // 更新尾节点 - this.queSize--; - return value; - } - - /* 队首出队操作 */ - popFirst(): number { - if (this.queSize === 0) { - return null; - } - const value: number = this.front.val; // 存储尾节点值 - // 删除头节点 - let temp: ListNode = this.front.next; - if (temp !== null) { - temp.prev = null; - this.front.next = null; - } - this.front = temp; // 更新头节点 - this.queSize--; - return value; - } - - /* 访问队尾元素 */ - peekLast(): number { - return this.queSize === 0 ? null : this.rear.val; - } - - /* 访问队首元素 */ - peekFirst(): number { - return this.queSize === 0 ? null : this.front.val; - } - - /* 获取双向队列的长度 */ - size(): number { - return this.queSize; - } - - /* 判断双向队列是否为空 */ - isEmpty(): boolean { - return this.queSize === 0; - } - - /* 打印双向队列 */ - print(): void { - const arr: number[] = []; - let temp: ListNode = this.front; - while (temp !== null) { - arr.push(temp.val); - temp = temp.next; - } - console.log('[' + arr.join(', ') + ']'); - } - } + [class]{LinkedListDeque}-[func]{} ``` === "Dart" ```dart title="linkedlist_deque.dart" - /* 双向链表节点 */ - class ListNode { - int val; // 节点值 - ListNode? next; // 后继节点引用 - ListNode? prev; // 前驱节点引用 + [class]{ListNode}-[func]{} - ListNode(this.val, {this.next, this.prev}); - } - - /* 基于双向链表实现的双向对列 */ - class LinkedListDeque { - late ListNode? _front; // 头节点 _front - late ListNode? _rear; // 尾节点 _rear - int _queSize = 0; // 双向队列的长度 - - LinkedListDeque() { - this._front = null; - this._rear = null; - } - - /* 获取双向队列长度 */ - int size() { - return this._queSize; - } - - /* 判断双向队列是否为空 */ - bool isEmpty() { - return size() == 0; - } - - /* 入队操作 */ - void push(int _num, bool isFront) { - final ListNode node = ListNode(_num); - if (isEmpty()) { - // 若链表为空,则令 _front 和 _rear 都指向 node - _front = _rear = node; - } else if (isFront) { - // 队首入队操作 - // 将 node 添加至链表头部 - _front!.prev = node; - node.next = _front; - _front = node; // 更新头节点 - } else { - // 队尾入队操作 - // 将 node 添加至链表尾部 - _rear!.next = node; - node.prev = _rear; - _rear = node; // 更新尾节点 - } - _queSize++; // 更新队列长度 - } - - /* 队首入队 */ - void pushFirst(int _num) { - push(_num, true); - } - - /* 队尾入队 */ - void pushLast(int _num) { - push(_num, false); - } - - /* 出队操作 */ - int? pop(bool isFront) { - // 若队列为空,直接返回 null - if (isEmpty()) { - return null; - } - final int val; - if (isFront) { - // 队首出队操作 - val = _front!.val; // 暂存头节点值 - // 删除头节点 - ListNode? fNext = _front!.next; - if (fNext != null) { - fNext.prev = null; - _front!.next = null; - } - _front = fNext; // 更新头节点 - } else { - // 队尾出队操作 - val = _rear!.val; // 暂存尾节点值 - // 删除尾节点 - ListNode? rPrev = _rear!.prev; - if (rPrev != null) { - rPrev.next = null; - _rear!.prev = null; - } - _rear = rPrev; // 更新尾节点 - } - _queSize--; // 更新队列长度 - return val; - } - - /* 队首出队 */ - int? popFirst() { - return pop(true); - } - - /* 队尾出队 */ - int? popLast() { - return pop(false); - } - - /* 访问队首元素 */ - int? peekFirst() { - return _front?.val; - } - - /* 访问队尾元素 */ - int? peekLast() { - return _rear?.val; - } - - /* 返回数组用于打印 */ - List toArray() { - ListNode? node = _front; - final List res = []; - for (int i = 0; i < _queSize; i++) { - res.add(node!.val); - node = node.next; - } - return res; - } - } + [class]{LinkedListDeque}-[func]{} ``` === "Rust" ```rust title="linkedlist_deque.rs" - /* 双向链表节点 */ - pub struct ListNode { - pub val: T, // 节点值 - pub next: Option>>>, // 后继节点指针 - pub prev: Option>>>, // 前驱节点指针 - } + [class]{ListNode}-[func]{} - impl ListNode { - pub fn new(val: T) -> Rc>> { - Rc::new(RefCell::new(ListNode { - val, - next: None, - prev: None, - })) - } - } - - /* 基于双向链表实现的双向队列 */ - #[allow(dead_code)] - pub struct LinkedListDeque { - front: Option>>>, // 头节点 front - rear: Option>>>, // 尾节点 rear - que_size: usize, // 双向队列的长度 - } - - impl LinkedListDeque { - pub fn new() -> Self { - Self { - front: None, - rear: None, - que_size: 0, - } - } - - /* 获取双向队列的长度 */ - pub fn size(&self) -> usize { - return self.que_size; - } - - /* 判断双向队列是否为空 */ - pub fn is_empty(&self) -> bool { - return self.size() == 0; - } - - /* 入队操作 */ - pub fn push(&mut self, num: T, is_front: bool) { - let node = ListNode::new(num); - // 队首入队操作 - if is_front { - match self.front.take() { - // 若链表为空,则令 front 和 rear 都指向 node - None => { - self.rear = Some(node.clone()); - self.front = Some(node); - } - // 将 node 添加至链表头部 - Some(old_front) => { - old_front.borrow_mut().prev = Some(node.clone()); - node.borrow_mut().next = Some(old_front); - self.front = Some(node); // 更新头节点 - } - } - } - // 队尾入队操作 - else { - match self.rear.take() { - // 若链表为空,则令 front 和 rear 都指向 node - None => { - self.front = Some(node.clone()); - self.rear = Some(node); - } - // 将 node 添加至链表尾部 - Some(old_rear) => { - old_rear.borrow_mut().next = Some(node.clone()); - node.borrow_mut().prev = Some(old_rear); - self.rear = Some(node); // 更新尾节点 - } - } - } - self.que_size += 1; // 更新队列长度 - } - - /* 队首入队 */ - pub fn push_first(&mut self, num: T) { - self.push(num, true); - } - - /* 队尾入队 */ - pub fn push_last(&mut self, num: T) { - self.push(num, false); - } - - /* 出队操作 */ - pub fn pop(&mut self, is_front: bool) -> Option { - // 若队列为空,直接返回 None - if self.is_empty() { - return None; - }; - // 队首出队操作 - if is_front { - self.front.take().map(|old_front| { - match old_front.borrow_mut().next.take() { - Some(new_front) => { - new_front.borrow_mut().prev.take(); - self.front = Some(new_front); // 更新头节点 - } - None => { - self.rear.take(); - } - } - self.que_size -= 1; // 更新队列长度 - Rc::try_unwrap(old_front).ok().unwrap().into_inner().val - }) - } - // 队尾出队操作 - else { - self.rear.take().map(|old_rear| { - match old_rear.borrow_mut().prev.take() { - Some(new_rear) => { - new_rear.borrow_mut().next.take(); - self.rear = Some(new_rear); // 更新尾节点 - } - None => { - self.front.take(); - } - } - self.que_size -= 1; // 更新队列长度 - Rc::try_unwrap(old_rear).ok().unwrap().into_inner().val - }) - } - } - - /* 队首出队 */ - pub fn pop_first(&mut self) -> Option { - return self.pop(true); - } - - /* 队尾出队 */ - pub fn pop_last(&mut self) -> Option { - return self.pop(false); - } - - /* 访问队首元素 */ - pub fn peek_first(&self) -> Option<&Rc>>> { - self.front.as_ref() - } - - /* 访问队尾元素 */ - pub fn peek_last(&self) -> Option<&Rc>>> { - self.rear.as_ref() - } - - /* 返回数组用于打印 */ - pub fn to_array(&self, head: Option<&Rc>>>) -> Vec { - if let Some(node) = head { - let mut nums = self.to_array(node.borrow().next.as_ref()); - nums.insert(0, node.borrow().val); - return nums; - } - return Vec::new(); - } - } + [class]{LinkedListDeque}-[func]{} ``` === "C" ```c title="linkedlist_deque.c" - /* 双向链表节点 */ - typedef struct DoublyListNode { - int val; // 节点值 - struct DoublyListNode *next; // 后继节点 - struct DoublyListNode *prev; // 前驱节点 - } DoublyListNode; + [class]{DoublyListNode}-[func]{} - /* 构造函数 */ - DoublyListNode *newDoublyListNode(int num) { - DoublyListNode *new = (DoublyListNode *)malloc(sizeof(DoublyListNode)); - new->val = num; - new->next = NULL; - new->prev = NULL; - return new; - } - - /* 析构函数 */ - void delDoublyListNode(DoublyListNode *node) { - free(node); - } - - /* 基于双向链表实现的双向队列 */ - typedef struct { - DoublyListNode *front, *rear; // 头节点 front ,尾节点 rear - int queSize; // 双向队列的长度 - } LinkedListDeque; - - /* 构造函数 */ - LinkedListDeque *newLinkedListDeque() { - LinkedListDeque *deque = (LinkedListDeque *)malloc(sizeof(LinkedListDeque)); - deque->front = NULL; - deque->rear = NULL; - deque->queSize = 0; - return deque; - } - - /* 析构函数 */ - void delLinkedListdeque(LinkedListDeque *deque) { - // 释放所有节点 - for (int i = 0; i < deque->queSize && deque->front != NULL; i++) { - DoublyListNode *tmp = deque->front; - deque->front = deque->front->next; - free(tmp); - } - // 释放 deque 结构体 - free(deque); - } - - /* 获取队列的长度 */ - int size(LinkedListDeque *deque) { - return deque->queSize; - } - - /* 判断队列是否为空 */ - bool empty(LinkedListDeque *deque) { - return (size(deque) == 0); - } - - /* 入队 */ - void push(LinkedListDeque *deque, int num, bool isFront) { - DoublyListNode *node = newDoublyListNode(num); - // 若链表为空,则令 front 和 rear 都指向node - if (empty(deque)) { - deque->front = deque->rear = node; - } - // 队首入队操作 - else if (isFront) { - // 将 node 添加至链表头部 - deque->front->prev = node; - node->next = deque->front; - deque->front = node; // 更新头节点 - } - // 队尾入队操作 - else { - // 将 node 添加至链表尾部 - deque->rear->next = node; - node->prev = deque->rear; - deque->rear = node; - } - deque->queSize++; // 更新队列长度 - } - - /* 队首入队 */ - void pushFirst(LinkedListDeque *deque, int num) { - push(deque, num, true); - } - - /* 队尾入队 */ - void pushLast(LinkedListDeque *deque, int num) { - push(deque, num, false); - } - - /* 访问队首元素 */ - int peekFirst(LinkedListDeque *deque) { - assert(size(deque) && deque->front); - return deque->front->val; - } - - /* 访问队尾元素 */ - int peekLast(LinkedListDeque *deque) { - assert(size(deque) && deque->rear); - return deque->rear->val; - } - - /* 出队 */ - int pop(LinkedListDeque *deque, bool isFront) { - if (empty(deque)) - return -1; - int val; - // 队首出队操作 - if (isFront) { - val = peekFirst(deque); // 暂存头节点值 - DoublyListNode *fNext = deque->front->next; - if (fNext) { - fNext->prev = NULL; - deque->front->next = NULL; - } - delDoublyListNode(deque->front); - deque->front = fNext; // 更新头节点 - } - // 队尾出队操作 - else { - val = peekLast(deque); // 暂存尾节点值 - DoublyListNode *rPrev = deque->rear->prev; - if (rPrev) { - rPrev->next = NULL; - deque->rear->prev = NULL; - } - delDoublyListNode(deque->rear); - deque->rear = rPrev; // 更新尾节点 - } - deque->queSize--; // 更新队列长度 - return val; - } - - /* 队首出队 */ - int popFirst(LinkedListDeque *deque) { - return pop(deque, true); - } - - /* 队尾出队 */ - int popLast(LinkedListDeque *deque) { - return pop(deque, false); - } - - /* 打印队列 */ - void printLinkedListDeque(LinkedListDeque *deque) { - int *arr = malloc(sizeof(int) * deque->queSize); - // 拷贝链表中的数据到数组 - int i; - DoublyListNode *node; - for (i = 0, node = deque->front; i < deque->queSize; i++) { - arr[i] = node->val; - node = node->next; - } - printArray(arr, deque->queSize); - free(arr); - } + [class]{LinkedListDeque}-[func]{} ``` === "Kotlin" ```kotlin title="linkedlist_deque.kt" - /* 双向链表节点 */ - class ListNode(var _val: Int) { - // 节点值 - var next: ListNode? = null // 后继节点引用 - var prev: ListNode? = null // 前驱节点引用 - } + [class]{ListNode}-[func]{} - /* 基于双向链表实现的双向队列 */ - class LinkedListDeque { - private var front: ListNode? = null // 头节点 front - private var rear: ListNode? = null // 尾节点 rear - private var queSize: Int = 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 _val: Int - // 队首出队操作 - if (isFront) { - _val = front!!._val // 暂存头节点值 - // 删除头节点 - val fNext = front!!.next - if (fNext != null) { - fNext.prev = null - front!!.next = null - } - front = fNext // 更新头节点 - // 队尾出队操作 - } else { - _val = rear!!._val // 暂存尾节点值 - // 删除尾节点 - val rPrev = rear!!.prev - if (rPrev != null) { - rPrev.next = null - rear!!.prev = null - } - rear = rPrev // 更新尾节点 - } - queSize-- // 更新队列长度 - return _val - } - - /* 队首出队 */ - fun popFirst(): Int { - return pop(true) - } - - /* 队尾出队 */ - fun popLast(): Int { - return pop(false) - } - - /* 访问队首元素 */ - fun peekFirst(): Int { - if (isEmpty()) throw IndexOutOfBoundsException() - return front!!._val - } - - /* 访问队尾元素 */ - fun peekLast(): Int { - if (isEmpty()) throw IndexOutOfBoundsException() - return rear!!._val - } - - /* 返回数组用于打印 */ - fun toArray(): IntArray { - var node = front - val res = IntArray(size()) - for (i in res.indices) { - res[i] = node!!._val - node = node.next - } - return res - } - } + [class]{LinkedListDeque}-[func]{} ``` === "Ruby" ```ruby title="linkedlist_deque.rb" - =begin - File: linkedlist_deque.rb - Created Time: 2024-04-06 - Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) - =end + [class]{ListNode}-[func]{} - ### 双向链表节点 - class ListNode - attr_accessor :val - attr_accessor :next # 后继节点引用 - attr_accessor :prev # 前躯节点引用 - - ### 构造方法 ### - def initialize(val) - @val = val - end - end - - ### 基于双向链表实现的双向队列 ### - class LinkedListDeque - ### 获取双向队列的长度 ### - attr_reader :size - - ### 构造方法 ### - def initialize - @front = nil # 头节点 front - @rear = nil # 尾节点 rear - @size = 0 # 双向队列的长度 - end - - ### 判断双向队列是否为空 ### - def is_empty? - size.zero? - end - - ### 入队操作 ### - def push(num, is_front) - node = ListNode.new(num) - # 若链表为空, 则令 front 和 rear 都指向 node - if is_empty? - @front = @rear = node - # 队首入队操作 - elsif is_front - # 将 node 添加至链表头部 - @front.prev = node - node.next = @front - @front = node # 更新头节点 - # 队尾入队操作 - else - # 将 node 添加至链表尾部 - @rear.next = node - node.prev = @rear - @rear = node # 更新尾节点 - end - @size += 1 # 更新队列长度 - end - - ### 队首入队 ### - def push_first(num) - push(num, true) - end - - ### 队尾入队 ### - def push_last(num) - push(num, false) - end - - ### 出队操作 ### - def pop(is_front) - raise IndexError, '双向队列为空' if is_empty? - - # 队首出队操作 - if is_front - val = @front.val # 暂存头节点值 - # 删除头节点 - fnext = @front.next - unless fnext.nil? - fnext.prev = nil - @front.next = nil - end - @front = fnext # 更新头节点 - # 队尾出队操作 - else - val = @rear.val # 暂存尾节点值 - # 删除尾节点 - rprev = @rear.prev - unless rprev.nil? - rprev.next = nil - @rear.prev = nil - end - @rear = rprev # 更新尾节点 - end - @size -= 1 # 更新队列长度 - - val - end - - ### 队首出队 ### - def pop_first - pop(true) - end - - ### 队首出队 ### - def pop_last - pop(false) - end - - ### 访问队首元素 ### - def peek_first - raise IndexError, '双向队列为空' if is_empty? - - @front.val - end - - ### 访问队尾元素 ### - def peek_last - raise IndexError, '双向队列为空' if is_empty? - - @rear.val - end - - ### 返回数组用于打印 ### - def to_array - node = @front - res = Array.new(size, 0) - for i in 0...size - res[i] = node.val - node = node.next - end - res - end - end + [class]{LinkedListDeque}-[func]{} ``` === "Zig" ```zig title="linkedlist_deque.zig" - // 双向链表节点 - fn ListNode(comptime T: type) type { - return struct { - const Self = @This(); - - val: T = undefined, // 节点值 - next: ?*Self = null, // 后继节点指针 - prev: ?*Self = null, // 前驱节点指针 + [class]{ListNode}-[func]{} - // Initialize a list node with specific value - pub fn init(self: *Self, x: i32) void { - self.val = x; - self.next = null; - self.prev = null; - } - }; - } - - // 基于双向链表实现的双向队列 - fn LinkedListDeque(comptime T: type) type { - return struct { - const Self = @This(); - - front: ?*ListNode(T) = null, // 头节点 front - rear: ?*ListNode(T) = null, // 尾节点 rear - que_size: usize = 0, // 双向队列的长度 - mem_arena: ?std.heap.ArenaAllocator = null, - mem_allocator: std.mem.Allocator = undefined, // 内存分配器 - - // 构造函数(分配内存+初始化队列) - pub fn init(self: *Self, allocator: std.mem.Allocator) !void { - if (self.mem_arena == null) { - self.mem_arena = std.heap.ArenaAllocator.init(allocator); - self.mem_allocator = self.mem_arena.?.allocator(); - } - self.front = null; - self.rear = null; - self.que_size = 0; - } - - // 析构函数(释放内存) - pub fn deinit(self: *Self) void { - if (self.mem_arena == null) return; - self.mem_arena.?.deinit(); - } - - // 获取双向队列的长度 - pub fn size(self: *Self) usize { - return self.que_size; - } - - // 判断双向队列是否为空 - pub fn isEmpty(self: *Self) bool { - return self.size() == 0; - } - - // 入队操作 - pub fn push(self: *Self, num: T, is_front: bool) !void { - var node = try self.mem_allocator.create(ListNode(T)); - node.init(num); - // 若链表为空,则令 front 和 rear 都指向 node - if (self.isEmpty()) { - self.front = node; - self.rear = node; - // 队首入队操作 - } else if (is_front) { - // 将 node 添加至链表头部 - self.front.?.prev = node; - node.next = self.front; - self.front = node; // 更新头节点 - // 队尾入队操作 - } else { - // 将 node 添加至链表尾部 - self.rear.?.next = node; - node.prev = self.rear; - self.rear = node; // 更新尾节点 - } - self.que_size += 1; // 更新队列长度 - } - - // 队首入队 - pub fn pushFirst(self: *Self, num: T) !void { - try self.push(num, true); - } - - // 队尾入队 - pub fn pushLast(self: *Self, num: T) !void { - try self.push(num, false); - } - - // 出队操作 - pub fn pop(self: *Self, is_front: bool) T { - if (self.isEmpty()) @panic("双向队列为空"); - var val: T = undefined; - // 队首出队操作 - if (is_front) { - val = self.front.?.val; // 暂存头节点值 - // 删除头节点 - var fNext = self.front.?.next; - if (fNext != null) { - fNext.?.prev = null; - self.front.?.next = null; - } - self.front = fNext; // 更新头节点 - // 队尾出队操作 - } else { - val = self.rear.?.val; // 暂存尾节点值 - // 删除尾节点 - var rPrev = self.rear.?.prev; - if (rPrev != null) { - rPrev.?.next = null; - self.rear.?.prev = null; - } - self.rear = rPrev; // 更新尾节点 - } - self.que_size -= 1; // 更新队列长度 - return val; - } - - // 队首出队 - pub fn popFirst(self: *Self) T { - return self.pop(true); - } - - // 队尾出队 - pub fn popLast(self: *Self) T { - return self.pop(false); - } - - // 访问队首元素 - pub fn peekFirst(self: *Self) T { - if (self.isEmpty()) @panic("双向队列为空"); - return self.front.?.val; - } - - // 访问队尾元素 - pub fn peekLast(self: *Self) T { - if (self.isEmpty()) @panic("双向队列为空"); - return self.rear.?.val; - } - - // 返回数组用于打印 - pub fn toArray(self: *Self) ![]T { - var node = self.front; - var res = try self.mem_allocator.alloc(T, self.size()); - @memset(res, @as(T, 0)); - var i: usize = 0; - while (i < res.len) : (i += 1) { - res[i] = node.?.val; - node = node.?.next; - } - return res; - } - }; - } + [class]{LinkedListDeque}-[func]{} ``` ### 2.   Implementation based on array @@ -2289,87 +747,87 @@ The implementation only needs to add methods for "front enqueue" and "rear deque ```python title="array_deque.py" class ArrayDeque: - """基于环形数组实现的双向队列""" + """Double-ended queue class based on circular array""" def __init__(self, capacity: int): - """构造方法""" + """Constructor""" self._nums: list[int] = [0] * capacity self._front: int = 0 self._size: int = 0 def capacity(self) -> int: - """获取双向队列的容量""" + """Get the capacity of the double-ended queue""" return len(self._nums) def size(self) -> int: - """获取双向队列的长度""" + """Get the length of the double-ended queue""" return self._size def is_empty(self) -> bool: - """判断双向队列是否为空""" + """Determine if the double-ended queue is empty""" return self._size == 0 def index(self, i: int) -> int: - """计算环形数组索引""" - # 通过取余操作实现数组首尾相连 - # 当 i 越过数组尾部后,回到头部 - # 当 i 越过数组头部后,回到尾部 + """Calculate circular array index""" + # Implement circular array by modulo operation + # When i exceeds the tail of the array, return to the head + # When i exceeds the head of the array, return to the tail return (i + self.capacity()) % self.capacity() def push_first(self, num: int): - """队首入队""" + """Front enqueue""" if self._size == self.capacity(): - print("双向队列已满") + print("Double-ended queue is full") return - # 队首指针向左移动一位 - # 通过取余操作实现 front 越过数组头部后回到尾部 + # Move the front pointer one position to the left + # Implement front crossing the head of the array to return to the tail by modulo operation self._front = self.index(self._front - 1) - # 将 num 添加至队首 + # Add num to the front self._nums[self._front] = num self._size += 1 def push_last(self, num: int): - """队尾入队""" + """Rear enqueue""" if self._size == self.capacity(): - print("双向队列已满") + print("Double-ended queue is full") return - # 计算队尾指针,指向队尾索引 + 1 + # Calculate rear pointer, pointing to rear index + 1 rear = self.index(self._front + self._size) - # 将 num 添加至队尾 + # Add num to the rear self._nums[rear] = num self._size += 1 def pop_first(self) -> int: - """队首出队""" + """Front dequeue""" num = self.peek_first() - # 队首指针向后移动一位 + # Move front pointer one position backward self._front = self.index(self._front + 1) self._size -= 1 return num def pop_last(self) -> int: - """队尾出队""" + """Rear dequeue""" num = self.peek_last() self._size -= 1 return num def peek_first(self) -> int: - """访问队首元素""" + """Access front element""" if self.is_empty(): - raise IndexError("双向队列为空") + raise IndexError("Double-ended queue is empty") return self._nums[self._front] def peek_last(self) -> int: - """访问队尾元素""" + """Access rear element""" if self.is_empty(): - raise IndexError("双向队列为空") - # 计算尾元素索引 + raise IndexError("Double-ended queue is empty") + # Calculate rear element index last = self.index(self._front + self._size - 1) return self._nums[last] def to_array(self) -> list[int]: - """返回数组用于打印""" - # 仅转换有效长度范围内的列表元素 + """Return array for printing""" + # Only convert elements within valid length range res = [] for i in range(self._size): res.append(self._nums[self.index(self._front + i)]) @@ -2379,214 +837,109 @@ The implementation only needs to add methods for "front enqueue" and "rear deque === "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; - } - }; + [class]{ArrayDeque}-[func]{} ``` === "Java" ```java title="array_deque.java" - /* 基于环形数组实现的双向队列 */ + /* Double-ended queue class based on circular array */ class ArrayDeque { - private int[] nums; // 用于存储双向队列元素的数组 - private int front; // 队首指针,指向队首元素 - private int queSize; // 双向队列长度 + private int[] nums; // Array used to store elements of the double-ended queue + private int front; // Front pointer, pointing to the front element + private int queSize; // Length of the double-ended queue - /* 构造方法 */ + /* Constructor */ public ArrayDeque(int capacity) { this.nums = new int[capacity]; front = queSize = 0; } - /* 获取双向队列的容量 */ + /* Get the capacity of the double-ended queue */ public int capacity() { return nums.length; } - /* 获取双向队列的长度 */ + /* Get the length of the double-ended queue */ public int size() { return queSize; } - /* 判断双向队列是否为空 */ + /* Determine if the double-ended queue is empty */ public boolean isEmpty() { return queSize == 0; } - /* 计算环形数组索引 */ + /* Calculate circular array index */ private int index(int i) { - // 通过取余操作实现数组首尾相连 - // 当 i 越过数组尾部后,回到头部 - // 当 i 越过数组头部后,回到尾部 + // Implement circular array by modulo operation + // When i exceeds the tail of the array, return to the head + // When i exceeds the head of the array, return to the tail return (i + capacity()) % capacity(); } - /* 队首入队 */ + /* Front enqueue */ public void pushFirst(int num) { if (queSize == capacity()) { - System.out.println("双向队列已满"); + System.out.println("Double-ended queue is full"); return; } - // 队首指针向左移动一位 - // 通过取余操作实现 front 越过数组头部后回到尾部 + // Move the front pointer one position to the left + // Implement front crossing the head of the array to return to the tail by modulo operation front = index(front - 1); - // 将 num 添加至队首 + // Add num to the front nums[front] = num; queSize++; } - /* 队尾入队 */ + /* Rear enqueue */ public void pushLast(int num) { if (queSize == capacity()) { - System.out.println("双向队列已满"); + System.out.println("Double-ended queue is full"); return; } - // 计算队尾指针,指向队尾索引 + 1 + // Calculate rear pointer, pointing to rear index + 1 int rear = index(front + queSize); - // 将 num 添加至队尾 + // Add num to the rear nums[rear] = num; queSize++; } - /* 队首出队 */ + /* Front dequeue */ public int popFirst() { int num = peekFirst(); - // 队首指针向后移动一位 + // Move front pointer one position backward front = index(front + 1); queSize--; return num; } - /* 队尾出队 */ + /* Rear dequeue */ public int popLast() { int num = peekLast(); queSize--; return num; } - /* 访问队首元素 */ + /* Access front element */ public int peekFirst() { if (isEmpty()) throw new IndexOutOfBoundsException(); return nums[front]; } - /* 访问队尾元素 */ + /* Access rear element */ public int peekLast() { if (isEmpty()) throw new IndexOutOfBoundsException(); - // 计算尾元素索引 + // Calculate rear element index int last = index(front + queSize - 1); return nums[last]; } - /* 返回数组用于打印 */ + /* Return array for printing */ public int[] toArray() { - // 仅转换有效长度范围内的列表元素 + // Only convert elements within valid length range int[] res = new int[queSize]; for (int i = 0, j = front; i < queSize; i++, j++) { res[i] = nums[index(j)]; @@ -2599,1097 +952,61 @@ The implementation only needs to add methods for "front enqueue" and "rear deque === "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; - } - } + [class]{ArrayDeque}-[func]{} ``` === "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 - } + [class]{arrayDeque}-[func]{} ``` === "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)] } - } - } + [class]{ArrayDeque}-[func]{} ``` === "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; - } - } + [class]{ArrayDeque}-[func]{} ``` === "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; - } - } + [class]{ArrayDeque}-[func]{} ``` === "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; - } - } + [class]{ArrayDeque}-[func]{} ``` === "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 - } - } + [class]{ArrayDeque}-[func]{} ``` === "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; - } + [class]{ArrayDeque}-[func]{} ``` === "Kotlin" ```kotlin title="array_deque.kt" - /* 构造方法 */ - class ArrayDeque(capacity: Int) { - private var nums: IntArray = IntArray(capacity) // 用于存储双向队列元素的数组 - private var front: Int = 0 // 队首指针,指向队首元素 - private var queSize: Int = 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 - } - } + [class]{ArrayDeque}-[func]{} ``` === "Ruby" ```ruby title="array_deque.rb" - ### 基于环形数组实现的双向队列 ### - class ArrayDeque - ### 获取双向队列的长度 ### - attr_reader :size - - ### 构造方法 ### - def initialize(capacity) - @nums = Array.new(capacity, 0) - @front = 0 - @size = 0 - end - - ### 获取双向队列的容量 ### - def capacity - @nums.length - end - - ### 判断双向队列是否为空 ### - def is_empty? - size.zero? - end - - ### 队首入队 ### - def push_first(num) - if size == capacity - puts '双向队列已满' - return - end - - # 队首指针向左移动一位 - # 通过取余操作实现 front 越过数组头部后回到尾部 - @front = index(@front - 1) - # 将 num 添加至队首 - @nums[@front] = num - @size += 1 - end - - ### 队尾入队 ### - def push_last(num) - if size == capacity - puts '双向队列已满' - return - end - - # 计算队尾指针,指向队尾索引 + 1 - rear = index(@front + size) - # 将 num 添加至队尾 - @nums[rear] = num - @size += 1 - end - - ### 队首出队 ### - def pop_first - num = peek_first - # 队首指针向后移动一位 - @front = index(@front + 1) - @size -= 1 - num - end - - ### 队尾出队 ### - def pop_last - num = peek_last - @size -= 1 - num - end - - ### 访问队首元素 ### - def peek_first - raise IndexError, '双向队列为空' if is_empty? - - @nums[@front] - end - - ### 访问队尾元素 ### - def peek_last - raise IndexError, '双向队列为空' if is_empty? - - # 计算尾元素索引 - last = index(@front + size - 1) - @nums[last] - end - - ### 返回数组用于打印 ### - def to_array - # 仅转换有效长度范围内的列表元素 - res = [] - for i in 0...size - res << @nums[index(@front + i)] - end - res - end - - private - - ### 计算环形数组索引 ### - def index(i) - # 通过取余操作实现数组首尾相连 - # 当 i 越过数组尾部后,回到头部 - # 当 i 越过数组头部后,回到尾部 - (i + capacity) % capacity - end - end + [class]{ArrayDeque}-[func]{} ``` === "Zig" diff --git a/en/docs/chapter_stack_and_queue/queue.md b/en/docs/chapter_stack_and_queue/queue.md index 82b65befe..9e51c167c 100755 --- a/en/docs/chapter_stack_and_queue/queue.md +++ b/en/docs/chapter_stack_and_queue/queue.md @@ -354,52 +354,52 @@ Below is the code for implementing a queue using a linked list: ```python title="linkedlist_queue.py" class LinkedListQueue: - """基于链表实现的队列""" + """Queue class based on linked list""" def __init__(self): - """构造方法""" - self._front: ListNode | None = None # 头节点 front - self._rear: ListNode | None = None # 尾节点 rear + """Constructor""" + self._front: ListNode | None = None # Head node front + self._rear: ListNode | None = None # Tail node rear self._size: int = 0 def size(self) -> int: - """获取队列的长度""" + """Get the length of the queue""" return self._size def is_empty(self) -> bool: - """判断队列是否为空""" + """Determine if the queue is empty""" return self._size == 0 def push(self, num: int): - """入队""" - # 在尾节点后添加 num + """Enqueue""" + # Add num behind the tail node node = ListNode(num) - # 如果队列为空,则令头、尾节点都指向该节点 + # If the queue is empty, make the head and tail nodes both point to that node if self._front is None: self._front = node self._rear = node - # 如果队列不为空,则将该节点添加到尾节点后 + # If the queue is not empty, add that node behind the tail node else: self._rear.next = node self._rear = node self._size += 1 def pop(self) -> int: - """出队""" + """Dequeue""" num = self.peek() - # 删除头节点 + # Remove head node self._front = self._front.next self._size -= 1 return num def peek(self) -> int: - """访问队首元素""" + """Access front element""" if self.is_empty(): - raise IndexError("队列为空") + raise IndexError("Queue is empty") return self._front.val def to_list(self) -> list[int]: - """转化为列表用于打印""" + """Convert to a list for printing""" queue = [] temp = self._front while temp: @@ -411,89 +411,15 @@ Below is the code for implementing a queue using a linked list: === "C++" ```cpp title="linkedlist_queue.cpp" - /* 基于链表实现的队列 */ - class LinkedListQueue { - private: - ListNode *front, *rear; // 头节点 front ,尾节点 rear - int queSize; - - public: - LinkedListQueue() { - front = nullptr; - rear = nullptr; - queSize = 0; - } - - ~LinkedListQueue() { - // 遍历链表删除节点,释放内存 - freeMemoryLinkedList(front); - } - - /* 获取队列的长度 */ - int size() { - return queSize; - } - - /* 判断队列是否为空 */ - bool isEmpty() { - return queSize == 0; - } - - /* 入队 */ - void push(int num) { - // 在尾节点后添加 num - ListNode *node = new ListNode(num); - // 如果队列为空,则令头、尾节点都指向该节点 - if (front == nullptr) { - front = node; - rear = node; - } - // 如果队列不为空,则将该节点添加到尾节点后 - else { - rear->next = node; - rear = node; - } - queSize++; - } - - /* 出队 */ - int pop() { - int num = peek(); - // 删除头节点 - ListNode *tmp = front; - front = front->next; - // 释放内存 - delete tmp; - queSize--; - return num; - } - - /* 访问队首元素 */ - int peek() { - if (size() == 0) - throw out_of_range("队列为空"); - return front->val; - } - - /* 将链表转化为 Vector 并返回 */ - vector toVector() { - ListNode *node = front; - vector res(size()); - for (int i = 0; i < res.size(); i++) { - res[i] = node->val; - node = node->next; - } - return res; - } - }; + [class]{LinkedListQueue}-[func]{} ``` === "Java" ```java title="linkedlist_queue.java" - /* 基于链表实现的队列 */ + /* Queue class based on linked list */ class LinkedListQueue { - private ListNode front, rear; // 头节点 front ,尾节点 rear + private ListNode front, rear; // Front node front, back node rear private int queSize = 0; public LinkedListQueue() { @@ -501,25 +427,25 @@ Below is the code for implementing a queue using a linked list: rear = null; } - /* 获取队列的长度 */ + /* Get the length of the queue */ public int size() { return queSize; } - /* 判断队列是否为空 */ + /* Determine if the queue is empty */ public boolean isEmpty() { return size() == 0; } - /* 入队 */ + /* Enqueue */ public void push(int num) { - // 在尾节点后添加 num + // Add num behind the tail node ListNode node = new ListNode(num); - // 如果队列为空,则令头、尾节点都指向该节点 + // If the queue is empty, make the head and tail nodes both point to that node if (front == null) { front = node; rear = node; - // 如果队列不为空,则将该节点添加到尾节点后 + // If the queue is not empty, add that node behind the tail node } else { rear.next = node; rear = node; @@ -527,23 +453,23 @@ Below is the code for implementing a queue using a linked list: queSize++; } - /* 出队 */ + /* Dequeue */ public int pop() { int num = peek(); - // 删除头节点 + // Remove head node front = front.next; queSize--; return num; } - /* 访问队首元素 */ + /* Access front element */ public int peek() { if (isEmpty()) throw new IndexOutOfBoundsException(); return front.val; } - /* 将链表转化为 Array 并返回 */ + /* Convert the linked list to Array and return */ public int[] toArray() { ListNode node = front; int[] res = new int[size()]; @@ -559,804 +485,69 @@ Below is the code for implementing a queue using a linked list: === "C#" ```csharp title="linkedlist_queue.cs" - /* 基于链表实现的队列 */ - class LinkedListQueue { - ListNode? front, rear; // 头节点 front ,尾节点 rear - int queSize = 0; - - public LinkedListQueue() { - front = null; - rear = null; - } - - /* 获取队列的长度 */ - public int Size() { - return queSize; - } - - /* 判断队列是否为空 */ - public bool IsEmpty() { - return Size() == 0; - } - - /* 入队 */ - public void Push(int num) { - // 在尾节点后添加 num - ListNode node = new(num); - // 如果队列为空,则令头、尾节点都指向该节点 - if (front == null) { - front = node; - rear = node; - // 如果队列不为空,则将该节点添加到尾节点后 - } else if (rear != null) { - rear.next = node; - rear = node; - } - queSize++; - } - - /* 出队 */ - public int Pop() { - int num = Peek(); - // 删除头节点 - front = front?.next; - queSize--; - return num; - } - - /* 访问队首元素 */ - public int Peek() { - if (IsEmpty()) - throw new Exception(); - return front!.val; - } - - /* 将链表转化为 Array 并返回 */ - public int[] ToArray() { - if (front == null) - return []; - - ListNode? node = front; - int[] res = new int[Size()]; - for (int i = 0; i < res.Length; i++) { - res[i] = node!.val; - node = node.next; - } - return res; - } - } + [class]{LinkedListQueue}-[func]{} ``` === "Go" ```go title="linkedlist_queue.go" - /* 基于链表实现的队列 */ - type linkedListQueue struct { - // 使用内置包 list 来实现队列 - data *list.List - } - - /* 初始化队列 */ - func newLinkedListQueue() *linkedListQueue { - return &linkedListQueue{ - data: list.New(), - } - } - - /* 入队 */ - func (s *linkedListQueue) push(value any) { - s.data.PushBack(value) - } - - /* 出队 */ - func (s *linkedListQueue) pop() any { - if s.isEmpty() { - return nil - } - e := s.data.Front() - s.data.Remove(e) - return e.Value - } - - /* 访问队首元素 */ - func (s *linkedListQueue) peek() any { - if s.isEmpty() { - return nil - } - e := s.data.Front() - return e.Value - } - - /* 获取队列的长度 */ - func (s *linkedListQueue) size() int { - return s.data.Len() - } - - /* 判断队列是否为空 */ - func (s *linkedListQueue) isEmpty() bool { - return s.data.Len() == 0 - } - - /* 获取 List 用于打印 */ - func (s *linkedListQueue) toList() *list.List { - return s.data - } + [class]{linkedListQueue}-[func]{} ``` === "Swift" ```swift title="linkedlist_queue.swift" - /* 基于链表实现的队列 */ - class LinkedListQueue { - private var front: ListNode? // 头节点 - private var rear: ListNode? // 尾节点 - private var _size: Int - - init() { - _size = 0 - } - - /* 获取队列的长度 */ - func size() -> Int { - _size - } - - /* 判断队列是否为空 */ - func isEmpty() -> Bool { - size() == 0 - } - - /* 入队 */ - func push(num: Int) { - // 在尾节点后添加 num - let node = ListNode(x: num) - // 如果队列为空,则令头、尾节点都指向该节点 - if front == nil { - front = node - rear = node - } - // 如果队列不为空,则将该节点添加到尾节点后 - else { - rear?.next = node - rear = node - } - _size += 1 - } - - /* 出队 */ - @discardableResult - func pop() -> Int { - let num = peek() - // 删除头节点 - front = front?.next - _size -= 1 - return num - } - - /* 访问队首元素 */ - func peek() -> Int { - if isEmpty() { - fatalError("队列为空") - } - return front!.val - } - - /* 将链表转化为 Array 并返回 */ - func toArray() -> [Int] { - var node = front - var res = Array(repeating: 0, count: size()) - for i in res.indices { - res[i] = node!.val - node = node?.next - } - return res - } - } + [class]{LinkedListQueue}-[func]{} ``` === "JS" ```javascript title="linkedlist_queue.js" - /* 基于链表实现的队列 */ - class LinkedListQueue { - #front; // 头节点 #front - #rear; // 尾节点 #rear - #queSize = 0; - - constructor() { - this.#front = null; - this.#rear = null; - } - - /* 获取队列的长度 */ - get size() { - return this.#queSize; - } - - /* 判断队列是否为空 */ - isEmpty() { - return this.size === 0; - } - - /* 入队 */ - push(num) { - // 在尾节点后添加 num - const node = new ListNode(num); - // 如果队列为空,则令头、尾节点都指向该节点 - if (!this.#front) { - this.#front = node; - this.#rear = node; - // 如果队列不为空,则将该节点添加到尾节点后 - } else { - this.#rear.next = node; - this.#rear = node; - } - this.#queSize++; - } - - /* 出队 */ - pop() { - const num = this.peek(); - // 删除头节点 - this.#front = this.#front.next; - this.#queSize--; - return num; - } - - /* 访问队首元素 */ - peek() { - if (this.size === 0) throw new Error('队列为空'); - return this.#front.val; - } - - /* 将链表转化为 Array 并返回 */ - toArray() { - let node = this.#front; - const res = new Array(this.size); - for (let i = 0; i < res.length; i++) { - res[i] = node.val; - node = node.next; - } - return res; - } - } + [class]{LinkedListQueue}-[func]{} ``` === "TS" ```typescript title="linkedlist_queue.ts" - /* 基于链表实现的队列 */ - class LinkedListQueue { - private front: ListNode | null; // 头节点 front - private rear: ListNode | null; // 尾节点 rear - private queSize: number = 0; - - constructor() { - this.front = null; - this.rear = null; - } - - /* 获取队列的长度 */ - get size(): number { - return this.queSize; - } - - /* 判断队列是否为空 */ - isEmpty(): boolean { - return this.size === 0; - } - - /* 入队 */ - push(num: number): void { - // 在尾节点后添加 num - const node = new ListNode(num); - // 如果队列为空,则令头、尾节点都指向该节点 - if (!this.front) { - this.front = node; - this.rear = node; - // 如果队列不为空,则将该节点添加到尾节点后 - } else { - this.rear!.next = node; - this.rear = node; - } - this.queSize++; - } - - /* 出队 */ - pop(): number { - const num = this.peek(); - if (!this.front) throw new Error('队列为空'); - // 删除头节点 - this.front = this.front.next; - this.queSize--; - return num; - } - - /* 访问队首元素 */ - peek(): number { - if (this.size === 0) throw new Error('队列为空'); - return this.front!.val; - } - - /* 将链表转化为 Array 并返回 */ - toArray(): number[] { - let node = this.front; - const res = new Array(this.size); - for (let i = 0; i < res.length; i++) { - res[i] = node!.val; - node = node!.next; - } - return res; - } - } + [class]{LinkedListQueue}-[func]{} ``` === "Dart" ```dart title="linkedlist_queue.dart" - /* 基于链表实现的队列 */ - class LinkedListQueue { - ListNode? _front; // 头节点 _front - ListNode? _rear; // 尾节点 _rear - int _queSize = 0; // 队列长度 - - LinkedListQueue() { - _front = null; - _rear = null; - } - - /* 获取队列的长度 */ - int size() { - return _queSize; - } - - /* 判断队列是否为空 */ - bool isEmpty() { - return _queSize == 0; - } - - /* 入队 */ - void push(int _num) { - // 在尾节点后添加 _num - final node = ListNode(_num); - // 如果队列为空,则令头、尾节点都指向该节点 - if (_front == null) { - _front = node; - _rear = node; - } else { - // 如果队列不为空,则将该节点添加到尾节点后 - _rear!.next = node; - _rear = node; - } - _queSize++; - } - - /* 出队 */ - int pop() { - final int _num = peek(); - // 删除头节点 - _front = _front!.next; - _queSize--; - return _num; - } - - /* 访问队首元素 */ - int peek() { - if (_queSize == 0) { - throw Exception('队列为空'); - } - return _front!.val; - } - - /* 将链表转化为 Array 并返回 */ - List toArray() { - ListNode? node = _front; - final List queue = []; - while (node != null) { - queue.add(node.val); - node = node.next; - } - return queue; - } - } + [class]{LinkedListQueue}-[func]{} ``` === "Rust" ```rust title="linkedlist_queue.rs" - /* 基于链表实现的队列 */ - #[allow(dead_code)] - pub struct LinkedListQueue { - front: Option>>>, // 头节点 front - rear: Option>>>, // 尾节点 rear - que_size: usize, // 队列的长度 - } - - impl LinkedListQueue { - pub fn new() -> Self { - Self { - front: None, - rear: None, - que_size: 0, - } - } - - /* 获取队列的长度 */ - pub fn size(&self) -> usize { - return self.que_size; - } - - /* 判断队列是否为空 */ - pub fn is_empty(&self) -> bool { - return self.size() == 0; - } - - /* 入队 */ - pub fn push(&mut self, num: T) { - // 在尾节点后添加 num - let new_rear = ListNode::new(num); - match self.rear.take() { - // 如果队列不为空,则将该节点添加到尾节点后 - Some(old_rear) => { - old_rear.borrow_mut().next = Some(new_rear.clone()); - self.rear = Some(new_rear); - } - // 如果队列为空,则令头、尾节点都指向该节点 - None => { - self.front = Some(new_rear.clone()); - self.rear = Some(new_rear); - } - } - self.que_size += 1; - } - - /* 出队 */ - pub fn pop(&mut self) -> Option { - self.front.take().map(|old_front| { - match old_front.borrow_mut().next.take() { - Some(new_front) => { - self.front = Some(new_front); - } - None => { - self.rear.take(); - } - } - self.que_size -= 1; - Rc::try_unwrap(old_front).ok().unwrap().into_inner().val - }) - } - - /* 访问队首元素 */ - pub fn peek(&self) -> Option<&Rc>>> { - self.front.as_ref() - } - - /* 将链表转化为 Array 并返回 */ - pub fn to_array(&self, head: Option<&Rc>>>) -> Vec { - if let Some(node) = head { - let mut nums = self.to_array(node.borrow().next.as_ref()); - nums.insert(0, node.borrow().val); - return nums; - } - return Vec::new(); - } - } + [class]{LinkedListQueue}-[func]{} ``` === "C" ```c title="linkedlist_queue.c" - /* 基于链表实现的队列 */ - typedef struct { - ListNode *front, *rear; - int queSize; - } LinkedListQueue; - - /* 构造函数 */ - LinkedListQueue *newLinkedListQueue() { - LinkedListQueue *queue = (LinkedListQueue *)malloc(sizeof(LinkedListQueue)); - queue->front = NULL; - queue->rear = NULL; - queue->queSize = 0; - return queue; - } - - /* 析构函数 */ - void delLinkedListQueue(LinkedListQueue *queue) { - // 释放所有节点 - while (queue->front != NULL) { - ListNode *tmp = queue->front; - queue->front = queue->front->next; - free(tmp); - } - // 释放 queue 结构体 - free(queue); - } - - /* 获取队列的长度 */ - int size(LinkedListQueue *queue) { - return queue->queSize; - } - - /* 判断队列是否为空 */ - bool empty(LinkedListQueue *queue) { - return (size(queue) == 0); - } - - /* 入队 */ - void push(LinkedListQueue *queue, int num) { - // 尾节点处添加 node - ListNode *node = newListNode(num); - // 如果队列为空,则令头、尾节点都指向该节点 - if (queue->front == NULL) { - queue->front = node; - queue->rear = node; - } - // 如果队列不为空,则将该节点添加到尾节点后 - else { - queue->rear->next = node; - queue->rear = node; - } - queue->queSize++; - } - - /* 访问队首元素 */ - int peek(LinkedListQueue *queue) { - assert(size(queue) && queue->front); - return queue->front->val; - } - - /* 出队 */ - int pop(LinkedListQueue *queue) { - int num = peek(queue); - ListNode *tmp = queue->front; - queue->front = queue->front->next; - free(tmp); - queue->queSize--; - return num; - } - - /* 打印队列 */ - void printLinkedListQueue(LinkedListQueue *queue) { - int *arr = malloc(sizeof(int) * queue->queSize); - // 拷贝链表中的数据到数组 - int i; - ListNode *node; - for (i = 0, node = queue->front; i < queue->queSize; i++) { - arr[i] = node->val; - node = node->next; - } - printArray(arr, queue->queSize); - free(arr); - } + [class]{LinkedListQueue}-[func]{} ``` === "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!!._val - } - - /* 将链表转化为 Array 并返回 */ - fun toArray(): IntArray { - var node = front - val res = IntArray(size()) - for (i in res.indices) { - res[i] = node!!._val - node = node.next - } - return res - } - } + [class]{LinkedListQueue}-[func]{} ``` === "Ruby" ```ruby title="linkedlist_queue.rb" - ### 基于链表头现的队列 ### - class LinkedListQueue - ### 获取队列的长度 ### - attr_reader :size - - ### 构造方法 ### - def initialize - @front = nil # 头节点 front - @rear = nil # 尾节点 rear - @size = 0 - end - - ### 判断队列是否为空 ### - def is_empty? - @front.nil? - end - - ### 入队 ### - def push(num) - # 在尾节点后添加 num - node = ListNode.new(num) - - # 如果队列为空,则令头,尾节点都指向该节点 - if @front.nil? - @front = node - @rear = node - # 如果队列不为空,则令该节点添加到尾节点后 - else - @rear.next = node - @rear = node - end - - @size += 1 - end - - ### 出队 ### - def pop - num = peek - # 删除头节点 - @front = @front.next - @size -= 1 - num - end - - ### 访问队首元素 ### - def peek - raise IndexError, '队列为空' if is_empty? - - @front.val - end - - ### 将链表为 Array 并返回 ### - def to_array - queue = [] - temp = @front - while temp - queue << temp.val - temp = temp.next - end - queue - end - end + [class]{LinkedListQueue}-[func]{} ``` === "Zig" ```zig title="linkedlist_queue.zig" - // 基于链表实现的队列 - fn LinkedListQueue(comptime T: type) type { - return struct { - const Self = @This(); - - front: ?*inc.ListNode(T) = null, // 头节点 front - rear: ?*inc.ListNode(T) = null, // 尾节点 rear - que_size: usize = 0, // 队列的长度 - mem_arena: ?std.heap.ArenaAllocator = null, - mem_allocator: std.mem.Allocator = undefined, // 内存分配器 - - // 构造函数(分配内存+初始化队列) - pub fn init(self: *Self, allocator: std.mem.Allocator) !void { - if (self.mem_arena == null) { - self.mem_arena = std.heap.ArenaAllocator.init(allocator); - self.mem_allocator = self.mem_arena.?.allocator(); - } - self.front = null; - self.rear = null; - self.que_size = 0; - } - - // 析构函数(释放内存) - pub fn deinit(self: *Self) void { - if (self.mem_arena == null) return; - self.mem_arena.?.deinit(); - } - - // 获取队列的长度 - pub fn size(self: *Self) usize { - return self.que_size; - } - - // 判断队列是否为空 - pub fn isEmpty(self: *Self) bool { - return self.size() == 0; - } - - // 访问队首元素 - pub fn peek(self: *Self) T { - if (self.size() == 0) @panic("队列为空"); - return self.front.?.val; - } - - // 入队 - pub fn push(self: *Self, num: T) !void { - // 在尾节点后添加 num - var node = try self.mem_allocator.create(inc.ListNode(T)); - node.init(num); - // 如果队列为空,则令头、尾节点都指向该节点 - if (self.front == null) { - self.front = node; - self.rear = node; - // 如果队列不为空,则将该节点添加到尾节点后 - } else { - self.rear.?.next = node; - self.rear = node; - } - self.que_size += 1; - } - - // 出队 - pub fn pop(self: *Self) T { - var num = self.peek(); - // 删除头节点 - self.front = self.front.?.next; - self.que_size -= 1; - return num; - } - - // 将链表转换为数组 - pub fn toArray(self: *Self) ![]T { - var node = self.front; - var res = try self.mem_allocator.alloc(T, self.size()); - @memset(res, @as(T, 0)); - var i: usize = 0; - while (i < res.len) : (i += 1) { - res[i] = node.?.val; - node = node.?.next; - } - return res; - } - }; - } + [class]{LinkedListQueue}-[func]{} ``` -??? pythontutor "Code Visualization" - -
- - ### 2.   Implementation based on an array Deleting the first element in an array has a time complexity of $O(n)$, which would make the dequeue operation inefficient. However, this problem can be cleverly avoided as follows. @@ -1389,53 +580,53 @@ In a circular array, `front` or `rear` needs to loop back to the start of the ar ```python title="array_queue.py" class ArrayQueue: - """基于环形数组实现的队列""" + """Queue class based on circular array""" def __init__(self, size: int): - """构造方法""" - self._nums: list[int] = [0] * size # 用于存储队列元素的数组 - self._front: int = 0 # 队首指针,指向队首元素 - self._size: int = 0 # 队列长度 + """Constructor""" + self._nums: list[int] = [0] * size # Array for storing queue elements + self._front: int = 0 # Front pointer, pointing to the front element + self._size: int = 0 # Queue length def capacity(self) -> int: - """获取队列的容量""" + """Get the capacity of the queue""" return len(self._nums) def size(self) -> int: - """获取队列的长度""" + """Get the length of the queue""" return self._size def is_empty(self) -> bool: - """判断队列是否为空""" + """Determine if the queue is empty""" return self._size == 0 def push(self, num: int): - """入队""" + """Enqueue""" if self._size == self.capacity(): - raise IndexError("队列已满") - # 计算队尾指针,指向队尾索引 + 1 - # 通过取余操作实现 rear 越过数组尾部后回到头部 + raise IndexError("Queue is full") + # Calculate rear pointer, pointing to rear index + 1 + # Use modulo operation to wrap the rear pointer from the end of the array back to the start rear: int = (self._front + self._size) % self.capacity() - # 将 num 添加至队尾 + # Add num to the rear self._nums[rear] = num self._size += 1 def pop(self) -> int: - """出队""" + """Dequeue""" num: int = self.peek() - # 队首指针向后移动一位,若越过尾部,则返回到数组头部 + # Move front pointer one position backward, returning to the head of the array if it exceeds the tail self._front = (self._front + 1) % self.capacity() self._size -= 1 return num def peek(self) -> int: - """访问队首元素""" + """Access front element""" if self.is_empty(): - raise IndexError("队列为空") + raise IndexError("Queue is empty") return self._nums[self._front] def to_list(self) -> list[int]: - """返回列表用于打印""" + """Return array for printing""" res = [0] * self.size() j: int = self._front for i in range(self.size()): @@ -1447,145 +638,71 @@ In a circular array, `front` or `rear` needs to loop back to the start of the ar === "C++" ```cpp title="array_queue.cpp" - /* 基于环形数组实现的队列 */ - class ArrayQueue { - private: - int *nums; // 用于存储队列元素的数组 - int front; // 队首指针,指向队首元素 - int queSize; // 队列长度 - int queCapacity; // 队列容量 - - public: - ArrayQueue(int capacity) { - // 初始化数组 - nums = new int[capacity]; - queCapacity = capacity; - front = queSize = 0; - } - - ~ArrayQueue() { - delete[] nums; - } - - /* 获取队列的容量 */ - int capacity() { - return queCapacity; - } - - /* 获取队列的长度 */ - int size() { - return queSize; - } - - /* 判断队列是否为空 */ - bool isEmpty() { - return size() == 0; - } - - /* 入队 */ - void push(int num) { - if (queSize == queCapacity) { - cout << "队列已满" << endl; - return; - } - // 计算队尾指针,指向队尾索引 + 1 - // 通过取余操作实现 rear 越过数组尾部后回到头部 - int rear = (front + queSize) % queCapacity; - // 将 num 添加至队尾 - nums[rear] = num; - queSize++; - } - - /* 出队 */ - int pop() { - int num = peek(); - // 队首指针向后移动一位,若越过尾部,则返回到数组头部 - front = (front + 1) % queCapacity; - queSize--; - return num; - } - - /* 访问队首元素 */ - int peek() { - if (isEmpty()) - throw out_of_range("队列为空"); - return nums[front]; - } - - /* 将数组转化为 Vector 并返回 */ - vector toVector() { - // 仅转换有效长度范围内的列表元素 - vector arr(queSize); - for (int i = 0, j = front; i < queSize; i++, j++) { - arr[i] = nums[j % queCapacity]; - } - return arr; - } - }; + [class]{ArrayQueue}-[func]{} ``` === "Java" ```java title="array_queue.java" - /* 基于环形数组实现的队列 */ + /* Queue class based on circular array */ class ArrayQueue { - private int[] nums; // 用于存储队列元素的数组 - private int front; // 队首指针,指向队首元素 - private int queSize; // 队列长度 + private int[] nums; // Array for storing queue elements + private int front; // Front pointer, pointing to the front element + private int queSize; // Queue length public ArrayQueue(int capacity) { nums = new int[capacity]; front = queSize = 0; } - /* 获取队列的容量 */ + /* Get the capacity of the queue */ public int capacity() { return nums.length; } - /* 获取队列的长度 */ + /* Get the length of the queue */ public int size() { return queSize; } - /* 判断队列是否为空 */ + /* Determine if the queue is empty */ public boolean isEmpty() { return queSize == 0; } - /* 入队 */ + /* Enqueue */ public void push(int num) { if (queSize == capacity()) { - System.out.println("队列已满"); + System.out.println("Queue is full"); return; } - // 计算队尾指针,指向队尾索引 + 1 - // 通过取余操作实现 rear 越过数组尾部后回到头部 + // Calculate rear pointer, pointing to rear index + 1 + // Use modulo operation to wrap the rear pointer from the end of the array back to the start int rear = (front + queSize) % capacity(); - // 将 num 添加至队尾 + // Add num to the rear nums[rear] = num; queSize++; } - /* 出队 */ + /* Dequeue */ public int pop() { int num = peek(); - // 队首指针向后移动一位,若越过尾部,则返回到数组头部 + // Move front pointer one position backward, returning to the head of the array if it exceeds the tail front = (front + 1) % capacity(); queSize--; return num; } - /* 访问队首元素 */ + /* Access front element */ public int peek() { if (isEmpty()) throw new IndexOutOfBoundsException(); return nums[front]; } - /* 返回数组 */ + /* Return array */ public int[] toArray() { - // 仅转换有效长度范围内的列表元素 + // Only convert elements within valid length range int[] res = new int[queSize]; for (int i = 0, j = front; i < queSize; i++, j++) { res[i] = nums[j % capacity()]; @@ -1598,816 +715,69 @@ In a circular array, `front` or `rear` needs to loop back to the start of the ar === "C#" ```csharp title="array_queue.cs" - /* 基于环形数组实现的队列 */ - class ArrayQueue { - int[] nums; // 用于存储队列元素的数组 - int front; // 队首指针,指向队首元素 - int queSize; // 队列长度 - - public ArrayQueue(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; - } - - /* 入队 */ - public void Push(int num) { - if (queSize == Capacity()) { - Console.WriteLine("队列已满"); - return; - } - // 计算队尾指针,指向队尾索引 + 1 - // 通过取余操作实现 rear 越过数组尾部后回到头部 - int rear = (front + queSize) % Capacity(); - // 将 num 添加至队尾 - nums[rear] = num; - queSize++; - } - - /* 出队 */ - public int Pop() { - int num = Peek(); - // 队首指针向后移动一位,若越过尾部,则返回到数组头部 - front = (front + 1) % Capacity(); - queSize--; - return num; - } - - /* 访问队首元素 */ - public int Peek() { - if (IsEmpty()) - throw new Exception(); - return nums[front]; - } - - /* 返回数组 */ - public int[] ToArray() { - // 仅转换有效长度范围内的列表元素 - int[] res = new int[queSize]; - for (int i = 0, j = front; i < queSize; i++, j++) { - res[i] = nums[j % this.Capacity()]; - } - return res; - } - } + [class]{ArrayQueue}-[func]{} ``` === "Go" ```go title="array_queue.go" - /* 基于环形数组实现的队列 */ - type arrayQueue struct { - nums []int // 用于存储队列元素的数组 - front int // 队首指针,指向队首元素 - queSize int // 队列长度 - queCapacity int // 队列容量(即最大容纳元素数量) - } - - /* 初始化队列 */ - func newArrayQueue(queCapacity int) *arrayQueue { - return &arrayQueue{ - nums: make([]int, queCapacity), - queCapacity: queCapacity, - front: 0, - queSize: 0, - } - } - - /* 获取队列的长度 */ - func (q *arrayQueue) size() int { - return q.queSize - } - - /* 判断队列是否为空 */ - func (q *arrayQueue) isEmpty() bool { - return q.queSize == 0 - } - - /* 入队 */ - func (q *arrayQueue) push(num int) { - // 当 rear == queCapacity 表示队列已满 - if q.queSize == q.queCapacity { - return - } - // 计算队尾指针,指向队尾索引 + 1 - // 通过取余操作实现 rear 越过数组尾部后回到头部 - rear := (q.front + q.queSize) % q.queCapacity - // 将 num 添加至队尾 - q.nums[rear] = num - q.queSize++ - } - - /* 出队 */ - func (q *arrayQueue) pop() any { - num := q.peek() - // 队首指针向后移动一位,若越过尾部,则返回到数组头部 - q.front = (q.front + 1) % q.queCapacity - q.queSize-- - return num - } - - /* 访问队首元素 */ - func (q *arrayQueue) peek() any { - if q.isEmpty() { - return nil - } - return q.nums[q.front] - } - - /* 获取 Slice 用于打印 */ - func (q *arrayQueue) toSlice() []int { - rear := (q.front + q.queSize) - if rear >= q.queCapacity { - rear %= q.queCapacity - return append(q.nums[q.front:], q.nums[:rear]...) - } - return q.nums[q.front:rear] - } + [class]{arrayQueue}-[func]{} ``` === "Swift" ```swift title="array_queue.swift" - /* 基于环形数组实现的队列 */ - class ArrayQueue { - 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 - } - - /* 入队 */ - func push(num: Int) { - if size() == capacity() { - print("队列已满") - return - } - // 计算队尾指针,指向队尾索引 + 1 - // 通过取余操作实现 rear 越过数组尾部后回到头部 - let rear = (front + size()) % capacity() - // 将 num 添加至队尾 - nums[rear] = num - _size += 1 - } - - /* 出队 */ - @discardableResult - func pop() -> Int { - let num = peek() - // 队首指针向后移动一位,若越过尾部,则返回到数组头部 - front = (front + 1) % capacity() - _size -= 1 - return num - } - - /* 访问队首元素 */ - func peek() -> Int { - if isEmpty() { - fatalError("队列为空") - } - return nums[front] - } - - /* 返回数组 */ - func toArray() -> [Int] { - // 仅转换有效长度范围内的列表元素 - (front ..< front + size()).map { nums[$0 % capacity()] } - } - } + [class]{ArrayQueue}-[func]{} ``` === "JS" ```javascript title="array_queue.js" - /* 基于环形数组实现的队列 */ - class ArrayQueue { - #nums; // 用于存储队列元素的数组 - #front = 0; // 队首指针,指向队首元素 - #queSize = 0; // 队列长度 - - constructor(capacity) { - this.#nums = new Array(capacity); - } - - /* 获取队列的容量 */ - get capacity() { - return this.#nums.length; - } - - /* 获取队列的长度 */ - get size() { - return this.#queSize; - } - - /* 判断队列是否为空 */ - isEmpty() { - return this.#queSize === 0; - } - - /* 入队 */ - push(num) { - if (this.size === this.capacity) { - console.log('队列已满'); - return; - } - // 计算队尾指针,指向队尾索引 + 1 - // 通过取余操作实现 rear 越过数组尾部后回到头部 - const rear = (this.#front + this.size) % this.capacity; - // 将 num 添加至队尾 - this.#nums[rear] = num; - this.#queSize++; - } - - /* 出队 */ - pop() { - const num = this.peek(); - // 队首指针向后移动一位,若越过尾部,则返回到数组头部 - this.#front = (this.#front + 1) % this.capacity; - this.#queSize--; - return num; - } - - /* 访问队首元素 */ - peek() { - if (this.isEmpty()) throw new Error('队列为空'); - return this.#nums[this.#front]; - } - - /* 返回 Array */ - toArray() { - // 仅转换有效长度范围内的列表元素 - const arr = new Array(this.size); - for (let i = 0, j = this.#front; i < this.size; i++, j++) { - arr[i] = this.#nums[j % this.capacity]; - } - return arr; - } - } + [class]{ArrayQueue}-[func]{} ``` === "TS" ```typescript title="array_queue.ts" - /* 基于环形数组实现的队列 */ - class ArrayQueue { - private nums: number[]; // 用于存储队列元素的数组 - private front: number; // 队首指针,指向队首元素 - private queSize: number; // 队列长度 - - constructor(capacity: number) { - this.nums = new Array(capacity); - this.front = this.queSize = 0; - } - - /* 获取队列的容量 */ - get capacity(): number { - return this.nums.length; - } - - /* 获取队列的长度 */ - get size(): number { - return this.queSize; - } - - /* 判断队列是否为空 */ - isEmpty(): boolean { - return this.queSize === 0; - } - - /* 入队 */ - push(num: number): void { - if (this.size === this.capacity) { - console.log('队列已满'); - return; - } - // 计算队尾指针,指向队尾索引 + 1 - // 通过取余操作实现 rear 越过数组尾部后回到头部 - const rear = (this.front + this.queSize) % this.capacity; - // 将 num 添加至队尾 - this.nums[rear] = num; - this.queSize++; - } - - /* 出队 */ - pop(): number { - const num = this.peek(); - // 队首指针向后移动一位,若越过尾部,则返回到数组头部 - this.front = (this.front + 1) % this.capacity; - this.queSize--; - return num; - } - - /* 访问队首元素 */ - peek(): number { - if (this.isEmpty()) throw new Error('队列为空'); - return this.nums[this.front]; - } - - /* 返回 Array */ - toArray(): number[] { - // 仅转换有效长度范围内的列表元素 - const arr = new Array(this.size); - for (let i = 0, j = this.front; i < this.size; i++, j++) { - arr[i] = this.nums[j % this.capacity]; - } - return arr; - } - } + [class]{ArrayQueue}-[func]{} ``` === "Dart" ```dart title="array_queue.dart" - /* 基于环形数组实现的队列 */ - class ArrayQueue { - late List _nums; // 用于储存队列元素的数组 - late int _front; // 队首指针,指向队首元素 - late int _queSize; // 队列长度 - - ArrayQueue(int capacity) { - _nums = List.filled(capacity, 0); - _front = _queSize = 0; - } - - /* 获取队列的容量 */ - int capaCity() { - return _nums.length; - } - - /* 获取队列的长度 */ - int size() { - return _queSize; - } - - /* 判断队列是否为空 */ - bool isEmpty() { - return _queSize == 0; - } - - /* 入队 */ - void push(int _num) { - if (_queSize == capaCity()) { - throw Exception("队列已满"); - } - // 计算队尾指针,指向队尾索引 + 1 - // 通过取余操作实现 rear 越过数组尾部后回到头部 - int rear = (_front + _queSize) % capaCity(); - // 将 _num 添加至队尾 - _nums[rear] = _num; - _queSize++; - } - - /* 出队 */ - int pop() { - int _num = peek(); - // 队首指针向后移动一位,若越过尾部,则返回到数组头部 - _front = (_front + 1) % capaCity(); - _queSize--; - return _num; - } - - /* 访问队首元素 */ - int peek() { - if (isEmpty()) { - throw Exception("队列为空"); - } - return _nums[_front]; - } - - /* 返回 Array */ - List toArray() { - // 仅转换有效长度范围内的列表元素 - final List res = List.filled(_queSize, 0); - for (int i = 0, j = _front; i < _queSize; i++, j++) { - res[i] = _nums[j % capaCity()]; - } - return res; - } - } + [class]{ArrayQueue}-[func]{} ``` === "Rust" ```rust title="array_queue.rs" - /* 基于环形数组实现的队列 */ - struct ArrayQueue { - nums: Vec, // 用于存储队列元素的数组 - front: i32, // 队首指针,指向队首元素 - que_size: i32, // 队列长度 - que_capacity: i32, // 队列容量 - } - - impl ArrayQueue { - /* 构造方法 */ - fn new(capacity: i32) -> ArrayQueue { - ArrayQueue { - nums: vec![0; capacity as usize], - front: 0, - que_size: 0, - que_capacity: capacity, - } - } - - /* 获取队列的容量 */ - fn capacity(&self) -> i32 { - self.que_capacity - } - - /* 获取队列的长度 */ - fn size(&self) -> i32 { - self.que_size - } - - /* 判断队列是否为空 */ - fn is_empty(&self) -> bool { - self.que_size == 0 - } - - /* 入队 */ - fn push(&mut self, num: i32) { - if self.que_size == self.capacity() { - println!("队列已满"); - return; - } - // 计算队尾指针,指向队尾索引 + 1 - // 通过取余操作实现 rear 越过数组尾部后回到头部 - let rear = (self.front + self.que_size) % self.que_capacity; - // 将 num 添加至队尾 - self.nums[rear as usize] = num; - self.que_size += 1; - } - - /* 出队 */ - fn pop(&mut self) -> i32 { - let num = self.peek(); - // 队首指针向后移动一位,若越过尾部,则返回到数组头部 - self.front = (self.front + 1) % self.que_capacity; - self.que_size -= 1; - num - } - - /* 访问队首元素 */ - fn peek(&self) -> i32 { - if self.is_empty() { - panic!("index out of bounds"); - } - self.nums[self.front as usize] - } - - /* 返回数组 */ - fn to_vector(&self) -> Vec { - let cap = self.que_capacity; - let mut j = self.front; - let mut arr = vec![0; self.que_size as usize]; - for i in 0..self.que_size { - arr[i as usize] = self.nums[(j % cap) as usize]; - j += 1; - } - arr - } - } + [class]{ArrayQueue}-[func]{} ``` === "C" ```c title="array_queue.c" - /* 基于环形数组实现的队列 */ - typedef struct { - int *nums; // 用于存储队列元素的数组 - int front; // 队首指针,指向队首元素 - int queSize; // 尾指针,指向队尾 + 1 - int queCapacity; // 队列容量 - } ArrayQueue; - - /* 构造函数 */ - ArrayQueue *newArrayQueue(int capacity) { - ArrayQueue *queue = (ArrayQueue *)malloc(sizeof(ArrayQueue)); - // 初始化数组 - queue->queCapacity = capacity; - queue->nums = (int *)malloc(sizeof(int) * queue->queCapacity); - queue->front = queue->queSize = 0; - return queue; - } - - /* 析构函数 */ - void delArrayQueue(ArrayQueue *queue) { - free(queue->nums); - free(queue); - } - - /* 获取队列的容量 */ - int capacity(ArrayQueue *queue) { - return queue->queCapacity; - } - - /* 获取队列的长度 */ - int size(ArrayQueue *queue) { - return queue->queSize; - } - - /* 判断队列是否为空 */ - bool empty(ArrayQueue *queue) { - return queue->queSize == 0; - } - - /* 访问队首元素 */ - int peek(ArrayQueue *queue) { - assert(size(queue) != 0); - return queue->nums[queue->front]; - } - - /* 入队 */ - void push(ArrayQueue *queue, int num) { - if (size(queue) == capacity(queue)) { - printf("队列已满\r\n"); - return; - } - // 计算队尾指针,指向队尾索引 + 1 - // 通过取余操作实现 rear 越过数组尾部后回到头部 - int rear = (queue->front + queue->queSize) % queue->queCapacity; - // 将 num 添加至队尾 - queue->nums[rear] = num; - queue->queSize++; - } - - /* 出队 */ - int pop(ArrayQueue *queue) { - int num = peek(queue); - // 队首指针向后移动一位,若越过尾部,则返回到数组头部 - queue->front = (queue->front + 1) % queue->queCapacity; - queue->queSize--; - return num; - } + [class]{ArrayQueue}-[func]{} ``` === "Kotlin" ```kotlin title="array_queue.kt" - /* 基于环形数组实现的队列 */ - class ArrayQueue(capacity: Int) { - private val nums: IntArray = IntArray(capacity) // 用于存储队列元素的数组 - private var front: Int = 0 // 队首指针,指向队首元素 - private var queSize: Int = 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 - } - } + [class]{ArrayQueue}-[func]{} ``` === "Ruby" ```ruby title="array_queue.rb" - ### 基于环形数组实现的队列 ### - class ArrayQueue - ### 获取队列的长度 ### - attr_reader :size - - ### 构造方法 ### - def initialize(size) - @nums = Array.new(size, 0) # 用于存储队列元素的数组 - @front = 0 # 队首指针,指向队首元素 - @size = 0 # 队列长度 - end - - ### 获取队列的容量 ### - def capacity - @nums.length - end - - ### 判断队列是否为空 ### - def is_empty? - size.zero? - end - - ### 入队 ### - def push(num) - raise IndexError, '队列已满' if size == capacity - - # 计算队尾指针,指向队尾索引 + 1 - # 通过取余操作实现 rear 越过数组尾部后回到头部 - rear = (@front + size) % capacity - # 将 num 添加至队尾 - @nums[rear] = num - @size += 1 - end - - ### 出队 ### - def pop - num = peek - # 队首指针向后移动一位,若越过尾部,则返回到数组头部 - @front = (@front + 1) % capacity - @size -= 1 - num - end - - ### 访问队首元素 ### - def peek - raise IndexError, '队列为空' if is_empty? - - @nums[@front] - end - - ### 返回列表用于打印 ### - def to_array - res = Array.new(size, 0) - j = @front - - for i in 0...size - res[i] = @nums[j % capacity] - j += 1 - end - - res - end - end + [class]{ArrayQueue}-[func]{} ``` === "Zig" ```zig title="array_queue.zig" - // 基于环形数组实现的队列 - fn ArrayQueue(comptime T: type) type { - return struct { - const Self = @This(); - - nums: []T = undefined, // 用于存储队列元素的数组 - cap: usize = 0, // 队列容量 - front: usize = 0, // 队首指针,指向队首元素 - queSize: usize = 0, // 尾指针,指向队尾 + 1 - mem_arena: ?std.heap.ArenaAllocator = null, - mem_allocator: std.mem.Allocator = undefined, // 内存分配器 - - // 构造函数(分配内存+初始化数组) - pub fn init(self: *Self, allocator: std.mem.Allocator, cap: usize) !void { - if (self.mem_arena == null) { - self.mem_arena = std.heap.ArenaAllocator.init(allocator); - self.mem_allocator = self.mem_arena.?.allocator(); - } - self.cap = cap; - self.nums = try self.mem_allocator.alloc(T, self.cap); - @memset(self.nums, @as(T, 0)); - } - - // 析构函数(释放内存) - pub fn deinit(self: *Self) void { - if (self.mem_arena == null) return; - self.mem_arena.?.deinit(); - } - - // 获取队列的容量 - pub fn capacity(self: *Self) usize { - return self.cap; - } - - // 获取队列的长度 - pub fn size(self: *Self) usize { - return self.queSize; - } - - // 判断队列是否为空 - pub fn isEmpty(self: *Self) bool { - return self.queSize == 0; - } - - // 入队 - pub fn push(self: *Self, num: T) !void { - if (self.size() == self.capacity()) { - std.debug.print("队列已满\n", .{}); - return; - } - // 计算队尾指针,指向队尾索引 + 1 - // 通过取余操作实现 rear 越过数组尾部后回到头部 - var rear = (self.front + self.queSize) % self.capacity(); - // 在尾节点后添加 num - self.nums[rear] = num; - self.queSize += 1; - } - - // 出队 - pub fn pop(self: *Self) T { - var num = self.peek(); - // 队首指针向后移动一位,若越过尾部,则返回到数组头部 - self.front = (self.front + 1) % self.capacity(); - self.queSize -= 1; - return num; - } - - // 访问队首元素 - pub fn peek(self: *Self) T { - if (self.isEmpty()) @panic("队列为空"); - return self.nums[self.front]; - } - - // 返回数组 - pub fn toArray(self: *Self) ![]T { - // 仅转换有效长度范围内的列表元素 - var res = try self.mem_allocator.alloc(T, self.size()); - @memset(res, @as(T, 0)); - var i: usize = 0; - var j: usize = self.front; - while (i < self.size()) : ({ i += 1; j += 1; }) { - res[i] = self.nums[j % self.capacity()]; - } - return res; - } - }; - } + [class]{ArrayQueue}-[func]{} ``` -??? pythontutor "Code Visualization" - -
- - The above implementation of the queue still has its limitations: its length is fixed. However, this issue is not difficult to resolve. We can replace the array with a dynamic array that can expand itself if needed. Interested readers can try to implement this themselves. The comparison of the two implementations is consistent with that of the stack and is not repeated here. diff --git a/en/docs/chapter_stack_and_queue/stack.md b/en/docs/chapter_stack_and_queue/stack.md index 3c595ddfe..52cacb48b 100755 --- a/en/docs/chapter_stack_and_queue/stack.md +++ b/en/docs/chapter_stack_and_queue/stack.md @@ -352,43 +352,43 @@ Below is an example code for implementing a stack based on a linked list: ```python title="linkedlist_stack.py" class LinkedListStack: - """基于链表实现的栈""" + """Stack class based on linked list""" def __init__(self): - """构造方法""" + """Constructor""" self._peek: ListNode | None = None self._size: int = 0 def size(self) -> int: - """获取栈的长度""" + """Get the length of the stack""" return self._size def is_empty(self) -> bool: - """判断栈是否为空""" + """Determine if the stack is empty""" return self._size == 0 def push(self, val: int): - """入栈""" + """Push""" node = ListNode(val) node.next = self._peek self._peek = node self._size += 1 def pop(self) -> int: - """出栈""" + """Pop""" num = self.peek() self._peek = self._peek.next self._size -= 1 return num def peek(self) -> int: - """访问栈顶元素""" + """Access stack top element""" if self.is_empty(): - raise IndexError("栈为空") + raise IndexError("Stack is empty") return self._peek.val def to_list(self) -> list[int]: - """转化为列表用于打印""" + """Convert to a list for printing""" arr = [] node = self._peek while node: @@ -401,95 +401,32 @@ Below is an example code for implementing a stack based on a linked list: === "C++" ```cpp title="linkedlist_stack.cpp" - /* 基于链表实现的栈 */ - class LinkedListStack { - private: - ListNode *stackTop; // 将头节点作为栈顶 - int stkSize; // 栈的长度 - - public: - LinkedListStack() { - stackTop = nullptr; - stkSize = 0; - } - - ~LinkedListStack() { - // 遍历链表删除节点,释放内存 - freeMemoryLinkedList(stackTop); - } - - /* 获取栈的长度 */ - int size() { - return stkSize; - } - - /* 判断栈是否为空 */ - bool isEmpty() { - return size() == 0; - } - - /* 入栈 */ - void push(int num) { - ListNode *node = new ListNode(num); - node->next = stackTop; - stackTop = node; - stkSize++; - } - - /* 出栈 */ - int pop() { - int num = top(); - ListNode *tmp = stackTop; - stackTop = stackTop->next; - // 释放内存 - delete tmp; - stkSize--; - return num; - } - - /* 访问栈顶元素 */ - int top() { - if (isEmpty()) - throw out_of_range("栈为空"); - return stackTop->val; - } - - /* 将 List 转化为 Array 并返回 */ - vector toVector() { - ListNode *node = stackTop; - vector res(size()); - for (int i = res.size() - 1; i >= 0; i--) { - res[i] = node->val; - node = node->next; - } - return res; - } - }; + [class]{LinkedListStack}-[func]{} ``` === "Java" ```java title="linkedlist_stack.java" - /* 基于链表实现的栈 */ + /* Stack class based on linked list */ class LinkedListStack { - private ListNode stackPeek; // 将头节点作为栈顶 - private int stkSize = 0; // 栈的长度 + private ListNode stackPeek; // Use the head node as the top of the stack + private int stkSize = 0; // Length of the stack public LinkedListStack() { stackPeek = null; } - /* 获取栈的长度 */ + /* Get the length of the stack */ public int size() { return stkSize; } - /* 判断栈是否为空 */ + /* Determine if the stack is empty */ public boolean isEmpty() { return size() == 0; } - /* 入栈 */ + /* Push */ public void push(int num) { ListNode node = new ListNode(num); node.next = stackPeek; @@ -497,7 +434,7 @@ Below is an example code for implementing a stack based on a linked list: stkSize++; } - /* 出栈 */ + /* Pop */ public int pop() { int num = peek(); stackPeek = stackPeek.next; @@ -505,14 +442,14 @@ Below is an example code for implementing a stack based on a linked list: return num; } - /* 访问栈顶元素 */ + /* Access stack top element */ public int peek() { if (isEmpty()) throw new IndexOutOfBoundsException(); return stackPeek.val; } - /* 将 List 转化为 Array 并返回 */ + /* Convert the List to Array and return */ public int[] toArray() { ListNode node = stackPeek; int[] res = new int[size()]; @@ -528,682 +465,69 @@ Below is an example code for implementing a stack based on a linked list: === "C#" ```csharp title="linkedlist_stack.cs" - /* 基于链表实现的栈 */ - class LinkedListStack { - ListNode? stackPeek; // 将头节点作为栈顶 - int stkSize = 0; // 栈的长度 - - public LinkedListStack() { - stackPeek = null; - } - - /* 获取栈的长度 */ - public int Size() { - return stkSize; - } - - /* 判断栈是否为空 */ - public bool IsEmpty() { - return Size() == 0; - } - - /* 入栈 */ - public void Push(int num) { - ListNode node = new(num) { - next = stackPeek - }; - stackPeek = node; - stkSize++; - } - - /* 出栈 */ - public int Pop() { - int num = Peek(); - stackPeek = stackPeek!.next; - stkSize--; - return num; - } - - /* 访问栈顶元素 */ - public int Peek() { - if (IsEmpty()) - throw new Exception(); - return stackPeek!.val; - } - - /* 将 List 转化为 Array 并返回 */ - public int[] ToArray() { - if (stackPeek == null) - return []; - - ListNode? node = stackPeek; - int[] res = new int[Size()]; - for (int i = res.Length - 1; i >= 0; i--) { - res[i] = node!.val; - node = node.next; - } - return res; - } - } + [class]{LinkedListStack}-[func]{} ``` === "Go" ```go title="linkedlist_stack.go" - /* 基于链表实现的栈 */ - type linkedListStack struct { - // 使用内置包 list 来实现栈 - data *list.List - } - - /* 初始化栈 */ - func newLinkedListStack() *linkedListStack { - return &linkedListStack{ - data: list.New(), - } - } - - /* 入栈 */ - func (s *linkedListStack) push(value int) { - s.data.PushBack(value) - } - - /* 出栈 */ - func (s *linkedListStack) pop() any { - if s.isEmpty() { - return nil - } - e := s.data.Back() - s.data.Remove(e) - return e.Value - } - - /* 访问栈顶元素 */ - func (s *linkedListStack) peek() any { - if s.isEmpty() { - return nil - } - e := s.data.Back() - return e.Value - } - - /* 获取栈的长度 */ - func (s *linkedListStack) size() int { - return s.data.Len() - } - - /* 判断栈是否为空 */ - func (s *linkedListStack) isEmpty() bool { - return s.data.Len() == 0 - } - - /* 获取 List 用于打印 */ - func (s *linkedListStack) toList() *list.List { - return s.data - } + [class]{linkedListStack}-[func]{} ``` === "Swift" ```swift title="linkedlist_stack.swift" - /* 基于链表实现的栈 */ - class LinkedListStack { - private var _peek: ListNode? // 将头节点作为栈顶 - private var _size: Int // 栈的长度 - - init() { - _size = 0 - } - - /* 获取栈的长度 */ - func size() -> Int { - _size - } - - /* 判断栈是否为空 */ - func isEmpty() -> Bool { - size() == 0 - } - - /* 入栈 */ - func push(num: Int) { - let node = ListNode(x: num) - node.next = _peek - _peek = node - _size += 1 - } - - /* 出栈 */ - @discardableResult - func pop() -> Int { - let num = peek() - _peek = _peek?.next - _size -= 1 - return num - } - - /* 访问栈顶元素 */ - func peek() -> Int { - if isEmpty() { - fatalError("栈为空") - } - return _peek!.val - } - - /* 将 List 转化为 Array 并返回 */ - func toArray() -> [Int] { - var node = _peek - var res = Array(repeating: 0, count: size()) - for i in res.indices.reversed() { - res[i] = node!.val - node = node?.next - } - return res - } - } + [class]{LinkedListStack}-[func]{} ``` === "JS" ```javascript title="linkedlist_stack.js" - /* 基于链表实现的栈 */ - class LinkedListStack { - #stackPeek; // 将头节点作为栈顶 - #stkSize = 0; // 栈的长度 - - constructor() { - this.#stackPeek = null; - } - - /* 获取栈的长度 */ - get size() { - return this.#stkSize; - } - - /* 判断栈是否为空 */ - isEmpty() { - return this.size === 0; - } - - /* 入栈 */ - push(num) { - const node = new ListNode(num); - node.next = this.#stackPeek; - this.#stackPeek = node; - this.#stkSize++; - } - - /* 出栈 */ - pop() { - const num = this.peek(); - this.#stackPeek = this.#stackPeek.next; - this.#stkSize--; - return num; - } - - /* 访问栈顶元素 */ - peek() { - if (!this.#stackPeek) throw new Error('栈为空'); - return this.#stackPeek.val; - } - - /* 将链表转化为 Array 并返回 */ - toArray() { - let node = this.#stackPeek; - const res = new Array(this.size); - for (let i = res.length - 1; i >= 0; i--) { - res[i] = node.val; - node = node.next; - } - return res; - } - } + [class]{LinkedListStack}-[func]{} ``` === "TS" ```typescript title="linkedlist_stack.ts" - /* 基于链表实现的栈 */ - class LinkedListStack { - private stackPeek: ListNode | null; // 将头节点作为栈顶 - private stkSize: number = 0; // 栈的长度 - - constructor() { - this.stackPeek = null; - } - - /* 获取栈的长度 */ - get size(): number { - return this.stkSize; - } - - /* 判断栈是否为空 */ - isEmpty(): boolean { - return this.size === 0; - } - - /* 入栈 */ - push(num: number): void { - const node = new ListNode(num); - node.next = this.stackPeek; - this.stackPeek = node; - this.stkSize++; - } - - /* 出栈 */ - pop(): number { - const num = this.peek(); - if (!this.stackPeek) throw new Error('栈为空'); - this.stackPeek = this.stackPeek.next; - this.stkSize--; - return num; - } - - /* 访问栈顶元素 */ - peek(): number { - if (!this.stackPeek) throw new Error('栈为空'); - return this.stackPeek.val; - } - - /* 将链表转化为 Array 并返回 */ - toArray(): number[] { - let node = this.stackPeek; - const res = new Array(this.size); - for (let i = res.length - 1; i >= 0; i--) { - res[i] = node!.val; - node = node!.next; - } - return res; - } - } + [class]{LinkedListStack}-[func]{} ``` === "Dart" ```dart title="linkedlist_stack.dart" - /* 基于链表类实现的栈 */ - class LinkedListStack { - ListNode? _stackPeek; // 将头节点作为栈顶 - int _stkSize = 0; // 栈的长度 - - LinkedListStack() { - _stackPeek = null; - } - - /* 获取栈的长度 */ - int size() { - return _stkSize; - } - - /* 判断栈是否为空 */ - bool isEmpty() { - return _stkSize == 0; - } - - /* 入栈 */ - void push(int _num) { - final ListNode node = ListNode(_num); - node.next = _stackPeek; - _stackPeek = node; - _stkSize++; - } - - /* 出栈 */ - int pop() { - final int _num = peek(); - _stackPeek = _stackPeek!.next; - _stkSize--; - return _num; - } - - /* 访问栈顶元素 */ - int peek() { - if (_stackPeek == null) { - throw Exception("栈为空"); - } - return _stackPeek!.val; - } - - /* 将链表转化为 List 并返回 */ - List toList() { - ListNode? node = _stackPeek; - List list = []; - while (node != null) { - list.add(node.val); - node = node.next; - } - list = list.reversed.toList(); - return list; - } - } + [class]{LinkedListStack}-[func]{} ``` === "Rust" ```rust title="linkedlist_stack.rs" - /* 基于链表实现的栈 */ - #[allow(dead_code)] - pub struct LinkedListStack { - stack_peek: Option>>>, // 将头节点作为栈顶 - stk_size: usize, // 栈的长度 - } - - impl LinkedListStack { - pub fn new() -> Self { - Self { - stack_peek: None, - stk_size: 0, - } - } - - /* 获取栈的长度 */ - pub fn size(&self) -> usize { - return self.stk_size; - } - - /* 判断栈是否为空 */ - pub fn is_empty(&self) -> bool { - return self.size() == 0; - } - - /* 入栈 */ - pub fn push(&mut self, num: T) { - let node = ListNode::new(num); - node.borrow_mut().next = self.stack_peek.take(); - self.stack_peek = Some(node); - self.stk_size += 1; - } - - /* 出栈 */ - pub fn pop(&mut self) -> Option { - self.stack_peek.take().map(|old_head| { - match old_head.borrow_mut().next.take() { - Some(new_head) => { - self.stack_peek = Some(new_head); - } - None => { - self.stack_peek = None; - } - } - self.stk_size -= 1; - Rc::try_unwrap(old_head).ok().unwrap().into_inner().val - }) - } - - /* 访问栈顶元素 */ - pub fn peek(&self) -> Option<&Rc>>> { - self.stack_peek.as_ref() - } - - /* 将 List 转化为 Array 并返回 */ - pub fn to_array(&self, head: Option<&Rc>>>) -> Vec { - if let Some(node) = head { - let mut nums = self.to_array(node.borrow().next.as_ref()); - nums.push(node.borrow().val); - return nums; - } - return Vec::new(); - } - } + [class]{LinkedListStack}-[func]{} ``` === "C" ```c title="linkedlist_stack.c" - /* 基于链表实现的栈 */ - typedef struct { - ListNode *top; // 将头节点作为栈顶 - int size; // 栈的长度 - } LinkedListStack; - - /* 构造函数 */ - LinkedListStack *newLinkedListStack() { - LinkedListStack *s = malloc(sizeof(LinkedListStack)); - s->top = NULL; - s->size = 0; - return s; - } - - /* 析构函数 */ - void delLinkedListStack(LinkedListStack *s) { - while (s->top) { - ListNode *n = s->top->next; - free(s->top); - s->top = n; - } - free(s); - } - - /* 获取栈的长度 */ - int size(LinkedListStack *s) { - return s->size; - } - - /* 判断栈是否为空 */ - bool isEmpty(LinkedListStack *s) { - return size(s) == 0; - } - - /* 入栈 */ - void push(LinkedListStack *s, int num) { - ListNode *node = (ListNode *)malloc(sizeof(ListNode)); - node->next = s->top; // 更新新加节点指针域 - node->val = num; // 更新新加节点数据域 - s->top = node; // 更新栈顶 - s->size++; // 更新栈大小 - } - - /* 访问栈顶元素 */ - int peek(LinkedListStack *s) { - if (s->size == 0) { - printf("栈为空\n"); - return INT_MAX; - } - return s->top->val; - } - - /* 出栈 */ - int pop(LinkedListStack *s) { - int val = peek(s); - ListNode *tmp = s->top; - s->top = s->top->next; - // 释放内存 - free(tmp); - s->size--; - return val; - } + [class]{LinkedListStack}-[func]{} ``` === "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?._val - } - - /* 将 List 转化为 Array 并返回 */ - fun toArray(): IntArray { - var node = stackPeek - val res = IntArray(size()) - for (i in res.size - 1 downTo 0) { - res[i] = node?._val!! - node = node.next - } - return res - } - } + [class]{LinkedListStack}-[func]{} ``` === "Ruby" ```ruby title="linkedlist_stack.rb" - ### 基于链表实现的栈 ### - class LinkedListStack - attr_reader :size - - ### 构造方法 ### - def initialize - @size = 0 - end - - ### 判断栈是否为空 ### - def is_empty? - @peek.nil? - end - - ### 入栈 ### - def push(val) - node = ListNode.new(val) - node.next = @peek - @peek = node - @size += 1 - end - - ### 出栈 ### - def pop - num = peek - @peek = @peek.next - @size -= 1 - num - end - - ### 访问栈顶元素 ### - def peek - raise IndexError, '栈为空' if is_empty? - - @peek.val - end - - ### 将链表转化为 Array 并反回 ### - def to_array - arr = [] - node = @peek - while node - arr << node.val - node = node.next - end - arr.reverse - end - end + [class]{LinkedListStack}-[func]{} ``` === "Zig" ```zig title="linkedlist_stack.zig" - // 基于链表实现的栈 - fn LinkedListStack(comptime T: type) type { - return struct { - const Self = @This(); - - stack_top: ?*inc.ListNode(T) = null, // 将头节点作为栈顶 - stk_size: usize = 0, // 栈的长度 - mem_arena: ?std.heap.ArenaAllocator = null, - mem_allocator: std.mem.Allocator = undefined, // 内存分配器 - - // 构造函数(分配内存+初始化栈) - pub fn init(self: *Self, allocator: std.mem.Allocator) !void { - if (self.mem_arena == null) { - self.mem_arena = std.heap.ArenaAllocator.init(allocator); - self.mem_allocator = self.mem_arena.?.allocator(); - } - self.stack_top = null; - self.stk_size = 0; - } - - // 析构函数(释放内存) - pub fn deinit(self: *Self) void { - if (self.mem_arena == null) return; - self.mem_arena.?.deinit(); - } - - // 获取栈的长度 - pub fn size(self: *Self) usize { - return self.stk_size; - } - - // 判断栈是否为空 - pub fn isEmpty(self: *Self) bool { - return self.size() == 0; - } - - // 访问栈顶元素 - pub fn peek(self: *Self) T { - if (self.size() == 0) @panic("栈为空"); - return self.stack_top.?.val; - } - - // 入栈 - pub fn push(self: *Self, num: T) !void { - var node = try self.mem_allocator.create(inc.ListNode(T)); - node.init(num); - node.next = self.stack_top; - self.stack_top = node; - self.stk_size += 1; - } - - // 出栈 - pub fn pop(self: *Self) T { - var num = self.peek(); - self.stack_top = self.stack_top.?.next; - self.stk_size -= 1; - return num; - } - - // 将栈转换为数组 - pub fn toArray(self: *Self) ![]T { - var node = self.stack_top; - var res = try self.mem_allocator.alloc(T, self.size()); - @memset(res, @as(T, 0)); - var i: usize = 0; - while (i < res.len) : (i += 1) { - res[res.len - i - 1] = node.?.val; - node = node.?.next; - } - return res; - } - }; - } + [class]{LinkedListStack}-[func]{} ``` -??? pythontutor "Code Visualization" - -
- - ### 2.   Implementation based on an array When implementing a stack using an array, we can consider the end of the array as the top of the stack. As shown in Figure 5-3, push and pop operations correspond to adding and removing elements at the end of the array, respectively, both with a time complexity of $O(1)$. @@ -1225,128 +549,89 @@ Since the elements to be pushed onto the stack may continuously increase, we can ```python title="array_stack.py" class ArrayStack: - """基于数组实现的栈""" + """Stack class based on array""" def __init__(self): - """构造方法""" + """Constructor""" self._stack: list[int] = [] def size(self) -> int: - """获取栈的长度""" + """Get the length of the stack""" return len(self._stack) def is_empty(self) -> bool: - """判断栈是否为空""" + """Determine if the stack is empty""" return self.size() == 0 def push(self, item: int): - """入栈""" + """Push""" self._stack.append(item) def pop(self) -> int: - """出栈""" + """Pop""" if self.is_empty(): - raise IndexError("栈为空") + raise IndexError("Stack is empty") return self._stack.pop() def peek(self) -> int: - """访问栈顶元素""" + """Access stack top element""" if self.is_empty(): - raise IndexError("栈为空") + raise IndexError("Stack is empty") return self._stack[-1] def to_list(self) -> list[int]: - """返回列表用于打印""" + """Return array for printing""" return self._stack ``` === "C++" ```cpp title="array_stack.cpp" - /* 基于数组实现的栈 */ - class ArrayStack { - private: - vector stack; - - public: - /* 获取栈的长度 */ - int size() { - return stack.size(); - } - - /* 判断栈是否为空 */ - bool isEmpty() { - return stack.size() == 0; - } - - /* 入栈 */ - void push(int num) { - stack.push_back(num); - } - - /* 出栈 */ - int pop() { - int num = top(); - stack.pop_back(); - return num; - } - - /* 访问栈顶元素 */ - int top() { - if (isEmpty()) - throw out_of_range("栈为空"); - return stack.back(); - } - - /* 返回 Vector */ - vector toVector() { - return stack; - } - }; + [class]{ArrayStack}-[func]{} ``` === "Java" ```java title="array_stack.java" - /* 基于数组实现的栈 */ + /* Stack class based on array */ class ArrayStack { private ArrayList stack; public ArrayStack() { - // 初始化列表(动态数组) + // Initialize the list (dynamic array) stack = new ArrayList<>(); } - /* 获取栈的长度 */ + /* Get the length of the stack */ public int size() { return stack.size(); } - /* 判断栈是否为空 */ + /* Determine if the stack is empty */ public boolean isEmpty() { return size() == 0; } - /* 入栈 */ + /* Push */ public void push(int num) { stack.add(num); } - /* 出栈 */ + /* Pop */ public int pop() { if (isEmpty()) throw new IndexOutOfBoundsException(); return stack.remove(size() - 1); } - /* 访问栈顶元素 */ + /* Access stack top element */ public int peek() { if (isEmpty()) throw new IndexOutOfBoundsException(); return stack.get(size() - 1); } - /* 将 List 转化为 Array 并返回 */ + /* Convert the List to Array and return */ public Object[] toArray() { return stack.toArray(); } @@ -1356,554 +641,69 @@ Since the elements to be pushed onto the stack may continuously increase, we can === "C#" ```csharp title="array_stack.cs" - /* 基于数组实现的栈 */ - class ArrayStack { - List stack; - public ArrayStack() { - // 初始化列表(动态数组) - stack = []; - } - - /* 获取栈的长度 */ - public int Size() { - return stack.Count; - } - - /* 判断栈是否为空 */ - public bool IsEmpty() { - return Size() == 0; - } - - /* 入栈 */ - public void Push(int num) { - stack.Add(num); - } - - /* 出栈 */ - public int Pop() { - if (IsEmpty()) - throw new Exception(); - var val = Peek(); - stack.RemoveAt(Size() - 1); - return val; - } - - /* 访问栈顶元素 */ - public int Peek() { - if (IsEmpty()) - throw new Exception(); - return stack[Size() - 1]; - } - - /* 将 List 转化为 Array 并返回 */ - public int[] ToArray() { - return [.. stack]; - } - } + [class]{ArrayStack}-[func]{} ``` === "Go" ```go title="array_stack.go" - /* 基于数组实现的栈 */ - type arrayStack struct { - data []int // 数据 - } - - /* 初始化栈 */ - func newArrayStack() *arrayStack { - return &arrayStack{ - // 设置栈的长度为 0,容量为 16 - data: make([]int, 0, 16), - } - } - - /* 栈的长度 */ - func (s *arrayStack) size() int { - return len(s.data) - } - - /* 栈是否为空 */ - func (s *arrayStack) isEmpty() bool { - return s.size() == 0 - } - - /* 入栈 */ - func (s *arrayStack) push(v int) { - // 切片会自动扩容 - s.data = append(s.data, v) - } - - /* 出栈 */ - func (s *arrayStack) pop() any { - val := s.peek() - s.data = s.data[:len(s.data)-1] - return val - } - - /* 获取栈顶元素 */ - func (s *arrayStack) peek() any { - if s.isEmpty() { - return nil - } - val := s.data[len(s.data)-1] - return val - } - - /* 获取 Slice 用于打印 */ - func (s *arrayStack) toSlice() []int { - return s.data - } + [class]{arrayStack}-[func]{} ``` === "Swift" ```swift title="array_stack.swift" - /* 基于数组实现的栈 */ - class ArrayStack { - private var stack: [Int] - - init() { - // 初始化列表(动态数组) - stack = [] - } - - /* 获取栈的长度 */ - func size() -> Int { - stack.count - } - - /* 判断栈是否为空 */ - func isEmpty() -> Bool { - stack.isEmpty - } - - /* 入栈 */ - func push(num: Int) { - stack.append(num) - } - - /* 出栈 */ - @discardableResult - func pop() -> Int { - if isEmpty() { - fatalError("栈为空") - } - return stack.removeLast() - } - - /* 访问栈顶元素 */ - func peek() -> Int { - if isEmpty() { - fatalError("栈为空") - } - return stack.last! - } - - /* 将 List 转化为 Array 并返回 */ - func toArray() -> [Int] { - stack - } - } + [class]{ArrayStack}-[func]{} ``` === "JS" ```javascript title="array_stack.js" - /* 基于数组实现的栈 */ - class ArrayStack { - #stack; - constructor() { - this.#stack = []; - } - - /* 获取栈的长度 */ - get size() { - return this.#stack.length; - } - - /* 判断栈是否为空 */ - isEmpty() { - return this.#stack.length === 0; - } - - /* 入栈 */ - push(num) { - this.#stack.push(num); - } - - /* 出栈 */ - pop() { - if (this.isEmpty()) throw new Error('栈为空'); - return this.#stack.pop(); - } - - /* 访问栈顶元素 */ - top() { - if (this.isEmpty()) throw new Error('栈为空'); - return this.#stack[this.#stack.length - 1]; - } - - /* 返回 Array */ - toArray() { - return this.#stack; - } - } + [class]{ArrayStack}-[func]{} ``` === "TS" ```typescript title="array_stack.ts" - /* 基于数组实现的栈 */ - class ArrayStack { - private stack: number[]; - constructor() { - this.stack = []; - } - - /* 获取栈的长度 */ - get size(): number { - return this.stack.length; - } - - /* 判断栈是否为空 */ - isEmpty(): boolean { - return this.stack.length === 0; - } - - /* 入栈 */ - push(num: number): void { - this.stack.push(num); - } - - /* 出栈 */ - pop(): number | undefined { - if (this.isEmpty()) throw new Error('栈为空'); - return this.stack.pop(); - } - - /* 访问栈顶元素 */ - top(): number | undefined { - if (this.isEmpty()) throw new Error('栈为空'); - return this.stack[this.stack.length - 1]; - } - - /* 返回 Array */ - toArray() { - return this.stack; - } - } + [class]{ArrayStack}-[func]{} ``` === "Dart" ```dart title="array_stack.dart" - /* 基于数组实现的栈 */ - class ArrayStack { - late List _stack; - ArrayStack() { - _stack = []; - } - - /* 获取栈的长度 */ - int size() { - return _stack.length; - } - - /* 判断栈是否为空 */ - bool isEmpty() { - return _stack.isEmpty; - } - - /* 入栈 */ - void push(int _num) { - _stack.add(_num); - } - - /* 出栈 */ - int pop() { - if (isEmpty()) { - throw Exception("栈为空"); - } - return _stack.removeLast(); - } - - /* 访问栈顶元素 */ - int peek() { - if (isEmpty()) { - throw Exception("栈为空"); - } - return _stack.last; - } - - /* 将栈转化为 Array 并返回 */ - List toArray() => _stack; - } + [class]{ArrayStack}-[func]{} ``` === "Rust" ```rust title="array_stack.rs" - /* 基于数组实现的栈 */ - struct ArrayStack { - stack: Vec, - } - - impl ArrayStack { - /* 初始化栈 */ - fn new() -> ArrayStack { - ArrayStack:: { - stack: Vec::::new(), - } - } - - /* 获取栈的长度 */ - fn size(&self) -> usize { - self.stack.len() - } - - /* 判断栈是否为空 */ - fn is_empty(&self) -> bool { - self.size() == 0 - } - - /* 入栈 */ - fn push(&mut self, num: T) { - self.stack.push(num); - } - - /* 出栈 */ - fn pop(&mut self) -> Option { - self.stack.pop() - } - - /* 访问栈顶元素 */ - fn peek(&self) -> Option<&T> { - if self.is_empty() { - panic!("栈为空") - }; - self.stack.last() - } - - /* 返回 &Vec */ - fn to_array(&self) -> &Vec { - &self.stack - } - } + [class]{ArrayStack}-[func]{} ``` === "C" ```c title="array_stack.c" - /* 基于数组实现的栈 */ - typedef struct { - int *data; - int size; - } ArrayStack; - - /* 构造函数 */ - ArrayStack *newArrayStack() { - ArrayStack *stack = malloc(sizeof(ArrayStack)); - // 初始化一个大容量,避免扩容 - stack->data = malloc(sizeof(int) * MAX_SIZE); - stack->size = 0; - return stack; - } - - /* 析构函数 */ - void delArrayStack(ArrayStack *stack) { - free(stack->data); - free(stack); - } - - /* 获取栈的长度 */ - int size(ArrayStack *stack) { - return stack->size; - } - - /* 判断栈是否为空 */ - bool isEmpty(ArrayStack *stack) { - return stack->size == 0; - } - - /* 入栈 */ - void push(ArrayStack *stack, int num) { - if (stack->size == MAX_SIZE) { - printf("栈已满\n"); - return; - } - stack->data[stack->size] = num; - stack->size++; - } - - /* 访问栈顶元素 */ - int peek(ArrayStack *stack) { - if (stack->size == 0) { - printf("栈为空\n"); - return INT_MAX; - } - return stack->data[stack->size - 1]; - } - - /* 出栈 */ - int pop(ArrayStack *stack) { - int val = peek(stack); - stack->size--; - return val; - } + [class]{ArrayStack}-[func]{} ``` === "Kotlin" ```kotlin title="array_stack.kt" - /* 基于数组实现的栈 */ - class ArrayStack { - // 初始化列表(动态数组) - private val stack = mutableListOf() - - /* 获取栈的长度 */ - 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.toTypedArray() - } - } + [class]{ArrayStack}-[func]{} ``` === "Ruby" ```ruby title="array_stack.rb" - ### 基于数组实现的栈 ### - class ArrayStack - ### 构造方法 ### - def initialize - @stack = [] - end - - ### 获取栈的长度 ### - def size - @stack.length - end - - ### 判断栈是否为空 ### - def is_empty? - @stack.empty? - end - - ### 入栈 ### - def push(item) - @stack << item - end - - ### 出栈 ### - def pop - raise IndexError, '栈为空' if is_empty? - - @stack.pop - end - - ### 访问栈顶元素 ### - def peek - raise IndexError, '栈为空' if is_empty? - - @stack.last - end - - ### 返回列表用于打印 ### - def to_array - @stack - end - end + [class]{ArrayStack}-[func]{} ``` === "Zig" ```zig title="array_stack.zig" - // 基于数组实现的栈 - fn ArrayStack(comptime T: type) type { - return struct { - const Self = @This(); - - stack: ?std.ArrayList(T) = null, - - // 构造方法(分配内存+初始化栈) - pub fn init(self: *Self, allocator: std.mem.Allocator) void { - if (self.stack == null) { - self.stack = std.ArrayList(T).init(allocator); - } - } - - // 析构方法(释放内存) - pub fn deinit(self: *Self) void { - if (self.stack == null) return; - self.stack.?.deinit(); - } - - // 获取栈的长度 - pub fn size(self: *Self) usize { - return self.stack.?.items.len; - } - - // 判断栈是否为空 - pub fn isEmpty(self: *Self) bool { - return self.size() == 0; - } - - // 访问栈顶元素 - pub fn peek(self: *Self) T { - if (self.isEmpty()) @panic("栈为空"); - return self.stack.?.items[self.size() - 1]; - } - - // 入栈 - pub fn push(self: *Self, num: T) !void { - try self.stack.?.append(num); - } - - // 出栈 - pub fn pop(self: *Self) T { - var num = self.stack.?.pop(); - return num; - } - - // 返回 ArrayList - pub fn toList(self: *Self) std.ArrayList(T) { - return self.stack.?; - } - }; - } + [class]{ArrayStack}-[func]{} ``` -??? pythontutor "Code Visualization" - -
- - ## 5.1.3   Comparison of the two implementations **Supported Operations** diff --git a/en/docs/chapter_tree/array_representation_of_tree.md b/en/docs/chapter_tree/array_representation_of_tree.md index 5885ffdbb..8d535eee8 100644 --- a/en/docs/chapter_tree/array_representation_of_tree.md +++ b/en/docs/chapter_tree/array_representation_of_tree.md @@ -161,74 +161,74 @@ The following code implements a binary tree based on array representation, inclu ```python title="array_binary_tree.py" class ArrayBinaryTree: - """数组表示下的二叉树类""" + """Array-based binary tree class""" def __init__(self, arr: list[int | None]): - """构造方法""" + """Constructor""" self._tree = list(arr) def size(self): - """列表容量""" + """List capacity""" return len(self._tree) def val(self, i: int) -> int | None: - """获取索引为 i 节点的值""" - # 若索引越界,则返回 None ,代表空位 + """Get the value of the node at index i""" + # If the index is out of bounds, return None, representing a vacancy if i < 0 or i >= self.size(): return None return self._tree[i] def left(self, i: int) -> int | None: - """获取索引为 i 节点的左子节点的索引""" + """Get the index of the left child of the node at index i""" return 2 * i + 1 def right(self, i: int) -> int | None: - """获取索引为 i 节点的右子节点的索引""" + """Get the index of the right child of the node at index i""" return 2 * i + 2 def parent(self, i: int) -> int | None: - """获取索引为 i 节点的父节点的索引""" + """Get the index of the parent of the node at index i""" return (i - 1) // 2 def level_order(self) -> list[int]: - """层序遍历""" + """Level-order traversal""" self.res = [] - # 直接遍历数组 + # Traverse array for i in range(self.size()): if self.val(i) is not None: self.res.append(self.val(i)) return self.res def dfs(self, i: int, order: str): - """深度优先遍历""" + """Depth-first traversal""" if self.val(i) is None: return - # 前序遍历 + # Pre-order traversal if order == "pre": self.res.append(self.val(i)) self.dfs(self.left(i), order) - # 中序遍历 + # In-order traversal if order == "in": self.res.append(self.val(i)) self.dfs(self.right(i), order) - # 后序遍历 + # Post-order traversal if order == "post": self.res.append(self.val(i)) def pre_order(self) -> list[int]: - """前序遍历""" + """Pre-order traversal""" self.res = [] self.dfs(0, order="pre") return self.res def in_order(self) -> list[int]: - """中序遍历""" + """In-order traversal""" self.res = [] self.dfs(0, order="in") return self.res def post_order(self) -> list[int]: - """后序遍历""" + """Post-order traversal""" self.res = [] self.dfs(0, order="post") return self.res @@ -237,141 +237,53 @@ The following code implements a binary tree based on array representation, inclu === "C++" ```cpp title="array_binary_tree.cpp" - /* 数组表示下的二叉树类 */ - class ArrayBinaryTree { - public: - /* 构造方法 */ - ArrayBinaryTree(vector arr) { - tree = arr; - } - - /* 列表容量 */ - int size() { - return tree.size(); - } - - /* 获取索引为 i 节点的值 */ - int val(int i) { - // 若索引越界,则返回 INT_MAX ,代表空位 - if (i < 0 || i >= size()) - return INT_MAX; - return tree[i]; - } - - /* 获取索引为 i 节点的左子节点的索引 */ - int left(int i) { - return 2 * i + 1; - } - - /* 获取索引为 i 节点的右子节点的索引 */ - int right(int i) { - return 2 * i + 2; - } - - /* 获取索引为 i 节点的父节点的索引 */ - int parent(int i) { - return (i - 1) / 2; - } - - /* 层序遍历 */ - vector levelOrder() { - vector res; - // 直接遍历数组 - for (int i = 0; i < size(); i++) { - if (val(i) != INT_MAX) - res.push_back(val(i)); - } - return res; - } - - /* 前序遍历 */ - vector preOrder() { - vector res; - dfs(0, "pre", res); - return res; - } - - /* 中序遍历 */ - vector inOrder() { - vector res; - dfs(0, "in", res); - return res; - } - - /* 后序遍历 */ - vector postOrder() { - vector res; - dfs(0, "post", res); - return res; - } - - private: - vector tree; - - /* 深度优先遍历 */ - void dfs(int i, string order, vector &res) { - // 若为空位,则返回 - if (val(i) == INT_MAX) - return; - // 前序遍历 - if (order == "pre") - res.push_back(val(i)); - dfs(left(i), order, res); - // 中序遍历 - if (order == "in") - res.push_back(val(i)); - dfs(right(i), order, res); - // 后序遍历 - if (order == "post") - res.push_back(val(i)); - } - }; + [class]{ArrayBinaryTree}-[func]{} ``` === "Java" ```java title="array_binary_tree.java" - /* 数组表示下的二叉树类 */ + /* Array-based binary tree class */ class ArrayBinaryTree { private List tree; - /* 构造方法 */ + /* Constructor */ public ArrayBinaryTree(List arr) { tree = new ArrayList<>(arr); } - /* 列表容量 */ + /* List capacity */ public int size() { return tree.size(); } - /* 获取索引为 i 节点的值 */ + /* Get the value of the node at index i */ public Integer val(int i) { - // 若索引越界,则返回 null ,代表空位 + // If the index is out of bounds, return null, representing an empty spot if (i < 0 || i >= size()) return null; return tree.get(i); } - /* 获取索引为 i 节点的左子节点的索引 */ + /* Get the index of the left child of the node at index i */ public Integer left(int i) { return 2 * i + 1; } - /* 获取索引为 i 节点的右子节点的索引 */ + /* Get the index of the right child of the node at index i */ public Integer right(int i) { return 2 * i + 2; } - /* 获取索引为 i 节点的父节点的索引 */ + /* Get the index of the parent of the node at index i */ public Integer parent(int i) { return (i - 1) / 2; } - /* 层序遍历 */ + /* Level-order traversal */ public List levelOrder() { List res = new ArrayList<>(); - // 直接遍历数组 + // Traverse array for (int i = 0; i < size(); i++) { if (val(i) != null) res.add(val(i)); @@ -379,39 +291,39 @@ The following code implements a binary tree based on array representation, inclu return res; } - /* 深度优先遍历 */ + /* Depth-first traversal */ private void dfs(Integer i, String order, List res) { - // 若为空位,则返回 + // If it is an empty spot, return if (val(i) == null) return; - // 前序遍历 + // Pre-order traversal if ("pre".equals(order)) res.add(val(i)); dfs(left(i), order, res); - // 中序遍历 + // In-order traversal if ("in".equals(order)) res.add(val(i)); dfs(right(i), order, res); - // 后序遍历 + // Post-order traversal if ("post".equals(order)) res.add(val(i)); } - /* 前序遍历 */ + /* Pre-order traversal */ public List preOrder() { List res = new ArrayList<>(); dfs(0, "pre", res); return res; } - /* 中序遍历 */ + /* In-order traversal */ public List inOrder() { List res = new ArrayList<>(); dfs(0, "in", res); return res; } - /* 后序遍历 */ + /* Post-order traversal */ public List postOrder() { List res = new ArrayList<>(); dfs(0, "post", res); @@ -423,920 +335,61 @@ The following code implements a binary tree based on array representation, inclu === "C#" ```csharp title="array_binary_tree.cs" - /* 数组表示下的二叉树类 */ - class ArrayBinaryTree(List arr) { - List tree = new(arr); - - /* 列表容量 */ - public int Size() { - return tree.Count; - } - - /* 获取索引为 i 节点的值 */ - public int? Val(int i) { - // 若索引越界,则返回 null ,代表空位 - if (i < 0 || i >= Size()) - return null; - return tree[i]; - } - - /* 获取索引为 i 节点的左子节点的索引 */ - public int Left(int i) { - return 2 * i + 1; - } - - /* 获取索引为 i 节点的右子节点的索引 */ - public int Right(int i) { - return 2 * i + 2; - } - - /* 获取索引为 i 节点的父节点的索引 */ - public int Parent(int i) { - return (i - 1) / 2; - } - - /* 层序遍历 */ - public List LevelOrder() { - List res = []; - // 直接遍历数组 - for (int i = 0; i < Size(); i++) { - if (Val(i).HasValue) - res.Add(Val(i)!.Value); - } - return res; - } - - /* 深度优先遍历 */ - void DFS(int i, string order, List res) { - // 若为空位,则返回 - if (!Val(i).HasValue) - return; - // 前序遍历 - if (order == "pre") - res.Add(Val(i)!.Value); - DFS(Left(i), order, res); - // 中序遍历 - if (order == "in") - res.Add(Val(i)!.Value); - DFS(Right(i), order, res); - // 后序遍历 - if (order == "post") - res.Add(Val(i)!.Value); - } - - /* 前序遍历 */ - public List PreOrder() { - List res = []; - DFS(0, "pre", res); - return res; - } - - /* 中序遍历 */ - public List InOrder() { - List res = []; - DFS(0, "in", res); - return res; - } - - /* 后序遍历 */ - public List PostOrder() { - List res = []; - DFS(0, "post", res); - return res; - } - } + [class]{ArrayBinaryTree}-[func]{} ``` === "Go" ```go title="array_binary_tree.go" - /* 数组表示下的二叉树类 */ - type arrayBinaryTree struct { - tree []any - } - - /* 构造方法 */ - func newArrayBinaryTree(arr []any) *arrayBinaryTree { - return &arrayBinaryTree{ - tree: arr, - } - } - - /* 列表容量 */ - func (abt *arrayBinaryTree) size() int { - return len(abt.tree) - } - - /* 获取索引为 i 节点的值 */ - func (abt *arrayBinaryTree) val(i int) any { - // 若索引越界,则返回 null ,代表空位 - if i < 0 || i >= abt.size() { - return nil - } - return abt.tree[i] - } - - /* 获取索引为 i 节点的左子节点的索引 */ - func (abt *arrayBinaryTree) left(i int) int { - return 2*i + 1 - } - - /* 获取索引为 i 节点的右子节点的索引 */ - func (abt *arrayBinaryTree) right(i int) int { - return 2*i + 2 - } - - /* 获取索引为 i 节点的父节点的索引 */ - func (abt *arrayBinaryTree) parent(i int) int { - return (i - 1) / 2 - } - - /* 层序遍历 */ - func (abt *arrayBinaryTree) levelOrder() []any { - var res []any - // 直接遍历数组 - for i := 0; i < abt.size(); i++ { - if abt.val(i) != nil { - res = append(res, abt.val(i)) - } - } - return res - } - - /* 深度优先遍历 */ - func (abt *arrayBinaryTree) dfs(i int, order string, res *[]any) { - // 若为空位,则返回 - if abt.val(i) == nil { - return - } - // 前序遍历 - if order == "pre" { - *res = append(*res, abt.val(i)) - } - abt.dfs(abt.left(i), order, res) - // 中序遍历 - if order == "in" { - *res = append(*res, abt.val(i)) - } - abt.dfs(abt.right(i), order, res) - // 后序遍历 - if order == "post" { - *res = append(*res, abt.val(i)) - } - } - - /* 前序遍历 */ - func (abt *arrayBinaryTree) preOrder() []any { - var res []any - abt.dfs(0, "pre", &res) - return res - } - - /* 中序遍历 */ - func (abt *arrayBinaryTree) inOrder() []any { - var res []any - abt.dfs(0, "in", &res) - return res - } - - /* 后序遍历 */ - func (abt *arrayBinaryTree) postOrder() []any { - var res []any - abt.dfs(0, "post", &res) - return res - } + [class]{arrayBinaryTree}-[func]{} ``` === "Swift" ```swift title="array_binary_tree.swift" - /* 数组表示下的二叉树类 */ - class ArrayBinaryTree { - private var tree: [Int?] - - /* 构造方法 */ - init(arr: [Int?]) { - tree = arr - } - - /* 列表容量 */ - func size() -> Int { - tree.count - } - - /* 获取索引为 i 节点的值 */ - func val(i: Int) -> Int? { - // 若索引越界,则返回 null ,代表空位 - if i < 0 || i >= size() { - return nil - } - return tree[i] - } - - /* 获取索引为 i 节点的左子节点的索引 */ - func left(i: Int) -> Int { - 2 * i + 1 - } - - /* 获取索引为 i 节点的右子节点的索引 */ - func right(i: Int) -> Int { - 2 * i + 2 - } - - /* 获取索引为 i 节点的父节点的索引 */ - func parent(i: Int) -> Int { - (i - 1) / 2 - } - - /* 层序遍历 */ - func levelOrder() -> [Int] { - var res: [Int] = [] - // 直接遍历数组 - for i in 0 ..< size() { - if let val = val(i: i) { - res.append(val) - } - } - return res - } - - /* 深度优先遍历 */ - private func dfs(i: Int, order: String, res: inout [Int]) { - // 若为空位,则返回 - guard let val = val(i: i) else { - return - } - // 前序遍历 - if order == "pre" { - res.append(val) - } - dfs(i: left(i: i), order: order, res: &res) - // 中序遍历 - if order == "in" { - res.append(val) - } - dfs(i: right(i: i), order: order, res: &res) - // 后序遍历 - if order == "post" { - res.append(val) - } - } - - /* 前序遍历 */ - func preOrder() -> [Int] { - var res: [Int] = [] - dfs(i: 0, order: "pre", res: &res) - return res - } - - /* 中序遍历 */ - func inOrder() -> [Int] { - var res: [Int] = [] - dfs(i: 0, order: "in", res: &res) - return res - } - - /* 后序遍历 */ - func postOrder() -> [Int] { - var res: [Int] = [] - dfs(i: 0, order: "post", res: &res) - return res - } - } + [class]{ArrayBinaryTree}-[func]{} ``` === "JS" ```javascript title="array_binary_tree.js" - /* 数组表示下的二叉树类 */ - class ArrayBinaryTree { - #tree; - - /* 构造方法 */ - constructor(arr) { - this.#tree = arr; - } - - /* 列表容量 */ - size() { - return this.#tree.length; - } - - /* 获取索引为 i 节点的值 */ - val(i) { - // 若索引越界,则返回 null ,代表空位 - if (i < 0 || i >= this.size()) return null; - return this.#tree[i]; - } - - /* 获取索引为 i 节点的左子节点的索引 */ - left(i) { - return 2 * i + 1; - } - - /* 获取索引为 i 节点的右子节点的索引 */ - right(i) { - return 2 * i + 2; - } - - /* 获取索引为 i 节点的父节点的索引 */ - parent(i) { - return Math.floor((i - 1) / 2); // 向下整除 - } - - /* 层序遍历 */ - levelOrder() { - let res = []; - // 直接遍历数组 - for (let i = 0; i < this.size(); i++) { - if (this.val(i) !== null) res.push(this.val(i)); - } - return res; - } - - /* 深度优先遍历 */ - #dfs(i, order, res) { - // 若为空位,则返回 - if (this.val(i) === null) return; - // 前序遍历 - if (order === 'pre') res.push(this.val(i)); - this.#dfs(this.left(i), order, res); - // 中序遍历 - if (order === 'in') res.push(this.val(i)); - this.#dfs(this.right(i), order, res); - // 后序遍历 - if (order === 'post') res.push(this.val(i)); - } - - /* 前序遍历 */ - preOrder() { - const res = []; - this.#dfs(0, 'pre', res); - return res; - } - - /* 中序遍历 */ - inOrder() { - const res = []; - this.#dfs(0, 'in', res); - return res; - } - - /* 后序遍历 */ - postOrder() { - const res = []; - this.#dfs(0, 'post', res); - return res; - } - } + [class]{ArrayBinaryTree}-[func]{} ``` === "TS" ```typescript title="array_binary_tree.ts" - /* 数组表示下的二叉树类 */ - class ArrayBinaryTree { - #tree: (number | null)[]; - - /* 构造方法 */ - constructor(arr: (number | null)[]) { - this.#tree = arr; - } - - /* 列表容量 */ - size(): number { - return this.#tree.length; - } - - /* 获取索引为 i 节点的值 */ - val(i: number): number | null { - // 若索引越界,则返回 null ,代表空位 - if (i < 0 || i >= this.size()) return null; - return this.#tree[i]; - } - - /* 获取索引为 i 节点的左子节点的索引 */ - left(i: number): number { - return 2 * i + 1; - } - - /* 获取索引为 i 节点的右子节点的索引 */ - right(i: number): number { - return 2 * i + 2; - } - - /* 获取索引为 i 节点的父节点的索引 */ - parent(i: number): number { - return Math.floor((i - 1) / 2); // 向下整除 - } - - /* 层序遍历 */ - levelOrder(): number[] { - let res = []; - // 直接遍历数组 - for (let i = 0; i < this.size(); i++) { - if (this.val(i) !== null) res.push(this.val(i)); - } - return res; - } - - /* 深度优先遍历 */ - #dfs(i: number, order: Order, res: (number | null)[]): void { - // 若为空位,则返回 - if (this.val(i) === null) return; - // 前序遍历 - if (order === 'pre') res.push(this.val(i)); - this.#dfs(this.left(i), order, res); - // 中序遍历 - if (order === 'in') res.push(this.val(i)); - this.#dfs(this.right(i), order, res); - // 后序遍历 - if (order === 'post') res.push(this.val(i)); - } - - /* 前序遍历 */ - preOrder(): (number | null)[] { - const res = []; - this.#dfs(0, 'pre', res); - return res; - } - - /* 中序遍历 */ - inOrder(): (number | null)[] { - const res = []; - this.#dfs(0, 'in', res); - return res; - } - - /* 后序遍历 */ - postOrder(): (number | null)[] { - const res = []; - this.#dfs(0, 'post', res); - return res; - } - } + [class]{ArrayBinaryTree}-[func]{} ``` === "Dart" ```dart title="array_binary_tree.dart" - /* 数组表示下的二叉树类 */ - class ArrayBinaryTree { - late List _tree; - - /* 构造方法 */ - ArrayBinaryTree(this._tree); - - /* 列表容量 */ - int size() { - return _tree.length; - } - - /* 获取索引为 i 节点的值 */ - int? val(int i) { - // 若索引越界,则返回 null ,代表空位 - if (i < 0 || i >= size()) { - return null; - } - return _tree[i]; - } - - /* 获取索引为 i 节点的左子节点的索引 */ - int? left(int i) { - return 2 * i + 1; - } - - /* 获取索引为 i 节点的右子节点的索引 */ - int? right(int i) { - return 2 * i + 2; - } - - /* 获取索引为 i 节点的父节点的索引 */ - int? parent(int i) { - return (i - 1) ~/ 2; - } - - /* 层序遍历 */ - List levelOrder() { - List res = []; - for (int i = 0; i < size(); i++) { - if (val(i) != null) { - res.add(val(i)!); - } - } - return res; - } - - /* 深度优先遍历 */ - void dfs(int i, String order, List res) { - // 若为空位,则返回 - if (val(i) == null) { - return; - } - // 前序遍历 - if (order == 'pre') { - res.add(val(i)); - } - dfs(left(i)!, order, res); - // 中序遍历 - if (order == 'in') { - res.add(val(i)); - } - dfs(right(i)!, order, res); - // 后序遍历 - if (order == 'post') { - res.add(val(i)); - } - } - - /* 前序遍历 */ - List preOrder() { - List res = []; - dfs(0, 'pre', res); - return res; - } - - /* 中序遍历 */ - List inOrder() { - List res = []; - dfs(0, 'in', res); - return res; - } - - /* 后序遍历 */ - List postOrder() { - List res = []; - dfs(0, 'post', res); - return res; - } - } + [class]{ArrayBinaryTree}-[func]{} ``` === "Rust" ```rust title="array_binary_tree.rs" - /* 数组表示下的二叉树类 */ - struct ArrayBinaryTree { - tree: Vec>, - } - - impl ArrayBinaryTree { - /* 构造方法 */ - fn new(arr: Vec>) -> Self { - Self { tree: arr } - } - - /* 列表容量 */ - fn size(&self) -> i32 { - self.tree.len() as i32 - } - - /* 获取索引为 i 节点的值 */ - fn val(&self, i: i32) -> Option { - // 若索引越界,则返回 None ,代表空位 - if i < 0 || i >= self.size() { - None - } else { - self.tree[i as usize] - } - } - - /* 获取索引为 i 节点的左子节点的索引 */ - fn left(&self, i: i32) -> i32 { - 2 * i + 1 - } - - /* 获取索引为 i 节点的右子节点的索引 */ - fn right(&self, i: i32) -> i32 { - 2 * i + 2 - } - - /* 获取索引为 i 节点的父节点的索引 */ - fn parent(&self, i: i32) -> i32 { - (i - 1) / 2 - } - - /* 层序遍历 */ - fn level_order(&self) -> Vec { - let mut res = vec![]; - // 直接遍历数组 - for i in 0..self.size() { - if let Some(val) = self.val(i) { - res.push(val) - } - } - res - } - - /* 深度优先遍历 */ - fn dfs(&self, i: i32, order: &str, res: &mut Vec) { - if self.val(i).is_none() { - return; - } - let val = self.val(i).unwrap(); - // 前序遍历 - if order == "pre" { - res.push(val); - } - self.dfs(self.left(i), order, res); - // 中序遍历 - if order == "in" { - res.push(val); - } - self.dfs(self.right(i), order, res); - // 后序遍历 - if order == "post" { - res.push(val); - } - } - - /* 前序遍历 */ - fn pre_order(&self) -> Vec { - let mut res = vec![]; - self.dfs(0, "pre", &mut res); - res - } - - /* 中序遍历 */ - fn in_order(&self) -> Vec { - let mut res = vec![]; - self.dfs(0, "in", &mut res); - res - } - - /* 后序遍历 */ - fn post_order(&self) -> Vec { - let mut res = vec![]; - self.dfs(0, "post", &mut res); - res - } - } + [class]{ArrayBinaryTree}-[func]{} ``` === "C" ```c title="array_binary_tree.c" - /* 数组表示下的二叉树结构体 */ - typedef struct { - int *tree; - int size; - } ArrayBinaryTree; - - /* 构造函数 */ - ArrayBinaryTree *newArrayBinaryTree(int *arr, int arrSize) { - ArrayBinaryTree *abt = (ArrayBinaryTree *)malloc(sizeof(ArrayBinaryTree)); - abt->tree = malloc(sizeof(int) * arrSize); - memcpy(abt->tree, arr, sizeof(int) * arrSize); - abt->size = arrSize; - return abt; - } - - /* 析构函数 */ - void delArrayBinaryTree(ArrayBinaryTree *abt) { - free(abt->tree); - free(abt); - } - - /* 列表容量 */ - int size(ArrayBinaryTree *abt) { - return abt->size; - } - - /* 获取索引为 i 节点的值 */ - int val(ArrayBinaryTree *abt, int i) { - // 若索引越界,则返回 INT_MAX ,代表空位 - if (i < 0 || i >= size(abt)) - return INT_MAX; - return abt->tree[i]; - } - - /* 层序遍历 */ - int *levelOrder(ArrayBinaryTree *abt, int *returnSize) { - int *res = (int *)malloc(sizeof(int) * size(abt)); - int index = 0; - // 直接遍历数组 - for (int i = 0; i < size(abt); i++) { - if (val(abt, i) != INT_MAX) - res[index++] = val(abt, i); - } - *returnSize = index; - return res; - } - - /* 深度优先遍历 */ - void dfs(ArrayBinaryTree *abt, int i, char *order, int *res, int *index) { - // 若为空位,则返回 - if (val(abt, i) == INT_MAX) - return; - // 前序遍历 - if (strcmp(order, "pre") == 0) - res[(*index)++] = val(abt, i); - dfs(abt, left(i), order, res, index); - // 中序遍历 - if (strcmp(order, "in") == 0) - res[(*index)++] = val(abt, i); - dfs(abt, right(i), order, res, index); - // 后序遍历 - if (strcmp(order, "post") == 0) - res[(*index)++] = val(abt, i); - } - - /* 前序遍历 */ - int *preOrder(ArrayBinaryTree *abt, int *returnSize) { - int *res = (int *)malloc(sizeof(int) * size(abt)); - int index = 0; - dfs(abt, 0, "pre", res, &index); - *returnSize = index; - return res; - } - - /* 中序遍历 */ - int *inOrder(ArrayBinaryTree *abt, int *returnSize) { - int *res = (int *)malloc(sizeof(int) * size(abt)); - int index = 0; - dfs(abt, 0, "in", res, &index); - *returnSize = index; - return res; - } - - /* 后序遍历 */ - int *postOrder(ArrayBinaryTree *abt, int *returnSize) { - int *res = (int *)malloc(sizeof(int) * size(abt)); - int index = 0; - dfs(abt, 0, "post", res, &index); - *returnSize = index; - return res; - } + [class]{ArrayBinaryTree}-[func]{} ``` === "Kotlin" ```kotlin title="array_binary_tree.kt" - /* 数组表示下的二叉树类 */ - class ArrayBinaryTree(val tree: MutableList) { - /* 列表容量 */ - fun size(): Int { - return tree.size - } - - /* 获取索引为 i 节点的值 */ - fun _val(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(): MutableList { - val res = mutableListOf() - // 直接遍历数组 - for (i in 0..) { - // 若为空位,则返回 - if (_val(i) == null) - return - // 前序遍历 - if ("pre" == order) - res.add(_val(i)) - dfs(left(i), order, res) - // 中序遍历 - if ("in" == order) - res.add(_val(i)) - dfs(right(i), order, res) - // 后序遍历 - if ("post" == order) - res.add(_val(i)) - } - - /* 前序遍历 */ - fun preOrder(): MutableList { - val res = mutableListOf() - dfs(0, "pre", res) - return res - } - - /* 中序遍历 */ - fun inOrder(): MutableList { - val res = mutableListOf() - dfs(0, "in", res) - return res - } - - /* 后序遍历 */ - fun postOrder(): MutableList { - val res = mutableListOf() - dfs(0, "post", res) - return res - } - } + [class]{ArrayBinaryTree}-[func]{} ``` === "Ruby" ```ruby title="array_binary_tree.rb" - ### 数组表示下的二叉树类 ### - class ArrayBinaryTree - ### 构造方法 ### - def initialize(arr) - @tree = arr.to_a - end - - ### 列表容量 ### - def size - @tree.length - end - - ### 获取索引为 i 节点的值 ### - def val(i) - # 若索引越界,则返回 nil ,代表空位 - return if i < 0 || i >= size - - @tree[i] - end - - ### 获取索引为 i 节点的左子节点的索引 ### - def left(i) - 2 * i + 1 - end - - ### 获取索引为 i 节点的右子节点的索引 ### - def right(i) - 2 * i + 2 - end - - ### 获取索引为 i 节点的父节点的索引 ### - def parent(i) - (i - 1) / 2 - end - - ### 层序遍历 ### - def level_order - @res = [] - - # 直接遍历数组 - for i in 0...size - @res << val(i) unless val(i).nil? - end - - @res - end - - ### 深度优先遍历 ### - def dfs(i, order) - return if val(i).nil? - # 前序遍历 - @res << val(i) if order == :pre - dfs(left(i), order) - # 中序遍历 - @res << val(i) if order == :in - dfs(right(i), order) - # 后序遍历 - @res << val(i) if order == :post - end - - ### 前序遍历 ### - def pre_order - @res = [] - dfs(0, :pre) - @res - end - - ### 中序遍历 ### - def in_order - @res = [] - dfs(0, :in) - @res - end - - ### 后序遍历 ### - def post_order - @res = [] - dfs(0, :post) - @res - end - end + [class]{ArrayBinaryTree}-[func]{} ``` === "Zig" @@ -1345,11 +398,6 @@ The following code implements a binary tree based on array representation, inclu [class]{ArrayBinaryTree}-[func]{} ``` -??? pythontutor "Code Visualization" - -
- - ## 7.3.3   Advantages and limitations The array representation of binary trees has the following advantages: diff --git a/en/docs/chapter_tree/avl_tree.md b/en/docs/chapter_tree/avl_tree.md index 0a29d2611..3268d226b 100644 --- a/en/docs/chapter_tree/avl_tree.md +++ b/en/docs/chapter_tree/avl_tree.md @@ -237,46 +237,38 @@ The "node height" refers to the distance from that node to its farthest leaf nod ```python title="avl_tree.py" def height(self, node: TreeNode | None) -> int: - """获取节点高度""" - # 空节点高度为 -1 ,叶节点高度为 0 + """Get node height""" + # Empty node height is -1, leaf node height is 0 if node is not None: return node.height return -1 def update_height(self, node: TreeNode | None): - """更新节点高度""" - # 节点高度等于最高子树高度 + 1 + """Update node height""" + # Node height equals the height of the tallest subtree + 1 node.height = max([self.height(node.left), self.height(node.right)]) + 1 ``` === "C++" ```cpp title="avl_tree.cpp" - /* 获取节点高度 */ - int height(TreeNode *node) { - // 空节点高度为 -1 ,叶节点高度为 0 - return node == nullptr ? -1 : node->height; - } + [class]{AVLTree}-[func]{height} - /* 更新节点高度 */ - void updateHeight(TreeNode *node) { - // 节点高度等于最高子树高度 + 1 - node->height = max(height(node->left), height(node->right)) + 1; - } + [class]{AVLTree}-[func]{updateHeight} ``` === "Java" ```java title="avl_tree.java" - /* 获取节点高度 */ + /* Get node height */ int height(TreeNode node) { - // 空节点高度为 -1 ,叶节点高度为 0 + // Empty node height is -1, leaf node height is 0 return node == null ? -1 : node.height; } - /* 更新节点高度 */ + /* Update node height */ void updateHeight(TreeNode node) { - // 节点高度等于最高子树高度 + 1 + // Node height equals the height of the tallest subtree + 1 node.height = Math.max(height(node.left), height(node.right)) + 1; } ``` @@ -284,207 +276,89 @@ The "node height" refers to the distance from that node to its farthest leaf nod === "C#" ```csharp title="avl_tree.cs" - /* 获取节点高度 */ - int Height(TreeNode? node) { - // 空节点高度为 -1 ,叶节点高度为 0 - return node == null ? -1 : node.height; - } + [class]{AVLTree}-[func]{Height} - /* 更新节点高度 */ - void UpdateHeight(TreeNode node) { - // 节点高度等于最高子树高度 + 1 - node.height = Math.Max(Height(node.left), Height(node.right)) + 1; - } + [class]{AVLTree}-[func]{UpdateHeight} ``` === "Go" ```go title="avl_tree.go" - /* 获取节点高度 */ - func (t *aVLTree) height(node *TreeNode) int { - // 空节点高度为 -1 ,叶节点高度为 0 - if node != nil { - return node.Height - } - return -1 - } + [class]{aVLTree}-[func]{height} - /* 更新节点高度 */ - func (t *aVLTree) updateHeight(node *TreeNode) { - lh := t.height(node.Left) - rh := t.height(node.Right) - // 节点高度等于最高子树高度 + 1 - if lh > rh { - node.Height = lh + 1 - } else { - node.Height = rh + 1 - } - } + [class]{aVLTree}-[func]{updateHeight} ``` === "Swift" ```swift title="avl_tree.swift" - /* 获取节点高度 */ - func height(node: TreeNode?) -> Int { - // 空节点高度为 -1 ,叶节点高度为 0 - node?.height ?? -1 - } + [class]{AVLTree}-[func]{height} - /* 更新节点高度 */ - func updateHeight(node: TreeNode?) { - // 节点高度等于最高子树高度 + 1 - node?.height = max(height(node: node?.left), height(node: node?.right)) + 1 - } + [class]{AVLTree}-[func]{updateHeight} ``` === "JS" ```javascript title="avl_tree.js" - /* 获取节点高度 */ - height(node) { - // 空节点高度为 -1 ,叶节点高度为 0 - return node === null ? -1 : node.height; - } + [class]{AVLTree}-[func]{height} - /* 更新节点高度 */ - #updateHeight(node) { - // 节点高度等于最高子树高度 + 1 - node.height = - Math.max(this.height(node.left), this.height(node.right)) + 1; - } + [class]{AVLTree}-[func]{updateHeight} ``` === "TS" ```typescript title="avl_tree.ts" - /* 获取节点高度 */ - height(node: TreeNode): number { - // 空节点高度为 -1 ,叶节点高度为 0 - return node === null ? -1 : node.height; - } + [class]{AVLTree}-[func]{height} - /* 更新节点高度 */ - updateHeight(node: TreeNode): void { - // 节点高度等于最高子树高度 + 1 - node.height = - Math.max(this.height(node.left), this.height(node.right)) + 1; - } + [class]{AVLTree}-[func]{updateHeight} ``` === "Dart" ```dart title="avl_tree.dart" - /* 获取节点高度 */ - int height(TreeNode? node) { - // 空节点高度为 -1 ,叶节点高度为 0 - return node == null ? -1 : node.height; - } + [class]{AVLTree}-[func]{height} - /* 更新节点高度 */ - void updateHeight(TreeNode? node) { - // 节点高度等于最高子树高度 + 1 - node!.height = max(height(node.left), height(node.right)) + 1; - } + [class]{AVLTree}-[func]{updateHeight} ``` === "Rust" ```rust title="avl_tree.rs" - /* 获取节点高度 */ - fn height(node: OptionTreeNodeRc) -> i32 { - // 空节点高度为 -1 ,叶节点高度为 0 - match node { - Some(node) => node.borrow().height, - None => -1, - } - } + [class]{AVLTree}-[func]{height} - /* 更新节点高度 */ - fn update_height(node: OptionTreeNodeRc) { - if let Some(node) = node { - let left = node.borrow().left.clone(); - let right = node.borrow().right.clone(); - // 节点高度等于最高子树高度 + 1 - node.borrow_mut().height = std::cmp::max(Self::height(left), Self::height(right)) + 1; - } - } + [class]{AVLTree}-[func]{update_height} ``` === "C" ```c title="avl_tree.c" - /* 获取节点高度 */ - int height(TreeNode *node) { - // 空节点高度为 -1 ,叶节点高度为 0 - if (node != NULL) { - return node->height; - } - return -1; - } + [class]{}-[func]{height} - /* 更新节点高度 */ - void updateHeight(TreeNode *node) { - int lh = height(node->left); - int rh = height(node->right); - // 节点高度等于最高子树高度 + 1 - if (lh > rh) { - node->height = lh + 1; - } else { - node->height = rh + 1; - } - } + [class]{}-[func]{updateHeight} ``` === "Kotlin" ```kotlin title="avl_tree.kt" - /* 获取节点高度 */ - fun height(node: TreeNode?): Int { - // 空节点高度为 -1 ,叶节点高度为 0 - return node?.height ?: -1 - } + [class]{AVLTree}-[func]{height} - /* 更新节点高度 */ - fun updateHeight(node: TreeNode?) { - // 节点高度等于最高子树高度 + 1 - node?.height = max(height(node?.left), height(node?.right)) + 1 - } + [class]{AVLTree}-[func]{updateHeight} ``` === "Ruby" ```ruby title="avl_tree.rb" - ### 获取节点高度 ### - def height(node) - # 空节点高度为 -1 ,叶节点高度为 0 - return node.height unless node.nil? + [class]{AVLTree}-[func]{height} - -1 - end - - ### 更新节点高度 ### - def update_height(node) - # 节点高度等于最高子树高度 + 1 - node.height = [height(node.left), height(node.right)].max + 1 - end + [class]{AVLTree}-[func]{update_height} ``` === "Zig" ```zig title="avl_tree.zig" - // 获取节点高度 - fn height(self: *Self, node: ?*inc.TreeNode(T)) i32 { - _ = self; - // 空节点高度为 -1 ,叶节点高度为 0 - return if (node == null) -1 else node.?.height; - } + [class]{AVLTree}-[func]{height} - // 更新节点高度 - fn updateHeight(self: *Self, node: ?*inc.TreeNode(T)) void { - // 节点高度等于最高子树高度 + 1 - node.?.height = @max(self.height(node.?.left), self.height(node.?.right)) + 1; - } + [class]{AVLTree}-[func]{updateHeight} ``` ### 2.   Node balance factor @@ -495,36 +369,29 @@ The balance factor of a node is defined as the height of the node's left ```python title="avl_tree.py" def balance_factor(self, node: TreeNode | None) -> int: - """获取平衡因子""" - # 空节点平衡因子为 0 + """Get balance factor""" + # Empty node balance factor is 0 if node is None: return 0 - # 节点平衡因子 = 左子树高度 - 右子树高度 + # Node balance factor = left subtree height - right subtree height return self.height(node.left) - self.height(node.right) ``` === "C++" ```cpp title="avl_tree.cpp" - /* 获取平衡因子 */ - int balanceFactor(TreeNode *node) { - // 空节点平衡因子为 0 - if (node == nullptr) - return 0; - // 节点平衡因子 = 左子树高度 - 右子树高度 - return height(node->left) - height(node->right); - } + [class]{AVLTree}-[func]{balanceFactor} ``` === "Java" ```java title="avl_tree.java" - /* 获取平衡因子 */ + /* Get balance factor */ int balanceFactor(TreeNode node) { - // 空节点平衡因子为 0 + // Empty node balance factor is 0 if (node == null) return 0; - // 节点平衡因子 = 左子树高度 - 右子树高度 + // Node balance factor = left subtree height - right subtree height return height(node.left) - height(node.right); } ``` @@ -532,142 +399,67 @@ The balance factor of a node is defined as the height of the node's left === "C#" ```csharp title="avl_tree.cs" - /* 获取平衡因子 */ - int BalanceFactor(TreeNode? node) { - // 空节点平衡因子为 0 - if (node == null) return 0; - // 节点平衡因子 = 左子树高度 - 右子树高度 - return Height(node.left) - Height(node.right); - } + [class]{AVLTree}-[func]{BalanceFactor} ``` === "Go" ```go title="avl_tree.go" - /* 获取平衡因子 */ - func (t *aVLTree) balanceFactor(node *TreeNode) int { - // 空节点平衡因子为 0 - if node == nil { - return 0 - } - // 节点平衡因子 = 左子树高度 - 右子树高度 - return t.height(node.Left) - t.height(node.Right) - } + [class]{aVLTree}-[func]{balanceFactor} ``` === "Swift" ```swift title="avl_tree.swift" - /* 获取平衡因子 */ - func balanceFactor(node: TreeNode?) -> Int { - // 空节点平衡因子为 0 - guard let node = node else { return 0 } - // 节点平衡因子 = 左子树高度 - 右子树高度 - return height(node: node.left) - height(node: node.right) - } + [class]{AVLTree}-[func]{balanceFactor} ``` === "JS" ```javascript title="avl_tree.js" - /* 获取平衡因子 */ - balanceFactor(node) { - // 空节点平衡因子为 0 - if (node === null) return 0; - // 节点平衡因子 = 左子树高度 - 右子树高度 - return this.height(node.left) - this.height(node.right); - } + [class]{AVLTree}-[func]{balanceFactor} ``` === "TS" ```typescript title="avl_tree.ts" - /* 获取平衡因子 */ - balanceFactor(node: TreeNode): number { - // 空节点平衡因子为 0 - if (node === null) return 0; - // 节点平衡因子 = 左子树高度 - 右子树高度 - return this.height(node.left) - this.height(node.right); - } + [class]{AVLTree}-[func]{balanceFactor} ``` === "Dart" ```dart title="avl_tree.dart" - /* 获取平衡因子 */ - int balanceFactor(TreeNode? node) { - // 空节点平衡因子为 0 - if (node == null) return 0; - // 节点平衡因子 = 左子树高度 - 右子树高度 - return height(node.left) - height(node.right); - } + [class]{AVLTree}-[func]{balanceFactor} ``` === "Rust" ```rust title="avl_tree.rs" - /* 获取平衡因子 */ - fn balance_factor(node: OptionTreeNodeRc) -> i32 { - match node { - // 空节点平衡因子为 0 - None => 0, - // 节点平衡因子 = 左子树高度 - 右子树高度 - Some(node) => { - Self::height(node.borrow().left.clone()) - Self::height(node.borrow().right.clone()) - } - } - } + [class]{AVLTree}-[func]{balance_factor} ``` === "C" ```c title="avl_tree.c" - /* 获取平衡因子 */ - int balanceFactor(TreeNode *node) { - // 空节点平衡因子为 0 - if (node == NULL) { - return 0; - } - // 节点平衡因子 = 左子树高度 - 右子树高度 - return height(node->left) - height(node->right); - } + [class]{}-[func]{balanceFactor} ``` === "Kotlin" ```kotlin title="avl_tree.kt" - /* 获取平衡因子 */ - fun balanceFactor(node: TreeNode?): Int { - // 空节点平衡因子为 0 - if (node == null) return 0 - // 节点平衡因子 = 左子树高度 - 右子树高度 - return height(node.left) - height(node.right) - } + [class]{AVLTree}-[func]{balanceFactor} ``` === "Ruby" ```ruby title="avl_tree.rb" - ### 获取平衡因子 ### - def balance_factor(node) - # 空节点平衡因子为 0 - return 0 if node.nil? - - # 节点平衡因子 = 左子树高度 - 右子树高度 - height(node.left) - height(node.right) - end + [class]{AVLTree}-[func]{balance_factor} ``` === "Zig" ```zig title="avl_tree.zig" - // 获取平衡因子 - fn balanceFactor(self: *Self, node: ?*inc.TreeNode(T)) i32 { - // 空节点平衡因子为 0 - if (node == null) return 0; - // 节点平衡因子 = 左子树高度 - 右子树高度 - return self.height(node.?.left) - self.height(node.?.right); - } + [class]{AVLTree}-[func]{balanceFactor} ``` !!! tip @@ -710,51 +502,39 @@ As shown in Figure 7-27, when the `child` node has a right child (denoted as `gr ```python title="avl_tree.py" def right_rotate(self, node: TreeNode | None) -> TreeNode | None: - """右旋操作""" + """Right rotation operation""" child = node.left grand_child = child.right - # 以 child 为原点,将 node 向右旋转 + # Rotate node to the right around child child.right = node node.left = grand_child - # 更新节点高度 + # Update node height self.update_height(node) self.update_height(child) - # 返回旋转后子树的根节点 + # Return the root of the subtree after rotation return child ``` === "C++" ```cpp title="avl_tree.cpp" - /* 右旋操作 */ - TreeNode *rightRotate(TreeNode *node) { - TreeNode *child = node->left; - TreeNode *grandChild = child->right; - // 以 child 为原点,将 node 向右旋转 - child->right = node; - node->left = grandChild; - // 更新节点高度 - updateHeight(node); - updateHeight(child); - // 返回旋转后子树的根节点 - return child; - } + [class]{AVLTree}-[func]{rightRotate} ``` === "Java" ```java title="avl_tree.java" - /* 右旋操作 */ + /* Right rotation operation */ TreeNode rightRotate(TreeNode node) { TreeNode child = node.left; TreeNode grandChild = child.right; - // 以 child 为原点,将 node 向右旋转 + // Rotate node to the right around child child.right = node; node.left = grandChild; - // 更新节点高度 + // Update node height updateHeight(node); updateHeight(child); - // 返回旋转后子树的根节点 + // Return the root of the subtree after rotation return child; } ``` @@ -762,205 +542,67 @@ As shown in Figure 7-27, when the `child` node has a right child (denoted as `gr === "C#" ```csharp title="avl_tree.cs" - /* 右旋操作 */ - TreeNode? RightRotate(TreeNode? node) { - TreeNode? child = node?.left; - TreeNode? grandChild = child?.right; - // 以 child 为原点,将 node 向右旋转 - child.right = node; - node.left = grandChild; - // 更新节点高度 - UpdateHeight(node); - UpdateHeight(child); - // 返回旋转后子树的根节点 - return child; - } + [class]{AVLTree}-[func]{RightRotate} ``` === "Go" ```go title="avl_tree.go" - /* 右旋操作 */ - func (t *aVLTree) rightRotate(node *TreeNode) *TreeNode { - child := node.Left - grandChild := child.Right - // 以 child 为原点,将 node 向右旋转 - child.Right = node - node.Left = grandChild - // 更新节点高度 - t.updateHeight(node) - t.updateHeight(child) - // 返回旋转后子树的根节点 - return child - } + [class]{aVLTree}-[func]{rightRotate} ``` === "Swift" ```swift title="avl_tree.swift" - /* 右旋操作 */ - func rightRotate(node: TreeNode?) -> TreeNode? { - let child = node?.left - let grandChild = child?.right - // 以 child 为原点,将 node 向右旋转 - child?.right = node - node?.left = grandChild - // 更新节点高度 - updateHeight(node: node) - updateHeight(node: child) - // 返回旋转后子树的根节点 - return child - } + [class]{AVLTree}-[func]{rightRotate} ``` === "JS" ```javascript title="avl_tree.js" - /* 右旋操作 */ - #rightRotate(node) { - const child = node.left; - const grandChild = child.right; - // 以 child 为原点,将 node 向右旋转 - child.right = node; - node.left = grandChild; - // 更新节点高度 - this.#updateHeight(node); - this.#updateHeight(child); - // 返回旋转后子树的根节点 - return child; - } + [class]{AVLTree}-[func]{rightRotate} ``` === "TS" ```typescript title="avl_tree.ts" - /* 右旋操作 */ - rightRotate(node: TreeNode): TreeNode { - const child = node.left; - const grandChild = child.right; - // 以 child 为原点,将 node 向右旋转 - child.right = node; - node.left = grandChild; - // 更新节点高度 - this.updateHeight(node); - this.updateHeight(child); - // 返回旋转后子树的根节点 - return child; - } + [class]{AVLTree}-[func]{rightRotate} ``` === "Dart" ```dart title="avl_tree.dart" - /* 右旋操作 */ - TreeNode? rightRotate(TreeNode? node) { - TreeNode? child = node!.left; - TreeNode? grandChild = child!.right; - // 以 child 为原点,将 node 向右旋转 - child.right = node; - node.left = grandChild; - // 更新节点高度 - updateHeight(node); - updateHeight(child); - // 返回旋转后子树的根节点 - return child; - } + [class]{AVLTree}-[func]{rightRotate} ``` === "Rust" ```rust title="avl_tree.rs" - /* 右旋操作 */ - fn right_rotate(node: OptionTreeNodeRc) -> OptionTreeNodeRc { - match node { - Some(node) => { - let child = node.borrow().left.clone().unwrap(); - let grand_child = child.borrow().right.clone(); - // 以 child 为原点,将 node 向右旋转 - child.borrow_mut().right = Some(node.clone()); - node.borrow_mut().left = grand_child; - // 更新节点高度 - Self::update_height(Some(node)); - Self::update_height(Some(child.clone())); - // 返回旋转后子树的根节点 - Some(child) - } - None => None, - } - } + [class]{AVLTree}-[func]{right_rotate} ``` === "C" ```c title="avl_tree.c" - /* 右旋操作 */ - TreeNode *rightRotate(TreeNode *node) { - TreeNode *child, *grandChild; - child = node->left; - grandChild = child->right; - // 以 child 为原点,将 node 向右旋转 - child->right = node; - node->left = grandChild; - // 更新节点高度 - updateHeight(node); - updateHeight(child); - // 返回旋转后子树的根节点 - return child; - } + [class]{}-[func]{rightRotate} ``` === "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 - } + [class]{AVLTree}-[func]{rightRotate} ``` === "Ruby" ```ruby title="avl_tree.rb" - ### 右旋操作 ### - def right_rotate(node) - child = node.left - grand_child = child.right - # 以 child 为原点,将 node 向右旋转 - child.right = node - node.left = grand_child - # 更新节点高度 - update_height(node) - update_height(child) - # 返回旋转后子树的根节点 - child - end + [class]{AVLTree}-[func]{right_rotate} ``` === "Zig" ```zig title="avl_tree.zig" - // 右旋操作 - fn rightRotate(self: *Self, node: ?*inc.TreeNode(T)) ?*inc.TreeNode(T) { - var child = node.?.left; - var grandChild = child.?.right; - // 以 child 为原点,将 node 向右旋转 - child.?.right = node; - node.?.left = grandChild; - // 更新节点高度 - self.updateHeight(node); - self.updateHeight(child); - // 返回旋转后子树的根节点 - return child; - } + [class]{AVLTree}-[func]{rightRotate} ``` ### 2.   Left rotation @@ -983,51 +625,39 @@ It can be observed that **the right and left rotation operations are logically s ```python title="avl_tree.py" def left_rotate(self, node: TreeNode | None) -> TreeNode | None: - """左旋操作""" + """Left rotation operation""" child = node.right grand_child = child.left - # 以 child 为原点,将 node 向左旋转 + # Rotate node to the left around child child.left = node node.right = grand_child - # 更新节点高度 + # Update node height self.update_height(node) self.update_height(child) - # 返回旋转后子树的根节点 + # Return the root of the subtree after rotation return child ``` === "C++" ```cpp title="avl_tree.cpp" - /* 左旋操作 */ - TreeNode *leftRotate(TreeNode *node) { - TreeNode *child = node->right; - TreeNode *grandChild = child->left; - // 以 child 为原点,将 node 向左旋转 - child->left = node; - node->right = grandChild; - // 更新节点高度 - updateHeight(node); - updateHeight(child); - // 返回旋转后子树的根节点 - return child; - } + [class]{AVLTree}-[func]{leftRotate} ``` === "Java" ```java title="avl_tree.java" - /* 左旋操作 */ + /* Left rotation operation */ TreeNode leftRotate(TreeNode node) { TreeNode child = node.right; TreeNode grandChild = child.left; - // 以 child 为原点,将 node 向左旋转 + // Rotate node to the left around child child.left = node; node.right = grandChild; - // 更新节点高度 + // Update node height updateHeight(node); updateHeight(child); - // 返回旋转后子树的根节点 + // Return the root of the subtree after rotation return child; } ``` @@ -1035,205 +665,67 @@ It can be observed that **the right and left rotation operations are logically s === "C#" ```csharp title="avl_tree.cs" - /* 左旋操作 */ - TreeNode? LeftRotate(TreeNode? node) { - TreeNode? child = node?.right; - TreeNode? grandChild = child?.left; - // 以 child 为原点,将 node 向左旋转 - child.left = node; - node.right = grandChild; - // 更新节点高度 - UpdateHeight(node); - UpdateHeight(child); - // 返回旋转后子树的根节点 - return child; - } + [class]{AVLTree}-[func]{LeftRotate} ``` === "Go" ```go title="avl_tree.go" - /* 左旋操作 */ - func (t *aVLTree) leftRotate(node *TreeNode) *TreeNode { - child := node.Right - grandChild := child.Left - // 以 child 为原点,将 node 向左旋转 - child.Left = node - node.Right = grandChild - // 更新节点高度 - t.updateHeight(node) - t.updateHeight(child) - // 返回旋转后子树的根节点 - return child - } + [class]{aVLTree}-[func]{leftRotate} ``` === "Swift" ```swift title="avl_tree.swift" - /* 左旋操作 */ - func leftRotate(node: TreeNode?) -> TreeNode? { - let child = node?.right - let grandChild = child?.left - // 以 child 为原点,将 node 向左旋转 - child?.left = node - node?.right = grandChild - // 更新节点高度 - updateHeight(node: node) - updateHeight(node: child) - // 返回旋转后子树的根节点 - return child - } + [class]{AVLTree}-[func]{leftRotate} ``` === "JS" ```javascript title="avl_tree.js" - /* 左旋操作 */ - #leftRotate(node) { - const child = node.right; - const grandChild = child.left; - // 以 child 为原点,将 node 向左旋转 - child.left = node; - node.right = grandChild; - // 更新节点高度 - this.#updateHeight(node); - this.#updateHeight(child); - // 返回旋转后子树的根节点 - return child; - } + [class]{AVLTree}-[func]{leftRotate} ``` === "TS" ```typescript title="avl_tree.ts" - /* 左旋操作 */ - leftRotate(node: TreeNode): TreeNode { - const child = node.right; - const grandChild = child.left; - // 以 child 为原点,将 node 向左旋转 - child.left = node; - node.right = grandChild; - // 更新节点高度 - this.updateHeight(node); - this.updateHeight(child); - // 返回旋转后子树的根节点 - return child; - } + [class]{AVLTree}-[func]{leftRotate} ``` === "Dart" ```dart title="avl_tree.dart" - /* 左旋操作 */ - TreeNode? leftRotate(TreeNode? node) { - TreeNode? child = node!.right; - TreeNode? grandChild = child!.left; - // 以 child 为原点,将 node 向左旋转 - child.left = node; - node.right = grandChild; - // 更新节点高度 - updateHeight(node); - updateHeight(child); - // 返回旋转后子树的根节点 - return child; - } + [class]{AVLTree}-[func]{leftRotate} ``` === "Rust" ```rust title="avl_tree.rs" - /* 左旋操作 */ - fn left_rotate(node: OptionTreeNodeRc) -> OptionTreeNodeRc { - match node { - Some(node) => { - let child = node.borrow().right.clone().unwrap(); - let grand_child = child.borrow().left.clone(); - // 以 child 为原点,将 node 向左旋转 - child.borrow_mut().left = Some(node.clone()); - node.borrow_mut().right = grand_child; - // 更新节点高度 - Self::update_height(Some(node)); - Self::update_height(Some(child.clone())); - // 返回旋转后子树的根节点 - Some(child) - } - None => None, - } - } + [class]{AVLTree}-[func]{left_rotate} ``` === "C" ```c title="avl_tree.c" - /* 左旋操作 */ - TreeNode *leftRotate(TreeNode *node) { - TreeNode *child, *grandChild; - child = node->right; - grandChild = child->left; - // 以 child 为原点,将 node 向左旋转 - child->left = node; - node->right = grandChild; - // 更新节点高度 - updateHeight(node); - updateHeight(child); - // 返回旋转后子树的根节点 - return child; - } + [class]{}-[func]{leftRotate} ``` === "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 - } + [class]{AVLTree}-[func]{leftRotate} ``` === "Ruby" ```ruby title="avl_tree.rb" - ### 左旋操作 ### - def left_rotate(node) - child = node.right - grand_child = child.left - # 以 child 为原点,将 node 向左旋转 - child.left = node - node.right = grand_child - # 更新节点高度 - update_height(node) - update_height(child) - # 返回旋转后子树的根节点 - child - end + [class]{AVLTree}-[func]{left_rotate} ``` === "Zig" ```zig title="avl_tree.zig" - // 左旋操作 - fn leftRotate(self: *Self, node: ?*inc.TreeNode(T)) ?*inc.TreeNode(T) { - var child = node.?.right; - var grandChild = child.?.left; - // 以 child 为原点,将 node 向左旋转 - child.?.left = node; - node.?.right = grandChild; - // 更新节点高度 - self.updateHeight(node); - self.updateHeight(child); - // 返回旋转后子树的根节点 - return child; - } + [class]{AVLTree}-[func]{leftRotate} ``` ### 3.   Right-left rotation @@ -1281,95 +773,67 @@ For convenience, we encapsulate the rotation operations into a function. **With ```python title="avl_tree.py" def rotate(self, node: TreeNode | None) -> TreeNode | None: - """执行旋转操作,使该子树重新恢复平衡""" - # 获取节点 node 的平衡因子 + """Perform rotation operation to restore balance to the subtree""" + # Get the balance factor of node balance_factor = self.balance_factor(node) - # 左偏树 + # Left-leaning tree if balance_factor > 1: if self.balance_factor(node.left) >= 0: - # 右旋 + # Right rotation return self.right_rotate(node) else: - # 先左旋后右旋 + # First left rotation then right rotation node.left = self.left_rotate(node.left) return self.right_rotate(node) - # 右偏树 + # Right-leaning tree elif balance_factor < -1: if self.balance_factor(node.right) <= 0: - # 左旋 + # Left rotation return self.left_rotate(node) else: - # 先右旋后左旋 + # First right rotation then left rotation node.right = self.right_rotate(node.right) return self.left_rotate(node) - # 平衡树,无须旋转,直接返回 + # Balanced tree, no rotation needed, return return node ``` === "C++" ```cpp title="avl_tree.cpp" - /* 执行旋转操作,使该子树重新恢复平衡 */ - TreeNode *rotate(TreeNode *node) { - // 获取节点 node 的平衡因子 - int _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; - } + [class]{AVLTree}-[func]{rotate} ``` === "Java" ```java title="avl_tree.java" - /* 执行旋转操作,使该子树重新恢复平衡 */ + /* Perform rotation operation to restore balance to the subtree */ TreeNode rotate(TreeNode node) { - // 获取节点 node 的平衡因子 + // Get the balance factor of node int balanceFactor = balanceFactor(node); - // 左偏树 + // Left-leaning tree if (balanceFactor > 1) { if (balanceFactor(node.left) >= 0) { - // 右旋 + // Right rotation return rightRotate(node); } else { - // 先左旋后右旋 + // First left rotation then right rotation node.left = leftRotate(node.left); return rightRotate(node); } } - // 右偏树 + // Right-leaning tree if (balanceFactor < -1) { if (balanceFactor(node.right) <= 0) { - // 左旋 + // Left rotation return leftRotate(node); } else { - // 先右旋后左旋 + // First right rotation then left rotation node.right = rightRotate(node.right); return leftRotate(node); } } - // 平衡树,无须旋转,直接返回 + // Balanced tree, no rotation needed, return return node; } ``` @@ -1377,380 +841,67 @@ For convenience, we encapsulate the rotation operations into a function. **With === "C#" ```csharp title="avl_tree.cs" - /* 执行旋转操作,使该子树重新恢复平衡 */ - TreeNode? Rotate(TreeNode? node) { - // 获取节点 node 的平衡因子 - int balanceFactorInt = BalanceFactor(node); - // 左偏树 - if (balanceFactorInt > 1) { - if (BalanceFactor(node?.left) >= 0) { - // 右旋 - return RightRotate(node); - } else { - // 先左旋后右旋 - node!.left = LeftRotate(node!.left); - return RightRotate(node); - } - } - // 右偏树 - if (balanceFactorInt < -1) { - if (BalanceFactor(node?.right) <= 0) { - // 左旋 - return LeftRotate(node); - } else { - // 先右旋后左旋 - node!.right = RightRotate(node!.right); - return LeftRotate(node); - } - } - // 平衡树,无须旋转,直接返回 - return node; - } + [class]{AVLTree}-[func]{Rotate} ``` === "Go" ```go title="avl_tree.go" - /* 执行旋转操作,使该子树重新恢复平衡 */ - func (t *aVLTree) rotate(node *TreeNode) *TreeNode { - // 获取节点 node 的平衡因子 - // Go 推荐短变量,这里 bf 指代 t.balanceFactor - bf := t.balanceFactor(node) - // 左偏树 - if bf > 1 { - if t.balanceFactor(node.Left) >= 0 { - // 右旋 - return t.rightRotate(node) - } else { - // 先左旋后右旋 - node.Left = t.leftRotate(node.Left) - return t.rightRotate(node) - } - } - // 右偏树 - if bf < -1 { - if t.balanceFactor(node.Right) <= 0 { - // 左旋 - return t.leftRotate(node) - } else { - // 先右旋后左旋 - node.Right = t.rightRotate(node.Right) - return t.leftRotate(node) - } - } - // 平衡树,无须旋转,直接返回 - return node - } + [class]{aVLTree}-[func]{rotate} ``` === "Swift" ```swift title="avl_tree.swift" - /* 执行旋转操作,使该子树重新恢复平衡 */ - func rotate(node: TreeNode?) -> TreeNode? { - // 获取节点 node 的平衡因子 - let balanceFactor = balanceFactor(node: node) - // 左偏树 - if balanceFactor > 1 { - if self.balanceFactor(node: node?.left) >= 0 { - // 右旋 - return rightRotate(node: node) - } else { - // 先左旋后右旋 - node?.left = leftRotate(node: node?.left) - return rightRotate(node: node) - } - } - // 右偏树 - if balanceFactor < -1 { - if self.balanceFactor(node: node?.right) <= 0 { - // 左旋 - return leftRotate(node: node) - } else { - // 先右旋后左旋 - node?.right = rightRotate(node: node?.right) - return leftRotate(node: node) - } - } - // 平衡树,无须旋转,直接返回 - return node - } + [class]{AVLTree}-[func]{rotate} ``` === "JS" ```javascript title="avl_tree.js" - /* 执行旋转操作,使该子树重新恢复平衡 */ - #rotate(node) { - // 获取节点 node 的平衡因子 - const balanceFactor = this.balanceFactor(node); - // 左偏树 - if (balanceFactor > 1) { - if (this.balanceFactor(node.left) >= 0) { - // 右旋 - return this.#rightRotate(node); - } else { - // 先左旋后右旋 - node.left = this.#leftRotate(node.left); - return this.#rightRotate(node); - } - } - // 右偏树 - if (balanceFactor < -1) { - if (this.balanceFactor(node.right) <= 0) { - // 左旋 - return this.#leftRotate(node); - } else { - // 先右旋后左旋 - node.right = this.#rightRotate(node.right); - return this.#leftRotate(node); - } - } - // 平衡树,无须旋转,直接返回 - return node; - } + [class]{AVLTree}-[func]{rotate} ``` === "TS" ```typescript title="avl_tree.ts" - /* 执行旋转操作,使该子树重新恢复平衡 */ - rotate(node: TreeNode): TreeNode { - // 获取节点 node 的平衡因子 - const balanceFactor = this.balanceFactor(node); - // 左偏树 - if (balanceFactor > 1) { - if (this.balanceFactor(node.left) >= 0) { - // 右旋 - return this.rightRotate(node); - } else { - // 先左旋后右旋 - node.left = this.leftRotate(node.left); - return this.rightRotate(node); - } - } - // 右偏树 - if (balanceFactor < -1) { - if (this.balanceFactor(node.right) <= 0) { - // 左旋 - return this.leftRotate(node); - } else { - // 先右旋后左旋 - node.right = this.rightRotate(node.right); - return this.leftRotate(node); - } - } - // 平衡树,无须旋转,直接返回 - return node; - } + [class]{AVLTree}-[func]{rotate} ``` === "Dart" ```dart title="avl_tree.dart" - /* 执行旋转操作,使该子树重新恢复平衡 */ - TreeNode? rotate(TreeNode? node) { - // 获取节点 node 的平衡因子 - int factor = balanceFactor(node); - // 左偏树 - if (factor > 1) { - if (balanceFactor(node!.left) >= 0) { - // 右旋 - return rightRotate(node); - } else { - // 先左旋后右旋 - node.left = leftRotate(node.left); - return rightRotate(node); - } - } - // 右偏树 - if (factor < -1) { - if (balanceFactor(node!.right) <= 0) { - // 左旋 - return leftRotate(node); - } else { - // 先右旋后左旋 - node.right = rightRotate(node.right); - return leftRotate(node); - } - } - // 平衡树,无须旋转,直接返回 - return node; - } + [class]{AVLTree}-[func]{rotate} ``` === "Rust" ```rust title="avl_tree.rs" - /* 执行旋转操作,使该子树重新恢复平衡 */ - fn rotate(node: OptionTreeNodeRc) -> OptionTreeNodeRc { - // 获取节点 node 的平衡因子 - let balance_factor = Self::balance_factor(node.clone()); - // 左偏树 - if balance_factor > 1 { - let node = node.unwrap(); - if Self::balance_factor(node.borrow().left.clone()) >= 0 { - // 右旋 - Self::right_rotate(Some(node)) - } else { - // 先左旋后右旋 - let left = node.borrow().left.clone(); - node.borrow_mut().left = Self::left_rotate(left); - Self::right_rotate(Some(node)) - } - } - // 右偏树 - else if balance_factor < -1 { - let node = node.unwrap(); - if Self::balance_factor(node.borrow().right.clone()) <= 0 { - // 左旋 - Self::left_rotate(Some(node)) - } else { - // 先右旋后左旋 - let right = node.borrow().right.clone(); - node.borrow_mut().right = Self::right_rotate(right); - Self::left_rotate(Some(node)) - } - } else { - // 平衡树,无须旋转,直接返回 - node - } - } + [class]{AVLTree}-[func]{rotate} ``` === "C" ```c title="avl_tree.c" - /* 执行旋转操作,使该子树重新恢复平衡 */ - TreeNode *rotate(TreeNode *node) { - // 获取节点 node 的平衡因子 - int bf = balanceFactor(node); - // 左偏树 - if (bf > 1) { - if (balanceFactor(node->left) >= 0) { - // 右旋 - return rightRotate(node); - } else { - // 先左旋后右旋 - node->left = leftRotate(node->left); - return rightRotate(node); - } - } - // 右偏树 - if (bf < -1) { - if (balanceFactor(node->right) <= 0) { - // 左旋 - return leftRotate(node); - } else { - // 先右旋后左旋 - node->right = rightRotate(node->right); - return leftRotate(node); - } - } - // 平衡树,无须旋转,直接返回 - return node; - } + [class]{}-[func]{rotate} ``` === "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 - } + [class]{AVLTree}-[func]{rotate} ``` === "Ruby" ```ruby title="avl_tree.rb" - ### 执行旋转操作,使该子树重新恢复平衡 ### - def rotate(node) - # 获取节点 node 的平衡因子 - balance_factor = balance_factor(node) - # 左遍树 - if balance_factor > 1 - if balance_factor(node.left) >= 0 - # 右旋 - return right_rotate(node) - else - # 先左旋后右旋 - node.left = left_rotate(node.left) - return right_rotate(node) - end - # 右遍树 - elsif balance_factor < -1 - if balance_factor(node.right) <= 0 - # 左旋 - return left_rotate(node) - else - # 先右旋后左旋 - node.right = right_rotate(node.right) - return left_rotate(node) - end - end - # 平衡树,无须旋转,直接返回 - node - end + [class]{AVLTree}-[func]{rotate} ``` === "Zig" ```zig title="avl_tree.zig" - // 执行旋转操作,使该子树重新恢复平衡 - fn rotate(self: *Self, node: ?*inc.TreeNode(T)) ?*inc.TreeNode(T) { - // 获取节点 node 的平衡因子 - var balance_factor = self.balanceFactor(node); - // 左偏树 - if (balance_factor > 1) { - if (self.balanceFactor(node.?.left) >= 0) { - // 右旋 - return self.rightRotate(node); - } else { - // 先左旋后右旋 - node.?.left = self.leftRotate(node.?.left); - return self.rightRotate(node); - } - } - // 右偏树 - if (balance_factor < -1) { - if (self.balanceFactor(node.?.right) <= 0) { - // 左旋 - return self.leftRotate(node); - } else { - // 先右旋后左旋 - node.?.right = self.rightRotate(node.?.right); - return self.leftRotate(node); - } - } - // 平衡树,无须旋转,直接返回 - return node; - } + [class]{AVLTree}-[func]{rotate} ``` ## 7.5.3   Common operations in AVL trees @@ -1763,77 +914,58 @@ The node insertion operation in AVL trees is similar to that in binary search tr ```python title="avl_tree.py" def insert(self, val): - """插入节点""" + """Insert node""" self._root = self.insert_helper(self._root, val) def insert_helper(self, node: TreeNode | None, val: int) -> TreeNode: - """递归插入节点(辅助方法)""" + """Recursively insert node (helper method)""" if node is None: return TreeNode(val) - # 1. 查找插入位置并插入节点 + # 1. Find insertion position and insert node if val < node.val: node.left = self.insert_helper(node.left, val) elif val > node.val: node.right = self.insert_helper(node.right, val) else: - # 重复节点不插入,直接返回 + # Do not insert duplicate nodes, return return node - # 更新节点高度 + # Update node height self.update_height(node) - # 2. 执行旋转操作,使该子树重新恢复平衡 + # 2. Perform rotation operation to restore balance to the subtree return self.rotate(node) ``` === "C++" ```cpp title="avl_tree.cpp" - /* 插入节点 */ - void insert(int val) { - root = insertHelper(root, val); - } + [class]{AVLTree}-[func]{insert} - /* 递归插入节点(辅助方法) */ - TreeNode *insertHelper(TreeNode *node, int val) { - if (node == nullptr) - return new TreeNode(val); - /* 1. 查找插入位置并插入节点 */ - if (val < node->val) - node->left = insertHelper(node->left, val); - else if (val > node->val) - node->right = insertHelper(node->right, val); - else - return node; // 重复节点不插入,直接返回 - updateHeight(node); // 更新节点高度 - /* 2. 执行旋转操作,使该子树重新恢复平衡 */ - node = rotate(node); - // 返回子树的根节点 - return node; - } + [class]{AVLTree}-[func]{insertHelper} ``` === "Java" ```java title="avl_tree.java" - /* 插入节点 */ + /* Insert node */ void insert(int val) { root = insertHelper(root, val); } - /* 递归插入节点(辅助方法) */ + /* Recursively insert node (helper method) */ TreeNode insertHelper(TreeNode node, int val) { if (node == null) return new TreeNode(val); - /* 1. 查找插入位置并插入节点 */ + /* 1. Find insertion position and insert node */ if (val < node.val) node.left = insertHelper(node.left, val); else if (val > node.val) node.right = insertHelper(node.right, val); else - return node; // 重复节点不插入,直接返回 - updateHeight(node); // 更新节点高度 - /* 2. 执行旋转操作,使该子树重新恢复平衡 */ + return node; // Do not insert duplicate nodes, return + updateHeight(node); // Update node height + /* 2. Perform rotation operation to restore balance to the subtree */ node = rotate(node); - // 返回子树的根节点 + // Return the root node of the subtree return node; } ``` @@ -1841,326 +973,89 @@ The node insertion operation in AVL trees is similar to that in binary search tr === "C#" ```csharp title="avl_tree.cs" - /* 插入节点 */ - void Insert(int val) { - root = InsertHelper(root, val); - } + [class]{AVLTree}-[func]{Insert} - /* 递归插入节点(辅助方法) */ - TreeNode? InsertHelper(TreeNode? node, int val) { - if (node == null) return new TreeNode(val); - /* 1. 查找插入位置并插入节点 */ - if (val < node.val) - node.left = InsertHelper(node.left, val); - else if (val > node.val) - node.right = InsertHelper(node.right, val); - else - return node; // 重复节点不插入,直接返回 - UpdateHeight(node); // 更新节点高度 - /* 2. 执行旋转操作,使该子树重新恢复平衡 */ - node = Rotate(node); - // 返回子树的根节点 - return node; - } + [class]{AVLTree}-[func]{InsertHelper} ``` === "Go" ```go title="avl_tree.go" - /* 插入节点 */ - func (t *aVLTree) insert(val int) { - t.root = t.insertHelper(t.root, val) - } + [class]{aVLTree}-[func]{insert} - /* 递归插入节点(辅助函数) */ - func (t *aVLTree) insertHelper(node *TreeNode, val int) *TreeNode { - if node == nil { - return NewTreeNode(val) - } - /* 1. 查找插入位置并插入节点 */ - if val < node.Val.(int) { - node.Left = t.insertHelper(node.Left, val) - } else if val > node.Val.(int) { - node.Right = t.insertHelper(node.Right, val) - } else { - // 重复节点不插入,直接返回 - return node - } - // 更新节点高度 - t.updateHeight(node) - /* 2. 执行旋转操作,使该子树重新恢复平衡 */ - node = t.rotate(node) - // 返回子树的根节点 - return node - } + [class]{aVLTree}-[func]{insertHelper} ``` === "Swift" ```swift title="avl_tree.swift" - /* 插入节点 */ - func insert(val: Int) { - root = insertHelper(node: root, val: val) - } + [class]{AVLTree}-[func]{insert} - /* 递归插入节点(辅助方法) */ - func insertHelper(node: TreeNode?, val: Int) -> TreeNode? { - var node = node - if node == nil { - return TreeNode(x: val) - } - /* 1. 查找插入位置并插入节点 */ - if val < node!.val { - node?.left = insertHelper(node: node?.left, val: val) - } else if val > node!.val { - node?.right = insertHelper(node: node?.right, val: val) - } else { - return node // 重复节点不插入,直接返回 - } - updateHeight(node: node) // 更新节点高度 - /* 2. 执行旋转操作,使该子树重新恢复平衡 */ - node = rotate(node: node) - // 返回子树的根节点 - return node - } + [class]{AVLTree}-[func]{insertHelper} ``` === "JS" ```javascript title="avl_tree.js" - /* 插入节点 */ - insert(val) { - this.root = this.#insertHelper(this.root, val); - } + [class]{AVLTree}-[func]{insert} - /* 递归插入节点(辅助方法) */ - #insertHelper(node, val) { - if (node === null) return new TreeNode(val); - /* 1. 查找插入位置并插入节点 */ - if (val < node.val) node.left = this.#insertHelper(node.left, val); - else if (val > node.val) - node.right = this.#insertHelper(node.right, val); - else return node; // 重复节点不插入,直接返回 - this.#updateHeight(node); // 更新节点高度 - /* 2. 执行旋转操作,使该子树重新恢复平衡 */ - node = this.#rotate(node); - // 返回子树的根节点 - return node; - } + [class]{AVLTree}-[func]{insertHelper} ``` === "TS" ```typescript title="avl_tree.ts" - /* 插入节点 */ - insert(val: number): void { - this.root = this.insertHelper(this.root, val); - } + [class]{AVLTree}-[func]{insert} - /* 递归插入节点(辅助方法) */ - insertHelper(node: TreeNode, val: number): TreeNode { - if (node === null) return new TreeNode(val); - /* 1. 查找插入位置并插入节点 */ - if (val < node.val) { - node.left = this.insertHelper(node.left, val); - } else if (val > node.val) { - node.right = this.insertHelper(node.right, val); - } else { - return node; // 重复节点不插入,直接返回 - } - this.updateHeight(node); // 更新节点高度 - /* 2. 执行旋转操作,使该子树重新恢复平衡 */ - node = this.rotate(node); - // 返回子树的根节点 - return node; - } + [class]{AVLTree}-[func]{insertHelper} ``` === "Dart" ```dart title="avl_tree.dart" - /* 插入节点 */ - void insert(int val) { - root = insertHelper(root, val); - } + [class]{AVLTree}-[func]{insert} - /* 递归插入节点(辅助方法) */ - TreeNode? insertHelper(TreeNode? node, int val) { - if (node == null) return TreeNode(val); - /* 1. 查找插入位置并插入节点 */ - if (val < node.val) - node.left = insertHelper(node.left, val); - else if (val > node.val) - node.right = insertHelper(node.right, val); - else - return node; // 重复节点不插入,直接返回 - updateHeight(node); // 更新节点高度 - /* 2. 执行旋转操作,使该子树重新恢复平衡 */ - node = rotate(node); - // 返回子树的根节点 - return node; - } + [class]{AVLTree}-[func]{insertHelper} ``` === "Rust" ```rust title="avl_tree.rs" - /* 插入节点 */ - fn insert(&mut self, val: i32) { - self.root = Self::insert_helper(self.root.clone(), val); - } + [class]{AVLTree}-[func]{insert} - /* 递归插入节点(辅助方法) */ - fn insert_helper(node: OptionTreeNodeRc, val: i32) -> OptionTreeNodeRc { - match node { - Some(mut node) => { - /* 1. 查找插入位置并插入节点 */ - match { - let node_val = node.borrow().val; - node_val - } - .cmp(&val) - { - Ordering::Greater => { - let left = node.borrow().left.clone(); - node.borrow_mut().left = Self::insert_helper(left, val); - } - Ordering::Less => { - let right = node.borrow().right.clone(); - node.borrow_mut().right = Self::insert_helper(right, val); - } - Ordering::Equal => { - return Some(node); // 重复节点不插入,直接返回 - } - } - Self::update_height(Some(node.clone())); // 更新节点高度 - - /* 2. 执行旋转操作,使该子树重新恢复平衡 */ - node = Self::rotate(Some(node)).unwrap(); - // 返回子树的根节点 - Some(node) - } - None => Some(TreeNode::new(val)), - } - } + [class]{AVLTree}-[func]{insert_helper} ``` === "C" ```c title="avl_tree.c" - /* 插入节点 */ - void insert(AVLTree *tree, int val) { - tree->root = insertHelper(tree->root, val); - } + [class]{AVLTree}-[func]{insert} - /* 递归插入节点(辅助函数) */ - TreeNode *insertHelper(TreeNode *node, int val) { - if (node == NULL) { - return newTreeNode(val); - } - /* 1. 查找插入位置并插入节点 */ - if (val < node->val) { - node->left = insertHelper(node->left, val); - } else if (val > node->val) { - node->right = insertHelper(node->right, val); - } else { - // 重复节点不插入,直接返回 - return node; - } - // 更新节点高度 - updateHeight(node); - /* 2. 执行旋转操作,使该子树重新恢复平衡 */ - node = rotate(node); - // 返回子树的根节点 - return node; - } + [class]{}-[func]{insertHelper} ``` === "Kotlin" ```kotlin title="avl_tree.kt" - /* 插入节点 */ - fun insert(_val: Int) { - root = insertHelper(root, _val) - } + [class]{AVLTree}-[func]{insert} - /* 递归插入节点(辅助方法) */ - fun insertHelper(n: TreeNode?, _val: Int): TreeNode { - if (n == null) - return TreeNode(_val) - var node = n - /* 1. 查找插入位置并插入节点 */ - if (_val < node._val) - node.left = insertHelper(node.left, _val) - else if (_val > node._val) - node.right = insertHelper(node.right, _val) - else - return node // 重复节点不插入,直接返回 - updateHeight(node) // 更新节点高度 - /* 2. 执行旋转操作,使该子树重新恢复平衡 */ - node = rotate(node) - // 返回子树的根节点 - return node - } + [class]{AVLTree}-[func]{insertHelper} ``` === "Ruby" ```ruby title="avl_tree.rb" - ### 插入节点 ### - def insert(val) - @root = insert_helper(@root, val) - end + [class]{AVLTree}-[func]{insert} - ### 递归插入节点(辅助方法)### - def insert_helper(node, val) - return TreeNode.new(val) if node.nil? - # 1. 查找插入位置并插入节点 - if val < node.val - node.left = insert_helper(node.left, val) - elsif val > node.val - node.right = insert_helper(node.right, val) - else - # 重复节点不插入,直接返回 - return node - end - # 更新节点高度 - update_height(node) - # 2. 执行旋转操作,使该子树重新恢复平衡 - rotate(node) - end + [class]{AVLTree}-[func]{insert_helper} ``` === "Zig" ```zig title="avl_tree.zig" - // 插入节点 - fn insert(self: *Self, val: T) !void { - self.root = (try self.insertHelper(self.root, val)).?; - } + [class]{AVLTree}-[func]{insert} - // 递归插入节点(辅助方法) - fn insertHelper(self: *Self, node_: ?*inc.TreeNode(T), val: T) !?*inc.TreeNode(T) { - var node = node_; - if (node == null) { - var tmp_node = try self.mem_allocator.create(inc.TreeNode(T)); - tmp_node.init(val); - return tmp_node; - } - // 1. 查找插入位置并插入节点 - if (val < node.?.val) { - node.?.left = try self.insertHelper(node.?.left, val); - } else if (val > node.?.val) { - node.?.right = try self.insertHelper(node.?.right, val); - } else { - return node; // 重复节点不插入,直接返回 - } - self.updateHeight(node); // 更新节点高度 - // 2. 执行旋转操作,使该子树重新恢复平衡 - node = self.rotate(node); - // 返回子树的根节点 - return node; - } + [class]{AVLTree}-[func]{insertHelper} ``` ### 2.   Node removal @@ -2171,14 +1066,14 @@ Similarly, based on the method of removing nodes in binary search trees, rotatio ```python title="avl_tree.py" def remove(self, val: int): - """删除节点""" + """Remove node""" self._root = self.remove_helper(self._root, val) def remove_helper(self, node: TreeNode | None, val: int) -> TreeNode | None: - """递归删除节点(辅助方法)""" + """Recursively remove node (helper method)""" if node is None: return None - # 1. 查找节点并删除 + # 1. Find and remove the node if val < node.val: node.left = self.remove_helper(node.left, val) elif val > node.val: @@ -2186,87 +1081,46 @@ Similarly, based on the method of removing nodes in binary search trees, rotatio else: if node.left is None or node.right is None: child = node.left or node.right - # 子节点数量 = 0 ,直接删除 node 并返回 + # Number of child nodes = 0, remove node and return if child is None: return None - # 子节点数量 = 1 ,直接删除 node + # Number of child nodes = 1, remove node else: node = child else: - # 子节点数量 = 2 ,则将中序遍历的下个节点删除,并用该节点替换当前节点 + # Number of child nodes = 2, remove the next node in in-order traversal and replace the current node with it temp = node.right while temp.left is not None: temp = temp.left node.right = self.remove_helper(node.right, temp.val) node.val = temp.val - # 更新节点高度 + # Update node height self.update_height(node) - # 2. 执行旋转操作,使该子树重新恢复平衡 + # 2. Perform rotation operation to restore balance to the subtree return self.rotate(node) ``` === "C++" ```cpp title="avl_tree.cpp" - /* 删除节点 */ - void remove(int val) { - root = removeHelper(root, val); - } + [class]{AVLTree}-[func]{remove} - /* 递归删除节点(辅助方法) */ - TreeNode *removeHelper(TreeNode *node, int val) { - if (node == nullptr) - return nullptr; - /* 1. 查找节点并删除 */ - if (val < node->val) - node->left = removeHelper(node->left, val); - else if (val > node->val) - node->right = removeHelper(node->right, val); - else { - if (node->left == nullptr || node->right == nullptr) { - TreeNode *child = node->left != nullptr ? node->left : node->right; - // 子节点数量 = 0 ,直接删除 node 并返回 - if (child == nullptr) { - delete node; - return nullptr; - } - // 子节点数量 = 1 ,直接删除 node - else { - delete node; - node = child; - } - } else { - // 子节点数量 = 2 ,则将中序遍历的下个节点删除,并用该节点替换当前节点 - TreeNode *temp = node->right; - while (temp->left != nullptr) { - temp = temp->left; - } - int tempVal = temp->val; - node->right = removeHelper(node->right, temp->val); - node->val = tempVal; - } - } - updateHeight(node); // 更新节点高度 - /* 2. 执行旋转操作,使该子树重新恢复平衡 */ - node = rotate(node); - // 返回子树的根节点 - return node; - } + [class]{AVLTree}-[func]{removeHelper} ``` === "Java" ```java title="avl_tree.java" - /* 删除节点 */ + /* Remove node */ void remove(int val) { root = removeHelper(root, val); } - /* 递归删除节点(辅助方法) */ + /* Recursively remove node (helper method) */ TreeNode removeHelper(TreeNode node, int val) { if (node == null) return null; - /* 1. 查找节点并删除 */ + /* 1. Find and remove the node */ if (val < node.val) node.left = removeHelper(node.left, val); else if (val > node.val) @@ -2274,14 +1128,14 @@ Similarly, based on the method of removing nodes in binary search trees, rotatio else { if (node.left == null || node.right == null) { TreeNode child = node.left != null ? node.left : node.right; - // 子节点数量 = 0 ,直接删除 node 并返回 + // Number of child nodes = 0, remove node and return if (child == null) return null; - // 子节点数量 = 1 ,直接删除 node + // Number of child nodes = 1, remove node else node = child; } else { - // 子节点数量 = 2 ,则将中序遍历的下个节点删除,并用该节点替换当前节点 + // Number of child nodes = 2, remove the next node in in-order traversal and replace the current node with it TreeNode temp = node.right; while (temp.left != null) { temp = temp.left; @@ -2290,10 +1144,10 @@ Similarly, based on the method of removing nodes in binary search trees, rotatio node.val = temp.val; } } - updateHeight(node); // 更新节点高度 - /* 2. 执行旋转操作,使该子树重新恢复平衡 */ + updateHeight(node); // Update node height + /* 2. Perform rotation operation to restore balance to the subtree */ node = rotate(node); - // 返回子树的根节点 + // Return the root node of the subtree return node; } ``` @@ -2301,512 +1155,89 @@ Similarly, based on the method of removing nodes in binary search trees, rotatio === "C#" ```csharp title="avl_tree.cs" - /* 删除节点 */ - void Remove(int val) { - root = RemoveHelper(root, val); - } + [class]{AVLTree}-[func]{Remove} - /* 递归删除节点(辅助方法) */ - TreeNode? RemoveHelper(TreeNode? node, int val) { - if (node == null) return null; - /* 1. 查找节点并删除 */ - if (val < node.val) - node.left = RemoveHelper(node.left, val); - else if (val > node.val) - node.right = RemoveHelper(node.right, val); - else { - if (node.left == null || node.right == null) { - TreeNode? child = node.left ?? node.right; - // 子节点数量 = 0 ,直接删除 node 并返回 - if (child == null) - return null; - // 子节点数量 = 1 ,直接删除 node - else - node = child; - } else { - // 子节点数量 = 2 ,则将中序遍历的下个节点删除,并用该节点替换当前节点 - TreeNode? temp = node.right; - while (temp.left != null) { - temp = temp.left; - } - node.right = RemoveHelper(node.right, temp.val!.Value); - node.val = temp.val; - } - } - UpdateHeight(node); // 更新节点高度 - /* 2. 执行旋转操作,使该子树重新恢复平衡 */ - node = Rotate(node); - // 返回子树的根节点 - return node; - } + [class]{AVLTree}-[func]{RemoveHelper} ``` === "Go" ```go title="avl_tree.go" - /* 删除节点 */ - func (t *aVLTree) remove(val int) { - t.root = t.removeHelper(t.root, val) - } + [class]{aVLTree}-[func]{remove} - /* 递归删除节点(辅助函数) */ - func (t *aVLTree) removeHelper(node *TreeNode, val int) *TreeNode { - if node == nil { - return nil - } - /* 1. 查找节点并删除 */ - if val < node.Val.(int) { - node.Left = t.removeHelper(node.Left, val) - } else if val > node.Val.(int) { - node.Right = t.removeHelper(node.Right, val) - } else { - if node.Left == nil || node.Right == nil { - child := node.Left - if node.Right != nil { - child = node.Right - } - if child == nil { - // 子节点数量 = 0 ,直接删除 node 并返回 - return nil - } else { - // 子节点数量 = 1 ,直接删除 node - node = child - } - } else { - // 子节点数量 = 2 ,则将中序遍历的下个节点删除,并用该节点替换当前节点 - temp := node.Right - for temp.Left != nil { - temp = temp.Left - } - node.Right = t.removeHelper(node.Right, temp.Val.(int)) - node.Val = temp.Val - } - } - // 更新节点高度 - t.updateHeight(node) - /* 2. 执行旋转操作,使该子树重新恢复平衡 */ - node = t.rotate(node) - // 返回子树的根节点 - return node - } + [class]{aVLTree}-[func]{removeHelper} ``` === "Swift" ```swift title="avl_tree.swift" - /* 删除节点 */ - func remove(val: Int) { - root = removeHelper(node: root, val: val) - } + [class]{AVLTree}-[func]{remove} - /* 递归删除节点(辅助方法) */ - func removeHelper(node: TreeNode?, val: Int) -> TreeNode? { - var node = node - if node == nil { - return nil - } - /* 1. 查找节点并删除 */ - if val < node!.val { - node?.left = removeHelper(node: node?.left, val: val) - } else if val > node!.val { - node?.right = removeHelper(node: node?.right, val: val) - } else { - if node?.left == nil || node?.right == nil { - let child = node?.left ?? node?.right - // 子节点数量 = 0 ,直接删除 node 并返回 - if child == nil { - return nil - } - // 子节点数量 = 1 ,直接删除 node - else { - node = child - } - } else { - // 子节点数量 = 2 ,则将中序遍历的下个节点删除,并用该节点替换当前节点 - var temp = node?.right - while temp?.left != nil { - temp = temp?.left - } - node?.right = removeHelper(node: node?.right, val: temp!.val) - node?.val = temp!.val - } - } - updateHeight(node: node) // 更新节点高度 - /* 2. 执行旋转操作,使该子树重新恢复平衡 */ - node = rotate(node: node) - // 返回子树的根节点 - return node - } + [class]{AVLTree}-[func]{removeHelper} ``` === "JS" ```javascript title="avl_tree.js" - /* 删除节点 */ - remove(val) { - this.root = this.#removeHelper(this.root, val); - } + [class]{AVLTree}-[func]{remove} - /* 递归删除节点(辅助方法) */ - #removeHelper(node, val) { - if (node === null) return null; - /* 1. 查找节点并删除 */ - if (val < node.val) node.left = this.#removeHelper(node.left, val); - else if (val > node.val) - node.right = this.#removeHelper(node.right, val); - else { - if (node.left === null || node.right === null) { - const child = node.left !== null ? node.left : node.right; - // 子节点数量 = 0 ,直接删除 node 并返回 - if (child === null) return null; - // 子节点数量 = 1 ,直接删除 node - else node = child; - } else { - // 子节点数量 = 2 ,则将中序遍历的下个节点删除,并用该节点替换当前节点 - let temp = node.right; - while (temp.left !== null) { - temp = temp.left; - } - node.right = this.#removeHelper(node.right, temp.val); - node.val = temp.val; - } - } - this.#updateHeight(node); // 更新节点高度 - /* 2. 执行旋转操作,使该子树重新恢复平衡 */ - node = this.#rotate(node); - // 返回子树的根节点 - return node; - } + [class]{AVLTree}-[func]{removeHelper} ``` === "TS" ```typescript title="avl_tree.ts" - /* 删除节点 */ - remove(val: number): void { - this.root = this.removeHelper(this.root, val); - } + [class]{AVLTree}-[func]{remove} - /* 递归删除节点(辅助方法) */ - removeHelper(node: TreeNode, val: number): TreeNode { - if (node === null) return null; - /* 1. 查找节点并删除 */ - if (val < node.val) { - node.left = this.removeHelper(node.left, val); - } else if (val > node.val) { - node.right = this.removeHelper(node.right, val); - } else { - if (node.left === null || node.right === null) { - const child = node.left !== null ? node.left : node.right; - // 子节点数量 = 0 ,直接删除 node 并返回 - if (child === null) { - return null; - } else { - // 子节点数量 = 1 ,直接删除 node - node = child; - } - } else { - // 子节点数量 = 2 ,则将中序遍历的下个节点删除,并用该节点替换当前节点 - let temp = node.right; - while (temp.left !== null) { - temp = temp.left; - } - node.right = this.removeHelper(node.right, temp.val); - node.val = temp.val; - } - } - this.updateHeight(node); // 更新节点高度 - /* 2. 执行旋转操作,使该子树重新恢复平衡 */ - node = this.rotate(node); - // 返回子树的根节点 - return node; - } + [class]{AVLTree}-[func]{removeHelper} ``` === "Dart" ```dart title="avl_tree.dart" - /* 删除节点 */ - void remove(int val) { - root = removeHelper(root, val); - } + [class]{AVLTree}-[func]{remove} - /* 递归删除节点(辅助方法) */ - TreeNode? removeHelper(TreeNode? node, int val) { - if (node == null) return null; - /* 1. 查找节点并删除 */ - if (val < node.val) - node.left = removeHelper(node.left, val); - else if (val > node.val) - node.right = removeHelper(node.right, val); - else { - if (node.left == null || node.right == null) { - TreeNode? child = node.left ?? node.right; - // 子节点数量 = 0 ,直接删除 node 并返回 - if (child == null) - return null; - // 子节点数量 = 1 ,直接删除 node - else - node = child; - } else { - // 子节点数量 = 2 ,则将中序遍历的下个节点删除,并用该节点替换当前节点 - TreeNode? temp = node.right; - while (temp!.left != null) { - temp = temp.left; - } - node.right = removeHelper(node.right, temp.val); - node.val = temp.val; - } - } - updateHeight(node); // 更新节点高度 - /* 2. 执行旋转操作,使该子树重新恢复平衡 */ - node = rotate(node); - // 返回子树的根节点 - return node; - } + [class]{AVLTree}-[func]{removeHelper} ``` === "Rust" ```rust title="avl_tree.rs" - /* 删除节点 */ - fn remove(&self, val: i32) { - Self::remove_helper(self.root.clone(), val); - } + [class]{AVLTree}-[func]{remove} - /* 递归删除节点(辅助方法) */ - fn remove_helper(node: OptionTreeNodeRc, val: i32) -> OptionTreeNodeRc { - match node { - Some(mut node) => { - /* 1. 查找节点并删除 */ - if val < node.borrow().val { - let left = node.borrow().left.clone(); - node.borrow_mut().left = Self::remove_helper(left, val); - } else if val > node.borrow().val { - let right = node.borrow().right.clone(); - node.borrow_mut().right = Self::remove_helper(right, val); - } else if node.borrow().left.is_none() || node.borrow().right.is_none() { - let child = if node.borrow().left.is_some() { - node.borrow().left.clone() - } else { - node.borrow().right.clone() - }; - match child { - // 子节点数量 = 0 ,直接删除 node 并返回 - None => { - return None; - } - // 子节点数量 = 1 ,直接删除 node - Some(child) => node = child, - } - } else { - // 子节点数量 = 2 ,则将中序遍历的下个节点删除,并用该节点替换当前节点 - let mut temp = node.borrow().right.clone().unwrap(); - loop { - let temp_left = temp.borrow().left.clone(); - if temp_left.is_none() { - break; - } - temp = temp_left.unwrap(); - } - let right = node.borrow().right.clone(); - node.borrow_mut().right = Self::remove_helper(right, temp.borrow().val); - node.borrow_mut().val = temp.borrow().val; - } - Self::update_height(Some(node.clone())); // 更新节点高度 - - /* 2. 执行旋转操作,使该子树重新恢复平衡 */ - node = Self::rotate(Some(node)).unwrap(); - // 返回子树的根节点 - Some(node) - } - None => None, - } - } + [class]{AVLTree}-[func]{remove_helper} ``` === "C" ```c title="avl_tree.c" - /* 删除节点 */ - // 由于引入了 stdio.h ,此处无法使用 remove 关键词 - void removeItem(AVLTree *tree, int val) { - TreeNode *root = removeHelper(tree->root, val); - } + [class]{AVLTree}-[func]{removeItem} - /* 递归删除节点(辅助函数) */ - TreeNode *removeHelper(TreeNode *node, int val) { - TreeNode *child, *grandChild; - if (node == NULL) { - return NULL; - } - /* 1. 查找节点并删除 */ - if (val < node->val) { - node->left = removeHelper(node->left, val); - } else if (val > node->val) { - node->right = removeHelper(node->right, val); - } else { - if (node->left == NULL || node->right == NULL) { - child = node->left; - if (node->right != NULL) { - child = node->right; - } - // 子节点数量 = 0 ,直接删除 node 并返回 - if (child == NULL) { - return NULL; - } else { - // 子节点数量 = 1 ,直接删除 node - node = child; - } - } else { - // 子节点数量 = 2 ,则将中序遍历的下个节点删除,并用该节点替换当前节点 - TreeNode *temp = node->right; - while (temp->left != NULL) { - temp = temp->left; - } - int tempVal = temp->val; - node->right = removeHelper(node->right, temp->val); - node->val = tempVal; - } - } - // 更新节点高度 - updateHeight(node); - /* 2. 执行旋转操作,使该子树重新恢复平衡 */ - node = rotate(node); - // 返回子树的根节点 - return node; - } + [class]{}-[func]{removeHelper} ``` === "Kotlin" ```kotlin title="avl_tree.kt" - /* 删除节点 */ - fun remove(_val: Int) { - root = removeHelper(root, _val) - } + [class]{AVLTree}-[func]{remove} - /* 递归删除节点(辅助方法) */ - fun removeHelper(n: TreeNode?, _val: Int): TreeNode? { - var node = n ?: return null - /* 1. 查找节点并删除 */ - if (_val < node._val) - node.left = removeHelper(node.left, _val) - else if (_val > node._val) - node.right = removeHelper(node.right, _val) - 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 - // 子节点数量 = 1 ,直接删除 node - else - node = child - } else { - // 子节点数量 = 2 ,则将中序遍历的下个节点删除,并用该节点替换当前节点 - var temp = node.right - while (temp!!.left != null) { - temp = temp.left - } - node.right = removeHelper(node.right, temp._val) - node._val = temp._val - } - } - updateHeight(node) // 更新节点高度 - /* 2. 执行旋转操作,使该子树重新恢复平衡 */ - node = rotate(node) - // 返回子树的根节点 - return node - } + [class]{AVLTree}-[func]{removeHelper} ``` === "Ruby" ```ruby title="avl_tree.rb" - ### 删除节点 ### - def remove(val) - @root = remove_helper(@root, val) - end + [class]{AVLTree}-[func]{remove} - ### 递归删除节点(辅助方法)### - def remove_helper(node, val) - return if node.nil? - # 1. 查找节点并删除 - if val < node.val - node.left = remove_helper(node.left, val) - elsif val > node.val - node.right = remove_helper(node.right, val) - else - if node.left.nil? || node.right.nil? - child = node.left || node.right - # 子节点数量 = 0 ,直接删除 node 并返回 - return if child.nil? - # 子节点数量 = 1 ,直接删除 node - node = child - else - # 子节点数量 = 2 ,则将中序遍历的下个节点删除,并用该节点替换当前节点 - temp = node.right - while !temp.left.nil? - temp = temp.left - end - node.right = remove_helper(node.right, temp.val) - node.val = temp.val - end - end - # 更新节点高度 - update_height(node) - # 2. 执行旋转操作,使该子树重新恢复平衡 - rotate(node) - end + [class]{AVLTree}-[func]{remove_helper} ``` === "Zig" ```zig title="avl_tree.zig" - // 删除节点 - fn remove(self: *Self, val: T) void { - self.root = self.removeHelper(self.root, val).?; - } + [class]{AVLTree}-[func]{remove} - // 递归删除节点(辅助方法) - fn removeHelper(self: *Self, node_: ?*inc.TreeNode(T), val: T) ?*inc.TreeNode(T) { - var node = node_; - if (node == null) return null; - // 1. 查找节点并删除 - if (val < node.?.val) { - node.?.left = self.removeHelper(node.?.left, val); - } else if (val > node.?.val) { - node.?.right = self.removeHelper(node.?.right, val); - } else { - if (node.?.left == null or node.?.right == null) { - var child = if (node.?.left != null) node.?.left else node.?.right; - // 子节点数量 = 0 ,直接删除 node 并返回 - if (child == null) { - return null; - // 子节点数量 = 1 ,直接删除 node - } else { - node = child; - } - } else { - // 子节点数量 = 2 ,则将中序遍历的下个节点删除,并用该节点替换当前节点 - var temp = node.?.right; - while (temp.?.left != null) { - temp = temp.?.left; - } - node.?.right = self.removeHelper(node.?.right, temp.?.val); - node.?.val = temp.?.val; - } - } - self.updateHeight(node); // 更新节点高度 - // 2. 执行旋转操作,使该子树重新恢复平衡 - node = self.rotate(node); - // 返回子树的根节点 - return node; - } + [class]{AVLTree}-[func]{removeHelper} ``` ### 3.   Node search diff --git a/en/docs/chapter_tree/binary_search_tree.md b/en/docs/chapter_tree/binary_search_tree.md index 8a3affa82..69f45a611 100755 --- a/en/docs/chapter_tree/binary_search_tree.md +++ b/en/docs/chapter_tree/binary_search_tree.md @@ -45,17 +45,17 @@ The search operation in a binary search tree works on the same principle as the ```python title="binary_search_tree.py" def search(self, num: int) -> TreeNode | None: - """查找节点""" + """Search node""" cur = self._root - # 循环查找,越过叶节点后跳出 + # Loop find, break after passing leaf nodes while cur is not None: - # 目标节点在 cur 的右子树中 + # Target node is in cur's right subtree if cur.val < num: cur = cur.right - # 目标节点在 cur 的左子树中 + # Target node is in cur's left subtree elif cur.val > num: cur = cur.left - # 找到目标节点,跳出循环 + # Found target node, break loop else: break return cur @@ -64,45 +64,28 @@ The search operation in a binary search tree works on the same principle as the === "C++" ```cpp title="binary_search_tree.cpp" - /* 查找节点 */ - TreeNode *search(int num) { - TreeNode *cur = root; - // 循环查找,越过叶节点后跳出 - while (cur != nullptr) { - // 目标节点在 cur 的右子树中 - if (cur->val < num) - cur = cur->right; - // 目标节点在 cur 的左子树中 - else if (cur->val > num) - cur = cur->left; - // 找到目标节点,跳出循环 - else - break; - } - // 返回目标节点 - return cur; - } + [class]{BinarySearchTree}-[func]{search} ``` === "Java" ```java title="binary_search_tree.java" - /* 查找节点 */ + /* Search node */ TreeNode search(int num) { TreeNode cur = root; - // 循环查找,越过叶节点后跳出 + // Loop find, break after passing leaf nodes while (cur != null) { - // 目标节点在 cur 的右子树中 + // Target node is in cur's right subtree if (cur.val < num) cur = cur.right; - // 目标节点在 cur 的左子树中 + // Target node is in cur's left subtree else if (cur.val > num) cur = cur.left; - // 找到目标节点,跳出循环 + // Found target node, break loop else break; } - // 返回目标节点 + // Return target node return cur; } ``` @@ -110,263 +93,69 @@ The search operation in a binary search tree works on the same principle as the === "C#" ```csharp title="binary_search_tree.cs" - /* 查找节点 */ - TreeNode? Search(int num) { - TreeNode? cur = root; - // 循环查找,越过叶节点后跳出 - while (cur != null) { - // 目标节点在 cur 的右子树中 - if (cur.val < num) cur = - cur.right; - // 目标节点在 cur 的左子树中 - else if (cur.val > num) - cur = cur.left; - // 找到目标节点,跳出循环 - else - break; - } - // 返回目标节点 - return cur; - } + [class]{BinarySearchTree}-[func]{Search} ``` === "Go" ```go title="binary_search_tree.go" - /* 查找节点 */ - func (bst *binarySearchTree) search(num int) *TreeNode { - node := bst.root - // 循环查找,越过叶节点后跳出 - for node != nil { - if node.Val.(int) < num { - // 目标节点在 cur 的右子树中 - node = node.Right - } else if node.Val.(int) > num { - // 目标节点在 cur 的左子树中 - node = node.Left - } else { - // 找到目标节点,跳出循环 - break - } - } - // 返回目标节点 - return node - } + [class]{binarySearchTree}-[func]{search} ``` === "Swift" ```swift title="binary_search_tree.swift" - /* 查找节点 */ - func search(num: Int) -> TreeNode? { - var cur = root - // 循环查找,越过叶节点后跳出 - while cur != nil { - // 目标节点在 cur 的右子树中 - if cur!.val < num { - cur = cur?.right - } - // 目标节点在 cur 的左子树中 - else if cur!.val > num { - cur = cur?.left - } - // 找到目标节点,跳出循环 - else { - break - } - } - // 返回目标节点 - return cur - } + [class]{BinarySearchTree}-[func]{search} ``` === "JS" ```javascript title="binary_search_tree.js" - /* 查找节点 */ - search(num) { - let cur = this.root; - // 循环查找,越过叶节点后跳出 - while (cur !== null) { - // 目标节点在 cur 的右子树中 - if (cur.val < num) cur = cur.right; - // 目标节点在 cur 的左子树中 - else if (cur.val > num) cur = cur.left; - // 找到目标节点,跳出循环 - else break; - } - // 返回目标节点 - return cur; - } + [class]{BinarySearchTree}-[func]{search} ``` === "TS" ```typescript title="binary_search_tree.ts" - /* 查找节点 */ - search(num: number): TreeNode | null { - let cur = this.root; - // 循环查找,越过叶节点后跳出 - while (cur !== null) { - // 目标节点在 cur 的右子树中 - if (cur.val < num) cur = cur.right; - // 目标节点在 cur 的左子树中 - else if (cur.val > num) cur = cur.left; - // 找到目标节点,跳出循环 - else break; - } - // 返回目标节点 - return cur; - } + [class]{BinarySearchTree}-[func]{search} ``` === "Dart" ```dart title="binary_search_tree.dart" - /* 查找节点 */ - TreeNode? search(int _num) { - TreeNode? cur = _root; - // 循环查找,越过叶节点后跳出 - while (cur != null) { - // 目标节点在 cur 的右子树中 - if (cur.val < _num) - cur = cur.right; - // 目标节点在 cur 的左子树中 - else if (cur.val > _num) - cur = cur.left; - // 找到目标节点,跳出循环 - else - break; - } - // 返回目标节点 - return cur; - } + [class]{BinarySearchTree}-[func]{search} ``` === "Rust" ```rust title="binary_search_tree.rs" - /* 查找节点 */ - pub fn search(&self, num: i32) -> OptionTreeNodeRc { - let mut cur = self.root.clone(); - // 循环查找,越过叶节点后跳出 - while let Some(node) = cur.clone() { - match num.cmp(&node.borrow().val) { - // 目标节点在 cur 的右子树中 - Ordering::Greater => cur = node.borrow().right.clone(), - // 目标节点在 cur 的左子树中 - Ordering::Less => cur = node.borrow().left.clone(), - // 找到目标节点,跳出循环 - Ordering::Equal => break, - } - } - - // 返回目标节点 - cur - } + [class]{BinarySearchTree}-[func]{search} ``` === "C" ```c title="binary_search_tree.c" - /* 查找节点 */ - TreeNode *search(BinarySearchTree *bst, int num) { - TreeNode *cur = bst->root; - // 循环查找,越过叶节点后跳出 - while (cur != NULL) { - if (cur->val < num) { - // 目标节点在 cur 的右子树中 - cur = cur->right; - } else if (cur->val > num) { - // 目标节点在 cur 的左子树中 - cur = cur->left; - } else { - // 找到目标节点,跳出循环 - break; - } - } - // 返回目标节点 - return cur; - } + [class]{BinarySearchTree}-[func]{search} ``` === "Kotlin" ```kotlin title="binary_search_tree.kt" - /* 查找节点 */ - fun search(num: Int): TreeNode? { - var cur = root - // 循环查找,越过叶节点后跳出 - while (cur != null) { - // 目标节点在 cur 的右子树中 - cur = if (cur._val < num) - cur.right - // 目标节点在 cur 的左子树中 - else if (cur._val > num) - cur.left - // 找到目标节点,跳出循环 - else - break - } - // 返回目标节点 - return cur - } + [class]{BinarySearchTree}-[func]{search} ``` === "Ruby" ```ruby title="binary_search_tree.rb" - ### 查找节点 ### - def search(num) - cur = @root - - # 循环查找,越过叶节点后跳出 - while !cur.nil? - # 目标节点在 cur 的右子树中 - if cur.val < num - cur = cur.right - # 目标节点在 cur 的左子树中 - elsif cur.val > num - cur = cur.left - # 找到目标节点,跳出循环 - else - break - end - end - - cur - end + [class]{BinarySearchTree}-[func]{search} ``` === "Zig" ```zig title="binary_search_tree.zig" - // 查找节点 - fn search(self: *Self, num: T) ?*inc.TreeNode(T) { - var cur = self.root; - // 循环查找,越过叶节点后跳出 - while (cur != null) { - // 目标节点在 cur 的右子树中 - if (cur.?.val < num) { - cur = cur.?.right; - // 目标节点在 cur 的左子树中 - } else if (cur.?.val > num) { - cur = cur.?.left; - // 找到目标节点,跳出循环 - } else { - break; - } - } - // 返回目标节点 - return cur; - } + [class]{BinarySearchTree}-[func]{search} ``` -??? pythontutor "Code Visualization" - -
- - ### 2.   Inserting a node Given an element `num` to be inserted, to maintain the property of the binary search tree "left subtree < root node < right subtree," the insertion operation proceeds as shown in Figure 7-18. @@ -387,25 +176,25 @@ In the code implementation, note the following two points. ```python title="binary_search_tree.py" def insert(self, num: int): - """插入节点""" - # 若树为空,则初始化根节点 + """Insert node""" + # If tree is empty, initialize root node if self._root is None: self._root = TreeNode(num) return - # 循环查找,越过叶节点后跳出 + # Loop find, break after passing leaf nodes cur, pre = self._root, None while cur is not None: - # 找到重复节点,直接返回 + # Found duplicate node, thus return if cur.val == num: return pre = cur - # 插入位置在 cur 的右子树中 + # Insertion position is in cur's right subtree if cur.val < num: cur = cur.right - # 插入位置在 cur 的左子树中 + # Insertion position is in cur's left subtree else: cur = cur.left - # 插入节点 + # Insert node node = TreeNode(num) if pre.val < num: pre.right = node @@ -416,61 +205,34 @@ In the code implementation, note the following two points. === "C++" ```cpp title="binary_search_tree.cpp" - /* 插入节点 */ - void insert(int num) { - // 若树为空,则初始化根节点 - if (root == nullptr) { - root = new TreeNode(num); - return; - } - TreeNode *cur = root, *pre = nullptr; - // 循环查找,越过叶节点后跳出 - while (cur != nullptr) { - // 找到重复节点,直接返回 - if (cur->val == num) - return; - pre = cur; - // 插入位置在 cur 的右子树中 - if (cur->val < num) - cur = cur->right; - // 插入位置在 cur 的左子树中 - else - cur = cur->left; - } - // 插入节点 - TreeNode *node = new TreeNode(num); - if (pre->val < num) - pre->right = node; - else - pre->left = node; - } + [class]{BinarySearchTree}-[func]{insert} ``` === "Java" ```java title="binary_search_tree.java" - /* 插入节点 */ + /* Insert node */ void insert(int num) { - // 若树为空,则初始化根节点 + // If tree is empty, initialize root node if (root == null) { root = new TreeNode(num); return; } TreeNode cur = root, pre = null; - // 循环查找,越过叶节点后跳出 + // Loop find, break after passing leaf nodes while (cur != null) { - // 找到重复节点,直接返回 + // Found duplicate node, thus return if (cur.val == num) return; pre = cur; - // 插入位置在 cur 的右子树中 + // Insertion position is in cur's right subtree if (cur.val < num) cur = cur.right; - // 插入位置在 cur 的左子树中 + // Insertion position is in cur's left subtree else cur = cur.left; } - // 插入节点 + // Insert node TreeNode node = new TreeNode(num); if (pre.val < num) pre.right = node; @@ -482,391 +244,69 @@ In the code implementation, note the following two points. === "C#" ```csharp title="binary_search_tree.cs" - /* 插入节点 */ - void Insert(int num) { - // 若树为空,则初始化根节点 - if (root == null) { - root = new TreeNode(num); - return; - } - TreeNode? cur = root, pre = null; - // 循环查找,越过叶节点后跳出 - while (cur != null) { - // 找到重复节点,直接返回 - if (cur.val == num) - return; - pre = cur; - // 插入位置在 cur 的右子树中 - if (cur.val < num) - cur = cur.right; - // 插入位置在 cur 的左子树中 - else - cur = cur.left; - } - - // 插入节点 - TreeNode node = new(num); - if (pre != null) { - if (pre.val < num) - pre.right = node; - else - pre.left = node; - } - } + [class]{BinarySearchTree}-[func]{Insert} ``` === "Go" ```go title="binary_search_tree.go" - /* 插入节点 */ - func (bst *binarySearchTree) insert(num int) { - cur := bst.root - // 若树为空,则初始化根节点 - if cur == nil { - bst.root = NewTreeNode(num) - return - } - // 待插入节点之前的节点位置 - var pre *TreeNode = nil - // 循环查找,越过叶节点后跳出 - for cur != nil { - if cur.Val == num { - return - } - pre = cur - if cur.Val.(int) < num { - cur = cur.Right - } else { - cur = cur.Left - } - } - // 插入节点 - node := NewTreeNode(num) - if pre.Val.(int) < num { - pre.Right = node - } else { - pre.Left = node - } - } + [class]{binarySearchTree}-[func]{insert} ``` === "Swift" ```swift title="binary_search_tree.swift" - /* 插入节点 */ - func insert(num: Int) { - // 若树为空,则初始化根节点 - if root == nil { - root = TreeNode(x: num) - return - } - var cur = root - var pre: TreeNode? - // 循环查找,越过叶节点后跳出 - while cur != nil { - // 找到重复节点,直接返回 - if cur!.val == num { - return - } - pre = cur - // 插入位置在 cur 的右子树中 - if cur!.val < num { - cur = cur?.right - } - // 插入位置在 cur 的左子树中 - else { - cur = cur?.left - } - } - // 插入节点 - let node = TreeNode(x: num) - if pre!.val < num { - pre?.right = node - } else { - pre?.left = node - } - } + [class]{BinarySearchTree}-[func]{insert} ``` === "JS" ```javascript title="binary_search_tree.js" - /* 插入节点 */ - insert(num) { - // 若树为空,则初始化根节点 - if (this.root === null) { - this.root = new TreeNode(num); - return; - } - let cur = this.root, - pre = null; - // 循环查找,越过叶节点后跳出 - while (cur !== null) { - // 找到重复节点,直接返回 - if (cur.val === num) return; - pre = cur; - // 插入位置在 cur 的右子树中 - if (cur.val < num) cur = cur.right; - // 插入位置在 cur 的左子树中 - else cur = cur.left; - } - // 插入节点 - const node = new TreeNode(num); - if (pre.val < num) pre.right = node; - else pre.left = node; - } + [class]{BinarySearchTree}-[func]{insert} ``` === "TS" ```typescript title="binary_search_tree.ts" - /* 插入节点 */ - insert(num: number): void { - // 若树为空,则初始化根节点 - if (this.root === null) { - this.root = new TreeNode(num); - return; - } - let cur: TreeNode | null = this.root, - pre: TreeNode | null = null; - // 循环查找,越过叶节点后跳出 - while (cur !== null) { - // 找到重复节点,直接返回 - if (cur.val === num) return; - pre = cur; - // 插入位置在 cur 的右子树中 - if (cur.val < num) cur = cur.right; - // 插入位置在 cur 的左子树中 - else cur = cur.left; - } - // 插入节点 - const node = new TreeNode(num); - if (pre!.val < num) pre!.right = node; - else pre!.left = node; - } + [class]{BinarySearchTree}-[func]{insert} ``` === "Dart" ```dart title="binary_search_tree.dart" - /* 插入节点 */ - void insert(int _num) { - // 若树为空,则初始化根节点 - if (_root == null) { - _root = TreeNode(_num); - return; - } - TreeNode? cur = _root; - TreeNode? pre = null; - // 循环查找,越过叶节点后跳出 - while (cur != null) { - // 找到重复节点,直接返回 - if (cur.val == _num) return; - pre = cur; - // 插入位置在 cur 的右子树中 - if (cur.val < _num) - cur = cur.right; - // 插入位置在 cur 的左子树中 - else - cur = cur.left; - } - // 插入节点 - TreeNode? node = TreeNode(_num); - if (pre!.val < _num) - pre.right = node; - else - pre.left = node; - } + [class]{BinarySearchTree}-[func]{insert} ``` === "Rust" ```rust title="binary_search_tree.rs" - /* 插入节点 */ - pub fn insert(&mut self, num: i32) { - // 若树为空,则初始化根节点 - if self.root.is_none() { - self.root = Some(TreeNode::new(num)); - return; - } - let mut cur = self.root.clone(); - let mut pre = None; - // 循环查找,越过叶节点后跳出 - while let Some(node) = cur.clone() { - match num.cmp(&node.borrow().val) { - // 找到重复节点,直接返回 - Ordering::Equal => return, - // 插入位置在 cur 的右子树中 - Ordering::Greater => { - pre = cur.clone(); - cur = node.borrow().right.clone(); - } - // 插入位置在 cur 的左子树中 - Ordering::Less => { - pre = cur.clone(); - cur = node.borrow().left.clone(); - } - } - } - // 插入节点 - let pre = pre.unwrap(); - let node = Some(TreeNode::new(num)); - if num > pre.borrow().val { - pre.borrow_mut().right = node; - } else { - pre.borrow_mut().left = node; - } - } + [class]{BinarySearchTree}-[func]{insert} ``` === "C" ```c title="binary_search_tree.c" - /* 插入节点 */ - void insert(BinarySearchTree *bst, int num) { - // 若树为空,则初始化根节点 - if (bst->root == NULL) { - bst->root = newTreeNode(num); - return; - } - TreeNode *cur = bst->root, *pre = NULL; - // 循环查找,越过叶节点后跳出 - while (cur != NULL) { - // 找到重复节点,直接返回 - if (cur->val == num) { - return; - } - pre = cur; - if (cur->val < num) { - // 插入位置在 cur 的右子树中 - cur = cur->right; - } else { - // 插入位置在 cur 的左子树中 - cur = cur->left; - } - } - // 插入节点 - TreeNode *node = newTreeNode(num); - if (pre->val < num) { - pre->right = node; - } else { - pre->left = node; - } - } + [class]{BinarySearchTree}-[func]{insert} ``` === "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._val == num) - return - pre = cur - // 插入位置在 cur 的右子树中 - cur = if (cur._val < num) - cur.right - // 插入位置在 cur 的左子树中 - else - cur.left - } - // 插入节点 - val node = TreeNode(num) - if (pre?._val!! < num) - pre.right = node - else - pre.left = node - } + [class]{BinarySearchTree}-[func]{insert} ``` === "Ruby" ```ruby title="binary_search_tree.rb" - ### 插入节点 ### - def insert(num) - # 若树为空,则初始化根节点 - if @root.nil? - @root = TreeNode.new(num) - return - end - - # 循环查找,越过叶节点后跳出 - cur, pre = @root, nil - while !cur.nil? - # 找到重复节点,直接返回 - return if cur.val == num - - pre = cur - # 插入位置在 cur 的右子树中 - if cur.val < num - cur = cur.right - # 插入位置在 cur 的左子树中 - else - cur = cur.left - end - end - - # 插入节点 - node = TreeNode.new(num) - if pre.val < num - pre.right = node - else - pre.left = node - end - end + [class]{BinarySearchTree}-[func]{insert} ``` === "Zig" ```zig title="binary_search_tree.zig" - // 插入节点 - fn insert(self: *Self, num: T) !void { - // 若树为空,则初始化根节点 - if (self.root == null) { - self.root = try self.mem_allocator.create(inc.TreeNode(T)); - return; - } - var cur = self.root; - var pre: ?*inc.TreeNode(T) = null; - // 循环查找,越过叶节点后跳出 - while (cur != null) { - // 找到重复节点,直接返回 - if (cur.?.val == num) return; - pre = cur; - // 插入位置在 cur 的右子树中 - if (cur.?.val < num) { - cur = cur.?.right; - // 插入位置在 cur 的左子树中 - } else { - cur = cur.?.left; - } - } - // 插入节点 - var node = try self.mem_allocator.create(inc.TreeNode(T)); - node.init(num); - if (pre.?.val < num) { - pre.?.right = node; - } else { - pre.?.left = node; - } - } + [class]{BinarySearchTree}-[func]{insert} ``` -??? pythontutor "Code Visualization" - -
- - Similar to searching for a node, inserting a node uses $O(\log n)$ time. ### 3.   Removing a node @@ -912,160 +352,108 @@ The operation of removing a node also uses $O(\log n)$ time, where finding the n ```python title="binary_search_tree.py" def remove(self, num: int): - """删除节点""" - # 若树为空,直接提前返回 + """Remove node""" + # If tree is empty, return if self._root is None: return - # 循环查找,越过叶节点后跳出 + # Loop find, break after passing leaf nodes cur, pre = self._root, None while cur is not None: - # 找到待删除节点,跳出循环 + # Found node to be removed, break loop if cur.val == num: break pre = cur - # 待删除节点在 cur 的右子树中 + # Node to be removed is in cur's right subtree if cur.val < num: cur = cur.right - # 待删除节点在 cur 的左子树中 + # Node to be removed is in cur's left subtree else: cur = cur.left - # 若无待删除节点,则直接返回 + # If no node to be removed, return if cur is None: return - # 子节点数量 = 0 or 1 + # Number of child nodes = 0 or 1 if cur.left is None or cur.right is None: - # 当子节点数量 = 0 / 1 时, child = null / 该子节点 + # When the number of child nodes = 0/1, child = null/that child node child = cur.left or cur.right - # 删除节点 cur + # Remove node cur if cur != self._root: if pre.left == cur: pre.left = child else: pre.right = child else: - # 若删除节点为根节点,则重新指定根节点 + # If the removed node is the root, reassign the root self._root = child - # 子节点数量 = 2 + # Number of child nodes = 2 else: - # 获取中序遍历中 cur 的下一个节点 + # Get the next node in in-order traversal of cur tmp: TreeNode = cur.right while tmp.left is not None: tmp = tmp.left - # 递归删除节点 tmp + # Recursively remove node tmp self.remove(tmp.val) - # 用 tmp 覆盖 cur + # Replace cur with tmp cur.val = tmp.val ``` === "C++" ```cpp title="binary_search_tree.cpp" - /* 删除节点 */ - void remove(int num) { - // 若树为空,直接提前返回 - if (root == nullptr) - return; - TreeNode *cur = root, *pre = nullptr; - // 循环查找,越过叶节点后跳出 - while (cur != nullptr) { - // 找到待删除节点,跳出循环 - if (cur->val == num) - break; - pre = cur; - // 待删除节点在 cur 的右子树中 - if (cur->val < num) - cur = cur->right; - // 待删除节点在 cur 的左子树中 - else - cur = cur->left; - } - // 若无待删除节点,则直接返回 - if (cur == nullptr) - return; - // 子节点数量 = 0 or 1 - if (cur->left == nullptr || cur->right == nullptr) { - // 当子节点数量 = 0 / 1 时, child = nullptr / 该子节点 - TreeNode *child = cur->left != nullptr ? cur->left : cur->right; - // 删除节点 cur - if (cur != root) { - if (pre->left == cur) - pre->left = child; - else - pre->right = child; - } else { - // 若删除节点为根节点,则重新指定根节点 - root = child; - } - // 释放内存 - delete cur; - } - // 子节点数量 = 2 - else { - // 获取中序遍历中 cur 的下一个节点 - TreeNode *tmp = cur->right; - while (tmp->left != nullptr) { - tmp = tmp->left; - } - int tmpVal = tmp->val; - // 递归删除节点 tmp - remove(tmp->val); - // 用 tmp 覆盖 cur - cur->val = tmpVal; - } - } + [class]{BinarySearchTree}-[func]{remove} ``` === "Java" ```java title="binary_search_tree.java" - /* 删除节点 */ + /* Remove node */ void remove(int num) { - // 若树为空,直接提前返回 + // If tree is empty, return if (root == null) return; TreeNode cur = root, pre = null; - // 循环查找,越过叶节点后跳出 + // Loop find, break after passing leaf nodes while (cur != null) { - // 找到待删除节点,跳出循环 + // Found node to be removed, break loop if (cur.val == num) break; pre = cur; - // 待删除节点在 cur 的右子树中 + // Node to be removed is in cur's right subtree if (cur.val < num) cur = cur.right; - // 待删除节点在 cur 的左子树中 + // Node to be removed is in cur's left subtree else cur = cur.left; } - // 若无待删除节点,则直接返回 + // If no node to be removed, return if (cur == null) return; - // 子节点数量 = 0 or 1 + // Number of child nodes = 0 or 1 if (cur.left == null || cur.right == null) { - // 当子节点数量 = 0 / 1 时, child = null / 该子节点 + // When the number of child nodes = 0/1, child = null/that child node TreeNode child = cur.left != null ? cur.left : cur.right; - // 删除节点 cur + // Remove node cur if (cur != root) { if (pre.left == cur) pre.left = child; else pre.right = child; } else { - // 若删除节点为根节点,则重新指定根节点 + // If the removed node is the root, reassign the root root = child; } } - // 子节点数量 = 2 + // Number of child nodes = 2 else { - // 获取中序遍历中 cur 的下一个节点 + // Get the next node in in-order traversal of cur TreeNode tmp = cur.right; while (tmp.left != null) { tmp = tmp.left; } - // 递归删除节点 tmp + // Recursively remove node tmp remove(tmp.val); - // 用 tmp 覆盖 cur + // Replace cur with tmp cur.val = tmp.val; } } @@ -1074,635 +462,69 @@ The operation of removing a node also uses $O(\log n)$ time, where finding the n === "C#" ```csharp title="binary_search_tree.cs" - /* 删除节点 */ - void Remove(int num) { - // 若树为空,直接提前返回 - if (root == null) - return; - TreeNode? cur = root, pre = null; - // 循环查找,越过叶节点后跳出 - while (cur != null) { - // 找到待删除节点,跳出循环 - if (cur.val == num) - break; - pre = cur; - // 待删除节点在 cur 的右子树中 - if (cur.val < num) - cur = cur.right; - // 待删除节点在 cur 的左子树中 - else - cur = cur.left; - } - // 若无待删除节点,则直接返回 - if (cur == null) - return; - // 子节点数量 = 0 or 1 - if (cur.left == null || cur.right == null) { - // 当子节点数量 = 0 / 1 时, child = null / 该子节点 - TreeNode? child = cur.left ?? cur.right; - // 删除节点 cur - if (cur != root) { - if (pre!.left == cur) - pre.left = child; - else - pre.right = child; - } else { - // 若删除节点为根节点,则重新指定根节点 - root = child; - } - } - // 子节点数量 = 2 - else { - // 获取中序遍历中 cur 的下一个节点 - TreeNode? tmp = cur.right; - while (tmp.left != null) { - tmp = tmp.left; - } - // 递归删除节点 tmp - Remove(tmp.val!.Value); - // 用 tmp 覆盖 cur - cur.val = tmp.val; - } - } + [class]{BinarySearchTree}-[func]{Remove} ``` === "Go" ```go title="binary_search_tree.go" - /* 删除节点 */ - func (bst *binarySearchTree) remove(num int) { - cur := bst.root - // 若树为空,直接提前返回 - if cur == nil { - return - } - // 待删除节点之前的节点位置 - var pre *TreeNode = nil - // 循环查找,越过叶节点后跳出 - for cur != nil { - if cur.Val == num { - break - } - pre = cur - if cur.Val.(int) < num { - // 待删除节点在右子树中 - cur = cur.Right - } else { - // 待删除节点在左子树中 - cur = cur.Left - } - } - // 若无待删除节点,则直接返回 - if cur == nil { - return - } - // 子节点数为 0 或 1 - if cur.Left == nil || cur.Right == nil { - var child *TreeNode = nil - // 取出待删除节点的子节点 - if cur.Left != nil { - child = cur.Left - } else { - child = cur.Right - } - // 删除节点 cur - if cur != bst.root { - if pre.Left == cur { - pre.Left = child - } else { - pre.Right = child - } - } else { - // 若删除节点为根节点,则重新指定根节点 - bst.root = child - } - // 子节点数为 2 - } else { - // 获取中序遍历中待删除节点 cur 的下一个节点 - tmp := cur.Right - for tmp.Left != nil { - tmp = tmp.Left - } - // 递归删除节点 tmp - bst.remove(tmp.Val.(int)) - // 用 tmp 覆盖 cur - cur.Val = tmp.Val - } - } + [class]{binarySearchTree}-[func]{remove} ``` === "Swift" ```swift title="binary_search_tree.swift" - /* 删除节点 */ - func remove(num: Int) { - // 若树为空,直接提前返回 - if root == nil { - return - } - var cur = root - var pre: TreeNode? - // 循环查找,越过叶节点后跳出 - while cur != nil { - // 找到待删除节点,跳出循环 - if cur!.val == num { - break - } - pre = cur - // 待删除节点在 cur 的右子树中 - if cur!.val < num { - cur = cur?.right - } - // 待删除节点在 cur 的左子树中 - else { - cur = cur?.left - } - } - // 若无待删除节点,则直接返回 - if cur == nil { - return - } - // 子节点数量 = 0 or 1 - if cur?.left == nil || cur?.right == nil { - // 当子节点数量 = 0 / 1 时, child = null / 该子节点 - let child = cur?.left ?? 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 != nil { - tmp = tmp?.left - } - // 递归删除节点 tmp - remove(num: tmp!.val) - // 用 tmp 覆盖 cur - cur?.val = tmp!.val - } - } + [class]{BinarySearchTree}-[func]{remove} ``` === "JS" ```javascript title="binary_search_tree.js" - /* 删除节点 */ - remove(num) { - // 若树为空,直接提前返回 - if (this.root === null) return; - let cur = this.root, - pre = null; - // 循环查找,越过叶节点后跳出 - while (cur !== null) { - // 找到待删除节点,跳出循环 - if (cur.val === num) break; - pre = cur; - // 待删除节点在 cur 的右子树中 - if (cur.val < num) cur = cur.right; - // 待删除节点在 cur 的左子树中 - else cur = cur.left; - } - // 若无待删除节点,则直接返回 - if (cur === null) return; - // 子节点数量 = 0 or 1 - if (cur.left === null || cur.right === null) { - // 当子节点数量 = 0 / 1 时, child = null / 该子节点 - const child = cur.left !== null ? cur.left : cur.right; - // 删除节点 cur - if (cur !== this.root) { - if (pre.left === cur) pre.left = child; - else pre.right = child; - } else { - // 若删除节点为根节点,则重新指定根节点 - this.root = child; - } - } - // 子节点数量 = 2 - else { - // 获取中序遍历中 cur 的下一个节点 - let tmp = cur.right; - while (tmp.left !== null) { - tmp = tmp.left; - } - // 递归删除节点 tmp - this.remove(tmp.val); - // 用 tmp 覆盖 cur - cur.val = tmp.val; - } - } + [class]{BinarySearchTree}-[func]{remove} ``` === "TS" ```typescript title="binary_search_tree.ts" - /* 删除节点 */ - remove(num: number): void { - // 若树为空,直接提前返回 - if (this.root === null) return; - let cur: TreeNode | null = this.root, - pre: TreeNode | null = null; - // 循环查找,越过叶节点后跳出 - while (cur !== null) { - // 找到待删除节点,跳出循环 - if (cur.val === num) break; - pre = cur; - // 待删除节点在 cur 的右子树中 - if (cur.val < num) cur = cur.right; - // 待删除节点在 cur 的左子树中 - else cur = cur.left; - } - // 若无待删除节点,则直接返回 - if (cur === null) return; - // 子节点数量 = 0 or 1 - if (cur.left === null || cur.right === null) { - // 当子节点数量 = 0 / 1 时, child = null / 该子节点 - const child: TreeNode | null = - cur.left !== null ? cur.left : cur.right; - // 删除节点 cur - if (cur !== this.root) { - if (pre!.left === cur) pre!.left = child; - else pre!.right = child; - } else { - // 若删除节点为根节点,则重新指定根节点 - this.root = child; - } - } - // 子节点数量 = 2 - else { - // 获取中序遍历中 cur 的下一个节点 - let tmp: TreeNode | null = cur.right; - while (tmp!.left !== null) { - tmp = tmp!.left; - } - // 递归删除节点 tmp - this.remove(tmp!.val); - // 用 tmp 覆盖 cur - cur.val = tmp!.val; - } - } + [class]{BinarySearchTree}-[func]{remove} ``` === "Dart" ```dart title="binary_search_tree.dart" - /* 删除节点 */ - void remove(int _num) { - // 若树为空,直接提前返回 - if (_root == null) return; - TreeNode? cur = _root; - TreeNode? pre = null; - // 循环查找,越过叶节点后跳出 - while (cur != null) { - // 找到待删除节点,跳出循环 - if (cur.val == _num) break; - pre = cur; - // 待删除节点在 cur 的右子树中 - if (cur.val < _num) - cur = cur.right; - // 待删除节点在 cur 的左子树中 - else - cur = cur.left; - } - // 若无待删除节点,直接返回 - if (cur == null) return; - // 子节点数量 = 0 or 1 - if (cur.left == null || cur.right == null) { - // 当子节点数量 = 0 / 1 时, child = null / 该子节点 - TreeNode? child = cur.left ?? cur.right; - // 删除节点 cur - if (cur != _root) { - if (pre!.left == cur) - pre.left = child; - else - pre.right = child; - } else { - // 若删除节点为根节点,则重新指定根节点 - _root = child; - } - } else { - // 子节点数量 = 2 - // 获取中序遍历中 cur 的下一个节点 - TreeNode? tmp = cur.right; - while (tmp!.left != null) { - tmp = tmp.left; - } - // 递归删除节点 tmp - remove(tmp.val); - // 用 tmp 覆盖 cur - cur.val = tmp.val; - } - } + [class]{BinarySearchTree}-[func]{remove} ``` === "Rust" ```rust title="binary_search_tree.rs" - /* 删除节点 */ - pub fn remove(&mut self, num: i32) { - // 若树为空,直接提前返回 - if self.root.is_none() { - return; - } - let mut cur = self.root.clone(); - let mut pre = None; - // 循环查找,越过叶节点后跳出 - while let Some(node) = cur.clone() { - match num.cmp(&node.borrow().val) { - // 找到待删除节点,跳出循环 - Ordering::Equal => break, - // 待删除节点在 cur 的右子树中 - Ordering::Greater => { - pre = cur.clone(); - cur = node.borrow().right.clone(); - } - // 待删除节点在 cur 的左子树中 - Ordering::Less => { - pre = cur.clone(); - cur = node.borrow().left.clone(); - } - } - } - // 若无待删除节点,则直接返回 - if cur.is_none() { - return; - } - let cur = cur.unwrap(); - let (left_child, right_child) = (cur.borrow().left.clone(), cur.borrow().right.clone()); - match (left_child.clone(), right_child.clone()) { - // 子节点数量 = 0 or 1 - (None, None) | (Some(_), None) | (None, Some(_)) => { - // 当子节点数量 = 0 / 1 时, child = nullptr / 该子节点 - let child = left_child.or(right_child); - let pre = pre.unwrap(); - // 删除节点 cur - if !Rc::ptr_eq(&cur, self.root.as_ref().unwrap()) { - let left = pre.borrow().left.clone(); - if left.is_some() && Rc::ptr_eq(&left.as_ref().unwrap(), &cur) { - pre.borrow_mut().left = child; - } else { - pre.borrow_mut().right = child; - } - } else { - // 若删除节点为根节点,则重新指定根节点 - self.root = child; - } - } - // 子节点数量 = 2 - (Some(_), Some(_)) => { - // 获取中序遍历中 cur 的下一个节点 - let mut tmp = cur.borrow().right.clone(); - while let Some(node) = tmp.clone() { - if node.borrow().left.is_some() { - tmp = node.borrow().left.clone(); - } else { - break; - } - } - let tmpval = tmp.unwrap().borrow().val; - // 递归删除节点 tmp - self.remove(tmpval); - // 用 tmp 覆盖 cur - cur.borrow_mut().val = tmpval; - } - } - } + [class]{BinarySearchTree}-[func]{remove} ``` === "C" ```c title="binary_search_tree.c" - /* 删除节点 */ - // 由于引入了 stdio.h ,此处无法使用 remove 关键词 - void removeItem(BinarySearchTree *bst, int num) { - // 若树为空,直接提前返回 - if (bst->root == NULL) - return; - TreeNode *cur = bst->root, *pre = NULL; - // 循环查找,越过叶节点后跳出 - while (cur != NULL) { - // 找到待删除节点,跳出循环 - if (cur->val == num) - break; - pre = cur; - if (cur->val < num) { - // 待删除节点在 root 的右子树中 - cur = cur->right; - } else { - // 待删除节点在 root 的左子树中 - cur = cur->left; - } - } - // 若无待删除节点,则直接返回 - if (cur == NULL) - return; - // 判断待删除节点是否存在子节点 - if (cur->left == NULL || cur->right == NULL) { - /* 子节点数量 = 0 or 1 */ - // 当子节点数量 = 0 / 1 时, child = nullptr / 该子节点 - TreeNode *child = cur->left != NULL ? cur->left : cur->right; - // 删除节点 cur - if (pre->left == cur) { - pre->left = child; - } else { - pre->right = child; - } - // 释放内存 - free(cur); - } else { - /* 子节点数量 = 2 */ - // 获取中序遍历中 cur 的下一个节点 - TreeNode *tmp = cur->right; - while (tmp->left != NULL) { - tmp = tmp->left; - } - int tmpVal = tmp->val; - // 递归删除节点 tmp - removeItem(bst, tmp->val); - // 用 tmp 覆盖 cur - cur->val = tmpVal; - } - } + [class]{BinarySearchTree}-[func]{removeItem} ``` === "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._val == num) - break - pre = cur - // 待删除节点在 cur 的右子树中 - cur = if (cur._val < 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._val) - // 用 tmp 覆盖 cur - cur._val = tmp._val - } - } + [class]{BinarySearchTree}-[func]{remove} ``` === "Ruby" ```ruby title="binary_search_tree.rb" - ### 删除节点 ### - def remove(num) - # 若树为空,直接提前返回 - return if @root.nil? - - # 循环查找,越过叶节点后跳出 - cur, pre = @root, nil - while !cur.nil? - # 找到待删除节点,跳出循环 - break if cur.val == num - - pre = cur - # 待删除节点在 cur 的右子树中 - if cur.val < num - cur = cur.right - # 待删除节点在 cur 的左子树中 - else - cur = cur.left - end - end - # 若无待删除节点,则直接返回 - return if cur.nil? - - # 子节点数量 = 0 or 1 - if cur.left.nil? || cur.right.nil? - # 当子节点数量 = 0 / 1 时, child = null / 该子节点 - child = cur.left || cur.right - # 删除节点 cur - if cur != @root - if pre.left == cur - pre.left = child - else - pre.right = child - end - else - # 若删除节点为根节点,则重新指定根节点 - @root = child - end - # 子节点数量 = 2 - else - # 获取中序遍历中 cur 的下一个节点 - tmp = cur.right - while !tmp.left.nil? - tmp = tmp.left - end - # 递归删除节点 tmp - remove(tmp.val) - # 用 tmp 覆盖 cur - cur.val = tmp.val - end - end + [class]{BinarySearchTree}-[func]{remove} ``` === "Zig" ```zig title="binary_search_tree.zig" - // 删除节点 - fn remove(self: *Self, num: T) void { - // 若树为空,直接提前返回 - if (self.root == null) return; - var cur = self.root; - var pre: ?*inc.TreeNode(T) = null; - // 循环查找,越过叶节点后跳出 - while (cur != null) { - // 找到待删除节点,跳出循环 - if (cur.?.val == num) break; - pre = cur; - // 待删除节点在 cur 的右子树中 - if (cur.?.val < num) { - cur = cur.?.right; - // 待删除节点在 cur 的左子树中 - } else { - cur = cur.?.left; - } - } - // 若无待删除节点,则直接返回 - if (cur == null) return; - // 子节点数量 = 0 or 1 - if (cur.?.left == null or cur.?.right == null) { - // 当子节点数量 = 0 / 1 时, child = null / 该子节点 - var child = if (cur.?.left != null) cur.?.left else cur.?.right; - // 删除节点 cur - if (pre.?.left == cur) { - pre.?.left = child; - } else { - pre.?.right = child; - } - // 子节点数量 = 2 - } else { - // 获取中序遍历中 cur 的下一个节点 - var tmp = cur.?.right; - while (tmp.?.left != null) { - tmp = tmp.?.left; - } - var tmp_val = tmp.?.val; - // 递归删除节点 tmp - self.remove(tmp.?.val); - // 用 tmp 覆盖 cur - cur.?.val = tmp_val; - } - } + [class]{BinarySearchTree}-[func]{remove} ``` -??? pythontutor "Code Visualization" - -
- - ### 4.   In-order traversal is ordered As shown in Figure 7-22, the in-order traversal of a binary tree follows the "left $\rightarrow$ root $\rightarrow$ right" traversal order, and a binary search tree satisfies the size relationship "left child node $<$ root node $<$ right child node". diff --git a/en/docs/chapter_tree/binary_tree_traversal.md b/en/docs/chapter_tree/binary_tree_traversal.md index db9779352..ce6284433 100755 --- a/en/docs/chapter_tree/binary_tree_traversal.md +++ b/en/docs/chapter_tree/binary_tree_traversal.md @@ -26,62 +26,45 @@ Breadth-first traversal is usually implemented with the help of a "queue". The q ```python title="binary_tree_bfs.py" def level_order(root: TreeNode | None) -> list[int]: - """层序遍历""" - # 初始化队列,加入根节点 + """Level-order traversal""" + # Initialize queue, add root node queue: deque[TreeNode] = deque() queue.append(root) - # 初始化一个列表,用于保存遍历序列 + # Initialize a list to store the traversal sequence res = [] while queue: - node: TreeNode = queue.popleft() # 队列出队 - res.append(node.val) # 保存节点值 + node: TreeNode = queue.popleft() # Queue dequeues + res.append(node.val) # Save node value if node.left is not None: - queue.append(node.left) # 左子节点入队 + queue.append(node.left) # Left child node enqueues if node.right is not None: - queue.append(node.right) # 右子节点入队 + queue.append(node.right) # Right child node enqueues return res ``` === "C++" ```cpp title="binary_tree_bfs.cpp" - /* 层序遍历 */ - vector levelOrder(TreeNode *root) { - // 初始化队列,加入根节点 - queue queue; - queue.push(root); - // 初始化一个列表,用于保存遍历序列 - vector vec; - while (!queue.empty()) { - TreeNode *node = queue.front(); - queue.pop(); // 队列出队 - vec.push_back(node->val); // 保存节点值 - if (node->left != nullptr) - queue.push(node->left); // 左子节点入队 - if (node->right != nullptr) - queue.push(node->right); // 右子节点入队 - } - return vec; - } + [class]{}-[func]{levelOrder} ``` === "Java" ```java title="binary_tree_bfs.java" - /* 层序遍历 */ + /* Level-order traversal */ List levelOrder(TreeNode root) { - // 初始化队列,加入根节点 + // Initialize queue, add root node Queue queue = new LinkedList<>(); queue.add(root); - // 初始化一个列表,用于保存遍历序列 + // Initialize a list to store the traversal sequence List list = new ArrayList<>(); while (!queue.isEmpty()) { - TreeNode node = queue.poll(); // 队列出队 - list.add(node.val); // 保存节点值 + TreeNode node = queue.poll(); // Queue dequeues + list.add(node.val); // Save node value if (node.left != null) - queue.offer(node.left); // 左子节点入队 + queue.offer(node.left); // Left child node enqueues if (node.right != null) - queue.offer(node.right); // 右子节点入队 + queue.offer(node.right); // Right child node enqueues } return list; } @@ -90,287 +73,69 @@ Breadth-first traversal is usually implemented with the help of a "queue". The q === "C#" ```csharp title="binary_tree_bfs.cs" - /* 层序遍历 */ - List LevelOrder(TreeNode root) { - // 初始化队列,加入根节点 - Queue queue = new(); - queue.Enqueue(root); - // 初始化一个列表,用于保存遍历序列 - List list = []; - while (queue.Count != 0) { - TreeNode node = queue.Dequeue(); // 队列出队 - list.Add(node.val!.Value); // 保存节点值 - if (node.left != null) - queue.Enqueue(node.left); // 左子节点入队 - if (node.right != null) - queue.Enqueue(node.right); // 右子节点入队 - } - return list; - } + [class]{binary_tree_bfs}-[func]{LevelOrder} ``` === "Go" ```go title="binary_tree_bfs.go" - /* 层序遍历 */ - func levelOrder(root *TreeNode) []any { - // 初始化队列,加入根节点 - queue := list.New() - queue.PushBack(root) - // 初始化一个切片,用于保存遍历序列 - nums := make([]any, 0) - for queue.Len() > 0 { - // 队列出队 - node := queue.Remove(queue.Front()).(*TreeNode) - // 保存节点值 - nums = append(nums, node.Val) - if node.Left != nil { - // 左子节点入队 - queue.PushBack(node.Left) - } - if node.Right != nil { - // 右子节点入队 - queue.PushBack(node.Right) - } - } - return nums - } + [class]{}-[func]{levelOrder} ``` === "Swift" ```swift title="binary_tree_bfs.swift" - /* 层序遍历 */ - func levelOrder(root: TreeNode) -> [Int] { - // 初始化队列,加入根节点 - var queue: [TreeNode] = [root] - // 初始化一个列表,用于保存遍历序列 - var list: [Int] = [] - while !queue.isEmpty { - let node = queue.removeFirst() // 队列出队 - list.append(node.val) // 保存节点值 - if let left = node.left { - queue.append(left) // 左子节点入队 - } - if let right = node.right { - queue.append(right) // 右子节点入队 - } - } - return list - } + [class]{}-[func]{levelOrder} ``` === "JS" ```javascript title="binary_tree_bfs.js" - /* 层序遍历 */ - function levelOrder(root) { - // 初始化队列,加入根节点 - const queue = [root]; - // 初始化一个列表,用于保存遍历序列 - const list = []; - while (queue.length) { - let node = queue.shift(); // 队列出队 - list.push(node.val); // 保存节点值 - if (node.left) queue.push(node.left); // 左子节点入队 - if (node.right) queue.push(node.right); // 右子节点入队 - } - return list; - } + [class]{}-[func]{levelOrder} ``` === "TS" ```typescript title="binary_tree_bfs.ts" - /* 层序遍历 */ - function levelOrder(root: TreeNode | null): number[] { - // 初始化队列,加入根节点 - const queue = [root]; - // 初始化一个列表,用于保存遍历序列 - const list: number[] = []; - while (queue.length) { - let node = queue.shift() as TreeNode; // 队列出队 - list.push(node.val); // 保存节点值 - if (node.left) { - queue.push(node.left); // 左子节点入队 - } - if (node.right) { - queue.push(node.right); // 右子节点入队 - } - } - return list; - } + [class]{}-[func]{levelOrder} ``` === "Dart" ```dart title="binary_tree_bfs.dart" - /* 层序遍历 */ - List levelOrder(TreeNode? root) { - // 初始化队列,加入根节点 - Queue queue = Queue(); - queue.add(root); - // 初始化一个列表,用于保存遍历序列 - List res = []; - while (queue.isNotEmpty) { - TreeNode? node = queue.removeFirst(); // 队列出队 - res.add(node!.val); // 保存节点值 - if (node.left != null) queue.add(node.left); // 左子节点入队 - if (node.right != null) queue.add(node.right); // 右子节点入队 - } - return res; - } + [class]{}-[func]{levelOrder} ``` === "Rust" ```rust title="binary_tree_bfs.rs" - /* 层序遍历 */ - fn level_order(root: &Rc>) -> Vec { - // 初始化队列,加入根节点 - let mut que = VecDeque::new(); - que.push_back(root.clone()); - // 初始化一个列表,用于保存遍历序列 - let mut vec = Vec::new(); - - while let Some(node) = que.pop_front() { - // 队列出队 - vec.push(node.borrow().val); // 保存节点值 - if let Some(left) = node.borrow().left.as_ref() { - que.push_back(left.clone()); // 左子节点入队 - } - if let Some(right) = node.borrow().right.as_ref() { - que.push_back(right.clone()); // 右子节点入队 - }; - } - vec - } + [class]{}-[func]{level_order} ``` === "C" ```c title="binary_tree_bfs.c" - /* 层序遍历 */ - int *levelOrder(TreeNode *root, int *size) { - /* 辅助队列 */ - int front, rear; - int index, *arr; - TreeNode *node; - TreeNode **queue; - - /* 辅助队列 */ - queue = (TreeNode **)malloc(sizeof(TreeNode *) * MAX_SIZE); - // 队列指针 - front = 0, rear = 0; - // 加入根节点 - queue[rear++] = root; - // 初始化一个列表,用于保存遍历序列 - /* 辅助数组 */ - arr = (int *)malloc(sizeof(int) * MAX_SIZE); - // 数组指针 - index = 0; - while (front < rear) { - // 队列出队 - node = queue[front++]; - // 保存节点值 - arr[index++] = node->val; - if (node->left != NULL) { - // 左子节点入队 - queue[rear++] = node->left; - } - if (node->right != NULL) { - // 右子节点入队 - queue[rear++] = node->right; - } - } - // 更新数组长度的值 - *size = index; - arr = realloc(arr, sizeof(int) * (*size)); - - // 释放辅助数组空间 - free(queue); - return arr; - } + [class]{}-[func]{levelOrder} ``` === "Kotlin" ```kotlin title="binary_tree_bfs.kt" - /* 层序遍历 */ - fun levelOrder(root: TreeNode?): MutableList { - // 初始化队列,加入根节点 - val queue = LinkedList() - queue.add(root) - // 初始化一个列表,用于保存遍历序列 - val list = mutableListOf() - while (queue.isNotEmpty()) { - val node = queue.poll() // 队列出队 - list.add(node?._val!!) // 保存节点值 - if (node.left != null) - queue.offer(node.left) // 左子节点入队 - if (node.right != null) - queue.offer(node.right) // 右子节点入队 - } - return list - } + [class]{}-[func]{levelOrder} ``` === "Ruby" ```ruby title="binary_tree_bfs.rb" - ### 层序遍历 ### - def level_order(root) - # 初始化队列,加入根节点 - queue = [root] - # 初始化一个列表,用于保存遍历序列 - res = [] - while !queue.empty? - node = queue.shift # 队列出队 - res << node.val # 保存节点值 - queue << node.left unless node.left.nil? # 左子节点入队 - queue << node.right unless node.right.nil? # 右子节点入队 - end - res - end + [class]{}-[func]{level_order} ``` === "Zig" ```zig title="binary_tree_bfs.zig" - // 层序遍历 - fn levelOrder(comptime T: type, mem_allocator: std.mem.Allocator, root: *inc.TreeNode(T)) !std.ArrayList(T) { - // 初始化队列,加入根节点 - const L = std.TailQueue(*inc.TreeNode(T)); - var queue = L{}; - var root_node = try mem_allocator.create(L.Node); - root_node.data = root; - queue.append(root_node); - // 初始化一个列表,用于保存遍历序列 - var list = std.ArrayList(T).init(std.heap.page_allocator); - while (queue.len > 0) { - var queue_node = queue.popFirst().?; // 队列出队 - var node = queue_node.data; - try list.append(node.val); // 保存节点值 - if (node.left != null) { - var tmp_node = try mem_allocator.create(L.Node); - tmp_node.data = node.left.?; - queue.append(tmp_node); // 左子节点入队 - } - if (node.right != null) { - var tmp_node = try mem_allocator.create(L.Node); - tmp_node.data = node.right.?; - queue.append(tmp_node); // 右子节点入队 - } - } - return list; - } + [class]{}-[func]{levelOrder} ``` -??? pythontutor "Code Visualization" - -
- - ### 2.   Complexity analysis - **Time complexity is $O(n)$**: All nodes are visited once, using $O(n)$ time, where $n$ is the number of nodes. @@ -394,28 +159,28 @@ Depth-first search is usually implemented based on recursion: ```python title="binary_tree_dfs.py" def pre_order(root: TreeNode | None): - """前序遍历""" + """Pre-order traversal""" if root is None: return - # 访问优先级:根节点 -> 左子树 -> 右子树 + # Visit priority: root node -> left subtree -> right subtree res.append(root.val) pre_order(root=root.left) pre_order(root=root.right) def in_order(root: TreeNode | None): - """中序遍历""" + """In-order traversal""" if root is None: return - # 访问优先级:左子树 -> 根节点 -> 右子树 + # Visit priority: left subtree -> root node -> right subtree in_order(root=root.left) res.append(root.val) in_order(root=root.right) def post_order(root: TreeNode | None): - """后序遍历""" + """Post-order traversal""" if root is None: return - # 访问优先级:左子树 -> 右子树 -> 根节点 + # Visit priority: left subtree -> right subtree -> root node post_order(root=root.left) post_order(root=root.right) res.append(root.val) @@ -424,65 +189,41 @@ Depth-first search is usually implemented based on recursion: === "C++" ```cpp title="binary_tree_dfs.cpp" - /* 前序遍历 */ - void preOrder(TreeNode *root) { - if (root == nullptr) - return; - // 访问优先级:根节点 -> 左子树 -> 右子树 - vec.push_back(root->val); - preOrder(root->left); - preOrder(root->right); - } + [class]{}-[func]{preOrder} - /* 中序遍历 */ - void inOrder(TreeNode *root) { - if (root == nullptr) - return; - // 访问优先级:左子树 -> 根节点 -> 右子树 - inOrder(root->left); - vec.push_back(root->val); - inOrder(root->right); - } + [class]{}-[func]{inOrder} - /* 后序遍历 */ - void postOrder(TreeNode *root) { - if (root == nullptr) - return; - // 访问优先级:左子树 -> 右子树 -> 根节点 - postOrder(root->left); - postOrder(root->right); - vec.push_back(root->val); - } + [class]{}-[func]{postOrder} ``` === "Java" ```java title="binary_tree_dfs.java" - /* 前序遍历 */ + /* Pre-order traversal */ void preOrder(TreeNode root) { if (root == null) return; - // 访问优先级:根节点 -> 左子树 -> 右子树 + // Visit priority: root node -> left subtree -> right subtree list.add(root.val); preOrder(root.left); preOrder(root.right); } - /* 中序遍历 */ + /* In-order traversal */ void inOrder(TreeNode root) { if (root == null) return; - // 访问优先级:左子树 -> 根节点 -> 右子树 + // Visit priority: left subtree -> root node -> right subtree inOrder(root.left); list.add(root.val); inOrder(root.right); } - /* 后序遍历 */ + /* Post-order traversal */ void postOrder(TreeNode root) { if (root == null) return; - // 访问优先级:左子树 -> 右子树 -> 根节点 + // Visit priority: left subtree -> right subtree -> root node postOrder(root.left); postOrder(root.right); list.add(root.val); @@ -492,385 +233,113 @@ Depth-first search is usually implemented based on recursion: === "C#" ```csharp title="binary_tree_dfs.cs" - /* 前序遍历 */ - void PreOrder(TreeNode? root) { - if (root == null) return; - // 访问优先级:根节点 -> 左子树 -> 右子树 - list.Add(root.val!.Value); - PreOrder(root.left); - PreOrder(root.right); - } + [class]{binary_tree_dfs}-[func]{PreOrder} - /* 中序遍历 */ - void InOrder(TreeNode? root) { - if (root == null) return; - // 访问优先级:左子树 -> 根节点 -> 右子树 - InOrder(root.left); - list.Add(root.val!.Value); - InOrder(root.right); - } + [class]{binary_tree_dfs}-[func]{InOrder} - /* 后序遍历 */ - void PostOrder(TreeNode? root) { - if (root == null) return; - // 访问优先级:左子树 -> 右子树 -> 根节点 - PostOrder(root.left); - PostOrder(root.right); - list.Add(root.val!.Value); - } + [class]{binary_tree_dfs}-[func]{PostOrder} ``` === "Go" ```go title="binary_tree_dfs.go" - /* 前序遍历 */ - func preOrder(node *TreeNode) { - if node == nil { - return - } - // 访问优先级:根节点 -> 左子树 -> 右子树 - nums = append(nums, node.Val) - preOrder(node.Left) - preOrder(node.Right) - } + [class]{}-[func]{preOrder} - /* 中序遍历 */ - func inOrder(node *TreeNode) { - if node == nil { - return - } - // 访问优先级:左子树 -> 根节点 -> 右子树 - inOrder(node.Left) - nums = append(nums, node.Val) - inOrder(node.Right) - } + [class]{}-[func]{inOrder} - /* 后序遍历 */ - func postOrder(node *TreeNode) { - if node == nil { - return - } - // 访问优先级:左子树 -> 右子树 -> 根节点 - postOrder(node.Left) - postOrder(node.Right) - nums = append(nums, node.Val) - } + [class]{}-[func]{postOrder} ``` === "Swift" ```swift title="binary_tree_dfs.swift" - /* 前序遍历 */ - func preOrder(root: TreeNode?) { - guard let root = root else { - return - } - // 访问优先级:根节点 -> 左子树 -> 右子树 - list.append(root.val) - preOrder(root: root.left) - preOrder(root: root.right) - } + [class]{}-[func]{preOrder} - /* 中序遍历 */ - func inOrder(root: TreeNode?) { - guard let root = root else { - return - } - // 访问优先级:左子树 -> 根节点 -> 右子树 - inOrder(root: root.left) - list.append(root.val) - inOrder(root: root.right) - } + [class]{}-[func]{inOrder} - /* 后序遍历 */ - func postOrder(root: TreeNode?) { - guard let root = root else { - return - } - // 访问优先级:左子树 -> 右子树 -> 根节点 - postOrder(root: root.left) - postOrder(root: root.right) - list.append(root.val) - } + [class]{}-[func]{postOrder} ``` === "JS" ```javascript title="binary_tree_dfs.js" - /* 前序遍历 */ - function preOrder(root) { - if (root === null) return; - // 访问优先级:根节点 -> 左子树 -> 右子树 - list.push(root.val); - preOrder(root.left); - preOrder(root.right); - } + [class]{}-[func]{preOrder} - /* 中序遍历 */ - function inOrder(root) { - if (root === null) return; - // 访问优先级:左子树 -> 根节点 -> 右子树 - inOrder(root.left); - list.push(root.val); - inOrder(root.right); - } + [class]{}-[func]{inOrder} - /* 后序遍历 */ - function postOrder(root) { - if (root === null) return; - // 访问优先级:左子树 -> 右子树 -> 根节点 - postOrder(root.left); - postOrder(root.right); - list.push(root.val); - } + [class]{}-[func]{postOrder} ``` === "TS" ```typescript title="binary_tree_dfs.ts" - /* 前序遍历 */ - function preOrder(root: TreeNode | null): void { - if (root === null) { - return; - } - // 访问优先级:根节点 -> 左子树 -> 右子树 - list.push(root.val); - preOrder(root.left); - preOrder(root.right); - } + [class]{}-[func]{preOrder} - /* 中序遍历 */ - function inOrder(root: TreeNode | null): void { - if (root === null) { - return; - } - // 访问优先级:左子树 -> 根节点 -> 右子树 - inOrder(root.left); - list.push(root.val); - inOrder(root.right); - } + [class]{}-[func]{inOrder} - /* 后序遍历 */ - function postOrder(root: TreeNode | null): void { - if (root === null) { - return; - } - // 访问优先级:左子树 -> 右子树 -> 根节点 - postOrder(root.left); - postOrder(root.right); - list.push(root.val); - } + [class]{}-[func]{postOrder} ``` === "Dart" ```dart title="binary_tree_dfs.dart" - /* 前序遍历 */ - void preOrder(TreeNode? node) { - if (node == null) return; - // 访问优先级:根节点 -> 左子树 -> 右子树 - list.add(node.val); - preOrder(node.left); - preOrder(node.right); - } + [class]{}-[func]{preOrder} - /* 中序遍历 */ - void inOrder(TreeNode? node) { - if (node == null) return; - // 访问优先级:左子树 -> 根节点 -> 右子树 - inOrder(node.left); - list.add(node.val); - inOrder(node.right); - } + [class]{}-[func]{inOrder} - /* 后序遍历 */ - void postOrder(TreeNode? node) { - if (node == null) return; - // 访问优先级:左子树 -> 右子树 -> 根节点 - postOrder(node.left); - postOrder(node.right); - list.add(node.val); - } + [class]{}-[func]{postOrder} ``` === "Rust" ```rust title="binary_tree_dfs.rs" - /* 前序遍历 */ - fn pre_order(root: Option<&Rc>>) -> Vec { - let mut result = vec![]; + [class]{}-[func]{pre_order} - if let Some(node) = root { - // 访问优先级:根节点 -> 左子树 -> 右子树 - result.push(node.borrow().val); - result.extend(pre_order(node.borrow().left.as_ref())); - result.extend(pre_order(node.borrow().right.as_ref())); - } - result - } + [class]{}-[func]{in_order} - /* 中序遍历 */ - fn in_order(root: Option<&Rc>>) -> Vec { - let mut result = vec![]; - - if let Some(node) = root { - // 访问优先级:左子树 -> 根节点 -> 右子树 - result.extend(in_order(node.borrow().left.as_ref())); - result.push(node.borrow().val); - result.extend(in_order(node.borrow().right.as_ref())); - } - result - } - - /* 后序遍历 */ - fn post_order(root: Option<&Rc>>) -> Vec { - let mut result = vec![]; - - if let Some(node) = root { - // 访问优先级:左子树 -> 右子树 -> 根节点 - result.extend(post_order(node.borrow().left.as_ref())); - result.extend(post_order(node.borrow().right.as_ref())); - result.push(node.borrow().val); - } - result - } + [class]{}-[func]{post_order} ``` === "C" ```c title="binary_tree_dfs.c" - /* 前序遍历 */ - void preOrder(TreeNode *root, int *size) { - if (root == NULL) - return; - // 访问优先级:根节点 -> 左子树 -> 右子树 - arr[(*size)++] = root->val; - preOrder(root->left, size); - preOrder(root->right, size); - } + [class]{}-[func]{preOrder} - /* 中序遍历 */ - void inOrder(TreeNode *root, int *size) { - if (root == NULL) - return; - // 访问优先级:左子树 -> 根节点 -> 右子树 - inOrder(root->left, size); - arr[(*size)++] = root->val; - inOrder(root->right, size); - } + [class]{}-[func]{inOrder} - /* 后序遍历 */ - void postOrder(TreeNode *root, int *size) { - if (root == NULL) - return; - // 访问优先级:左子树 -> 右子树 -> 根节点 - postOrder(root->left, size); - postOrder(root->right, size); - arr[(*size)++] = root->val; - } + [class]{}-[func]{postOrder} ``` === "Kotlin" ```kotlin title="binary_tree_dfs.kt" - /* 前序遍历 */ - fun preOrder(root: TreeNode?) { - if (root == null) return - // 访问优先级:根节点 -> 左子树 -> 右子树 - list.add(root._val) - preOrder(root.left) - preOrder(root.right) - } + [class]{}-[func]{preOrder} - /* 中序遍历 */ - fun inOrder(root: TreeNode?) { - if (root == null) return - // 访问优先级:左子树 -> 根节点 -> 右子树 - inOrder(root.left) - list.add(root._val) - inOrder(root.right) - } + [class]{}-[func]{inOrder} - /* 后序遍历 */ - fun postOrder(root: TreeNode?) { - if (root == null) return - // 访问优先级:左子树 -> 右子树 -> 根节点 - postOrder(root.left) - postOrder(root.right) - list.add(root._val) - } + [class]{}-[func]{postOrder} ``` === "Ruby" ```ruby title="binary_tree_dfs.rb" - ### 前序遍历 ### - def pre_order(root) - return if root.nil? + [class]{}-[func]{pre_order} - # 访问优先级:根节点 -> 左子树 -> 右子树 - $res << root.val - pre_order(root.left) - pre_order(root.right) - end + [class]{}-[func]{in_order} - ### 中序遍历 ### - def in_order(root) - return if root.nil? - - # 访问优先级:左子树 -> 根节点 -> 右子树 - in_order(root.left) - $res << root.val - in_order(root.right) - end - - ### 后序遍历 ### - def post_order(root) - return if root.nil? - - # 访问优先级:左子树 -> 右子树 -> 根节点 - post_order(root.left) - post_order(root.right) - $res << root.val - end + [class]{}-[func]{post_order} ``` === "Zig" ```zig title="binary_tree_dfs.zig" - // 前序遍历 - fn preOrder(comptime T: type, root: ?*inc.TreeNode(T)) !void { - if (root == null) return; - // 访问优先级:根节点 -> 左子树 -> 右子树 - try list.append(root.?.val); - try preOrder(T, root.?.left); - try preOrder(T, root.?.right); - } + [class]{}-[func]{preOrder} - // 中序遍历 - fn inOrder(comptime T: type, root: ?*inc.TreeNode(T)) !void { - if (root == null) return; - // 访问优先级:左子树 -> 根节点 -> 右子树 - try inOrder(T, root.?.left); - try list.append(root.?.val); - try inOrder(T, root.?.right); - } + [class]{}-[func]{inOrder} - // 后序遍历 - fn postOrder(comptime T: type, root: ?*inc.TreeNode(T)) !void { - if (root == null) return; - // 访问优先级:左子树 -> 右子树 -> 根节点 - try postOrder(T, root.?.left); - try postOrder(T, root.?.right); - try list.append(root.?.val); - } + [class]{}-[func]{postOrder} ``` -??? pythontutor "Code Visualization" - -
- - !!! tip Depth-first search can also be implemented based on iteration, interested readers can study this on their own.