From 3d2d669b43532cbb4cd4f7f70160603ce7ca2c34 Mon Sep 17 00:00:00 2001 From: krahets Date: Fri, 6 Oct 2023 14:10:18 +0800 Subject: [PATCH] build --- zh/chapter_array_and_linkedlist/array.md | 739 ++++- .../linked_list.md | 477 ++- zh/chapter_array_and_linkedlist/list.md | 1204 +++++++- .../backtracking_algorithm.md | 1060 ++++++- zh/chapter_backtracking/n_queens_problem.md | 506 ++- .../permutations_problem.md | 706 ++++- zh/chapter_backtracking/subset_sum_problem.md | 1188 ++++++- .../iteration_and_recursion.md | 1044 ++++++- .../space_complexity.md | 971 +++++- .../time_complexity.md | 1710 +++++++++- .../binary_search_recur.md | 265 +- .../build_binary_tree_problem.md | 290 +- .../hanota_problem.md | 301 +- .../dp_problem_features.md | 557 +++- .../dp_solution_pipeline.md | 935 +++++- .../edit_distance_problem.md | 617 +++- .../intro_to_dynamic_programming.md | 1108 ++++++- .../knapsack_problem.md | 881 +++++- .../unbounded_knapsack_problem.md | 1486 ++++++++- zh/chapter_graph/graph_operations.md | 1943 +++++++++++- zh/chapter_graph/graph_traversal.md | 606 +++- .../fractional_knapsack_problem.md | 380 ++- zh/chapter_greedy/greedy_algorithm.md | 214 +- zh/chapter_greedy/max_capacity_problem.md | 232 +- .../max_product_cutting_problem.md | 226 +- zh/chapter_hashing/hash_algorithm.md | 458 ++- zh/chapter_hashing/hash_collision.md | 2489 ++++++++++++++- zh/chapter_hashing/hash_map.md | 1077 ++++++- zh/chapter_heap/build_heap.md | 127 +- zh/chapter_heap/heap.md | 949 +++++- zh/chapter_heap/top_k.md | 178 +- zh/chapter_searching/binary_search.md | 466 ++- zh/chapter_searching/binary_search_edge.md | 282 +- .../binary_search_insertion.md | 385 ++- .../replace_linear_by_hashing.md | 367 ++- zh/chapter_sorting/bubble_sort.md | 413 ++- zh/chapter_sorting/bucket_sort.md | 322 +- zh/chapter_sorting/counting_sort.md | 605 +++- zh/chapter_sorting/heap_sort.md | 422 ++- zh/chapter_sorting/insertion_sort.md | 173 +- zh/chapter_sorting/merge_sort.md | 513 ++- zh/chapter_sorting/quick_sort.md | 1034 ++++++- zh/chapter_sorting/radix_sort.md | 606 +++- zh/chapter_sorting/selection_sort.md | 183 +- zh/chapter_stack_and_queue/deque.md | 2738 ++++++++++++++++- zh/chapter_stack_and_queue/queue.md | 1664 +++++++++- zh/chapter_stack_and_queue/stack.md | 1240 +++++++- .../array_representation_of_tree.md | 966 +++++- zh/chapter_tree/avl_tree.md | 1792 ++++++++++- zh/chapter_tree/binary_search_tree.md | 1210 +++++++- zh/chapter_tree/binary_tree_traversal.md | 617 +++- 51 files changed, 38928 insertions(+), 1994 deletions(-) diff --git a/zh/chapter_array_and_linkedlist/array.md b/zh/chapter_array_and_linkedlist/array.md index f35370341..c7e95397f 100755 --- a/zh/chapter_array_and_linkedlist/array.md +++ b/zh/chapter_array_and_linkedlist/array.md @@ -134,73 +134,157 @@ comments: true === "Python" ```python title="array.py" - [class]{}-[func]{random_access} + def random_access(nums: list[int]) -> int: + """随机访问元素""" + # 在区间 [0, len(nums)-1] 中随机抽取一个数字 + random_index = random.randint(0, len(nums) - 1) + # 获取并返回随机元素 + random_num = nums[random_index] + return random_num ``` === "C++" ```cpp title="array.cpp" - [class]{}-[func]{randomAccess} + /* 随机访问元素 */ + int randomAccess(int *nums, int size) { + // 在区间 [0, size) 中随机抽取一个数字 + int randomIndex = rand() % size; + // 获取并返回随机元素 + int randomNum = nums[randomIndex]; + return randomNum; + } ``` === "Java" ```java title="array.java" - [class]{array}-[func]{randomAccess} + /* 随机访问元素 */ + int randomAccess(int[] nums) { + // 在区间 [0, nums.length) 中随机抽取一个数字 + int randomIndex = ThreadLocalRandom.current().nextInt(0, nums.length); + // 获取并返回随机元素 + int randomNum = nums[randomIndex]; + return randomNum; + } ``` === "C#" ```csharp title="array.cs" - [class]{array}-[func]{randomAccess} + /* 随机访问元素 */ + int randomAccess(int[] nums) { + Random random = new(); + // 在区间 [0, nums.Length) 中随机抽取一个数字 + int randomIndex = random.Next(nums.Length); + // 获取并返回随机元素 + int randomNum = nums[randomIndex]; + return randomNum; + } ``` === "Go" ```go title="array.go" - [class]{}-[func]{randomAccess} + /* 随机访问元素 */ + func randomAccess(nums []int) (randomNum int) { + // 在区间 [0, nums.length) 中随机抽取一个数字 + randomIndex := rand.Intn(len(nums)) + // 获取并返回随机元素 + randomNum = nums[randomIndex] + return + } ``` === "Swift" ```swift title="array.swift" - [class]{}-[func]{randomAccess} + /* 随机访问元素 */ + func randomAccess(nums: [Int]) -> Int { + // 在区间 [0, nums.count) 中随机抽取一个数字 + let randomIndex = nums.indices.randomElement()! + // 获取并返回随机元素 + let randomNum = nums[randomIndex] + return randomNum + } ``` === "JS" ```javascript title="array.js" - [class]{}-[func]{randomAccess} + /* 随机访问元素 */ + function randomAccess(nums) { + // 在区间 [0, nums.length) 中随机抽取一个数字 + const random_index = Math.floor(Math.random() * nums.length); + // 获取并返回随机元素 + const random_num = nums[random_index]; + return random_num; + } ``` === "TS" ```typescript title="array.ts" - [class]{}-[func]{randomAccess} + /* 随机访问元素 */ + 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; + } ``` === "Dart" ```dart title="array.dart" - [class]{}-[func]{randomAccess} + /* 随机访问元素 */ + int randomAccess(List nums) { + // 在区间 [0, nums.length) 中随机抽取一个数字 + int randomIndex = Random().nextInt(nums.length); + // 获取并返回随机元素 + int randomNum = nums[randomIndex]; + return randomNum; + } ``` === "Rust" ```rust title="array.rs" - [class]{}-[func]{random_access} + /* 随机访问元素 */ + 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 + } ``` === "C" ```c title="array.c" - [class]{}-[func]{randomAccess} + /* 随机访问元素 */ + int randomAccess(int *nums, int size) { + // 在区间 [0, size) 中随机抽取一个数字 + int randomIndex = rand() % size; + // 获取并返回随机元素 + int randomNum = nums[randomIndex]; + return randomNum; + } ``` === "Zig" ```zig title="array.zig" - [class]{}-[func]{randomAccess} + // 随机访问元素 + fn randomAccess(nums: []i32) i32 { + // 在区间 [0, nums.len) 中随机抽取一个整数 + var randomIndex = std.crypto.random.intRangeLessThan(usize, 0, nums.len); + // 获取并返回随机元素 + var randomNum = nums[randomIndex]; + return randomNum; + } ``` ### 3.   插入元素 @@ -216,73 +300,168 @@ comments: true === "Python" ```python title="array.py" - [class]{}-[func]{insert} + def insert(nums: list[int], num: int, index: int): + """在数组的索引 index 处插入元素 num""" + # 把索引 index 以及之后的所有元素向后移动一位 + for i in range(len(nums) - 1, index, -1): + nums[i] = nums[i - 1] + # 将 num 赋给 index 处元素 + nums[index] = num ``` === "C++" ```cpp title="array.cpp" - [class]{}-[func]{insert} + /* 在数组的索引 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; + } ``` === "Java" ```java title="array.java" - [class]{array}-[func]{insert} + /* 在数组的索引 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; + } ``` === "C#" ```csharp title="array.cs" - [class]{array}-[func]{insert} + /* 在数组的索引 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; + } ``` === "Go" ```go title="array.go" - [class]{}-[func]{insert} + /* 在数组的索引 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 + } ``` === "Swift" ```swift title="array.swift" - [class]{}-[func]{insert} + /* 在数组的索引 index 处插入元素 num */ + func insert(nums: inout [Int], num: Int, index: Int) { + // 把索引 index 以及之后的所有元素向后移动一位 + for i in sequence(first: nums.count - 1, next: { $0 > index + 1 ? $0 - 1 : nil }) { + nums[i] = nums[i - 1] + } + // 将 num 赋给 index 处元素 + nums[index] = num + } ``` === "JS" ```javascript title="array.js" - [class]{}-[func]{insert} + /* 在数组的索引 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; + } ``` === "TS" ```typescript title="array.ts" - [class]{}-[func]{insert} + /* 在数组的索引 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; + } ``` === "Dart" ```dart title="array.dart" - [class]{}-[func]{insert} + /* 在数组的索引 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; + } ``` === "Rust" ```rust title="array.rs" - [class]{}-[func]{insert} + /* 在数组的索引 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; + } ``` === "C" ```c title="array.c" - [class]{}-[func]{insert} + /* 在数组的索引 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; + } ``` === "Zig" ```zig title="array.zig" - [class]{}-[func]{insert} + // 在数组的索引 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; + } ``` ### 4.   删除元素 @@ -298,73 +477,146 @@ comments: true === "Python" ```python title="array.py" - [class]{}-[func]{remove} + def remove(nums: list[int], index: int): + """删除索引 index 处元素""" + # 把索引 index 之后的所有元素向前移动一位 + for i in range(index, len(nums) - 1): + nums[i] = nums[i + 1] ``` === "C++" ```cpp title="array.cpp" - [class]{}-[func]{remove} + /* 删除索引 index 处元素 */ + void remove(int *nums, int size, int index) { + // 把索引 index 之后的所有元素向前移动一位 + for (int i = index; i < size - 1; i++) { + nums[i] = nums[i + 1]; + } + } ``` === "Java" ```java title="array.java" - [class]{array}-[func]{remove} + /* 删除索引 index 处元素 */ + void remove(int[] nums, int index) { + // 把索引 index 之后的所有元素向前移动一位 + for (int i = index; i < nums.length - 1; i++) { + nums[i] = nums[i + 1]; + } + } ``` === "C#" ```csharp title="array.cs" - [class]{array}-[func]{remove} + /* 删除索引 index 处元素 */ + void remove(int[] nums, int index) { + // 把索引 index 之后的所有元素向前移动一位 + for (int i = index; i < nums.Length - 1; i++) { + nums[i] = nums[i + 1]; + } + } ``` === "Go" ```go title="array.go" - [class]{}-[func]{remove} + /* 删除索引 index 处元素 */ + func remove(nums []int, index int) { + // 把索引 index 之后的所有元素向前移动一位 + for i := index; i < len(nums)-1; i++ { + nums[i] = nums[i+1] + } + } ``` === "Swift" ```swift title="array.swift" - [class]{}-[func]{remove} + /* 删除索引 index 处元素 */ + func remove(nums: inout [Int], index: Int) { + let count = nums.count + // 把索引 index 之后的所有元素向前移动一位 + for i in sequence(first: index, next: { $0 < count - 1 - 1 ? $0 + 1 : nil }) { + nums[i] = nums[i + 1] + } + } ``` === "JS" ```javascript title="array.js" - [class]{}-[func]{remove} + /* 删除索引 index 处元素 */ + function remove(nums, index) { + // 把索引 index 之后的所有元素向前移动一位 + for (let i = index; i < nums.length - 1; i++) { + nums[i] = nums[i + 1]; + } + } ``` === "TS" ```typescript title="array.ts" - [class]{}-[func]{remove} + /* 删除索引 index 处元素 */ + function remove(nums: number[], index: number): void { + // 把索引 index 之后的所有元素向前移动一位 + for (let i = index; i < nums.length - 1; i++) { + nums[i] = nums[i + 1]; + } + } ``` === "Dart" ```dart title="array.dart" - [class]{}-[func]{remove} + /* 删除索引 index 处元素 */ + void remove(List nums, int index) { + // 把索引 index 之后的所有元素向前移动一位 + for (var i = index; i < nums.length - 1; i++) { + nums[i] = nums[i + 1]; + } + } ``` === "Rust" ```rust title="array.rs" - [class]{}-[func]{remove} + /* 删除索引 index 处元素 */ + fn remove(nums: &mut Vec, index: usize) { + // 把索引 index 之后的所有元素向前移动一位 + for i in index..nums.len() - 1 { + nums[i] = nums[i + 1]; + } + } ``` === "C" ```c title="array.c" - [class]{}-[func]{removeItem} + /* 删除索引 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]; + } + } ``` === "Zig" ```zig title="array.zig" - [class]{}-[func]{remove} + // 删除索引 index 处元素 + fn remove(nums: []i32, index: usize) void { + // 把索引 index 之后的所有元素向前移动一位 + var i = index; + while (i < nums.len - 1) : (i += 1) { + nums[i] = nums[i + 1]; + } + } ``` 总的来看,数组的插入与删除操作有以下缺点。 @@ -380,73 +632,204 @@ comments: true === "Python" ```python title="array.py" - [class]{}-[func]{traverse} + def traverse(nums: list[int]): + """遍历数组""" + count = 0 + # 通过索引遍历数组 + for i in range(len(nums)): + count += 1 + # 直接遍历数组 + for num in nums: + count += 1 + # 同时遍历数据索引和元素 + for i, num in enumerate(nums): + count += 1 ``` === "C++" ```cpp title="array.cpp" - [class]{}-[func]{traverse} + /* 遍历数组 */ + void traverse(int *nums, int size) { + int count = 0; + // 通过索引遍历数组 + for (int i = 0; i < size; i++) { + count++; + } + } ``` === "Java" ```java title="array.java" - [class]{array}-[func]{traverse} + /* 遍历数组 */ + void traverse(int[] nums) { + int count = 0; + // 通过索引遍历数组 + for (int i = 0; i < nums.length; i++) { + count++; + } + // 直接遍历数组 + for (int num : nums) { + count++; + } + } ``` === "C#" ```csharp title="array.cs" - [class]{array}-[func]{traverse} + /* 遍历数组 */ + void traverse(int[] nums) { + int count = 0; + // 通过索引遍历数组 + for (int i = 0; i < nums.Length; i++) { + count++; + } + // 直接遍历数组 + foreach (int num in nums) { + count++; + } + } ``` === "Go" ```go title="array.go" - [class]{}-[func]{traverse} + /* 遍历数组 */ + func traverse(nums []int) { + count := 0 + // 通过索引遍历数组 + for i := 0; i < len(nums); i++ { + count++ + } + count = 0 + // 直接遍历数组 + for range nums { + count++ + } + } ``` === "Swift" ```swift title="array.swift" - [class]{}-[func]{traverse} + /* 遍历数组 */ + func traverse(nums: [Int]) { + var count = 0 + // 通过索引遍历数组 + for _ in nums.indices { + count += 1 + } + // 直接遍历数组 + for _ in nums { + count += 1 + } + } ``` === "JS" ```javascript title="array.js" - [class]{}-[func]{traverse} + /* 遍历数组 */ + function traverse(nums) { + let count = 0; + // 通过索引遍历数组 + for (let i = 0; i < nums.length; i++) { + count++; + } + // 直接遍历数组 + for (const num of nums) { + count += 1; + } + } ``` === "TS" ```typescript title="array.ts" - [class]{}-[func]{traverse} + /* 遍历数组 */ + function traverse(nums: number[]): void { + let count = 0; + // 通过索引遍历数组 + for (let i = 0; i < nums.length; i++) { + count++; + } + // 直接遍历数组 + for (const num of nums) { + count += 1; + } + } ``` === "Dart" ```dart title="array.dart" - [class]{}-[func]{traverse} + /* 遍历数组元素 */ + void traverse(List nums) { + var count = 0; + // 通过索引遍历数组 + for (var i = 0; i < nums.length; i++) { + count++; + } + // 直接遍历数组 + for (var num in nums) { + count++; + } + // 通过 forEach 方法遍历数组 + nums.forEach((element) { + count++; + }); + } ``` === "Rust" ```rust title="array.rs" - [class]{}-[func]{traverse} + /* 遍历数组 */ + fn traverse(nums: &[i32]) { + let mut _count = 0; + // 通过索引遍历数组 + for _ in 0..nums.len() { + _count += 1; + } + // 直接遍历数组 + for _ in nums { + _count += 1; + } + } ``` === "C" ```c title="array.c" - [class]{}-[func]{traverse} + /* 遍历数组 */ + void traverse(int *nums, int size) { + int count = 0; + // 通过索引遍历数组 + for (int i = 0; i < size; i++) { + count++; + } + } ``` === "Zig" ```zig title="array.zig" - [class]{}-[func]{traverse} + // 遍历数组 + fn traverse(nums: []i32) void { + var count: i32 = 0; + // 通过索引遍历数组 + var i: i32 = 0; + while (i < nums.len) : (i += 1) { + count += 1; + } + count = 0; + // 直接遍历数组 + for (nums) |_| { + count += 1; + } + } ``` ### 6.   查找元素 @@ -458,73 +841,158 @@ comments: true === "Python" ```python title="array.py" - [class]{}-[func]{find} + def find(nums: list[int], target: int) -> int: + """在数组中查找指定元素""" + for i in range(len(nums)): + if nums[i] == target: + return i + return -1 ``` === "C++" ```cpp title="array.cpp" - [class]{}-[func]{find} + /* 在数组中查找指定元素 */ + int find(int *nums, int size, int target) { + for (int i = 0; i < size; i++) { + if (nums[i] == target) + return i; + } + return -1; + } ``` === "Java" ```java title="array.java" - [class]{array}-[func]{find} + /* 在数组中查找指定元素 */ + int find(int[] nums, int target) { + for (int i = 0; i < nums.length; i++) { + if (nums[i] == target) + return i; + } + return -1; + } ``` === "C#" ```csharp title="array.cs" - [class]{array}-[func]{find} + /* 在数组中查找指定元素 */ + int find(int[] nums, int target) { + for (int i = 0; i < nums.Length; i++) { + if (nums[i] == target) + return i; + } + return -1; + } ``` === "Go" ```go title="array.go" - [class]{}-[func]{find} + /* 在数组中查找指定元素 */ + 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 + } ``` === "Swift" ```swift title="array.swift" - [class]{}-[func]{find} + /* 在数组中查找指定元素 */ + func find(nums: [Int], target: Int) -> Int { + for i in nums.indices { + if nums[i] == target { + return i + } + } + return -1 + } ``` === "JS" ```javascript title="array.js" - [class]{}-[func]{find} + /* 在数组中查找指定元素 */ + function find(nums, target) { + for (let i = 0; i < nums.length; i++) { + if (nums[i] === target) return i; + } + return -1; + } ``` === "TS" ```typescript title="array.ts" - [class]{}-[func]{find} + /* 在数组中查找指定元素 */ + function find(nums: number[], target: number): number { + for (let i = 0; i < nums.length; i++) { + if (nums[i] === target) { + return i; + } + } + return -1; + } ``` === "Dart" ```dart title="array.dart" - [class]{}-[func]{find} + /* 在数组中查找指定元素 */ + int find(List nums, int target) { + for (var i = 0; i < nums.length; i++) { + if (nums[i] == target) return i; + } + return -1; + } ``` === "Rust" ```rust title="array.rs" - [class]{}-[func]{find} + /* 在数组中查找指定元素 */ + fn find(nums: &[i32], target: i32) -> Option { + for i in 0..nums.len() { + if nums[i] == target { + return Some(i); + } + } + None + } ``` === "C" ```c title="array.c" - [class]{}-[func]{find} + /* 在数组中查找指定元素 */ + int find(int *nums, int size, int target) { + for (int i = 0; i < size; i++) { + if (nums[i] == target) + return i; + } + return -1; + } ``` === "Zig" ```zig title="array.zig" - [class]{}-[func]{find} + // 在数组中查找指定元素 + fn find(nums: []i32, target: i32) i32 { + for (nums, 0..) |num, i| { + if (num == target) return @intCast(i); + } + return -1; + } ``` ### 7.   扩容数组 @@ -536,73 +1004,200 @@ comments: true === "Python" ```python title="array.py" - [class]{}-[func]{extend} + def extend(nums: list[int], enlarge: int) -> list[int]: + """扩展数组长度""" + # 初始化一个扩展长度后的数组 + res = [0] * (len(nums) + enlarge) + # 将原数组中的所有元素复制到新数组 + for i in range(len(nums)): + res[i] = nums[i] + # 返回扩展后的新数组 + return res ``` === "C++" ```cpp title="array.cpp" - [class]{}-[func]{extend} + /* 扩展数组长度 */ + 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; + } ``` === "Java" ```java title="array.java" - [class]{array}-[func]{extend} + /* 扩展数组长度 */ + 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; + } ``` === "C#" ```csharp title="array.cs" - [class]{array}-[func]{extend} + /* 扩展数组长度 */ + 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; + } ``` === "Go" ```go title="array.go" - [class]{}-[func]{extend} + /* 扩展数组长度 */ + func extend(nums []int, enlarge int) []int { + // 初始化一个扩展长度后的数组 + res := make([]int, len(nums)+enlarge) + // 将原数组中的所有元素复制到新数组 + for i, num := range nums { + res[i] = num + } + // 返回扩展后的新数组 + return res + } ``` === "Swift" ```swift title="array.swift" - [class]{}-[func]{extend} + /* 扩展数组长度 */ + 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 + } ``` === "JS" ```javascript title="array.js" - [class]{}-[func]{extend} + /* 扩展数组长度 */ + // 请注意,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; + } ``` === "TS" ```typescript title="array.ts" - [class]{}-[func]{extend} + /* 扩展数组长度 */ + // 请注意,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; + } ``` === "Dart" ```dart title="array.dart" - [class]{}-[func]{extend} + /* 扩展数组长度 */ + 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; + } ``` === "Rust" ```rust title="array.rs" - [class]{}-[func]{extend} + /* 扩展数组长度 */ + 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 + } ``` === "C" ```c title="array.c" - [class]{}-[func]{extend} + /* 扩展数组长度 */ + 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; + } ``` === "Zig" ```zig title="array.zig" - [class]{}-[func]{extend} + // 扩展数组长度 + 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; + } ``` ## 4.1.2   数组优点与局限性 diff --git a/zh/chapter_array_and_linkedlist/linked_list.md b/zh/chapter_array_and_linkedlist/linked_list.md index ae0d80c4d..d2314aa01 100755 --- a/zh/chapter_array_and_linkedlist/linked_list.md +++ b/zh/chapter_array_and_linkedlist/linked_list.md @@ -416,73 +416,133 @@ comments: true === "Python" ```python title="linked_list.py" - [class]{}-[func]{insert} + def insert(n0: ListNode, P: ListNode): + """在链表的节点 n0 之后插入节点 P""" + n1 = n0.next + P.next = n1 + n0.next = P ``` === "C++" ```cpp title="linked_list.cpp" - [class]{}-[func]{insert} + /* 在链表的节点 n0 之后插入节点 P */ + void insert(ListNode *n0, ListNode *P) { + ListNode *n1 = n0->next; + P->next = n1; + n0->next = P; + } ``` === "Java" ```java title="linked_list.java" - [class]{linked_list}-[func]{insert} + /* 在链表的节点 n0 之后插入节点 P */ + void insert(ListNode n0, ListNode P) { + ListNode n1 = n0.next; + P.next = n1; + n0.next = P; + } ``` === "C#" ```csharp title="linked_list.cs" - [class]{linked_list}-[func]{insert} + /* 在链表的节点 n0 之后插入节点 P */ + void insert(ListNode n0, ListNode P) { + ListNode? n1 = n0.next; + P.next = n1; + n0.next = P; + } ``` === "Go" ```go title="linked_list.go" - [class]{}-[func]{insertNode} + /* 在链表的节点 n0 之后插入节点 P */ + func insertNode(n0 *ListNode, P *ListNode) { + n1 := n0.Next + P.Next = n1 + n0.Next = P + } ``` === "Swift" ```swift title="linked_list.swift" - [class]{}-[func]{insert} + /* 在链表的节点 n0 之后插入节点 P */ + func insert(n0: ListNode, P: ListNode) { + let n1 = n0.next + P.next = n1 + n0.next = P + } ``` === "JS" ```javascript title="linked_list.js" - [class]{}-[func]{insert} + /* 在链表的节点 n0 之后插入节点 P */ + function insert(n0, P) { + const n1 = n0.next; + P.next = n1; + n0.next = P; + } ``` === "TS" ```typescript title="linked_list.ts" - [class]{}-[func]{insert} + /* 在链表的节点 n0 之后插入节点 P */ + function insert(n0: ListNode, P: ListNode): void { + const n1 = n0.next; + P.next = n1; + n0.next = P; + } ``` === "Dart" ```dart title="linked_list.dart" - [class]{}-[func]{insert} + /* 在链表的节点 n0 之后插入节点 P */ + void insert(ListNode n0, ListNode P) { + ListNode? n1 = n0.next; + P.next = n1; + n0.next = P; + } ``` === "Rust" ```rust title="linked_list.rs" - [class]{}-[func]{insert} + /* 在链表的节点 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); + } ``` === "C" ```c title="linked_list.c" - [class]{}-[func]{insert} + /* 在链表的节点 n0 之后插入节点 P */ + void insert(ListNode *n0, ListNode *P) { + ListNode *n1 = n0->next; + P->next = n1; + n0->next = P; + } ``` === "Zig" ```zig title="linked_list.zig" - [class]{}-[func]{insert} + // 在链表的节点 n0 之后插入节点 P + fn insert(n0: ?*inc.ListNode(i32), P: ?*inc.ListNode(i32)) void { + var n1 = n0.?.next; + P.?.next = n1; + n0.?.next = P; + } ``` ### 3.   删除节点 @@ -498,73 +558,176 @@ comments: true === "Python" ```python title="linked_list.py" - [class]{}-[func]{remove} + def remove(n0: ListNode): + """删除链表的节点 n0 之后的首个节点""" + if not n0.next: + return + # n0 -> P -> n1 + P = n0.next + n1 = P.next + n0.next = n1 ``` === "C++" ```cpp title="linked_list.cpp" - [class]{}-[func]{remove} + /* 删除链表的节点 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; + } ``` === "Java" ```java title="linked_list.java" - [class]{linked_list}-[func]{remove} + /* 删除链表的节点 n0 之后的首个节点 */ + void remove(ListNode n0) { + if (n0.next == null) + return; + // n0 -> P -> n1 + ListNode P = n0.next; + ListNode n1 = P.next; + n0.next = n1; + } ``` === "C#" ```csharp title="linked_list.cs" - [class]{linked_list}-[func]{remove} + /* 删除链表的节点 n0 之后的首个节点 */ + void remove(ListNode n0) { + if (n0.next == null) + return; + // n0 -> P -> n1 + ListNode P = n0.next; + ListNode? n1 = P.next; + n0.next = n1; + } ``` === "Go" ```go title="linked_list.go" - [class]{}-[func]{removeNode} + /* 删除链表的节点 n0 之后的首个节点 */ + func removeNode(n0 *ListNode) { + if n0.Next == nil { + return + } + // n0 -> P -> n1 + P := n0.Next + n1 := P.Next + n0.Next = n1 + } ``` === "Swift" ```swift title="linked_list.swift" - [class]{}-[func]{remove} + /* 删除链表的节点 n0 之后的首个节点 */ + func remove(n0: ListNode) { + if n0.next == nil { + return + } + // n0 -> P -> n1 + let P = n0.next + let n1 = P?.next + n0.next = n1 + P?.next = nil + } ``` === "JS" ```javascript title="linked_list.js" - [class]{}-[func]{remove} + /* 删除链表的节点 n0 之后的首个节点 */ + function remove(n0) { + if (!n0.next) return; + // n0 -> P -> n1 + const P = n0.next; + const n1 = P.next; + n0.next = n1; + } ``` === "TS" ```typescript title="linked_list.ts" - [class]{}-[func]{remove} + /* 删除链表的节点 n0 之后的首个节点 */ + function remove(n0: ListNode): void { + if (!n0.next) { + return; + } + // n0 -> P -> n1 + const P = n0.next; + const n1 = P.next; + n0.next = n1; + } ``` === "Dart" ```dart title="linked_list.dart" - [class]{}-[func]{remove} + /* 删除链表的节点 n0 之后的首个节点 */ + void remove(ListNode n0) { + if (n0.next == null) return; + // n0 -> P -> n1 + ListNode P = n0.next!; + ListNode? n1 = P.next; + n0.next = n1; + } ``` === "Rust" ```rust title="linked_list.rs" - [class]{}-[func]{remove} + /* 删除链表的节点 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; + } + } ``` === "C" ```c title="linked_list.c" - [class]{}-[func]{removeNode} + /* 删除链表的节点 n0 之后的首个节点 */ + // 注意:stdio.h 占用了 remove 关键词 + void removeNode(ListNode *n0) { + if (!n0->next) + return; + // n0 -> P -> n1 + ListNode *P = n0->next; + ListNode *n1 = P->next; + n0->next = n1; + // 释放内存 + free(P); + } ``` === "Zig" ```zig title="linked_list.zig" - [class]{}-[func]{remove} + // 删除链表的节点 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; + } ``` ### 4.   访问节点 @@ -574,73 +737,170 @@ comments: true === "Python" ```python title="linked_list.py" - [class]{}-[func]{access} + def access(head: ListNode, index: int) -> ListNode | None: + """访问链表中索引为 index 的节点""" + for _ in range(index): + if not head: + return None + head = head.next + return head ``` === "C++" ```cpp title="linked_list.cpp" - [class]{}-[func]{access} + /* 访问链表中索引为 index 的节点 */ + ListNode *access(ListNode *head, int index) { + for (int i = 0; i < index; i++) { + if (head == nullptr) + return nullptr; + head = head->next; + } + return head; + } ``` === "Java" ```java title="linked_list.java" - [class]{linked_list}-[func]{access} + /* 访问链表中索引为 index 的节点 */ + ListNode access(ListNode head, int index) { + for (int i = 0; i < index; i++) { + if (head == null) + return null; + head = head.next; + } + return head; + } ``` === "C#" ```csharp title="linked_list.cs" - [class]{linked_list}-[func]{access} + /* 访问链表中索引为 index 的节点 */ + ListNode? access(ListNode head, int index) { + for (int i = 0; i < index; i++) { + if (head == null) + return null; + head = head.next; + } + return head; + } ``` === "Go" ```go title="linked_list.go" - [class]{}-[func]{access} + /* 访问链表中索引为 index 的节点 */ + func access(head *ListNode, index int) *ListNode { + for i := 0; i < index; i++ { + if head == nil { + return nil + } + head = head.Next + } + return head + } ``` === "Swift" ```swift title="linked_list.swift" - [class]{}-[func]{access} + /* 访问链表中索引为 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 + } ``` === "JS" ```javascript title="linked_list.js" - [class]{}-[func]{access} + /* 访问链表中索引为 index 的节点 */ + function access(head, index) { + for (let i = 0; i < index; i++) { + if (!head) { + return null; + } + head = head.next; + } + return head; + } ``` === "TS" ```typescript title="linked_list.ts" - [class]{}-[func]{access} + /* 访问链表中索引为 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; + } ``` === "Dart" ```dart title="linked_list.dart" - [class]{}-[func]{access} + /* 访问链表中索引为 index 的节点 */ + ListNode? access(ListNode? head, int index) { + for (var i = 0; i < index; i++) { + if (head == null) return null; + head = head.next; + } + return head; + } ``` === "Rust" ```rust title="linked_list.rs" - [class]{}-[func]{access} + /* 访问链表中索引为 index 的节点 */ + pub fn access(head: Rc>>, index: i32) -> Rc>> { + if index <= 0 {return head}; + if let Some(node) = &head.borrow_mut().next { + return access(node.clone(), index - 1); + } + return head; + } ``` === "C" ```c title="linked_list.c" - [class]{}-[func]{access} + /* 访问链表中索引为 index 的节点 */ + ListNode *access(ListNode *head, int index) { + while (head && head->next && index) { + head = head->next; + index--; + } + return head; + } ``` === "Zig" ```zig title="linked_list.zig" - [class]{}-[func]{access} + // 访问链表中索引为 index 的节点 + fn access(node: ?*inc.ListNode(i32), index: i32) ?*inc.ListNode(i32) { + var head = node; + var i: i32 = 0; + while (i < index) : (i += 1) { + head = head.?.next; + if (head == null) return null; + } + return head; + } ``` ### 5.   查找节点 @@ -650,73 +910,194 @@ comments: true === "Python" ```python title="linked_list.py" - [class]{}-[func]{find} + def find(head: ListNode, target: int) -> int: + """在链表中查找值为 target 的首个节点""" + index = 0 + while head: + if head.val == target: + return index + head = head.next + index += 1 + return -1 ``` === "C++" ```cpp title="linked_list.cpp" - [class]{}-[func]{find} + /* 在链表中查找值为 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; + } ``` === "Java" ```java title="linked_list.java" - [class]{linked_list}-[func]{find} + /* 在链表中查找值为 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; + } ``` === "C#" ```csharp title="linked_list.cs" - [class]{linked_list}-[func]{find} + /* 在链表中查找值为 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; + } ``` === "Go" ```go title="linked_list.go" - [class]{}-[func]{findNode} + /* 在链表中查找值为 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 + } ``` === "Swift" ```swift title="linked_list.swift" - [class]{}-[func]{find} + /* 在链表中查找值为 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 + } ``` === "JS" ```javascript title="linked_list.js" - [class]{}-[func]{find} + /* 在链表中查找值为 target 的首个节点 */ + function find(head, target) { + let index = 0; + while (head !== null) { + if (head.val === target) { + return index; + } + head = head.next; + index += 1; + } + return -1; + } ``` === "TS" ```typescript title="linked_list.ts" - [class]{}-[func]{find} + /* 在链表中查找值为 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; + } ``` === "Dart" ```dart title="linked_list.dart" - [class]{}-[func]{find} + /* 在链表中查找值为 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; + } ``` === "Rust" ```rust title="linked_list.rs" - [class]{}-[func]{find} + /* 在链表中查找值为 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; + } ``` === "C" ```c title="linked_list.c" - [class]{}-[func]{find} + /* 在链表中查找值为 target 的首个节点 */ + int find(ListNode *head, int target) { + int index = 0; + while (head) { + if (head->val == target) + return index; + head = head->next; + index++; + } + return -1; + } ``` === "Zig" ```zig title="linked_list.zig" - [class]{}-[func]{find} + // 在链表中查找值为 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; + } ``` ## 4.2.2   数组 VS 链表 diff --git a/zh/chapter_array_and_linkedlist/list.md b/zh/chapter_array_and_linkedlist/list.md index 35473b6a3..f21a968a3 100755 --- a/zh/chapter_array_and_linkedlist/list.md +++ b/zh/chapter_array_and_linkedlist/list.md @@ -867,71 +867,1251 @@ comments: true === "Python" ```python title="my_list.py" - [class]{MyList}-[func]{} + class MyList: + """列表类简易实现""" + + def __init__(self): + """构造方法""" + self.__capacity: int = 10 # 列表容量 + self.__nums: list[int] = [0] * self.__capacity # 数组(存储列表元素) + self.__size: int = 0 # 列表长度(即当前元素数量) + self.__extend_ratio: int = 2 # 每次列表扩容的倍数 + + def size(self) -> int: + """获取列表长度(即当前元素数量)""" + return self.__size + + def capacity(self) -> int: + """获取列表容量""" + return self.__capacity + + def get(self, index: int) -> int: + """访问元素""" + # 索引如果越界则抛出异常,下同 + if index < 0 or index >= self.__size: + raise IndexError("索引越界") + return self.__nums[index] + + def set(self, num: int, index: int): + """更新元素""" + if index < 0 or index >= self.__size: + raise IndexError("索引越界") + self.__nums[index] = num + + def add(self, num: int): + """尾部添加元素""" + # 元素数量超出容量时,触发扩容机制 + if self.size() == self.capacity(): + self.extend_capacity() + self.__nums[self.__size] = num + self.__size += 1 + + def insert(self, num: int, index: int): + """中间插入元素""" + if index < 0 or index >= self.__size: + raise IndexError("索引越界") + # 元素数量超出容量时,触发扩容机制 + if self.__size == self.capacity(): + self.extend_capacity() + # 将索引 index 以及之后的元素都向后移动一位 + for j in range(self.__size - 1, index - 1, -1): + self.__nums[j + 1] = self.__nums[j] + self.__nums[index] = num + # 更新元素数量 + self.__size += 1 + + def remove(self, index: int) -> int: + """删除元素""" + if index < 0 or index >= self.__size: + raise IndexError("索引越界") + num = self.__nums[index] + # 索引 i 之后的元素都向前移动一位 + for j in range(index, self.__size - 1): + self.__nums[j] = self.__nums[j + 1] + # 更新元素数量 + self.__size -= 1 + # 返回被删除元素 + return num + + def extend_capacity(self): + """列表扩容""" + # 新建一个长度为原数组 __extend_ratio 倍的新数组,并将原数组拷贝到新数组 + self.__nums = self.__nums + [0] * self.capacity() * (self.__extend_ratio - 1) + # 更新列表容量 + self.__capacity = len(self.__nums) + + def to_array(self) -> list[int]: + """返回有效长度的列表""" + return self.__nums[: self.__size] ``` === "C++" ```cpp title="my_list.cpp" - [class]{MyList}-[func]{} + /* 列表类简易实现 */ + class MyList { + private: + int *nums; // 数组(存储列表元素) + int numsCapacity = 10; // 列表容量 + int numsSize = 0; // 列表长度(即当前元素数量) + int extendRatio = 2; // 每次列表扩容的倍数 + + public: + /* 构造方法 */ + MyList() { + nums = new int[numsCapacity]; + } + + /* 析构方法 */ + ~MyList() { + delete[] nums; + } + + /* 获取列表长度(即当前元素数量)*/ + int size() { + return numsSize; + } + + /* 获取列表容量 */ + int capacity() { + return numsCapacity; + } + + /* 访问元素 */ + int get(int index) { + // 索引如果越界则抛出异常,下同 + if (index < 0 || index >= size()) + throw out_of_range("索引越界"); + return nums[index]; + } + + /* 更新元素 */ + void set(int index, int num) { + if (index < 0 || index >= size()) + throw out_of_range("索引越界"); + nums[index] = num; + } + + /* 尾部添加元素 */ + void add(int num) { + // 元素数量超出容量时,触发扩容机制 + if (size() == capacity()) + extendCapacity(); + nums[size()] = num; + // 更新元素数量 + numsSize++; + } + + /* 中间插入元素 */ + 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--) { + nums[j + 1] = nums[j]; + } + nums[index] = num; + // 更新元素数量 + numsSize++; + } + + /* 删除元素 */ + int remove(int index) { + if (index < 0 || index >= size()) + throw out_of_range("索引越界"); + int num = nums[index]; + // 索引 i 之后的元素都向前移动一位 + for (int j = index; j < size() - 1; j++) { + nums[j] = nums[j + 1]; + } + // 更新元素数量 + numsSize--; + // 返回被删除元素 + return num; + } + + /* 列表扩容 */ + void extendCapacity() { + // 新建一个长度为原数组 extendRatio 倍的新数组 + int newCapacity = capacity() * extendRatio; + int *tmp = nums; + nums = new int[newCapacity]; + // 将原数组中的所有元素复制到新数组 + for (int i = 0; i < size(); i++) { + nums[i] = tmp[i]; + } + // 释放内存 + delete[] tmp; + numsCapacity = newCapacity; + } + + /* 将列表转换为 Vector 用于打印 */ + vector toVector() { + // 仅转换有效长度范围内的列表元素 + vector vec(size()); + for (int i = 0; i < size(); i++) { + vec[i] = nums[i]; + } + return vec; + } + }; ``` === "Java" ```java title="my_list.java" - [class]{MyList}-[func]{} + /* 列表类简易实现 */ + class MyList { + private int[] nums; // 数组(存储列表元素) + private int capacity = 10; // 列表容量 + private int size = 0; // 列表长度(即当前元素数量) + private int extendRatio = 2; // 每次列表扩容的倍数 + + /* 构造方法 */ + public MyList() { + nums = new int[capacity]; + } + + /* 获取列表长度(即当前元素数量) */ + public int size() { + return size; + } + + /* 获取列表容量 */ + public int capacity() { + return capacity; + } + + /* 访问元素 */ + public int get(int index) { + // 索引如果越界则抛出异常,下同 + if (index < 0 || index >= size) + throw new IndexOutOfBoundsException("索引越界"); + return nums[index]; + } + + /* 更新元素 */ + public void set(int index, int num) { + if (index < 0 || index >= size) + throw new IndexOutOfBoundsException("索引越界"); + nums[index] = num; + } + + /* 尾部添加元素 */ + public void add(int num) { + // 元素数量超出容量时,触发扩容机制 + if (size == capacity()) + extendCapacity(); + nums[size] = num; + // 更新元素数量 + size++; + } + + /* 中间插入元素 */ + public void insert(int index, int num) { + if (index < 0 || index >= size) + throw new IndexOutOfBoundsException("索引越界"); + // 元素数量超出容量时,触发扩容机制 + if (size == capacity()) + extendCapacity(); + // 将索引 index 以及之后的元素都向后移动一位 + for (int j = size - 1; j >= index; j--) { + nums[j + 1] = nums[j]; + } + nums[index] = num; + // 更新元素数量 + size++; + } + + /* 删除元素 */ + public int remove(int index) { + if (index < 0 || index >= size) + throw new IndexOutOfBoundsException("索引越界"); + int num = nums[index]; + // 将索引 index 之后的元素都向前移动一位 + for (int j = index; j < size - 1; j++) { + nums[j] = nums[j + 1]; + } + // 更新元素数量 + size--; + // 返回被删除元素 + return num; + } + + /* 列表扩容 */ + public void extendCapacity() { + // 新建一个长度为原数组 extendRatio 倍的新数组,并将原数组拷贝到新数组 + nums = Arrays.copyOf(nums, capacity() * extendRatio); + // 更新列表容量 + capacity = nums.length; + } + + /* 将列表转换为数组 */ + public int[] toArray() { + int size = size(); + // 仅转换有效长度范围内的列表元素 + int[] nums = new int[size]; + for (int i = 0; i < size; i++) { + nums[i] = get(i); + } + return nums; + } + } ``` === "C#" ```csharp title="my_list.cs" - [class]{MyList}-[func]{} + /* 列表类简易实现 */ + class MyList { + private int[] nums; // 数组(存储列表元素) + private int numsCapacity = 10; // 列表容量 + private int numsSize = 0; // 列表长度(即当前元素数量) + private int extendRatio = 2; // 每次列表扩容的倍数 + + /* 构造方法 */ + public MyList() { + nums = new int[numsCapacity]; + } + + /* 获取列表长度(即当前元素数量)*/ + public int size() { + return numsSize; + } + + /* 获取列表容量 */ + public int capacity() { + return numsCapacity; + } + + /* 访问元素 */ + public int get(int index) { + // 索引如果越界则抛出异常,下同 + if (index < 0 || index >= numsSize) + throw new IndexOutOfRangeException("索引越界"); + return nums[index]; + } + + /* 更新元素 */ + public void set(int index, int num) { + if (index < 0 || index >= numsSize) + throw new IndexOutOfRangeException("索引越界"); + nums[index] = num; + } + + /* 尾部添加元素 */ + public void add(int num) { + // 元素数量超出容量时,触发扩容机制 + if (numsSize == numsCapacity) + extendCapacity(); + nums[numsSize] = num; + // 更新元素数量 + numsSize++; + } + + /* 中间插入元素 */ + public void insert(int index, int num) { + if (index < 0 || index >= numsSize) + throw new IndexOutOfRangeException("索引越界"); + // 元素数量超出容量时,触发扩容机制 + if (numsSize == numsCapacity) + extendCapacity(); + // 将索引 index 以及之后的元素都向后移动一位 + for (int j = numsSize - 1; j >= index; j--) { + nums[j + 1] = nums[j]; + } + nums[index] = num; + // 更新元素数量 + numsSize++; + } + + /* 删除元素 */ + public int remove(int index) { + if (index < 0 || index >= numsSize) + throw new IndexOutOfRangeException("索引越界"); + int num = nums[index]; + // 将索引 index 之后的元素都向前移动一位 + for (int j = index; j < numsSize - 1; j++) { + nums[j] = nums[j + 1]; + } + // 更新元素数量 + numsSize--; + // 返回被删除元素 + return num; + } + + /* 列表扩容 */ + public void extendCapacity() { + // 新建一个长度为 numsCapacity * extendRatio 的数组,并将原数组拷贝到新数组 + Array.Resize(ref nums, numsCapacity * extendRatio); + // 更新列表容量 + numsCapacity = nums.Length; + } + + /* 将列表转换为数组 */ + public int[] toArray() { + // 仅转换有效长度范围内的列表元素 + int[] nums = new int[numsSize]; + for (int i = 0; i < numsSize; i++) { + nums[i] = get(i); + } + return nums; + } + } ``` === "Go" ```go title="my_list.go" - [class]{myList}-[func]{} + /* 列表类简易实现 */ + type myList struct { + numsCapacity int + nums []int + numsSize int + extendRatio int + } + + /* 构造函数 */ + func newMyList() *myList { + return &myList{ + numsCapacity: 10, // 列表容量 + nums: make([]int, 10), // 数组(存储列表元素) + numsSize: 0, // 列表长度(即当前元素数量) + extendRatio: 2, // 每次列表扩容的倍数 + } + } + + /* 获取列表长度(即当前元素数量) */ + func (l *myList) size() int { + return l.numsSize + } + + /* 获取列表容量 */ + func (l *myList) capacity() int { + return l.numsCapacity + } + + /* 访问元素 */ + func (l *myList) get(index int) int { + // 索引如果越界则抛出异常,下同 + if index < 0 || index >= l.numsSize { + panic("索引越界") + } + return l.nums[index] + } + + /* 更新元素 */ + func (l *myList) set(num, index int) { + if index < 0 || index >= l.numsSize { + panic("索引越界") + } + l.nums[index] = num + } + + /* 尾部添加元素 */ + func (l *myList) add(num int) { + // 元素数量超出容量时,触发扩容机制 + if l.numsSize == l.numsCapacity { + l.extendCapacity() + } + l.nums[l.numsSize] = num + // 更新元素数量 + l.numsSize++ + } + + /* 中间插入元素 */ + func (l *myList) insert(num, index int) { + if index < 0 || index >= l.numsSize { + panic("索引越界") + } + // 元素数量超出容量时,触发扩容机制 + if l.numsSize == l.numsCapacity { + l.extendCapacity() + } + // 将索引 index 以及之后的元素都向后移动一位 + for j := l.numsSize - 1; j >= index; j-- { + l.nums[j+1] = l.nums[j] + } + l.nums[index] = num + // 更新元素数量 + l.numsSize++ + } + + /* 删除元素 */ + func (l *myList) remove(index int) int { + if index < 0 || index >= l.numsSize { + panic("索引越界") + } + num := l.nums[index] + // 索引 i 之后的元素都向前移动一位 + for j := index; j < l.numsSize-1; j++ { + l.nums[j] = l.nums[j+1] + } + // 更新元素数量 + l.numsSize-- + // 返回被删除元素 + return num + } + + /* 列表扩容 */ + func (l *myList) extendCapacity() { + // 新建一个长度为原数组 extendRatio 倍的新数组,并将原数组拷贝到新数组 + l.nums = append(l.nums, make([]int, l.numsCapacity*(l.extendRatio-1))...) + // 更新列表容量 + l.numsCapacity = len(l.nums) + } + + /* 返回有效长度的列表 */ + func (l *myList) toArray() []int { + // 仅转换有效长度范围内的列表元素 + return l.nums[:l.numsSize] + } ``` === "Swift" ```swift title="my_list.swift" - [class]{MyList}-[func]{} + /* 列表类简易实现 */ + class MyList { + private var nums: [Int] // 数组(存储列表元素) + private var _capacity = 10 // 列表容量 + private var _size = 0 // 列表长度(即当前元素数量) + private let extendRatio = 2 // 每次列表扩容的倍数 + + /* 构造方法 */ + init() { + nums = 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 nums[index] + } + + /* 更新元素 */ + func set(index: Int, num: Int) { + if index < 0 || index >= _size { + fatalError("索引越界") + } + nums[index] = num + } + + /* 尾部添加元素 */ + func add(num: Int) { + // 元素数量超出容量时,触发扩容机制 + if _size == _capacity { + extendCapacity() + } + nums[_size] = num + // 更新元素数量 + _size += 1 + } + + /* 中间插入元素 */ + func insert(index: Int, num: Int) { + if index < 0 || index >= _size { + fatalError("索引越界") + } + // 元素数量超出容量时,触发扩容机制 + if _size == _capacity { + extendCapacity() + } + // 将索引 index 以及之后的元素都向后移动一位 + for j in sequence(first: _size - 1, next: { $0 >= index + 1 ? $0 - 1 : nil }) { + nums[j + 1] = nums[j] + } + nums[index] = num + // 更新元素数量 + _size += 1 + } + + /* 删除元素 */ + @discardableResult + func remove(index: Int) -> Int { + if index < 0 || index >= _size { + fatalError("索引越界") + } + let num = nums[index] + // 将索引 index 之后的元素都向前移动一位 + for j in index ..< (_size - 1) { + nums[j] = nums[j + 1] + } + // 更新元素数量 + _size -= 1 + // 返回被删除元素 + return num + } + + /* 列表扩容 */ + func extendCapacity() { + // 新建一个长度为原数组 extendRatio 倍的新数组,并将原数组拷贝到新数组 + nums = nums + Array(repeating: 0, count: _capacity * (extendRatio - 1)) + // 更新列表容量 + _capacity = nums.count + } + + /* 将列表转换为数组 */ + func toArray() -> [Int] { + var nums = Array(repeating: 0, count: _size) + for i in 0 ..< _size { + nums[i] = get(index: i) + } + return nums + } + } ``` === "JS" ```javascript title="my_list.js" - [class]{MyList}-[func]{} + /* 列表类简易实现 */ + class MyList { + #nums = new Array(); // 数组(存储列表元素) + #capacity = 10; // 列表容量 + #size = 0; // 列表长度(即当前元素数量) + #extendRatio = 2; // 每次列表扩容的倍数 + + /* 构造方法 */ + constructor() { + this.#nums = 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.#nums[index]; + } + + /* 更新元素 */ + set(index, num) { + if (index < 0 || index >= this.#size) throw new Error('索引越界'); + this.#nums[index] = num; + } + + /* 尾部添加元素 */ + add(num) { + // 如果长度等于容量,则需要扩容 + if (this.#size === this.#capacity) { + this.extendCapacity(); + } + // 将新元素添加到列表尾部 + this.#nums[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.#nums[j + 1] = this.#nums[j]; + } + // 更新元素数量 + this.#nums[index] = num; + this.#size++; + } + + /* 删除元素 */ + remove(index) { + if (index < 0 || index >= this.#size) throw new Error('索引越界'); + let num = this.#nums[index]; + // 将索引 index 之后的元素都向前移动一位 + for (let j = index; j < this.#size - 1; j++) { + this.#nums[j] = this.#nums[j + 1]; + } + // 更新元素数量 + this.#size--; + // 返回被删除元素 + return num; + } + + /* 列表扩容 */ + extendCapacity() { + // 新建一个长度为原数组 extendRatio 倍的新数组,并将原数组拷贝到新数组 + this.#nums = this.#nums.concat( + new Array(this.capacity() * (this.#extendRatio - 1)) + ); + // 更新列表容量 + this.#capacity = this.#nums.length; + } + + /* 将列表转换为数组 */ + toArray() { + let size = this.size(); + // 仅转换有效长度范围内的列表元素 + const nums = new Array(size); + for (let i = 0; i < size; i++) { + nums[i] = this.get(i); + } + return nums; + } + } ``` === "TS" ```typescript title="my_list.ts" - [class]{MyList}-[func]{} + /* 列表类简易实现 */ + class MyList { + private nums: Array; // 数组(存储列表元素) + private _capacity: number = 10; // 列表容量 + private _size: number = 0; // 列表长度(即当前元素数量) + private extendRatio: number = 2; // 每次列表扩容的倍数 + + /* 构造方法 */ + constructor() { + this.nums = 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.nums[index]; + } + + /* 更新元素 */ + public set(index: number, num: number): void { + if (index < 0 || index >= this._size) throw new Error('索引越界'); + this.nums[index] = num; + } + + /* 尾部添加元素 */ + public add(num: number): void { + // 如果长度等于容量,则需要扩容 + if (this._size === this._capacity) this.extendCapacity(); + // 将新元素添加到列表尾部 + this.nums[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.nums[j + 1] = this.nums[j]; + } + // 更新元素数量 + this.nums[index] = num; + this._size++; + } + + /* 删除元素 */ + public remove(index: number): number { + if (index < 0 || index >= this._size) throw new Error('索引越界'); + let num = this.nums[index]; + // 将索引 index 之后的元素都向前移动一位 + for (let j = index; j < this._size - 1; j++) { + this.nums[j] = this.nums[j + 1]; + } + // 更新元素数量 + this._size--; + // 返回被删除元素 + return num; + } + + /* 列表扩容 */ + public extendCapacity(): void { + // 新建一个长度为 size 的数组,并将原数组拷贝到新数组 + this.nums = this.nums.concat( + new Array(this.capacity() * (this.extendRatio - 1)) + ); + // 更新列表容量 + this._capacity = this.nums.length; + } + + /* 将列表转换为数组 */ + public toArray(): number[] { + let size = this.size(); + // 仅转换有效长度范围内的列表元素 + const nums = new Array(size); + for (let i = 0; i < size; i++) { + nums[i] = this.get(i); + } + return nums; + } + } ``` === "Dart" ```dart title="my_list.dart" - [class]{MyList}-[func]{} + /* 列表类简易实现 */ + class MyList { + late List _nums; // 数组(存储列表元素) + int _capacity = 10; // 列表容量 + int _size = 0; // 列表长度(即当前元素数量) + int _extendRatio = 2; // 每次列表扩容的倍数 + + /* 构造方法 */ + MyList() { + _nums = List.filled(_capacity, 0); + } + + /* 获取列表长度(即当前元素数量)*/ + int size() => _size; + + /* 获取列表容量 */ + int capacity() => _capacity; + + /* 访问元素 */ + int get(int index) { + if (index >= _size) throw RangeError('索引越界'); + return _nums[index]; + } + + /* 更新元素 */ + void set(int index, int num) { + if (index >= _size) throw RangeError('索引越界'); + _nums[index] = num; + } + + /* 尾部添加元素 */ + void add(int num) { + // 元素数量超出容量时,触发扩容机制 + if (_size == _capacity) extendCapacity(); + _nums[_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--) { + _nums[j + 1] = _nums[j]; + } + _nums[index] = num; + // 更新元素数量 + _size++; + } + + /* 删除元素 */ + int remove(int index) { + if (index >= _size) throw RangeError('索引越界'); + int num = _nums[index]; + // 将索引 index 之后的元素都向前移动一位 + for (var j = index; j < _size - 1; j++) { + _nums[j] = _nums[j + 1]; + } + // 更新元素数量 + _size--; + // 返回被删除元素 + return num; + } + + /* 列表扩容 */ + void extendCapacity() { + // 新建一个长度为原数组 _extendRatio 倍的新数组 + final _newNums = List.filled(_capacity * _extendRatio, 0); + // 将原数组拷贝到新数组 + List.copyRange(_newNums, 0, _nums); + // 更新 _nums 的引用 + _nums = _newNums; + // 更新列表容量 + _capacity = _nums.length; + } + + /* 将列表转换为数组 */ + List toArray() { + List nums = []; + for (var i = 0; i < _size; i++) { + nums.add(get(i)); + } + return nums; + } + } ``` === "Rust" ```rust title="my_list.rs" - [class]{MyList}-[func]{} + /* 列表类简易实现 */ + #[allow(dead_code)] + struct MyList { + nums: 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 { + nums: 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 < 0 || index >= self.size {panic!("索引越界")}; + return self.nums[index]; + } + + /* 更新元素 */ + pub fn set(&mut self, index: usize, num: i32) { + if index < 0 || index >= self.size {panic!("索引越界")}; + self.nums[index] = num; + } + + /* 尾部添加元素 */ + pub fn add(&mut self, num: i32) { + // 元素数量超出容量时,触发扩容机制 + if self.size == self.capacity() { + self.extend_capacity(); + } + self.nums[self.size] = num; + // 更新元素数量 + self.size += 1; + } + + /* 中间插入元素 */ + pub fn insert(&mut self, index: usize, num: i32) { + if index < 0 || index >= self.size() {panic!("索引越界")}; + // 元素数量超出容量时,触发扩容机制 + if self.size == self.capacity() { + self.extend_capacity(); + } + // 将索引 index 以及之后的元素都向后移动一位 + for j in (index..self.size).rev() { + self.nums[j + 1] = self.nums[j]; + } + self.nums[index] = num; + // 更新元素数量 + self.size += 1; + } + + /* 删除元素 */ + pub fn remove(&mut self, index: usize) -> i32 { + if index < 0 || index >= self.size() {panic!("索引越界")}; + let num = self.nums[index]; + // 将索引 index 之后的元素都向前移动一位 + for j in (index..self.size - 1) { + self.nums[j] = self.nums[j + 1]; + } + // 更新元素数量 + self.size -= 1; + // 返回被删除元素 + return num; + } + + /* 列表扩容 */ + pub fn extend_capacity(&mut self) { + // 新建一个长度为原数组 extend_ratio 倍的新数组,并将原数组拷贝到新数组 + let new_capacity = self.capacity * self.extend_ratio; + self.nums.resize(new_capacity, 0); + // 更新列表容量 + self.capacity = new_capacity; + } + + /* 将列表转换为数组 */ + pub fn to_array(&mut self) -> Vec { + // 仅转换有效长度范围内的列表元素 + let mut nums = Vec::new(); + for i in 0..self.size { + nums.push(self.get(i)); + } + nums + } + } ``` === "C" ```c title="my_list.c" - [class]{myList}-[func]{} + /* 列表类简易实现 */ + struct myList { + int *nums; // 数组(存储列表元素) + int capacity; // 列表容量 + int size; // 列表大小 + int extendRatio; // 列表每次扩容的倍数 + }; + + typedef struct myList myList; + + /* 构造函数 */ + myList *newMyList() { + myList *list = malloc(sizeof(myList)); + list->capacity = 10; + list->nums = malloc(sizeof(int) * list->capacity); + list->size = 0; + list->extendRatio = 2; + return list; + } + + /* 析构函数 */ + void delMyList(myList *list) { + free(list->nums); + free(list); + } + + /* 获取列表长度 */ + int size(myList *list) { + return list->size; + } + + /* 获取列表容量 */ + int capacity(myList *list) { + return list->capacity; + } + + /* 访问元素 */ + int get(myList *list, int index) { + assert(index >= 0 && index < list->size); + return list->nums[index]; + } + + /* 更新元素 */ + void set(myList *list, int index, int num) { + assert(index >= 0 && index < list->size); + list->nums[index] = num; + } + + /* 尾部添加元素 */ + void add(myList *list, int num) { + if (size(list) == capacity(list)) { + extendCapacity(list); // 扩容 + } + list->nums[size(list)] = num; + list->size++; + } + + /* 中间插入元素 */ + void insert(myList *list, int index, int num) { + assert(index >= 0 && index < size(list)); + // 元素数量超出容量时,触发扩容机制 + if (size(list) == capacity(list)) { + extendCapacity(list); // 扩容 + } + for (int i = size(list); i > index; --i) { + list->nums[i] = list->nums[i - 1]; + } + list->nums[index] = num; + list->size++; + } + + /* 删除元素 */ + // 注意:stdio.h 占用了 remove 关键词 + int removeNum(myList *list, int index) { + assert(index >= 0 && index < size(list)); + int num = list->nums[index]; + for (int i = index; i < size(list) - 1; i++) { + list->nums[i] = list->nums[i + 1]; + } + list->size--; + return num; + } + + /* 列表扩容 */ + void extendCapacity(myList *list) { + // 先分配空间 + int newCapacity = capacity(list) * list->extendRatio; + int *extend = (int *)malloc(sizeof(int) * newCapacity); + int *temp = list->nums; + + // 拷贝旧数据到新数据 + for (int i = 0; i < size(list); i++) + extend[i] = list->nums[i]; + + // 释放旧数据 + free(temp); + + // 更新新数据 + list->nums = extend; + list->capacity = newCapacity; + } + + /* 将列表转换为 Array 用于打印 */ + int *toArray(myList *list) { + return list->nums; + } ``` === "Zig" ```zig title="my_list.zig" - [class]{MyList}-[func]{} + // 列表类简易实现 + fn MyList(comptime T: type) type { + return struct { + const Self = @This(); + + nums: []T = undefined, // 数组(存储列表元素) + numsCapacity: 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.nums = try self.mem_allocator.alloc(T, self.numsCapacity); + @memset(self.nums, @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.numsCapacity; + } + + // 访问元素 + pub fn get(self: *Self, index: usize) T { + // 索引如果越界则抛出异常,下同 + if (index < 0 or index >= self.size()) @panic("索引越界"); + return self.nums[index]; + } + + // 更新元素 + pub fn set(self: *Self, index: usize, num: T) void { + // 索引如果越界则抛出异常,下同 + if (index < 0 or index >= self.size()) @panic("索引越界"); + self.nums[index] = num; + } + + // 尾部添加元素 + pub fn add(self: *Self, num: T) !void { + // 元素数量超出容量时,触发扩容机制 + if (self.size() == self.capacity()) try self.extendCapacity(); + self.nums[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.nums[j + 1] = self.nums[j]; + } + self.nums[index] = num; + // 更新元素数量 + self.numSize += 1; + } + + // 删除元素 + pub fn remove(self: *Self, index: usize) T { + if (index < 0 or index >= self.size()) @panic("索引越界"); + var num = self.nums[index]; + // 索引 i 之后的元素都向前移动一位 + var j = index; + while (j < self.size() - 1) : (j += 1) { + self.nums[j] = self.nums[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.nums); + self.nums = extend; + // 更新列表容量 + self.numsCapacity = newCapacity; + } + + // 将列表转换为数组 + pub fn toArray(self: *Self) ![]T { + // 仅转换有效长度范围内的列表元素 + var nums = try self.mem_allocator.alloc(T, self.size()); + @memset(nums, @as(T, 0)); + for (nums, 0..) |*num, i| { + num.* = self.get(i); + } + return nums; + } + }; + } ``` diff --git a/zh/chapter_backtracking/backtracking_algorithm.md b/zh/chapter_backtracking/backtracking_algorithm.md index a04d03ea4..b7e0fa514 100644 --- a/zh/chapter_backtracking/backtracking_algorithm.md +++ b/zh/chapter_backtracking/backtracking_algorithm.md @@ -17,61 +17,170 @@ comments: true === "Python" ```python title="preorder_traversal_i_compact.py" - [class]{}-[func]{pre_order} + def pre_order(root: TreeNode): + """前序遍历:例题一""" + if root is None: + return + if root.val == 7: + # 记录解 + res.append(root) + pre_order(root.left) + pre_order(root.right) ``` === "C++" ```cpp title="preorder_traversal_i_compact.cpp" - [class]{}-[func]{preOrder} + /* 前序遍历:例题一 */ + void preOrder(TreeNode *root) { + if (root == nullptr) { + return; + } + if (root->val == 7) { + // 记录解 + res.push_back(root); + } + preOrder(root->left); + preOrder(root->right); + } ``` === "Java" ```java title="preorder_traversal_i_compact.java" - [class]{preorder_traversal_i_compact}-[func]{preOrder} + /* 前序遍历:例题一 */ + void preOrder(TreeNode root) { + if (root == null) { + return; + } + if (root.val == 7) { + // 记录解 + res.add(root); + } + preOrder(root.left); + preOrder(root.right); + } ``` === "C#" ```csharp title="preorder_traversal_i_compact.cs" - [class]{preorder_traversal_i_compact}-[func]{preOrder} + /* 前序遍历:例题一 */ + void preOrder(TreeNode root) { + if (root == null) { + return; + } + if (root.val == 7) { + // 记录解 + res.Add(root); + } + preOrder(root.left); + preOrder(root.right); + } ``` === "Go" ```go title="preorder_traversal_i_compact.go" - [class]{}-[func]{preOrderI} + /* 前序遍历:例题一 */ + 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) + } ``` === "Swift" ```swift title="preorder_traversal_i_compact.swift" - [class]{}-[func]{preOrder} + /* 前序遍历:例题一 */ + 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) + } ``` === "JS" ```javascript title="preorder_traversal_i_compact.js" - [class]{}-[func]{preOrder} + /* 前序遍历:例题一 */ + function preOrder(root, res) { + if (root === null) { + return; + } + if (root.val === 7) { + // 记录解 + res.push(root); + } + preOrder(root.left, res); + preOrder(root.right, res); + } ``` === "TS" ```typescript title="preorder_traversal_i_compact.ts" - [class]{}-[func]{preOrder} + /* 前序遍历:例题一 */ + 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); + } ``` === "Dart" ```dart title="preorder_traversal_i_compact.dart" - [class]{}-[func]{preOrder} + /* 前序遍历:例题一 */ + 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); + } ``` === "Rust" ```rust title="preorder_traversal_i_compact.rs" - [class]{}-[func]{pre_order} + /* 前序遍历:例题一 */ + 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()); + } + } ``` === "C" @@ -107,67 +216,246 @@ comments: true === "Python" ```python title="preorder_traversal_ii_compact.py" - [class]{}-[func]{pre_order} + def pre_order(root: TreeNode): + """前序遍历:例题二""" + if root is None: + return + # 尝试 + path.append(root) + if root.val == 7: + # 记录解 + res.append(list(path)) + pre_order(root.left) + pre_order(root.right) + # 回退 + path.pop() ``` === "C++" ```cpp title="preorder_traversal_ii_compact.cpp" - [class]{}-[func]{preOrder} + /* 前序遍历:例题二 */ + 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(); + } ``` === "Java" ```java title="preorder_traversal_ii_compact.java" - [class]{preorder_traversal_ii_compact}-[func]{preOrder} + /* 前序遍历:例题二 */ + void preOrder(TreeNode root) { + if (root == null) { + return; + } + // 尝试 + path.add(root); + if (root.val == 7) { + // 记录解 + res.add(new ArrayList<>(path)); + } + preOrder(root.left); + preOrder(root.right); + // 回退 + path.remove(path.size() - 1); + } ``` === "C#" ```csharp title="preorder_traversal_ii_compact.cs" - [class]{preorder_traversal_ii_compact}-[func]{preOrder} + /* 前序遍历:例题二 */ + 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); + } ``` === "Go" ```go title="preorder_traversal_ii_compact.go" - [class]{}-[func]{preOrderII} + /* 前序遍历:例题二 */ + func preOrderII(root *TreeNode, res *[][]*TreeNode, path *[]*TreeNode) { + if root == nil { + return + } + // 尝试 + *path = append(*path, root) + if root.Val.(int) == 7 { + // 记录解 + *res = append(*res, *path) + } + preOrderII(root.Left, res, path) + preOrderII(root.Right, res, path) + // 回退 + *path = (*path)[:len(*path)-1] + } ``` === "Swift" ```swift title="preorder_traversal_ii_compact.swift" - [class]{}-[func]{preOrder} + /* 前序遍历:例题二 */ + 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() + } ``` === "JS" ```javascript title="preorder_traversal_ii_compact.js" - [class]{}-[func]{preOrder} + /* 前序遍历:例题二 */ + 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(); + } ``` === "TS" ```typescript title="preorder_traversal_ii_compact.ts" - [class]{}-[func]{preOrder} + /* 前序遍历:例题二 */ + 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(); + } ``` === "Dart" ```dart title="preorder_traversal_ii_compact.dart" - [class]{}-[func]{preOrder} + /* 前序遍历:例题二 */ + 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(); + } ``` === "Rust" ```rust title="preorder_traversal_ii_compact.rs" - [class]{}-[func]{pre_order} + /* 前序遍历:例题二 */ + 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); + } + } ``` === "C" ```c title="preorder_traversal_ii_compact.c" - [class]{}-[func]{preOrder} + /* 前序遍历:例题二 */ + void preOrder(TreeNode *root, vector *path, vector *res) { + if (root == NULL) { + return; + } + // 尝试 + vectorPushback(path, root, sizeof(TreeNode)); + if (root->val == 7) { + // 记录解 + vector *newPath = newVector(); + for (int i = 0; i < path->size; i++) { + vectorPushback(newPath, path->data[i], sizeof(int)); + } + vectorPushback(res, newPath, sizeof(vector)); + } + + preOrder(root->left, path, res); + preOrder(root->right, path, res); + + // 回退 + vectorPopback(path); + } ``` === "Zig" @@ -228,67 +516,257 @@ comments: true === "Python" ```python title="preorder_traversal_iii_compact.py" - [class]{}-[func]{pre_order} + def pre_order(root: TreeNode): + """前序遍历:例题三""" + # 剪枝 + if root is None or root.val == 3: + return + # 尝试 + path.append(root) + if root.val == 7: + # 记录解 + res.append(list(path)) + pre_order(root.left) + pre_order(root.right) + # 回退 + path.pop() ``` === "C++" ```cpp title="preorder_traversal_iii_compact.cpp" - [class]{}-[func]{preOrder} + /* 前序遍历:例题三 */ + 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(); + } ``` === "Java" ```java title="preorder_traversal_iii_compact.java" - [class]{preorder_traversal_iii_compact}-[func]{preOrder} + /* 前序遍历:例题三 */ + void preOrder(TreeNode root) { + // 剪枝 + if (root == null || root.val == 3) { + return; + } + // 尝试 + path.add(root); + if (root.val == 7) { + // 记录解 + res.add(new ArrayList<>(path)); + } + preOrder(root.left); + preOrder(root.right); + // 回退 + path.remove(path.size() - 1); + } ``` === "C#" ```csharp title="preorder_traversal_iii_compact.cs" - [class]{preorder_traversal_iii_compact}-[func]{preOrder} + /* 前序遍历:例题三 */ + 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); + } ``` === "Go" ```go title="preorder_traversal_iii_compact.go" - [class]{}-[func]{preOrderIII} + /* 前序遍历:例题三 */ + 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, *path) + } + preOrderIII(root.Left, res, path) + preOrderIII(root.Right, res, path) + // 回退 + *path = (*path)[:len(*path)-1] + } ``` === "Swift" ```swift title="preorder_traversal_iii_compact.swift" - [class]{}-[func]{preOrder} + /* 前序遍历:例题三 */ + 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() + } ``` === "JS" ```javascript title="preorder_traversal_iii_compact.js" - [class]{}-[func]{preOrder} + /* 前序遍历:例题三 */ + 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(); + } ``` === "TS" ```typescript title="preorder_traversal_iii_compact.ts" - [class]{}-[func]{preOrder} + /* 前序遍历:例题三 */ + 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(); + } ``` === "Dart" ```dart title="preorder_traversal_iii_compact.dart" - [class]{}-[func]{preOrder} + /* 前序遍历:例题三 */ + 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(); + } ``` === "Rust" ```rust title="preorder_traversal_iii_compact.rs" - [class]{}-[func]{pre_order} + /* 前序遍历:例题三 */ + 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); + } + } ``` === "C" ```c title="preorder_traversal_iii_compact.c" - [class]{}-[func]{preOrder} + /* 前序遍历:例题三 */ + void preOrder(TreeNode *root, vector *path, vector *res) { + // 剪枝 + if (root == NULL || root->val == 3) { + return; + } + // 尝试 + vectorPushback(path, root, sizeof(TreeNode)); + if (root->val == 7) { + // 记录解 + vector *newPath = newVector(); + for (int i = 0; i < path->size; i++) { + vectorPushback(newPath, path->data[i], sizeof(int)); + } + vectorPushback(res, newPath, sizeof(vector)); + res->depth++; + } + + preOrder(root->left, path, res); + preOrder(root->right, path, res); + + // 回退 + vectorPopback(path); + } ``` === "Zig" @@ -602,177 +1080,563 @@ comments: true === "Python" ```python title="preorder_traversal_iii_template.py" - [class]{}-[func]{is_solution} + def is_solution(state: list[TreeNode]) -> bool: + """判断当前状态是否为解""" + return state and state[-1].val == 7 - [class]{}-[func]{record_solution} + def record_solution(state: list[TreeNode], res: list[list[TreeNode]]): + """记录解""" + res.append(list(state)) - [class]{}-[func]{is_valid} + def is_valid(state: list[TreeNode], choice: TreeNode) -> bool: + """判断在当前状态下,该选择是否合法""" + return choice is not None and choice.val != 3 - [class]{}-[func]{make_choice} + def make_choice(state: list[TreeNode], choice: TreeNode): + """更新状态""" + state.append(choice) - [class]{}-[func]{undo_choice} + def undo_choice(state: list[TreeNode], choice: TreeNode): + """恢复状态""" + state.pop() - [class]{}-[func]{backtrack} + def backtrack( + state: list[TreeNode], choices: list[TreeNode], res: list[list[TreeNode]] + ): + """回溯算法:例题三""" + # 检查是否为解 + if is_solution(state): + # 记录解 + record_solution(state, res) + # 遍历所有选择 + for choice in choices: + # 剪枝:检查选择是否合法 + if is_valid(state, choice): + # 尝试:做出选择,更新状态 + make_choice(state, choice) + # 进行下一轮选择 + backtrack(state, [choice.left, choice.right], res) + # 回退:撤销选择,恢复到之前的状态 + undo_choice(state, choice) ``` === "C++" ```cpp title="preorder_traversal_iii_template.cpp" - [class]{}-[func]{isSolution} + /* 判断当前状态是否为解 */ + bool isSolution(vector &state) { + return !state.empty() && state.back()->val == 7; + } - [class]{}-[func]{recordSolution} + /* 记录解 */ + void recordSolution(vector &state, vector> &res) { + res.push_back(state); + } - [class]{}-[func]{isValid} + /* 判断在当前状态下,该选择是否合法 */ + bool isValid(vector &state, TreeNode *choice) { + return choice != nullptr && choice->val != 3; + } - [class]{}-[func]{makeChoice} + /* 更新状态 */ + void makeChoice(vector &state, TreeNode *choice) { + state.push_back(choice); + } - [class]{}-[func]{undoChoice} + /* 恢复状态 */ + void undoChoice(vector &state, TreeNode *choice) { + state.pop_back(); + } - [class]{}-[func]{backtrack} + /* 回溯算法:例题三 */ + 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); + } + } + } ``` === "Java" ```java title="preorder_traversal_iii_template.java" - [class]{preorder_traversal_iii_template}-[func]{isSolution} + /* 判断当前状态是否为解 */ + boolean isSolution(List state) { + return !state.isEmpty() && state.get(state.size() - 1).val == 7; + } - [class]{preorder_traversal_iii_template}-[func]{recordSolution} + /* 记录解 */ + void recordSolution(List state, List> res) { + res.add(new ArrayList<>(state)); + } - [class]{preorder_traversal_iii_template}-[func]{isValid} + /* 判断在当前状态下,该选择是否合法 */ + boolean isValid(List state, TreeNode choice) { + return choice != null && choice.val != 3; + } - [class]{preorder_traversal_iii_template}-[func]{makeChoice} + /* 更新状态 */ + void makeChoice(List state, TreeNode choice) { + state.add(choice); + } - [class]{preorder_traversal_iii_template}-[func]{undoChoice} + /* 恢复状态 */ + void undoChoice(List state, TreeNode choice) { + state.remove(state.size() - 1); + } - [class]{preorder_traversal_iii_template}-[func]{backtrack} + /* 回溯算法:例题三 */ + void backtrack(List state, List choices, List> res) { + // 检查是否为解 + if (isSolution(state)) { + // 记录解 + recordSolution(state, res); + } + // 遍历所有选择 + for (TreeNode choice : choices) { + // 剪枝:检查选择是否合法 + if (isValid(state, choice)) { + // 尝试:做出选择,更新状态 + makeChoice(state, choice); + // 进行下一轮选择 + backtrack(state, Arrays.asList(choice.left, choice.right), res); + // 回退:撤销选择,恢复到之前的状态 + undoChoice(state, choice); + } + } + } ``` === "C#" ```csharp title="preorder_traversal_iii_template.cs" - [class]{preorder_traversal_iii_template}-[func]{isSolution} + /* 判断当前状态是否为解 */ + bool isSolution(List state) { + return state.Count != 0 && state[^1].val == 7; + } - [class]{preorder_traversal_iii_template}-[func]{recordSolution} + /* 记录解 */ + void recordSolution(List state, List> res) { + res.Add(new List(state)); + } - [class]{preorder_traversal_iii_template}-[func]{isValid} + /* 判断在当前状态下,该选择是否合法 */ + bool isValid(List state, TreeNode choice) { + return choice != null && choice.val != 3; + } - [class]{preorder_traversal_iii_template}-[func]{makeChoice} + /* 更新状态 */ + void makeChoice(List state, TreeNode choice) { + state.Add(choice); + } - [class]{preorder_traversal_iii_template}-[func]{undoChoice} + /* 恢复状态 */ + void undoChoice(List state, TreeNode choice) { + state.RemoveAt(state.Count - 1); + } - [class]{preorder_traversal_iii_template}-[func]{backtrack} + /* 回溯算法:例题三 */ + 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, new List { choice.left, choice.right }, res); + // 回退:撤销选择,恢复到之前的状态 + undoChoice(state, choice); + } + } + } ``` === "Go" ```go title="preorder_traversal_iii_template.go" - [class]{}-[func]{isSolution} + /* 判断当前状态是否为解 */ + func isSolution(state *[]*TreeNode) bool { + return len(*state) != 0 && (*state)[len(*state)-1].Val == 7 + } - [class]{}-[func]{recordSolution} + /* 记录解 */ + func recordSolution(state *[]*TreeNode, res *[][]*TreeNode) { + *res = append(*res, *state) + } - [class]{}-[func]{isValid} + /* 判断在当前状态下,该选择是否合法 */ + func isValid(state *[]*TreeNode, choice *TreeNode) bool { + return choice != nil && choice.Val != 3 + } - [class]{}-[func]{makeChoice} + /* 更新状态 */ + func makeChoice(state *[]*TreeNode, choice *TreeNode) { + *state = append(*state, choice) + } - [class]{}-[func]{undoChoice} + /* 恢复状态 */ + func undoChoice(state *[]*TreeNode, choice *TreeNode) { + *state = (*state)[:len(*state)-1] + } - [class]{}-[func]{backtrackIII} + /* 回溯算法:例题三 */ + 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) + } + } + } ``` === "Swift" ```swift title="preorder_traversal_iii_template.swift" - [class]{}-[func]{isSolution} + /* 判断当前状态是否为解 */ + func isSolution(state: [TreeNode]) -> Bool { + !state.isEmpty && state.last!.val == 7 + } - [class]{}-[func]{recordSolution} + /* 记录解 */ + func recordSolution(state: [TreeNode], res: inout [[TreeNode]]) { + res.append(state) + } - [class]{}-[func]{isValid} + /* 判断在当前状态下,该选择是否合法 */ + func isValid(state: [TreeNode], choice: TreeNode?) -> Bool { + choice != nil && choice!.val != 3 + } - [class]{}-[func]{makeChoice} + /* 更新状态 */ + func makeChoice(state: inout [TreeNode], choice: TreeNode) { + state.append(choice) + } - [class]{}-[func]{undoChoice} + /* 恢复状态 */ + func undoChoice(state: inout [TreeNode], choice: TreeNode) { + state.removeLast() + } - [class]{}-[func]{backtrack} + /* 回溯算法:例题三 */ + 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) + } + } + } ``` === "JS" ```javascript title="preorder_traversal_iii_template.js" - [class]{}-[func]{isSolution} + /* 判断当前状态是否为解 */ + function isSolution(state) { + return state && state[state.length - 1]?.val === 7; + } - [class]{}-[func]{recordSolution} + /* 记录解 */ + function recordSolution(state, res) { + res.push([...state]); + } - [class]{}-[func]{isValid} + /* 判断在当前状态下,该选择是否合法 */ + function isValid(state, choice) { + return choice !== null && choice.val !== 3; + } - [class]{}-[func]{makeChoice} + /* 更新状态 */ + function makeChoice(state, choice) { + state.push(choice); + } - [class]{}-[func]{undoChoice} + /* 恢复状态 */ + function undoChoice(state) { + state.pop(); + } - [class]{}-[func]{backtrack} + /* 回溯算法:例题三 */ + 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); + } + } + } ``` === "TS" ```typescript title="preorder_traversal_iii_template.ts" - [class]{}-[func]{isSolution} + /* 判断当前状态是否为解 */ + function isSolution(state: TreeNode[]): boolean { + return state && state[state.length - 1]?.val === 7; + } - [class]{}-[func]{recordSolution} + /* 记录解 */ + function recordSolution(state: TreeNode[], res: TreeNode[][]): void { + res.push([...state]); + } - [class]{}-[func]{isValid} + /* 判断在当前状态下,该选择是否合法 */ + function isValid(state: TreeNode[], choice: TreeNode): boolean { + return choice !== null && choice.val !== 3; + } - [class]{}-[func]{makeChoice} + /* 更新状态 */ + function makeChoice(state: TreeNode[], choice: TreeNode): void { + state.push(choice); + } - [class]{}-[func]{undoChoice} + /* 恢复状态 */ + function undoChoice(state: TreeNode[]): void { + state.pop(); + } - [class]{}-[func]{backtrack} + /* 回溯算法:例题三 */ + 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); + } + } + } ``` === "Dart" ```dart title="preorder_traversal_iii_template.dart" - [class]{}-[func]{isSolution} + /* 判断当前状态是否为解 */ + bool isSolution(List state) { + return state.isNotEmpty && state.last.val == 7; + } - [class]{}-[func]{recordSolution} + /* 记录解 */ + void recordSolution(List state, List> res) { + res.add(List.from(state)); + } - [class]{}-[func]{isValid} + /* 判断在当前状态下,该选择是否合法 */ + bool isValid(List state, TreeNode? choice) { + return choice != null && choice.val != 3; + } - [class]{}-[func]{makeChoice} + /* 更新状态 */ + void makeChoice(List state, TreeNode? choice) { + state.add(choice!); + } - [class]{}-[func]{undoChoice} + /* 恢复状态 */ + void undoChoice(List state, TreeNode? choice) { + state.removeLast(); + } - [class]{}-[func]{backtrack} + /* 回溯算法:例题三 */ + 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); + } + } + } ``` === "Rust" ```rust title="preorder_traversal_iii_template.rs" - [class]{}-[func]{is_solution} + /* 判断当前状态是否为解 */ + fn is_solution(state: &mut Vec>>) -> bool { + return !state.is_empty() && state.get(state.len() - 1).unwrap().borrow().val == 7; + } - [class]{}-[func]{record_solution} + /* 记录解 */ + fn record_solution(state: &mut Vec>>, res: &mut Vec>>>) { + res.push(state.clone()); + } - [class]{}-[func]{is_valid} + /* 判断在当前状态下,该选择是否合法 */ + fn is_valid(_: &mut Vec>>, choice: Rc>) -> bool { + return choice.borrow().val != 3; + } - [class]{}-[func]{make_choice} + /* 更新状态 */ + fn make_choice(state: &mut Vec>>, choice: Rc>) { + state.push(choice); + } - [class]{}-[func]{undo_choice} + /* 恢复状态 */ + fn undo_choice(state: &mut Vec>>, _: Rc>) { + state.remove(state.len() - 1); + } - [class]{}-[func]{backtrack} + /* 回溯算法:例题三 */ + 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()); + } + } + } ``` === "C" ```c title="preorder_traversal_iii_template.c" - [class]{}-[func]{isSolution} + /* 判断当前状态是否为解 */ + bool isSolution(vector *state) { + return state->size != 0 && ((TreeNode *)(state->data[state->size - 1]))->val == 7; + } - [class]{}-[func]{recordSolution} + /* 记录解 */ + void recordSolution(vector *state, vector *res) { + vector *newPath = newVector(); + for (int i = 0; i < state->size; i++) { + vectorPushback(newPath, state->data[i], sizeof(int)); + } + vectorPushback(res, newPath, sizeof(vector)); + } - [class]{}-[func]{isValid} + /* 判断在当前状态下,该选择是否合法 */ + bool isValid(vector *state, TreeNode *choice) { + return choice != NULL && choice->val != 3; + } - [class]{}-[func]{makeChoice} + /* 更新状态 */ + void makeChoice(vector *state, TreeNode *choice) { + vectorPushback(state, choice, sizeof(TreeNode)); + } - [class]{}-[func]{undoChoice} + /* 恢复状态 */ + void undoChoice(vector *state, TreeNode *choice) { + vectorPopback(state); + } - [class]{}-[func]{backtrack} + /* 回溯算法:例题三 */ + void backtrack(vector *state, vector *choices, vector *res) { + // 检查是否为解 + if (isSolution(state)) { + // 记录解 + recordSolution(state, res); + return; + } + // 遍历所有选择 + for (int i = 0; i < choices->size; i++) { + TreeNode *choice = choices->data[i]; + // 剪枝:检查选择是否合法 + if (isValid(state, choice)) { + // 尝试:做出选择,更新状态 + makeChoice(state, choice); + // 进行下一轮选择 + vector *nextChoices = newVector(); + vectorPushback(nextChoices, choice->left, sizeof(TreeNode)); + vectorPushback(nextChoices, choice->right, sizeof(TreeNode)); + backtrack(state, nextChoices, res); + // 回退:撤销选择,恢复到之前的状态 + undoChoice(state, choice); + } + } + } ``` === "Zig" diff --git a/zh/chapter_backtracking/n_queens_problem.md b/zh/chapter_backtracking/n_queens_problem.md index 4b8edc09d..84897e192 100644 --- a/zh/chapter_backtracking/n_queens_problem.md +++ b/zh/chapter_backtracking/n_queens_problem.md @@ -55,81 +55,547 @@ comments: true === "Python" ```python title="n_queens.py" - [class]{}-[func]{backtrack} + def backtrack( + row: int, + n: int, + state: list[list[str]], + res: list[list[list[str]]], + cols: list[bool], + diags1: list[bool], + diags2: list[bool], + ): + """回溯算法:N 皇后""" + # 当放置完所有行时,记录解 + if row == n: + res.append([list(row) for row in state]) + return + # 遍历所有列 + for col in range(n): + # 计算该格子对应的主对角线和副对角线 + diag1 = row - col + n - 1 + diag2 = row + col + # 剪枝:不允许该格子所在列、主对角线、副对角线存在皇后 + if not cols[col] and not diags1[diag1] and not 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]{n_queens} + def n_queens(n: int) -> list[list[list[str]]]: + """求解 N 皇后""" + # 初始化 n*n 大小的棋盘,其中 'Q' 代表皇后,'#' 代表空位 + state = [["#" for _ in range(n)] for _ in range(n)] + cols = [False] * n # 记录列是否有皇后 + diags1 = [False] * (2 * n - 1) # 记录主对角线是否有皇后 + diags2 = [False] * (2 * n - 1) # 记录副对角线是否有皇后 + res = [] + backtrack(0, n, state, res, cols, diags1, diags2) + + return res ``` === "C++" ```cpp title="n_queens.cpp" - [class]{}-[func]{backtrack} + /* 回溯算法: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]{nQueens} + /* 求解 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; + } ``` === "Java" ```java title="n_queens.java" - [class]{n_queens}-[func]{backtrack} + /* 回溯算法:N 皇后 */ + void backtrack(int row, int n, List> state, List>> res, + boolean[] cols, boolean[] diags1, boolean[] diags2) { + // 当放置完所有行时,记录解 + if (row == n) { + List> copyState = new ArrayList<>(); + for (List sRow : state) { + copyState.add(new ArrayList<>(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.get(row).set(col, "Q"); + cols[col] = diags1[diag1] = diags2[diag2] = true; + // 放置下一行 + backtrack(row + 1, n, state, res, cols, diags1, diags2); + // 回退:将该格子恢复为空位 + state.get(row).set(col, "#"); + cols[col] = diags1[diag1] = diags2[diag2] = false; + } + } + } - [class]{n_queens}-[func]{nQueens} + /* 求解 N 皇后 */ + List>> nQueens(int n) { + // 初始化 n*n 大小的棋盘,其中 'Q' 代表皇后,'#' 代表空位 + List> state = new ArrayList<>(); + for (int i = 0; i < n; i++) { + List row = new ArrayList<>(); + for (int j = 0; j < n; j++) { + row.add("#"); + } + state.add(row); + } + boolean[] cols = new boolean[n]; // 记录列是否有皇后 + boolean[] diags1 = new boolean[2 * n - 1]; // 记录主对角线是否有皇后 + boolean[] diags2 = new boolean[2 * n - 1]; // 记录副对角线是否有皇后 + List>> res = new ArrayList<>(); + + backtrack(0, n, state, res, cols, diags1, diags2); + + return res; + } ``` === "C#" ```csharp title="n_queens.cs" - [class]{n_queens}-[func]{backtrack} + /* 回溯算法:N 皇后 */ + void backtrack(int row, int n, List> state, List>> res, + bool[] cols, bool[] diags1, bool[] diags2) { + // 当放置完所有行时,记录解 + if (row == n) { + List> copyState = new List>(); + 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]{nQueens} + /* 求解 N 皇后 */ + List>> nQueens(int n) { + // 初始化 n*n 大小的棋盘,其中 'Q' 代表皇后,'#' 代表空位 + List> state = new List>(); + for (int i = 0; i < n; i++) { + List row = new List(); + 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 = new List>>(); + + backtrack(0, n, state, res, cols, diags1, diags2); + + return res; + } ``` === "Go" ```go title="n_queens.go" - [class]{}-[func]{backtrack} + /* 回溯算法: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]{nQueens} + } + *res = append(*res, newState) + } + // 遍历所有列 + 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 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]) + + } + *res = append(*res, newState) + } + // 遍历所有列 + 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 + } + } + } + + 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 + } ``` === "Swift" ```swift title="n_queens.swift" - [class]{}-[func]{backtrack} + /* 回溯算法: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]{nQueens} + /* 求解 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 + } ``` === "JS" ```javascript title="n_queens.js" - [class]{}-[func]{backtrack} + /* 回溯算法: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]{nQueens} + /* 求解 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; + } ``` === "TS" ```typescript title="n_queens.ts" - [class]{}-[func]{backtrack} + /* 回溯算法: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]{nQueens} + /* 求解 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; + } ``` === "Dart" ```dart title="n_queens.dart" - [class]{}-[func]{backtrack} + /* 回溯算法: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]{nQueens} + /* 求解 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; + } ``` === "Rust" ```rust title="n_queens.rs" - [class]{}-[func]{backtrack} + /* 回溯算法: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]{n_queens} + /* 求解 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 + } ``` === "C" diff --git a/zh/chapter_backtracking/permutations_problem.md b/zh/chapter_backtracking/permutations_problem.md index 40abb7d59..ce5898ee3 100644 --- a/zh/chapter_backtracking/permutations_problem.md +++ b/zh/chapter_backtracking/permutations_problem.md @@ -58,89 +58,411 @@ comments: true === "Python" ```python title="permutations_i.py" - [class]{}-[func]{backtrack} + def backtrack( + state: list[int], choices: list[int], selected: list[bool], res: list[list[int]] + ): + """回溯算法:全排列 I""" + # 当状态长度等于元素数量时,记录解 + if len(state) == len(choices): + res.append(list(state)) + return + # 遍历所有选择 + for i, choice in enumerate(choices): + # 剪枝:不允许重复选择元素 + if not selected[i]: + # 尝试:做出选择,更新状态 + selected[i] = True + state.append(choice) + # 进行下一轮选择 + backtrack(state, choices, selected, res) + # 回退:撤销选择,恢复到之前的状态 + selected[i] = False + state.pop() - [class]{}-[func]{permutations_i} + def permutations_i(nums: list[int]) -> list[list[int]]: + """全排列 I""" + res = [] + backtrack(state=[], choices=nums, selected=[False] * len(nums), res=res) + return res ``` === "C++" ```cpp title="permutations_i.cpp" - [class]{}-[func]{backtrack} + /* 回溯算法:全排列 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]{permutationsI} + /* 全排列 I */ + vector> permutationsI(vector nums) { + vector state; + vector selected(nums.size(), false); + vector> res; + backtrack(state, nums, selected, res); + return res; + } ``` === "Java" ```java title="permutations_i.java" - [class]{permutations_i}-[func]{backtrack} + /* 回溯算法:全排列 I */ + void backtrack(List state, int[] choices, boolean[] selected, List> res) { + // 当状态长度等于元素数量时,记录解 + if (state.size() == choices.length) { + res.add(new ArrayList(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.remove(state.size() - 1); + } + } + } - [class]{permutations_i}-[func]{permutationsI} + /* 全排列 I */ + List> permutationsI(int[] nums) { + List> res = new ArrayList>(); + backtrack(new ArrayList(), nums, new boolean[nums.length], res); + return res; + } ``` === "C#" ```csharp title="permutations_i.cs" - [class]{permutations_i}-[func]{backtrack} + /* 回溯算法:全排列 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]{permutationsI} + /* 全排列 I */ + List> permutationsI(int[] nums) { + List> res = new List>(); + backtrack(new List(), nums, new bool[nums.Length], res); + return res; + } ``` === "Go" ```go title="permutations_i.go" - [class]{}-[func]{backtrackI} + /* 回溯算法:全排列 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]{permutationsI} + /* 全排列 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 + } ``` === "Swift" ```swift title="permutations_i.swift" - [class]{}-[func]{backtrack} + /* 回溯算法:全排列 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]{permutationsI} + /* 全排列 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 + } ``` === "JS" ```javascript title="permutations_i.js" - [class]{}-[func]{backtrack} + /* 回溯算法:全排列 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]{permutationsI} + /* 全排列 I */ + function permutationsI(nums) { + const res = []; + backtrack([], nums, Array(nums.length).fill(false), res); + return res; + } ``` === "TS" ```typescript title="permutations_i.ts" - [class]{}-[func]{backtrack} + /* 回溯算法:全排列 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]{permutationsI} + /* 全排列 I */ + function permutationsI(nums: number[]): number[][] { + const res: number[][] = []; + backtrack([], nums, Array(nums.length).fill(false), res); + return res; + } ``` === "Dart" ```dart title="permutations_i.dart" - [class]{}-[func]{backtrack} + /* 回溯算法:全排列 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]{permutationsI} + /* 全排列 I */ + List> permutationsI(List nums) { + List> res = []; + backtrack([], nums, List.filled(nums.length, false), res); + return res; + } ``` === "Rust" ```rust title="permutations_i.rs" - [class]{}-[func]{backtrack} + /* 回溯算法:全排列 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]{permutations_i} + /* 全排列 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 + } ``` === "C" ```c title="permutations_i.c" - [class]{}-[func]{backtrack} + /* 回溯算法:全排列 I */ + void backtrack(vector *state, vector *choices, vector *selected, vector *res) { + // 当状态长度等于元素数量时,记录解 + if (state->size == choices->size) { + vector *newState = newVector(); + for (int i = 0; i < state->size; i++) { + vectorPushback(newState, state->data[i], sizeof(int)); + } + vectorPushback(res, newState, sizeof(vector)); + return; + } + // 遍历所有选择 + for (int i = 0; i < choices->size; i++) { + int *choice = malloc(sizeof(int)); + *choice = *((int *)(choices->data[i])); + // 剪枝:不允许重复选择元素 且 不允许重复选择相等元素 + bool select = *((bool *)(selected->data[i])); + if (!select) { + // 尝试:做出选择,更新状态 + *((bool *)selected->data[i]) = true; + vectorPushback(state, choice, sizeof(int)); + // 进行下一轮选择 + backtrack(state, choices, selected, res); + // 回退:撤销选择,恢复到之前的状态 + *((bool *)selected->data[i]) = false; + vectorPopback(state); + } + } + } - [class]{}-[func]{permutationsI} + /* 全排列 I */ + vector *permutationsI(vector *nums) { + vector *iState = newVector(); + + int select[3] = {false, false, false}; + vector *bSelected = newVector(); + for (int i = 0; i < nums->size; i++) { + vectorPushback(bSelected, &select[i], sizeof(int)); + } + + vector *res = newVector(); + + // 前序遍历 + backtrack(iState, nums, bSelected, res); + return res; + } ``` === "Zig" @@ -186,81 +508,381 @@ comments: true === "Python" ```python title="permutations_ii.py" - [class]{}-[func]{backtrack} + def backtrack( + state: list[int], choices: list[int], selected: list[bool], res: list[list[int]] + ): + """回溯算法:全排列 II""" + # 当状态长度等于元素数量时,记录解 + if len(state) == len(choices): + res.append(list(state)) + return + # 遍历所有选择 + duplicated = set[int]() + for i, choice in enumerate(choices): + # 剪枝:不允许重复选择元素 且 不允许重复选择相等元素 + if not selected[i] and choice not in duplicated: + # 尝试:做出选择,更新状态 + duplicated.add(choice) # 记录选择过的元素值 + selected[i] = True + state.append(choice) + # 进行下一轮选择 + backtrack(state, choices, selected, res) + # 回退:撤销选择,恢复到之前的状态 + selected[i] = False + state.pop() - [class]{}-[func]{permutations_ii} + def permutations_ii(nums: list[int]) -> list[list[int]]: + """全排列 II""" + res = [] + backtrack(state=[], choices=nums, selected=[False] * len(nums), res=res) + return res ``` === "C++" ```cpp title="permutations_ii.cpp" - [class]{}-[func]{backtrack} + /* 回溯算法:全排列 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]{permutationsII} + /* 全排列 II */ + vector> permutationsII(vector nums) { + vector state; + vector selected(nums.size(), false); + vector> res; + backtrack(state, nums, selected, res); + return res; + } ``` === "Java" ```java title="permutations_ii.java" - [class]{permutations_ii}-[func]{backtrack} + /* 回溯算法:全排列 II */ + void backtrack(List state, int[] choices, boolean[] selected, List> res) { + // 当状态长度等于元素数量时,记录解 + if (state.size() == choices.length) { + res.add(new ArrayList(state)); + return; + } + // 遍历所有选择 + Set duplicated = new HashSet(); + 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.remove(state.size() - 1); + } + } + } - [class]{permutations_ii}-[func]{permutationsII} + /* 全排列 II */ + List> permutationsII(int[] nums) { + List> res = new ArrayList>(); + backtrack(new ArrayList(), nums, new boolean[nums.length], res); + return res; + } ``` === "C#" ```csharp title="permutations_ii.cs" - [class]{permutations_ii}-[func]{backtrack} + /* 回溯算法:全排列 II */ + void backtrack(List state, int[] choices, bool[] selected, List> res) { + // 当状态长度等于元素数量时,记录解 + if (state.Count == choices.Length) { + res.Add(new List(state)); + return; + } + // 遍历所有选择 + ISet duplicated = new HashSet(); + 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]{permutationsII} + /* 全排列 II */ + List> permutationsII(int[] nums) { + List> res = new List>(); + backtrack(new List(), nums, new bool[nums.Length], res); + return res; + } ``` === "Go" ```go title="permutations_ii.go" - [class]{}-[func]{backtrackII} + /* 回溯算法:全排列 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) + // 进行下一轮选择 + backtrackI(state, choices, selected, res) + // 回退:撤销选择,恢复到之前的状态 + (*selected)[i] = false + *state = (*state)[:len(*state)-1] + } + } + } - [class]{}-[func]{permutationsII} + /* 全排列 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 + } ``` === "Swift" ```swift title="permutations_ii.swift" - [class]{}-[func]{backtrack} + /* 回溯算法:全排列 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]{permutationsII} + /* 全排列 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 + } ``` === "JS" ```javascript title="permutations_ii.js" - [class]{}-[func]{backtrack} + /* 回溯算法:全排列 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]{permutationsII} + /* 全排列 II */ + function permutationsII(nums) { + const res = []; + backtrack([], nums, Array(nums.length).fill(false), res); + return res; + } ``` === "TS" ```typescript title="permutations_ii.ts" - [class]{}-[func]{backtrack} + /* 回溯算法:全排列 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]{permutationsII} + /* 全排列 II */ + function permutationsII(nums: number[]): number[][] { + const res: number[][] = []; + backtrack([], nums, Array(nums.length).fill(false), res); + return res; + } ``` === "Dart" ```dart title="permutations_ii.dart" - [class]{}-[func]{backtrack} + /* 回溯算法:全排列 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]{permutationsII} + /* 全排列 II */ + List> permutationsII(List nums) { + List> res = []; + backtrack([], nums, List.filled(nums.length, false), res); + return res; + } ``` === "Rust" ```rust title="permutations_ii.rs" - [class]{}-[func]{backtrack} + /* 回溯算法:全排列 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]{permutations_ii} + /* 全排列 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 + } ``` === "C" diff --git a/zh/chapter_backtracking/subset_sum_problem.md b/zh/chapter_backtracking/subset_sum_problem.md index 9ab95c614..b00d00da9 100644 --- a/zh/chapter_backtracking/subset_sum_problem.md +++ b/zh/chapter_backtracking/subset_sum_problem.md @@ -24,89 +24,404 @@ comments: true === "Python" ```python title="subset_sum_i_naive.py" - [class]{}-[func]{backtrack} + def backtrack( + state: list[int], + target: int, + total: int, + choices: list[int], + res: list[list[int]], + ): + """回溯算法:子集和 I""" + # 子集和等于 target 时,记录解 + if total == target: + res.append(list(state)) + return + # 遍历所有选择 + for i in range(len(choices)): + # 剪枝:若子集和超过 target ,则跳过该选择 + if total + choices[i] > target: + continue + # 尝试:做出选择,更新元素和 total + state.append(choices[i]) + # 进行下一轮选择 + backtrack(state, target, total + choices[i], choices, res) + # 回退:撤销选择,恢复到之前的状态 + state.pop() - [class]{}-[func]{subset_sum_i_naive} + def subset_sum_i_naive(nums: list[int], target: int) -> list[list[int]]: + """求解子集和 I(包含重复子集)""" + state = [] # 状态(子集) + total = 0 # 子集和 + res = [] # 结果列表(子集列表) + backtrack(state, target, total, nums, res) + return res ``` === "C++" ```cpp title="subset_sum_i_naive.cpp" - [class]{}-[func]{backtrack} + /* 回溯算法:子集和 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]{subsetSumINaive} + /* 求解子集和 I(包含重复子集) */ + vector> subsetSumINaive(vector &nums, int target) { + vector state; // 状态(子集) + int total = 0; // 子集和 + vector> res; // 结果列表(子集列表) + backtrack(state, target, total, nums, res); + return res; + } ``` === "Java" ```java title="subset_sum_i_naive.java" - [class]{subset_sum_i_naive}-[func]{backtrack} + /* 回溯算法:子集和 I */ + void backtrack(List state, int target, int total, int[] choices, List> res) { + // 子集和等于 target 时,记录解 + if (total == target) { + res.add(new ArrayList<>(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.remove(state.size() - 1); + } + } - [class]{subset_sum_i_naive}-[func]{subsetSumINaive} + /* 求解子集和 I(包含重复子集) */ + List> subsetSumINaive(int[] nums, int target) { + List state = new ArrayList<>(); // 状态(子集) + int total = 0; // 子集和 + List> res = new ArrayList<>(); // 结果列表(子集列表) + backtrack(state, target, total, nums, res); + return res; + } ``` === "C#" ```csharp title="subset_sum_i_naive.cs" - [class]{subset_sum_i_naive}-[func]{backtrack} + /* 回溯算法:子集和 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]{subsetSumINaive} + /* 求解子集和 I(包含重复子集) */ + List> subsetSumINaive(int[] nums, int target) { + List state = new List(); // 状态(子集) + int total = 0; // 子集和 + List> res = new List>(); // 结果列表(子集列表) + backtrack(state, target, total, nums, res); + return res; + } ``` === "Go" ```go title="subset_sum_i_naive.go" - [class]{}-[func]{backtrackSubsetSumINaive} + /* 回溯算法:子集和 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]{subsetSumINaive} + /* 求解子集和 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 + } ``` === "Swift" ```swift title="subset_sum_i_naive.swift" - [class]{}-[func]{backtrack} + /* 回溯算法:子集和 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 stride(from: 0, to: choices.count, by: 1) { + // 剪枝:若子集和超过 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]{subsetSumINaive} + /* 求解子集和 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 + } ``` === "JS" ```javascript title="subset_sum_i_naive.js" - [class]{}-[func]{backtrack} + /* 回溯算法:子集和 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]{subsetSumINaive} + /* 求解子集和 I(包含重复子集) */ + function subsetSumINaive(nums, target) { + const state = []; // 状态(子集) + const total = 0; // 子集和 + const res = []; // 结果列表(子集列表) + backtrack(state, target, total, nums, res); + return res; + } ``` === "TS" ```typescript title="subset_sum_i_naive.ts" - [class]{}-[func]{backtrack} + /* 回溯算法:子集和 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]{subsetSumINaive} + /* 求解子集和 I(包含重复子集) */ + function subsetSumINaive(nums: number[], target: number): number[][] { + const state = []; // 状态(子集) + const total = 0; // 子集和 + const res = []; // 结果列表(子集列表) + backtrack(state, target, total, nums, res); + return res; + } ``` === "Dart" ```dart title="subset_sum_i_naive.dart" - [class]{}-[func]{backtrack} + /* 回溯算法:子集和 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]{subsetSumINaive} + /* 求解子集和 I(包含重复子集) */ + List> subsetSumINaive(List nums, int target) { + List state = []; // 状态(子集) + int total = 0; // 元素和 + List> res = []; // 结果列表(子集列表) + backtrack(state, target, total, nums, res); + return res; + } ``` === "Rust" ```rust title="subset_sum_i_naive.rs" - [class]{}-[func]{backtrack} + /* 回溯算法:子集和 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]{subset_sum_i_naive} + /* 求解子集和 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 + } ``` === "C" ```c title="subset_sum_i_naive.c" - [class]{}-[func]{backtrack} + /* 回溯算法:子集和 I */ + void backtrack(vector *state, int target, int total, vector *choices, vector *res) { + // 子集和等于 target 时,记录解 + if (total == target) { + vector *tmpVector = newVector(); + for (int i = 0; i < state->size; i++) { + vectorPushback(tmpVector, state->data[i], sizeof(int)); + } + vectorPushback(res, tmpVector, sizeof(vector)); + return; + } + // 遍历所有选择 + for (size_t i = 0; i < choices->size; i++) { + // 剪枝:若子集和超过 target ,则跳过该选择 + if (total + *(int *)(choices->data[i]) > target) { + continue; + } + // 尝试:做出选择,更新元素和 total + vectorPushback(state, choices->data[i], sizeof(int)); + // 进行下一轮选择 + backtrack(state, target, total + *(int *)(choices->data[i]), choices, res); + // 回退:撤销选择,恢复到之前的状态 + vectorPopback(state); + } + } - [class]{}-[func]{subsetSumINaive} + /* 求解子集和 I(包含重复子集) */ + vector *subsetSumINaive(vector *nums, int target) { + vector *state = newVector(); // 状态(子集) + int total = 0; // 子集和 + vector *res = newVector(); // 结果列表(子集列表) + backtrack(state, target, total, nums, res); + return res; + } ``` === "Zig" @@ -161,89 +476,432 @@ comments: true === "Python" ```python title="subset_sum_i.py" - [class]{}-[func]{backtrack} + def backtrack( + state: list[int], target: int, choices: list[int], start: int, res: list[list[int]] + ): + """回溯算法:子集和 I""" + # 子集和等于 target 时,记录解 + if target == 0: + res.append(list(state)) + return + # 遍历所有选择 + # 剪枝二:从 start 开始遍历,避免生成重复子集 + for i in range(start, len(choices)): + # 剪枝一:若子集和超过 target ,则直接结束循环 + # 这是因为数组已排序,后边元素更大,子集和一定超过 target + if target - choices[i] < 0: + break + # 尝试:做出选择,更新 target, start + state.append(choices[i]) + # 进行下一轮选择 + backtrack(state, target - choices[i], choices, i, res) + # 回退:撤销选择,恢复到之前的状态 + state.pop() - [class]{}-[func]{subset_sum_i} + def subset_sum_i(nums: list[int], target: int) -> list[list[int]]: + """求解子集和 I""" + state = [] # 状态(子集) + nums.sort() # 对 nums 进行排序 + start = 0 # 遍历起始点 + res = [] # 结果列表(子集列表) + backtrack(state, target, nums, start, res) + return res ``` === "C++" ```cpp title="subset_sum_i.cpp" - [class]{}-[func]{backtrack} + /* 回溯算法:子集和 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]{subsetSumI} + /* 求解子集和 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; + } ``` === "Java" ```java title="subset_sum_i.java" - [class]{subset_sum_i}-[func]{backtrack} + /* 回溯算法:子集和 I */ + void backtrack(List state, int target, int[] choices, int start, List> res) { + // 子集和等于 target 时,记录解 + if (target == 0) { + res.add(new ArrayList<>(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.remove(state.size() - 1); + } + } - [class]{subset_sum_i}-[func]{subsetSumI} + /* 求解子集和 I */ + List> subsetSumI(int[] nums, int target) { + List state = new ArrayList<>(); // 状态(子集) + Arrays.sort(nums); // 对 nums 进行排序 + int start = 0; // 遍历起始点 + List> res = new ArrayList<>(); // 结果列表(子集列表) + backtrack(state, target, nums, start, res); + return res; + } ``` === "C#" ```csharp title="subset_sum_i.cs" - [class]{subset_sum_i}-[func]{backtrack} + /* 回溯算法:子集和 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]{subsetSumI} + /* 求解子集和 I */ + List> subsetSumI(int[] nums, int target) { + List state = new List(); // 状态(子集) + Array.Sort(nums); // 对 nums 进行排序 + int start = 0; // 遍历起始点 + List> res = new List>(); // 结果列表(子集列表) + backtrack(state, target, nums, start, res); + return res; + } ``` === "Go" ```go title="subset_sum_i.go" - [class]{}-[func]{backtrackSubsetSumI} + /* 回溯算法:子集和 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]{subsetSumI} + /* 求解子集和 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 + } ``` === "Swift" ```swift title="subset_sum_i.swift" - [class]{}-[func]{backtrack} + /* 回溯算法:子集和 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 stride(from: start, to: choices.count, by: 1) { + // 剪枝一:若子集和超过 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]{subsetSumI} + /* 求解子集和 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 + } ``` === "JS" ```javascript title="subset_sum_i.js" - [class]{}-[func]{backtrack} + /* 回溯算法:子集和 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]{subsetSumI} + /* 求解子集和 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; + } ``` === "TS" ```typescript title="subset_sum_i.ts" - [class]{}-[func]{backtrack} + /* 回溯算法:子集和 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]{subsetSumI} + /* 求解子集和 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; + } ``` === "Dart" ```dart title="subset_sum_i.dart" - [class]{}-[func]{backtrack} + /* 回溯算法:子集和 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]{subsetSumI} + /* 求解子集和 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; + } ``` === "Rust" ```rust title="subset_sum_i.rs" - [class]{}-[func]{backtrack} + /* 回溯算法:子集和 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]{subset_sum_i} + /* 求解子集和 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 + } ``` === "C" ```c title="subset_sum_i.c" - [class]{}-[func]{backtrack} + /* 回溯算法:子集和 I */ + void backtrack(vector *state, int target, vector *choices, int start, vector *res) { + // 子集和等于 target 时,记录解 + if (target == 0) { + vector *tmpVector = newVector(); + for (int i = 0; i < state->size; i++) { + vectorPushback(tmpVector, state->data[i], sizeof(int)); + } + vectorPushback(res, tmpVector, sizeof(vector)); + return; + } + // 遍历所有选择 + // 剪枝二:从 start 开始遍历,避免生成重复子集 + for (int i = start; i < choices->size; i++) { + // 剪枝:若子集和超过 target ,则跳过该选择 + if (target - *(int *)(choices->data[i]) < 0) { + break; + } + // 尝试:做出选择,更新 target, start + vectorPushback(state, choices->data[i], sizeof(int)); + // 进行下一轮选择 + backtrack(state, target - *(int *)(choices->data[i]), choices, i, res); + // 回退:撤销选择,恢复到之前的状态 + vectorPopback(state); + } + } - [class]{}-[func]{subsetSumI} + /* 求解子集和 I */ + vector *subsetSumI(vector *nums, int target) { + vector *state = newVector(); // 状态(子集) + qsort(nums->data, nums->size, sizeof(int *), comp); // 对 nums 进行排序 + int start = 0; // 子集和 + vector *res = newVector(); // 结果列表(子集列表) + backtrack(state, target, nums, start, res); + return res; + } ``` === "Zig" @@ -285,89 +943,487 @@ comments: true === "Python" ```python title="subset_sum_ii.py" - [class]{}-[func]{backtrack} + def backtrack( + state: list[int], target: int, choices: list[int], start: int, res: list[list[int]] + ): + """回溯算法:子集和 II""" + # 子集和等于 target 时,记录解 + if target == 0: + res.append(list(state)) + return + # 遍历所有选择 + # 剪枝二:从 start 开始遍历,避免生成重复子集 + # 剪枝三:从 start 开始遍历,避免重复选择同一元素 + for i in range(start, len(choices)): + # 剪枝一:若子集和超过 target ,则直接结束循环 + # 这是因为数组已排序,后边元素更大,子集和一定超过 target + if target - choices[i] < 0: + break + # 剪枝四:如果该元素与左边元素相等,说明该搜索分支重复,直接跳过 + if i > start and choices[i] == choices[i - 1]: + continue + # 尝试:做出选择,更新 target, start + state.append(choices[i]) + # 进行下一轮选择 + backtrack(state, target - choices[i], choices, i + 1, res) + # 回退:撤销选择,恢复到之前的状态 + state.pop() - [class]{}-[func]{subset_sum_ii} + def subset_sum_ii(nums: list[int], target: int) -> list[list[int]]: + """求解子集和 II""" + state = [] # 状态(子集) + nums.sort() # 对 nums 进行排序 + start = 0 # 遍历起始点 + res = [] # 结果列表(子集列表) + backtrack(state, target, nums, start, res) + return res ``` === "C++" ```cpp title="subset_sum_ii.cpp" - [class]{}-[func]{backtrack} + /* 回溯算法:子集和 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]{subsetSumII} + /* 求解子集和 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; + } ``` === "Java" ```java title="subset_sum_ii.java" - [class]{subset_sum_ii}-[func]{backtrack} + /* 回溯算法:子集和 II */ + void backtrack(List state, int target, int[] choices, int start, List> res) { + // 子集和等于 target 时,记录解 + if (target == 0) { + res.add(new ArrayList<>(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.remove(state.size() - 1); + } + } - [class]{subset_sum_ii}-[func]{subsetSumII} + /* 求解子集和 II */ + List> subsetSumII(int[] nums, int target) { + List state = new ArrayList<>(); // 状态(子集) + Arrays.sort(nums); // 对 nums 进行排序 + int start = 0; // 遍历起始点 + List> res = new ArrayList<>(); // 结果列表(子集列表) + backtrack(state, target, nums, start, res); + return res; + } ``` === "C#" ```csharp title="subset_sum_ii.cs" - [class]{subset_sum_ii}-[func]{backtrack} + /* 回溯算法:子集和 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]{subsetSumII} + /* 求解子集和 II */ + List> subsetSumII(int[] nums, int target) { + List state = new List(); // 状态(子集) + Array.Sort(nums); // 对 nums 进行排序 + int start = 0; // 遍历起始点 + List> res = new List>(); // 结果列表(子集列表) + backtrack(state, target, nums, start, res); + return res; + } ``` === "Go" ```go title="subset_sum_ii.go" - [class]{}-[func]{backtrackSubsetSumII} + /* 回溯算法:子集和 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]{subsetSumII} + /* 求解子集和 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 + } ``` === "Swift" ```swift title="subset_sum_ii.swift" - [class]{}-[func]{backtrack} + /* 回溯算法:子集和 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 stride(from: start, to: choices.count, by: 1) { + // 剪枝一:若子集和超过 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]{subsetSumII} + /* 求解子集和 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 + } ``` === "JS" ```javascript title="subset_sum_ii.js" - [class]{}-[func]{backtrack} + /* 回溯算法:子集和 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]{subsetSumII} + /* 求解子集和 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; + } ``` === "TS" ```typescript title="subset_sum_ii.ts" - [class]{}-[func]{backtrack} + /* 回溯算法:子集和 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]{subsetSumII} + /* 求解子集和 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; + } ``` === "Dart" ```dart title="subset_sum_ii.dart" - [class]{}-[func]{backtrack} + /* 回溯算法:子集和 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]{subsetSumII} + /* 求解子集和 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; + } ``` === "Rust" ```rust title="subset_sum_ii.rs" - [class]{}-[func]{backtrack} + /* 回溯算法:子集和 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]{subset_sum_ii} + /* 求解子集和 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 + } ``` === "C" ```c title="subset_sum_ii.c" - [class]{}-[func]{backtrack} + /* 回溯算法:子集和 II */ + void backtrack(vector *state, int target, vector *choices, int start, vector *res) { + // 子集和等于 target 时,记录解 + if (target == 0) { + vector *tmpVector = newVector(); + for (int i = 0; i < state->size; i++) { + vectorPushback(tmpVector, state->data[i], sizeof(int)); + } + vectorPushback(res, tmpVector, sizeof(vector)); + return; + } + // 遍历所有选择 + // 剪枝二:从 start 开始遍历,避免生成重复子集 + // 剪枝三:从 start 开始遍历,避免重复选择同一元素 + for (int i = start; i < choices->size; i++) { + // 剪枝一:若子集和超过 target ,则直接结束循环 + // 这是因为数组已排序,后边元素更大,子集和一定超过 target + if (target - *(int *)(choices->data[i]) < 0) { + continue; + } + // 剪枝四:如果该元素与左边元素相等,说明该搜索分支重复,直接跳过 + if (i > start && *(int *)(choices->data[i]) == *(int *)(choices->data[i - 1])) { + continue; + } + // 尝试:做出选择,更新 target, start + vectorPushback(state, choices->data[i], sizeof(int)); + // 进行下一轮选择 + backtrack(state, target - *(int *)(choices->data[i]), choices, i + 1, res); + // 回退:撤销选择,恢复到之前的状态 + vectorPopback(state); + } + } - [class]{}-[func]{subsetSumII} + /* 求解子集和 II */ + vector *subsetSumII(vector *nums, int target) { + vector *state = newVector(); // 状态(子集) + qsort(nums->data, nums->size, sizeof(int *), comp); // 对 nums 进行排序 + int start = 0; // 子集和 + vector *res = newVector(); // 结果列表(子集列表) + backtrack(state, target, nums, start, res); + return res; + } ``` === "Zig" diff --git a/zh/chapter_computational_complexity/iteration_and_recursion.md b/zh/chapter_computational_complexity/iteration_and_recursion.md index cbd921ab5..909dfcdd5 100644 --- a/zh/chapter_computational_complexity/iteration_and_recursion.md +++ b/zh/chapter_computational_complexity/iteration_and_recursion.md @@ -20,67 +20,153 @@ status: new === "Python" ```python title="iteration.py" - [class]{}-[func]{for_loop} + def for_loop(n: int) -> int: + """for 循环""" + res = 0 + # 循环求和 1, 2, ..., n-1, n + for i in range(1, n + 1): + res += i + return res ``` === "C++" ```cpp title="iteration.cpp" - [class]{}-[func]{forLoop} + /* for 循环 */ + int forLoop(int n) { + int res = 0; + // 循环求和 1, 2, ..., n-1, n + for (int i = 1; i <= n; ++i) { + res += i; + } + return res; + } ``` === "Java" ```java title="iteration.java" - [class]{iteration}-[func]{forLoop} + /* for 循环 */ + int forLoop(int n) { + int res = 0; + // 循环求和 1, 2, ..., n-1, n + for (int i = 1; i <= n; i++) { + res += i; + } + return res; + } ``` === "C#" ```csharp title="iteration.cs" - [class]{iteration}-[func]{forLoop} + /* for 循环 */ + int forLoop(int n) { + int res = 0; + // 循环求和 1, 2, ..., n-1, n + for (int i = 1; i <= n; i++) { + res += i; + } + return res; + } ``` === "Go" ```go title="iteration.go" - [class]{}-[func]{forLoop} + /* for 循环 */ + func forLoop(n int) int { + res := 0 + // 循环求和 1, 2, ..., n-1, n + for i := 1; i <= n; i++ { + res += i + } + return res + } ``` === "Swift" ```swift title="iteration.swift" - [class]{}-[func]{forLoop} + /* for 循环 */ + func forLoop(n: Int) -> Int { + var res = 0 + // 循环求和 1, 2, ..., n-1, n + for i in 1 ... n { + res += i + } + return res + } ``` === "JS" ```javascript title="iteration.js" - [class]{}-[func]{forLoop} + /* for 循环 */ + function forLoop(n) { + let res = 0; + // 循环求和 1, 2, ..., n-1, n + for (let i = 1; i <= n; i++) { + res += i; + } + return res; + } ``` === "TS" ```typescript title="iteration.ts" - [class]{}-[func]{forLoop} + /* 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; + } ``` === "Dart" ```dart title="iteration.dart" - [class]{}-[func]{forLoop} + /* for 循环 */ + int forLoop(int n) { + int res = 0; + // 循环求和 1, 2, ..., n-1, n + for (int i = 1; i <= n; i++) { + res += i; + } + return res; + } ``` === "Rust" ```rust title="iteration.rs" - [class]{}-[func]{for_loop} + /* for 循环 */ + fn for_loop(n: i32) -> i32 { + let mut res = 0; + // 循环求和 1, 2, ..., n-1, n + for i in 1..=n { + res += i; + } + res + } ``` === "C" ```c title="iteration.c" - [class]{}-[func]{forLoop} + /* for 循环 */ + int forLoop(int n) { + int res = 0; + // 循环求和 1, 2, ..., n-1, n + for (int i = 1; i <= n; i++) { + res += i; + } + return res; + } ``` === "Zig" @@ -106,67 +192,177 @@ status: new === "Python" ```python title="iteration.py" - [class]{}-[func]{while_loop} + def while_loop(n: int) -> int: + """while 循环""" + res = 0 + i = 1 # 初始化条件变量 + # 循环求和 1, 2, ..., n-1, n + while i <= n: + res += i + i += 1 # 更新条件变量 + return res ``` === "C++" ```cpp title="iteration.cpp" - [class]{}-[func]{whileLoop} + /* while 循环 */ + int whileLoop(int n) { + int res = 0; + int i = 1; // 初始化条件变量 + // 循环求和 1, 2, ..., n-1, n + while (i <= n) { + res += i; + i++; // 更新条件变量 + } + return res; + } ``` === "Java" ```java title="iteration.java" - [class]{iteration}-[func]{whileLoop} + /* while 循环 */ + int whileLoop(int n) { + int res = 0; + int i = 1; // 初始化条件变量 + // 循环求和 1, 2, ..., n-1, n + while (i <= n) { + res += i; + i++; // 更新条件变量 + } + return res; + } ``` === "C#" ```csharp title="iteration.cs" - [class]{iteration}-[func]{whileLoop} + /* 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; + } ``` === "Go" ```go title="iteration.go" - [class]{}-[func]{whileLoop} + /* while 循环 */ + func whileLoop(n int) int { + res := 0 + // 初始化条件变量 + i := 1 + // 循环求和 1, 2, ..., n-1, n + for i <= n { + res += i + // 更新条件变量 + i++ + } + return res + } ``` === "Swift" ```swift title="iteration.swift" - [class]{}-[func]{whileLoop} + /* 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 + } ``` === "JS" ```javascript title="iteration.js" - [class]{}-[func]{whileLoop} + /* while 循环 */ + function whileLoop(n) { + let res = 0; + let i = 1; // 初始化条件变量 + // 循环求和 1, 2, ..., n-1, n + while (i <= n) { + res += i; + i++; // 更新条件变量 + } + return res; + } ``` === "TS" ```typescript title="iteration.ts" - [class]{}-[func]{whileLoop} + /* 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; + } ``` === "Dart" ```dart title="iteration.dart" - [class]{}-[func]{whileLoop} + /* while 循环 */ + int whileLoop(int n) { + int res = 0; + int i = 1; // 初始化条件变量 + // 循环求和 1, 2, ..., n-1, n + while (i <= n) { + res += i; + i++; // 更新条件变量 + } + return res; + } ``` === "Rust" ```rust title="iteration.rs" - [class]{}-[func]{while_loop} + /* 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 + } ``` === "C" ```c title="iteration.c" - [class]{}-[func]{whileLoop} + /* while 循环 */ + int whileLoop(int n) { + int res = 0; + int i = 1; // 初始化条件变量 + // 循环求和 1, 2, ..., n-1, n + while (i <= n) { + res += i; + i++; // 更新条件变量 + } + return res; + } ``` === "Zig" @@ -182,67 +378,198 @@ status: new === "Python" ```python title="iteration.py" - [class]{}-[func]{while_loop_ii} + def while_loop_ii(n: int) -> int: + """while 循环(两次更新)""" + res = 0 + i = 1 # 初始化条件变量 + # 循环求和 1, 4, ... + while i <= n: + res += i + # 更新条件变量 + i += 1 + i *= 2 + return res ``` === "C++" ```cpp title="iteration.cpp" - [class]{}-[func]{whileLoopII} + /* while 循环(两次更新) */ + int whileLoopII(int n) { + int res = 0; + int i = 1; // 初始化条件变量 + // 循环求和 1, 4, ... + while (i <= n) { + res += i; + // 更新条件变量 + i++; + i *= 2; + } + return res; + } ``` === "Java" ```java title="iteration.java" - [class]{iteration}-[func]{whileLoopII} + /* while 循环(两次更新) */ + int whileLoopII(int n) { + int res = 0; + int i = 1; // 初始化条件变量 + // 循环求和 1, 4, ... + while (i <= n) { + res += i; + // 更新条件变量 + i++; + i *= 2; + } + return res; + } ``` === "C#" ```csharp title="iteration.cs" - [class]{iteration}-[func]{whileLoopII} + /* while 循环(两次更新) */ + int whileLoopII(int n) { + int res = 0; + int i = 1; // 初始化条件变量 + // 循环求和 1, 2, 4, 5... + while (i <= n) { + res += i; + // 更新条件变量 + i += 1; + i *= 2; + } + return res; + } ``` === "Go" ```go title="iteration.go" - [class]{}-[func]{whileLoopII} + /* while 循环(两次更新) */ + func whileLoopII(n int) int { + res := 0 + // 初始化条件变量 + i := 1 + // 循环求和 1, 4, ... + for i <= n { + res += i + // 更新条件变量 + i++ + i *= 2 + } + return res + } ``` === "Swift" ```swift title="iteration.swift" - [class]{}-[func]{whileLoopII} + /* while 循环(两次更新) */ + func whileLoopII(n: Int) -> Int { + var res = 0 + var i = 1 // 初始化条件变量 + // 循环求和 1, 4, ... + while i <= n { + res += i + // 更新条件变量 + i += 1 + i *= 2 + } + return res + } ``` === "JS" ```javascript title="iteration.js" - [class]{}-[func]{whileLoopII} + /* while 循环(两次更新) */ + function whileLoopII(n) { + let res = 0; + let i = 1; // 初始化条件变量 + // 循环求和 1, 4, ... + while (i <= n) { + res += i; + // 更新条件变量 + i++; + i *= 2; + } + return res; + } ``` === "TS" ```typescript title="iteration.ts" - [class]{}-[func]{whileLoopII} + /* while 循环(两次更新) */ + function whileLoopII(n: number): number { + let res = 0; + let i = 1; // 初始化条件变量 + // 循环求和 1, 4, ... + while (i <= n) { + res += i; + // 更新条件变量 + i++; + i *= 2; + } + return res; + } ``` === "Dart" ```dart title="iteration.dart" - [class]{}-[func]{whileLoopII} + /* while 循环(两次更新) */ + int whileLoopII(int n) { + int res = 0; + int i = 1; // 初始化条件变量 + // 循环求和 1, 4, ... + while (i <= n) { + res += i; + // 更新条件变量 + i++; + i *= 2; + } + return res; + } ``` === "Rust" ```rust title="iteration.rs" - [class]{}-[func]{while_loop_ii} + /* while 循环(两次更新) */ + fn while_loop_ii(n: i32) -> i32 { + let mut res = 0; + let mut i = 1; // 初始化条件变量 + // 循环求和 1, 4, ... + while i <= n { + res += i; + // 更新条件变量 + i += 1; + i *= 2; + } + res + } ``` === "C" ```c title="iteration.c" - [class]{}-[func]{whileLoopII} + /* while 循环(两次更新) */ + int whileLoopII(int n) { + int res = 0; + int i = 1; // 初始化条件变量 + // 循环求和 1, 4, ... + while (i <= n) { + res += i; + // 更新条件变量 + i++; + i *= 2; + } + return res; + } ``` === "Zig" @@ -260,67 +587,189 @@ status: new === "Python" ```python title="iteration.py" - [class]{}-[func]{nested_for_loop} + def nested_for_loop(n: int) -> str: + """双层 for 循环""" + res = "" + # 循环 i = 1, 2, ..., n-1, n + for i in range(1, n + 1): + # 循环 j = 1, 2, ..., n-1, n + for j in range(1, n + 1): + res += f"({i}, {j}), " + return res ``` === "C++" ```cpp title="iteration.cpp" - [class]{}-[func]{nestedForLoop} + /* 双层 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(); + } ``` === "Java" ```java title="iteration.java" - [class]{iteration}-[func]{nestedForLoop} + /* 双层 for 循环 */ + String nestedForLoop(int n) { + StringBuilder res = new StringBuilder(); + // 循环 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(); + } ``` === "C#" ```csharp title="iteration.cs" - [class]{iteration}-[func]{nestedForLoop} + /* 双层 for 循环 */ + string nestedForLoop(int n) { + StringBuilder res = new StringBuilder(); + // 循环 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(); + } ``` === "Go" ```go title="iteration.go" - [class]{}-[func]{nestedForLoop} + /* 双层 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 + } ``` === "Swift" ```swift title="iteration.swift" - [class]{}-[func]{nestedForLoop} + /* 双层 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 + } ``` === "JS" ```javascript title="iteration.js" - [class]{}-[func]{nestedForLoop} + /* 双层 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; + } ``` === "TS" ```typescript title="iteration.ts" - [class]{}-[func]{nestedForLoop} + /* 双层 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; + } ``` === "Dart" ```dart title="iteration.dart" - [class]{}-[func]{nestedForLoop} + /* 双层 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; + } ``` === "Rust" ```rust title="iteration.rs" - [class]{}-[func]{nested_for_loop} + /* 双层 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("") + } ``` === "C" ```c title="iteration.c" - [class]{}-[func]{nestedForLoop} + /* 双层 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; + } ``` === "Zig" @@ -357,67 +806,165 @@ status: new === "Python" ```python title="recursion.py" - [class]{}-[func]{recur} + def recur(n: int) -> int: + """递归""" + # 终止条件 + if n == 1: + return 1 + # 递:递归调用 + res = recur(n - 1) + # 归:返回结果 + return n + res ``` === "C++" ```cpp title="recursion.cpp" - [class]{}-[func]{recur} + /* 递归 */ + int recur(int n) { + // 终止条件 + if (n == 1) + return 1; + // 递:递归调用 + int res = recur(n - 1); + // 归:返回结果 + return n + res; + } ``` === "Java" ```java title="recursion.java" - [class]{recursion}-[func]{recur} + /* 递归 */ + int recur(int n) { + // 终止条件 + if (n == 1) + return 1; + // 递:递归调用 + int res = recur(n - 1); + // 归:返回结果 + return n + res; + } ``` === "C#" ```csharp title="recursion.cs" - [class]{recursion}-[func]{recur} + /* 递归 */ + int recur(int n) { + // 终止条件 + if (n == 1) + return 1; + // 递:递归调用 + int res = recur(n - 1); + // 归:返回结果 + return n + res; + } ``` === "Go" ```go title="recursion.go" - [class]{}-[func]{recur} + /* 递归 */ + func recur(n int) int { + // 终止条件 + if n == 1 { + return 1 + } + // 递:递归调用 + res := recur(n - 1) + // 归:返回结果 + return n + res + } ``` === "Swift" ```swift title="recursion.swift" - [class]{}-[func]{recur} + /* 递归 */ + func recur(n: Int) -> Int { + // 终止条件 + if n == 1 { + return 1 + } + // 递:递归调用 + let res = recur(n: n - 1) + // 归:返回结果 + return n + res + } ``` === "JS" ```javascript title="recursion.js" - [class]{}-[func]{recur} + /* 递归 */ + function recur(n) { + // 终止条件 + if (n === 1) return 1; + // 递:递归调用 + const res = recur(n - 1); + // 归:返回结果 + return n + res; + } ``` === "TS" ```typescript title="recursion.ts" - [class]{}-[func]{recur} + /* 递归 */ + function recur(n: number): number { + // 终止条件 + if (n === 1) return 1; + // 递:递归调用 + const res = recur(n - 1); + // 归:返回结果 + return n + res; + } ``` === "Dart" ```dart title="recursion.dart" - [class]{}-[func]{recur} + /* 递归 */ + int recur(int n) { + // 终止条件 + if (n == 1) return 1; + // 递:递归调用 + int res = recur(n - 1); + // 归:返回结果 + return n + res; + } ``` === "Rust" ```rust title="recursion.rs" - [class]{}-[func]{recur} + /* 递归 */ + fn recur(n: i32) -> i32 { + // 终止条件 + if n == 1 { + return 1; + } + // 递:递归调用 + let res = recur(n - 1); + // 归:返回结果 + n + res + } ``` === "C" ```c title="recursion.c" - [class]{}-[func]{recur} + /* 递归 */ + int recur(int n) { + // 终止条件 + if (n == 1) + return 1; + // 递:递归调用 + int res = recur(n - 1); + // 归:返回结果 + return n + res; + } ``` === "Zig" @@ -469,67 +1016,143 @@ status: new === "Python" ```python title="recursion.py" - [class]{}-[func]{tail_recur} + def tail_recur(n, res): + """尾递归""" + # 终止条件 + if n == 0: + return res + # 尾递归调用 + return tail_recur(n - 1, res + n) ``` === "C++" ```cpp title="recursion.cpp" - [class]{}-[func]{tailRecur} + /* 尾递归 */ + int tailRecur(int n, int res) { + // 终止条件 + if (n == 0) + return res; + // 尾递归调用 + return tailRecur(n - 1, res + n); + } ``` === "Java" ```java title="recursion.java" - [class]{recursion}-[func]{tailRecur} + /* 尾递归 */ + int tailRecur(int n, int res) { + // 终止条件 + if (n == 0) + return res; + // 尾递归调用 + return tailRecur(n - 1, res + n); + } ``` === "C#" ```csharp title="recursion.cs" - [class]{recursion}-[func]{tailRecur} + /* 尾递归 */ + int tailRecur(int n, int res) { + // 终止条件 + if (n == 0) + return res; + // 尾递归调用 + return tailRecur(n - 1, res + n); + } ``` === "Go" ```go title="recursion.go" - [class]{}-[func]{tailRecur} + /* 尾递归 */ + func tailRecur(n int, res int) int { + // 终止条件 + if n == 0 { + return res + } + // 尾递归调用 + return tailRecur(n-1, res+n) + } ``` === "Swift" ```swift title="recursion.swift" - [class]{}-[func]{tailRecur} + /* 尾递归 */ + func tailRecur(n: Int, res: Int) -> Int { + // 终止条件 + if n == 0 { + return res + } + // 尾递归调用 + return tailRecur(n: n - 1, res: res + n) + } ``` === "JS" ```javascript title="recursion.js" - [class]{}-[func]{tailRecur} + /* 尾递归 */ + function tailRecur(n, res) { + // 终止条件 + if (n === 0) return res; + // 尾递归调用 + return tailRecur(n - 1, res + n); + } ``` === "TS" ```typescript title="recursion.ts" - [class]{}-[func]{tailRecur} + /* 尾递归 */ + function tailRecur(n: number, res: number): number { + // 终止条件 + if (n === 0) return res; + // 尾递归调用 + return tailRecur(n - 1, res + n); + } ``` === "Dart" ```dart title="recursion.dart" - [class]{}-[func]{tailRecur} + /* 尾递归 */ + int tailRecur(int n, int res) { + // 终止条件 + if (n == 0) return res; + // 尾递归调用 + return tailRecur(n - 1, res + n); + } ``` === "Rust" ```rust title="recursion.rs" - [class]{}-[func]{tail_recur} + /* 尾递归 */ + fn tail_recur(n: i32, res: i32) -> i32 { + // 终止条件 + if n == 0 { + return res; + } + // 尾递归调用 + tail_recur(n - 1, res + n) + } ``` === "C" ```c title="recursion.c" - [class]{}-[func]{tailRecur} + /* 尾递归 */ + int tailRecur(int n, int res) { + // 终止条件 + if (n == 0) + return res; + // 尾递归调用 + return tailRecur(n - 1, res + n); + } ``` === "Zig" @@ -569,67 +1192,165 @@ status: new === "Python" ```python title="recursion.py" - [class]{}-[func]{fib} + def fib(n: int) -> int: + """斐波那契数列:递归""" + # 终止条件 f(1) = 0, f(2) = 1 + if n == 1 or n == 2: + return n - 1 + # 递归调用 f(n) = f(n-1) + f(n-2) + res = fib(n - 1) + fib(n - 2) + # 返回结果 f(n) + return res ``` === "C++" ```cpp title="recursion.cpp" - [class]{}-[func]{fib} + /* 斐波那契数列:递归 */ + 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; + } ``` === "Java" ```java title="recursion.java" - [class]{recursion}-[func]{fib} + /* 斐波那契数列:递归 */ + 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; + } ``` === "C#" ```csharp title="recursion.cs" - [class]{recursion}-[func]{fib} + /* 斐波那契数列:递归 */ + 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; + } ``` === "Go" ```go title="recursion.go" - [class]{}-[func]{fib} + /* 斐波那契数列:递归 */ + 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 + } ``` === "Swift" ```swift title="recursion.swift" - [class]{}-[func]{fib} + /* 斐波那契数列:递归 */ + 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 + } ``` === "JS" ```javascript title="recursion.js" - [class]{}-[func]{fib} + /* 斐波那契数列:递归 */ + 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; + } ``` === "TS" ```typescript title="recursion.ts" - [class]{}-[func]{fib} + /* 斐波那契数列:递归 */ + 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; + } ``` === "Dart" ```dart title="recursion.dart" - [class]{}-[func]{fib} + /* 斐波那契数列:递归 */ + 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; + } ``` === "Rust" ```rust title="recursion.rs" - [class]{}-[func]{fib} + /* 斐波那契数列:递归 */ + 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 + } ``` === "C" ```c title="recursion.c" - [class]{}-[func]{fib} + /* 斐波那契数列:递归 */ + 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; + } ``` === "Zig" @@ -682,25 +1403,91 @@ status: new === "Python" ```python title="recursion.py" - [class]{}-[func]{for_loop_recur} + def for_loop_recur(n: int) -> int: + """使用迭代模拟递归""" + # 使用一个显式的栈来模拟系统调用栈 + stack = [] + res = 0 + # 递:递归调用 + for i in range(n, 0, -1): + # 通过“入栈操作”模拟“递” + stack.append(i) + # 归:返回结果 + while stack: + # 通过“出栈操作”模拟“归” + res += stack.pop() + # res = 1+2+3+...+n + return res ``` === "C++" ```cpp title="recursion.cpp" - [class]{}-[func]{forLoopRecur} + /* 使用迭代模拟递归 */ + 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; + } ``` === "Java" ```java title="recursion.java" - [class]{recursion}-[func]{forLoopRecur} + /* 使用迭代模拟递归 */ + int forLoopRecur(int n) { + // 使用一个显式的栈来模拟系统调用栈 + Stack stack = new Stack<>(); + int res = 0; + // 递:递归调用 + for (int i = n; i > 0; i--) { + // 通过“入栈操作”模拟“递” + stack.push(i); + } + // 归:返回结果 + while (!stack.isEmpty()) { + // 通过“出栈操作”模拟“归” + res += stack.pop(); + } + // res = 1+2+3+...+n + return res; + } ``` === "C#" ```csharp title="recursion.cs" - [class]{recursion}-[func]{forLoopRecur} + /* 使用迭代模拟递归 */ + int forLoopRecur(int n) { + // 使用一个显式的栈来模拟系统调用栈 + Stack stack = new Stack(); + 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; + } ``` === "Go" @@ -712,31 +1499,116 @@ status: new === "Swift" ```swift title="recursion.swift" - [class]{}-[func]{forLoopRecur} + /* 使用迭代模拟递归 */ + func forLoopRecur(n: Int) -> Int { + // 使用一个显式的栈来模拟系统调用栈 + var stack: [Int] = [] + var res = 0 + // 递:递归调用 + for i in stride(from: n, to: 0, by: -1) { + // 通过“入栈操作”模拟“递” + stack.append(i) + } + // 归:返回结果 + while !stack.isEmpty { + // 通过“出栈操作”模拟“归” + res += stack.removeLast() + } + // res = 1+2+3+...+n + return res + } ``` === "JS" ```javascript title="recursion.js" - [class]{}-[func]{forLoopRecur} + /* 使用迭代模拟递归 */ + function forLoopRecur(n) { + // 使用一个显式的栈来模拟系统调用栈 + const stack = []; + let res = 0; + // 递:递归调用 + for (let i = 1; i <= n; i++) { + // 通过“入栈操作”模拟“递” + stack.push(i); + } + // 归:返回结果 + while (stack.length) { + // 通过“出栈操作”模拟“归” + res += stack.pop(); + } + // res = 1+2+3+...+n + return res; + } ``` === "TS" ```typescript title="recursion.ts" - [class]{}-[func]{forLoopRecur} + /* 使用迭代模拟递归 */ + function forLoopRecur(n: number): number { + // 使用一个显式的栈来模拟系统调用栈 + const stack: number[] = []; + let res: number = 0; + // 递:递归调用 + for (let i = 1; i <= n; i++) { + // 通过“入栈操作”模拟“递” + stack.push(i); + } + // 归:返回结果 + while (stack.length) { + // 通过“出栈操作”模拟“归” + res += stack.pop(); + } + // res = 1+2+3+...+n + return res; + } ``` === "Dart" ```dart title="recursion.dart" - [class]{}-[func]{forLoopRecur} + /* 使用迭代模拟递归 */ + 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; + } ``` === "Rust" ```rust title="recursion.rs" - [class]{}-[func]{for_loop_recur} + /* 使用迭代模拟递归 */ + 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 + } ``` === "C" diff --git a/zh/chapter_computational_complexity/space_complexity.md b/zh/chapter_computational_complexity/space_complexity.md index 834c396ad..d9724e374 100755 --- a/zh/chapter_computational_complexity/space_complexity.md +++ b/zh/chapter_computational_complexity/space_complexity.md @@ -730,89 +730,298 @@ $$ === "Python" ```python title="space_complexity.py" - [class]{}-[func]{function} + def function() -> int: + """函数""" + # 执行某些操作 + return 0 - [class]{}-[func]{constant} + def constant(n: int): + """常数阶""" + # 常量、变量、对象占用 O(1) 空间 + a = 0 + nums = [0] * 10000 + node = ListNode(0) + # 循环中的变量占用 O(1) 空间 + for _ in range(n): + c = 0 + # 循环中的函数占用 O(1) 空间 + for _ in range(n): + function() ``` === "C++" ```cpp title="space_complexity.cpp" - [class]{}-[func]{func} + /* 函数 */ + int func() { + // 执行某些操作 + return 0; + } - [class]{}-[func]{constant} + /* 常数阶 */ + 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(); + } + } ``` === "Java" ```java title="space_complexity.java" - [class]{space_complexity}-[func]{function} + /* 函数 */ + int function() { + // 执行某些操作 + return 0; + } - [class]{space_complexity}-[func]{constant} + /* 常数阶 */ + void constant(int n) { + // 常量、变量、对象占用 O(1) 空间 + final int a = 0; + int b = 0; + int[] nums = new int[10000]; + ListNode node = new ListNode(0); + // 循环中的变量占用 O(1) 空间 + for (int i = 0; i < n; i++) { + int c = 0; + } + // 循环中的函数占用 O(1) 空间 + for (int i = 0; i < n; i++) { + function(); + } + } ``` === "C#" ```csharp title="space_complexity.cs" - [class]{space_complexity}-[func]{function} + /* 函数 */ + int function() { + // 执行某些操作 + return 0; + } - [class]{space_complexity}-[func]{constant} + /* 常数阶 */ + void constant(int n) { + // 常量、变量、对象占用 O(1) 空间 + int a = 0; + int b = 0; + int[] nums = new int[10000]; + ListNode node = new ListNode(0); + // 循环中的变量占用 O(1) 空间 + for (int i = 0; i < n; i++) { + int c = 0; + } + // 循环中的函数占用 O(1) 空间 + for (int i = 0; i < n; i++) { + function(); + } + } ``` === "Go" ```go title="space_complexity.go" - [class]{}-[func]{function} + /* 函数 */ + func function() int { + // 执行某些操作... + return 0 + } - [class]{}-[func]{spaceConstant} + /* 常数阶 */ + func spaceConstant(n int) { + // 常量、变量、对象占用 O(1) 空间 + const a = 0 + b := 0 + nums := make([]int, 10000) + ListNode := newNode(0) + // 循环中的变量占用 O(1) 空间 + var c int + for i := 0; i < n; i++ { + c = 0 + } + // 循环中的函数占用 O(1) 空间 + for i := 0; i < n; i++ { + function() + } + fmt.Println(a, b, nums, c, ListNode) + } ``` === "Swift" ```swift title="space_complexity.swift" - [class]{}-[func]{function} + /* 函数 */ + @discardableResult + func function() -> Int { + // 执行某些操作 + return 0 + } - [class]{}-[func]{constant} + /* 常数阶 */ + 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() + } + } ``` === "JS" ```javascript title="space_complexity.js" - [class]{}-[func]{constFunc} + /* 函数 */ + function constFunc() { + // 执行某些操作 + return 0; + } - [class]{}-[func]{constant} + /* 常数阶 */ + 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(); + } + } ``` === "TS" ```typescript title="space_complexity.ts" - [class]{}-[func]{constFunc} + /* 函数 */ + function constFunc(): number { + // 执行某些操作 + return 0; + } - [class]{}-[func]{constant} + /* 常数阶 */ + 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(); + } + } ``` === "Dart" ```dart title="space_complexity.dart" - [class]{}-[func]{function} + /* 函数 */ + int function() { + // 执行某些操作 + return 0; + } - [class]{}-[func]{constant} + /* 常数阶 */ + 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(); + } + } ``` === "Rust" ```rust title="space_complexity.rs" - [class]{}-[func]{function} + /* 函数 */ + fn function() ->i32 { + // 执行某些操作 + return 0; + } - [class]{}-[func]{constant} + /* 常数阶 */ + #[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(); + } + } ``` === "C" ```c title="space_complexity.c" - [class]{}-[func]{func} + /* 函数 */ + int func() { + // 执行某些操作 + return 0; + } - [class]{}-[func]{constant} + /* 常数阶 */ + 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(); + } + } ``` === "Zig" @@ -820,7 +1029,29 @@ $$ ```zig title="space_complexity.zig" [class]{}-[func]{function} - [class]{}-[func]{constant} + // 常数阶 + fn constant(n: i32) void { + // 常量、变量、对象占用 O(1) 空间 + const a: i32 = 0; + var b: i32 = 0; + var nums = [_]i32{0}**10000; + var node = inc.ListNode(i32){.val = 0}; + var i: i32 = 0; + // 循环中的变量占用 O(1) 空间 + while (i < n) : (i += 1) { + var c: i32 = 0; + _ = c; + } + // 循环中的函数占用 O(1) 空间 + i = 0; + while (i < n) : (i += 1) { + _ = function(); + } + _ = a; + _ = b; + _ = nums; + _ = node; + } ``` ### 2.   线性阶 $O(n)$ @@ -830,75 +1061,263 @@ $$ === "Python" ```python title="space_complexity.py" - [class]{}-[func]{linear} + def linear(n: int): + """线性阶""" + # 长度为 n 的列表占用 O(n) 空间 + nums = [0] * n + # 长度为 n 的哈希表占用 O(n) 空间 + hmap = dict[int, str]() + for i in range(n): + hmap[i] = str(i) ``` === "C++" ```cpp title="space_complexity.cpp" - [class]{}-[func]{linear} + /* 线性阶 */ + 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); + } + } ``` === "Java" ```java title="space_complexity.java" - [class]{space_complexity}-[func]{linear} + /* 线性阶 */ + void linear(int n) { + // 长度为 n 的数组占用 O(n) 空间 + int[] nums = new int[n]; + // 长度为 n 的列表占用 O(n) 空间 + List nodes = new ArrayList<>(); + for (int i = 0; i < n; i++) { + nodes.add(new ListNode(i)); + } + // 长度为 n 的哈希表占用 O(n) 空间 + Map map = new HashMap<>(); + for (int i = 0; i < n; i++) { + map.put(i, String.valueOf(i)); + } + } ``` === "C#" ```csharp title="space_complexity.cs" - [class]{space_complexity}-[func]{linear} + /* 线性阶 */ + void linear(int n) { + // 长度为 n 的数组占用 O(n) 空间 + int[] nums = new int[n]; + // 长度为 n 的列表占用 O(n) 空间 + List nodes = new(); + for (int i = 0; i < n; i++) { + nodes.Add(new ListNode(i)); + } + // 长度为 n 的哈希表占用 O(n) 空间 + Dictionary map = new(); + for (int i = 0; i < n; i++) { + map.Add(i, i.ToString()); + } + } ``` === "Go" ```go title="space_complexity.go" - [class]{}-[func]{spaceLinear} + /* 线性阶 */ + 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) + } + } ``` === "Swift" ```swift title="space_complexity.swift" - [class]{}-[func]{linear} + /* 线性阶 */ + 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)") }) + } ``` === "JS" ```javascript title="space_complexity.js" - [class]{}-[func]{linear} + /* 线性阶 */ + 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()); + } + } ``` === "TS" ```typescript title="space_complexity.ts" - [class]{}-[func]{linear} + /* 线性阶 */ + 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()); + } + } ``` === "Dart" ```dart title="space_complexity.dart" - [class]{}-[func]{linear} + /* 线性阶 */ + 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()); + } + } ``` === "Rust" ```rust title="space_complexity.rs" - [class]{}-[func]{linear} + /* 线性阶 */ + #[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()); + } + } ``` === "C" ```c title="space_complexity.c" - [class]{hashTable}-[func]{} + /* 哈希表 */ + struct hashTable { + int key; + int val; + UT_hash_handle hh; // 基于 uthash.h 实现 + }; - [class]{}-[func]{linear} + typedef struct hashTable hashTable; + + /* 线性阶 */ + 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); + } + } ``` === "Zig" ```zig title="space_complexity.zig" - [class]{}-[func]{linear} + // 线性阶 + fn linear(comptime n: i32) !void { + // 长度为 n 的数组占用 O(n) 空间 + var nums = [_]i32{0}**n; + // 长度为 n 的列表占用 O(n) 空间 + var nodes = std.ArrayList(i32).init(std.heap.page_allocator); + defer nodes.deinit(); + var i: i32 = 0; + while (i < n) : (i += 1) { + try nodes.append(i); + } + // 长度为 n 的哈希表占用 O(n) 空间 + var map = std.AutoArrayHashMap(i32, []const u8).init(std.heap.page_allocator); + defer map.deinit(); + var j: i32 = 0; + while (j < n) : (j += 1) { + const string = try std.fmt.allocPrint(std.heap.page_allocator, "{d}", .{j}); + defer std.heap.page_allocator.free(string); + try map.put(i, string); + } + _ = nums; + } ``` 如图 2-17 所示,此函数的递归深度为 $n$ ,即同时存在 $n$ 个未返回的 `linear_recur()` 函数,使用 $O(n)$ 大小的栈帧空间: @@ -906,73 +1325,140 @@ $$ === "Python" ```python title="space_complexity.py" - [class]{}-[func]{linear_recur} + def linear_recur(n: int): + """线性阶(递归实现)""" + print("递归 n =", n) + if n == 1: + return + linear_recur(n - 1) ``` === "C++" ```cpp title="space_complexity.cpp" - [class]{}-[func]{linearRecur} + /* 线性阶(递归实现) */ + void linearRecur(int n) { + cout << "递归 n = " << n << endl; + if (n == 1) + return; + linearRecur(n - 1); + } ``` === "Java" ```java title="space_complexity.java" - [class]{space_complexity}-[func]{linearRecur} + /* 线性阶(递归实现) */ + void linearRecur(int n) { + System.out.println("递归 n = " + n); + if (n == 1) + return; + linearRecur(n - 1); + } ``` === "C#" ```csharp title="space_complexity.cs" - [class]{space_complexity}-[func]{linearRecur} + /* 线性阶(递归实现) */ + void linearRecur(int n) { + Console.WriteLine("递归 n = " + n); + if (n == 1) return; + linearRecur(n - 1); + } ``` === "Go" ```go title="space_complexity.go" - [class]{}-[func]{spaceLinearRecur} + /* 线性阶(递归实现) */ + func spaceLinearRecur(n int) { + fmt.Println("递归 n =", n) + if n == 1 { + return + } + spaceLinearRecur(n - 1) + } ``` === "Swift" ```swift title="space_complexity.swift" - [class]{}-[func]{linearRecur} + /* 线性阶(递归实现) */ + func linearRecur(n: Int) { + print("递归 n = \(n)") + if n == 1 { + return + } + linearRecur(n: n - 1) + } ``` === "JS" ```javascript title="space_complexity.js" - [class]{}-[func]{linearRecur} + /* 线性阶(递归实现) */ + function linearRecur(n) { + console.log(`递归 n = ${n}`); + if (n === 1) return; + linearRecur(n - 1); + } ``` === "TS" ```typescript title="space_complexity.ts" - [class]{}-[func]{linearRecur} + /* 线性阶(递归实现) */ + function linearRecur(n: number): void { + console.log(`递归 n = ${n}`); + if (n === 1) return; + linearRecur(n - 1); + } ``` === "Dart" ```dart title="space_complexity.dart" - [class]{}-[func]{linearRecur} + /* 线性阶(递归实现) */ + void linearRecur(int n) { + print('递归 n = $n'); + if (n == 1) return; + linearRecur(n - 1); + } ``` === "Rust" ```rust title="space_complexity.rs" - [class]{}-[func]{linear_recur} + /* 线性阶(递归实现) */ + fn linear_recur(n: i32) { + println!("递归 n = {}", n); + if n == 1 {return}; + linear_recur(n - 1); + } ``` === "C" ```c title="space_complexity.c" - [class]{}-[func]{linearRecur} + /* 线性阶(递归实现) */ + void linearRecur(int n) { + printf("递归 n = %d\r\n", n); + if (n == 1) + return; + linearRecur(n - 1); + } ``` === "Zig" ```zig title="space_complexity.zig" - [class]{}-[func]{linearRecur} + // 线性阶(递归实现) + fn linearRecur(comptime n: i32) void { + std.debug.print("递归 n = {}\n", .{n}); + if (n == 1) return; + linearRecur(n - 1); + } ``` ![递归函数产生的线性阶空间复杂度](space_complexity.assets/space_complexity_recursive_linear.png) @@ -986,73 +1472,213 @@ $$ === "Python" ```python title="space_complexity.py" - [class]{}-[func]{quadratic} + def quadratic(n: int): + """平方阶""" + # 二维列表占用 O(n^2) 空间 + num_matrix = [[0] * n for _ in range(n)] ``` === "C++" ```cpp title="space_complexity.cpp" - [class]{}-[func]{quadratic} + /* 平方阶 */ + 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); + } + } ``` === "Java" ```java title="space_complexity.java" - [class]{space_complexity}-[func]{quadratic} + /* 平方阶 */ + void quadratic(int n) { + // 矩阵占用 O(n^2) 空间 + int[][] numMatrix = new int[n][n]; + // 二维列表占用 O(n^2) 空间 + List> numList = new ArrayList<>(); + for (int i = 0; i < n; i++) { + List tmp = new ArrayList<>(); + for (int j = 0; j < n; j++) { + tmp.add(0); + } + numList.add(tmp); + } + } ``` === "C#" ```csharp title="space_complexity.cs" - [class]{space_complexity}-[func]{quadratic} + /* 平方阶 */ + void quadratic(int n) { + // 矩阵占用 O(n^2) 空间 + int[,] numMatrix = new int[n, n]; + // 二维列表占用 O(n^2) 空间 + List> numList = new(); + for (int i = 0; i < n; i++) { + List tmp = new(); + for (int j = 0; j < n; j++) { + tmp.Add(0); + } + numList.Add(tmp); + } + } ``` === "Go" ```go title="space_complexity.go" - [class]{}-[func]{spaceQuadratic} + /* 平方阶 */ + func spaceQuadratic(n int) { + // 矩阵占用 O(n^2) 空间 + numMatrix := make([][]int, n) + for i := 0; i < n; i++ { + numMatrix[i] = make([]int, n) + } + } ``` === "Swift" ```swift title="space_complexity.swift" - [class]{}-[func]{quadratic} + /* 平方阶 */ + func quadratic(n: Int) { + // 二维列表占用 O(n^2) 空间 + let numList = Array(repeating: Array(repeating: 0, count: n), count: n) + } ``` === "JS" ```javascript title="space_complexity.js" - [class]{}-[func]{quadratic} + /* 平方阶 */ + 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); + } + } ``` === "TS" ```typescript title="space_complexity.ts" - [class]{}-[func]{quadratic} + /* 平方阶 */ + 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); + } + } ``` === "Dart" ```dart title="space_complexity.dart" - [class]{}-[func]{quadratic} + /* 平方阶 */ + 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); + } + } ``` === "Rust" ```rust title="space_complexity.rs" - [class]{}-[func]{quadratic} + /* 平方阶 */ + #[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); + } + } ``` === "C" ```c title="space_complexity.c" - [class]{}-[func]{quadratic} + /* 平方阶 */ + 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); + } ``` === "Zig" ```zig title="space_complexity.zig" - [class]{}-[func]{quadratic} + // 平方阶 + fn quadratic(n: i32) !void { + // 二维列表占用 O(n^2) 空间 + var nodes = std.ArrayList(std.ArrayList(i32)).init(std.heap.page_allocator); + defer nodes.deinit(); + var i: i32 = 0; + while (i < n) : (i += 1) { + var tmp = std.ArrayList(i32).init(std.heap.page_allocator); + defer tmp.deinit(); + var j: i32 = 0; + while (j < n) : (j += 1) { + try tmp.append(0); + } + try nodes.append(tmp); + } + } ``` 如图 2-18 所示,该函数的递归深度为 $n$ ,在每个递归函数中都初始化了一个数组,长度分别为 $n$、$n-1$、$\dots$、$2$、$1$ ,平均长度为 $n / 2$ ,因此总体占用 $O(n^2)$ 空间: @@ -1060,73 +1686,158 @@ $$ === "Python" ```python title="space_complexity.py" - [class]{}-[func]{quadratic_recur} + def quadratic_recur(n: int) -> int: + """平方阶(递归实现)""" + if n <= 0: + return 0 + # 数组 nums 长度为 n, n-1, ..., 2, 1 + nums = [0] * n + return quadratic_recur(n - 1) ``` === "C++" ```cpp title="space_complexity.cpp" - [class]{}-[func]{quadraticRecur} + /* 平方阶(递归实现) */ + int quadraticRecur(int n) { + if (n <= 0) + return 0; + vector nums(n); + cout << "递归 n = " << n << " 中的 nums 长度 = " << nums.size() << endl; + return quadraticRecur(n - 1); + } ``` === "Java" ```java title="space_complexity.java" - [class]{space_complexity}-[func]{quadraticRecur} + /* 平方阶(递归实现) */ + int quadraticRecur(int n) { + if (n <= 0) + return 0; + // 数组 nums 长度为 n, n-1, ..., 2, 1 + int[] nums = new int[n]; + System.out.println("递归 n = " + n + " 中的 nums 长度 = " + nums.length); + return quadraticRecur(n - 1); + } ``` === "C#" ```csharp title="space_complexity.cs" - [class]{space_complexity}-[func]{quadraticRecur} + /* 平方阶(递归实现) */ + 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); + } ``` === "Go" ```go title="space_complexity.go" - [class]{}-[func]{spaceQuadraticRecur} + /* 平方阶(递归实现) */ + 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) + } ``` === "Swift" ```swift title="space_complexity.swift" - [class]{}-[func]{quadraticRecur} + /* 平方阶(递归实现) */ + @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) + } ``` === "JS" ```javascript title="space_complexity.js" - [class]{}-[func]{quadraticRecur} + /* 平方阶(递归实现) */ + function quadraticRecur(n) { + if (n <= 0) return 0; + const nums = new Array(n); + console.log(`递归 n = ${n} 中的 nums 长度 = ${nums.length}`); + return quadraticRecur(n - 1); + } ``` === "TS" ```typescript title="space_complexity.ts" - [class]{}-[func]{quadraticRecur} + /* 平方阶(递归实现) */ + 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); + } ``` === "Dart" ```dart title="space_complexity.dart" - [class]{}-[func]{quadraticRecur} + /* 平方阶(递归实现) */ + 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); + } ``` === "Rust" ```rust title="space_complexity.rs" - [class]{}-[func]{quadratic_recur} + /* 平方阶(递归实现) */ + 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); + } ``` === "C" ```c title="space_complexity.c" - [class]{}-[func]{quadraticRecur} + /* 平方阶(递归实现) */ + 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; + } ``` === "Zig" ```zig title="space_complexity.zig" - [class]{}-[func]{quadraticRecur} + // 平方阶(递归实现) + 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); + } ``` ![递归函数产生的平方阶空间复杂度](space_complexity.assets/space_complexity_recursive_quadratic.png) @@ -1140,73 +1851,165 @@ $$ === "Python" ```python title="space_complexity.py" - [class]{}-[func]{build_tree} + def build_tree(n: int) -> TreeNode | None: + """指数阶(建立满二叉树)""" + if n == 0: + return None + root = TreeNode(0) + root.left = build_tree(n - 1) + root.right = build_tree(n - 1) + return root ``` === "C++" ```cpp title="space_complexity.cpp" - [class]{}-[func]{buildTree} + /* 指数阶(建立满二叉树) */ + 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; + } ``` === "Java" ```java title="space_complexity.java" - [class]{space_complexity}-[func]{buildTree} + /* 指数阶(建立满二叉树) */ + TreeNode buildTree(int n) { + if (n == 0) + return null; + TreeNode root = new TreeNode(0); + root.left = buildTree(n - 1); + root.right = buildTree(n - 1); + return root; + } ``` === "C#" ```csharp title="space_complexity.cs" - [class]{space_complexity}-[func]{buildTree} + /* 指数阶(建立满二叉树) */ + TreeNode? buildTree(int n) { + if (n == 0) return null; + TreeNode root = new TreeNode(0); + root.left = buildTree(n - 1); + root.right = buildTree(n - 1); + return root; + } ``` === "Go" ```go title="space_complexity.go" - [class]{}-[func]{buildTree} + /* 指数阶(建立满二叉树) */ + 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 + } ``` === "Swift" ```swift title="space_complexity.swift" - [class]{}-[func]{buildTree} + /* 指数阶(建立满二叉树) */ + 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 + } ``` === "JS" ```javascript title="space_complexity.js" - [class]{}-[func]{buildTree} + /* 指数阶(建立满二叉树) */ + 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; + } ``` === "TS" ```typescript title="space_complexity.ts" - [class]{}-[func]{buildTree} + /* 指数阶(建立满二叉树) */ + 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; + } ``` === "Dart" ```dart title="space_complexity.dart" - [class]{}-[func]{buildTree} + /* 指数阶(建立满二叉树) */ + 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; + } ``` === "Rust" ```rust title="space_complexity.rs" - [class]{}-[func]{build_tree} + /* 指数阶(建立满二叉树) */ + 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); + } ``` === "C" ```c title="space_complexity.c" - [class]{}-[func]{buildTree} + /* 指数阶(建立满二叉树) */ + 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; + } ``` === "Zig" ```zig title="space_complexity.zig" - [class]{}-[func]{buildTree} + // 指数阶(建立满二叉树) + 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; + } ``` ![满二叉树产生的指数阶空间复杂度](space_complexity.assets/space_complexity_exponential.png) diff --git a/zh/chapter_computational_complexity/time_complexity.md b/zh/chapter_computational_complexity/time_complexity.md index 32b488584..5f6275126 100755 --- a/zh/chapter_computational_complexity/time_complexity.md +++ b/zh/chapter_computational_complexity/time_complexity.md @@ -914,73 +914,164 @@ $$ === "Python" ```python title="time_complexity.py" - [class]{}-[func]{constant} + def constant(n: int) -> int: + """常数阶""" + count = 0 + size = 100000 + for _ in range(size): + count += 1 + return count ``` === "C++" ```cpp title="time_complexity.cpp" - [class]{}-[func]{constant} + /* 常数阶 */ + int constant(int n) { + int count = 0; + int size = 100000; + for (int i = 0; i < size; i++) + count++; + return count; + } ``` === "Java" ```java title="time_complexity.java" - [class]{time_complexity}-[func]{constant} + /* 常数阶 */ + int constant(int n) { + int count = 0; + int size = 100000; + for (int i = 0; i < size; i++) + count++; + return count; + } ``` === "C#" ```csharp title="time_complexity.cs" - [class]{time_complexity}-[func]{constant} + /* 常数阶 */ + int constant(int n) { + int count = 0; + int size = 100000; + for (int i = 0; i < size; i++) + count++; + return count; + } ``` === "Go" ```go title="time_complexity.go" - [class]{}-[func]{constant} + /* 常数阶 */ + func constant(n int) int { + count := 0 + size := 100000 + for i := 0; i < size; i++ { + count++ + } + return count + } ``` === "Swift" ```swift title="time_complexity.swift" - [class]{}-[func]{constant} + /* 常数阶 */ + func constant(n: Int) -> Int { + var count = 0 + let size = 100_000 + for _ in 0 ..< size { + count += 1 + } + return count + } ``` === "JS" ```javascript title="time_complexity.js" - [class]{}-[func]{constant} + /* 常数阶 */ + function constant(n) { + let count = 0; + const size = 100000; + for (let i = 0; i < size; i++) count++; + return count; + } ``` === "TS" ```typescript title="time_complexity.ts" - [class]{}-[func]{constant} + /* 常数阶 */ + function constant(n: number): number { + let count = 0; + const size = 100000; + for (let i = 0; i < size; i++) count++; + return count; + } ``` === "Dart" ```dart title="time_complexity.dart" - [class]{}-[func]{constant} + /* 常数阶 */ + int constant(int n) { + int count = 0; + int size = 100000; + for (var i = 0; i < size; i++) { + count++; + } + return count; + } ``` === "Rust" ```rust title="time_complexity.rs" - [class]{}-[func]{constant} + /* 常数阶 */ + fn constant(n: i32) -> i32 { + _ = n; + let mut count = 0; + let size = 100_000; + for _ in 0..size { + count += 1; + } + count + } ``` === "C" ```c title="time_complexity.c" - [class]{}-[func]{constant} + /* 常数阶 */ + int constant(int n) { + int count = 0; + int size = 100000; + int i = 0; + for (int i = 0; i < size; i++) { + count++; + } + return count; + } ``` === "Zig" ```zig title="time_complexity.zig" - [class]{}-[func]{constant} + // 常数阶 + fn constant(n: i32) i32 { + _ = n; + var count: i32 = 0; + const size: i32 = 100_000; + var i: i32 = 0; + while(i int: + """线性阶""" + count = 0 + for _ in range(n): + count += 1 + return count ``` === "C++" ```cpp title="time_complexity.cpp" - [class]{}-[func]{linear} + /* 线性阶 */ + int linear(int n) { + int count = 0; + for (int i = 0; i < n; i++) + count++; + return count; + } ``` === "Java" ```java title="time_complexity.java" - [class]{time_complexity}-[func]{linear} + /* 线性阶 */ + int linear(int n) { + int count = 0; + for (int i = 0; i < n; i++) + count++; + return count; + } ``` === "C#" ```csharp title="time_complexity.cs" - [class]{time_complexity}-[func]{linear} + /* 线性阶 */ + int linear(int n) { + int count = 0; + for (int i = 0; i < n; i++) + count++; + return count; + } ``` === "Go" ```go title="time_complexity.go" - [class]{}-[func]{linear} + /* 线性阶 */ + func linear(n int) int { + count := 0 + for i := 0; i < n; i++ { + count++ + } + return count + } ``` === "Swift" ```swift title="time_complexity.swift" - [class]{}-[func]{linear} + /* 线性阶 */ + func linear(n: Int) -> Int { + var count = 0 + for _ in 0 ..< n { + count += 1 + } + return count + } ``` === "JS" ```javascript title="time_complexity.js" - [class]{}-[func]{linear} + /* 线性阶 */ + function linear(n) { + let count = 0; + for (let i = 0; i < n; i++) count++; + return count; + } ``` === "TS" ```typescript title="time_complexity.ts" - [class]{}-[func]{linear} + /* 线性阶 */ + function linear(n: number): number { + let count = 0; + for (let i = 0; i < n; i++) count++; + return count; + } ``` === "Dart" ```dart title="time_complexity.dart" - [class]{}-[func]{linear} + /* 线性阶 */ + int linear(int n) { + int count = 0; + for (var i = 0; i < n; i++) { + count++; + } + return count; + } ``` === "Rust" ```rust title="time_complexity.rs" - [class]{}-[func]{linear} + /* 线性阶 */ + fn linear(n: i32) -> i32 { + let mut count = 0; + for _ in 0..n { + count += 1; + } + count + } ``` === "C" ```c title="time_complexity.c" - [class]{}-[func]{linear} + /* 线性阶 */ + int linear(int n) { + int count = 0; + for (int i = 0; i < n; i++) { + count++; + } + return count; + } ``` === "Zig" ```zig title="time_complexity.zig" - [class]{}-[func]{linear} + // 线性阶 + fn linear(n: i32) i32 { + var count: i32 = 0; + var i: i32 = 0; + while (i < n) : (i += 1) { + count += 1; + } + return count; + } ``` 遍历数组和遍历链表等操作的时间复杂度均为 $O(n)$ ,其中 $n$ 为数组或链表的长度: @@ -1064,73 +1231,167 @@ $$ === "Python" ```python title="time_complexity.py" - [class]{}-[func]{array_traversal} + def array_traversal(nums: list[int]) -> int: + """线性阶(遍历数组)""" + count = 0 + # 循环次数与数组长度成正比 + for num in nums: + count += 1 + return count ``` === "C++" ```cpp title="time_complexity.cpp" - [class]{}-[func]{arrayTraversal} + /* 线性阶(遍历数组) */ + int arrayTraversal(vector &nums) { + int count = 0; + // 循环次数与数组长度成正比 + for (int num : nums) { + count++; + } + return count; + } ``` === "Java" ```java title="time_complexity.java" - [class]{time_complexity}-[func]{arrayTraversal} + /* 线性阶(遍历数组) */ + int arrayTraversal(int[] nums) { + int count = 0; + // 循环次数与数组长度成正比 + for (int num : nums) { + count++; + } + return count; + } ``` === "C#" ```csharp title="time_complexity.cs" - [class]{time_complexity}-[func]{arrayTraversal} + /* 线性阶(遍历数组) */ + int arrayTraversal(int[] nums) { + int count = 0; + // 循环次数与数组长度成正比 + foreach (int num in nums) { + count++; + } + return count; + } ``` === "Go" ```go title="time_complexity.go" - [class]{}-[func]{arrayTraversal} + /* 线性阶(遍历数组) */ + func arrayTraversal(nums []int) int { + count := 0 + // 循环次数与数组长度成正比 + for range nums { + count++ + } + return count + } ``` === "Swift" ```swift title="time_complexity.swift" - [class]{}-[func]{arrayTraversal} + /* 线性阶(遍历数组) */ + func arrayTraversal(nums: [Int]) -> Int { + var count = 0 + // 循环次数与数组长度成正比 + for _ in nums { + count += 1 + } + return count + } ``` === "JS" ```javascript title="time_complexity.js" - [class]{}-[func]{arrayTraversal} + /* 线性阶(遍历数组) */ + function arrayTraversal(nums) { + let count = 0; + // 循环次数与数组长度成正比 + for (let i = 0; i < nums.length; i++) { + count++; + } + return count; + } ``` === "TS" ```typescript title="time_complexity.ts" - [class]{}-[func]{arrayTraversal} + /* 线性阶(遍历数组) */ + function arrayTraversal(nums: number[]): number { + let count = 0; + // 循环次数与数组长度成正比 + for (let i = 0; i < nums.length; i++) { + count++; + } + return count; + } ``` === "Dart" ```dart title="time_complexity.dart" - [class]{}-[func]{arrayTraversal} + /* 线性阶(遍历数组) */ + int arrayTraversal(List nums) { + int count = 0; + // 循环次数与数组长度成正比 + for (var num in nums) { + count++; + } + return count; + } ``` === "Rust" ```rust title="time_complexity.rs" - [class]{}-[func]{array_traversal} + /* 线性阶(遍历数组) */ + fn array_traversal(nums: &[i32]) -> i32 { + let mut count = 0; + // 循环次数与数组长度成正比 + for _ in nums { + count += 1; + } + count + } ``` === "C" ```c title="time_complexity.c" - [class]{}-[func]{arrayTraversal} + /* 线性阶(遍历数组) */ + int arrayTraversal(int *nums, int n) { + int count = 0; + // 循环次数与数组长度成正比 + for (int i = 0; i < n; i++) { + count++; + } + return count; + } ``` === "Zig" ```zig title="time_complexity.zig" - [class]{}-[func]{arrayTraversal} + // 线性阶(遍历数组) + fn arrayTraversal(nums: []i32) i32 { + var count: i32 = 0; + // 循环次数与数组长度成正比 + for (nums) |_| { + count += 1; + } + return count; + } ``` 值得注意的是,**输入数据大小 $n$ 需根据输入数据的类型来具体确定**。比如在第一个示例中,变量 $n$ 为输入数据大小;在第二个示例中,数组长度 $n$ 为数据大小。 @@ -1142,73 +1403,192 @@ $$ === "Python" ```python title="time_complexity.py" - [class]{}-[func]{quadratic} + def quadratic(n: int) -> int: + """平方阶""" + count = 0 + # 循环次数与数组长度成平方关系 + for i in range(n): + for j in range(n): + count += 1 + return count ``` === "C++" ```cpp title="time_complexity.cpp" - [class]{}-[func]{quadratic} + /* 平方阶 */ + int quadratic(int n) { + int count = 0; + // 循环次数与数组长度成平方关系 + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + count++; + } + } + return count; + } ``` === "Java" ```java title="time_complexity.java" - [class]{time_complexity}-[func]{quadratic} + /* 平方阶 */ + int quadratic(int n) { + int count = 0; + // 循环次数与数组长度成平方关系 + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + count++; + } + } + return count; + } ``` === "C#" ```csharp title="time_complexity.cs" - [class]{time_complexity}-[func]{quadratic} + /* 平方阶 */ + int quadratic(int n) { + int count = 0; + // 循环次数与数组长度成平方关系 + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + count++; + } + } + return count; + } ``` === "Go" ```go title="time_complexity.go" - [class]{}-[func]{quadratic} + /* 平方阶 */ + func quadratic(n int) int { + count := 0 + // 循环次数与数组长度成平方关系 + for i := 0; i < n; i++ { + for j := 0; j < n; j++ { + count++ + } + } + return count + } ``` === "Swift" ```swift title="time_complexity.swift" - [class]{}-[func]{quadratic} + /* 平方阶 */ + func quadratic(n: Int) -> Int { + var count = 0 + // 循环次数与数组长度成平方关系 + for _ in 0 ..< n { + for _ in 0 ..< n { + count += 1 + } + } + return count + } ``` === "JS" ```javascript title="time_complexity.js" - [class]{}-[func]{quadratic} + /* 平方阶 */ + function quadratic(n) { + let count = 0; + // 循环次数与数组长度成平方关系 + for (let i = 0; i < n; i++) { + for (let j = 0; j < n; j++) { + count++; + } + } + return count; + } ``` === "TS" ```typescript title="time_complexity.ts" - [class]{}-[func]{quadratic} + /* 平方阶 */ + function quadratic(n: number): number { + let count = 0; + // 循环次数与数组长度成平方关系 + for (let i = 0; i < n; i++) { + for (let j = 0; j < n; j++) { + count++; + } + } + return count; + } ``` === "Dart" ```dart title="time_complexity.dart" - [class]{}-[func]{quadratic} + /* 平方阶 */ + int quadratic(int n) { + int count = 0; + // 循环次数与数组长度成平方关系 + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + count++; + } + } + return count; + } ``` === "Rust" ```rust title="time_complexity.rs" - [class]{}-[func]{quadratic} + /* 平方阶 */ + fn quadratic(n: i32) -> i32 { + let mut count = 0; + // 循环次数与数组长度成平方关系 + for _ in 0..n { + for _ in 0..n { + count += 1; + } + } + count + } ``` === "C" ```c title="time_complexity.c" - [class]{}-[func]{quadratic} + /* 平方阶 */ + int quadratic(int n) { + int count = 0; + // 循环次数与数组长度成平方关系 + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + count++; + } + } + return count; + } ``` === "Zig" ```zig title="time_complexity.zig" - [class]{}-[func]{quadratic} + // 平方阶 + fn quadratic(n: i32) i32 { + var count: i32 = 0; + var i: i32 = 0; + // 循环次数与数组长度成平方关系 + while (i < n) : (i += 1) { + var j: i32 = 0; + while (j < n) : (j += 1) { + count += 1; + } + } + return count; + } ``` 图 2-10 对比了常数阶、线性阶和平方阶三种时间复杂度。 @@ -1222,73 +1602,273 @@ $$ === "Python" ```python title="time_complexity.py" - [class]{}-[func]{bubble_sort} + def bubble_sort(nums: list[int]) -> int: + """平方阶(冒泡排序)""" + count = 0 # 计数器 + # 外循环:未排序区间为 [0, i] + for i in range(len(nums) - 1, 0, -1): + # 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端 + for j in range(i): + if nums[j] > nums[j + 1]: + # 交换 nums[j] 与 nums[j + 1] + tmp: int = nums[j] + nums[j] = nums[j + 1] + nums[j + 1] = tmp + count += 3 # 元素交换包含 3 个单元操作 + return count ``` === "C++" ```cpp title="time_complexity.cpp" - [class]{}-[func]{bubbleSort} + /* 平方阶(冒泡排序) */ + 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; + } ``` === "Java" ```java title="time_complexity.java" - [class]{time_complexity}-[func]{bubbleSort} + /* 平方阶(冒泡排序) */ + 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] + int tmp = nums[j]; + nums[j] = nums[j + 1]; + nums[j + 1] = tmp; + count += 3; // 元素交换包含 3 个单元操作 + } + } + } + return count; + } ``` === "C#" ```csharp title="time_complexity.cs" - [class]{time_complexity}-[func]{bubbleSort} + /* 平方阶(冒泡排序) */ + 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; + } ``` === "Go" ```go title="time_complexity.go" - [class]{}-[func]{bubbleSort} + /* 平方阶(冒泡排序) */ + 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 + } ``` === "Swift" ```swift title="time_complexity.swift" - [class]{}-[func]{bubbleSort} + /* 平方阶(冒泡排序) */ + func bubbleSort(nums: inout [Int]) -> Int { + var count = 0 // 计数器 + // 外循环:未排序区间为 [0, i] + for i in stride(from: nums.count - 1, to: 0, by: -1) { + // 内循环:将未排序区间 [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 + } ``` === "JS" ```javascript title="time_complexity.js" - [class]{}-[func]{bubbleSort} + /* 平方阶(冒泡排序) */ + 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; + } ``` === "TS" ```typescript title="time_complexity.ts" - [class]{}-[func]{bubbleSort} + /* 平方阶(冒泡排序) */ + 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; + } ``` === "Dart" ```dart title="time_complexity.dart" - [class]{}-[func]{bubbleSort} + /* 平方阶(冒泡排序) */ + 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; + } ``` === "Rust" ```rust title="time_complexity.rs" - [class]{}-[func]{bubble_sort} + /* 平方阶(冒泡排序) */ + 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 + } ``` === "C" ```c title="time_complexity.c" - [class]{}-[func]{bubbleSort} + /* 平方阶(冒泡排序) */ + 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; + } ``` === "Zig" ```zig title="time_complexity.zig" - [class]{}-[func]{bubbleSort} + // 平方阶(冒泡排序) + 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; + } ``` ### 4.   指数阶 $O(2^n)$ @@ -1300,73 +1880,223 @@ $$ === "Python" ```python title="time_complexity.py" - [class]{}-[func]{exponential} + def exponential(n: int) -> int: + """指数阶(循环实现)""" + count = 0 + base = 1 + # 细胞每轮一分为二,形成数列 1, 2, 4, 8, ..., 2^(n-1) + for _ in range(n): + for _ in range(base): + count += 1 + base *= 2 + # count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1 + return count ``` === "C++" ```cpp title="time_complexity.cpp" - [class]{}-[func]{exponential} + /* 指数阶(循环实现) */ + 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; + } ``` === "Java" ```java title="time_complexity.java" - [class]{time_complexity}-[func]{exponential} + /* 指数阶(循环实现) */ + 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; + } ``` === "C#" ```csharp title="time_complexity.cs" - [class]{time_complexity}-[func]{exponential} + /* 指数阶(循环实现) */ + 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; + } ``` === "Go" ```go title="time_complexity.go" - [class]{}-[func]{exponential} + /* 指数阶(循环实现)*/ + 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 + } ``` === "Swift" ```swift title="time_complexity.swift" - [class]{}-[func]{exponential} + /* 指数阶(循环实现) */ + 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 + } ``` === "JS" ```javascript title="time_complexity.js" - [class]{}-[func]{exponential} + /* 指数阶(循环实现) */ + 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; + } ``` === "TS" ```typescript title="time_complexity.ts" - [class]{}-[func]{exponential} + /* 指数阶(循环实现) */ + 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; + } ``` === "Dart" ```dart title="time_complexity.dart" - [class]{}-[func]{exponential} + /* 指数阶(循环实现) */ + 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; + } ``` === "Rust" ```rust title="time_complexity.rs" - [class]{}-[func]{exponential} + /* 指数阶(循环实现) */ + 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 + } ``` === "C" ```c title="time_complexity.c" - [class]{}-[func]{exponential} + /* 指数阶(循环实现) */ + 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; + } ``` === "Zig" ```zig title="time_complexity.zig" - [class]{}-[func]{exponential} + // 指数阶(循环实现) + fn exponential(n: i32) i32 { + var count: i32 = 0; + var bas: i32 = 1; + var i: i32 = 0; + // 细胞每轮一分为二,形成数列 1, 2, 4, 8, ..., 2^(n-1) + while (i < n) : (i += 1) { + var j: i32 = 0; + while (j < bas) : (j += 1) { + count += 1; + } + bas *= 2; + } + // count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1 + return count; + } ``` ![指数阶的时间复杂度](time_complexity.assets/time_complexity_exponential.png) @@ -1378,73 +2108,130 @@ $$ === "Python" ```python title="time_complexity.py" - [class]{}-[func]{exp_recur} + def exp_recur(n: int) -> int: + """指数阶(递归实现)""" + if n == 1: + return 1 + return exp_recur(n - 1) + exp_recur(n - 1) + 1 ``` === "C++" ```cpp title="time_complexity.cpp" - [class]{}-[func]{expRecur} + /* 指数阶(递归实现) */ + int expRecur(int n) { + if (n == 1) + return 1; + return expRecur(n - 1) + expRecur(n - 1) + 1; + } ``` === "Java" ```java title="time_complexity.java" - [class]{time_complexity}-[func]{expRecur} + /* 指数阶(递归实现) */ + int expRecur(int n) { + if (n == 1) + return 1; + return expRecur(n - 1) + expRecur(n - 1) + 1; + } ``` === "C#" ```csharp title="time_complexity.cs" - [class]{time_complexity}-[func]{expRecur} + /* 指数阶(递归实现) */ + int expRecur(int n) { + if (n == 1) return 1; + return expRecur(n - 1) + expRecur(n - 1) + 1; + } ``` === "Go" ```go title="time_complexity.go" - [class]{}-[func]{expRecur} + /* 指数阶(递归实现)*/ + func expRecur(n int) int { + if n == 1 { + return 1 + } + return expRecur(n-1) + expRecur(n-1) + 1 + } ``` === "Swift" ```swift title="time_complexity.swift" - [class]{}-[func]{expRecur} + /* 指数阶(递归实现) */ + func expRecur(n: Int) -> Int { + if n == 1 { + return 1 + } + return expRecur(n: n - 1) + expRecur(n: n - 1) + 1 + } ``` === "JS" ```javascript title="time_complexity.js" - [class]{}-[func]{expRecur} + /* 指数阶(递归实现) */ + function expRecur(n) { + if (n === 1) return 1; + return expRecur(n - 1) + expRecur(n - 1) + 1; + } ``` === "TS" ```typescript title="time_complexity.ts" - [class]{}-[func]{expRecur} + /* 指数阶(递归实现) */ + function expRecur(n: number): number { + if (n === 1) return 1; + return expRecur(n - 1) + expRecur(n - 1) + 1; + } ``` === "Dart" ```dart title="time_complexity.dart" - [class]{}-[func]{expRecur} + /* 指数阶(递归实现) */ + int expRecur(int n) { + if (n == 1) return 1; + return expRecur(n - 1) + expRecur(n - 1) + 1; + } ``` === "Rust" ```rust title="time_complexity.rs" - [class]{}-[func]{exp_recur} + /* 指数阶(递归实现) */ + fn exp_recur(n: i32) -> i32 { + if n == 1 { + return 1; + } + exp_recur(n - 1) + exp_recur(n - 1) + 1 + } ``` === "C" ```c title="time_complexity.c" - [class]{}-[func]{expRecur} + /* 指数阶(递归实现) */ + int expRecur(int n) { + if (n == 1) + return 1; + return expRecur(n - 1) + expRecur(n - 1) + 1; + } ``` === "Zig" ```zig title="time_complexity.zig" - [class]{}-[func]{expRecur} + // 指数阶(递归实现) + fn expRecur(n: i32) i32 { + if (n == 1) return 1; + return expRecur(n - 1) + expRecur(n - 1) + 1; + } ``` 指数阶增长非常迅速,在穷举法(暴力搜索、回溯等)中比较常见。对于数据规模较大的问题,指数阶是不可接受的,通常需要使用动态规划或贪心等算法来解决。 @@ -1458,73 +2245,170 @@ $$ === "Python" ```python title="time_complexity.py" - [class]{}-[func]{logarithmic} + def logarithmic(n: float) -> int: + """对数阶(循环实现)""" + count = 0 + while n > 1: + n = n / 2 + count += 1 + return count ``` === "C++" ```cpp title="time_complexity.cpp" - [class]{}-[func]{logarithmic} + /* 对数阶(循环实现) */ + int logarithmic(float n) { + int count = 0; + while (n > 1) { + n = n / 2; + count++; + } + return count; + } ``` === "Java" ```java title="time_complexity.java" - [class]{time_complexity}-[func]{logarithmic} + /* 对数阶(循环实现) */ + int logarithmic(float n) { + int count = 0; + while (n > 1) { + n = n / 2; + count++; + } + return count; + } ``` === "C#" ```csharp title="time_complexity.cs" - [class]{time_complexity}-[func]{logarithmic} + /* 对数阶(循环实现) */ + int logarithmic(float n) { + int count = 0; + while (n > 1) { + n = n / 2; + count++; + } + return count; + } ``` === "Go" ```go title="time_complexity.go" - [class]{}-[func]{logarithmic} + /* 对数阶(循环实现)*/ + func logarithmic(n float64) int { + count := 0 + for n > 1 { + n = n / 2 + count++ + } + return count + } ``` === "Swift" ```swift title="time_complexity.swift" - [class]{}-[func]{logarithmic} + /* 对数阶(循环实现) */ + func logarithmic(n: Double) -> Int { + var count = 0 + var n = n + while n > 1 { + n = n / 2 + count += 1 + } + return count + } ``` === "JS" ```javascript title="time_complexity.js" - [class]{}-[func]{logarithmic} + /* 对数阶(循环实现) */ + function logarithmic(n) { + let count = 0; + while (n > 1) { + n = n / 2; + count++; + } + return count; + } ``` === "TS" ```typescript title="time_complexity.ts" - [class]{}-[func]{logarithmic} + /* 对数阶(循环实现) */ + function logarithmic(n: number): number { + let count = 0; + while (n > 1) { + n = n / 2; + count++; + } + return count; + } ``` === "Dart" ```dart title="time_complexity.dart" - [class]{}-[func]{logarithmic} + /* 对数阶(循环实现) */ + int logarithmic(num n) { + int count = 0; + while (n > 1) { + n = n / 2; + count++; + } + return count; + } ``` === "Rust" ```rust title="time_complexity.rs" - [class]{}-[func]{logarithmic} + /* 对数阶(循环实现) */ + fn logarithmic(mut n: f32) -> i32 { + let mut count = 0; + while n > 1.0 { + n = n / 2.0; + count += 1; + } + count + } ``` === "C" ```c title="time_complexity.c" - [class]{}-[func]{logarithmic} + /* 对数阶(循环实现) */ + int logarithmic(float n) { + int count = 0; + while (n > 1) { + n = n / 2; + count++; + } + return count; + } ``` === "Zig" ```zig title="time_complexity.zig" - [class]{}-[func]{logarithmic} + // 对数阶(循环实现) + fn logarithmic(n: f32) i32 { + var count: i32 = 0; + var n_var = n; + while (n_var > 1) + { + n_var = n_var / 2; + count +=1; + } + return count; + } ``` ![对数阶的时间复杂度](time_complexity.assets/time_complexity_logarithmic.png) @@ -1536,73 +2420,130 @@ $$ === "Python" ```python title="time_complexity.py" - [class]{}-[func]{log_recur} + def log_recur(n: float) -> int: + """对数阶(递归实现)""" + if n <= 1: + return 0 + return log_recur(n / 2) + 1 ``` === "C++" ```cpp title="time_complexity.cpp" - [class]{}-[func]{logRecur} + /* 对数阶(递归实现) */ + int logRecur(float n) { + if (n <= 1) + return 0; + return logRecur(n / 2) + 1; + } ``` === "Java" ```java title="time_complexity.java" - [class]{time_complexity}-[func]{logRecur} + /* 对数阶(递归实现) */ + int logRecur(float n) { + if (n <= 1) + return 0; + return logRecur(n / 2) + 1; + } ``` === "C#" ```csharp title="time_complexity.cs" - [class]{time_complexity}-[func]{logRecur} + /* 对数阶(递归实现) */ + int logRecur(float n) { + if (n <= 1) return 0; + return logRecur(n / 2) + 1; + } ``` === "Go" ```go title="time_complexity.go" - [class]{}-[func]{logRecur} + /* 对数阶(递归实现)*/ + func logRecur(n float64) int { + if n <= 1 { + return 0 + } + return logRecur(n/2) + 1 + } ``` === "Swift" ```swift title="time_complexity.swift" - [class]{}-[func]{logRecur} + /* 对数阶(递归实现) */ + func logRecur(n: Double) -> Int { + if n <= 1 { + return 0 + } + return logRecur(n: n / 2) + 1 + } ``` === "JS" ```javascript title="time_complexity.js" - [class]{}-[func]{logRecur} + /* 对数阶(递归实现) */ + function logRecur(n) { + if (n <= 1) return 0; + return logRecur(n / 2) + 1; + } ``` === "TS" ```typescript title="time_complexity.ts" - [class]{}-[func]{logRecur} + /* 对数阶(递归实现) */ + function logRecur(n: number): number { + if (n <= 1) return 0; + return logRecur(n / 2) + 1; + } ``` === "Dart" ```dart title="time_complexity.dart" - [class]{}-[func]{logRecur} + /* 对数阶(递归实现) */ + int logRecur(num n) { + if (n <= 1) return 0; + return logRecur(n / 2) + 1; + } ``` === "Rust" ```rust title="time_complexity.rs" - [class]{}-[func]{log_recur} + /* 对数阶(递归实现) */ + fn log_recur(n: f32) -> i32 { + if n <= 1.0 { + return 0; + } + log_recur(n / 2.0) + 1 + } ``` === "C" ```c title="time_complexity.c" - [class]{}-[func]{logRecur} + /* 对数阶(递归实现) */ + int logRecur(float n) { + if (n <= 1) + return 0; + return logRecur(n / 2) + 1; + } ``` === "Zig" ```zig title="time_complexity.zig" - [class]{}-[func]{logRecur} + // 对数阶(递归实现) + fn logRecur(n: f32) i32 { + if (n <= 1) return 0; + return logRecur(n / 2) + 1; + } ``` 对数阶常出现于基于分治策略的算法中,体现了“一分为多”和“化繁为简”的算法思想。它增长缓慢,是仅次于常数阶的理想的时间复杂度。 @@ -1624,73 +2565,183 @@ $$ === "Python" ```python title="time_complexity.py" - [class]{}-[func]{linear_log_recur} + def linear_log_recur(n: float) -> int: + """线性对数阶""" + if n <= 1: + return 1 + count: int = linear_log_recur(n // 2) + linear_log_recur(n // 2) + for _ in range(n): + count += 1 + return count ``` === "C++" ```cpp title="time_complexity.cpp" - [class]{}-[func]{linearLogRecur} + /* 线性对数阶 */ + int linearLogRecur(float n) { + if (n <= 1) + return 1; + int count = linearLogRecur(n / 2) + linearLogRecur(n / 2); + for (int i = 0; i < n; i++) { + count++; + } + return count; + } ``` === "Java" ```java title="time_complexity.java" - [class]{time_complexity}-[func]{linearLogRecur} + /* 线性对数阶 */ + int linearLogRecur(float n) { + if (n <= 1) + return 1; + int count = linearLogRecur(n / 2) + + linearLogRecur(n / 2); + for (int i = 0; i < n; i++) { + count++; + } + return count; + } ``` === "C#" ```csharp title="time_complexity.cs" - [class]{time_complexity}-[func]{linearLogRecur} + /* 线性对数阶 */ + int linearLogRecur(float n) { + if (n <= 1) return 1; + int count = linearLogRecur(n / 2) + + linearLogRecur(n / 2); + for (int i = 0; i < n; i++) { + count++; + } + return count; + } ``` === "Go" ```go title="time_complexity.go" - [class]{}-[func]{linearLogRecur} + /* 线性对数阶 */ + func linearLogRecur(n float64) int { + if n <= 1 { + return 1 + } + count := linearLogRecur(n/2) + + linearLogRecur(n/2) + for i := 0.0; i < n; i++ { + count++ + } + return count + } ``` === "Swift" ```swift title="time_complexity.swift" - [class]{}-[func]{linearLogRecur} + /* 线性对数阶 */ + func linearLogRecur(n: Double) -> 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 + } ``` === "JS" ```javascript title="time_complexity.js" - [class]{}-[func]{linearLogRecur} + /* 线性对数阶 */ + 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; + } ``` === "TS" ```typescript title="time_complexity.ts" - [class]{}-[func]{linearLogRecur} + /* 线性对数阶 */ + 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; + } ``` === "Dart" ```dart title="time_complexity.dart" - [class]{}-[func]{linearLogRecur} + /* 线性对数阶 */ + int linearLogRecur(num n) { + if (n <= 1) return 1; + int count = linearLogRecur(n / 2) + linearLogRecur(n / 2); + for (var i = 0; i < n; i++) { + count++; + } + return count; + } ``` === "Rust" ```rust title="time_complexity.rs" - [class]{}-[func]{linear_log_recur} + /* 线性对数阶 */ + fn linear_log_recur(n: f32) -> i32 { + if n <= 1.0 { + return 1; + } + let mut count = linear_log_recur(n / 2.0) + + linear_log_recur(n / 2.0); + for _ in 0 ..n as i32 { + count += 1; + } + return count + } ``` === "C" ```c title="time_complexity.c" - [class]{}-[func]{linearLogRecur} + /* 线性对数阶 */ + int linearLogRecur(float n) { + if (n <= 1) + return 1; + int count = linearLogRecur(n / 2) + linearLogRecur(n / 2); + for (int i = 0; i < n; i++) { + count++; + } + return count; + } ``` === "Zig" ```zig title="time_complexity.zig" - [class]{}-[func]{linearLogRecur} + // 线性对数阶 + fn linearLogRecur(n: f32) i32 { + if (n <= 1) return 1; + var count: i32 = linearLogRecur(n / 2) + + linearLogRecur(n / 2); + var i: f32 = 0; + while (i < n) : (i += 1) { + count += 1; + } + return count; + } ``` 图 2-13 展示了线性对数阶的生成方式。二叉树的每一层的操作总数都为 $n$ ,树共有 $\log_2 n + 1$ 层,因此时间复杂度为 $O(n \log n)$ 。 @@ -1714,73 +2765,189 @@ $$ === "Python" ```python title="time_complexity.py" - [class]{}-[func]{factorial_recur} + def factorial_recur(n: int) -> int: + """阶乘阶(递归实现)""" + if n == 0: + return 1 + count = 0 + # 从 1 个分裂出 n 个 + for _ in range(n): + count += factorial_recur(n - 1) + return count ``` === "C++" ```cpp title="time_complexity.cpp" - [class]{}-[func]{factorialRecur} + /* 阶乘阶(递归实现) */ + 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; + } ``` === "Java" ```java title="time_complexity.java" - [class]{time_complexity}-[func]{factorialRecur} + /* 阶乘阶(递归实现) */ + 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; + } ``` === "C#" ```csharp title="time_complexity.cs" - [class]{time_complexity}-[func]{factorialRecur} + /* 阶乘阶(递归实现) */ + 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; + } ``` === "Go" ```go title="time_complexity.go" - [class]{}-[func]{factorialRecur} + /* 阶乘阶(递归实现) */ + 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 + } ``` === "Swift" ```swift title="time_complexity.swift" - [class]{}-[func]{factorialRecur} + /* 阶乘阶(递归实现) */ + 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 + } ``` === "JS" ```javascript title="time_complexity.js" - [class]{}-[func]{factorialRecur} + /* 阶乘阶(递归实现) */ + 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; + } ``` === "TS" ```typescript title="time_complexity.ts" - [class]{}-[func]{factorialRecur} + /* 阶乘阶(递归实现) */ + 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; + } ``` === "Dart" ```dart title="time_complexity.dart" - [class]{}-[func]{factorialRecur} + /* 阶乘阶(递归实现) */ + 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; + } ``` === "Rust" ```rust title="time_complexity.rs" - [class]{}-[func]{factorial_recur} + /* 阶乘阶(递归实现) */ + 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 + } ``` === "C" ```c title="time_complexity.c" - [class]{}-[func]{factorialRecur} + /* 阶乘阶(递归实现) */ + 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; + } ``` === "Zig" ```zig title="time_complexity.zig" - [class]{}-[func]{factorialRecur} + // 阶乘阶(递归实现) + fn factorialRecur(n: i32) i32 { + if (n == 0) return 1; + var count: i32 = 0; + var i: i32 = 0; + // 从 1 个分裂出 n 个 + while (i < n) : (i += 1) { + count += factorialRecur(n - 1); + } + return count; + } ``` ![阶乘阶的时间复杂度](time_complexity.assets/time_complexity_factorial.png) @@ -1801,89 +2968,324 @@ $$ === "Python" ```python title="worst_best_time_complexity.py" - [class]{}-[func]{random_numbers} + def random_numbers(n: int) -> list[int]: + """生成一个数组,元素为: 1, 2, ..., n ,顺序被打乱""" + # 生成数组 nums =: 1, 2, 3, ..., n + nums = [i for i in range(1, n + 1)] + # 随机打乱数组元素 + random.shuffle(nums) + return nums - [class]{}-[func]{find_one} + def find_one(nums: list[int]) -> int: + """查找数组 nums 中数字 1 所在索引""" + for i in range(len(nums)): + # 当元素 1 在数组头部时,达到最佳时间复杂度 O(1) + # 当元素 1 在数组尾部时,达到最差时间复杂度 O(n) + if nums[i] == 1: + return i + return -1 ``` === "C++" ```cpp title="worst_best_time_complexity.cpp" - [class]{}-[func]{randomNumbers} + /* 生成一个数组,元素为 { 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]{findOne} + /* 查找数组 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; + } ``` === "Java" ```java title="worst_best_time_complexity.java" - [class]{worst_best_time_complexity}-[func]{randomNumbers} + /* 生成一个数组,元素为 { 1, 2, ..., n },顺序被打乱 */ + int[] randomNumbers(int n) { + Integer[] nums = new Integer[n]; + // 生成数组 nums = { 1, 2, 3, ..., n } + for (int i = 0; i < n; i++) { + nums[i] = i + 1; + } + // 随机打乱数组元素 + Collections.shuffle(Arrays.asList(nums)); + // Integer[] -> int[] + int[] res = new int[n]; + for (int i = 0; i < n; i++) { + res[i] = nums[i]; + } + return res; + } - [class]{worst_best_time_complexity}-[func]{findOne} + /* 查找数组 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; + } ``` === "C#" ```csharp title="worst_best_time_complexity.cs" - [class]{worst_best_time_complexity}-[func]{randomNumbers} + /* 生成一个数组,元素为 { 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]{findOne} + // 随机打乱数组元素 + for (int i = 0; i < nums.Length; i++) { + var index = new Random().Next(i, nums.Length); + var tmp = nums[i]; + var ran = nums[index]; + nums[i] = ran; + nums[index] = tmp; + } + 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; + } ``` === "Go" ```go title="worst_best_time_complexity.go" - [class]{}-[func]{randomNumbers} + /* 生成一个数组,元素为 { 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]{findOne} + /* 查找数组 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 + } ``` === "Swift" ```swift title="worst_best_time_complexity.swift" - [class]{}-[func]{randomNumbers} + /* 生成一个数组,元素为 { 1, 2, ..., n },顺序被打乱 */ + func randomNumbers(n: Int) -> [Int] { + // 生成数组 nums = { 1, 2, 3, ..., n } + var nums = Array(1 ... n) + // 随机打乱数组元素 + nums.shuffle() + return nums + } - [class]{}-[func]{findOne} + /* 查找数组 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 + } ``` === "JS" ```javascript title="worst_best_time_complexity.js" - [class]{}-[func]{randomNumbers} + /* 生成一个数组,元素为 { 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]{findOne} + /* 查找数组 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; + } ``` === "TS" ```typescript title="worst_best_time_complexity.ts" - [class]{}-[func]{randomNumbers} + /* 生成一个数组,元素为 { 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]{findOne} + /* 查找数组 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; + } ``` === "Dart" ```dart title="worst_best_time_complexity.dart" - [class]{}-[func]{randomNumbers} + /* 生成一个数组,元素为 { 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]{findOne} + 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; + } ``` === "Rust" ```rust title="worst_best_time_complexity.rs" - [class]{}-[func]{random_numbers} + /* 生成一个数组,元素为 { 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]{find_one} + /* 查找数组 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 + } ``` === "C" ```c title="worst_best_time_complexity.c" - [class]{}-[func]{randomNumbers} + /* 生成一个数组,元素为 { 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]{findOne} + /* 查找数组 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; + } ``` === "Zig" diff --git a/zh/chapter_divide_and_conquer/binary_search_recur.md b/zh/chapter_divide_and_conquer/binary_search_recur.md index 69b16bc68..e0cc4496f 100644 --- a/zh/chapter_divide_and_conquer/binary_search_recur.md +++ b/zh/chapter_divide_and_conquer/binary_search_recur.md @@ -49,81 +49,306 @@ comments: true === "Python" ```python title="binary_search_recur.py" - [class]{}-[func]{dfs} + def dfs(nums: list[int], target: int, i: int, j: int) -> int: + """二分查找:问题 f(i, j)""" + # 若区间为空,代表无目标元素,则返回 -1 + if i > j: + return -1 + # 计算中点索引 m + m = (i + j) // 2 + if nums[m] < target: + # 递归子问题 f(m+1, j) + return dfs(nums, target, m + 1, j) + elif nums[m] > target: + # 递归子问题 f(i, m-1) + return dfs(nums, target, i, m - 1) + else: + # 找到目标元素,返回其索引 + return m - [class]{}-[func]{binary_search} + def binary_search(nums: list[int], target: int) -> int: + """二分查找""" + n = len(nums) + # 求解问题 f(0, n-1) + return dfs(nums, target, 0, n - 1) ``` === "C++" ```cpp title="binary_search_recur.cpp" - [class]{}-[func]{dfs} + /* 二分查找:问题 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]{binarySearch} + /* 二分查找 */ + int binarySearch(vector &nums, int target) { + int n = nums.size(); + // 求解问题 f(0, n-1) + return dfs(nums, target, 0, n - 1); + } ``` === "Java" ```java title="binary_search_recur.java" - [class]{binary_search_recur}-[func]{dfs} + /* 二分查找:问题 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]{binarySearch} + /* 二分查找 */ + int binarySearch(int[] nums, int target) { + int n = nums.length; + // 求解问题 f(0, n-1) + return dfs(nums, target, 0, n - 1); + } ``` === "C#" ```csharp title="binary_search_recur.cs" - [class]{binary_search_recur}-[func]{dfs} + /* 二分查找:问题 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]{binarySearch} + /* 二分查找 */ + int binarySearch(int[] nums, int target) { + int n = nums.Length; + // 求解问题 f(0, n-1) + return dfs(nums, target, 0, n - 1); + } ``` === "Go" ```go title="binary_search_recur.go" - [class]{}-[func]{dfs} + /* 二分查找:问题 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]{binarySearch} + /* 二分查找 */ + func binarySearch(nums []int, target int) int { + n := len(nums) + return dfs(nums, target, 0, n-1) + } ``` === "Swift" ```swift title="binary_search_recur.swift" - [class]{}-[func]{dfs} + /* 二分查找:问题 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]{binarySearch} + /* 二分查找 */ + func binarySearch(nums: [Int], target: Int) -> Int { + let n = nums.count + // 求解问题 f(0, n-1) + return dfs(nums: nums, target: target, i: 0, j: n - 1) + } ``` === "JS" ```javascript title="binary_search_recur.js" - [class]{}-[func]{dfs} + /* 二分查找:问题 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]{binarySearch} + /* 二分查找 */ + function binarySearch(nums, target) { + const n = nums.length; + // 求解问题 f(0, n-1) + return dfs(nums, target, 0, n - 1); + } ``` === "TS" ```typescript title="binary_search_recur.ts" - [class]{}-[func]{dfs} + /* 二分查找:问题 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]{binarySearch} + /* 二分查找 */ + function binarySearch(nums: number[], target: number): number { + const n = nums.length; + // 求解问题 f(0, n-1) + return dfs(nums, target, 0, n - 1); + } ``` === "Dart" ```dart title="binary_search_recur.dart" - [class]{}-[func]{dfs} + /* 二分查找:问题 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]{binarySearch} + /* 二分查找 */ + int binarySearch(List nums, int target) { + int n = nums.length; + // 求解问题 f(0, n-1) + return dfs(nums, target, 0, n - 1); + } ``` === "Rust" ```rust title="binary_search_recur.rs" - [class]{}-[func]{dfs} + /* 二分查找:问题 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]{binary_search} + /* 二分查找 */ + fn binary_search(nums: &[i32], target: i32) -> i32 { + let n = nums.len() as i32; + // 求解问题 f(0, n-1) + dfs(nums, target, 0, n - 1) + } ``` === "C" diff --git a/zh/chapter_divide_and_conquer/build_binary_tree_problem.md b/zh/chapter_divide_and_conquer/build_binary_tree_problem.md index 349de895b..81399c23c 100644 --- a/zh/chapter_divide_and_conquer/build_binary_tree_problem.md +++ b/zh/chapter_divide_and_conquer/build_binary_tree_problem.md @@ -74,81 +74,331 @@ comments: true === "Python" ```python title="build_tree.py" - [class]{}-[func]{dfs} + def dfs( + preorder: list[int], + inorder_map: dict[int, int], + i: int, + l: int, + r: int, + ) -> TreeNode | None: + """构建二叉树:分治""" + # 子树区间为空时终止 + if r - l < 0: + return None + # 初始化根节点 + root = TreeNode(preorder[i]) + # 查询 m ,从而划分左右子树 + m = inorder_map[preorder[i]] + # 子问题:构建左子树 + root.left = dfs(preorder, inorder_map, i + 1, l, m - 1) + # 子问题:构建右子树 + root.right = dfs(preorder, inorder_map, i + 1 + m - l, m + 1, r) + # 返回根节点 + return root - [class]{}-[func]{build_tree} + def build_tree(preorder: list[int], inorder: list[int]) -> TreeNode | None: + """构建二叉树""" + # 初始化哈希表,存储 inorder 元素到索引的映射 + inorder_map = {val: i for i, val in enumerate(inorder)} + root = dfs(preorder, inorder_map, 0, 0, len(inorder) - 1) + return root ``` === "C++" ```cpp title="build_tree.cpp" - [class]{}-[func]{dfs} + /* 构建二叉树:分治 */ + 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]{buildTree} + /* 构建二叉树 */ + 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; + } ``` === "Java" ```java title="build_tree.java" - [class]{build_tree}-[func]{dfs} + /* 构建二叉树:分治 */ + TreeNode dfs(int[] preorder, Map inorderMap, int i, int l, int r) { + // 子树区间为空时终止 + if (r - l < 0) + return null; + // 初始化根节点 + TreeNode root = new TreeNode(preorder[i]); + // 查询 m ,从而划分左右子树 + int 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]{build_tree}-[func]{buildTree} + /* 构建二叉树 */ + TreeNode buildTree(int[] preorder, int[] inorder) { + // 初始化哈希表,存储 inorder 元素到索引的映射 + Map inorderMap = new HashMap<>(); + for (int i = 0; i < inorder.length; i++) { + inorderMap.put(inorder[i], i); + } + TreeNode root = dfs(preorder, inorderMap, 0, 0, inorder.length - 1); + return root; + } ``` === "C#" ```csharp title="build_tree.cs" - [class]{build_tree}-[func]{dfs} + /* 构建二叉树:分治 */ + TreeNode dfs(int[] preorder, Dictionary 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]{build_tree}-[func]{buildTree} + /* 构建二叉树 */ + TreeNode buildTree(int[] preorder, int[] inorder) { + // 初始化哈希表,存储 inorder 元素到索引的映射 + Dictionary inorderMap = new Dictionary(); + 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; + } ``` === "Go" ```go title="build_tree.go" - [class]{}-[func]{dfsBuildTree} + /* 构建二叉树:分治 */ + 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]{buildTree} + /* 构建二叉树 */ + 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 + } ``` === "Swift" ```swift title="build_tree.swift" - [class]{}-[func]{dfs} + /* 构建二叉树:分治 */ + 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]{buildTree} + /* 构建二叉树 */ + 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: 0, l: 0, r: inorder.count - 1) + } ``` === "JS" ```javascript title="build_tree.js" - [class]{}-[func]{dfs} + /* 构建二叉树:分治 */ + 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]{buildTree} + /* 构建二叉树 */ + 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; + } ``` === "TS" ```typescript title="build_tree.ts" - [class]{}-[func]{dfs} + /* 构建二叉树:分治 */ + 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]{buildTree} + /* 构建二叉树 */ + 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; + } ``` === "Dart" ```dart title="build_tree.dart" - [class]{}-[func]{dfs} + /* 构建二叉树:分治 */ + 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]{buildTree} + /* 构建二叉树 */ + 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; + } ``` === "Rust" ```rust title="build_tree.rs" - [class]{}-[func]{dfs} + /* 构建二叉树:分治 */ + 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]{build_tree} + /* 构建二叉树 */ + 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 + } ``` === "C" diff --git a/zh/chapter_divide_and_conquer/hanota_problem.md b/zh/chapter_divide_and_conquer/hanota_problem.md index 2fcdd38e3..94d76315d 100644 --- a/zh/chapter_divide_and_conquer/hanota_problem.md +++ b/zh/chapter_divide_and_conquer/hanota_problem.md @@ -99,101 +99,342 @@ comments: true === "Python" ```python title="hanota.py" - [class]{}-[func]{move} + def move(src: list[int], tar: list[int]): + """移动一个圆盘""" + # 从 src 顶部拿出一个圆盘 + pan = src.pop() + # 将圆盘放入 tar 顶部 + tar.append(pan) - [class]{}-[func]{dfs} + def dfs(i: int, src: list[int], buf: list[int], tar: list[int]): + """求解汉诺塔:问题 f(i)""" + # 若 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]{solve_hanota} + def solve_hanota(A: list[int], B: list[int], C: list[int]): + """求解汉诺塔""" + n = len(A) + # 将 A 顶部 n 个圆盘借助 B 移到 C + dfs(n, A, B, C) ``` === "C++" ```cpp title="hanota.cpp" - [class]{}-[func]{move} + /* 移动一个圆盘 */ + void move(vector &src, vector &tar) { + // 从 src 顶部拿出一个圆盘 + int pan = src.back(); + src.pop_back(); + // 将圆盘放入 tar 顶部 + tar.push_back(pan); + } - [class]{}-[func]{dfs} + /* 求解汉诺塔:问题 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]{solveHanota} + /* 求解汉诺塔 */ + void solveHanota(vector &A, vector &B, vector &C) { + int n = A.size(); + // 将 A 顶部 n 个圆盘借助 B 移到 C + dfs(n, A, B, C); + } ``` === "Java" ```java title="hanota.java" - [class]{hanota}-[func]{move} + /* 移动一个圆盘 */ + void move(List src, List tar) { + // 从 src 顶部拿出一个圆盘 + Integer pan = src.remove(src.size() - 1); + // 将圆盘放入 tar 顶部 + tar.add(pan); + } - [class]{hanota}-[func]{dfs} + /* 求解汉诺塔:问题 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]{solveHanota} + /* 求解汉诺塔 */ + void solveHanota(List A, List B, List C) { + int n = A.size(); + // 将 A 顶部 n 个圆盘借助 B 移到 C + dfs(n, A, B, C); + } ``` === "C#" ```csharp title="hanota.cs" - [class]{hanota}-[func]{move} + /* 移动一个圆盘 */ + void move(List src, List tar) { + // 从 src 顶部拿出一个圆盘 + int pan = src[^1]; + src.RemoveAt(src.Count - 1); + // 将圆盘放入 tar 顶部 + tar.Add(pan); + } - [class]{hanota}-[func]{dfs} + /* 求解汉诺塔:问题 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]{solveHanota} + /* 求解汉诺塔 */ + void solveHanota(List A, List B, List C) { + int n = A.Count; + // 将 A 顶部 n 个圆盘借助 B 移到 C + dfs(n, A, B, C); + } ``` === "Go" ```go title="hanota.go" - [class]{}-[func]{move} + /* 移动一个圆盘 */ + func move(src, tar *list.List) { + // 从 src 顶部拿出一个圆盘 + pan := src.Back() + // 将圆盘放入 tar 顶部 + tar.PushBack(pan.Value) + // 移除 src 顶部圆盘 + src.Remove(pan) + } - [class]{}-[func]{dfsHanota} + /* 求解汉诺塔:问题 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]{solveHanota} + /* 求解汉诺塔 */ + func solveHanota(A, B, C *list.List) { + n := A.Len() + // 将 A 顶部 n 个圆盘借助 B 移到 C + dfsHanota(n, A, B, C) + } ``` === "Swift" ```swift title="hanota.swift" - [class]{}-[func]{move} + /* 移动一个圆盘 */ + func move(src: inout [Int], tar: inout [Int]) { + // 从 src 顶部拿出一个圆盘 + let pan = src.popLast()! + // 将圆盘放入 tar 顶部 + tar.append(pan) + } - [class]{}-[func]{dfs} + /* 求解汉诺塔:问题 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]{solveHanota} + /* 求解汉诺塔 */ + 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) + } ``` === "JS" ```javascript title="hanota.js" - [class]{}-[func]{move} + /* 移动一个圆盘 */ + function move(src, tar) { + // 从 src 顶部拿出一个圆盘 + const pan = src.pop(); + // 将圆盘放入 tar 顶部 + tar.push(pan); + } - [class]{}-[func]{dfs} + /* 求解汉诺塔:问题 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]{solveHanota} + /* 求解汉诺塔 */ + function solveHanota(A, B, C) { + const n = A.length; + // 将 A 顶部 n 个圆盘借助 B 移到 C + dfs(n, A, B, C); + } ``` === "TS" ```typescript title="hanota.ts" - [class]{}-[func]{move} + /* 移动一个圆盘 */ + function move(src: number[], tar: number[]): void { + // 从 src 顶部拿出一个圆盘 + const pan = src.pop(); + // 将圆盘放入 tar 顶部 + tar.push(pan); + } - [class]{}-[func]{dfs} + /* 求解汉诺塔:问题 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]{solveHanota} + /* 求解汉诺塔 */ + function solveHanota(A: number[], B: number[], C: number[]): void { + const n = A.length; + // 将 A 顶部 n 个圆盘借助 B 移到 C + dfs(n, A, B, C); + } ``` === "Dart" ```dart title="hanota.dart" - [class]{}-[func]{move} + /* 移动一个圆盘 */ + void move(List src, List tar) { + // 从 src 顶部拿出一个圆盘 + int pan = src.removeLast(); + // 将圆盘放入 tar 顶部 + tar.add(pan); + } - [class]{}-[func]{dfs} + /* 求解汉诺塔:问题 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]{solveHanota} + /* 求解汉诺塔 */ + void solveHanota(List A, List B, List C) { + int n = A.length; + // 将 A 顶部 n 个圆盘借助 B 移到 C + dfs(n, A, B, C); + } ``` === "Rust" ```rust title="hanota.rs" - [class]{}-[func]{move_pan} + /* 移动一个圆盘 */ + fn move_pan(src: &mut Vec, tar: &mut Vec) { + // 从 src 顶部拿出一个圆盘 + let pan = src.remove(src.len() - 1); + // 将圆盘放入 tar 顶部 + tar.push(pan); + } - [class]{}-[func]{dfs} + /* 求解汉诺塔:问题 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]{solve_hanota} + /* 求解汉诺塔 */ + 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); + } ``` === "C" diff --git a/zh/chapter_dynamic_programming/dp_problem_features.md b/zh/chapter_dynamic_programming/dp_problem_features.md index f79ce1c59..f213b1341 100644 --- a/zh/chapter_dynamic_programming/dp_problem_features.md +++ b/zh/chapter_dynamic_programming/dp_problem_features.md @@ -43,61 +43,210 @@ $$ === "Python" ```python title="min_cost_climbing_stairs_dp.py" - [class]{}-[func]{min_cost_climbing_stairs_dp} + def min_cost_climbing_stairs_dp(cost: list[int]) -> int: + """爬楼梯最小代价:动态规划""" + n = len(cost) - 1 + if n == 1 or n == 2: + return cost[n] + # 初始化 dp 表,用于存储子问题的解 + dp = [0] * (n + 1) + # 初始状态:预设最小子问题的解 + dp[1], dp[2] = cost[1], cost[2] + # 状态转移:从较小子问题逐步求解较大子问题 + for i in range(3, n + 1): + dp[i] = min(dp[i - 1], dp[i - 2]) + cost[i] + return dp[n] ``` === "C++" ```cpp title="min_cost_climbing_stairs_dp.cpp" - [class]{}-[func]{minCostClimbingStairsDP} + /* 爬楼梯最小代价:动态规划 */ + 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]; + } ``` === "Java" ```java title="min_cost_climbing_stairs_dp.java" - [class]{min_cost_climbing_stairs_dp}-[func]{minCostClimbingStairsDP} + /* 爬楼梯最小代价:动态规划 */ + 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]; + } ``` === "C#" ```csharp title="min_cost_climbing_stairs_dp.cs" - [class]{min_cost_climbing_stairs_dp}-[func]{minCostClimbingStairsDP} + /* 爬楼梯最小代价:动态规划 */ + 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]; + } ``` === "Go" ```go title="min_cost_climbing_stairs_dp.go" - [class]{}-[func]{minCostClimbingStairsDP} + /* 爬楼梯最小代价:动态规划 */ + func minCostClimbingStairsDP(cost []int) int { + n := len(cost) - 1 + if n == 1 || n == 2 { + return cost[n] + } + // 初始化 dp 表,用于存储子问题的解 + dp := make([]int, n+1) + // 初始状态:预设最小子问题的解 + dp[1] = cost[1] + dp[2] = cost[2] + // 状态转移:从较小子问题逐步求解较大子问题 + for i := 3; i <= n; i++ { + dp[i] = int(math.Min(float64(dp[i-1]), float64(dp[i-2]+cost[i]))) + } + return dp[n] + } ``` === "Swift" ```swift title="min_cost_climbing_stairs_dp.swift" - [class]{}-[func]{minCostClimbingStairsDP} + /* 爬楼梯最小代价:动态规划 */ + 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 stride(from: 3, through: n, by: 1) { + dp[i] = min(dp[i - 1], dp[i - 2]) + cost[i] + } + return dp[n] + } ``` === "JS" ```javascript title="min_cost_climbing_stairs_dp.js" - [class]{}-[func]{minCostClimbingStairsDP} + /* 爬楼梯最小代价:动态规划 */ + 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]; + } ``` === "TS" ```typescript title="min_cost_climbing_stairs_dp.ts" - [class]{}-[func]{minCostClimbingStairsDP} + /* 爬楼梯最小代价:动态规划 */ + 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]; + } ``` === "Dart" ```dart title="min_cost_climbing_stairs_dp.dart" - [class]{}-[func]{minCostClimbingStairsDP} + /* 爬楼梯最小代价:动态规划 */ + 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]; + } ``` === "Rust" ```rust title="min_cost_climbing_stairs_dp.rs" - [class]{}-[func]{min_cost_climbing_stairs_dp} + /* 爬楼梯最小代价:动态规划 */ + 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] + } ``` === "C" @@ -109,7 +258,23 @@ $$ === "Zig" ```zig title="min_cost_climbing_stairs_dp.zig" - [class]{}-[func]{minCostClimbingStairsDP} + // 爬楼梯最小代价:动态规划 + 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]; + } ``` 图 14-7 展示了以上代码的动态规划过程。 @@ -123,61 +288,181 @@ $$ === "Python" ```python title="min_cost_climbing_stairs_dp.py" - [class]{}-[func]{min_cost_climbing_stairs_dp_comp} + def min_cost_climbing_stairs_dp_comp(cost: list[int]) -> int: + """爬楼梯最小代价:空间优化后的动态规划""" + n = len(cost) - 1 + if n == 1 or n == 2: + return cost[n] + a, b = cost[1], cost[2] + for i in range(3, n + 1): + a, b = b, min(a, b) + cost[i] + return b ``` === "C++" ```cpp title="min_cost_climbing_stairs_dp.cpp" - [class]{}-[func]{minCostClimbingStairsDPComp} + /* 爬楼梯最小代价:空间优化后的动态规划 */ + 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; + } ``` === "Java" ```java title="min_cost_climbing_stairs_dp.java" - [class]{min_cost_climbing_stairs_dp}-[func]{minCostClimbingStairsDPComp} + /* 爬楼梯最小代价:空间优化后的动态规划 */ + 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; + } ``` === "C#" ```csharp title="min_cost_climbing_stairs_dp.cs" - [class]{min_cost_climbing_stairs_dp}-[func]{minCostClimbingStairsDPComp} + /* 爬楼梯最小代价:空间优化后的动态规划 */ + 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; + } ``` === "Go" ```go title="min_cost_climbing_stairs_dp.go" - [class]{}-[func]{minCostClimbingStairsDPComp} + /* 爬楼梯最小代价:空间优化后的动态规划 */ + func minCostClimbingStairsDPComp(cost []int) int { + n := len(cost) - 1 + if n == 1 || n == 2 { + return cost[n] + } + // 初始状态:预设最小子问题的解 + a, b := cost[1], cost[2] + // 状态转移:从较小子问题逐步求解较大子问题 + for i := 3; i <= n; i++ { + tmp := b + b = int(math.Min(float64(a), float64(tmp+cost[i]))) + a = tmp + } + return b + } ``` === "Swift" ```swift title="min_cost_climbing_stairs_dp.swift" - [class]{}-[func]{minCostClimbingStairsDPComp} + /* 爬楼梯最小代价:空间优化后的动态规划 */ + 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 stride(from: 3, through: n, by: 1) { + (a, b) = (b, min(a, b) + cost[i]) + } + return b + } ``` === "JS" ```javascript title="min_cost_climbing_stairs_dp.js" - [class]{}-[func]{minCostClimbingStairsDPComp} + /* 爬楼梯最小代价:状态压缩后的动态规划 */ + 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; + } ``` === "TS" ```typescript title="min_cost_climbing_stairs_dp.ts" - [class]{}-[func]{minCostClimbingStairsDPComp} + /* 爬楼梯最小代价:状态压缩后的动态规划 */ + 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; + } ``` === "Dart" ```dart title="min_cost_climbing_stairs_dp.dart" - [class]{}-[func]{minCostClimbingStairsDPComp} + /* 爬楼梯最小代价:空间优化后的动态规划 */ + 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; + } ``` === "Rust" ```rust title="min_cost_climbing_stairs_dp.rs" - [class]{}-[func]{min_cost_climbing_stairs_dp_comp} + /* 爬楼梯最小代价:空间优化后的动态规划 */ + 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 + } ``` === "C" @@ -189,7 +474,22 @@ $$ === "Zig" ```zig title="min_cost_climbing_stairs_dp.zig" - [class]{}-[func]{minCostClimbingStairsDPComp} + // 爬楼梯最小代价:空间优化后的动态规划 + 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; + } ``` ## 14.2.2   无后效性 @@ -237,61 +537,234 @@ $$ === "Python" ```python title="climbing_stairs_constraint_dp.py" - [class]{}-[func]{climbing_stairs_constraint_dp} + def climbing_stairs_constraint_dp(n: int) -> int: + """带约束爬楼梯:动态规划""" + if n == 1 or n == 2: + return 1 + # 初始化 dp 表,用于存储子问题的解 + dp = [[0] * 3 for _ in range(n + 1)] + # 初始状态:预设最小子问题的解 + dp[1][1], dp[1][2] = 1, 0 + dp[2][1], dp[2][2] = 0, 1 + # 状态转移:从较小子问题逐步求解较大子问题 + 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] + return dp[n][1] + dp[n][2] ``` === "C++" ```cpp title="climbing_stairs_constraint_dp.cpp" - [class]{}-[func]{climbingStairsConstraintDP} + /* 带约束爬楼梯:动态规划 */ + 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]; + } ``` === "Java" ```java title="climbing_stairs_constraint_dp.java" - [class]{climbing_stairs_constraint_dp}-[func]{climbingStairsConstraintDP} + /* 带约束爬楼梯:动态规划 */ + 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]; + } ``` === "C#" ```csharp title="climbing_stairs_constraint_dp.cs" - [class]{climbing_stairs_constraint_dp}-[func]{climbingStairsConstraintDP} + /* 带约束爬楼梯:动态规划 */ + 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]; + } ``` === "Go" ```go title="climbing_stairs_constraint_dp.go" - [class]{}-[func]{climbingStairsConstraintDP} + /* 带约束爬楼梯:动态规划 */ + 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] + } ``` === "Swift" ```swift title="climbing_stairs_constraint_dp.swift" - [class]{}-[func]{climbingStairsConstraintDP} + /* 带约束爬楼梯:动态规划 */ + 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 stride(from: 3, through: n, by: 1) { + 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] + } ``` === "JS" ```javascript title="climbing_stairs_constraint_dp.js" - [class]{}-[func]{climbingStairsConstraintDP} + /* 带约束爬楼梯:动态规划 */ + 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]; + } ``` === "TS" ```typescript title="climbing_stairs_constraint_dp.ts" - [class]{}-[func]{climbingStairsConstraintDP} + /* 带约束爬楼梯:动态规划 */ + 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]; + } ``` === "Dart" ```dart title="climbing_stairs_constraint_dp.dart" - [class]{}-[func]{climbingStairsConstraintDP} + /* 带约束爬楼梯:动态规划 */ + 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]; + } ``` === "Rust" ```rust title="climbing_stairs_constraint_dp.rs" - [class]{}-[func]{climbing_stairs_constraint_dp} + /* 带约束爬楼梯:动态规划 */ + 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] + } ``` === "C" @@ -303,7 +776,25 @@ $$ === "Zig" ```zig title="climbing_stairs_constraint_dp.zig" - [class]{}-[func]{climbingStairsConstraintDP} + // 带约束爬楼梯:动态规划 + 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]; + } ``` 在上面的案例中,由于仅需多考虑前面一个状态,我们仍然可以通过扩展状态定义,使得问题重新满足无后效性。然而,某些问题具有非常严重的“有后效性”。 diff --git a/zh/chapter_dynamic_programming/dp_solution_pipeline.md b/zh/chapter_dynamic_programming/dp_solution_pipeline.md index a9a79aa15..17e6d2bde 100644 --- a/zh/chapter_dynamic_programming/dp_solution_pipeline.md +++ b/zh/chapter_dynamic_programming/dp_solution_pipeline.md @@ -113,61 +113,213 @@ $$ === "Python" ```python title="min_path_sum.py" - [class]{}-[func]{min_path_sum_dfs} + def min_path_sum_dfs(grid: list[list[int]], i: int, j: int) -> int: + """最小路径和:暴力搜索""" + # 若为左上角单元格,则终止搜索 + if i == 0 and j == 0: + return grid[0][0] + # 若行列索引越界,则返回 +∞ 代价 + if i < 0 or j < 0: + return inf + # 计算从左上角到 (i-1, j) 和 (i, j-1) 的最小路径代价 + left = min_path_sum_dfs(grid, i - 1, j) + up = min_path_sum_dfs(grid, i, j - 1) + # 返回从左上角到 (i, j) 的最小路径代价 + return min(left, up) + grid[i][j] ``` === "C++" ```cpp title="min_path_sum.cpp" - [class]{}-[func]{minPathSumDFS} + /* 最小路径和:暴力搜索 */ + 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 left = minPathSumDFS(grid, i - 1, j); + int up = minPathSumDFS(grid, i, j - 1); + // 返回从左上角到 (i, j) 的最小路径代价 + return min(left, up) != INT_MAX ? min(left, up) + grid[i][j] : INT_MAX; + } ``` === "Java" ```java title="min_path_sum.java" - [class]{min_path_sum}-[func]{minPathSumDFS} + /* 最小路径和:暴力搜索 */ + int minPathSumDFS(int[][] grid, int i, int j) { + // 若为左上角单元格,则终止搜索 + if (i == 0 && j == 0) { + return grid[0][0]; + } + // 若行列索引越界,则返回 +∞ 代价 + if (i < 0 || j < 0) { + return Integer.MAX_VALUE; + } + // 计算从左上角到 (i-1, j) 和 (i, j-1) 的最小路径代价 + int left = minPathSumDFS(grid, i - 1, j); + int up = minPathSumDFS(grid, i, j - 1); + // 返回从左上角到 (i, j) 的最小路径代价 + return Math.min(left, up) + grid[i][j]; + } ``` === "C#" ```csharp title="min_path_sum.cs" - [class]{min_path_sum}-[func]{minPathSumDFS} + /* 最小路径和:暴力搜索 */ + 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 left = minPathSumDFS(grid, i - 1, j); + int up = minPathSumDFS(grid, i, j - 1); + // 返回从左上角到 (i, j) 的最小路径代价 + return Math.Min(left, up) + grid[i][j]; + } ``` === "Go" ```go title="min_path_sum.go" - [class]{}-[func]{minPathSumDFS} + /* 最小路径和:暴力搜索 */ + 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) 的最小路径代价 + left := minPathSumDFS(grid, i-1, j) + up := minPathSumDFS(grid, i, j-1) + // 返回从左上角到 (i, j) 的最小路径代价 + return int(math.Min(float64(left), float64(up))) + grid[i][j] + } ``` === "Swift" ```swift title="min_path_sum.swift" - [class]{}-[func]{minPathSumDFS} + /* 最小路径和:暴力搜索 */ + 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 left = minPathSumDFS(grid: grid, i: i - 1, j: j) + let up = minPathSumDFS(grid: grid, i: i, j: j - 1) + // 返回从左上角到 (i, j) 的最小路径代价 + return min(left, up) + grid[i][j] + } ``` === "JS" ```javascript title="min_path_sum.js" - [class]{}-[func]{minPathSumDFS} + /* 最小路径和:暴力搜索 */ + 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 left = minPathSumDFS(grid, i - 1, j); + const up = minPathSumDFS(grid, i, j - 1); + // 返回从左上角到 (i, j) 的最小路径代价 + return Math.min(left, up) + grid[i][j]; + } ``` === "TS" ```typescript title="min_path_sum.ts" - [class]{}-[func]{minPathSumDFS} + /* 最小路径和:暴力搜索 */ + 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 left = minPathSumDFS(grid, i - 1, j); + const up = minPathSumDFS(grid, i, j - 1); + // 返回从左上角到 (i, j) 的最小路径代价 + return Math.min(left, up) + grid[i][j]; + } ``` === "Dart" ```dart title="min_path_sum.dart" - [class]{}-[func]{minPathSumDFS} + /* 最小路径和:暴力搜索 */ + 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 left = minPathSumDFS(grid, i - 1, j); + int up = minPathSumDFS(grid, i, j - 1); + // 返回从左上角到 (i, j) 的最小路径代价 + return min(left, up) + grid[i][j]; + } ``` === "Rust" ```rust title="min_path_sum.rs" - [class]{}-[func]{min_path_sum_dfs} + /* 最小路径和:暴力搜索 */ + 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 left = min_path_sum_dfs(grid, i - 1, j); + let up = min_path_sum_dfs(grid, i, j - 1); + // 返回从左上角到 (i, j) 的最小路径代价 + std::cmp::min(left, up) + grid[i as usize][j as usize] + } ``` === "C" @@ -179,7 +331,22 @@ $$ === "Zig" ```zig title="min_path_sum.zig" - [class]{}-[func]{minPathSumDFS} + // 最小路径和:暴力搜索 + 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 left = minPathSumDFS(grid, i - 1, j); + var up = minPathSumDFS(grid, i, j - 1); + // 返回从左上角到 (i, j) 的最小路径代价 + return @min(left, up) + grid[@as(usize, @intCast(i))][@as(usize, @intCast(j))]; + } ``` 图 14-14 给出了以 $dp[2, 1]$ 为根节点的递归树,其中包含一些重叠子问题,其数量会随着网格 `grid` 的尺寸变大而急剧增多。 @@ -199,61 +366,265 @@ $$ === "Python" ```python title="min_path_sum.py" - [class]{}-[func]{min_path_sum_dfs_mem} + def min_path_sum_dfs_mem( + grid: list[list[int]], mem: list[list[int]], i: int, j: int + ) -> int: + """最小路径和:记忆化搜索""" + # 若为左上角单元格,则终止搜索 + if i == 0 and j == 0: + return grid[0][0] + # 若行列索引越界,则返回 +∞ 代价 + if i < 0 or j < 0: + return inf + # 若已有记录,则直接返回 + if mem[i][j] != -1: + return mem[i][j] + # 左边和上边单元格的最小路径代价 + left = min_path_sum_dfs_mem(grid, mem, i - 1, j) + up = min_path_sum_dfs_mem(grid, mem, i, j - 1) + # 记录并返回左上角到 (i, j) 的最小路径代价 + mem[i][j] = min(left, up) + grid[i][j] + return mem[i][j] ``` === "C++" ```cpp title="min_path_sum.cpp" - [class]{}-[func]{minPathSumDFSMem} + /* 最小路径和:记忆化搜索 */ + 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 left = minPathSumDFSMem(grid, mem, i - 1, j); + int up = 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]; + } ``` === "Java" ```java title="min_path_sum.java" - [class]{min_path_sum}-[func]{minPathSumDFSMem} + /* 最小路径和:记忆化搜索 */ + 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 Integer.MAX_VALUE; + } + // 若已有记录,则直接返回 + if (mem[i][j] != -1) { + return mem[i][j]; + } + // 左边和上边单元格的最小路径代价 + int left = minPathSumDFSMem(grid, mem, i - 1, j); + int up = minPathSumDFSMem(grid, mem, i, j - 1); + // 记录并返回左上角到 (i, j) 的最小路径代价 + mem[i][j] = Math.min(left, up) + grid[i][j]; + return mem[i][j]; + } ``` === "C#" ```csharp title="min_path_sum.cs" - [class]{min_path_sum}-[func]{minPathSumDFSMem} + /* 最小路径和:记忆化搜索 */ + 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 left = minPathSumDFSMem(grid, mem, i - 1, j); + int up = minPathSumDFSMem(grid, mem, i, j - 1); + // 记录并返回左上角到 (i, j) 的最小路径代价 + mem[i][j] = Math.Min(left, up) + grid[i][j]; + return mem[i][j]; + } ``` === "Go" ```go title="min_path_sum.go" - [class]{}-[func]{minPathSumDFSMem} + /* 最小路径和:记忆化搜索 */ + 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] + } + // 左边和上边单元格的最小路径代价 + left := minPathSumDFSMem(grid, mem, i-1, j) + up := 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] + } ``` === "Swift" ```swift title="min_path_sum.swift" - [class]{}-[func]{minPathSumDFSMem} + /* 最小路径和:记忆化搜索 */ + 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 left = minPathSumDFSMem(grid: grid, mem: &mem, i: i - 1, j: j) + let up = 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] + } ``` === "JS" ```javascript title="min_path_sum.js" - [class]{}-[func]{minPathSumDFSMem} + /* 最小路径和:记忆化搜索 */ + 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 left = minPathSumDFSMem(grid, mem, i - 1, j); + const up = minPathSumDFSMem(grid, mem, i, j - 1); + // 记录并返回左上角到 (i, j) 的最小路径代价 + mem[i][j] = Math.min(left, up) + grid[i][j]; + return mem[i][j]; + } ``` === "TS" ```typescript title="min_path_sum.ts" - [class]{}-[func]{minPathSumDFSMem} + /* 最小路径和:记忆化搜索 */ + 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 left = minPathSumDFSMem(grid, mem, i - 1, j); + const up = minPathSumDFSMem(grid, mem, i, j - 1); + // 记录并返回左上角到 (i, j) 的最小路径代价 + mem[i][j] = Math.min(left, up) + grid[i][j]; + return mem[i][j]; + } ``` === "Dart" ```dart title="min_path_sum.dart" - [class]{}-[func]{minPathSumDFSMem} + /* 最小路径和:记忆化搜索 */ + 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 left = minPathSumDFSMem(grid, mem, i - 1, j); + int up = minPathSumDFSMem(grid, mem, i, j - 1); + // 记录并返回左上角到 (i, j) 的最小路径代价 + mem[i][j] = min(left, up) + grid[i][j]; + return mem[i][j]; + } ``` === "Rust" ```rust title="min_path_sum.rs" - [class]{}-[func]{min_path_sum_dfs_mem} + /* 最小路径和:记忆化搜索 */ + 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 left = min_path_sum_dfs_mem(grid, mem, i - 1, j); + let up = 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] + } ``` === "C" @@ -265,7 +636,28 @@ $$ === "Zig" ```zig title="min_path_sum.zig" - [class]{}-[func]{minPathSumDFSMem} + // 最小路径和:记忆化搜索 + 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 left = minPathSumDFSMem(grid, mem, i - 1, j); + var up = 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))]; + } ``` 如图 14-15 所示,在引入记忆化后,所有子问题的解只需计算一次,因此时间复杂度取决于状态总数,即网格尺寸 $O(nm)$ 。 @@ -281,61 +673,276 @@ $$ === "Python" ```python title="min_path_sum.py" - [class]{}-[func]{min_path_sum_dp} + def min_path_sum_dp(grid: list[list[int]]) -> int: + """最小路径和:动态规划""" + n, m = len(grid), len(grid[0]) + # 初始化 dp 表 + dp = [[0] * m for _ in range(n)] + dp[0][0] = grid[0][0] + # 状态转移:首行 + for j in range(1, m): + dp[0][j] = dp[0][j - 1] + grid[0][j] + # 状态转移:首列 + for i in range(1, n): + dp[i][0] = dp[i - 1][0] + grid[i][0] + # 状态转移:其余行列 + 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] + return dp[n - 1][m - 1] ``` === "C++" ```cpp title="min_path_sum.cpp" - [class]{}-[func]{minPathSumDP} + /* 最小路径和:动态规划 */ + 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]; + } ``` === "Java" ```java title="min_path_sum.java" - [class]{min_path_sum}-[func]{minPathSumDP} + /* 最小路径和:动态规划 */ + 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]; + } ``` === "C#" ```csharp title="min_path_sum.cs" - [class]{min_path_sum}-[func]{minPathSumDP} + /* 最小路径和:动态规划 */ + 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]; + } ``` === "Go" ```go title="min_path_sum.go" - [class]{}-[func]{minPathSumDP} + /* 最小路径和:动态规划 */ + 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] + } ``` === "Swift" ```swift title="min_path_sum.swift" - [class]{}-[func]{minPathSumDP} + /* 最小路径和:动态规划 */ + 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 stride(from: 1, to: m, by: 1) { + dp[0][j] = dp[0][j - 1] + grid[0][j] + } + // 状态转移:首列 + for i in stride(from: 1, to: n, by: 1) { + dp[i][0] = dp[i - 1][0] + grid[i][0] + } + // 状态转移:其余行列 + for i in stride(from: 1, to: n, by: 1) { + for j in stride(from: 1, to: m, by: 1) { + dp[i][j] = min(dp[i][j - 1], dp[i - 1][j]) + grid[i][j] + } + } + return dp[n - 1][m - 1] + } ``` === "JS" ```javascript title="min_path_sum.js" - [class]{}-[func]{minPathSumDP} + /* 最小路径和:动态规划 */ + 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]; + } ``` === "TS" ```typescript title="min_path_sum.ts" - [class]{}-[func]{minPathSumDP} + /* 最小路径和:动态规划 */ + 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]; + } ``` === "Dart" ```dart title="min_path_sum.dart" - [class]{}-[func]{minPathSumDP} + /* 最小路径和:动态规划 */ + 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]; + } ``` === "Rust" ```rust title="min_path_sum.rs" - [class]{}-[func]{min_path_sum_dp} + /* 最小路径和:动态规划 */ + 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] + } ``` === "C" @@ -347,7 +954,29 @@ $$ === "Zig" ```zig title="min_path_sum.zig" - [class]{}-[func]{minPathSumDP} + // 最小路径和:动态规划 + fn minPathSumDP(comptime grid: anytype) i32 { + comptime var n = grid.len; + comptime var m = grid[0].len; + // 初始化 dp 表 + var dp = [_][m]i32{[_]i32{0} ** m} ** n; + dp[0][0] = grid[0][0]; + // 状态转移:首行 + for (1..m) |j| { + dp[0][j] = dp[0][j - 1] + grid[0][j]; + } + // 状态转移:首列 + for (1..n) |i| { + dp[i][0] = dp[i - 1][0] + grid[i][0]; + } + // 状态转移:其余行列 + for (1..n) |i| { + for (1..m) |j| { + dp[i][j] = @min(dp[i][j - 1], dp[i - 1][j]) + grid[i][j]; + } + } + return dp[n - 1][m - 1]; + } ``` 图 14-16 展示了最小路径和的状态转移过程,其遍历了整个网格,**因此时间复杂度为 $O(nm)$** 。 @@ -401,61 +1030,259 @@ $$ === "Python" ```python title="min_path_sum.py" - [class]{}-[func]{min_path_sum_dp_comp} + def min_path_sum_dp_comp(grid: list[list[int]]) -> int: + """最小路径和:空间优化后的动态规划""" + n, m = len(grid), len(grid[0]) + # 初始化 dp 表 + dp = [0] * m + # 状态转移:首行 + dp[0] = grid[0][0] + for j in range(1, m): + dp[j] = dp[j - 1] + grid[0][j] + # 状态转移:其余行 + for i in range(1, n): + # 状态转移:首列 + dp[0] = dp[0] + grid[i][0] + # 状态转移:其余列 + for j in range(1, m): + dp[j] = min(dp[j - 1], dp[j]) + grid[i][j] + return dp[m - 1] ``` === "C++" ```cpp title="min_path_sum.cpp" - [class]{}-[func]{minPathSumDPComp} + /* 最小路径和:空间优化后的动态规划 */ + 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]; + } ``` === "Java" ```java title="min_path_sum.java" - [class]{min_path_sum}-[func]{minPathSumDPComp} + /* 最小路径和:空间优化后的动态规划 */ + 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]; + } ``` === "C#" ```csharp title="min_path_sum.cs" - [class]{min_path_sum}-[func]{minPathSumDPComp} + /* 最小路径和:空间优化后的动态规划 */ + 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]; + } ``` === "Go" ```go title="min_path_sum.go" - [class]{}-[func]{minPathSumDPComp} + /* 最小路径和:空间优化后的动态规划 */ + 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] + } ``` === "Swift" ```swift title="min_path_sum.swift" - [class]{}-[func]{minPathSumDPComp} + /* 最小路径和:空间优化后的动态规划 */ + 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 stride(from: 1, to: m, by: 1) { + dp[j] = dp[j - 1] + grid[0][j] + } + // 状态转移:其余行 + for i in stride(from: 1, to: n, by: 1) { + // 状态转移:首列 + dp[0] = dp[0] + grid[i][0] + // 状态转移:其余列 + for j in stride(from: 1, to: m, by: 1) { + dp[j] = min(dp[j - 1], dp[j]) + grid[i][j] + } + } + return dp[m - 1] + } ``` === "JS" ```javascript title="min_path_sum.js" - [class]{}-[func]{minPathSumDPComp} + /* 最小路径和:状态压缩后的动态规划 */ + 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]; + } ``` === "TS" ```typescript title="min_path_sum.ts" - [class]{}-[func]{minPathSumDPComp} + /* 最小路径和:状态压缩后的动态规划 */ + 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]; + } ``` === "Dart" ```dart title="min_path_sum.dart" - [class]{}-[func]{minPathSumDPComp} + /* 最小路径和:空间优化后的动态规划 */ + 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]; + } ``` === "Rust" ```rust title="min_path_sum.rs" - [class]{}-[func]{min_path_sum_dp_comp} + /* 最小路径和:空间优化后的动态规划 */ + 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] + } ``` === "C" @@ -467,5 +1294,25 @@ $$ === "Zig" ```zig title="min_path_sum.zig" - [class]{}-[func]{minPathSumDPComp} + // 最小路径和:空间优化后的动态规划 + fn minPathSumDPComp(comptime grid: anytype) i32 { + comptime var n = grid.len; + comptime var m = grid[0].len; + // 初始化 dp 表 + var dp = [_]i32{0} ** m; + // 状态转移:首行 + dp[0] = grid[0][0]; + for (1..m) |j| { + dp[j] = dp[j - 1] + grid[0][j]; + } + // 状态转移:其余行 + for (1..n) |i| { + // 状态转移:首列 + dp[0] = dp[0] + grid[i][0]; + for (1..m) |j| { + dp[j] = @min(dp[j - 1], dp[j]) + grid[i][j]; + } + } + return dp[m - 1]; + } ``` diff --git a/zh/chapter_dynamic_programming/edit_distance_problem.md b/zh/chapter_dynamic_programming/edit_distance_problem.md index 1fb96cb76..08e4d5a6f 100644 --- a/zh/chapter_dynamic_programming/edit_distance_problem.md +++ b/zh/chapter_dynamic_programming/edit_distance_problem.md @@ -80,61 +80,312 @@ $$ === "Python" ```python title="edit_distance.py" - [class]{}-[func]{edit_distance_dp} + def edit_distance_dp(s: str, t: str) -> int: + """编辑距离:动态规划""" + n, m = len(s), len(t) + dp = [[0] * (m + 1) for _ in range(n + 1)] + # 状态转移:首行首列 + for i in range(1, n + 1): + dp[i][0] = i + for j in range(1, m + 1): + dp[0][j] = j + # 状态转移:其余行列 + for i in range(1, n + 1): + for j in range(1, m + 1): + if s[i - 1] == t[j - 1]: + # 若两字符相等,则直接跳过此两字符 + dp[i][j] = dp[i - 1][j - 1] + else: + # 最少编辑步数 = 插入、删除、替换这三种操作的最少编辑步数 + 1 + dp[i][j] = min(dp[i][j - 1], dp[i - 1][j], dp[i - 1][j - 1]) + 1 + return dp[n][m] ``` === "C++" ```cpp title="edit_distance.cpp" - [class]{}-[func]{editDistanceDP} + /* 编辑距离:动态规划 */ + 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]; + } ``` === "Java" ```java title="edit_distance.java" - [class]{edit_distance}-[func]{editDistanceDP} + /* 编辑距离:动态规划 */ + 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.charAt(i - 1) == t.charAt(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]; + } ``` === "C#" ```csharp title="edit_distance.cs" - [class]{edit_distance}-[func]{editDistanceDP} + /* 编辑距离:动态规划 */ + 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]; + } ``` === "Go" ```go title="edit_distance.go" - [class]{}-[func]{editDistanceDP} + /* 编辑距离:动态规划 */ + 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] + } ``` === "Swift" ```swift title="edit_distance.swift" - [class]{}-[func]{editDistanceDP} + /* 编辑距离:动态规划 */ + 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 stride(from: 1, through: n, by: 1) { + dp[i][0] = i + } + for j in stride(from: 1, through: m, by: 1) { + dp[0][j] = j + } + // 状态转移:其余行列 + for i in stride(from: 1, through: n, by: 1) { + for j in stride(from: 1, through: m, by: 1) { + 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] + } ``` === "JS" ```javascript title="edit_distance.js" - [class]{}-[func]{editDistanceDP} + /* 编辑距离:动态规划 */ + 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( + Math.min(dp[i][j - 1], dp[i - 1][j]), + dp[i - 1][j - 1] + ) + 1; + } + } + } + return dp[n][m]; + } ``` === "TS" ```typescript title="edit_distance.ts" - [class]{}-[func]{editDistanceDP} + /* 编辑距离:动态规划 */ + 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( + Math.min(dp[i][j - 1], dp[i - 1][j]), + dp[i - 1][j - 1] + ) + 1; + } + } + } + return dp[n][m]; + } ``` === "Dart" ```dart title="edit_distance.dart" - [class]{}-[func]{editDistanceDP} + /* 编辑距离:动态规划 */ + 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]; + } ``` === "Rust" ```rust title="edit_distance.rs" - [class]{}-[func]{edit_distance_dp} + /* 编辑距离:动态规划 */ + 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] + } ``` === "C" @@ -146,7 +397,32 @@ $$ === "Zig" ```zig title="edit_distance.zig" - [class]{}-[func]{editDistanceDP} + // 编辑距离:动态规划 + 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]; + } ``` 如图 14-30 所示,编辑距离问题的状态转移过程与背包问题非常类似,都可以看作是填写一个二维网格的过程。 @@ -207,61 +483,330 @@ $$ === "Python" ```python title="edit_distance.py" - [class]{}-[func]{edit_distance_dp_comp} + def edit_distance_dp_comp(s: str, t: str) -> int: + """编辑距离:空间优化后的动态规划""" + n, m = len(s), len(t) + dp = [0] * (m + 1) + # 状态转移:首行 + for j in range(1, m + 1): + dp[j] = j + # 状态转移:其余行 + for i in range(1, n + 1): + # 状态转移:首列 + leftup = dp[0] # 暂存 dp[i-1, j-1] + dp[0] += 1 + # 状态转移:其余列 + for j in range(1, m + 1): + temp = dp[j] + if s[i - 1] == t[j - 1]: + # 若两字符相等,则直接跳过此两字符 + dp[j] = leftup + else: + # 最少编辑步数 = 插入、删除、替换这三种操作的最少编辑步数 + 1 + dp[j] = min(dp[j - 1], dp[j], leftup) + 1 + leftup = temp # 更新为下一轮的 dp[i-1, j-1] + return dp[m] ``` === "C++" ```cpp title="edit_distance.cpp" - [class]{}-[func]{editDistanceDPComp} + /* 编辑距离:空间优化后的动态规划 */ + 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]; + } ``` === "Java" ```java title="edit_distance.java" - [class]{edit_distance}-[func]{editDistanceDPComp} + /* 编辑距离:空间优化后的动态规划 */ + 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.charAt(i - 1) == t.charAt(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]; + } ``` === "C#" ```csharp title="edit_distance.cs" - [class]{edit_distance}-[func]{editDistanceDPComp} + /* 编辑距离:空间优化后的动态规划 */ + 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]; + } ``` === "Go" ```go title="edit_distance.go" - [class]{}-[func]{editDistanceDPComp} + /* 编辑距离:空间优化后的动态规划 */ + 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] + } ``` === "Swift" ```swift title="edit_distance.swift" - [class]{}-[func]{editDistanceDPComp} + /* 编辑距离:空间优化后的动态规划 */ + 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 stride(from: 1, through: m, by: 1) { + dp[j] = j + } + // 状态转移:其余行 + for i in stride(from: 1, through: n, by: 1) { + // 状态转移:首列 + var leftup = dp[0] // 暂存 dp[i-1, j-1] + dp[0] = i + // 状态转移:其余列 + for j in stride(from: 1, through: m, by: 1) { + 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] + } ``` === "JS" ```javascript title="edit_distance.js" - [class]{}-[func]{editDistanceDPComp} + /* 编辑距离:状态压缩后的动态规划 */ + 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(Math.min(dp[j - 1], dp[j]), leftup) + 1; + } + leftup = temp; // 更新为下一轮的 dp[i-1, j-1] + } + } + return dp[m]; + } ``` === "TS" ```typescript title="edit_distance.ts" - [class]{}-[func]{editDistanceDPComp} + /* 编辑距离:状态压缩后的动态规划 */ + 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(Math.min(dp[j - 1], dp[j]), leftup) + 1; + } + leftup = temp; // 更新为下一轮的 dp[i-1, j-1] + } + } + return dp[m]; + } ``` === "Dart" ```dart title="edit_distance.dart" - [class]{}-[func]{editDistanceDPComp} + /* 编辑距离:空间优化后的动态规划 */ + 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]; + } ``` === "Rust" ```rust title="edit_distance.rs" - [class]{}-[func]{edit_distance_dp_comp} + /* 编辑距离:空间优化后的动态规划 */ + 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] + } ``` === "C" @@ -273,5 +818,33 @@ $$ === "Zig" ```zig title="edit_distance.zig" - [class]{}-[func]{editDistanceDPComp} + // 编辑距离:空间优化后的动态规划 + 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]; + } ``` diff --git a/zh/chapter_dynamic_programming/intro_to_dynamic_programming.md b/zh/chapter_dynamic_programming/intro_to_dynamic_programming.md index 17af92677..4ddef50b7 100644 --- a/zh/chapter_dynamic_programming/intro_to_dynamic_programming.md +++ b/zh/chapter_dynamic_programming/intro_to_dynamic_programming.md @@ -23,97 +23,366 @@ comments: true === "Python" ```python title="climbing_stairs_backtrack.py" - [class]{}-[func]{backtrack} + def backtrack(choices: list[int], state: int, n: int, res: list[int]) -> int: + """回溯""" + # 当爬到第 n 阶时,方案数量加 1 + if state == n: + res[0] += 1 + # 遍历所有选择 + for choice in choices: + # 剪枝:不允许越过第 n 阶 + if state + choice > n: + break + # 尝试:做出选择,更新状态 + backtrack(choices, state + choice, n, res) + # 回退 - [class]{}-[func]{climbing_stairs_backtrack} + def climbing_stairs_backtrack(n: int) -> int: + """爬楼梯:回溯""" + choices = [1, 2] # 可选择向上爬 1 或 2 阶 + state = 0 # 从第 0 阶开始爬 + res = [0] # 使用 res[0] 记录方案数量 + backtrack(choices, state, n, res) + return res[0] ``` === "C++" ```cpp title="climbing_stairs_backtrack.cpp" - [class]{}-[func]{backtrack} + /* 回溯 */ + 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) + break; + // 尝试:做出选择,更新状态 + backtrack(choices, state + choice, n, res); + // 回退 + } + } - [class]{}-[func]{climbingStairsBacktrack} + /* 爬楼梯:回溯 */ + 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]; + } ``` === "Java" ```java title="climbing_stairs_backtrack.java" - [class]{climbing_stairs_backtrack}-[func]{backtrack} + /* 回溯 */ + void backtrack(List choices, int state, int n, List res) { + // 当爬到第 n 阶时,方案数量加 1 + if (state == n) + res.set(0, res.get(0) + 1); + // 遍历所有选择 + for (Integer choice : choices) { + // 剪枝:不允许越过第 n 阶 + if (state + choice > n) + break; + // 尝试:做出选择,更新状态 + backtrack(choices, state + choice, n, res); + // 回退 + } + } - [class]{climbing_stairs_backtrack}-[func]{climbingStairsBacktrack} + /* 爬楼梯:回溯 */ + int climbingStairsBacktrack(int n) { + List choices = Arrays.asList(1, 2); // 可选择向上爬 1 或 2 阶 + int state = 0; // 从第 0 阶开始爬 + List res = new ArrayList<>(); + res.add(0); // 使用 res[0] 记录方案数量 + backtrack(choices, state, n, res); + return res.get(0); + } ``` === "C#" ```csharp title="climbing_stairs_backtrack.cs" - [class]{climbing_stairs_backtrack}-[func]{backtrack} + /* 回溯 */ + 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) + break; + // 尝试:做出选择,更新状态 + backtrack(choices, state + choice, n, res); + // 回退 + } + } - [class]{climbing_stairs_backtrack}-[func]{climbingStairsBacktrack} + /* 爬楼梯:回溯 */ + int climbingStairsBacktrack(int n) { + List choices = new List { 1, 2 }; // 可选择向上爬 1 或 2 阶 + int state = 0; // 从第 0 阶开始爬 + List res = new List { 0 }; // 使用 res[0] 记录方案数量 + backtrack(choices, state, n, res); + return res[0]; + } ``` === "Go" ```go title="climbing_stairs_backtrack.go" - [class]{}-[func]{backtrack} + /* 回溯 */ + 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 { + break + } + // 尝试:做出选择,更新状态 + backtrack(choices, state+choice, n, res) + // 回退 + } + } - [class]{}-[func]{climbingStairsBacktrack} + /* 爬楼梯:回溯 */ + 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] + } ``` === "Swift" ```swift title="climbing_stairs_backtrack.swift" - [class]{}-[func]{backtrack} + /* 回溯 */ + 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 { + break + } + backtrack(choices: choices, state: state + choice, n: n, res: &res) + } + } - [class]{}-[func]{climbingStairsBacktrack} + /* 爬楼梯:回溯 */ + 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] + } ``` === "JS" ```javascript title="climbing_stairs_backtrack.js" - [class]{}-[func]{backtrack} + /* 回溯 */ + 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) break; + // 尝试:做出选择,更新状态 + backtrack(choices, state + choice, n, res); + // 回退 + } + } - [class]{}-[func]{climbingStairsBacktrack} + /* 爬楼梯:回溯 */ + 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); + } ``` === "TS" ```typescript title="climbing_stairs_backtrack.ts" - [class]{}-[func]{backtrack} + /* 回溯 */ + 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) break; + // 尝试:做出选择,更新状态 + backtrack(choices, state + choice, n, res); + // 回退 + } + } - [class]{}-[func]{climbingStairsBacktrack} + /* 爬楼梯:回溯 */ + 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); + } ``` === "Dart" ```dart title="climbing_stairs_backtrack.dart" - [class]{}-[func]{backtrack} + /* 回溯 */ + 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) break; + // 尝试:做出选择,更新状态 + backtrack(choices, state + choice, n, res); + // 回退 + } + } - [class]{}-[func]{climbingStairsBacktrack} + /* 爬楼梯:回溯 */ + 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]; + } ``` === "Rust" ```rust title="climbing_stairs_backtrack.rs" - [class]{}-[func]{backtrack} + /* 回溯 */ + 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 { break; } + // 尝试:做出选择,更新状态 + backtrack(choices, state + choice, n, res); + // 回退 + } + } - [class]{}-[func]{climbing_stairs_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] + } ``` === "C" ```c title="climbing_stairs_backtrack.c" - [class]{}-[func]{backtrack} + /* 回溯 */ + 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) + break; + // 尝试:做出选择,更新状态 + backtrack(choices, state + choice, n, res, len); + // 回退 + } + } - [class]{}-[func]{climbingStairsBacktrack} + /* 爬楼梯:回溯 */ + 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; + } ``` === "Zig" ```zig title="climbing_stairs_backtrack.zig" - [class]{}-[func]{backtrack} + // 回溯 + 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) { + break; + } + // 尝试:做出选择,更新状态 + backtrack(choices, state + choice, n, res); + // 回退 + } + } - [class]{}-[func]{climbingStairsBacktrack} + // 爬楼梯:回溯 + 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]; + } ``` ## 14.1.1   方法一:暴力搜索 @@ -147,97 +416,226 @@ $$ === "Python" ```python title="climbing_stairs_dfs.py" - [class]{}-[func]{dfs} + def dfs(i: int) -> int: + """搜索""" + # 已知 dp[1] 和 dp[2] ,返回之 + if i == 1 or i == 2: + return i + # dp[i] = dp[i-1] + dp[i-2] + count = dfs(i - 1) + dfs(i - 2) + return count - [class]{}-[func]{climbing_stairs_dfs} + def climbing_stairs_dfs(n: int) -> int: + """爬楼梯:搜索""" + return dfs(n) ``` === "C++" ```cpp title="climbing_stairs_dfs.cpp" - [class]{}-[func]{dfs} + /* 搜索 */ + 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]{climbingStairsDFS} + /* 爬楼梯:搜索 */ + int climbingStairsDFS(int n) { + return dfs(n); + } ``` === "Java" ```java title="climbing_stairs_dfs.java" - [class]{climbing_stairs_dfs}-[func]{dfs} + /* 搜索 */ + 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]{climbingStairsDFS} + /* 爬楼梯:搜索 */ + int climbingStairsDFS(int n) { + return dfs(n); + } ``` === "C#" ```csharp title="climbing_stairs_dfs.cs" - [class]{climbing_stairs_dfs}-[func]{dfs} + /* 搜索 */ + 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]{climbingStairsDFS} + /* 爬楼梯:搜索 */ + int climbingStairsDFS(int n) { + return dfs(n); + } ``` === "Go" ```go title="climbing_stairs_dfs.go" - [class]{}-[func]{dfs} + /* 搜索 */ + 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]{climbingStairsDFS} + /* 爬楼梯:搜索 */ + func climbingStairsDFS(n int) int { + return dfs(n) + } ``` === "Swift" ```swift title="climbing_stairs_dfs.swift" - [class]{}-[func]{dfs} + /* 搜索 */ + 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]{climbingStairsDFS} + /* 爬楼梯:搜索 */ + func climbingStairsDFS(n: Int) -> Int { + dfs(i: n) + } ``` === "JS" ```javascript title="climbing_stairs_dfs.js" - [class]{}-[func]{dfs} + /* 搜索 */ + 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]{climbingStairsDFS} + /* 爬楼梯:搜索 */ + function climbingStairsDFS(n) { + return dfs(n); + } ``` === "TS" ```typescript title="climbing_stairs_dfs.ts" - [class]{}-[func]{dfs} + /* 搜索 */ + 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]{climbingStairsDFS} + /* 爬楼梯:搜索 */ + function climbingStairsDFS(n: number): number { + return dfs(n); + } ``` === "Dart" ```dart title="climbing_stairs_dfs.dart" - [class]{}-[func]{dfs} + /* 搜索 */ + 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]{climbingStairsDFS} + /* 爬楼梯:搜索 */ + int climbingStairsDFS(int n) { + return dfs(n); + } ``` === "Rust" ```rust title="climbing_stairs_dfs.rs" - [class]{}-[func]{dfs} + /* 搜索 */ + 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]{climbing_stairs_dfs} + /* 爬楼梯:搜索 */ + fn climbing_stairs_dfs(n: usize) -> i32 { + dfs(n) + } ``` === "C" ```c title="climbing_stairs_dfs.c" - [class]{}-[func]{dfs} + /* 搜索 */ + 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]{climbingStairsDFS} + /* 爬楼梯:搜索 */ + int climbingStairsDFS(int n) { + return dfs(n); + } ``` === "Zig" ```zig title="climbing_stairs_dfs.zig" - [class]{}-[func]{dfs} + // 搜索 + 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]{climbingStairsDFS} + // 爬楼梯:搜索 + fn climbingStairsDFS(comptime n: usize) i32 { + return dfs(n); + } ``` 图 14-3 展示了暴力搜索形成的递归树。对于问题 $dp[n]$ ,其递归树的深度为 $n$ ,时间复杂度为 $O(2^n)$ 。指数阶属于爆炸式增长,如果我们输入一个比较大的 $n$ ,则会陷入漫长的等待之中。 @@ -260,97 +658,319 @@ $$ === "Python" ```python title="climbing_stairs_dfs_mem.py" - [class]{}-[func]{dfs} + def dfs(i: int, mem: list[int]) -> int: + """记忆化搜索""" + # 已知 dp[1] 和 dp[2] ,返回之 + if i == 1 or i == 2: + return i + # 若存在记录 dp[i] ,则直接返回之 + 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] + mem[i] = count + return count - [class]{}-[func]{climbing_stairs_dfs_mem} + def climbing_stairs_dfs_mem(n: int) -> int: + """爬楼梯:记忆化搜索""" + # mem[i] 记录爬到第 i 阶的方案总数,-1 代表无记录 + mem = [-1] * (n + 1) + return dfs(n, mem) ``` === "C++" ```cpp title="climbing_stairs_dfs_mem.cpp" - [class]{}-[func]{dfs} + /* 记忆化搜索 */ + 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]{climbingStairsDFSMem} + /* 爬楼梯:记忆化搜索 */ + int climbingStairsDFSMem(int n) { + // mem[i] 记录爬到第 i 阶的方案总数,-1 代表无记录 + vector mem(n + 1, -1); + return dfs(n, mem); + } ``` === "Java" ```java title="climbing_stairs_dfs_mem.java" - [class]{climbing_stairs_dfs_mem}-[func]{dfs} + /* 记忆化搜索 */ + 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]{climbingStairsDFSMem} + /* 爬楼梯:记忆化搜索 */ + int climbingStairsDFSMem(int n) { + // mem[i] 记录爬到第 i 阶的方案总数,-1 代表无记录 + int[] mem = new int[n + 1]; + Arrays.fill(mem, -1); + return dfs(n, mem); + } ``` === "C#" ```csharp title="climbing_stairs_dfs_mem.cs" - [class]{climbing_stairs_dfs_mem}-[func]{dfs} + /* 记忆化搜索 */ + 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]{climbingStairsDFSMem} + /* 爬楼梯:记忆化搜索 */ + int climbingStairsDFSMem(int n) { + // mem[i] 记录爬到第 i 阶的方案总数,-1 代表无记录 + int[] mem = new int[n + 1]; + Array.Fill(mem, -1); + return dfs(n, mem); + } ``` === "Go" ```go title="climbing_stairs_dfs_mem.go" - [class]{}-[func]{dfsMem} + /* 记忆化搜索 */ + 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]{climbingStairsDFSMem} + /* 爬楼梯:记忆化搜索 */ + 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) + } ``` === "Swift" ```swift title="climbing_stairs_dfs_mem.swift" - [class]{}-[func]{dfs} + /* 记忆化搜索 */ + 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]{climbingStairsDFSMem} + /* 爬楼梯:记忆化搜索 */ + func climbingStairsDFSMem(n: Int) -> Int { + // mem[i] 记录爬到第 i 阶的方案总数,-1 代表无记录 + var mem = Array(repeating: -1, count: n + 1) + return dfs(i: n, mem: &mem) + } ``` === "JS" ```javascript title="climbing_stairs_dfs_mem.js" - [class]{}-[func]{dfs} + /* 记忆化搜索 */ + 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]{climbingStairsDFSMem} + /* 爬楼梯:记忆化搜索 */ + function climbingStairsDFSMem(n) { + // mem[i] 记录爬到第 i 阶的方案总数,-1 代表无记录 + const mem = new Array(n + 1).fill(-1); + return dfs(n, mem); + } ``` === "TS" ```typescript title="climbing_stairs_dfs_mem.ts" - [class]{}-[func]{dfs} + /* 记忆化搜索 */ + 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]{climbingStairsDFSMem} + /* 爬楼梯:记忆化搜索 */ + function climbingStairsDFSMem(n: number): number { + // mem[i] 记录爬到第 i 阶的方案总数,-1 代表无记录 + const mem = new Array(n + 1).fill(-1); + return dfs(n, mem); + } ``` === "Dart" ```dart title="climbing_stairs_dfs_mem.dart" - [class]{}-[func]{dfs} + /* 记忆化搜索 */ + 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]{climbingStairsDFSMem} + /* 爬楼梯:记忆化搜索 */ + int climbingStairsDFSMem(int n) { + // mem[i] 记录爬到第 i 阶的方案总数,-1 代表无记录 + List mem = List.filled(n + 1, -1); + return dfs(n, mem); + } ``` === "Rust" ```rust title="climbing_stairs_dfs_mem.rs" - [class]{}-[func]{dfs} + /* 记忆化搜索 */ + 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]{climbing_stairs_dfs_mem} + /* 爬楼梯:记忆化搜索 */ + fn climbing_stairs_dfs_mem(n: usize) -> i32 { + // mem[i] 记录爬到第 i 阶的方案总数,-1 代表无记录 + let mut mem = vec![-1; n + 1]; + dfs(n, &mut mem) + } ``` === "C" ```c title="climbing_stairs_dfs_mem.c" - [class]{}-[func]{dfs} + /* 记忆化搜索 */ + 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]{climbingStairsDFSMem} + /* 爬楼梯:记忆化搜索 */ + 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; + } ``` === "Zig" ```zig title="climbing_stairs_dfs_mem.zig" - [class]{}-[func]{dfs} + // 记忆化搜索 + 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]{climbingStairsDFSMem} + // 爬楼梯:记忆化搜索 + fn climbingStairsDFSMem(comptime n: usize) i32 { + // mem[i] 记录爬到第 i 阶的方案总数,-1 代表无记录 + var mem = [_]i32{ -1 } ** (n + 1); + return dfs(n, &mem); + } ``` 观察图 14-4 ,**经过记忆化处理后,所有重叠子问题都只需被计算一次,时间复杂度被优化至 $O(n)$** ,这是一个巨大的飞跃。 @@ -370,73 +990,241 @@ $$ === "Python" ```python title="climbing_stairs_dp.py" - [class]{}-[func]{climbing_stairs_dp} + def climbing_stairs_dp(n: int) -> int: + """爬楼梯:动态规划""" + if n == 1 or n == 2: + return n + # 初始化 dp 表,用于存储子问题的解 + dp = [0] * (n + 1) + # 初始状态:预设最小子问题的解 + dp[1], dp[2] = 1, 2 + # 状态转移:从较小子问题逐步求解较大子问题 + for i in range(3, n + 1): + dp[i] = dp[i - 1] + dp[i - 2] + return dp[n] ``` === "C++" ```cpp title="climbing_stairs_dp.cpp" - [class]{}-[func]{climbingStairsDP} + /* 爬楼梯:动态规划 */ + 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]; + } ``` === "Java" ```java title="climbing_stairs_dp.java" - [class]{climbing_stairs_dp}-[func]{climbingStairsDP} + /* 爬楼梯:动态规划 */ + 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]; + } ``` === "C#" ```csharp title="climbing_stairs_dp.cs" - [class]{climbing_stairs_dp}-[func]{climbingStairsDP} + /* 爬楼梯:动态规划 */ + 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]; + } ``` === "Go" ```go title="climbing_stairs_dp.go" - [class]{}-[func]{climbingStairsDP} + /* 爬楼梯:动态规划 */ + 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] + } ``` === "Swift" ```swift title="climbing_stairs_dp.swift" - [class]{}-[func]{climbingStairsDP} + /* 爬楼梯:动态规划 */ + 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 stride(from: 3, through: n, by: 1) { + dp[i] = dp[i - 1] + dp[i - 2] + } + return dp[n] + } ``` === "JS" ```javascript title="climbing_stairs_dp.js" - [class]{}-[func]{climbingStairsDP} + /* 爬楼梯:动态规划 */ + 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]; + } ``` === "TS" ```typescript title="climbing_stairs_dp.ts" - [class]{}-[func]{climbingStairsDP} + /* 爬楼梯:动态规划 */ + 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]; + } ``` === "Dart" ```dart title="climbing_stairs_dp.dart" - [class]{}-[func]{climbingStairsDP} + /* 爬楼梯:动态规划 */ + 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]; + } ``` === "Rust" ```rust title="climbing_stairs_dp.rs" - [class]{}-[func]{climbing_stairs_dp} + /* 爬楼梯:动态规划 */ + 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] + } ``` === "C" ```c title="climbing_stairs_dp.c" - [class]{}-[func]{climbingStairsDP} + /* 爬楼梯:动态规划 */ + 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; + } ``` === "Zig" ```zig title="climbing_stairs_dp.zig" - [class]{}-[func]{climbingStairsDP} + // 爬楼梯:动态规划 + 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]; + } ``` 图 14-5 模拟了以上代码的执行过程。 @@ -460,73 +1248,201 @@ $$ === "Python" ```python title="climbing_stairs_dp.py" - [class]{}-[func]{climbing_stairs_dp_comp} + def climbing_stairs_dp_comp(n: int) -> int: + """爬楼梯:空间优化后的动态规划""" + if n == 1 or n == 2: + return n + a, b = 1, 2 + for _ in range(3, n + 1): + a, b = b, a + b + return b ``` === "C++" ```cpp title="climbing_stairs_dp.cpp" - [class]{}-[func]{climbingStairsDPComp} + /* 爬楼梯:空间优化后的动态规划 */ + 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; + } ``` === "Java" ```java title="climbing_stairs_dp.java" - [class]{climbing_stairs_dp}-[func]{climbingStairsDPComp} + /* 爬楼梯:空间优化后的动态规划 */ + 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; + } ``` === "C#" ```csharp title="climbing_stairs_dp.cs" - [class]{climbing_stairs_dp}-[func]{climbingStairsDPComp} + /* 爬楼梯:空间优化后的动态规划 */ + 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; + } ``` === "Go" ```go title="climbing_stairs_dp.go" - [class]{}-[func]{climbingStairsDPComp} + /* 爬楼梯:空间优化后的动态规划 */ + 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 + } ``` === "Swift" ```swift title="climbing_stairs_dp.swift" - [class]{}-[func]{climbingStairsDPComp} + /* 爬楼梯:空间优化后的动态规划 */ + func climbingStairsDPComp(n: Int) -> Int { + if n == 1 || n == 2 { + return n + } + var a = 1 + var b = 2 + for _ in stride(from: 3, through: n, by: 1) { + (a, b) = (b, a + b) + } + return b + } ``` === "JS" ```javascript title="climbing_stairs_dp.js" - [class]{}-[func]{climbingStairsDPComp} + /* 爬楼梯:空间优化后的动态规划 */ + 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; + } ``` === "TS" ```typescript title="climbing_stairs_dp.ts" - [class]{}-[func]{climbingStairsDPComp} + /* 爬楼梯:空间优化后的动态规划 */ + 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; + } ``` === "Dart" ```dart title="climbing_stairs_dp.dart" - [class]{}-[func]{climbingStairsDPComp} + /* 爬楼梯:空间优化后的动态规划 */ + 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; + } ``` === "Rust" ```rust title="climbing_stairs_dp.rs" - [class]{}-[func]{climbing_stairs_dp_comp} + /* 爬楼梯:空间优化后的动态规划 */ + 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 + } ``` === "C" ```c title="climbing_stairs_dp.c" - [class]{}-[func]{climbingStairsDPComp} + /* 爬楼梯:空间优化后的动态规划 */ + 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; + } ``` === "Zig" ```zig title="climbing_stairs_dp.zig" - [class]{}-[func]{climbingStairsDPComp} + // 爬楼梯:空间优化后的动态规划 + 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; + } ``` 观察以上代码,由于省去了数组 `dp` 占用的空间,因此空间复杂度从 $O(n)$ 降低至 $O(1)$ 。 diff --git a/zh/chapter_dynamic_programming/knapsack_problem.md b/zh/chapter_dynamic_programming/knapsack_problem.md index 9240e9484..44615cfa0 100644 --- a/zh/chapter_dynamic_programming/knapsack_problem.md +++ b/zh/chapter_dynamic_programming/knapsack_problem.md @@ -65,61 +65,213 @@ $$ === "Python" ```python title="knapsack.py" - [class]{}-[func]{knapsack_dfs} + def knapsack_dfs(wgt: list[int], val: list[int], i: int, c: int) -> int: + """0-1 背包:暴力搜索""" + # 若已选完所有物品或背包无容量,则返回价值 0 + if i == 0 or c == 0: + return 0 + # 若超过背包容量,则只能不放入背包 + if wgt[i - 1] > c: + return knapsack_dfs(wgt, val, i - 1, c) + # 计算不放入和放入物品 i 的最大价值 + no = knapsack_dfs(wgt, val, i - 1, c) + yes = knapsack_dfs(wgt, val, i - 1, c - wgt[i - 1]) + val[i - 1] + # 返回两种方案中价值更大的那一个 + return max(no, yes) ``` === "C++" ```cpp title="knapsack.cpp" - [class]{}-[func]{knapsackDFS} + /* 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); + } ``` === "Java" ```java title="knapsack.java" - [class]{knapsack}-[func]{knapsackDFS} + /* 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 Math.max(no, yes); + } ``` === "C#" ```csharp title="knapsack.cs" - [class]{knapsack}-[func]{knapsackDFS} + /* 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); + } ``` === "Go" ```go title="knapsack.go" - [class]{}-[func]{knapsackDFS} + /* 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))) + } ``` === "Swift" ```swift title="knapsack.swift" - [class]{}-[func]{knapsackDFS} + /* 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) + } ``` === "JS" ```javascript title="knapsack.js" - [class]{}-[func]{knapsackDFS} + /* 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); + } ``` === "TS" ```typescript title="knapsack.ts" - [class]{}-[func]{knapsackDFS} + /* 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); + } ``` === "Dart" ```dart title="knapsack.dart" - [class]{}-[func]{knapsackDFS} + /* 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); + } ``` === "Rust" ```rust title="knapsack.rs" - [class]{}-[func]{knapsack_dfs} + /* 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) + } ``` === "C" @@ -131,7 +283,22 @@ $$ === "Zig" ```zig title="knapsack.zig" - [class]{}-[func]{knapsackDFS} + // 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); + } ``` 如图 14-18 所示,由于每个物品都会产生不选和选两条搜索分支,因此时间复杂度为 $O(2^n)$ 。 @@ -151,61 +318,273 @@ $$ === "Python" ```python title="knapsack.py" - [class]{}-[func]{knapsack_dfs_mem} + def knapsack_dfs_mem( + wgt: list[int], val: list[int], mem: list[list[int]], i: int, c: int + ) -> int: + """0-1 背包:记忆化搜索""" + # 若已选完所有物品或背包无容量,则返回价值 0 + if i == 0 or c == 0: + return 0 + # 若已有记录,则直接返回 + if mem[i][c] != -1: + return mem[i][c] + # 若超过背包容量,则只能不放入背包 + if wgt[i - 1] > c: + return knapsack_dfs_mem(wgt, val, mem, i - 1, c) + # 计算不放入和放入物品 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] + # 记录并返回两种方案中价值更大的那一个 + mem[i][c] = max(no, yes) + return mem[i][c] ``` === "C++" ```cpp title="knapsack.cpp" - [class]{}-[func]{knapsackDFSMem} + /* 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]; + } ``` === "Java" ```java title="knapsack.java" - [class]{knapsack}-[func]{knapsackDFSMem} + /* 0-1 背包:记忆化搜索 */ + int knapsackDFSMem(int[] wgt, 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 (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] = Math.max(no, yes); + return mem[i][c]; + } ``` === "C#" ```csharp title="knapsack.cs" - [class]{knapsack}-[func]{knapsackDFSMem} + /* 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]; + } ``` === "Go" ```go title="knapsack.go" - [class]{}-[func]{knapsackDFSMem} + /* 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] + } ``` === "Swift" ```swift title="knapsack.swift" - [class]{}-[func]{knapsackDFSMem} + /* 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] + } ``` === "JS" ```javascript title="knapsack.js" - [class]{}-[func]{knapsackDFSMem} + /* 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]; + } ``` === "TS" ```typescript title="knapsack.ts" - [class]{}-[func]{knapsackDFSMem} + /* 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]; + } ``` === "Dart" ```dart title="knapsack.dart" - [class]{}-[func]{knapsackDFSMem} + /* 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]; + } ``` === "Rust" ```rust title="knapsack.rs" - [class]{}-[func]{knapsack_dfs_mem} + /* 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] + } ``` === "C" @@ -217,7 +596,27 @@ $$ === "Zig" ```zig title="knapsack.zig" - [class]{}-[func]{knapsackDFSMem} + // 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]; + } ``` 图 14-19 展示了在记忆化递归中被剪掉的搜索分支。 @@ -233,61 +632,254 @@ $$ === "Python" ```python title="knapsack.py" - [class]{}-[func]{knapsack_dp} + def knapsack_dp(wgt: list[int], val: list[int], cap: int) -> int: + """0-1 背包:动态规划""" + n = len(wgt) + # 初始化 dp 表 + dp = [[0] * (cap + 1) for _ in range(n + 1)] + # 状态转移 + for i in range(1, n + 1): + for c in range(1, cap + 1): + 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] ``` === "C++" ```cpp title="knapsack.cpp" - [class]{}-[func]{knapsackDP} + /* 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]; + } ``` === "Java" ```java title="knapsack.java" - [class]{knapsack}-[func]{knapsackDP} + /* 0-1 背包:动态规划 */ + int knapsackDP(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 - 1][c - wgt[i - 1]] + val[i - 1]); + } + } + } + return dp[n][cap]; + } ``` === "C#" ```csharp title="knapsack.cs" - [class]{knapsack}-[func]{knapsackDP} + /* 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]; + } ``` === "Go" ```go title="knapsack.go" - [class]{}-[func]{knapsackDP} + /* 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] + } ``` === "Swift" ```swift title="knapsack.swift" - [class]{}-[func]{knapsackDP} + /* 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 stride(from: 1, through: n, by: 1) { + for c in stride(from: 1, through: cap, by: 1) { + 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] + } ``` === "JS" ```javascript title="knapsack.js" - [class]{}-[func]{knapsackDP} + /* 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]; + } ``` === "TS" ```typescript title="knapsack.ts" - [class]{}-[func]{knapsackDP} + /* 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]; + } ``` === "Dart" ```dart title="knapsack.dart" - [class]{}-[func]{knapsackDP} + /* 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]; + } ``` === "Rust" ```rust title="knapsack.rs" - [class]{}-[func]{knapsack_dp} + /* 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] + } ``` === "C" @@ -299,7 +891,25 @@ $$ === "Zig" ```zig title="knapsack.zig" - [class]{}-[func]{knapsackDP} + // 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]; + } ``` 如图 14-20 所示,时间复杂度和空间复杂度都由数组 `dp` 大小决定,即 $O(n \times cap)$ 。 @@ -384,61 +994,227 @@ $$ === "Python" ```python title="knapsack.py" - [class]{}-[func]{knapsack_dp_comp} + def knapsack_dp_comp(wgt: list[int], val: list[int], cap: int) -> int: + """0-1 背包:空间优化后的动态规划""" + n = len(wgt) + # 初始化 dp 表 + dp = [0] * (cap + 1) + # 状态转移 + for i in range(1, n + 1): + # 倒序遍历 + for c in range(cap, 0, -1): + 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] ``` === "C++" ```cpp title="knapsack.cpp" - [class]{}-[func]{knapsackDPComp} + /* 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]; + } ``` === "Java" ```java title="knapsack.java" - [class]{knapsack}-[func]{knapsackDPComp} + /* 0-1 背包:空间优化后的动态规划 */ + int knapsackDPComp(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 = 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]; + } ``` === "C#" ```csharp title="knapsack.cs" - [class]{knapsack}-[func]{knapsackDPComp} + /* 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]; + } ``` === "Go" ```go title="knapsack.go" - [class]{}-[func]{knapsackDPComp} + /* 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] + } ``` === "Swift" ```swift title="knapsack.swift" - [class]{}-[func]{knapsackDPComp} + /* 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 stride(from: 1, through: n, by: 1) { + // 倒序遍历 + for c in stride(from: cap, through: 1, by: -1) { + if wgt[i - 1] <= c { + // 不选和选物品 i 这两种方案的较大值 + dp[c] = max(dp[c], dp[c - wgt[i - 1]] + val[i - 1]) + } + } + } + return dp[cap] + } ``` === "JS" ```javascript title="knapsack.js" - [class]{}-[func]{knapsackDPComp} + /* 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]; + } ``` === "TS" ```typescript title="knapsack.ts" - [class]{}-[func]{knapsackDPComp} + /* 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]; + } ``` === "Dart" ```dart title="knapsack.dart" - [class]{}-[func]{knapsackDPComp} + /* 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]; + } ``` === "Rust" ```rust title="knapsack.rs" - [class]{}-[func]{knapsack_dp_comp} + /* 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] + } ``` === "C" @@ -450,5 +1226,22 @@ $$ === "Zig" ```zig title="knapsack.zig" - [class]{}-[func]{knapsackDPComp} + // 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]; + } ``` diff --git a/zh/chapter_dynamic_programming/unbounded_knapsack_problem.md b/zh/chapter_dynamic_programming/unbounded_knapsack_problem.md index 00a258358..7ff818a13 100644 --- a/zh/chapter_dynamic_programming/unbounded_knapsack_problem.md +++ b/zh/chapter_dynamic_programming/unbounded_knapsack_problem.md @@ -41,61 +41,254 @@ $$ === "Python" ```python title="unbounded_knapsack.py" - [class]{}-[func]{unbounded_knapsack_dp} + def unbounded_knapsack_dp(wgt: list[int], val: list[int], cap: int) -> int: + """完全背包:动态规划""" + n = len(wgt) + # 初始化 dp 表 + dp = [[0] * (cap + 1) for _ in range(n + 1)] + # 状态转移 + for i in range(1, n + 1): + for c in range(1, cap + 1): + 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] ``` === "C++" ```cpp title="unbounded_knapsack.cpp" - [class]{}-[func]{unboundedKnapsackDP} + /* 完全背包:动态规划 */ + 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]; + } ``` === "Java" ```java title="unbounded_knapsack.java" - [class]{unbounded_knapsack}-[func]{unboundedKnapsackDP} + /* 完全背包:动态规划 */ + 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]; + } ``` === "C#" ```csharp title="unbounded_knapsack.cs" - [class]{unbounded_knapsack}-[func]{unboundedKnapsackDP} + /* 完全背包:动态规划 */ + 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]; + } ``` === "Go" ```go title="unbounded_knapsack.go" - [class]{}-[func]{unboundedKnapsackDP} + /* 完全背包:动态规划 */ + 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] + } ``` === "Swift" ```swift title="unbounded_knapsack.swift" - [class]{}-[func]{unboundedKnapsackDP} + /* 完全背包:动态规划 */ + 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 stride(from: 1, through: n, by: 1) { + for c in stride(from: 1, through: cap, by: 1) { + 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] + } ``` === "JS" ```javascript title="unbounded_knapsack.js" - [class]{}-[func]{unboundedKnapsackDP} + /* 完全背包:动态规划 */ + 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]; + } ``` === "TS" ```typescript title="unbounded_knapsack.ts" - [class]{}-[func]{unboundedKnapsackDP} + /* 完全背包:动态规划 */ + 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]; + } ``` === "Dart" ```dart title="unbounded_knapsack.dart" - [class]{}-[func]{unboundedKnapsackDP} + /* 完全背包:动态规划 */ + 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]; + } ``` === "Rust" ```rust title="unbounded_knapsack.rs" - [class]{}-[func]{unbounded_knapsack_dp} + /* 完全背包:动态规划 */ + 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]; + } ``` === "C" @@ -107,7 +300,25 @@ $$ === "Zig" ```zig title="unbounded_knapsack.zig" - [class]{}-[func]{unboundedKnapsackDP} + // 完全背包:动态规划 + 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]; + } ``` ### 3.   空间优化 @@ -141,61 +352,242 @@ $$ === "Python" ```python title="unbounded_knapsack.py" - [class]{}-[func]{unbounded_knapsack_dp_comp} + def unbounded_knapsack_dp_comp(wgt: list[int], val: list[int], cap: int) -> int: + """完全背包:空间优化后的动态规划""" + n = len(wgt) + # 初始化 dp 表 + dp = [0] * (cap + 1) + # 状态转移 + for i in range(1, n + 1): + # 正序遍历 + for c in range(1, cap + 1): + 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] ``` === "C++" ```cpp title="unbounded_knapsack.cpp" - [class]{}-[func]{unboundedKnapsackDPComp} + /* 完全背包:空间优化后的动态规划 */ + 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]; + } ``` === "Java" ```java title="unbounded_knapsack.java" - [class]{unbounded_knapsack}-[func]{unboundedKnapsackDPComp} + /* 完全背包:空间优化后的动态规划 */ + 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]; + } ``` === "C#" ```csharp title="unbounded_knapsack.cs" - [class]{unbounded_knapsack}-[func]{unboundedKnapsackDPComp} + /* 完全背包:空间优化后的动态规划 */ + 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]; + } ``` === "Go" ```go title="unbounded_knapsack.go" - [class]{}-[func]{unboundedKnapsackDPComp} + /* 完全背包:空间优化后的动态规划 */ + 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] + } ``` === "Swift" ```swift title="unbounded_knapsack.swift" - [class]{}-[func]{unboundedKnapsackDPComp} + /* 完全背包:空间优化后的动态规划 */ + 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 stride(from: 1, through: n, by: 1) { + for c in stride(from: 1, through: cap, by: 1) { + 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] + } ``` === "JS" ```javascript title="unbounded_knapsack.js" - [class]{}-[func]{unboundedKnapsackDPComp} + /* 完全背包:状态压缩后的动态规划 */ + 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]; + } ``` === "TS" ```typescript title="unbounded_knapsack.ts" - [class]{}-[func]{unboundedKnapsackDPComp} + /* 完全背包:状态压缩后的动态规划 */ + 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]; + } ``` === "Dart" ```dart title="unbounded_knapsack.dart" - [class]{}-[func]{unboundedKnapsackDPComp} + /* 完全背包:空间优化后的动态规划 */ + 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]; + } ``` === "Rust" ```rust title="unbounded_knapsack.rs" - [class]{}-[func]{unbounded_knapsack_dp_comp} + /* 完全背包:空间优化后的动态规划 */ + 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] + } ``` === "C" @@ -207,7 +599,25 @@ $$ === "Zig" ```zig title="unbounded_knapsack.zig" - [class]{}-[func]{unboundedKnapsackDPComp} + // 完全背包:空间优化后的动态规划 + 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]; + } ``` ## 14.5.2   零钱兑换问题 @@ -264,61 +674,296 @@ $$ === "Python" ```python title="coin_change.py" - [class]{}-[func]{coin_change_dp} + def coin_change_dp(coins: list[int], amt: int) -> int: + """零钱兑换:动态规划""" + n = len(coins) + MAX = amt + 1 + # 初始化 dp 表 + dp = [[0] * (amt + 1) for _ in range(n + 1)] + # 状态转移:首行首列 + for a in range(1, amt + 1): + dp[0][a] = MAX + # 状态转移:其余行列 + for i in range(1, n + 1): + for a in range(1, amt + 1): + 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] if dp[n][amt] != MAX else -1 ``` === "C++" ```cpp title="coin_change.cpp" - [class]{}-[func]{coinChangeDP} + /* 零钱兑换:动态规划 */ + 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; + } ``` === "Java" ```java title="coin_change.java" - [class]{coin_change}-[func]{coinChangeDP} + /* 零钱兑换:动态规划 */ + 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; + } ``` === "C#" ```csharp title="coin_change.cs" - [class]{coin_change}-[func]{coinChangeDP} + /* 零钱兑换:动态规划 */ + 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; + } ``` === "Go" ```go title="coin_change.go" - [class]{}-[func]{coinChangeDP} + /* 零钱兑换:动态规划 */ + 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 + } ``` === "Swift" ```swift title="coin_change.swift" - [class]{}-[func]{coinChangeDP} + /* 零钱兑换:动态规划 */ + 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 stride(from: 1, through: amt, by: 1) { + dp[0][a] = MAX + } + // 状态转移:其余行列 + for i in stride(from: 1, through: n, by: 1) { + for a in stride(from: 1, through: amt, by: 1) { + 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 + } ``` === "JS" ```javascript title="coin_change.js" - [class]{}-[func]{coinChangeDP} + /* 零钱兑换:动态规划 */ + 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; + } ``` === "TS" ```typescript title="coin_change.ts" - [class]{}-[func]{coinChangeDP} + /* 零钱兑换:动态规划 */ + 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; + } ``` === "Dart" ```dart title="coin_change.dart" - [class]{}-[func]{coinChangeDP} + /* 零钱兑换:动态规划 */ + 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; + } ``` === "Rust" ```rust title="coin_change.rs" - [class]{}-[func]{coin_change_dp} + /* 零钱兑换:动态规划 */ + 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 } + } ``` === "C" @@ -330,7 +975,34 @@ $$ === "Zig" ```zig title="coin_change.zig" - [class]{}-[func]{coinChangeDP} + // 零钱兑换:动态规划 + 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; + } + } ``` 图 14-25 展示了零钱兑换的动态规划过程,和完全背包非常相似。 @@ -389,61 +1061,267 @@ $$ === "Python" ```python title="coin_change.py" - [class]{}-[func]{coin_change_dp_comp} + def coin_change_dp_comp(coins: list[int], amt: int) -> int: + """零钱兑换:空间优化后的动态规划""" + n = len(coins) + MAX = amt + 1 + # 初始化 dp 表 + dp = [MAX] * (amt + 1) + dp[0] = 0 + # 状态转移 + for i in range(1, n + 1): + # 正序遍历 + for a in range(1, amt + 1): + 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] if dp[amt] != MAX else -1 ``` === "C++" ```cpp title="coin_change.cpp" - [class]{}-[func]{coinChangeDPComp} + /* 零钱兑换:空间优化后的动态规划 */ + 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; + } ``` === "Java" ```java title="coin_change.java" - [class]{coin_change}-[func]{coinChangeDPComp} + /* 零钱兑换:空间优化后的动态规划 */ + int coinChangeDPComp(int[] coins, int amt) { + int n = coins.length; + int MAX = amt + 1; + // 初始化 dp 表 + int[] dp = new int[amt + 1]; + Arrays.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; + } ``` === "C#" ```csharp title="coin_change.cs" - [class]{coin_change}-[func]{coinChangeDPComp} + /* 零钱兑换:空间优化后的动态规划 */ + 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; + } ``` === "Go" ```go title="coin_change.go" - [class]{}-[func]{coinChangeDPComp} + /* 零钱兑换:动态规划 */ + 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 + } ``` === "Swift" ```swift title="coin_change.swift" - [class]{}-[func]{coinChangeDPComp} + /* 零钱兑换:空间优化后的动态规划 */ + 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 stride(from: 1, through: n, by: 1) { + for a in stride(from: 1, through: amt, by: 1) { + 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 + } ``` === "JS" ```javascript title="coin_change.js" - [class]{}-[func]{coinChangeDPComp} + /* 零钱兑换:状态压缩后的动态规划 */ + 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; + } ``` === "TS" ```typescript title="coin_change.ts" - [class]{}-[func]{coinChangeDPComp} + /* 零钱兑换:状态压缩后的动态规划 */ + 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; + } ``` === "Dart" ```dart title="coin_change.dart" - [class]{}-[func]{coinChangeDPComp} + /* 零钱兑换:空间优化后的动态规划 */ + 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; + } ``` === "Rust" ```rust title="coin_change.rs" - [class]{}-[func]{coin_change_dp_comp} + /* 零钱兑换:空间优化后的动态规划 */ + 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 } + } ``` === "C" @@ -455,7 +1333,32 @@ $$ === "Zig" ```zig title="coin_change.zig" - [class]{}-[func]{coinChangeDPComp} + // 零钱兑换:空间优化后的动态规划 + 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; + } + } ``` ## 14.5.3   零钱兑换问题 II @@ -485,61 +1388,283 @@ $$ === "Python" ```python title="coin_change_ii.py" - [class]{}-[func]{coin_change_ii_dp} + def coin_change_ii_dp(coins: list[int], amt: int) -> int: + """零钱兑换 II:动态规划""" + n = len(coins) + # 初始化 dp 表 + dp = [[0] * (amt + 1) for _ in range(n + 1)] + # 初始化首列 + for i in range(n + 1): + dp[i][0] = 1 + # 状态转移 + for i in range(1, n + 1): + for a in range(1, amt + 1): + 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] ``` === "C++" ```cpp title="coin_change_ii.cpp" - [class]{}-[func]{coinChangeIIDP} + /* 零钱兑换 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]; + } ``` === "Java" ```java title="coin_change_ii.java" - [class]{coin_change_ii}-[func]{coinChangeIIDP} + /* 零钱兑换 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]; + } ``` === "C#" ```csharp title="coin_change_ii.cs" - [class]{coin_change_ii}-[func]{coinChangeIIDP} + /* 零钱兑换 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]; + } ``` === "Go" ```go title="coin_change_ii.go" - [class]{}-[func]{coinChangeIIDP} + /* 零钱兑换 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] + } ``` === "Swift" ```swift title="coin_change_ii.swift" - [class]{}-[func]{coinChangeIIDP} + /* 零钱兑换 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 stride(from: 0, through: n, by: 1) { + dp[i][0] = 1 + } + // 状态转移 + for i in stride(from: 1, through: n, by: 1) { + for a in stride(from: 1, through: amt, by: 1) { + 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] + } ``` === "JS" ```javascript title="coin_change_ii.js" - [class]{}-[func]{coinChangeIIDP} + /* 零钱兑换 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]; + } ``` === "TS" ```typescript title="coin_change_ii.ts" - [class]{}-[func]{coinChangeIIDP} + /* 零钱兑换 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]; + } ``` === "Dart" ```dart title="coin_change_ii.dart" - [class]{}-[func]{coinChangeIIDP} + /* 零钱兑换 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]; + } ``` === "Rust" ```rust title="coin_change_ii.rs" - [class]{}-[func]{coin_change_ii_dp} + /* 零钱兑换 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] + } ``` === "C" @@ -551,7 +1676,29 @@ $$ === "Zig" ```zig title="coin_change_ii.zig" - [class]{}-[func]{coinChangeIIDP} + // 零钱兑换 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]; + } ``` ### 3.   空间优化 @@ -561,61 +1708,249 @@ $$ === "Python" ```python title="coin_change_ii.py" - [class]{}-[func]{coin_change_ii_dp_comp} + def coin_change_ii_dp_comp(coins: list[int], amt: int) -> int: + """零钱兑换 II:空间优化后的动态规划""" + n = len(coins) + # 初始化 dp 表 + dp = [0] * (amt + 1) + dp[0] = 1 + # 状态转移 + for i in range(1, n + 1): + # 正序遍历 + for a in range(1, amt + 1): + if coins[i - 1] > a: + # 若超过背包容量,则不选硬币 i + dp[a] = dp[a] + else: + # 不选和选硬币 i 这两种方案之和 + dp[a] = dp[a] + dp[a - coins[i - 1]] + return dp[amt] ``` === "C++" ```cpp title="coin_change_ii.cpp" - [class]{}-[func]{coinChangeIIDPComp} + /* 零钱兑换 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]; + } ``` === "Java" ```java title="coin_change_ii.java" - [class]{coin_change_ii}-[func]{coinChangeIIDPComp} + /* 零钱兑换 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]; + } ``` === "C#" ```csharp title="coin_change_ii.cs" - [class]{coin_change_ii}-[func]{coinChangeIIDPComp} + /* 零钱兑换 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]; + } ``` === "Go" ```go title="coin_change_ii.go" - [class]{}-[func]{coinChangeIIDPComp} + /* 零钱兑换 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] + } ``` === "Swift" ```swift title="coin_change_ii.swift" - [class]{}-[func]{coinChangeIIDPComp} + /* 零钱兑换 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 stride(from: 1, through: n, by: 1) { + for a in stride(from: 1, through: amt, by: 1) { + if coins[i - 1] > a { + // 若超过背包容量,则不选硬币 i + dp[a] = dp[a] + } else { + // 不选和选硬币 i 这两种方案之和 + dp[a] = dp[a] + dp[a - coins[i - 1]] + } + } + } + return dp[amt] + } ``` === "JS" ```javascript title="coin_change_ii.js" - [class]{}-[func]{coinChangeIIDPComp} + /* 零钱兑换 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]; + } ``` === "TS" ```typescript title="coin_change_ii.ts" - [class]{}-[func]{coinChangeIIDPComp} + /* 零钱兑换 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]; + } ``` === "Dart" ```dart title="coin_change_ii.dart" - [class]{}-[func]{coinChangeIIDPComp} + /* 零钱兑换 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]; + } ``` === "Rust" ```rust title="coin_change_ii.rs" - [class]{}-[func]{coin_change_ii_dp_comp} + /* 零钱兑换 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] + } ``` === "C" @@ -627,5 +1962,24 @@ $$ === "Zig" ```zig title="coin_change_ii.zig" - [class]{}-[func]{coinChangeIIDPComp} + // 零钱兑换 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]; + } ``` diff --git a/zh/chapter_graph/graph_operations.md b/zh/chapter_graph/graph_operations.md index 0156787be..956015e02 100644 --- a/zh/chapter_graph/graph_operations.md +++ b/zh/chapter_graph/graph_operations.md @@ -37,67 +37,1083 @@ comments: true === "Python" ```python title="graph_adjacency_matrix.py" - [class]{GraphAdjMat}-[func]{} + class GraphAdjMat: + """基于邻接矩阵实现的无向图类""" + + # 顶点列表,元素代表“顶点值”,索引代表“顶点索引” + vertices: list[int] = [] + # 邻接矩阵,行列索引对应“顶点索引” + adj_mat: list[list[int]] = [] + + def __init__(self, vertices: list[int], edges: list[list[int]]): + """构造方法""" + self.vertices: list[int] = [] + self.adj_mat: list[list[int]] = [] + # 添加顶点 + for val in vertices: + self.add_vertex(val) + # 添加边 + # 请注意,edges 元素代表顶点索引,即对应 vertices 元素索引 + for e in edges: + self.add_edge(e[0], e[1]) + + def size(self) -> int: + """获取顶点数量""" + return len(self.vertices) + + def add_vertex(self, val: int): + """添加顶点""" + n = self.size() + # 向顶点列表中添加新顶点的值 + self.vertices.append(val) + # 在邻接矩阵中添加一行 + new_row = [0] * n + self.adj_mat.append(new_row) + # 在邻接矩阵中添加一列 + for row in self.adj_mat: + row.append(0) + + def remove_vertex(self, index: int): + """删除顶点""" + if index >= self.size(): + raise IndexError() + # 在顶点列表中移除索引 index 的顶点 + self.vertices.pop(index) + # 在邻接矩阵中删除索引 index 的行 + self.adj_mat.pop(index) + # 在邻接矩阵中删除索引 index 的列 + for row in self.adj_mat: + row.pop(index) + + def add_edge(self, i: int, j: int): + """添加边""" + # 参数 i, j 对应 vertices 元素索引 + # 索引越界与相等处理 + if i < 0 or j < 0 or i >= self.size() or j >= self.size() or i == j: + raise IndexError() + # 在无向图中,邻接矩阵沿主对角线对称,即满足 (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 元素索引 + # 索引越界与相等处理 + 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_matrix(self.adj_mat) ``` === "C++" ```cpp title="graph_adjacency_matrix.cpp" - [class]{GraphAdjMat}-[func]{} + /* 基于邻接矩阵实现的无向图类 */ + 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); + } + }; ``` === "Java" ```java title="graph_adjacency_matrix.java" - [class]{GraphAdjMat}-[func]{} + /* 基于邻接矩阵实现的无向图类 */ + class GraphAdjMat { + List vertices; // 顶点列表,元素代表“顶点值”,索引代表“顶点索引” + List> adjMat; // 邻接矩阵,行列索引对应“顶点索引” + + /* 构造方法 */ + public GraphAdjMat(int[] vertices, int[][] edges) { + this.vertices = new ArrayList<>(); + this.adjMat = new ArrayList<>(); + // 添加顶点 + for (int val : vertices) { + addVertex(val); + } + // 添加边 + // 请注意,edges 元素代表顶点索引,即对应 vertices 元素索引 + for (int[] e : edges) { + addEdge(e[0], e[1]); + } + } + + /* 获取顶点数量 */ + public int size() { + return vertices.size(); + } + + /* 添加顶点 */ + public void addVertex(int val) { + int n = size(); + // 向顶点列表中添加新顶点的值 + vertices.add(val); + // 在邻接矩阵中添加一行 + List newRow = new ArrayList<>(n); + for (int j = 0; j < n; j++) { + newRow.add(0); + } + adjMat.add(newRow); + // 在邻接矩阵中添加一列 + for (List row : adjMat) { + row.add(0); + } + } + + /* 删除顶点 */ + public void removeVertex(int index) { + if (index >= size()) + throw new IndexOutOfBoundsException(); + // 在顶点列表中移除索引 index 的顶点 + vertices.remove(index); + // 在邻接矩阵中删除索引 index 的行 + adjMat.remove(index); + // 在邻接矩阵中删除索引 index 的列 + for (List row : adjMat) { + row.remove(index); + } + } + + /* 添加边 */ + // 参数 i, j 对应 vertices 元素索引 + public void addEdge(int i, int j) { + // 索引越界与相等处理 + if (i < 0 || j < 0 || i >= size() || j >= size() || i == j) + throw new IndexOutOfBoundsException(); + // 在无向图中,邻接矩阵沿主对角线对称,即满足 (i, j) == (j, i) + adjMat.get(i).set(j, 1); + adjMat.get(j).set(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 IndexOutOfBoundsException(); + adjMat.get(i).set(j, 0); + adjMat.get(j).set(i, 0); + } + + /* 打印邻接矩阵 */ + public void print() { + System.out.print("顶点列表 = "); + System.out.println(vertices); + System.out.println("邻接矩阵 ="); + PrintUtil.printMatrix(adjMat); + } + } ``` === "C#" ```csharp title="graph_adjacency_matrix.cs" - [class]{GraphAdjMat}-[func]{} + /* 基于邻接矩阵实现的无向图类 */ + class GraphAdjMat { + List vertices; // 顶点列表,元素代表“顶点值”,索引代表“顶点索引” + List> adjMat; // 邻接矩阵,行列索引对应“顶点索引” + + /* 构造函数 */ + public GraphAdjMat(int[] vertices, int[][] edges) { + this.vertices = new List(); + this.adjMat = new List>(); + // 添加顶点 + foreach (int val in vertices) { + addVertex(val); + } + // 添加边 + // 请注意,edges 元素代表顶点索引,即对应 vertices 元素索引 + foreach (int[] e in edges) { + addEdge(e[0], e[1]); + } + } + + /* 获取顶点数量 */ + public int size() { + return vertices.Count; + } + + /* 添加顶点 */ + public void addVertex(int val) { + int n = size(); + // 向顶点列表中添加新顶点的值 + vertices.Add(val); + // 在邻接矩阵中添加一行 + List newRow = new List(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); + } + } ``` === "Go" ```go title="graph_adjacency_matrix.go" - [class]{graphAdjMat}-[func]{} + /* 基于邻接矩阵实现的无向图类 */ + 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]) + } + } ``` === "Swift" ```swift title="graph_adjacency_matrix.swift" - [class]{GraphAdjMat}-[func]{} + /* 基于邻接矩阵实现的无向图类 */ + 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) + } + } ``` === "JS" ```javascript title="graph_adjacency_matrix.js" - [class]{GraphAdjMat}-[func]{} + /* 基于邻接矩阵实现的无向图类 */ + 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); + } + } ``` === "TS" ```typescript title="graph_adjacency_matrix.ts" - [class]{GraphAdjMat}-[func]{} + /* 基于邻接矩阵实现的无向图类 */ + 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); + } + } ``` === "Dart" ```dart title="graph_adjacency_matrix.dart" - [class]{GraphAdjMat}-[func]{} + /* 基于邻接矩阵实现的无向图类 */ + 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); + } + } ``` === "Rust" ```rust title="graph_adjacency_matrix.rs" - [class]{GraphAdjMat}-[func]{} + /* 基于邻接矩阵实现的无向图类型 */ + 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!("]") + } + } ``` === "C" ```c title="graph_adjacency_matrix.c" - [class]{graphAdjMat}-[func]{} + /* 基于邻接矩阵实现的无向图类结构 */ + struct graphAdjMat { + int *vertices; // 顶点列表 + unsigned int **adjMat; // 邻接矩阵,元素代表“边”,索引代表“顶点索引” + unsigned int size; // 顶点数量 + unsigned int capacity; // 图容量 + }; + + typedef struct graphAdjMat graphAdjMat; + + /* 添加边 */ + // 参数 i, j 对应 vertices 元素索引 + void addEdge(graphAdjMat *t, int i, int j) { + // 越界检查 + if (i < 0 || j < 0 || i >= t->size || j >= t->size || i == j) { + printf("Out of range in %s:%d\n", __FILE__, __LINE__); + exit(1); + } + // 添加边 + // 参数 i, j 对应 vertices 元素索引 + t->adjMat[i][j] = 1; + t->adjMat[j][i] = 1; + } + + /* 删除边 */ + // 参数 i, j 对应 vertices 元素索引 + void removeEdge(graphAdjMat *t, int i, int j) { + // 越界检查 + if (i < 0 || j < 0 || i >= t->size || j >= t->size || i == j) { + printf("Out of range in %s:%d\n", __FILE__, __LINE__); + exit(1); + } + // 删除边 + // 参数 i, j 对应 vertices 元素索引 + t->adjMat[i][j] = 0; + t->adjMat[j][i] = 0; + } + + /* 添加顶点 */ + void addVertex(graphAdjMat *t, int val) { + // 如果实际使用不大于预设空间,则直接初始化新空间 + if (t->size < t->capacity) { + t->vertices[t->size] = val; // 初始化新顶点值 + for (int i = 0; i < t->size; i++) { + t->adjMat[i][t->size] = 0; // 邻接矩新列阵置0 + } + memset(t->adjMat[t->size], 0, sizeof(unsigned int) * (t->size + 1)); // 将新增行置 0 + t->size++; + return; + } + + // 扩容,申请新的顶点数组 + int *temp = (int *)malloc(sizeof(int) * (t->size * 2)); + memcpy(temp, t->vertices, sizeof(int) * t->size); + temp[t->size] = val; + + // 释放原数组 + free(t->vertices); + t->vertices = temp; + + // 扩容,申请新的二维数组 + unsigned int **tempMat = (unsigned int **)malloc(sizeof(unsigned int *) * t->size * 2); + unsigned int *tempMatLine = (unsigned int *)malloc(sizeof(unsigned int) * (t->size * 2) * (t->size * 2)); + memset(tempMatLine, 0, sizeof(unsigned int) * (t->size * 2) * (t->size * 2)); + for (int k = 0; k < t->size * 2; k++) { + tempMat[k] = tempMatLine + k * (t->size * 2); + } + + for (int i = 0; i < t->size; i++) { + memcpy(tempMat[i], t->adjMat[i], sizeof(unsigned int) * t->size); // 原数据复制到新数组 + } + + for (int i = 0; i < t->size; i++) { + tempMat[i][t->size] = 0; // 将新增列置 0 + } + memset(tempMat[t->size], 0, sizeof(unsigned int) * (t->size + 1)); // 将新增行置 0 + + // 释放原数组 + free(t->adjMat[0]); + free(t->adjMat); + + // 扩容后,指向新地址 + t->adjMat = tempMat; // 指向新的邻接矩阵地址 + t->capacity = t->size * 2; + t->size++; + } + + /* 删除顶点 */ + void removeVertex(graphAdjMat *t, unsigned int index) { + // 越界检查 + if (index < 0 || index >= t->size) { + printf("Out of range in %s:%d\n", __FILE__, __LINE__); + exit(1); + } + for (int i = index; i < t->size - 1; i++) { + t->vertices[i] = t->vertices[i + 1]; // 清除删除的顶点,并将其后所有顶点前移 + } + t->vertices[t->size - 1] = 0; // 将被前移的最后一个顶点置 0 + + // 清除邻接矩阵中删除的列 + for (int i = 0; i < t->size - 1; i++) { + if (i < index) { + for (int j = index; j < t->size - 1; j++) { + t->adjMat[i][j] = t->adjMat[i][j + 1]; // 被删除列后的所有列前移 + } + } else { + memcpy(t->adjMat[i], t->adjMat[i + 1], sizeof(unsigned int) * t->size); // 被删除行的下方所有行上移 + for (int j = index; j < t->size; j++) { + t->adjMat[i][j] = t->adjMat[i][j + 1]; // 被删除列后的所有列前移 + } + } + } + t->size--; + } + + /* 打印顶点与邻接矩阵 */ + void printGraph(graphAdjMat *t) { + if (t->size == 0) { + printf("graph is empty\n"); + return; + } + printf("顶点列表 = ["); + for (int i = 0; i < t->size; i++) { + if (i != t->size - 1) { + printf("%d, ", t->vertices[i]); + } else { + printf("%d", t->vertices[i]); + } + } + printf("]\n"); + printf("邻接矩阵 =\n[\n"); + for (int i = 0; i < t->size; i++) { + printf(" ["); + for (int j = 0; j < t->size; j++) { + if (j != t->size - 1) { + printf("%u, ", t->adjMat[i][j]); + } else { + printf("%u", t->adjMat[i][j]); + } + } + printf("],\n"); + } + printf("]\n"); + } + + /* 构造函数 */ + graphAdjMat *newGraphAjdMat(unsigned int numberVertices, int *vertices, unsigned int **adjMat) { + // 申请内存 + graphAdjMat *newGraph = (graphAdjMat *)malloc(sizeof(graphAdjMat)); // 为图分配内存 + newGraph->vertices = (int *)malloc(sizeof(int) * numberVertices * 2); // 为顶点列表分配内存 + newGraph->adjMat = (unsigned int **)malloc(sizeof(unsigned int *) * numberVertices * 2); // 为邻接矩阵分配二维内存 + unsigned int *temp = (unsigned int *)malloc(sizeof(unsigned int) * numberVertices * 2 * numberVertices * 2); // 为邻接矩阵分配一维内存 + newGraph->size = numberVertices; // 初始化顶点数量 + newGraph->capacity = numberVertices * 2; // 初始化图容量 + + // 配置二维数组 + for (int i = 0; i < numberVertices * 2; i++) { + newGraph->adjMat[i] = temp + i * numberVertices * 2; // 将二维指针指向一维数组 + } + + // 赋值 + memcpy(newGraph->vertices, vertices, sizeof(int) * numberVertices); + for (int i = 0; i < numberVertices; i++) { + memcpy(newGraph->adjMat[i], adjMat[i], sizeof(unsigned int) * numberVertices); // 将传入的邻接矩阵赋值给结构体内邻接矩阵 + } + + // 返回结构体指针 + return newGraph; + } ``` === "Zig" @@ -142,67 +1158,950 @@ comments: true === "Python" ```python title="graph_adjacency_list.py" - [class]{GraphAdjList}-[func]{} + class GraphAdjList: + """基于邻接表实现的无向图类""" + + def __init__(self, edges: list[list[Vertex]]): + """构造方法""" + # 邻接表,key: 顶点,value:该顶点的所有邻接顶点 + self.adj_list = dict[Vertex, list[Vertex]]() + # 添加所有顶点和边 + 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: + """获取顶点数量""" + return len(self.adj_list) + + def add_edge(self, vet1: Vertex, vet2: Vertex): + """添加边""" + if vet1 not in self.adj_list or vet2 not in self.adj_list or vet1 == vet2: + raise ValueError() + # 添加边 vet1 - vet2 + self.adj_list[vet1].append(vet2) + self.adj_list[vet2].append(vet1) + + def remove_edge(self, vet1: Vertex, vet2: Vertex): + """删除边""" + if vet1 not in self.adj_list or vet2 not in self.adj_list or vet1 == vet2: + raise ValueError() + # 删除边 vet1 - vet2 + self.adj_list[vet1].remove(vet2) + self.adj_list[vet2].remove(vet1) + + def add_vertex(self, vet: Vertex): + """添加顶点""" + if vet in self.adj_list: + return + # 在邻接表中添加一个新链表 + self.adj_list[vet] = [] + + def remove_vertex(self, vet: Vertex): + """删除顶点""" + if vet not in self.adj_list: + raise ValueError() + # 在邻接表中删除顶点 vet 对应的链表 + self.adj_list.pop(vet) + # 遍历其他顶点的链表,删除所有包含 vet 的边 + for vertex in self.adj_list: + if vet in self.adj_list[vertex]: + self.adj_list[vertex].remove(vet) + + def print(self): + """打印邻接表""" + print("邻接表 =") + for vertex in self.adj_list: + tmp = [v.val for v in self.adj_list[vertex]] + print(f"{vertex.val}: {tmp},") ``` === "C++" ```cpp title="graph_adjacency_list.cpp" - [class]{GraphAdjList}-[func]{} + /* 基于邻接表实现的无向图类 */ + 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)); + } + } + }; ``` === "Java" ```java title="graph_adjacency_list.java" - [class]{GraphAdjList}-[func]{} + /* 基于邻接表实现的无向图类 */ + class GraphAdjList { + // 邻接表,key: 顶点,value:该顶点的所有邻接顶点 + Map> adjList; + + /* 构造方法 */ + public GraphAdjList(Vertex[][] edges) { + this.adjList = new HashMap<>(); + // 添加所有顶点和边 + for (Vertex[] edge : edges) { + addVertex(edge[0]); + addVertex(edge[1]); + addEdge(edge[0], edge[1]); + } + } + + /* 获取顶点数量 */ + public int size() { + return adjList.size(); + } + + /* 添加边 */ + public void addEdge(Vertex vet1, Vertex vet2) { + if (!adjList.containsKey(vet1) || !adjList.containsKey(vet2) || vet1 == vet2) + throw new IllegalArgumentException(); + // 添加边 vet1 - vet2 + adjList.get(vet1).add(vet2); + adjList.get(vet2).add(vet1); + } + + /* 删除边 */ + public void removeEdge(Vertex vet1, Vertex vet2) { + if (!adjList.containsKey(vet1) || !adjList.containsKey(vet2) || vet1 == vet2) + throw new IllegalArgumentException(); + // 删除边 vet1 - vet2 + adjList.get(vet1).remove(vet2); + adjList.get(vet2).remove(vet1); + } + + /* 添加顶点 */ + public void addVertex(Vertex vet) { + if (adjList.containsKey(vet)) + return; + // 在邻接表中添加一个新链表 + adjList.put(vet, new ArrayList<>()); + } + + /* 删除顶点 */ + public void removeVertex(Vertex vet) { + if (!adjList.containsKey(vet)) + throw new IllegalArgumentException(); + // 在邻接表中删除顶点 vet 对应的链表 + adjList.remove(vet); + // 遍历其他顶点的链表,删除所有包含 vet 的边 + for (List list : adjList.values()) { + list.remove(vet); + } + } + + /* 打印邻接表 */ + public void print() { + System.out.println("邻接表 ="); + for (Map.Entry> pair : adjList.entrySet()) { + List tmp = new ArrayList<>(); + for (Vertex vertex : pair.getValue()) + tmp.add(vertex.val); + System.out.println(pair.getKey().val + ": " + tmp + ","); + } + } + } ``` === "C#" ```csharp title="graph_adjacency_list.cs" - [class]{GraphAdjList}-[func]{} + /* 基于邻接表实现的无向图类 */ + class GraphAdjList { + // 邻接表,key: 顶点,value:该顶点的所有邻接顶点 + public Dictionary> adjList; + + /* 构造函数 */ + public GraphAdjList(Vertex[][] edges) { + this.adjList = new Dictionary>(); + // 添加所有顶点和边 + foreach (Vertex[] edge in edges) { + addVertex(edge[0]); + addVertex(edge[1]); + addEdge(edge[0], edge[1]); + } + } + + /* 获取顶点数量 */ + public 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, new List()); + } + + /* 删除顶点 */ + 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 = new List(); + foreach (Vertex vertex in pair.Value) + tmp.Add(vertex.val); + Console.WriteLine(pair.Key.val + ": [" + string.Join(", ", tmp) + "],"); + } + } + } ``` === "Go" ```go title="graph_adjacency_list.go" - [class]{graphAdjList}-[func]{} + /* 基于邻接表实现的无向图类 */ + 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() + } + } ``` === "Swift" ```swift title="graph_adjacency_list.swift" - [class]{GraphAdjList}-[func]{} + /* 基于邻接表实现的无向图类 */ + 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(where: { $0 == vet2 }) + adjList[vet2]?.removeAll(where: { $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(where: { $0 == vet }) + } + } + + /* 打印邻接表 */ + public func print() { + Swift.print("邻接表 =") + for pair in adjList { + var tmp: [Int] = [] + for vertex in pair.value { + tmp.append(vertex.val) + } + Swift.print("\(pair.key.val): \(tmp),") + } + } + } ``` === "JS" ```javascript title="graph_adjacency_list.js" - [class]{GraphAdjList}-[func]{} + /* 基于邻接表实现的无向图类 */ + 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()); + } + } + } ``` === "TS" ```typescript title="graph_adjacency_list.ts" - [class]{GraphAdjList}-[func]{} + /* 基于邻接表实现的无向图类 */ + 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()); + } + } + } ``` === "Dart" ```dart title="graph_adjacency_list.dart" - [class]{GraphAdjList}-[func]{} + /* 基于邻接表实现的无向图类 */ + 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,"); + }); + } + } ``` === "Rust" ```rust title="graph_adjacency_list.rs" - [class]{GraphAdjList}-[func]{} + /* 基于邻接表实现的无向图类型 */ + 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); + } + } + } ``` === "C" ```c title="graph_adjacency_list.c" - [class]{graphAdjList}-[func]{} + /* 基于邻接链表实现的无向图类结构 */ + struct graphAdjList { + Vertex **verticesList; // 邻接表 + unsigned int size; // 顶点数量 + unsigned int capacity; // 顶点容量 + }; + + typedef struct graphAdjList graphAdjList; + + /* 添加边 */ + void addEdge(graphAdjList *t, int i, int j) { + // 越界检查 + if (i < 0 || j < 0 || i == j || i >= t->size || j >= t->size) { + printf("Out of range in %s:%d\n", __FILE__, __LINE__); + return; + } + // 查找欲添加边的顶点 vet1 - vet2 + Vertex *vet1 = t->verticesList[i]; + Vertex *vet2 = t->verticesList[j]; + + // 连接顶点 vet1 - vet2 + pushBack(vet1->linked, vet2); + pushBack(vet2->linked, vet1); + } + + /* 删除边 */ + void removeEdge(graphAdjList *t, int i, int j) { + // 越界检查 + if (i < 0 || j < 0 || i == j || i >= t->size || j >= t->size) { + printf("Out of range in %s:%d\n", __FILE__, __LINE__); + return; + } + + // 查找欲删除边的顶点 vet1 - vet2 + Vertex *vet1 = t->verticesList[i]; + Vertex *vet2 = t->verticesList[j]; + + // 移除待删除边 vet1 - vet2 + removeLink(vet1->linked, vet2); + removeLink(vet2->linked, vet1); + } + + /* 添加顶点 */ + void addVertex(graphAdjList *t, int val) { + // 若大小超过容量,则扩容 + if (t->size >= t->capacity) { + Vertex **tempList = (Vertex **)malloc(sizeof(Vertex *) * 2 * t->capacity); + memcpy(tempList, t->verticesList, sizeof(Vertex *) * t->size); + free(t->verticesList); // 释放原邻接表内存 + t->verticesList = tempList; // 指向新邻接表 + t->capacity = t->capacity * 2; // 容量扩大至2倍 + } + // 申请新顶点内存并将新顶点地址存入顶点列表 + Vertex *newV = newVertex(val); // 建立新顶点 + newV->pos = t->size; // 为新顶点标记下标 + newV->linked = newLinklist(newV); // 为新顶点建立链表 + t->verticesList[t->size] = newV; // 将新顶点加入邻接表 + t->size++; + } + + /* 删除顶点 */ + void removeVertex(graphAdjList *t, unsigned int index) { + // 越界检查 + if (index < 0 || index >= t->size) { + printf("Out of range in %s:%d\n", __FILE__, __LINE__); + exit(1); + } + + Vertex *vet = t->verticesList[index]; // 查找待删节点 + if (vet == 0) { // 若不存在该节点,则返回 + printf("index is:%d\n", index); + printf("Out of range in %s:%d\n", __FILE__, __LINE__); + return; + } + + // 遍历待删除顶点的链表,将所有与待删除结点有关的边删除 + Node *temp = vet->linked->head->next; + while (temp != 0) { + removeLink(temp->val->linked, vet); // 删除与该顶点有关的边 + temp = temp->next; + } + + // 将顶点前移 + for (int i = index; i < t->size - 1; i++) { + t->verticesList[i] = t->verticesList[i + 1]; // 顶点前移 + t->verticesList[i]->pos--; // 所有前移的顶点索引值减1 + } + t->verticesList[t->size - 1] = 0; // 将被删除顶点的位置置 0 + t->size--; + + // 释放内存 + freeVertex(vet); + } + + /* 打印顶点与邻接矩阵 */ + void printGraph(graphAdjList *t) { + printf("邻接表 =\n"); + for (int i = 0; i < t->size; i++) { + Node *n = t->verticesList[i]->linked->head->next; + printf("%d: [", t->verticesList[i]->val); + while (n != 0) { + if (n->next != 0) { + printf("%d, ", n->val->val); + } else { + printf("%d", n->val->val); + } + n = n->next; + } + printf("]\n"); + } + } + + /* 构造函数 */ + graphAdjList *newGraphAdjList(unsigned int verticesCapacity) { + // 申请内存 + graphAdjList *newGraph = (graphAdjList *)malloc(sizeof(graphAdjList)); + // 建立顶点表并分配内存 + newGraph->verticesList = (Vertex **)malloc(sizeof(Vertex *) * verticesCapacity); // 为顶点列表分配内存 + memset(newGraph->verticesList, 0, sizeof(Vertex *) * verticesCapacity); // 顶点列表置 0 + newGraph->size = 0; // 初始化顶点数量 + newGraph->capacity = verticesCapacity; // 初始化顶点容量 + // 返回图指针 + return newGraph; + } ``` === "Zig" diff --git a/zh/chapter_graph/graph_traversal.md b/zh/chapter_graph/graph_traversal.md index e6e714f3c..0d30b114d 100644 --- a/zh/chapter_graph/graph_traversal.md +++ b/zh/chapter_graph/graph_traversal.md @@ -29,67 +29,357 @@ BFS 通常借助队列来实现。队列具有“先入先出”的性质,这 === "Python" ```python title="graph_bfs.py" - [class]{}-[func]{graph_bfs} + def graph_bfs(graph: GraphAdjList, start_vet: Vertex) -> list[Vertex]: + """广度优先遍历 BFS""" + # 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 + # 顶点遍历序列 + res = [] + # 哈希表,用于记录已被访问过的顶点 + visited = set[Vertex]([start_vet]) + # 队列用于实现 BFS + que = deque[Vertex]([start_vet]) + # 以顶点 vet 为起点,循环直至访问完所有顶点 + while len(que) > 0: + vet = que.popleft() # 队首顶点出队 + res.append(vet) # 记录访问顶点 + # 遍历该顶点的所有邻接顶点 + for adj_vet in graph.adj_list[vet]: + if adj_vet in visited: + continue # 跳过已被访问过的顶点 + que.append(adj_vet) # 只入队未访问的顶点 + visited.add(adj_vet) # 标记该顶点已被访问 + # 返回顶点遍历序列 + return res ``` === "C++" ```cpp title="graph_bfs.cpp" - [class]{}-[func]{graphBFS} + /* 广度优先遍历 BFS */ + // 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 + 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; + } ``` === "Java" ```java title="graph_bfs.java" - [class]{graph_bfs}-[func]{graphBFS} + /* 广度优先遍历 BFS */ + // 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 + List graphBFS(GraphAdjList graph, Vertex startVet) { + // 顶点遍历序列 + List res = new ArrayList<>(); + // 哈希表,用于记录已被访问过的顶点 + Set visited = new HashSet<>(); + visited.add(startVet); + // 队列用于实现 BFS + Queue que = new LinkedList<>(); + que.offer(startVet); + // 以顶点 vet 为起点,循环直至访问完所有顶点 + while (!que.isEmpty()) { + Vertex vet = que.poll(); // 队首顶点出队 + res.add(vet); // 记录访问顶点 + // 遍历该顶点的所有邻接顶点 + for (Vertex adjVet : graph.adjList.get(vet)) { + if (visited.contains(adjVet)) + continue; // 跳过已被访问过的顶点 + que.offer(adjVet); // 只入队未访问的顶点 + visited.add(adjVet); // 标记该顶点已被访问 + } + } + // 返回顶点遍历序列 + return res; + } ``` === "C#" ```csharp title="graph_bfs.cs" - [class]{graph_bfs}-[func]{graphBFS} + /* 广度优先遍历 BFS */ + // 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 + List graphBFS(GraphAdjList graph, Vertex startVet) { + // 顶点遍历序列 + List res = new List(); + // 哈希表,用于记录已被访问过的顶点 + HashSet visited = new HashSet() { startVet }; + // 队列用于实现 BFS + Queue que = new Queue(); + 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; + } ``` === "Go" ```go title="graph_bfs.go" - [class]{}-[func]{graphBFS} + /* 广度优先遍历 BFS */ + // 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 + 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 + } ``` === "Swift" ```swift title="graph_bfs.swift" - [class]{}-[func]{graphBFS} + /* 广度优先遍历 BFS */ + // 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 + 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 + } ``` === "JS" ```javascript title="graph_bfs.js" - [class]{}-[func]{graphBFS} + /* 广度优先遍历 BFS */ + // 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 + 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; + } ``` === "TS" ```typescript title="graph_bfs.ts" - [class]{}-[func]{graphBFS} + /* 广度优先遍历 BFS */ + // 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 + 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; + } ``` === "Dart" ```dart title="graph_bfs.dart" - [class]{}-[func]{graphBFS} + /* 广度优先遍历 BFS */ + 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; + } ``` === "Rust" ```rust title="graph_bfs.rs" - [class]{}-[func]{graph_bfs} + /* 广度优先遍历 BFS */ + // 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 + 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 + } ``` === "C" ```c title="graph_bfs.c" - [class]{}-[func]{graphBFS} + /* 广度优先遍历 */ + // 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 + Vertex **graphBFS(graphAdjList *t, Vertex *startVet) { + // 顶点遍历序列 + Vertex **res = (Vertex **)malloc(sizeof(Vertex *) * t->size); + memset(res, 0, sizeof(Vertex *) * t->size); + // 队列用于实现 BFS + queue *que = newQueue(t->size); + // 哈希表,用于记录已被访问过的顶点 + hashTable *visited = newHash(t->size); + int resIndex = 0; + queuePush(que, startVet); // 将第一个元素入队 + hashMark(visited, startVet->pos); // 标记第一个入队的顶点 + // 以顶点 vet 为起点,循环直至访问完所有顶点 + while (que->head < que->tail) { + // 遍历该顶点的边链表,将所有与该顶点有连接的,并且未被标记的顶点入队 + Node *n = queueTop(que)->linked->head->next; + while (n != 0) { + // 查询哈希表,若该索引的顶点已入队,则跳过,否则入队并标记 + if (hashQuery(visited, n->val->pos) == 1) { + n = n->next; + continue; // 跳过已被访问过的顶点 + } + queuePush(que, n->val); // 只入队未访问的顶点 + hashMark(visited, n->val->pos); // 标记该顶点已被访问 + } + // 队首元素存入数组 + res[resIndex] = queueTop(que); // 队首顶点加入顶点遍历序列 + resIndex++; + queuePop(que); // 队首元素出队 + } + // 释放内存 + freeQueue(que); + freeHash(visited); + resIndex = 0; + // 返回顶点遍历序列 + return res; + } ``` === "Zig" @@ -160,89 +450,339 @@ BFS 通常借助队列来实现。队列具有“先入先出”的性质,这 === "Python" ```python title="graph_dfs.py" - [class]{}-[func]{dfs} + def dfs(graph: GraphAdjList, visited: set[Vertex], res: list[Vertex], vet: Vertex): + """深度优先遍历 DFS 辅助函数""" + res.append(vet) # 记录访问顶点 + visited.add(vet) # 标记该顶点已被访问 + # 遍历该顶点的所有邻接顶点 + for adjVet in graph.adj_list[vet]: + if adjVet in visited: + continue # 跳过已被访问过的顶点 + # 递归访问邻接顶点 + dfs(graph, visited, res, adjVet) - [class]{}-[func]{graph_dfs} + def graph_dfs(graph: GraphAdjList, start_vet: Vertex) -> list[Vertex]: + """深度优先遍历 DFS""" + # 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 + # 顶点遍历序列 + res = [] + # 哈希表,用于记录已被访问过的顶点 + visited = set[Vertex]() + dfs(graph, visited, res, start_vet) + return res ``` === "C++" ```cpp title="graph_dfs.cpp" - [class]{}-[func]{dfs} + /* 深度优先遍历 DFS 辅助函数 */ + 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]{graphDFS} + /* 深度优先遍历 DFS */ + // 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 + vector graphDFS(GraphAdjList &graph, Vertex *startVet) { + // 顶点遍历序列 + vector res; + // 哈希表,用于记录已被访问过的顶点 + unordered_set visited; + dfs(graph, visited, res, startVet); + return res; + } ``` === "Java" ```java title="graph_dfs.java" - [class]{graph_dfs}-[func]{dfs} + /* 深度优先遍历 DFS 辅助函数 */ + void dfs(GraphAdjList graph, Set visited, List res, Vertex vet) { + res.add(vet); // 记录访问顶点 + visited.add(vet); // 标记该顶点已被访问 + // 遍历该顶点的所有邻接顶点 + for (Vertex adjVet : graph.adjList.get(vet)) { + if (visited.contains(adjVet)) + continue; // 跳过已被访问过的顶点 + // 递归访问邻接顶点 + dfs(graph, visited, res, adjVet); + } + } - [class]{graph_dfs}-[func]{graphDFS} + /* 深度优先遍历 DFS */ + // 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 + List graphDFS(GraphAdjList graph, Vertex startVet) { + // 顶点遍历序列 + List res = new ArrayList<>(); + // 哈希表,用于记录已被访问过的顶点 + Set visited = new HashSet<>(); + dfs(graph, visited, res, startVet); + return res; + } ``` === "C#" ```csharp title="graph_dfs.cs" - [class]{graph_dfs}-[func]{dfs} + /* 深度优先遍历 DFS 辅助函数 */ + 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]{graphDFS} + /* 深度优先遍历 DFS */ + // 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 + List graphDFS(GraphAdjList graph, Vertex startVet) { + // 顶点遍历序列 + List res = new List(); + // 哈希表,用于记录已被访问过的顶点 + HashSet visited = new HashSet(); + dfs(graph, visited, res, startVet); + return res; + } ``` === "Go" ```go title="graph_dfs.go" - [class]{}-[func]{dfs} + /* 深度优先遍历 DFS 辅助函数 */ + 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]{graphDFS} + /* 深度优先遍历 DFS */ + // 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 + func graphDFS(g *graphAdjList, startVet Vertex) []Vertex { + // 顶点遍历序列 + res := make([]Vertex, 0) + // 哈希表,用于记录已被访问过的顶点 + visited := make(map[Vertex]struct{}) + dfs(g, visited, &res, startVet) + // 返回顶点遍历序列 + return res + } ``` === "Swift" ```swift title="graph_dfs.swift" - [class]{}-[func]{dfs} + /* 深度优先遍历 DFS 辅助函数 */ + 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]{graphDFS} + /* 深度优先遍历 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 + } ``` === "JS" ```javascript title="graph_dfs.js" - [class]{}-[func]{dfs} + /* 深度优先遍历 DFS */ + // 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 + 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]{graphDFS} + /* 深度优先遍历 DFS */ + // 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 + function graphDFS(graph, startVet) { + // 顶点遍历序列 + const res = []; + // 哈希表,用于记录已被访问过的顶点 + const visited = new Set(); + dfs(graph, visited, res, startVet); + return res; + } ``` === "TS" ```typescript title="graph_dfs.ts" - [class]{}-[func]{dfs} + /* 深度优先遍历 DFS 辅助函数 */ + 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]{graphDFS} + /* 深度优先遍历 DFS */ + // 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 + function graphDFS(graph: GraphAdjList, startVet: Vertex): Vertex[] { + // 顶点遍历序列 + const res: Vertex[] = []; + // 哈希表,用于记录已被访问过的顶点 + const visited: Set = new Set(); + dfs(graph, visited, res, startVet); + return res; + } ``` === "Dart" ```dart title="graph_dfs.dart" - [class]{}-[func]{dfs} + /* 深度优先遍历 DFS 辅助函数 */ + 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]{graphDFS} + /* 深度优先遍历 DFS */ + List graphDFS(GraphAdjList graph, Vertex startVet) { + // 顶点遍历序列 + List res = []; + // 哈希表,用于记录已被访问过的顶点 + Set visited = {}; + dfs(graph, visited, res, startVet); + return res; + } ``` === "Rust" ```rust title="graph_dfs.rs" - [class]{}-[func]{dfs} + /* 深度优先遍历 DFS 辅助函数 */ + 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]{graph_dfs} + /* 深度优先遍历 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 + } ``` === "C" ```c title="graph_dfs.c" - [class]{}-[func]{dfs} + /* 深度优先遍历 DFS 辅助函数 */ + int resIndex = 0; + void dfs(graphAdjList *graph, hashTable *visited, Vertex *vet, Vertex **res) { + if (hashQuery(visited, vet->pos) == 1) { + return; // 跳过已被访问过的顶点 + } + hashMark(visited, vet->pos); // 标记顶点并将顶点存入数组 + res[resIndex] = vet; // 将顶点存入数组 + resIndex++; + // 遍历该顶点链表 + Node *n = vet->linked->head->next; + while (n != 0) { + // 递归访问邻接顶点 + dfs(graph, visited, n->val, res); + n = n->next; + } + return; + } - [class]{}-[func]{graphDFS} + /* 深度优先遍历 DFS */ + // 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 + Vertex **graphDFS(graphAdjList *graph, Vertex *startVet) { + // 顶点遍历序列 + Vertex **res = (Vertex **)malloc(sizeof(Vertex *) * graph->size); + memset(res, 0, sizeof(Vertex *) * graph->size); + // 哈希表,用于记录已被访问过的顶点 + hashTable *visited = newHash(graph->size); + dfs(graph, visited, startVet, res); + // 释放哈希表内存并将数组索引归零 + freeHash(visited); + resIndex = 0; + // 返回遍历数组 + return res; + } ``` === "Zig" diff --git a/zh/chapter_greedy/fractional_knapsack_problem.md b/zh/chapter_greedy/fractional_knapsack_problem.md index 3753bf633..fadcaf2a3 100644 --- a/zh/chapter_greedy/fractional_knapsack_problem.md +++ b/zh/chapter_greedy/fractional_knapsack_problem.md @@ -42,89 +42,425 @@ comments: true === "Python" ```python title="fractional_knapsack.py" - [class]{Item}-[func]{} + class Item: + """物品""" - [class]{}-[func]{fractional_knapsack} + def __init__(self, w: int, v: int): + self.w = w # 物品重量 + self.v = v # 物品价值 + + def fractional_knapsack(wgt: list[int], val: list[int], cap: int) -> int: + """分数背包:贪心""" + # 创建物品列表,包含两个属性:重量、价值 + items = [Item(w, v) for w, v in zip(wgt, val)] + # 按照单位价值 item.v / item.w 从高到低进行排序 + items.sort(key=lambda item: item.v / item.w, reverse=True) + # 循环贪心选择 + res = 0 + for item in items: + if item.w <= cap: + # 若剩余容量充足,则将当前物品整个装进背包 + res += item.v + cap -= item.w + else: + # 若剩余容量不足,则将当前物品的一部分装进背包 + res += (item.v / item.w) * cap + # 已无剩余容量,因此跳出循环 + break + return res ``` === "C++" ```cpp title="fractional_knapsack.cpp" - [class]{Item}-[func]{} + /* 物品 */ + class Item { + public: + int w; // 物品重量 + int v; // 物品价值 - [class]{}-[func]{fractionalKnapsack} + 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; + } ``` === "Java" ```java title="fractional_knapsack.java" - [class]{Item}-[func]{} + /* 物品 */ + class Item { + int w; // 物品重量 + int v; // 物品价值 - [class]{fractional_knapsack}-[func]{fractionalKnapsack} + public Item(int w, int v) { + this.w = w; + this.v = v; + } + } + + /* 分数背包:贪心 */ + 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 从高到低进行排序 + Arrays.sort(items, Comparator.comparingDouble(item -> -((double) item.v / item.w))); + // 循环贪心选择 + double res = 0; + for (Item item : items) { + if (item.w <= cap) { + // 若剩余容量充足,则将当前物品整个装进背包 + res += item.v; + cap -= item.w; + } else { + // 若剩余容量不足,则将当前物品的一部分装进背包 + res += (double) item.v / item.w * cap; + // 已无剩余容量,因此跳出循环 + break; + } + } + return res; + } ``` === "C#" ```csharp title="fractional_knapsack.cs" - [class]{Item}-[func]{} + /* 物品 */ + class Item { + public int w; // 物品重量 + public int v; // 物品价值 - [class]{fractional_knapsack}-[func]{fractionalKnapsack} + public Item(int w, int v) { + this.w = w; + this.v = v; + } + } + + /* 分数背包:贪心 */ + 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; + } ``` === "Go" ```go title="fractional_knapsack.go" - [class]{Item}-[func]{} + /* 物品 */ + type Item struct { + w int // 物品重量 + v int // 物品价值 + } - [class]{}-[func]{fractionalKnapsack} + /* 分数背包:贪心 */ + 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 + } ``` === "Swift" ```swift title="fractional_knapsack.swift" - [class]{Item}-[func]{} + /* 物品 */ + class Item { + var w: Int // 物品重量 + var v: Int // 物品价值 - [class]{}-[func]{fractionalKnapsack} + 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(by: { -(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 + } ``` === "JS" ```javascript title="fractional_knapsack.js" - [class]{Item}-[func]{} + /* 物品 */ + class Item { + constructor(w, v) { + this.w = w; // 物品重量 + this.v = v; // 物品价值 + } + } - [class]{}-[func]{fractionalKnapsack} + /* 分数背包:贪心 */ + 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; + } ``` === "TS" ```typescript title="fractional_knapsack.ts" - [class]{Item}-[func]{} + /* 物品 */ + class Item { + w: number; // 物品重量 + v: number; // 物品价值 - [class]{}-[func]{fractionalKnapsack} + 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; + } ``` === "Dart" ```dart title="fractional_knapsack.dart" - [class]{Item}-[func]{} + /* 物品 */ + class Item { + int w; // 物品重量 + int v; // 物品价值 - [class]{}-[func]{fractionalKnapsack} + 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; + } ``` === "Rust" ```rust title="fractional_knapsack.rs" - [class]{Item}-[func]{} + /* 物品 */ + struct Item { + w: i32, // 物品重量 + v: i32, // 物品价值 + } - [class]{}-[func]{fractional_knapsack} + 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 + } ``` === "C" ```c title="fractional_knapsack.c" - [class]{Item}-[func]{} + /* 物品 */ + struct Item { + int w; // 物品重量 + int v; // 物品价值 + }; - [class]{}-[func]{fractionalKnapsack} + typedef struct Item Item; + + /* 分数背包:贪心 */ + 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; + } ``` === "Zig" diff --git a/zh/chapter_greedy/greedy_algorithm.md b/zh/chapter_greedy/greedy_algorithm.md index 8e48978bb..c92ea0c5d 100644 --- a/zh/chapter_greedy/greedy_algorithm.md +++ b/zh/chapter_greedy/greedy_algorithm.md @@ -28,67 +28,259 @@ comments: true === "Python" ```python title="coin_change_greedy.py" - [class]{}-[func]{coin_change_greedy} + def coin_change_greedy(coins: list[int], amt: int) -> int: + """零钱兑换:贪心""" + # 假设 coins 列表有序 + i = len(coins) - 1 + count = 0 + # 循环进行贪心选择,直到无剩余金额 + while amt > 0: + # 找到小于且最接近剩余金额的硬币 + while i > 0 and coins[i] > amt: + i -= 1 + # 选择 coins[i] + amt -= coins[i] + count += 1 + # 若未找到可行方案,则返回 -1 + return count if amt == 0 else -1 ``` === "C++" ```cpp title="coin_change_greedy.cpp" - [class]{}-[func]{coinChangeGreedy} + /* 零钱兑换:贪心 */ + 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; + } ``` === "Java" ```java title="coin_change_greedy.java" - [class]{coin_change_greedy}-[func]{coinChangeGreedy} + /* 零钱兑换:贪心 */ + 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; + } ``` === "C#" ```csharp title="coin_change_greedy.cs" - [class]{coin_change_greedy}-[func]{coinChangeGreedy} + /* 零钱兑换:贪心 */ + 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; + } ``` === "Go" ```go title="coin_change_greedy.go" - [class]{}-[func]{coinChangeGreedy} + /* 零钱兑换:贪心 */ + 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 + } ``` === "Swift" ```swift title="coin_change_greedy.swift" - [class]{}-[func]{coinChangeGreedy} + /* 零钱兑换:贪心 */ + 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 + } ``` === "JS" ```javascript title="coin_change_greedy.js" - [class]{}-[func]{coinChangeGreedy} + /* 零钱兑换:贪心 */ + 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; + } ``` === "TS" ```typescript title="coin_change_greedy.ts" - [class]{}-[func]{coinChangeGreedy} + /* 零钱兑换:贪心 */ + 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; + } ``` === "Dart" ```dart title="coin_change_greedy.dart" - [class]{}-[func]{coinChangeGreedy} + /* 零钱兑换:贪心 */ + 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; + } ``` === "Rust" ```rust title="coin_change_greedy.rs" - [class]{}-[func]{coin_change_greedy} + /* 零钱兑换:贪心 */ + 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 + } + } ``` === "C" ```c title="coin_change_greedy.c" - [class]{}-[func]{coinChangeGreedy} + /* 零钱兑换:贪心 */ + 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; + } ``` === "Zig" diff --git a/zh/chapter_greedy/max_capacity_problem.md b/zh/chapter_greedy/max_capacity_problem.md index 97155333b..47d46b158 100644 --- a/zh/chapter_greedy/max_capacity_problem.md +++ b/zh/chapter_greedy/max_capacity_problem.md @@ -95,67 +95,277 @@ $$ === "Python" ```python title="max_capacity.py" - [class]{}-[func]{max_capacity} + def max_capacity(ht: list[int]) -> int: + """最大容量:贪心""" + # 初始化 i, j 分列数组两端 + i, j = 0, len(ht) - 1 + # 初始最大容量为 0 + res = 0 + # 循环贪心选择,直至两板相遇 + while i < j: + # 更新最大容量 + cap = min(ht[i], ht[j]) * (j - i) + res = max(res, cap) + # 向内移动短板 + if ht[i] < ht[j]: + i += 1 + else: + j -= 1 + return res ``` === "C++" ```cpp title="max_capacity.cpp" - [class]{}-[func]{maxCapacity} + /* 最大容量:贪心 */ + 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; + } ``` === "Java" ```java title="max_capacity.java" - [class]{max_capacity}-[func]{maxCapacity} + /* 最大容量:贪心 */ + 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; + } ``` === "C#" ```csharp title="max_capacity.cs" - [class]{max_capacity}-[func]{maxCapacity} + /* 最大容量:贪心 */ + 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; + } ``` === "Go" ```go title="max_capacity.go" - [class]{}-[func]{maxCapacity} + /* 最大容量:贪心 */ + 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 + } ``` === "Swift" ```swift title="max_capacity.swift" - [class]{}-[func]{maxCapacity} + /* 最大容量:贪心 */ + func maxCapacity(ht: [Int]) -> Int { + // 初始化 i, j 分列数组两端 + var i = 0, j = ht.count - 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 + } ``` === "JS" ```javascript title="max_capacity.js" - [class]{}-[func]{maxCapacity} + /* 最大容量:贪心 */ + 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; + } ``` === "TS" ```typescript title="max_capacity.ts" - [class]{}-[func]{maxCapacity} + /* 最大容量:贪心 */ + 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; + } ``` === "Dart" ```dart title="max_capacity.dart" - [class]{}-[func]{maxCapacity} + /* 最大容量:贪心 */ + 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; + } ``` === "Rust" ```rust title="max_capacity.rs" - [class]{}-[func]{max_capacity} + /* 最大容量:贪心 */ + 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 + } ``` === "C" ```c title="max_capacity.c" - [class]{}-[func]{maxCapacity} + /* 最大容量:贪心 */ + int maxCapacity(int ht[], int htLength) { + // 初始化 i, j 分列数组两端 + int i = 0; + int j = htLength - 1; + // 初始最大容量为 0 + int res = 0; + // 循环贪心选择,直至两板相遇 + while (i < j) { + // 更新最大容量 + int capacity = MIN(ht[i], ht[j]) * (j - i); + res = MAX(res, capacity); + // 向内移动短板 + if (ht[i] < ht[j]) { + i++; + } else { + j--; + } + } + return res; + } ``` === "Zig" diff --git a/zh/chapter_greedy/max_product_cutting_problem.md b/zh/chapter_greedy/max_product_cutting_problem.md index 72f807ab5..570bfdf98 100644 --- a/zh/chapter_greedy/max_product_cutting_problem.md +++ b/zh/chapter_greedy/max_product_cutting_problem.md @@ -76,67 +76,271 @@ $$ === "Python" ```python title="max_product_cutting.py" - [class]{}-[func]{max_product_cutting} + def max_product_cutting(n: int) -> int: + """最大切分乘积:贪心""" + # 当 n <= 3 时,必须切分出一个 1 + if n <= 3: + return 1 * (n - 1) + # 贪心地切分出 3 ,a 为 3 的个数,b 为余数 + a, b = n // 3, 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)) ``` === "C++" ```cpp title="max_product_cutting.cpp" - [class]{}-[func]{maxProductCutting} + /* 最大切分乘积:贪心 */ + 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); + } ``` === "Java" ```java title="max_product_cutting.java" - [class]{max_product_cutting}-[func]{maxProductCutting} + /* 最大切分乘积:贪心 */ + 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); + } ``` === "C#" ```csharp title="max_product_cutting.cs" - [class]{max_product_cutting}-[func]{maxProductCutting} + /* 最大切分乘积:贪心 */ + 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); + } ``` === "Go" ```go title="max_product_cutting.go" - [class]{}-[func]{maxProductCutting} + /* 最大切分乘积:贪心 */ + 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))) + } ``` === "Swift" ```swift title="max_product_cutting.swift" - [class]{}-[func]{maxProductCutting} + /* 最大切分乘积:贪心 */ + 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) + } ``` === "JS" ```javascript title="max_product_cutting.js" - [class]{}-[func]{maxProductCutting} + /* 最大切分乘积:贪心 */ + 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); + } ``` === "TS" ```typescript title="max_product_cutting.ts" - [class]{}-[func]{maxProductCutting} + /* 最大切分乘积:贪心 */ + 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); + } ``` === "Dart" ```dart title="max_product_cutting.dart" - [class]{}-[func]{maxProductCutting} + /* 最大切分乘积:贪心 */ + 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(); + } ``` === "Rust" ```rust title="max_product_cutting.rs" - [class]{}-[func]{max_product_cutting} + /* 最大切分乘积:贪心 */ + 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) + } + } ``` === "C" ```c title="max_product_cutting.c" - [class]{}-[func]{maxProductCutting} + /* 最大切分乘积:贪心 */ + 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); + } ``` === "Zig" diff --git a/zh/chapter_hashing/hash_algorithm.md b/zh/chapter_hashing/hash_algorithm.md index 6cd5bcc62..927ae76f3 100644 --- a/zh/chapter_hashing/hash_algorithm.md +++ b/zh/chapter_hashing/hash_algorithm.md @@ -55,133 +55,503 @@ index = hash(key) % capacity === "Python" ```python title="simple_hash.py" - [class]{}-[func]{add_hash} + def add_hash(key: str) -> int: + """加法哈希""" + hash = 0 + modulus = 1000000007 + for c in key: + hash += ord(c) + return hash % modulus - [class]{}-[func]{mul_hash} + def mul_hash(key: str) -> int: + """乘法哈希""" + hash = 0 + modulus = 1000000007 + for c in key: + hash = 31 * hash + ord(c) + return hash % modulus - [class]{}-[func]{xor_hash} + def xor_hash(key: str) -> int: + """异或哈希""" + hash = 0 + modulus = 1000000007 + for c in key: + hash ^= ord(c) + return hash % modulus - [class]{}-[func]{rot_hash} + def rot_hash(key: str) -> int: + """旋转哈希""" + hash = 0 + modulus = 1000000007 + for c in key: + hash = (hash << 4) ^ (hash >> 28) ^ ord(c) + return hash % modulus ``` === "C++" ```cpp title="simple_hash.cpp" - [class]{}-[func]{addHash} + /* 加法哈希 */ + 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]{mulHash} + /* 乘法哈希 */ + 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]{xorHash} + /* 异或哈希 */ + int xorHash(string key) { + int hash = 0; + const int MODULUS = 1000000007; + for (unsigned char c : key) { + hash ^= (int)c; + } + return hash & MODULUS; + } - [class]{}-[func]{rotHash} + /* 旋转哈希 */ + 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; + } ``` === "Java" ```java title="simple_hash.java" - [class]{simple_hash}-[func]{addHash} + /* 加法哈希 */ + int addHash(String key) { + long hash = 0; + final int MODULUS = 1000000007; + for (char c : key.toCharArray()) { + hash = (hash + (int) c) % MODULUS; + } + return (int) hash; + } - [class]{simple_hash}-[func]{mulHash} + /* 乘法哈希 */ + int mulHash(String key) { + long hash = 0; + final int MODULUS = 1000000007; + for (char c : key.toCharArray()) { + hash = (31 * hash + (int) c) % MODULUS; + } + return (int) hash; + } - [class]{simple_hash}-[func]{xorHash} + /* 异或哈希 */ + int xorHash(String key) { + int hash = 0; + final int MODULUS = 1000000007; + for (char c : key.toCharArray()) { + hash ^= (int) c; + } + return hash & MODULUS; + } - [class]{simple_hash}-[func]{rotHash} + /* 旋转哈希 */ + int rotHash(String key) { + long hash = 0; + final int MODULUS = 1000000007; + for (char c : key.toCharArray()) { + hash = ((hash << 4) ^ (hash >> 28) ^ (int) c) % MODULUS; + } + return (int) hash; + } ``` === "C#" ```csharp title="simple_hash.cs" - [class]{simple_hash}-[func]{addHash} + /* 加法哈希 */ + 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]{mulHash} + /* 乘法哈希 */ + 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]{xorHash} + /* 异或哈希 */ + 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]{rotHash} + /* 旋转哈希 */ + 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; + } ``` === "Go" ```go title="simple_hash.go" - [class]{}-[func]{addHash} + /* 加法哈希 */ + func addHash(key string) int { + var hash int64 + var modulus int64 - [class]{}-[func]{mulHash} + modulus = 1000000007 + for _, b := range []byte(key) { + hash = (hash + int64(b)) % modulus + } + return int(hash) + } - [class]{}-[func]{xorHash} + /* 乘法哈希 */ + func mulHash(key string) int { + var hash int64 + var modulus int64 - [class]{}-[func]{rotHash} + 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) + } ``` === "Swift" ```swift title="simple_hash.swift" - [class]{}-[func]{addHash} + /* 加法哈希 */ + 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]{mulHash} + /* 乘法哈希 */ + 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]{xorHash} + /* 异或哈希 */ + 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]{rotHash} + /* 旋转哈希 */ + 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 + } ``` === "JS" ```javascript title="simple_hash.js" - [class]{}-[func]{addHash} + /* 加法哈希 */ + function addHash(key) { + let hash = 0; + const MODULUS = 1000000007; + for (const c of key) { + hash = (hash + c.charCodeAt(0)) % MODULUS; + } + return hash; + } - [class]{}-[func]{mulHash} + /* 乘法哈希 */ + 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]{xorHash} + /* 异或哈希 */ + function xorHash(key) { + let hash = 0; + const MODULUS = 1000000007; + for (const c of key) { + hash ^= c.charCodeAt(0); + } + return hash & MODULUS; + } - [class]{}-[func]{rotHash} + /* 旋转哈希 */ + 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; + } ``` === "TS" ```typescript title="simple_hash.ts" - [class]{}-[func]{addHash} + /* 加法哈希 */ + 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]{mulHash} + /* 乘法哈希 */ + 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]{xorHash} + /* 异或哈希 */ + 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]{rotHash} + /* 旋转哈希 */ + 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; + } ``` === "Dart" ```dart title="simple_hash.dart" - [class]{}-[func]{addHash} + /* 加法哈希 */ + 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]{mulHash} + /* 乘法哈希 */ + 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]{xorHash} + /* 异或哈希 */ + 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]{rotHash} + /* 旋转哈希 */ + 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; + } ``` === "Rust" ```rust title="simple_hash.rs" - [class]{}-[func]{add_hash} + /* 加法哈希 */ + fn add_hash(key: &str) -> i32 { + let mut hash = 0_i64; + const MODULUS: i64 = 1000000007; - [class]{}-[func]{mul_hash} + for c in key.chars() { + hash = (hash + c as i64) % MODULUS; + } - [class]{}-[func]{xor_hash} + hash as i32 + } - [class]{}-[func]{rot_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 + } ``` === "C" ```c title="simple_hash.c" - [class]{}-[func]{addHash} + /* 加法哈希 */ + 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]{mulHash} + /* 乘法哈希 */ + 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]{xorHash} + /* 异或哈希 */ + int xorHash(char *key) { + int hash = 0; + const int MODULUS = 1000000007; - [class]{}-[func]{rotHash} + 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; + } ``` === "Zig" diff --git a/zh/chapter_hashing/hash_collision.md b/zh/chapter_hashing/hash_collision.md index 10108f7e7..a375d0284 100644 --- a/zh/chapter_hashing/hash_collision.md +++ b/zh/chapter_hashing/hash_collision.md @@ -40,67 +40,1288 @@ comments: true === "Python" ```python title="hash_map_chaining.py" - [class]{HashMapChaining}-[func]{} + class HashMapChaining: + """链式地址哈希表""" + + 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)] # 桶数组 + + def hash_func(self, key: int) -> int: + """哈希函数""" + return key % self.capacity + + def load_factor(self) -> float: + """负载因子""" + return self.size / self.capacity + + def get(self, key: int) -> str | None: + """查询操作""" + index = self.hash_func(key) + bucket = self.buckets[index] + # 遍历桶,若找到 key 则返回对应 val + for pair in bucket: + if pair.key == key: + return pair.val + # 若未找到 key 则返回 None + return None + + def put(self, key: int, val: str): + """添加操作""" + # 当负载因子超过阈值时,执行扩容 + if self.load_factor() > self.load_thres: + self.extend() + index = self.hash_func(key) + bucket = self.buckets[index] + # 遍历桶,若遇到指定 key ,则更新对应 val 并返回 + for pair in bucket: + if pair.key == key: + pair.val = val + return + # 若无该 key ,则将键值对添加至尾部 + pair = Pair(key, val) + bucket.append(pair) + self.size += 1 + + def remove(self, key: int): + """删除操作""" + index = self.hash_func(key) + bucket = self.buckets[index] + # 遍历桶,从中删除键值对 + for pair in bucket: + if pair.key == key: + bucket.remove(pair) + self.size -= 1 + break + + def extend(self): + """扩容哈希表""" + # 暂存原哈希表 + buckets = self.buckets + # 初始化扩容后的新哈希表 + self.capacity *= self.extend_ratio + self.buckets = [[] for _ in range(self.capacity)] + self.size = 0 + # 将键值对从原哈希表搬运至新哈希表 + for bucket in buckets: + for pair in bucket: + self.put(pair.key, pair.val) + + def print(self): + """打印哈希表""" + for bucket in self.buckets: + res = [] + for pair in bucket: + res.append(str(pair.key) + " -> " + pair.val) + print(res) ``` === "C++" ```cpp title="hash_map_chaining.cpp" - [class]{HashMapChaining}-[func]{} + /* 链式地址哈希表 */ + 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"; + } + } + }; ``` === "Java" ```java title="hash_map_chaining.java" - [class]{HashMapChaining}-[func]{} + /* 链式地址哈希表 */ + 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 ArrayList<>(capacity); + for (int i = 0; i < capacity; i++) { + buckets.add(new ArrayList<>()); + } + } + + /* 哈希函数 */ + int hashFunc(int key) { + return key % capacity; + } + + /* 负载因子 */ + double loadFactor() { + return (double) size / capacity; + } + + /* 查询操作 */ + String get(int key) { + int index = hashFunc(key); + List bucket = buckets.get(index); + // 遍历桶,若找到 key 则返回对应 val + for (Pair pair : 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.get(index); + // 遍历桶,若遇到指定 key ,则更新对应 val 并返回 + for (Pair pair : bucket) { + if (pair.key == key) { + pair.val = val; + return; + } + } + // 若无该 key ,则将键值对添加至尾部 + Pair pair = new Pair(key, val); + bucket.add(pair); + size++; + } + + /* 删除操作 */ + void remove(int key) { + int index = hashFunc(key); + List bucket = buckets.get(index); + // 遍历桶,从中删除键值对 + for (Pair pair : bucket) { + if (pair.key == key) { + bucket.remove(pair); + size--; + break; + } + } + } + + /* 扩容哈希表 */ + void extend() { + // 暂存原哈希表 + List> bucketsTmp = buckets; + // 初始化扩容后的新哈希表 + capacity *= extendRatio; + buckets = new ArrayList<>(capacity); + for (int i = 0; i < capacity; i++) { + buckets.add(new ArrayList<>()); + } + size = 0; + // 将键值对从原哈希表搬运至新哈希表 + for (List bucket : bucketsTmp) { + for (Pair pair : bucket) { + put(pair.key, pair.val); + } + } + } + + /* 打印哈希表 */ + void print() { + for (List bucket : buckets) { + List res = new ArrayList<>(); + for (Pair pair : bucket) { + res.add(pair.key + " -> " + pair.val); + } + System.out.println(res); + } + } + } ``` === "C#" ```csharp title="hash_map_chaining.cs" - [class]{HashMapChaining}-[func]{} + /* 链式地址哈希表 */ + 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(new List()); + } + } + + /* 哈希函数 */ + private int hashFunc(int key) { + return key % capacity; + } + + /* 负载因子 */ + private 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; + } + } + } + + /* 扩容哈希表 */ + private void extend() { + // 暂存原哈希表 + List> bucketsTmp = buckets; + // 初始化扩容后的新哈希表 + capacity *= extendRatio; + buckets = new List>(capacity); + for (int i = 0; i < capacity; i++) { + buckets.Add(new List()); + } + 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 = new List(); + foreach (Pair pair in bucket) { + res.Add(pair.key + " -> " + pair.val); + } + foreach (string kv in res) { + Console.WriteLine(kv); + } + } + } + } ``` === "Go" ```go title="hash_map_chaining.go" - [class]{hashMapChaining}-[func]{} + /* 链式地址哈希表 */ + 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 / 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 _, p := range m.buckets[idx] { + if p.key == key { + p.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() + } + } ``` === "Swift" ```swift title="hash_map_chaining.swift" - [class]{HashMapChaining}-[func]{} + /* 链式地址哈希表 */ + 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 / 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 + } + + /* 扩容哈希表 */ + 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) + } + } + } ``` === "JS" ```javascript title="hash_map_chaining.js" - [class]{HashMapChaining}-[func]{} + /* 链式地址哈希表 */ + 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); + } + } + } ``` === "TS" ```typescript title="hash_map_chaining.ts" - [class]{HashMapChaining}-[func]{} + /* 链式地址哈希表 */ + 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); + } + } + } ``` === "Dart" ```dart title="hash_map_chaining.dart" - [class]{HashMapChaining}-[func]{} + /* 链式地址哈希表 */ + 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); + } + } + } ``` === "Rust" ```rust title="hash_map_chaining.rs" - [class]{HashMapChaining}-[func]{} + /* 链式地址哈希表 */ + 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.clone(); + return; + } + } + let bucket = &mut self.buckets[index]; + + // 若无该 key ,则将键值对添加至尾部 + let pair = Pair { + key, + val: val.clone(), + }; + 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 + } + } ``` === "C" ```c title="hash_map_chaining.c" - [class]{hashMapChaining}-[func]{} + /* 基于数组简易实现的链式地址哈希表 */ + struct hashMapChaining { + int size; // 键值对数量 + int capacity; // 哈希表容量 + double loadThres; // 触发扩容的负载因子阈值 + int extendRatio; // 扩容倍数 + Pair *buckets; // 桶数组 + }; + + typedef struct hashMapChaining hashMapChaining; + + /* 初始化桶数组 */ + hashMapChaining *newHashMapChaining() { + // 为哈希表分配空间 + int tableSize = 4; + hashMapChaining *hashmap = (hashMapChaining *)malloc(sizeof(hashMapChaining)); + + // 初始化数组 + hashmap->buckets = (Pair *)malloc(sizeof(Pair) * tableSize); + memset(hashmap->buckets, 0, sizeof(Pair) * tableSize); + + hashmap->capacity = tableSize; + hashmap->size = 0; + hashmap->extendRatio = 2; + hashmap->loadThres = 2.0 / 3.0; + + return hashmap; + } + + /* 销毁哈希表 */ + void delHashMapChaining(hashMapChaining *hashmap) { + for (int i = 0; i < hashmap->capacity; i++) { + Pair *pair = &hashmap->buckets[i]; + Node *node = pair->node; + while (node != NULL) { + Node *temp = node; + node = node->next; + free(temp->val); + free(temp); + } + } + free(hashmap->buckets); + free(hashmap); + } + + /* 哈希函数 */ + int hashFunc(hashMapChaining *hashmap, const int key) { + return key % hashmap->capacity; + } + + /* 负载因子 */ + double loadFactor(hashMapChaining *hashmap) { + return (double)hashmap->size / (double)hashmap->capacity; + } + + /* 添加操作 */ + void put(hashMapChaining *hashmap, const int key, char *val) { + if (loadFactor(hashmap) > hashmap->loadThres) { + extend(hashmap); + } + int index = hashFunc(hashmap, key); + + // 先为新节点分配空间再赋值 + Node *newNode = (Node *)malloc(sizeof(Node)); + memset(newNode, 0, sizeof(Node)); + newNode->key = key; + newNode->val = (char *)malloc(strlen(val) + 1); + strcpy(newNode->val, val); + newNode->val[strlen(val)] = '\0'; + + Pair *pair = &hashmap->buckets[index]; + Node *node = pair->node; + if (node == NULL) { + hashmap->buckets[index].node = newNode; + hashmap->size++; + return; + } + while (node != NULL) { + if (node->key == key) { + // 释放先前分配的内存 + free(node->val); + // 更新节点的值 + node->val = (char *)malloc(strlen(val) + 1); + strcpy(node->val, val); + node->val[strlen(val)] = '\0'; + return; + } + if (node->next == NULL) { + break; + } + node = node->next; + } + node->next = newNode; + hashmap->size++; + } + + /* 删除操作 */ + void removeItem(hashMapChaining *hashmap, int key) { + int index = hashFunc(hashmap, key); + Pair *pair = &hashmap->buckets[index]; + Node *node = pair->node; + // 保存后继的节点 + Node *prev = NULL; + while (node != NULL) { + if (node->key == key) { + // 如果要删除的节点是桶的第一个节点 + if (prev == NULL) { + pair->node = node->next; + } else { + prev->next = node->next; + } + // 释放内存 + free(node->val); + free(node); + hashmap->size--; + return; + } + prev = node; + node = node->next; + } + return; + } + + /* 扩容哈希表 */ + void extend(hashMapChaining *hashmap) { + // 暂存原哈希表 + Pair *oldBuckets = hashmap->buckets; + int oldCapacity = hashmap->capacity; + + // 创建新的哈希表,重新分配一段空间 + hashmap->capacity *= hashmap->extendRatio; + hashmap->buckets = (Pair *)malloc(sizeof(Pair) * hashmap->capacity); + memset(hashmap->buckets, 0, sizeof(Pair) * hashmap->capacity); + hashmap->size = 0; + + // 将原哈希表中的键值对重新哈希到新的哈希表中 + for (int i = 0; i < oldCapacity; i++) { + Node *node = oldBuckets[i].node; + while (node != NULL) { + put(hashmap, node->key, node->val); + node = node->next; + } + } + + // 释放原哈希表的内存 + for (int i = 0; i < oldCapacity; i++) { + Node *node = oldBuckets[i].node; + while (node != NULL) { + Node *temp = node; + node = node->next; + free(temp->val); + free(temp); + } + } + free(oldBuckets); + } + + /* 打印哈希表 */ + void print(hashMapChaining *hashmap) { + for (int i = 0; i < hashmap->capacity; i++) { + printf("["); + Pair *pair = &hashmap->buckets[i]; + Node *node = pair->node; + while (node != NULL) { + if (node->val != NULL) { + printf("%d->%s, ", node->key, node->val); + } + node = node->next; + } + printf("]\n"); + } + return; + } ``` === "Zig" @@ -149,61 +1370,1287 @@ comments: true === "Python" ```python title="hash_map_open_addressing.py" - [class]{HashMapOpenAddressing}-[func]{} + class HashMapOpenAddressing: + """开放寻址哈希表""" + + 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") # 删除标记 + + def hash_func(self, key: int) -> int: + """哈希函数""" + return key % self.capacity + + def load_factor(self) -> float: + """负载因子""" + return self.size / self.capacity + + def find_bucket(self, key: int) -> int: + """搜索 key 对应的桶索引""" + index = self.hash_func(key) + first_tombstone = -1 + # 线性探测,当遇到空桶时跳出 + while self.buckets[index] is not None: + # 若遇到 key ,返回对应桶索引 + if self.buckets[index].key == key: + # 若之前遇到了删除标记,则将键值对移动至该索引 + if first_tombstone != -1: + self.buckets[first_tombstone] = self.buckets[index] + self.buckets[index] = self.TOMBSTONE + return first_tombstone # 返回移动后的桶索引 + return index # 返回桶索引 + # 记录遇到的首个删除标记 + if first_tombstone == -1 and self.buckets[index] is self.TOMBSTONE: + first_tombstone = index + # 计算桶索引,越过尾部返回头部 + index = (index + 1) % self.capacity + # 若 key 不存在,则返回添加点的索引 + return index if first_tombstone == -1 else first_tombstone + + def get(self, key: int) -> str: + """查询操作""" + # 搜索 key 对应的桶索引 + index = self.find_bucket(key) + # 若找到键值对,则返回对应 val + if self.buckets[index] not in [None, self.TOMBSTONE]: + return self.buckets[index].val + # 若键值对不存在,则返回 None + return None + + def put(self, key: int, val: str): + """添加操作""" + # 当负载因子超过阈值时,执行扩容 + if self.load_factor() > self.load_thres: + self.extend() + # 搜索 key 对应的桶索引 + index = self.find_bucket(key) + # 若找到键值对,则覆盖 val 并返回 + if self.buckets[index] not in [None, self.TOMBSTONE]: + self.buckets[index].val = val + return + # 若键值对不存在,则添加该键值对 + self.buckets[index] = Pair(key, val) + self.size += 1 + + def remove(self, key: int): + """删除操作""" + # 搜索 key 对应的桶索引 + index = self.find_bucket(key) + # 若找到键值对,则用删除标记覆盖它 + if self.buckets[index] not in [None, self.TOMBSTONE]: + self.buckets[index] = self.TOMBSTONE + self.size -= 1 + + def extend(self): + """扩容哈希表""" + # 暂存原哈希表 + buckets_tmp = self.buckets + # 初始化扩容后的新哈希表 + self.capacity *= self.extend_ratio + self.buckets = [None] * self.capacity + self.size = 0 + # 将键值对从原哈希表搬运至新哈希表 + for pair in buckets_tmp: + if pair not in [None, self.TOMBSTONE]: + self.put(pair.key, pair.val) + + def print(self): + """打印哈希表""" + for pair in self.buckets: + if pair is None: + print("None") + elif pair is self.TOMBSTONE: + print("TOMBSTONE") + else: + print(pair.key, "->", pair.val) ``` === "C++" ```cpp title="hash_map_open_addressing.cpp" - [class]{HashMapOpenAddressing}-[func]{} + /* 开放寻址哈希表 */ + 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; + } + } + } + }; ``` === "Java" ```java title="hash_map_open_addressing.java" - [class]{HashMapOpenAddressing}-[func]{} + /* 开放寻址哈希表 */ + 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"); // 删除标记 + + /* 构造方法 */ + public HashMapOpenAddressing() { + size = 0; + buckets = new Pair[capacity]; + } + + /* 哈希函数 */ + private int hashFunc(int key) { + return key % capacity; + } + + /* 负载因子 */ + private double loadFactor() { + return (double) size / capacity; + } + + /* 搜索 key 对应的桶索引 */ + private 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--; + } + } + + /* 扩容哈希表 */ + private void extend() { + // 暂存原哈希表 + Pair[] bucketsTmp = buckets; + // 初始化扩容后的新哈希表 + capacity *= extendRatio; + buckets = new Pair[capacity]; + size = 0; + // 将键值对从原哈希表搬运至新哈希表 + for (Pair pair : bucketsTmp) { + if (pair != null && pair != TOMBSTONE) { + put(pair.key, pair.val); + } + } + } + + /* 打印哈希表 */ + public void print() { + for (Pair pair : buckets) { + if (pair == null) { + System.out.println("null"); + } else if (pair == TOMBSTONE) { + System.out.println("TOMBSTONE"); + } else { + System.out.println(pair.key + " -> " + pair.val); + } + } + } + } ``` === "C#" ```csharp title="hash_map_open_addressing.cs" - [class]{HashMapOpenAddressing}-[func]{} + /* 开放寻址哈希表 */ + class HashMapOpenAddressing { + private int size; // 键值对数量 + private int capacity = 4; // 哈希表容量 + private double loadThres = 2.0 / 3.0; // 触发扩容的负载因子阈值 + private int extendRatio = 2; // 扩容倍数 + private Pair[] buckets; // 桶数组 + private Pair TOMBSTONE = new Pair(-1, "-1"); // 删除标记 + + /* 构造方法 */ + public HashMapOpenAddressing() { + size = 0; + buckets = new Pair[capacity]; + } + + /* 哈希函数 */ + private int hashFunc(int key) { + return key % capacity; + } + + /* 负载因子 */ + private double loadFactor() { + return (double)size / capacity; + } + + /* 搜索 key 对应的桶索引 */ + private 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--; + } + } + + /* 扩容哈希表 */ + private 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); + } + } + } + } ``` === "Go" ```go title="hash_map_open_addressing.go" - [class]{hashMapOpenAddressing}-[func]{} + /* 开放寻址哈希表 */ + type hashMapOpenAddressing struct { + size int // 键值对数量 + capacity int // 哈希表容量 + loadThres float64 // 触发扩容的负载因子阈值 + extendRatio int // 扩容倍数 + buckets []pair // 桶数组 + removed pair // 删除标记 + } + + /* 构造方法 */ + func newHashMapOpenAddressing() *hashMapOpenAddressing { + buckets := make([]pair, 4) + return &hashMapOpenAddressing{ + size: 0, + capacity: 4, + loadThres: 2.0 / 3.0, + extendRatio: 2, + buckets: buckets, + removed: pair{ + key: -1, + val: "-1", + }, + } + } + + /* 哈希函数 */ + func (m *hashMapOpenAddressing) hashFunc(key int) int { + return key % m.capacity + } + + /* 负载因子 */ + func (m *hashMapOpenAddressing) loadFactor() float64 { + return float64(m.size) / float64(m.capacity) + } + + /* 查询操作 */ + func (m *hashMapOpenAddressing) get(key int) string { + idx := m.hashFunc(key) + // 线性探测,从 index 开始向后遍历 + for i := 0; i < m.capacity; i++ { + // 计算桶索引,越过尾部返回头部 + j := (idx + 1) % m.capacity + // 若遇到空桶,说明无此 key ,则返回 null + if m.buckets[j] == (pair{}) { + return "" + } + // 若遇到指定 key ,则返回对应 val + if m.buckets[j].key == key && m.buckets[j] != m.removed { + return m.buckets[j].val + } + } + // 若未找到 key 则返回空字符串 + return "" + } + + /* 添加操作 */ + func (m *hashMapOpenAddressing) put(key int, val string) { + // 当负载因子超过阈值时,执行扩容 + if m.loadFactor() > m.loadThres { + m.extend() + } + idx := m.hashFunc(key) + // 线性探测,从 index 开始向后遍历 + for i := 0; i < m.capacity; i++ { + // 计算桶索引,越过尾部返回头部 + j := (idx + i) % m.capacity + // 若遇到空桶、或带有删除标记的桶,则将键值对放入该桶 + if m.buckets[j] == (pair{}) || m.buckets[j] == m.removed { + m.buckets[j] = pair{ + key: key, + val: val, + } + m.size += 1 + return + } + // 若遇到指定 key ,则更新对应 val + if m.buckets[j].key == key { + m.buckets[j].val = val + } + } + } + + /* 删除操作 */ + func (m *hashMapOpenAddressing) remove(key int) { + idx := m.hashFunc(key) + // 遍历桶,从中删除键值对 + // 线性探测,从 index 开始向后遍历 + for i := 0; i < m.capacity; i++ { + // 计算桶索引,越过尾部返回头部 + j := (idx + 1) % m.capacity + // 若遇到空桶,说明无此 key ,则直接返回 + if m.buckets[j] == (pair{}) { + return + } + // 若遇到指定 key ,则标记删除并返回 + if m.buckets[j].key == key { + m.buckets[j] = m.removed + m.size -= 1 + } + } + } + + /* 扩容哈希表 */ + func (m *hashMapOpenAddressing) extend() { + // 暂存原哈希表 + tmpBuckets := make([]pair, len(m.buckets)) + copy(tmpBuckets, m.buckets) + + // 初始化扩容后的新哈希表 + m.capacity *= m.extendRatio + m.buckets = make([]pair, m.capacity) + m.size = 0 + // 将键值对从原哈希表搬运至新哈希表 + for _, p := range tmpBuckets { + if p != (pair{}) && p != m.removed { + m.put(p.key, p.val) + } + } + } + + /* 打印哈希表 */ + func (m *hashMapOpenAddressing) print() { + for _, p := range m.buckets { + if p != (pair{}) { + fmt.Println(strconv.Itoa(p.key) + " -> " + p.val) + } else { + fmt.Println("nil") + } + } + } ``` === "Swift" ```swift title="hash_map_open_addressing.swift" - [class]{HashMapOpenAddressing}-[func]{} + /* 开放寻址哈希表 */ + 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 / 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)") + } + } + } + } ``` === "JS" ```javascript title="hash_map_open_addressing.js" - [class]{HashMapOpenAddressing}-[func]{} + /* 开放寻址哈希表 */ + class HashMapOpenAddressing { + #size; // 键值对数量 + #capacity; // 哈希表容量 + #loadThres; // 触发扩容的负载因子阈值 + #extendRatio; // 扩容倍数 + #buckets; // 桶数组 + #removed; // 删除标记 + + /* 构造方法 */ + constructor() { + this.#size = 0; + this.#capacity = 4; + this.#loadThres = 2.0 / 3.0; + this.#extendRatio = 2; + this.#buckets = new Array(this.#capacity).fill(null); + this.#removed = new Pair(-1, '-1'); + } + + /* 哈希函数 */ + #hashFunc(key) { + return key % this.#capacity; + } + + /* 负载因子 */ + #loadFactor() { + return this.#size / this.#capacity; + } + + /* 查询操作 */ + get(key) { + const index = this.#hashFunc(key); + // 线性探测,从 index 开始向后遍历 + for (let i = 0; i < this.#capacity; i++) { + // 计算桶索引,越过尾部返回头部 + const j = (index + i) % this.#capacity; + // 若遇到空桶,说明无此 key ,则返回 null + if (this.#buckets[j] === null) return null; + // 若遇到指定 key ,则返回对应 val + if ( + this.#buckets[j].key === key && + this.#buckets[j][key] !== this.#removed.key + ) + return this.#buckets[j].val; + } + return null; + } + + /* 添加操作 */ + put(key, val) { + // 当负载因子超过阈值时,执行扩容 + if (this.#loadFactor() > this.#loadThres) { + this.#extend(); + } + const index = this.#hashFunc(key); + // 线性探测,从 index 开始向后遍历 + for (let i = 0; i < this.#capacity; i++) { + // 计算桶索引,越过尾部返回头部 + let j = (index + i) % this.#capacity; + // 若遇到空桶、或带有删除标记的桶,则将键值对放入该桶 + if ( + this.#buckets[j] === null || + this.#buckets[j][key] === this.#removed.key + ) { + this.#buckets[j] = new Pair(key, val); + this.#size += 1; + return; + } + // 若遇到指定 key ,则更新对应 val + if (this.#buckets[j].key === key) { + this.#buckets[j].val = val; + return; + } + } + } + + /* 删除操作 */ + remove(key) { + const index = this.#hashFunc(key); + // 线性探测,从 index 开始向后遍历 + for (let i = 0; i < this.#capacity; i++) { + // 计算桶索引,越过尾部返回头部 + const j = (index + i) % this.#capacity; + // 若遇到空桶,说明无此 key ,则直接返回 + if (this.#buckets[j] === null) { + return; + } + // 若遇到指定 key ,则标记删除并返回 + if (this.#buckets[j].key === key) { + this.#buckets[j] = this.#removed; + this.#size -= 1; + return; + } + } + } + + /* 扩容哈希表 */ + #extend() { + // 暂存原哈希表 + const bucketsTmp = this.#buckets; + // 初始化扩容后的新哈希表 + this.#capacity *= this.#extendRatio; + this.#buckets = new Array(this.#capacity).fill(null); + this.#size = 0; + // 将键值对从原哈希表搬运至新哈希表 + for (const pair of bucketsTmp) { + if (pair !== null && pair.key !== this.#removed.key) { + this.put(pair.key, pair.val); + } + } + } + + /* 打印哈希表 */ + print() { + for (const pair of this.#buckets) { + if (pair !== null) { + console.log(pair.key + ' -> ' + pair.val); + } else { + console.log('null'); + } + } + } + } ``` === "TS" ```typescript title="hash_map_open_addressing.ts" - [class]{HashMapOpenAddressing}-[func]{} + /* 开放寻址哈希表 */ + class HashMapOpenAddressing { + #size: number; // 键值对数量 + #capacity: number; // 哈希表容量 + #loadThres: number; // 触发扩容的负载因子阈值 + #extendRatio: number; // 扩容倍数 + #buckets: Pair[]; // 桶数组 + #removed: 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); + this.#removed = new Pair(-1, '-1'); + } + + /* 哈希函数 */ + #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); + // 线性探测,从 index 开始向后遍历 + for (let i = 0; i < this.#capacity; i++) { + // 计算桶索引,越过尾部返回头部 + const j = (index + i) % this.#capacity; + // 若遇到空桶,说明无此 key ,则返回 null + if (this.#buckets[j] === null) return null; + // 若遇到指定 key ,则返回对应 val + if ( + this.#buckets[j].key === key && + this.#buckets[j][key] !== this.#removed.key + ) + return this.#buckets[j].val; + } + return null; + } + + /* 添加操作 */ + put(key: number, val: string): void { + // 当负载因子超过阈值时,执行扩容 + if (this.#loadFactor() > this.#loadThres) { + this.#extend(); + } + const index = this.#hashFunc(key); + // 线性探测,从 index 开始向后遍历 + for (let i = 0; i < this.#capacity; i++) { + // 计算桶索引,越过尾部返回头部 + let j = (index + i) % this.#capacity; + // 若遇到空桶、或带有删除标记的桶,则将键值对放入该桶 + if ( + this.#buckets[j] === null || + this.#buckets[j][key] === this.#removed.key + ) { + this.#buckets[j] = new Pair(key, val); + this.#size += 1; + return; + } + // 若遇到指定 key ,则更新对应 val + if (this.#buckets[j].key === key) { + this.#buckets[j].val = val; + return; + } + } + } + + /* 删除操作 */ + remove(key: number): void { + const index = this.#hashFunc(key); + // 线性探测,从 index 开始向后遍历 + for (let i = 0; i < this.#capacity; i++) { + // 计算桶索引,越过尾部返回头部 + const j = (index + i) % this.#capacity; + // 若遇到空桶,说明无此 key ,则直接返回 + if (this.#buckets[j] === null) { + return; + } + // 若遇到指定 key ,则标记删除并返回 + if (this.#buckets[j].key === key) { + this.#buckets[j] = this.#removed; + this.#size -= 1; + return; + } + } + } + + /* 扩容哈希表 */ + #extend(): void { + // 暂存原哈希表 + const bucketsTmp = this.#buckets; + // 初始化扩容后的新哈希表 + this.#capacity *= this.#extendRatio; + this.#buckets = new Array(this.#capacity).fill(null); + this.#size = 0; + // 将键值对从原哈希表搬运至新哈希表 + for (const pair of bucketsTmp) { + if (pair !== null && pair.key !== this.#removed.key) { + this.put(pair.key, pair.val); + } + } + } + + /* 打印哈希表 */ + print(): void { + for (const pair of this.#buckets) { + if (pair !== null) { + console.log(pair.key + ' -> ' + pair.val); + } else { + console.log('null'); + } + } + } + } ``` === "Dart" ```dart title="hash_map_open_addressing.dart" - [class]{HashMapOpenAddressing}-[func]{} + /* 开放寻址哈希表 */ + 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}"); + } + } + } + } ``` === "Rust" ```rust title="hash_map_open_addressing.rs" - [class]{HashMapOpenAddressing}-[func]{} + /* 开放寻址哈希表 */ + 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); + } + } + } + } ``` === "C" diff --git a/zh/chapter_hashing/hash_map.md b/zh/chapter_hashing/hash_map.md index 5eb01d5dd..8e09e6724 100755 --- a/zh/chapter_hashing/hash_map.md +++ b/zh/chapter_hashing/hash_map.md @@ -506,41 +506,459 @@ index = hash(key) % capacity === "Python" ```python title="array_hash_map.py" - [class]{Pair}-[func]{} + class Pair: + """键值对""" - [class]{ArrayHashMap}-[func]{} + def __init__(self, key: int, val: str): + self.key = key + self.val = val + + class ArrayHashMap: + """基于数组简易实现的哈希表""" + + def __init__(self): + """构造方法""" + # 初始化数组,包含 100 个桶 + self.buckets: list[Pair | None] = [None] * 100 + + def hash_func(self, key: int) -> int: + """哈希函数""" + index = key % 100 + return index + + def get(self, key: int) -> str: + """查询操作""" + index: int = self.hash_func(key) + pair: Pair = self.buckets[index] + if pair is None: + return None + return pair.val + + def put(self, key: int, val: str): + """添加操作""" + pair = Pair(key, val) + index: int = self.hash_func(key) + self.buckets[index] = pair + + def remove(self, key: int): + """删除操作""" + index: int = self.hash_func(key) + # 置为 None ,代表删除 + self.buckets[index] = None + + def entry_set(self) -> list[Pair]: + """获取所有键值对""" + result: list[Pair] = [] + for pair in self.buckets: + if pair is not None: + result.append(pair) + return result + + def key_set(self) -> list[int]: + """获取所有键""" + result = [] + for pair in self.buckets: + if pair is not None: + result.append(pair.key) + return result + + def value_set(self) -> list[str]: + """获取所有值""" + result = [] + for pair in self.buckets: + if pair is not None: + result.append(pair.val) + return result + + def print(self): + """打印哈希表""" + for pair in self.buckets: + if pair is not None: + print(pair.key, "->", pair.val) ``` === "C++" ```cpp title="array_hash_map.cpp" - [class]{Pair}-[func]{} + /* 键值对 */ + struct Pair { + public: + int key; + string val; + Pair(int key, string val) { + this->key = key; + this->val = val; + } + }; - [class]{ArrayHashMap}-[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; + } + } + }; ``` === "Java" ```java title="array_hash_map.java" - [class]{Pair}-[func]{} + /* 键值对 */ + class Pair { + public int key; + public String val; - [class]{ArrayHashMap}-[func]{} + public Pair(int key, String val) { + this.key = key; + this.val = val; + } + } + + /* 基于数组简易实现的哈希表 */ + class ArrayHashMap { + private List buckets; + + public ArrayHashMap() { + // 初始化数组,包含 100 个桶 + buckets = new ArrayList<>(); + for (int i = 0; i < 100; i++) { + buckets.add(null); + } + } + + /* 哈希函数 */ + private int hashFunc(int key) { + int index = key % 100; + return index; + } + + /* 查询操作 */ + public String get(int key) { + int index = hashFunc(key); + Pair pair = buckets.get(index); + if (pair == null) + return null; + return pair.val; + } + + /* 添加操作 */ + public void put(int key, String val) { + Pair pair = new Pair(key, val); + int index = hashFunc(key); + buckets.set(index, pair); + } + + /* 删除操作 */ + public void remove(int key) { + int index = hashFunc(key); + // 置为 null ,代表删除 + buckets.set(index, null); + } + + /* 获取所有键值对 */ + public List pairSet() { + List pairSet = new ArrayList<>(); + for (Pair pair : buckets) { + if (pair != null) + pairSet.add(pair); + } + return pairSet; + } + + /* 获取所有键 */ + public List keySet() { + List keySet = new ArrayList<>(); + for (Pair pair : buckets) { + if (pair != null) + keySet.add(pair.key); + } + return keySet; + } + + /* 获取所有值 */ + public List valueSet() { + List valueSet = new ArrayList<>(); + for (Pair pair : buckets) { + if (pair != null) + valueSet.add(pair.val); + } + return valueSet; + } + + /* 打印哈希表 */ + public void print() { + for (Pair kv : pairSet()) { + System.out.println(kv.key + " -> " + kv.val); + } + } + } ``` === "C#" ```csharp title="array_hash_map.cs" - [class]{Pair}-[func]{} + /* 键值对 int->string */ + class Pair { + public int key; + public string val; + public Pair(int key, string val) { + this.key = key; + this.val = val; + } + } - [class]{ArrayHashMap}-[func]{} + /* 基于数组简易实现的哈希表 */ + class ArrayHashMap { + private List buckets; + public ArrayHashMap() { + // 初始化数组,包含 100 个桶 + buckets = new(); + for (int i = 0; i < 100; i++) { + buckets.Add(null); + } + } + + /* 哈希函数 */ + private 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 Pair(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 = new(); + foreach (Pair? pair in buckets) { + if (pair != null) + pairSet.Add(pair); + } + return pairSet; + } + + /* 获取所有键 */ + public List keySet() { + List keySet = new(); + foreach (Pair? pair in buckets) { + if (pair != null) + keySet.Add(pair.key); + } + return keySet; + } + + /* 获取所有值 */ + public List valueSet() { + List valueSet = new(); + 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); + } + } + } ``` === "Go" ```go title="array_hash_map.go" - [class]{pair}-[func]{} + /* 键值对 */ + type pair struct { + key int + val string + } - [class]{arrayHashMap}-[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) + } + } + } ``` === "Swift" @@ -557,55 +975,668 @@ index = hash(key) % capacity } } - [class]{ArrayHashMap}-[func]{} + /* 基于数组简易实现的哈希表 */ + class ArrayHashMap { + private var buckets: [Pair?] = [] + + init() { + // 初始化数组,包含 100 个桶 + for _ in 0 ..< 100 { + buckets.append(nil) + } + } + + /* 哈希函数 */ + 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] { + var pairSet: [Pair] = [] + for pair in buckets { + if let pair = pair { + pairSet.append(pair) + } + } + return pairSet + } + + /* 获取所有键 */ + func keySet() -> [Int] { + var keySet: [Int] = [] + for pair in buckets { + if let pair = pair { + keySet.append(pair.key) + } + } + return keySet + } + + /* 获取所有值 */ + func valueSet() -> [String] { + var valueSet: [String] = [] + for pair in buckets { + if let pair = pair { + valueSet.append(pair.val) + } + } + return valueSet + } + + /* 打印哈希表 */ + func print() { + for pair in pairSet() { + Swift.print("\(pair.key) -> \(pair.val)") + } + } + } ``` === "JS" ```javascript title="array_hash_map.js" - [class]{Pair}-[func]{} + /* 键值对 Number -> String */ + class Pair { + constructor(key, val) { + this.key = key; + this.val = val; + } + } - [class]{ArrayHashMap}-[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}`); + } + } + } ``` === "TS" ```typescript title="array_hash_map.ts" - [class]{Pair}-[func]{} + /* 键值对 Number -> String */ + class Pair { + public key: number; + public val: string; - [class]{ArrayHashMap}-[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}`); + } + } + } ``` === "Dart" ```dart title="array_hash_map.dart" - [class]{Pair}-[func]{} + /* 键值对 */ + class Pair { + int key; + String val; + Pair(this.key, this.val); + } - [class]{ArrayHashMap}-[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}"); + } + } + } ``` === "Rust" ```rust title="array_hash_map.rs" - [class]{Pair}-[func]{} + /* 键值对 */ + #[derive(Debug, Clone, PartialEq)] + pub struct Pair { + pub key: i32, + pub val: String, + } - [class]{ArrayHashMap}-[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); + } + } + } ``` === "C" ```c title="array_hash_map.c" - [class]{pair}-[func]{} + /* 键值对 int->string */ + struct pair { + int key; + char *val; + }; - [class]{arrayHashMap}-[func]{} + typedef struct pair pair; + + /* 基于数组简易实现的哈希表 */ + struct arrayHashMap { + pair *buckets[HASH_MAP_DEFAULT_SIZE]; + }; + + typedef struct arrayHashMap arrayHashMap; + + /* 哈希表初始化函数 */ + arrayHashMap *newArrayHashMap() { + arrayHashMap *map = malloc(sizeof(arrayHashMap)); + return map; + } + + /* 添加操作 */ + void put(arrayHashMap *d, 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); + d->buckets[index] = pair; + } + + /* 删除操作 */ + void removeItem(arrayHashMap *d, const int key) { + int index = hashFunc(key); + free(d->buckets[index]->val); + free(d->buckets[index]); + d->buckets[index] = NULL; + } + + /* 获取所有键值对 */ + void pairSet(arrayHashMap *d, mapSet *set) { + pair *entries; + int i = 0, index = 0; + int total = 0; + + /* 统计有效键值对数量 */ + for (i = 0; i < HASH_MAP_DEFAULT_SIZE; i++) { + if (d->buckets[i] != NULL) { + total++; + } + } + + entries = malloc(sizeof(pair) * total); + for (i = 0; i < HASH_MAP_DEFAULT_SIZE; i++) { + if (d->buckets[i] != NULL) { + entries[index].key = d->buckets[i]->key; + entries[index].val = malloc(strlen(d->buckets[i]->val + 1)); + strcpy(entries[index].val, d->buckets[i]->val); + index++; + } + } + + set->set = entries; + set->len = total; + } + + /* 获取所有键 */ + void keySet(arrayHashMap *d, mapSet *set) { + int *keys; + int i = 0, index = 0; + int total = 0; + + /* 统计有效键值对数量 */ + for (i = 0; i < HASH_MAP_DEFAULT_SIZE; i++) { + if (d->buckets[i] != NULL) { + total++; + } + } + + keys = malloc(total * sizeof(int)); + for (i = 0; i < HASH_MAP_DEFAULT_SIZE; i++) { + if (d->buckets[i] != NULL) { + keys[index] = d->buckets[i]->key; + index++; + } + } + + set->set = keys; + set->len = total; + } + + /* 获取所有值 */ + void valueSet(arrayHashMap *d, mapSet *set) { + char **vals; + int i = 0, index = 0; + int total = 0; + + /* 统计有效键值对数量 */ + for (i = 0; i < HASH_MAP_DEFAULT_SIZE; i++) { + if (d->buckets[i] != NULL) { + total++; + } + } + + vals = malloc(total * sizeof(char *)); + for (i = 0; i < HASH_MAP_DEFAULT_SIZE; i++) { + if (d->buckets[i] != NULL) { + vals[index] = d->buckets[i]->val; + index++; + } + } + + set->set = vals; + set->len = total; + } + + /* 打印哈希表 */ + void print(arrayHashMap *d) { + int i; + mapSet set; + pairSet(d, &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); + } ``` === "Zig" ```zig title="array_hash_map.zig" - [class]{Pair}-[func]{} + // 键值对 + const Pair = struct { + key: usize = undefined, + val: []const u8 = undefined, - [class]{ArrayHashMap}-[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}); + } + } + }; + } ``` ## 6.1.3   哈希冲突与扩容 diff --git a/zh/chapter_heap/build_heap.md b/zh/chapter_heap/build_heap.md index ecad0a1cf..aed127200 100644 --- a/zh/chapter_heap/build_heap.md +++ b/zh/chapter_heap/build_heap.md @@ -30,73 +30,176 @@ comments: true === "Python" ```python title="my_heap.py" - [class]{MaxHeap}-[func]{__init__} + def __init__(self, nums: list[int]): + """构造方法,根据输入列表建堆""" + # 将列表元素原封不动添加进堆 + self.max_heap = nums + # 堆化除叶节点以外的其他所有节点 + for i in range(self.parent(self.size() - 1), -1, -1): + self.sift_down(i) ``` === "C++" ```cpp title="my_heap.cpp" - [class]{MaxHeap}-[func]{MaxHeap} + /* 构造方法,根据输入列表建堆 */ + MaxHeap(vector nums) { + // 将列表元素原封不动添加进堆 + maxHeap = nums; + // 堆化除叶节点以外的其他所有节点 + for (int i = parent(size() - 1); i >= 0; i--) { + siftDown(i); + } + } ``` === "Java" ```java title="my_heap.java" - [class]{MaxHeap}-[func]{MaxHeap} + /* 构造方法,根据输入列表建堆 */ + MaxHeap(List nums) { + // 将列表元素原封不动添加进堆 + maxHeap = new ArrayList<>(nums); + // 堆化除叶节点以外的其他所有节点 + for (int i = parent(size() - 1); i >= 0; i--) { + siftDown(i); + } + } ``` === "C#" ```csharp title="my_heap.cs" - [class]{MaxHeap}-[func]{MaxHeap} + /* 构造函数,根据输入列表建堆 */ + MaxHeap(IEnumerable nums) { + // 将列表元素原封不动添加进堆 + maxHeap = new List(nums); + // 堆化除叶节点以外的其他所有节点 + var size = parent(this.size() - 1); + for (int i = size; i >= 0; i--) { + siftDown(i); + } + } ``` === "Go" ```go title="my_heap.go" - [class]{maxHeap}-[func]{newMaxHeap} + /* 构造函数,根据切片建堆 */ + 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 + } ``` === "Swift" ```swift title="my_heap.swift" - [class]{MaxHeap}-[func]{init} + /* 构造方法,根据输入列表建堆 */ + init(nums: [Int]) { + // 将列表元素原封不动添加进堆 + maxHeap = nums + // 堆化除叶节点以外的其他所有节点 + for i in stride(from: parent(i: size() - 1), through: 0, by: -1) { + siftDown(i: i) + } + } ``` === "JS" ```javascript title="my_heap.js" - [class]{MaxHeap}-[func]{constructor} + /* 构造方法,建立空堆或根据输入列表建堆 */ + constructor(nums) { + // 将列表元素原封不动添加进堆 + this.#maxHeap = nums === undefined ? [] : [...nums]; + // 堆化除叶节点以外的其他所有节点 + for (let i = this.#parent(this.size() - 1); i >= 0; i--) { + this.#siftDown(i); + } + } ``` === "TS" ```typescript title="my_heap.ts" - [class]{MaxHeap}-[func]{constructor} + /* 构造方法,建立空堆或根据输入列表建堆 */ + constructor(nums?: number[]) { + // 将列表元素原封不动添加进堆 + this.maxHeap = nums === undefined ? [] : [...nums]; + // 堆化除叶节点以外的其他所有节点 + for (let i = this.parent(this.size() - 1); i >= 0; i--) { + this.siftDown(i); + } + } ``` === "Dart" ```dart title="my_heap.dart" - [class]{MaxHeap}-[func]{MaxHeap} + /* 构造方法,根据输入列表建堆 */ + MaxHeap(List nums) { + // 将列表元素原封不动添加进堆 + _maxHeap = nums; + // 堆化除叶节点以外的其他所有节点 + for (int i = _parent(size() - 1); i >= 0; i--) { + siftDown(i); + } + } ``` === "Rust" ```rust title="my_heap.rs" - [class]{MaxHeap}-[func]{new} + /* 构造方法,根据输入列表建堆 */ + 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 + } ``` === "C" ```c title="my_heap.c" - [class]{maxHeap}-[func]{newMaxHeap} + /* 构造函数,根据切片建堆 */ + maxHeap *newMaxHeap(int nums[], int size) { + // 所有元素入堆 + maxHeap *h = (maxHeap *)malloc(sizeof(maxHeap)); + h->size = size; + memcpy(h->data, nums, size * sizeof(int)); + for (int i = parent(h, size - 1); i >= 0; i--) { + // 堆化除叶节点以外的其他所有节点 + siftDown(h, i); + } + return h; + } ``` === "Zig" ```zig title="my_heap.zig" - [class]{MaxHeap}-[func]{init} + // 构造方法,根据输入列表建堆 + 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); + } + } ``` ## 8.2.3   复杂度分析 diff --git a/zh/chapter_heap/heap.md b/zh/chapter_heap/heap.md index 7e3b62265..4dfca5035 100644 --- a/zh/chapter_heap/heap.md +++ b/zh/chapter_heap/heap.md @@ -376,121 +376,228 @@ comments: true === "Python" ```python title="my_heap.py" - [class]{MaxHeap}-[func]{left} + def left(self, i: int) -> int: + """获取左子节点索引""" + return 2 * i + 1 - [class]{MaxHeap}-[func]{right} + def right(self, i: int) -> int: + """获取右子节点索引""" + return 2 * i + 2 - [class]{MaxHeap}-[func]{parent} + def parent(self, i: int) -> int: + """获取父节点索引""" + return (i - 1) // 2 # 向下整除 ``` === "C++" ```cpp title="my_heap.cpp" - [class]{MaxHeap}-[func]{left} + /* 获取左子节点索引 */ + int left(int i) { + return 2 * i + 1; + } - [class]{MaxHeap}-[func]{right} + /* 获取右子节点索引 */ + int right(int i) { + return 2 * i + 2; + } - [class]{MaxHeap}-[func]{parent} + /* 获取父节点索引 */ + int parent(int i) { + return (i - 1) / 2; // 向下取整 + } ``` === "Java" ```java title="my_heap.java" - [class]{MaxHeap}-[func]{left} + /* 获取左子节点索引 */ + int left(int i) { + return 2 * i + 1; + } - [class]{MaxHeap}-[func]{right} + /* 获取右子节点索引 */ + int right(int i) { + return 2 * i + 2; + } - [class]{MaxHeap}-[func]{parent} + /* 获取父节点索引 */ + int parent(int i) { + return (i - 1) / 2; // 向下整除 + } ``` === "C#" ```csharp title="my_heap.cs" - [class]{MaxHeap}-[func]{left} + /* 获取左子节点索引 */ + int left(int i) { + return 2 * i + 1; + } - [class]{MaxHeap}-[func]{right} + /* 获取右子节点索引 */ + int right(int i) { + return 2 * i + 2; + } - [class]{MaxHeap}-[func]{parent} + /* 获取父节点索引 */ + int parent(int i) { + return (i - 1) / 2; // 向下整除 + } ``` === "Go" ```go title="my_heap.go" - [class]{maxHeap}-[func]{left} + /* 获取左子节点索引 */ + func (h *maxHeap) left(i int) int { + return 2*i + 1 + } - [class]{maxHeap}-[func]{right} + /* 获取右子节点索引 */ + func (h *maxHeap) right(i int) int { + return 2*i + 2 + } - [class]{maxHeap}-[func]{parent} + /* 获取父节点索引 */ + func (h *maxHeap) parent(i int) int { + // 向下整除 + return (i - 1) / 2 + } ``` === "Swift" ```swift title="my_heap.swift" - [class]{MaxHeap}-[func]{left} + /* 获取左子节点索引 */ + func left(i: Int) -> Int { + 2 * i + 1 + } - [class]{MaxHeap}-[func]{right} + /* 获取右子节点索引 */ + func right(i: Int) -> Int { + 2 * i + 2 + } - [class]{MaxHeap}-[func]{parent} + /* 获取父节点索引 */ + func parent(i: Int) -> Int { + (i - 1) / 2 // 向下整除 + } ``` === "JS" ```javascript title="my_heap.js" - [class]{MaxHeap}-[func]{#left} + /* 获取左子节点索引 */ + #left(i) { + return 2 * i + 1; + } - [class]{MaxHeap}-[func]{#right} + /* 获取右子节点索引 */ + #right(i) { + return 2 * i + 2; + } - [class]{MaxHeap}-[func]{#parent} + /* 获取父节点索引 */ + #parent(i) { + return Math.floor((i - 1) / 2); // 向下整除 + } ``` === "TS" ```typescript title="my_heap.ts" - [class]{MaxHeap}-[func]{left} + /* 获取左子节点索引 */ + left(i: number): number { + return 2 * i + 1; + } - [class]{MaxHeap}-[func]{right} + /* 获取右子节点索引 */ + right(i: number): number { + return 2 * i + 2; + } - [class]{MaxHeap}-[func]{parent} + /* 获取父节点索引 */ + parent(i: number): number { + return Math.floor((i - 1) / 2); // 向下整除 + } ``` === "Dart" ```dart title="my_heap.dart" - [class]{MaxHeap}-[func]{_left} + /* 获取左子节点索引 */ + int _left(int i) { + return 2 * i + 1; + } - [class]{MaxHeap}-[func]{_right} + /* 获取右子节点索引 */ + int _right(int i) { + return 2 * i + 2; + } - [class]{MaxHeap}-[func]{_parent} + /* 获取父节点索引 */ + int _parent(int i) { + return (i - 1) ~/ 2; // 向下整除 + } ``` === "Rust" ```rust title="my_heap.rs" - [class]{MaxHeap}-[func]{left} + /* 获取左子节点索引 */ + fn left(i: usize) -> usize { + 2 * i + 1 + } - [class]{MaxHeap}-[func]{right} + /* 获取右子节点索引 */ + fn right(i: usize) -> usize { + 2 * i + 2 + } - [class]{MaxHeap}-[func]{parent} + /* 获取父节点索引 */ + fn parent(i: usize) -> usize { + (i - 1) / 2 // 向下整除 + } ``` === "C" ```c title="my_heap.c" - [class]{maxHeap}-[func]{left} + /* 获取左子节点索引 */ + int left(maxHeap *h, int i) { + return 2 * i + 1; + } - [class]{maxHeap}-[func]{right} + /* 获取右子节点索引 */ + int right(maxHeap *h, int i) { + return 2 * i + 2; + } - [class]{maxHeap}-[func]{parent} + /* 获取父节点索引 */ + int parent(maxHeap *h, int i) { + return (i - 1) / 2; + } ``` === "Zig" ```zig title="my_heap.zig" - [class]{MaxHeap}-[func]{left} + // 获取左子节点索引 + fn left(i: usize) usize { + return 2 * i + 1; + } - [class]{MaxHeap}-[func]{right} + // 获取右子节点索引 + fn right(i: usize) usize { + return 2 * i + 2; + } - [class]{MaxHeap}-[func]{parent} + // 获取父节点索引 + fn parent(i: usize) usize { + // return (i - 1) / 2; // 向下整除 + return @divFloor(i - 1, 2); + } ``` ### 2.   访问堆顶元素 @@ -500,73 +607,108 @@ comments: true === "Python" ```python title="my_heap.py" - [class]{MaxHeap}-[func]{peek} + def peek(self) -> int: + """访问堆顶元素""" + return self.max_heap[0] ``` === "C++" ```cpp title="my_heap.cpp" - [class]{MaxHeap}-[func]{peek} + /* 访问堆顶元素 */ + int peek() { + return maxHeap[0]; + } ``` === "Java" ```java title="my_heap.java" - [class]{MaxHeap}-[func]{peek} + /* 访问堆顶元素 */ + int peek() { + return maxHeap.get(0); + } ``` === "C#" ```csharp title="my_heap.cs" - [class]{MaxHeap}-[func]{peek} + /* 访问堆顶元素 */ + int peek() { + return maxHeap[0]; + } ``` === "Go" ```go title="my_heap.go" - [class]{maxHeap}-[func]{peek} + /* 访问堆顶元素 */ + func (h *maxHeap) peek() any { + return h.data[0] + } ``` === "Swift" ```swift title="my_heap.swift" - [class]{MaxHeap}-[func]{peek} + /* 访问堆顶元素 */ + func peek() -> Int { + maxHeap[0] + } ``` === "JS" ```javascript title="my_heap.js" - [class]{MaxHeap}-[func]{peek} + /* 访问堆顶元素 */ + peek() { + return this.#maxHeap[0]; + } ``` === "TS" ```typescript title="my_heap.ts" - [class]{MaxHeap}-[func]{peek} + /* 访问堆顶元素 */ + peek(): number { + return this.maxHeap[0]; + } ``` === "Dart" ```dart title="my_heap.dart" - [class]{MaxHeap}-[func]{peek} + /* 访问堆顶元素 */ + int peek() { + return _maxHeap[0]; + } ``` === "Rust" ```rust title="my_heap.rs" - [class]{MaxHeap}-[func]{peek} + /* 访问堆顶元素 */ + fn peek(&self) -> Option { + self.max_heap.first().copied() + } ``` === "C" ```c title="my_heap.c" - [class]{maxHeap}-[func]{peek} + /* 访问堆顶元素 */ + int peek(maxHeap *h) { + return h->data[0]; + } ``` === "Zig" ```zig title="my_heap.zig" - [class]{MaxHeap}-[func]{peek} + // 访问堆顶元素 + fn peek(self: *Self) T { + return self.max_heap.?.items[0]; + } ``` ### 3.   元素入堆 @@ -609,97 +751,337 @@ comments: true === "Python" ```python title="my_heap.py" - [class]{MaxHeap}-[func]{push} + def push(self, val: int): + """元素入堆""" + # 添加节点 + self.max_heap.append(val) + # 从底至顶堆化 + self.sift_up(self.size() - 1) - [class]{MaxHeap}-[func]{sift_up} + def sift_up(self, i: int): + """从节点 i 开始,从底至顶堆化""" + while True: + # 获取节点 i 的父节点 + p = self.parent(i) + # 当“越过根节点”或“节点无须修复”时,结束堆化 + if p < 0 or self.max_heap[i] <= self.max_heap[p]: + break + # 交换两节点 + self.swap(i, p) + # 循环向上堆化 + i = p ``` === "C++" ```cpp title="my_heap.cpp" - [class]{MaxHeap}-[func]{push} + /* 元素入堆 */ + void push(int val) { + // 添加节点 + maxHeap.push_back(val); + // 从底至顶堆化 + siftUp(size() - 1); + } - [class]{MaxHeap}-[func]{siftUp} + /* 从节点 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; + } + } ``` === "Java" ```java title="my_heap.java" - [class]{MaxHeap}-[func]{push} + /* 元素入堆 */ + void push(int val) { + // 添加节点 + maxHeap.add(val); + // 从底至顶堆化 + siftUp(size() - 1); + } - [class]{MaxHeap}-[func]{siftUp} + /* 从节点 i 开始,从底至顶堆化 */ + void siftUp(int i) { + while (true) { + // 获取节点 i 的父节点 + int p = parent(i); + // 当“越过根节点”或“节点无须修复”时,结束堆化 + if (p < 0 || maxHeap.get(i) <= maxHeap.get(p)) + break; + // 交换两节点 + swap(i, p); + // 循环向上堆化 + i = p; + } + } ``` === "C#" ```csharp title="my_heap.cs" - [class]{MaxHeap}-[func]{push} + /* 元素入堆 */ + void push(int val) { + // 添加节点 + maxHeap.Add(val); + // 从底至顶堆化 + siftUp(size() - 1); + } - [class]{MaxHeap}-[func]{siftUp} + /* 从节点 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; + } + } ``` === "Go" ```go title="my_heap.go" - [class]{maxHeap}-[func]{push} + /* 元素入堆 */ + func (h *maxHeap) push(val any) { + // 添加节点 + h.data = append(h.data, val) + // 从底至顶堆化 + h.siftUp(len(h.data) - 1) + } - [class]{maxHeap}-[func]{siftUp} + /* 从节点 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 + } + } ``` === "Swift" ```swift title="my_heap.swift" - [class]{MaxHeap}-[func]{push} + /* 元素入堆 */ + func push(val: Int) { + // 添加节点 + maxHeap.append(val) + // 从底至顶堆化 + siftUp(i: size() - 1) + } - [class]{MaxHeap}-[func]{siftUp} + /* 从节点 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 + } + } ``` === "JS" ```javascript title="my_heap.js" - [class]{MaxHeap}-[func]{push} + /* 元素入堆 */ + push(val) { + // 添加节点 + this.#maxHeap.push(val); + // 从底至顶堆化 + this.#siftUp(this.size() - 1); + } - [class]{MaxHeap}-[func]{#siftUp} + /* 从节点 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; + } + } ``` === "TS" ```typescript title="my_heap.ts" - [class]{MaxHeap}-[func]{push} + /* 元素入堆 */ + push(val: number): void { + // 添加节点 + this.maxHeap.push(val); + // 从底至顶堆化 + this.siftUp(this.size() - 1); + } - [class]{MaxHeap}-[func]{siftUp} + /* 从节点 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; + } + } ``` === "Dart" ```dart title="my_heap.dart" - [class]{MaxHeap}-[func]{push} + /* 元素入堆 */ + void push(int val) { + // 添加节点 + _maxHeap.add(val); + // 从底至顶堆化 + siftUp(size() - 1); + } - [class]{MaxHeap}-[func]{siftUp} + /* 从节点 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; + } + } ``` === "Rust" ```rust title="my_heap.rs" - [class]{MaxHeap}-[func]{push} + /* 元素入堆 */ + fn push(&mut self, val: i32) { + // 添加节点 + self.max_heap.push(val); + // 从底至顶堆化 + self.sift_up(self.size() - 1); + } - [class]{MaxHeap}-[func]{sift_up} + /* 从节点 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; + } + } ``` === "C" ```c title="my_heap.c" - [class]{maxHeap}-[func]{push} + /* 元素入堆 */ + void push(maxHeap *h, int val) { + // 默认情况下,不应该添加这么多节点 + if (h->size == MAX_SIZE) { + printf("heap is full!"); + return; + } + // 添加节点 + h->data[h->size] = val; + h->size++; - [class]{maxHeap}-[func]{siftUp} + // 从底至顶堆化 + siftUp(h, h->size - 1); + } + + /* 从节点 i 开始,从底至顶堆化 */ + void siftUp(maxHeap *h, int i) { + while (true) { + // 获取节点 i 的父节点 + int p = parent(h, i); + // 当“越过根节点”或“节点无须修复”时,结束堆化 + if (p < 0 || h->data[i] <= h->data[p]) { + break; + } + // 交换两节点 + swap(h, i, p); + // 循环向上堆化 + i = p; + } + } ``` === "Zig" ```zig title="my_heap.zig" - [class]{MaxHeap}-[func]{push} + // 元素入堆 + fn push(self: *Self, val: T) !void { + // 添加节点 + try self.max_heap.?.append(val); + // 从底至顶堆化 + try self.siftUp(self.size() - 1); + } - [class]{MaxHeap}-[func]{siftUp} + // 从节点 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; + } + } ``` ### 4.   堆顶元素出堆 @@ -749,97 +1131,472 @@ comments: true === "Python" ```python title="my_heap.py" - [class]{MaxHeap}-[func]{pop} + def pop(self) -> int: + """元素出堆""" + # 判空处理 + if self.is_empty(): + raise IndexError("堆为空") + # 交换根节点与最右叶节点(即交换首元素与尾元素) + self.swap(0, self.size() - 1) + # 删除节点 + val = self.max_heap.pop() + # 从顶至底堆化 + self.sift_down(0) + # 返回堆顶元素 + return val - [class]{MaxHeap}-[func]{sift_down} + def sift_down(self, i: int): + """从节点 i 开始,从顶至底堆化""" + while True: + # 判断节点 i, l, r 中值最大的节点,记为 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 ma == i: + break + # 交换两节点 + self.swap(i, ma) + # 循环向下堆化 + i = ma ``` === "C++" ```cpp title="my_heap.cpp" - [class]{MaxHeap}-[func]{pop} + /* 元素出堆 */ + void pop() { + // 判空处理 + if (isEmpty()) { + throw out_of_range("堆为空"); + } + // 交换根节点与最右叶节点(即交换首元素与尾元素) + swap(maxHeap[0], maxHeap[size() - 1]); + // 删除节点 + maxHeap.pop_back(); + // 从顶至底堆化 + siftDown(0); + } - [class]{MaxHeap}-[func]{siftDown} + /* 从节点 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; + } + } ``` === "Java" ```java title="my_heap.java" - [class]{MaxHeap}-[func]{pop} + /* 元素出堆 */ + int pop() { + // 判空处理 + if (isEmpty()) + throw new IndexOutOfBoundsException(); + // 交换根节点与最右叶节点(即交换首元素与尾元素) + swap(0, size() - 1); + // 删除节点 + int val = maxHeap.remove(size() - 1); + // 从顶至底堆化 + siftDown(0); + // 返回堆顶元素 + return val; + } - [class]{MaxHeap}-[func]{siftDown} + /* 从节点 i 开始,从顶至底堆化 */ + void siftDown(int i) { + while (true) { + // 判断节点 i, l, r 中值最大的节点,记为 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 (ma == i) + break; + // 交换两节点 + swap(i, ma); + // 循环向下堆化 + i = ma; + } + } ``` === "C#" ```csharp title="my_heap.cs" - [class]{MaxHeap}-[func]{pop} + /* 元素出堆 */ + 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]{siftDown} + /* 从节点 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; + } + } ``` === "Go" ```go title="my_heap.go" - [class]{maxHeap}-[func]{pop} + /* 元素出堆 */ + 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]{siftDown} + // 返回堆顶元素 + 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 + } + } ``` === "Swift" ```swift title="my_heap.swift" - [class]{MaxHeap}-[func]{pop} + /* 元素出堆 */ + 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]{siftDown} + /* 从节点 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 + } + } ``` === "JS" ```javascript title="my_heap.js" - [class]{MaxHeap}-[func]{pop} + /* 元素出堆 */ + 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]{#siftDown} + /* 从节点 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; + } + } ``` === "TS" ```typescript title="my_heap.ts" - [class]{MaxHeap}-[func]{pop} + /* 元素出堆 */ + 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]{siftDown} + /* 从节点 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; + } + } ``` === "Dart" ```dart title="my_heap.dart" - [class]{MaxHeap}-[func]{pop} + /* 元素出堆 */ + int pop() { + // 判空处理 + if (isEmpty()) throw Exception('堆为空'); + // 交换根节点与最右叶节点(即交换首元素与尾元素) + _swap(0, size() - 1); + // 删除节点 + int val = _maxHeap.removeLast(); + // 从顶至底堆化 + siftDown(0); + // 返回堆顶元素 + return val; + } - [class]{MaxHeap}-[func]{siftDown} + /* 从节点 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; + } + } ``` === "Rust" ```rust title="my_heap.rs" - [class]{MaxHeap}-[func]{pop} + /* 元素出堆 */ + 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.remove(self.size() - 1); + // 从顶至底堆化 + self.sift_down(0); + // 返回堆顶元素 + val + } - [class]{MaxHeap}-[func]{sift_down} + /* 从节点 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; + } + } ``` === "C" ```c title="my_heap.c" - [class]{maxHeap}-[func]{pop} + /* 元素出堆 */ + int pop(maxHeap *h) { + // 判空处理 + if (isEmpty(h)) { + printf("heap is empty!"); + return INT_MAX; + } + // 交换根节点与最右叶节点(即交换首元素与尾元素) + swap(h, 0, size(h) - 1); + // 删除节点 + int val = h->data[h->size - 1]; + h->size--; + // 从顶至底堆化 + siftDown(h, 0); - [class]{maxHeap}-[func]{siftDown} + // 返回堆顶元素 + return val; + } + + /* 从节点 i 开始,从顶至底堆化 */ + void siftDown(maxHeap *h, int i) { + while (true) { + // 判断节点 i, l, r 中值最大的节点,记为 max + int l = left(h, i); + int r = right(h, i); + int max = i; + if (l < size(h) && h->data[l] > h->data[max]) { + max = l; + } + if (r < size(h) && h->data[r] > h->data[max]) { + max = r; + } + // 若节点 i 最大或索引 l, r 越界,则无须继续堆化,跳出 + if (max == i) { + break; + } + // 交换两节点 + swap(h, i, max); + // 循环向下堆化 + i = max; + } + } ``` === "Zig" ```zig title="my_heap.zig" - [class]{MaxHeap}-[func]{pop} + // 元素出堆 + 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]{siftDown} + // 从节点 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; + } + } ``` ## 8.1.3   堆常见应用 diff --git a/zh/chapter_heap/top_k.md b/zh/chapter_heap/top_k.md index 57f487055..f03169eb9 100644 --- a/zh/chapter_heap/top_k.md +++ b/zh/chapter_heap/top_k.md @@ -79,61 +79,219 @@ comments: true === "Python" ```python title="top_k.py" - [class]{}-[func]{top_k_heap} + def top_k_heap(nums: list[int], k: int) -> list[int]: + """基于堆查找数组中最大的 k 个元素""" + heap = [] + # 将数组的前 k 个元素入堆 + for i in range(k): + heapq.heappush(heap, nums[i]) + # 从第 k+1 个元素开始,保持堆的长度为 k + for i in range(k, len(nums)): + # 若当前元素大于堆顶元素,则将堆顶元素出堆、当前元素入堆 + if nums[i] > heap[0]: + heapq.heappop(heap) + heapq.heappush(heap, nums[i]) + return heap ``` === "C++" ```cpp title="top_k.cpp" - [class]{}-[func]{topKHeap} + /* 基于堆查找数组中最大的 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; + } ``` === "Java" ```java title="top_k.java" - [class]{top_k}-[func]{topKHeap} + /* 基于堆查找数组中最大的 k 个元素 */ + Queue topKHeap(int[] nums, int k) { + Queue heap = new PriorityQueue(); + // 将数组的前 k 个元素入堆 + for (int i = 0; i < k; i++) { + heap.offer(nums[i]); + } + // 从第 k+1 个元素开始,保持堆的长度为 k + for (int i = k; i < nums.length; i++) { + // 若当前元素大于堆顶元素,则将堆顶元素出堆、当前元素入堆 + if (nums[i] > heap.peek()) { + heap.poll(); + heap.offer(nums[i]); + } + } + return heap; + } ``` === "C#" ```csharp title="top_k.cs" - [class]{top_k}-[func]{topKHeap} + /* 基于堆查找数组中最大的 k 个元素 */ + PriorityQueue topKHeap(int[] nums, int k) { + PriorityQueue heap = new PriorityQueue(); + // 将数组的前 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; + } ``` === "Go" ```go title="top_k.go" - [class]{}-[func]{topKHeap} + /* 基于堆查找数组中最大的 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 + } ``` === "Swift" ```swift title="top_k.swift" - [class]{}-[func]{topKHeap} + /* 基于堆查找数组中最大的 k 个元素 */ + func topKHeap(nums: [Int], k: Int) -> [Int] { + // 将数组的前 k 个元素入堆 + var heap = Array(nums.prefix(k)) + // 从第 k+1 个元素开始,保持堆的长度为 k + for i in stride(from: k, to: nums.count, by: 1) { + // 若当前元素大于堆顶元素,则将堆顶元素出堆、当前元素入堆 + if nums[i] > heap.first! { + heap.removeFirst() + heap.insert(nums[i], at: 0) + } + } + return heap + } ``` === "JS" ```javascript title="top_k.js" - [class]{}-[func]{topKHeap} + /* 基于堆查找数组中最大的 k 个元素 */ + function topKHeap(nums, k) { + // 使用大顶堆 MaxHeap ,对数组 nums 取相反数 + const invertedNums = nums.map((num) => -num); + // 将数组的前 k 个元素入堆 + const heap = new MaxHeap(invertedNums.slice(0, k)); + // 从第 k+1 个元素开始,保持堆的长度为 k + for (let i = k; i < invertedNums.length; i++) { + // 若当前元素小于堆顶元素,则将堆顶元素出堆、当前元素入堆 + if (invertedNums[i] < heap.peek()) { + heap.pop(); + heap.push(invertedNums[i]); + } + } + // 取出堆中元素 + const maxHeap = heap.getMaxHeap(); + // 对堆中元素取相反数 + const invertedMaxHeap = maxHeap.map((num) => -num); + return invertedMaxHeap; + } ``` === "TS" ```typescript title="top_k.ts" - [class]{}-[func]{topKHeap} + /* 基于堆查找数组中最大的 k 个元素 */ + function topKHeap(nums: number[], k: number): number[] { + // 将堆中所有元素取反,从而用大顶堆来模拟小顶堆 + const invertedNums = nums.map((num) => -num); + // 将数组的前 k 个元素入堆 + const heap = new MaxHeap(invertedNums.slice(0, k)); + // 从第 k+1 个元素开始,保持堆的长度为 k + for (let i = k; i < invertedNums.length; i++) { + // 若当前元素小于堆顶元素,则将堆顶元素出堆、当前元素入堆 + if (invertedNums[i] < heap.peek()) { + heap.pop(); + heap.push(invertedNums[i]); + } + } + // 取出堆中元素 + const maxHeap = heap.getMaxHeap(); + // 对堆中元素取相反数 + const invertedMaxHeap = maxHeap.map((num) => -num); + return invertedMaxHeap; + } ``` === "Dart" ```dart title="top_k.dart" - [class]{}-[func]{topKHeap} + /* 基于堆查找数组中最大的 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; + } ``` === "Rust" ```rust title="top_k.rs" - [class]{}-[func]{top_k_heap} + /* 基于堆查找数组中最大的 k 个元素 */ + fn top_k_heap(nums: Vec, k: usize) -> BinaryHeap> { + // Rust 的 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 + } ``` === "C" diff --git a/zh/chapter_searching/binary_search.md b/zh/chapter_searching/binary_search.md index 4f2a3d863..a41018976 100755 --- a/zh/chapter_searching/binary_search.md +++ b/zh/chapter_searching/binary_search.md @@ -54,73 +54,282 @@ comments: true === "Python" ```python title="binary_search.py" - [class]{}-[func]{binary_search} + def binary_search(nums: list[int], target: int) -> int: + """二分查找(双闭区间)""" + # 初始化双闭区间 [0, n-1] ,即 i, j 分别指向数组首元素、尾元素 + i, j = 0, len(nums) - 1 + # 循环,当搜索区间为空时跳出(当 i > j 时为空) + while i <= j: + # 理论上 Python 的数字可以无限大(取决于内存大小),无须考虑大数越界问题 + m = (i + j) // 2 # 计算中点索引 m + if nums[m] < target: + i = m + 1 # 此情况说明 target 在区间 [m+1, j] 中 + elif nums[m] > target: + j = m - 1 # 此情况说明 target 在区间 [i, m-1] 中 + else: + return m # 找到目标元素,返回其索引 + return -1 # 未找到目标元素,返回 -1 ``` === "C++" ```cpp title="binary_search.cpp" - [class]{}-[func]{binarySearch} + /* 二分查找(双闭区间) */ + 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; + } ``` === "Java" ```java title="binary_search.java" - [class]{binary_search}-[func]{binarySearch} + /* 二分查找(双闭区间) */ + 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; + } ``` === "C#" ```csharp title="binary_search.cs" - [class]{binary_search}-[func]{binarySearch} + /* 二分查找(双闭区间) */ + 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; + } ``` === "Go" ```go title="binary_search.go" - [class]{}-[func]{binarySearch} + /* 二分查找(双闭区间) */ + 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 + } ``` === "Swift" ```swift title="binary_search.swift" - [class]{}-[func]{binarySearch} + /* 二分查找(双闭区间) */ + func binarySearch(nums: [Int], target: Int) -> Int { + // 初始化双闭区间 [0, n-1] ,即 i, j 分别指向数组首元素、尾元素 + var i = 0 + var j = nums.count - 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 + } ``` === "JS" ```javascript title="binary_search.js" - [class]{}-[func]{binarySearch} + /* 二分查找(双闭区间) */ + 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; + } ``` === "TS" ```typescript title="binary_search.ts" - [class]{}-[func]{binarySearch} + /* 二分查找(双闭区间) */ + 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 + } ``` === "Dart" ```dart title="binary_search.dart" - [class]{}-[func]{binarySearch} + /* 二分查找(双闭区间) */ + 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; + } ``` === "Rust" ```rust title="binary_search.rs" - [class]{}-[func]{binary_search} + /* 二分查找(双闭区间) */ + 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; + } ``` === "C" ```c title="binary_search.c" - [class]{}-[func]{binarySearch} + /* 二分查找(双闭区间) */ + 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; + } ``` === "Zig" ```zig title="binary_search.zig" - [class]{}-[func]{binarySearch} + // 二分查找(双闭区间) + 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; + } ``` **时间复杂度 $O(\log n)$** :在二分循环中,区间每轮缩小一半,循环次数为 $\log_2 n$ 。 @@ -136,73 +345,282 @@ comments: true === "Python" ```python title="binary_search.py" - [class]{}-[func]{binary_search_lcro} + def binary_search_lcro(nums: list[int], target: int) -> int: + """二分查找(左闭右开)""" + # 初始化左闭右开 [0, n) ,即 i, j 分别指向数组首元素、尾元素+1 + i, j = 0, len(nums) + # 循环,当搜索区间为空时跳出(当 i = j 时为空) + while i < j: + m = (i + j) // 2 # 计算中点索引 m + if nums[m] < target: + i = m + 1 # 此情况说明 target 在区间 [m+1, j) 中 + elif nums[m] > target: + j = m # 此情况说明 target 在区间 [i, m) 中 + else: + return m # 找到目标元素,返回其索引 + return -1 # 未找到目标元素,返回 -1 ``` === "C++" ```cpp title="binary_search.cpp" - [class]{}-[func]{binarySearchLCRO} + /* 二分查找(左闭右开) */ + 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; + } ``` === "Java" ```java title="binary_search.java" - [class]{binary_search}-[func]{binarySearchLCRO} + /* 二分查找(左闭右开) */ + 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; + } ``` === "C#" ```csharp title="binary_search.cs" - [class]{binary_search}-[func]{binarySearchLCRO} + /* 二分查找(左闭右开) */ + 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; + } ``` === "Go" ```go title="binary_search.go" - [class]{}-[func]{binarySearchLCRO} + /* 二分查找(左闭右开) */ + 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 + } ``` === "Swift" ```swift title="binary_search.swift" - [class]{}-[func]{binarySearchLCRO} + /* 二分查找(左闭右开) */ + func binarySearchLCRO(nums: [Int], target: Int) -> Int { + // 初始化左闭右开 [0, n) ,即 i, j 分别指向数组首元素、尾元素+1 + var i = 0 + var j = nums.count + // 循环,当搜索区间为空时跳出(当 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 + } ``` === "JS" ```javascript title="binary_search.js" - [class]{}-[func]{binarySearchLCRO} + /* 二分查找(左闭右开) */ + 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; + } ``` === "TS" ```typescript title="binary_search.ts" - [class]{}-[func]{binarySearchLCRO} + /* 二分查找(左闭右开) */ + 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 + } ``` === "Dart" ```dart title="binary_search.dart" - [class]{}-[func]{binarySearchLCRO} + /* 二分查找(左闭右开区间) */ + 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; + } ``` === "Rust" ```rust title="binary_search.rs" - [class]{}-[func]{binary_search_lcro} + /* 二分查找(左闭右开) */ + 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 - 1; + } else { // 找到目标元素,返回其索引 + return m; + } + } + // 未找到目标元素,返回 -1 + return -1; + } ``` === "C" ```c title="binary_search.c" - [class]{}-[func]{binarySearchLCRO} + /* 二分查找(左闭右开) */ + 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; + } ``` === "Zig" ```zig title="binary_search.zig" - [class]{}-[func]{binarySearchLCRO} + // 二分查找(左闭右开) + 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; + } ``` 如图 10-3 所示,在两种区间表示下,二分查找算法的初始化、循环条件和缩小区间操作皆有所不同。 diff --git a/zh/chapter_searching/binary_search_edge.md b/zh/chapter_searching/binary_search_edge.md index a553434f6..9c63d81fa 100644 --- a/zh/chapter_searching/binary_search_edge.md +++ b/zh/chapter_searching/binary_search_edge.md @@ -22,67 +22,175 @@ comments: true === "Python" ```python title="binary_search_edge.py" - [class]{}-[func]{binary_search_left_edge} + def binary_search_left_edge(nums: list[int], target: int) -> int: + """二分查找最左一个 target""" + # 等价于查找 target 的插入点 + i = binary_search_insertion(nums, target) + # 未找到 target ,返回 -1 + if i == len(nums) or nums[i] != target: + return -1 + # 找到 target ,返回索引 i + return i ``` === "C++" ```cpp title="binary_search_edge.cpp" - [class]{}-[func]{binarySearchLeftEdge} + /* 二分查找最左一个 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; + } ``` === "Java" ```java title="binary_search_edge.java" - [class]{binary_search_edge}-[func]{binarySearchLeftEdge} + /* 二分查找最左一个 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; + } ``` === "C#" ```csharp title="binary_search_edge.cs" - [class]{binary_search_edge}-[func]{binarySearchLeftEdge} + /* 二分查找最左一个 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; + } ``` === "Go" ```go title="binary_search_edge.go" - [class]{}-[func]{binarySearchLeftEdge} + /* 二分查找最左一个 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 + } ``` === "Swift" ```swift title="binary_search_edge.swift" - [class]{}-[func]{binarySearchLeftEdge} + /* 二分查找最左一个 target */ + func binarySearchLeftEdge(nums: [Int], target: Int) -> Int { + // 等价于查找 target 的插入点 + let i = binarySearchInsertion(nums: nums, target: target) + // 未找到 target ,返回 -1 + if i == nums.count || nums[i] != target { + return -1 + } + // 找到 target ,返回索引 i + return i + } ``` === "JS" ```javascript title="binary_search_edge.js" - [class]{}-[func]{binarySearchLeftEdge} + /* 二分查找最左一个 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; + } ``` === "TS" ```typescript title="binary_search_edge.ts" - [class]{}-[func]{binarySearchLeftEdge} + /* 二分查找最左一个 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; + } ``` === "Dart" ```dart title="binary_search_edge.dart" - [class]{}-[func]{binarySearchLeftEdge} + /* 二分查找最左一个 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; + } ``` === "Rust" ```rust title="binary_search_edge.rs" - [class]{}-[func]{binary_search_left_edge} + /* 二分查找最左一个 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 + } ``` === "C" ```c title="binary_search_edge.c" - [class]{}-[func]{binarySearchLeftEdge} + /* 二分查找最左一个 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; + } ``` === "Zig" @@ -112,67 +220,197 @@ comments: true === "Python" ```python title="binary_search_edge.py" - [class]{}-[func]{binary_search_right_edge} + def binary_search_right_edge(nums: list[int], target: int) -> int: + """二分查找最右一个 target""" + # 转化为查找最左一个 target + 1 + i = binary_search_insertion(nums, target + 1) + # j 指向最右一个 target ,i 指向首个大于 target 的元素 + j = i - 1 + # 未找到 target ,返回 -1 + if j == -1 or nums[j] != target: + return -1 + # 找到 target ,返回索引 j + return j ``` === "C++" ```cpp title="binary_search_edge.cpp" - [class]{}-[func]{binarySearchRightEdge} + /* 二分查找最右一个 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; + } ``` === "Java" ```java title="binary_search_edge.java" - [class]{binary_search_edge}-[func]{binarySearchRightEdge} + /* 二分查找最右一个 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; + } ``` === "C#" ```csharp title="binary_search_edge.cs" - [class]{binary_search_edge}-[func]{binarySearchRightEdge} + /* 二分查找最右一个 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; + } ``` === "Go" ```go title="binary_search_edge.go" - [class]{}-[func]{binarySearchRightEdge} + /* 二分查找最右一个 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 + } ``` === "Swift" ```swift title="binary_search_edge.swift" - [class]{}-[func]{binarySearchRightEdge} + /* 二分查找最右一个 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 + } ``` === "JS" ```javascript title="binary_search_edge.js" - [class]{}-[func]{binarySearchRightEdge} + /* 二分查找最右一个 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; + } ``` === "TS" ```typescript title="binary_search_edge.ts" - [class]{}-[func]{binarySearchRightEdge} + /* 二分查找最右一个 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; + } ``` === "Dart" ```dart title="binary_search_edge.dart" - [class]{}-[func]{binarySearchRightEdge} + /* 二分查找最右一个 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; + } ``` === "Rust" ```rust title="binary_search_edge.rs" - [class]{}-[func]{binary_search_right_edge} + /* 二分查找最右一个 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 + } ``` === "C" ```c title="binary_search_edge.c" - [class]{}-[func]{binarySearchRightEdge} + /* 二分查找最右一个 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; + } ``` === "Zig" diff --git a/zh/chapter_searching/binary_search_insertion.md b/zh/chapter_searching/binary_search_insertion.md index 233522c06..8ec04f5fe 100644 --- a/zh/chapter_searching/binary_search_insertion.md +++ b/zh/chapter_searching/binary_search_insertion.md @@ -31,67 +31,239 @@ comments: true === "Python" ```python title="binary_search_insertion.py" - [class]{}-[func]{binary_search_insertion_simple} + def binary_search_insertion_simple(nums: list[int], target: int) -> int: + """二分查找插入点(无重复元素)""" + i, j = 0, len(nums) - 1 # 初始化双闭区间 [0, n-1] + while i <= j: + m = (i + j) // 2 # 计算中点索引 m + if nums[m] < target: + i = m + 1 # target 在区间 [m+1, j] 中 + elif nums[m] > target: + j = m - 1 # target 在区间 [i, m-1] 中 + else: + return m # 找到 target ,返回插入点 m + # 未找到 target ,返回插入点 i + return i ``` === "C++" ```cpp title="binary_search_insertion.cpp" - [class]{}-[func]{binarySearchInsertionSimple} + /* 二分查找插入点(无重复元素) */ + 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; + } ``` === "Java" ```java title="binary_search_insertion.java" - [class]{binary_search_insertion}-[func]{binarySearchInsertionSimple} + /* 二分查找插入点(无重复元素) */ + 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; + } ``` === "C#" ```csharp title="binary_search_insertion.cs" - [class]{binary_search_insertion}-[func]{binarySearchInsertionSimple} + /* 二分查找插入点(无重复元素) */ + 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; + } ``` === "Go" ```go title="binary_search_insertion.go" - [class]{}-[func]{binarySearchInsertionSimple} + /* 二分查找插入点(无重复元素) */ + 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 + } ``` === "Swift" ```swift title="binary_search_insertion.swift" - [class]{}-[func]{binarySearchInsertionSimple} + /* 二分查找插入点(无重复元素) */ + func binarySearchInsertionSimple(nums: [Int], target: Int) -> Int { + var i = 0, j = nums.count - 1 // 初始化双闭区间 [0, n-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 + } ``` === "JS" ```javascript title="binary_search_insertion.js" - [class]{}-[func]{binarySearchInsertionSimple} + /* 二分查找插入点(无重复元素) */ + 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; + } ``` === "TS" ```typescript title="binary_search_insertion.ts" - [class]{}-[func]{binarySearchInsertionSimple} + /* 二分查找插入点(无重复元素) */ + 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; + } ``` === "Dart" ```dart title="binary_search_insertion.dart" - [class]{}-[func]{binarySearchInsertionSimple} + /* 二分查找插入点(无重复元素) */ + 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; + } ``` === "Rust" ```rust title="binary_search_insertion.rs" - [class]{}-[func]{binary_search_insertion} + /* 二分查找插入点(存在重复元素) */ + 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 + } ``` === "C" ```c title="binary_search_insertion.c" - [class]{}-[func]{binarySearchInsertionSimple} + /* 二分查找插入点(无重复元素) */ + 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; + } ``` === "Zig" @@ -159,67 +331,236 @@ comments: true === "Python" ```python title="binary_search_insertion.py" - [class]{}-[func]{binary_search_insertion} + def binary_search_insertion(nums: list[int], target: int) -> int: + """二分查找插入点(存在重复元素)""" + i, j = 0, len(nums) - 1 # 初始化双闭区间 [0, n-1] + while i <= j: + m = (i + j) // 2 # 计算中点索引 m + if nums[m] < target: + i = m + 1 # target 在区间 [m+1, j] 中 + elif nums[m] > target: + j = m - 1 # target 在区间 [i, m-1] 中 + else: + j = m - 1 # 首个小于 target 的元素在区间 [i, m-1] 中 + # 返回插入点 i + return i ``` === "C++" ```cpp title="binary_search_insertion.cpp" - [class]{}-[func]{binarySearchInsertion} + /* 二分查找插入点(存在重复元素) */ + 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; + } ``` === "Java" ```java title="binary_search_insertion.java" - [class]{binary_search_insertion}-[func]{binarySearchInsertion} + /* 二分查找插入点(存在重复元素) */ + 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; + } ``` === "C#" ```csharp title="binary_search_insertion.cs" - [class]{binary_search_insertion}-[func]{binarySearchInsertion} + /* 二分查找插入点(存在重复元素) */ + 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; + } ``` === "Go" ```go title="binary_search_insertion.go" - [class]{}-[func]{binarySearchInsertion} + /* 二分查找插入点(存在重复元素) */ + 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 + } ``` === "Swift" ```swift title="binary_search_insertion.swift" - [class]{}-[func]{binarySearchInsertion} + /* 二分查找插入点(存在重复元素) */ + func binarySearchInsertion(nums: [Int], target: Int) -> Int { + var i = 0, j = nums.count - 1 // 初始化双闭区间 [0, n-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 + } ``` === "JS" ```javascript title="binary_search_insertion.js" - [class]{}-[func]{binarySearchInsertion} + /* 二分查找插入点(存在重复元素) */ + 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; + } ``` === "TS" ```typescript title="binary_search_insertion.ts" - [class]{}-[func]{binarySearchInsertion} + /* 二分查找插入点(存在重复元素) */ + 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; + } ``` === "Dart" ```dart title="binary_search_insertion.dart" - [class]{}-[func]{binarySearchInsertion} + /* 二分查找插入点(存在重复元素) */ + 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; + } ``` === "Rust" ```rust title="binary_search_insertion.rs" - [class]{}-[func]{binary_search_insertion} + /* 二分查找插入点(存在重复元素) */ + 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 + } ``` === "C" ```c title="binary_search_insertion.c" - [class]{}-[func]{binarySearchInsertion} + /* 二分查找插入点(存在重复元素) */ + 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; + } ``` === "Zig" diff --git a/zh/chapter_searching/replace_linear_by_hashing.md b/zh/chapter_searching/replace_linear_by_hashing.md index cf7b9ed51..a9640f436 100755 --- a/zh/chapter_searching/replace_linear_by_hashing.md +++ b/zh/chapter_searching/replace_linear_by_hashing.md @@ -21,73 +21,210 @@ comments: true === "Python" ```python title="two_sum.py" - [class]{}-[func]{two_sum_brute_force} + def two_sum_brute_force(nums: list[int], target: int) -> list[int]: + """方法一:暴力枚举""" + # 两层循环,时间复杂度 O(n^2) + for i in range(len(nums) - 1): + for j in range(i + 1, len(nums)): + if nums[i] + nums[j] == target: + return [i, j] + return [] ``` === "C++" ```cpp title="two_sum.cpp" - [class]{}-[func]{twoSumBruteForce} + /* 方法一:暴力枚举 */ + 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 {}; + } ``` === "Java" ```java title="two_sum.java" - [class]{two_sum}-[func]{twoSumBruteForce} + /* 方法一:暴力枚举 */ + 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 new int[] { i, j }; + } + } + return new int[0]; + } ``` === "C#" ```csharp title="two_sum.cs" - [class]{two_sum}-[func]{twoSumBruteForce} + /* 方法一:暴力枚举 */ + 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 new int[] { i, j }; + } + } + return Array.Empty(); + } ``` === "Go" ```go title="two_sum.go" - [class]{}-[func]{twoSumBruteForce} + /* 方法一:暴力枚举 */ + func twoSumBruteForce(nums []int, target int) []int { + size := len(nums) + // 两层循环,时间复杂度 O(n^2) + for i := 0; i < size-1; i++ { + for j := i + 1; i < size; j++ { + if nums[i]+nums[j] == target { + return []int{i, j} + } + } + } + return nil + } ``` === "Swift" ```swift title="two_sum.swift" - [class]{}-[func]{twoSumBruteForce} + /* 方法一:暴力枚举 */ + 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] + } ``` === "JS" ```javascript title="two_sum.js" - [class]{}-[func]{twoSumBruteForce} + /* 方法一:暴力枚举 */ + 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 []; + } ``` === "TS" ```typescript title="two_sum.ts" - [class]{}-[func]{twoSumBruteForce} + /* 方法一:暴力枚举 */ + 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 []; + } ``` === "Dart" ```dart title="two_sum.dart" - [class]{}-[func]{twoSumBruteForce} + /* 方法一: 暴力枚举 */ + 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]; + } ``` === "Rust" ```rust title="two_sum.rs" - [class]{}-[func]{two_sum_brute_force} + /* 方法一:暴力枚举 */ + 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 + } ``` === "C" ```c title="two_sum.c" - [class]{}-[func]{twoSumBruteForce} + /* 方法一:暴力枚举 */ + 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; + } ``` === "Zig" ```zig title="two_sum.zig" - [class]{}-[func]{twoSumBruteForce} + // 方法一:暴力枚举 + fn twoSumBruteForce(nums: []i32, target: i32) ?[2]i32 { + var size: usize = nums.len; + var i: usize = 0; + // 两层循环,时间复杂度 O(n^2) + while (i < size - 1) : (i += 1) { + var j = i + 1; + while (j < size) : (j += 1) { + if (nums[i] + nums[j] == target) { + return [_]i32{@intCast(i), @intCast(j)}; + } + } + } + return null; + } ``` 此方法的时间复杂度为 $O(n^2)$ ,空间复杂度为 $O(1)$ ,在大数据量下非常耗时。 @@ -115,75 +252,255 @@ comments: true === "Python" ```python title="two_sum.py" - [class]{}-[func]{two_sum_hash_table} + def two_sum_hash_table(nums: list[int], target: int) -> list[int]: + """方法二:辅助哈希表""" + # 辅助哈希表,空间复杂度 O(n) + dic = {} + # 单层循环,时间复杂度 O(n) + for i in range(len(nums)): + if target - nums[i] in dic: + return [dic[target - nums[i]], i] + dic[nums[i]] = i + return [] ``` === "C++" ```cpp title="two_sum.cpp" - [class]{}-[func]{twoSumHashTable} + /* 方法二:辅助哈希表 */ + 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 {}; + } ``` === "Java" ```java title="two_sum.java" - [class]{two_sum}-[func]{twoSumHashTable} + /* 方法二:辅助哈希表 */ + int[] twoSumHashTable(int[] nums, int target) { + int size = nums.length; + // 辅助哈希表,空间复杂度 O(n) + Map dic = new HashMap<>(); + // 单层循环,时间复杂度 O(n) + for (int i = 0; i < size; i++) { + if (dic.containsKey(target - nums[i])) { + return new int[] { dic.get(target - nums[i]), i }; + } + dic.put(nums[i], i); + } + return new int[0]; + } ``` === "C#" ```csharp title="two_sum.cs" - [class]{two_sum}-[func]{twoSumHashTable} + /* 方法二:辅助哈希表 */ + int[] twoSumHashTable(int[] nums, int target) { + int size = nums.Length; + // 辅助哈希表,空间复杂度 O(n) + Dictionary dic = new(); + // 单层循环,时间复杂度 O(n) + for (int i = 0; i < size; i++) { + if (dic.ContainsKey(target - nums[i])) { + return new int[] { dic[target - nums[i]], i }; + } + dic.Add(nums[i], i); + } + return Array.Empty(); + } ``` === "Go" ```go title="two_sum.go" - [class]{}-[func]{twoSumHashTable} + /* 方法二:辅助哈希表 */ + 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 + } ``` === "Swift" ```swift title="two_sum.swift" - [class]{}-[func]{twoSumHashTable} + /* 方法二:辅助哈希表 */ + 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] + } ``` === "JS" ```javascript title="two_sum.js" - [class]{}-[func]{twoSumHashTable} + /* 方法二:辅助哈希表 */ + 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 []; + } ``` === "TS" ```typescript title="two_sum.ts" - [class]{}-[func]{twoSumHashTable} + /* 方法二:辅助哈希表 */ + 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 []; + } ``` === "Dart" ```dart title="two_sum.dart" - [class]{}-[func]{twoSumHashTable} + /* 方法二: 辅助哈希表 */ + 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]; + } ``` === "Rust" ```rust title="two_sum.rs" - [class]{}-[func]{two_sum_hash_table} + /* 方法二:辅助哈希表 */ + 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 + } ``` === "C" ```c title="two_sum.c" - [class]{hashTable}-[func]{} + /* 哈希表 */ + struct hashTable { + int key; + int val; + UT_hash_handle hh; // 基于 uthash.h 实现 + }; - [class]{}-[func]{twoSumHashTable} + typedef struct hashTable hashTable; + + /* 哈希表查询 */ + 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; + } ``` === "Zig" ```zig title="two_sum.zig" - [class]{}-[func]{twoSumHashTable} + // 方法二:辅助哈希表 + fn twoSumHashTable(nums: []i32, target: i32) !?[2]i32 { + var size: usize = nums.len; + // 辅助哈希表,空间复杂度 O(n) + var dic = std.AutoHashMap(i32, i32).init(std.heap.page_allocator); + defer dic.deinit(); + var i: usize = 0; + // 单层循环,时间复杂度 O(n) + while (i < size) : (i += 1) { + if (dic.contains(target - nums[i])) { + return [_]i32{dic.get(target - nums[i]).?, @intCast(i)}; + } + try dic.put(nums[i], @intCast(i)); + } + return null; + } ``` 此方法通过哈希查找将时间复杂度从 $O(n^2)$ 降低至 $O(n)$ ,大幅提升运行效率。 diff --git a/zh/chapter_sorting/bubble_sort.md b/zh/chapter_sorting/bubble_sort.md index b0f9e8fc3..414b005cd 100755 --- a/zh/chapter_sorting/bubble_sort.md +++ b/zh/chapter_sorting/bubble_sort.md @@ -47,73 +47,234 @@ comments: true === "Python" ```python title="bubble_sort.py" - [class]{}-[func]{bubble_sort} + def bubble_sort(nums: list[int]): + """冒泡排序""" + n = len(nums) + # 外循环:未排序区间为 [0, i] + for i in range(n - 1, 0, -1): + # 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端 + for j in range(i): + if nums[j] > nums[j + 1]: + # 交换 nums[j] 与 nums[j + 1] + nums[j], nums[j + 1] = nums[j + 1], nums[j] ``` === "C++" ```cpp title="bubble_sort.cpp" - [class]{}-[func]{bubbleSort} + /* 冒泡排序 */ + 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]); + } + } + } + } ``` === "Java" ```java title="bubble_sort.java" - [class]{bubble_sort}-[func]{bubbleSort} + /* 冒泡排序 */ + 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] + int tmp = nums[j]; + nums[j] = nums[j + 1]; + nums[j + 1] = tmp; + } + } + } + } ``` === "C#" ```csharp title="bubble_sort.cs" - [class]{bubble_sort}-[func]{bubbleSort} + /* 冒泡排序 */ + 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] + int tmp = nums[j]; + nums[j] = nums[j + 1]; + nums[j + 1] = tmp; + } + } + } + } ``` === "Go" ```go title="bubble_sort.go" - [class]{}-[func]{bubbleSort} + /* 冒泡排序 */ + 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] + } + } + } + } ``` === "Swift" ```swift title="bubble_sort.swift" - [class]{}-[func]{bubbleSort} + /* 冒泡排序 */ + func bubbleSort(nums: inout [Int]) { + // 外循环:未排序区间为 [0, i] + for i in stride(from: nums.count - 1, to: 0, by: -1) { + // 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端 + for j in stride(from: 0, to: i, by: 1) { + if nums[j] > nums[j + 1] { + // 交换 nums[j] 与 nums[j + 1] + let tmp = nums[j] + nums[j] = nums[j + 1] + nums[j + 1] = tmp + } + } + } + } ``` === "JS" ```javascript title="bubble_sort.js" - [class]{}-[func]{bubbleSort} + /* 冒泡排序 */ + 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; + } + } + } + } ``` === "TS" ```typescript title="bubble_sort.ts" - [class]{}-[func]{bubbleSort} + /* 冒泡排序 */ + 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; + } + } + } + } ``` === "Dart" ```dart title="bubble_sort.dart" - [class]{}-[func]{bubbleSort} + /* 冒泡排序 */ + 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; + } + } + } + } ``` === "Rust" ```rust title="bubble_sort.rs" - [class]{}-[func]{bubble_sort} + /* 冒泡排序 */ + 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; + } + } + } + } ``` === "C" ```c title="bubble_sort.c" - [class]{}-[func]{bubbleSort} + /* 冒泡排序 */ + void bubbleSort(int nums[], int size) { + // 外循环:未排序区间为 [0, i] + for (int i = 0; i < size - 1; i++) { + // 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端 + for (int j = 0; j < size - 1 - i; j++) { + if (nums[j] > nums[j + 1]) { + int temp = nums[j]; + nums[j] = nums[j + 1]; + nums[j + 1] = temp; + } + } + } + } ``` === "Zig" ```zig title="bubble_sort.zig" - [class]{}-[func]{bubbleSort} + // 冒泡排序 + 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; + } + } + } + } ``` ## 11.3.2   效率优化 @@ -125,73 +286,277 @@ comments: true === "Python" ```python title="bubble_sort.py" - [class]{}-[func]{bubble_sort_with_flag} + def bubble_sort_with_flag(nums: list[int]): + """冒泡排序(标志优化)""" + n = len(nums) + # 外循环:未排序区间为 [0, i] + for i in range(n - 1, 0, -1): + flag = False # 初始化标志位 + # 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端 + for j in range(i): + if nums[j] > nums[j + 1]: + # 交换 nums[j] 与 nums[j + 1] + nums[j], nums[j + 1] = nums[j + 1], nums[j] + flag = True # 记录交换元素 + if not flag: + break # 此轮冒泡未交换任何元素,直接跳出 ``` === "C++" ```cpp title="bubble_sort.cpp" - [class]{}-[func]{bubbleSortWithFlag} + /* 冒泡排序(标志优化)*/ + 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; // 此轮冒泡未交换任何元素,直接跳出 + } + } ``` === "Java" ```java title="bubble_sort.java" - [class]{bubble_sort}-[func]{bubbleSortWithFlag} + /* 冒泡排序(标志优化) */ + void bubbleSortWithFlag(int[] nums) { + // 外循环:未排序区间为 [0, i] + for (int i = nums.length - 1; i > 0; i--) { + boolean 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; // 此轮冒泡未交换任何元素,直接跳出 + } + } ``` === "C#" ```csharp title="bubble_sort.cs" - [class]{bubble_sort}-[func]{bubbleSortWithFlag} + /* 冒泡排序(标志优化)*/ + 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] + int tmp = nums[j]; + nums[j] = nums[j + 1]; + nums[j + 1] = tmp; + flag = true; // 记录交换元素 + } + } + if (!flag) break; // 此轮冒泡未交换任何元素,直接跳出 + } + } ``` === "Go" ```go title="bubble_sort.go" - [class]{}-[func]{bubbleSortWithFlag} + /* 冒泡排序(标志优化)*/ + 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 + } + } + } ``` === "Swift" ```swift title="bubble_sort.swift" - [class]{}-[func]{bubbleSortWithFlag} + /* 冒泡排序(标志优化)*/ + func bubbleSortWithFlag(nums: inout [Int]) { + // 外循环:未排序区间为 [0, i] + for i in stride(from: nums.count - 1, to: 0, by: -1) { + var flag = false // 初始化标志位 + for j in stride(from: 0, to: i, by: 1) { + 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 + } + } + } ``` === "JS" ```javascript title="bubble_sort.js" - [class]{}-[func]{bubbleSortWithFlag} + /* 冒泡排序(标志优化)*/ + 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; // 此轮冒泡未交换任何元素,直接跳出 + } + } ``` === "TS" ```typescript title="bubble_sort.ts" - [class]{}-[func]{bubbleSortWithFlag} + /* 冒泡排序(标志优化)*/ + 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; // 此轮冒泡未交换任何元素,直接跳出 + } + } ``` === "Dart" ```dart title="bubble_sort.dart" - [class]{}-[func]{bubbleSortWithFlag} + /* 冒泡排序(标志优化)*/ + 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; // 此轮冒泡未交换任何元素,直接跳出 + } + } ``` === "Rust" ```rust title="bubble_sort.rs" - [class]{}-[func]{bubble_sort_with_flag} + /* 冒泡排序(标志优化) */ + 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}; // 此轮冒泡未交换任何元素,直接跳出 + } + } ``` === "C" ```c title="bubble_sort.c" - [class]{}-[func]{bubbleSortWithFlag} + /* 冒泡排序(标志优化)*/ + void bubbleSortWithFlag(int nums[], int size) { + // 外循环:未排序区间为 [0, i] + for (int i = 0; i < size - 1; i++) { + bool flag = false; + // 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端 + for (int j = 0; j < size - 1 - 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; + } + } ``` === "Zig" ```zig title="bubble_sort.zig" - [class]{}-[func]{bubbleSortWithFlag} + // 冒泡排序(标志优化) + 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; // 此轮冒泡未交换任何元素,直接跳出 + } + } ``` ## 11.3.3   算法特性 diff --git a/zh/chapter_sorting/bucket_sort.md b/zh/chapter_sorting/bucket_sort.md index c4f1da687..341ecbbcb 100644 --- a/zh/chapter_sorting/bucket_sort.md +++ b/zh/chapter_sorting/bucket_sort.md @@ -23,67 +23,367 @@ comments: true === "Python" ```python title="bucket_sort.py" - [class]{}-[func]{bucket_sort} + def bucket_sort(nums: list[float]): + """桶排序""" + # 初始化 k = n/2 个桶,预期向每个桶分配 2 个元素 + k = len(nums) // 2 + buckets = [[] for _ in range(k)] + # 1. 将数组元素分配到各个桶中 + for num in nums: + # 输入数据范围 [0, 1),使用 num * k 映射到索引范围 [0, k-1] + i = int(num * k) + # 将 num 添加进桶 i + buckets[i].append(num) + # 2. 对各个桶执行排序 + for bucket in buckets: + # 使用内置排序函数,也可以替换成其他排序算法 + bucket.sort() + # 3. 遍历桶合并结果 + i = 0 + for bucket in buckets: + for num in bucket: + nums[i] = num + i += 1 ``` === "C++" ```cpp title="bucket_sort.cpp" - [class]{}-[func]{bucketSort} + /* 桶排序 */ + 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; + } + } + } ``` === "Java" ```java title="bucket_sort.java" - [class]{bucket_sort}-[func]{bucketSort} + /* 桶排序 */ + void bucketSort(float[] nums) { + // 初始化 k = n/2 个桶,预期向每个桶分配 2 个元素 + int k = nums.length / 2; + List> buckets = new ArrayList<>(); + for (int i = 0; i < k; i++) { + buckets.add(new ArrayList<>()); + } + // 1. 将数组元素分配到各个桶中 + for (float num : nums) { + // 输入数据范围 [0, 1),使用 num * k 映射到索引范围 [0, k-1] + int i = (int) (num * k); + // 将 num 添加进桶 i + buckets.get(i).add(num); + } + // 2. 对各个桶执行排序 + for (List bucket : buckets) { + // 使用内置排序函数,也可以替换成其他排序算法 + Collections.sort(bucket); + } + // 3. 遍历桶合并结果 + int i = 0; + for (List bucket : buckets) { + for (float num : bucket) { + nums[i++] = num; + } + } + } ``` === "C#" ```csharp title="bucket_sort.cs" - [class]{bucket_sort}-[func]{bucketSort} + /* 桶排序 */ + void bucketSort(float[] nums) { + // 初始化 k = n/2 个桶,预期向每个桶分配 2 个元素 + int k = nums.Length / 2; + List> buckets = new List>(); + for (int i = 0; i < k; i++) { + buckets.Add(new List()); + } + // 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; + } + } + } ``` === "Go" ```go title="bucket_sort.go" - [class]{}-[func]{bucketSort} + /* 桶排序 */ + 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++ + } + } + } ``` === "Swift" ```swift title="bucket_sort.swift" - [class]{}-[func]{bucketSort} + /* 桶排序 */ + 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 + nums.formIndex(after: &i) + } + } + } ``` === "JS" ```javascript title="bucket_sort.js" - [class]{}-[func]{bucketSort} + /* 桶排序 */ + 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; + } + } + } ``` === "TS" ```typescript title="bucket_sort.ts" - [class]{}-[func]{bucketSort} + /* 桶排序 */ + 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; + } + } + } ``` === "Dart" ```dart title="bucket_sort.dart" - [class]{}-[func]{bucketSort} + /* 桶排序 */ + 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; + } + } + } ``` === "Rust" ```rust title="bucket_sort.rs" - [class]{}-[func]{bucket_sort} + /* 桶排序 */ + 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; + } + } + } ``` === "C" ```c title="bucket_sort.c" - [class]{}-[func]{bucketSort} + /* 桶排序 */ + void bucketSort(float nums[], int size) { + // 初始化 k = n/2 个桶,预期向每个桶分配 2 个元素 + int k = size / 2; + float **buckets = calloc(k, sizeof(float *)); + for (int i = 0; i < k; i++) { + // 每个桶最多可以分配 k 个元素 + buckets[i] = calloc(ARRAY_SIZE, sizeof(float)); + } + + // 1. 将数组元素分配到各个桶中 + for (int i = 0; i < size; i++) { + // 输入数据范围 [0, 1),使用 num * k 映射到索引范围 [0, k-1] + int bucket_idx = nums[i] * k; + int j = 0; + // 如果桶中有数据且数据小于当前值 nums[i], 要将其放到当前桶的后面,相当于 cpp 中的 push_back + while (buckets[bucket_idx][j] > 0 && buckets[bucket_idx][j] < nums[i]) { + j++; + } + float temp = nums[i]; + while (j < ARRAY_SIZE && buckets[bucket_idx][j] > 0) { + swap(&temp, &buckets[bucket_idx][j]); + j++; + } + buckets[bucket_idx][j] = temp; + } + + // 2. 对各个桶执行排序 + for (int i = 0; i < k; i++) { + qsort(buckets[i], ARRAY_SIZE, sizeof(float), compare_float); + } + + // 3. 遍历桶合并结果 + for (int i = 0, j = 0; j < k; j++) { + for (int l = 0; l < ARRAY_SIZE; l++) { + if (buckets[j][l] > 0) { + nums[i++] = buckets[j][l]; + } + } + } + + // 释放上述分配的内存 + for (int i = 0; i < k; i++) { + free(buckets[i]); + } + free(buckets); + } ``` === "Zig" diff --git a/zh/chapter_sorting/counting_sort.md b/zh/chapter_sorting/counting_sort.md index ff0d5a2fb..bcf0ae42b 100644 --- a/zh/chapter_sorting/counting_sort.md +++ b/zh/chapter_sorting/counting_sort.md @@ -21,67 +21,294 @@ comments: true === "Python" ```python title="counting_sort.py" - [class]{}-[func]{counting_sort_naive} + def counting_sort_naive(nums: list[int]): + """计数排序""" + # 简单实现,无法用于排序对象 + # 1. 统计数组最大元素 m + m = 0 + for num in nums: + m = max(m, num) + # 2. 统计各数字的出现次数 + # counter[num] 代表 num 的出现次数 + counter = [0] * (m + 1) + for num in nums: + counter[num] += 1 + # 3. 遍历 counter ,将各元素填入原数组 nums + i = 0 + for num in range(m + 1): + for _ in range(counter[num]): + nums[i] = num + i += 1 ``` === "C++" ```cpp title="counting_sort.cpp" - [class]{}-[func]{countingSortNaive} + /* 计数排序 */ + // 简单实现,无法用于排序对象 + 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; + } + } + } ``` === "Java" ```java title="counting_sort.java" - [class]{counting_sort}-[func]{countingSortNaive} + /* 计数排序 */ + // 简单实现,无法用于排序对象 + void countingSortNaive(int[] nums) { + // 1. 统计数组最大元素 m + int m = 0; + for (int num : nums) { + m = Math.max(m, num); + } + // 2. 统计各数字的出现次数 + // counter[num] 代表 num 的出现次数 + int[] counter = new int[m + 1]; + 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; + } + } + } ``` === "C#" ```csharp title="counting_sort.cs" - [class]{counting_sort}-[func]{countingSortNaive} + /* 计数排序 */ + // 简单实现,无法用于排序对象 + 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; + } + } + } ``` === "Go" ```go title="counting_sort.go" - [class]{}-[func]{countingSortNaive} + /* 计数排序 */ + // 简单实现,无法用于排序对象 + 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++ + } + } + } ``` === "Swift" ```swift title="counting_sort.swift" - [class]{}-[func]{countingSortNaive} + /* 计数排序 */ + // 简单实现,无法用于排序对象 + 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 stride(from: 0, to: m + 1, by: 1) { + for _ in stride(from: 0, to: counter[num], by: 1) { + nums[i] = num + i += 1 + } + } + } ``` === "JS" ```javascript title="counting_sort.js" - [class]{}-[func]{countingSortNaive} + /* 计数排序 */ + // 简单实现,无法用于排序对象 + 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; + } + } + } ``` === "TS" ```typescript title="counting_sort.ts" - [class]{}-[func]{countingSortNaive} + /* 计数排序 */ + // 简单实现,无法用于排序对象 + 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; + } + } + } ``` === "Dart" ```dart title="counting_sort.dart" - [class]{}-[func]{countingSortNaive} + /* 计数排序 */ + // 简单实现,无法用于排序对象 + 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; + } + } + } ``` === "Rust" ```rust title="counting_sort.rs" - [class]{}-[func]{counting_sort_naive} + /* 计数排序 */ + // 简单实现,无法用于排序对象 + 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; + } + } + } ``` === "C" ```c title="counting_sort.c" - [class]{}-[func]{countingSortNaive} + /* 计数排序 */ + // 简单实现,无法用于排序对象 + 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 = malloc(sizeof(int) * m); + 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; + } + } + } ``` === "Zig" @@ -142,67 +369,401 @@ $$ === "Python" ```python title="counting_sort.py" - [class]{}-[func]{counting_sort} + def counting_sort(nums: list[int]): + """计数排序""" + # 完整实现,可排序对象,并且是稳定排序 + # 1. 统计数组最大元素 m + m = max(nums) + # 2. 统计各数字的出现次数 + # counter[num] 代表 num 的出现次数 + counter = [0] * (m + 1) + for num in nums: + counter[num] += 1 + # 3. 求 counter 的前缀和,将“出现次数”转换为“尾索引” + # 即 counter[num]-1 是 num 在 res 中最后一次出现的索引 + for i in range(m): + counter[i + 1] += counter[i] + # 4. 倒序遍历 nums ,将各元素填入结果数组 res + # 初始化数组 res 用于记录结果 + 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 + for i in range(n): + nums[i] = res[i] ``` === "C++" ```cpp title="counting_sort.cpp" - [class]{}-[func]{countingSort} + /* 计数排序 */ + // 完整实现,可排序对象,并且是稳定排序 + 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; + } ``` === "Java" ```java title="counting_sort.java" - [class]{counting_sort}-[func]{countingSort} + /* 计数排序 */ + // 完整实现,可排序对象,并且是稳定排序 + void countingSort(int[] nums) { + // 1. 统计数组最大元素 m + int m = 0; + for (int num : nums) { + m = Math.max(m, num); + } + // 2. 统计各数字的出现次数 + // counter[num] 代表 num 的出现次数 + int[] counter = new int[m + 1]; + 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.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]; + } + } ``` === "C#" ```csharp title="counting_sort.cs" - [class]{counting_sort}-[func]{countingSort} + /* 计数排序 */ + // 完整实现,可排序对象,并且是稳定排序 + 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]; + } + } ``` === "Go" ```go title="counting_sort.go" - [class]{}-[func]{countingSort} + /* 计数排序 */ + // 完整实现,可排序对象,并且是稳定排序 + 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) + } ``` === "Swift" ```swift title="counting_sort.swift" - [class]{}-[func]{countingSort} + /* 计数排序 */ + // 完整实现,可排序对象,并且是稳定排序 + 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 stride(from: 0, to: m, by: 1) { + counter[i + 1] += counter[i] + } + // 4. 倒序遍历 nums ,将各元素填入结果数组 res + // 初始化数组 res 用于记录结果 + var res = Array(repeating: 0, count: nums.count) + for i in stride(from: nums.count - 1, through: 0, by: -1) { + let num = nums[i] + res[counter[num] - 1] = num // 将 num 放置到对应索引处 + counter[num] -= 1 // 令前缀和自减 1 ,得到下次放置 num 的索引 + } + // 使用结果数组 res 覆盖原数组 nums + for i in stride(from: 0, to: nums.count, by: 1) { + nums[i] = res[i] + } + } ``` === "JS" ```javascript title="counting_sort.js" - [class]{}-[func]{countingSort} + /* 计数排序 */ + // 完整实现,可排序对象,并且是稳定排序 + 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]; + } + } ``` === "TS" ```typescript title="counting_sort.ts" - [class]{}-[func]{countingSort} + /* 计数排序 */ + // 完整实现,可排序对象,并且是稳定排序 + 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]; + } + } ``` === "Dart" ```dart title="counting_sort.dart" - [class]{}-[func]{countingSort} + /* 计数排序 */ + // 完整实现,可排序对象,并且是稳定排序 + 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); + } ``` === "Rust" ```rust title="counting_sort.rs" - [class]{}-[func]{counting_sort} + /* 计数排序 */ + // 完整实现,可排序对象,并且是稳定排序 + 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]; + } + } ``` === "C" ```c title="counting_sort.c" - [class]{}-[func]{countingSort} + /* 计数排序 */ + // 完整实现,可排序对象,并且是稳定排序 + 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 = malloc(sizeof(int) * m); + 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)); + } ``` === "Zig" diff --git a/zh/chapter_sorting/heap_sort.md b/zh/chapter_sorting/heap_sort.md index f102bc259..799ac9ea1 100644 --- a/zh/chapter_sorting/heap_sort.md +++ b/zh/chapter_sorting/heap_sort.md @@ -71,89 +71,467 @@ comments: true === "Python" ```python title="heap_sort.py" - [class]{}-[func]{sift_down} + def sift_down(nums: list[int], n: int, i: int): + """堆的长度为 n ,从节点 i 开始,从顶至底堆化""" + while True: + # 判断节点 i, l, r 中值最大的节点,记为 ma + l = 2 * i + 1 + r = 2 * i + 2 + ma = i + if l < n and nums[l] > nums[ma]: + ma = l + if r < n and 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]{heap_sort} + def heap_sort(nums: list[int]): + """堆排序""" + # 建堆操作:堆化除叶节点以外的其他所有节点 + for i in range(len(nums) // 2 - 1, -1, -1): + sift_down(nums, len(nums), i) + # 从堆中提取最大元素,循环 n-1 轮 + for i in range(len(nums) - 1, 0, -1): + # 交换根节点与最右叶节点(即交换首元素与尾元素) + nums[0], nums[i] = nums[i], nums[0] + # 以根节点为起点,从顶至底进行堆化 + sift_down(nums, i, 0) ``` === "C++" ```cpp title="heap_sort.cpp" - [class]{}-[func]{siftDown} + /* 堆的长度为 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]{heapSort} + /* 堆排序 */ + 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); + } + } ``` === "Java" ```java title="heap_sort.java" - [class]{heap_sort}-[func]{siftDown} + /* 堆的长度为 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; + // 交换两节点 + int temp = nums[i]; + nums[i] = nums[ma]; + nums[ma] = temp; + // 循环向下堆化 + i = ma; + } + } - [class]{heap_sort}-[func]{heapSort} + /* 堆排序 */ + 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--) { + // 交换根节点与最右叶节点(即交换首元素与尾元素) + int tmp = nums[0]; + nums[0] = nums[i]; + nums[i] = tmp; + // 以根节点为起点,从顶至底进行堆化 + siftDown(nums, i, 0); + } + } ``` === "C#" ```csharp title="heap_sort.cs" - [class]{heap_sort}-[func]{siftDown} + /* 堆的长度为 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]{heapSort} + /* 堆排序 */ + 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); + } + } ``` === "Go" ```go title="heap_sort.go" - [class]{}-[func]{siftDown} + /* 堆的长度为 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]{heapSort} + /* 堆排序 */ + 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) + } + } ``` === "Swift" ```swift title="heap_sort.swift" - [class]{}-[func]{siftDown} + /* 堆的长度为 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]{heapSort} + /* 堆排序 */ + 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 stride(from: nums.count - 1, to: 0, by: -1) { + // 交换根节点与最右叶节点(即交换首元素与尾元素) + nums.swapAt(0, i) + // 以根节点为起点,从顶至底进行堆化 + siftDown(nums: &nums, n: i, i: 0) + } + } ``` === "JS" ```javascript title="heap_sort.js" - [class]{}-[func]{siftDown} + /* 堆的长度为 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]{heapSort} + /* 堆排序 */ + 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); + } + } ``` === "TS" ```typescript title="heap_sort.ts" - [class]{}-[func]{siftDown} + /* 堆的长度为 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]{heapSort} + /* 堆排序 */ + 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); + } + } ``` === "Dart" ```dart title="heap_sort.dart" - [class]{}-[func]{siftDown} + /* 堆的长度为 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]{heapSort} + /* 堆排序 */ + 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); + } + } ``` === "Rust" ```rust title="heap_sort.rs" - [class]{}-[func]{sift_down} + /* 堆的长度为 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]{heap_sort} + /* 堆排序 */ + 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); + } + } ``` === "C" ```c title="heap_sort.c" - [class]{}-[func]{siftDown} + /* 堆的长度为 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]{heapSort} + /* 堆排序 */ + 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); + } + } ``` === "Zig" diff --git a/zh/chapter_sorting/insertion_sort.md b/zh/chapter_sorting/insertion_sort.md index de1e14b07..27e483ccb 100755 --- a/zh/chapter_sorting/insertion_sort.md +++ b/zh/chapter_sorting/insertion_sort.md @@ -30,73 +30,222 @@ comments: true === "Python" ```python title="insertion_sort.py" - [class]{}-[func]{insertion_sort} + def insertion_sort(nums: list[int]): + """插入排序""" + # 外循环:已排序区间为 [0, i-1] + for i in range(1, len(nums)): + base = nums[i] + j = i - 1 + # 内循环:将 base 插入到已排序区间 [0, i-1] 中的正确位置 + while j >= 0 and nums[j] > base: + nums[j + 1] = nums[j] # 将 nums[j] 向右移动一位 + j -= 1 + nums[j + 1] = base # 将 base 赋值到正确位置 ``` === "C++" ```cpp title="insertion_sort.cpp" - [class]{}-[func]{insertionSort} + /* 插入排序 */ + void insertionSort(vector &nums) { + // 外循环:已排序元素数量为 1, 2, ..., n + for (int i = 1; i < nums.size(); i++) { + int base = nums[i], j = i - 1; + // 内循环:将 base 插入到已排序部分的正确位置 + while (j >= 0 && nums[j] > base) { + nums[j + 1] = nums[j]; // 将 nums[j] 向右移动一位 + j--; + } + nums[j + 1] = base; // 将 base 赋值到正确位置 + } + } ``` === "Java" ```java title="insertion_sort.java" - [class]{insertion_sort}-[func]{insertionSort} + /* 插入排序 */ + void insertionSort(int[] nums) { + // 外循环:已排序元素数量为 1, 2, ..., n + for (int i = 1; i < nums.length; i++) { + int base = nums[i], j = i - 1; + // 内循环:将 base 插入到已排序部分的正确位置 + while (j >= 0 && nums[j] > base) { + nums[j + 1] = nums[j]; // 将 nums[j] 向右移动一位 + j--; + } + nums[j + 1] = base; // 将 base 赋值到正确位置 + } + } ``` === "C#" ```csharp title="insertion_sort.cs" - [class]{insertion_sort}-[func]{insertionSort} + /* 插入排序 */ + void insertionSort(int[] nums) { + // 外循环:已排序元素数量为 1, 2, ..., n + for (int i = 1; i < nums.Length; i++) { + int bas = nums[i], j = i - 1; + // 内循环:将 base 插入到已排序部分的正确位置 + while (j >= 0 && nums[j] > bas) { + nums[j + 1] = nums[j]; // 将 nums[j] 向右移动一位 + j--; + } + nums[j + 1] = bas; // 将 base 赋值到正确位置 + } + } ``` === "Go" ```go title="insertion_sort.go" - [class]{}-[func]{insertionSort} + /* 插入排序 */ + func insertionSort(nums []int) { + // 外循环:未排序区间为 [0, i] + for i := 1; i < len(nums); i++ { + base := nums[i] + j := i - 1 + // 内循环:将 base 插入到已排序部分的正确位置 + for j >= 0 && nums[j] > base { + nums[j+1] = nums[j] // 将 nums[j] 向右移动一位 + j-- + } + nums[j+1] = base // 将 base 赋值到正确位置 + } + } ``` === "Swift" ```swift title="insertion_sort.swift" - [class]{}-[func]{insertionSort} + /* 插入排序 */ + func insertionSort(nums: inout [Int]) { + // 外循环:已排序元素数量为 1, 2, ..., n + for i in stride(from: 1, to: nums.count, by: 1) { + let base = nums[i] + var j = i - 1 + // 内循环:将 base 插入到已排序部分的正确位置 + while j >= 0, nums[j] > base { + nums[j + 1] = nums[j] // 将 nums[j] 向右移动一位 + j -= 1 + } + nums[j + 1] = base // 将 base 赋值到正确位置 + } + } ``` === "JS" ```javascript title="insertion_sort.js" - [class]{}-[func]{insertionSort} + /* 插入排序 */ + function insertionSort(nums) { + // 外循环:已排序元素数量为 1, 2, ..., n + for (let i = 1; i < nums.length; i++) { + let base = nums[i], + j = i - 1; + // 内循环:将 base 插入到已排序部分的正确位置 + while (j >= 0 && nums[j] > base) { + nums[j + 1] = nums[j]; // 将 nums[j] 向右移动一位 + j--; + } + nums[j + 1] = base; // 将 base 赋值到正确位置 + } + } ``` === "TS" ```typescript title="insertion_sort.ts" - [class]{}-[func]{insertionSort} + /* 插入排序 */ + function insertionSort(nums: number[]): void { + // 外循环:已排序元素数量为 1, 2, ..., n + for (let i = 1; i < nums.length; i++) { + const base = nums[i]; + let j = i - 1; + // 内循环:将 base 插入到已排序部分的正确位置 + while (j >= 0 && nums[j] > base) { + nums[j + 1] = nums[j]; // 将 nums[j] 向右移动一位 + j--; + } + nums[j + 1] = base; // 将 base 赋值到正确位置 + } + } ``` === "Dart" ```dart title="insertion_sort.dart" - [class]{}-[func]{insertionSort} + /* 插入排序 */ + void insertionSort(List nums) { + // 外循环:已排序元素数量为 1, 2, ..., n + for (int i = 1; i < nums.length; i++) { + int base = nums[i], j = i - 1; + // 内循环:将 base 插入到已排序部分的正确位置 + while (j >= 0 && nums[j] > base) { + nums[j + 1] = nums[j]; // 将 nums[j] 向右移动一位 + j--; + } + nums[j + 1] = base; // 将 base 赋值到正确位置 + } + } ``` === "Rust" ```rust title="insertion_sort.rs" - [class]{}-[func]{insertion_sort} + /* 插入排序 */ + fn insertion_sort(nums: &mut [i32]) { + // 外循环:已排序元素数量为 1, 2, ..., n + for i in 1..nums.len() { + let (base, mut j) = (nums[i], (i - 1) as i32); + // 内循环:将 base 插入到已排序部分的正确位置 + 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 赋值到正确位置 + } + } ``` === "C" ```c title="insertion_sort.c" - [class]{}-[func]{insertionSort} + /* 插入排序 */ + void insertionSort(int nums[], int size) { + // 外循环:已排序元素数量为 1, 2, ..., n + for (int i = 1; i < size; i++) { + int base = nums[i], j = i - 1; + // 内循环:将 base 插入到已排序部分的正确位置 + while (j >= 0 && nums[j] > base) { + // 将 nums[j] 向右移动一位 + nums[j + 1] = nums[j]; + j--; + } + // 将 base 赋值到正确位置 + nums[j + 1] = base; + } + } ``` === "Zig" ```zig title="insertion_sort.zig" - [class]{}-[func]{insertionSort} + // 插入排序 + fn insertionSort(nums: []i32) void { + // 外循环:已排序元素数量为 1, 2, ..., n + var i: usize = 1; + while (i < nums.len) : (i += 1) { + var base = nums[i]; + var j: usize = i; + // 内循环:将 base 插入到已排序部分的正确位置 + while (j >= 1 and nums[j - 1] > base) : (j -= 1) { + nums[j] = nums[j - 1]; // 将 nums[j] 向右移动一位 + } + nums[j] = base; // 将 base 赋值到正确位置 + } + } ``` ## 11.4.2   算法特性 diff --git a/zh/chapter_sorting/merge_sort.md b/zh/chapter_sorting/merge_sort.md index 2582549ac..9e2289c41 100755 --- a/zh/chapter_sorting/merge_sort.md +++ b/zh/chapter_sorting/merge_sort.md @@ -62,97 +62,562 @@ comments: true === "Python" ```python title="merge_sort.py" - [class]{}-[func]{merge} + def merge(nums: list[int], left: int, mid: int, right: int): + """合并左子数组和右子数组""" + # 左子数组区间 [left, mid] + # 右子数组区间 [mid + 1, right] + # 初始化辅助数组 + tmp = list(nums[left : right + 1]) + # 左子数组的起始索引和结束索引 + left_start = 0 + left_end = mid - left + # 右子数组的起始索引和结束索引 + right_start = mid + 1 - left + right_end = right - left + # i, j 分别指向左子数组、右子数组的首元素 + i = left_start + j = right_start + # 通过覆盖原数组 nums 来合并左子数组和右子数组 + for k in range(left, right + 1): + # 若“左子数组已全部合并完”,则选取右子数组元素,并且 j++ + if i > left_end: + nums[k] = tmp[j] + j += 1 + # 否则,若“右子数组已全部合并完”或“左子数组元素 <= 右子数组元素”,则选取左子数组元素,并且 i++ + elif j > right_end or tmp[i] <= tmp[j]: + nums[k] = tmp[i] + i += 1 + # 否则,若“左右子数组都未全部合并完”且“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++ + else: + nums[k] = tmp[j] + j += 1 - [class]{}-[func]{merge_sort} + def merge_sort(nums: list[int], left: int, right: int): + """归并排序""" + # 终止条件 + if left >= right: + return # 当子数组长度为 1 时终止递归 + # 划分阶段 + mid = (left + right) // 2 # 计算中点 + merge_sort(nums, left, mid) # 递归左子数组 + merge_sort(nums, mid + 1, right) # 递归右子数组 + # 合并阶段 + merge(nums, left, mid, right) ``` === "C++" ```cpp title="merge_sort.cpp" - [class]{}-[func]{merge} + /* 合并左子数组和右子数组 */ + // 左子数组区间 [left, mid] + // 右子数组区间 [mid + 1, right] + void merge(vector &nums, int left, int mid, int right) { + // 初始化辅助数组 + vector tmp(nums.begin() + left, nums.begin() + right + 1); + // 左子数组的起始索引和结束索引 + int leftStart = left - left, leftEnd = mid - left; + // 右子数组的起始索引和结束索引 + int rightStart = mid + 1 - left, rightEnd = right - left; + // i, j 分别指向左子数组、右子数组的首元素 + int i = leftStart, j = rightStart; + // 通过覆盖原数组 nums 来合并左子数组和右子数组 + for (int k = left; k <= right; k++) { + // 若“左子数组已全部合并完”,则选取右子数组元素,并且 j++ + if (i > leftEnd) + nums[k] = tmp[j++]; + // 否则,若“右子数组已全部合并完”或“左子数组元素 <= 右子数组元素”,则选取左子数组元素,并且 i++ + else if (j > rightEnd || tmp[i] <= tmp[j]) + nums[k] = tmp[i++]; + // 否则,若“左右子数组都未全部合并完”且“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++ + else + nums[k] = tmp[j++]; + } + } - [class]{}-[func]{mergeSort} + /* 归并排序 */ + 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); + } ``` === "Java" ```java title="merge_sort.java" - [class]{merge_sort}-[func]{merge} + /* 合并左子数组和右子数组 */ + // 左子数组区间 [left, mid] + // 右子数组区间 [mid + 1, right] + void merge(int[] nums, int left, int mid, int right) { + // 初始化辅助数组 + int[] tmp = Arrays.copyOfRange(nums, left, right + 1); + // 左子数组的起始索引和结束索引 + int leftStart = left - left, leftEnd = mid - left; + // 右子数组的起始索引和结束索引 + int rightStart = mid + 1 - left, rightEnd = right - left; + // i, j 分别指向左子数组、右子数组的首元素 + int i = leftStart, j = rightStart; + // 通过覆盖原数组 nums 来合并左子数组和右子数组 + for (int k = left; k <= right; k++) { + // 若“左子数组已全部合并完”,则选取右子数组元素,并且 j++ + if (i > leftEnd) + nums[k] = tmp[j++]; + // 否则,若“右子数组已全部合并完”或“左子数组元素 <= 右子数组元素”,则选取左子数组元素,并且 i++ + else if (j > rightEnd || tmp[i] <= tmp[j]) + nums[k] = tmp[i++]; + // 否则,若“左右子数组都未全部合并完”且“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++ + else + nums[k] = tmp[j++]; + } + } - [class]{merge_sort}-[func]{mergeSort} + /* 归并排序 */ + 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); + } ``` === "C#" ```csharp title="merge_sort.cs" - [class]{merge_sort}-[func]{merge} + /* 合并左子数组和右子数组 */ + // 左子数组区间 [left, mid] + // 右子数组区间 [mid + 1, right] + void merge(int[] nums, int left, int mid, int right) { + // 初始化辅助数组 + int[] tmp = nums[left..(right + 1)]; + // 左子数组的起始索引和结束索引 + int leftStart = left - left, leftEnd = mid - left; + // 右子数组的起始索引和结束索引 + int rightStart = mid + 1 - left, rightEnd = right - left; + // i, j 分别指向左子数组、右子数组的首元素 + int i = leftStart, j = rightStart; + // 通过覆盖原数组 nums 来合并左子数组和右子数组 + for (int k = left; k <= right; k++) { + // 若“左子数组已全部合并完”,则选取右子数组元素,并且 j++ + if (i > leftEnd) + nums[k] = tmp[j++]; + // 否则,若“右子数组已全部合并完”或“左子数组元素 <= 右子数组元素”,则选取左子数组元素,并且 i++ + else if (j > rightEnd || tmp[i] <= tmp[j]) + nums[k] = tmp[i++]; + // 否则,若“左右子数组都未全部合并完”且“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++ + else + nums[k] = tmp[j++]; + } + } - [class]{merge_sort}-[func]{mergeSort} + /* 归并排序 */ + 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); + } ``` === "Go" ```go title="merge_sort.go" - [class]{}-[func]{merge} + /* 合并左子数组和右子数组 */ + // 左子数组区间 [left, mid] + // 右子数组区间 [mid + 1, right] + func merge(nums []int, left, mid, right int) { + // 初始化辅助数组 借助 copy 模块 + tmp := make([]int, right-left+1) + for i := left; i <= right; i++ { + tmp[i-left] = nums[i] + } + // 左子数组的起始索引和结束索引 + leftStart, leftEnd := left-left, mid-left + // 右子数组的起始索引和结束索引 + rightStart, rightEnd := mid+1-left, right-left + // i, j 分别指向左子数组、右子数组的首元素 + i, j := leftStart, rightStart + // 通过覆盖原数组 nums 来合并左子数组和右子数组 + for k := left; k <= right; k++ { + // 若“左子数组已全部合并完”,则选取右子数组元素,并且 j++ + if i > leftEnd { + nums[k] = tmp[j] + j++ + // 否则,若“右子数组已全部合并完”或“左子数组元素 <= 右子数组元素”,则选取左子数组元素,并且 i++ + } else if j > rightEnd || tmp[i] <= tmp[j] { + nums[k] = tmp[i] + i++ + // 否则,若“左右子数组都未全部合并完”且“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++ + } else { + nums[k] = tmp[j] + j++ + } + } + } - [class]{}-[func]{mergeSort} + /* 归并排序 */ + 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) + } ``` === "Swift" ```swift title="merge_sort.swift" - [class]{}-[func]{merge} + /* 合并左子数组和右子数组 */ + // 左子数组区间 [left, mid] + // 右子数组区间 [mid + 1, right] + func merge(nums: inout [Int], left: Int, mid: Int, right: Int) { + // 初始化辅助数组 + let tmp = Array(nums[left ..< (right + 1)]) + // 左子数组的起始索引和结束索引 + let leftStart = left - left + let leftEnd = mid - left + // 右子数组的起始索引和结束索引 + let rightStart = mid + 1 - left + let rightEnd = right - left + // i, j 分别指向左子数组、右子数组的首元素 + var i = leftStart + var j = rightStart + // 通过覆盖原数组 nums 来合并左子数组和右子数组 + for k in left ... right { + // 若“左子数组已全部合并完”,则选取右子数组元素,并且 j++ + if i > leftEnd { + nums[k] = tmp[j] + j += 1 + } + // 否则,若“右子数组已全部合并完”或“左子数组元素 <= 右子数组元素”,则选取左子数组元素,并且 i++ + else if j > rightEnd || tmp[i] <= tmp[j] { + nums[k] = tmp[i] + i += 1 + } + // 否则,若“左右子数组都未全部合并完”且“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++ + else { + nums[k] = tmp[j] + j += 1 + } + } + } - [class]{}-[func]{mergeSort} + /* 归并排序 */ + 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) + } ``` === "JS" ```javascript title="merge_sort.js" - [class]{}-[func]{merge} + /* 合并左子数组和右子数组 */ + // 左子数组区间 [left, mid] + // 右子数组区间 [mid + 1, right] + function merge(nums, left, mid, right) { + // 初始化辅助数组 + let tmp = nums.slice(left, right + 1); + // 左子数组的起始索引和结束索引 + let leftStart = left - left, + leftEnd = mid - left; + // 右子数组的起始索引和结束索引 + let rightStart = mid + 1 - left, + rightEnd = right - left; + // i, j 分别指向左子数组、右子数组的首元素 + let i = leftStart, + j = rightStart; + // 通过覆盖原数组 nums 来合并左子数组和右子数组 + for (let k = left; k <= right; k++) { + if (i > leftEnd) { + // 若“左子数组已全部合并完”,则选取右子数组元素,并且 j++ + nums[k] = tmp[j++]; + } else if (j > rightEnd || tmp[i] <= tmp[j]) { + // 否则,若“右子数组已全部合并完”或“左子数组元素 <= 右子数组元素”,则选取左子数组元素,并且 i++ + nums[k] = tmp[i++]; + } else { + // 否则,若“左右子数组都未全部合并完”且“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++ + nums[k] = tmp[j++]; + } + } + } - [class]{}-[func]{mergeSort} + /* 归并排序 */ + 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); + } ``` === "TS" ```typescript title="merge_sort.ts" - [class]{}-[func]{merge} + /* 合并左子数组和右子数组 */ + // 左子数组区间 [left, mid] + // 右子数组区间 [mid + 1, right] + function merge(nums: number[], left: number, mid: number, right: number): void { + // 初始化辅助数组 + let tmp = nums.slice(left, right + 1); + // 左子数组的起始索引和结束索引 + let leftStart = left - left, + leftEnd = mid - left; + // 右子数组的起始索引和结束索引 + let rightStart = mid + 1 - left, + rightEnd = right - left; + // i, j 分别指向左子数组、右子数组的首元素 + let i = leftStart, + j = rightStart; + // 通过覆盖原数组 nums 来合并左子数组和右子数组 + for (let k = left; k <= right; k++) { + if (i > leftEnd) { + // 若“左子数组已全部合并完”,则选取右子数组元素,并且 j++ + nums[k] = tmp[j++]; + // 否则,若“右子数组已全部合并完”或“左子数组元素 <= 右子数组元素”,则选取左子数组元素,并且 i++ + } else if (j > rightEnd || tmp[i] <= tmp[j]) { + nums[k] = tmp[i++]; + // 否则,若“左右子数组都未全部合并完”且“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++ + } else { + nums[k] = tmp[j++]; + } + } + } - [class]{}-[func]{mergeSort} + /* 归并排序 */ + 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); + } ``` === "Dart" ```dart title="merge_sort.dart" - [class]{}-[func]{merge} + /* 合并左子数组和右子数组 */ + // 左子数组区间 [left, mid] + // 右子数组区间 [mid + 1, right] + void merge(List nums, int left, int mid, int right) { + // 初始化辅助数组 + List tmp = nums.sublist(left, right + 1); + // 左子数组的起始索引和结束索引 + int leftStart = left - left, leftEnd = mid - left; + // 右子数组的起始索引和结束索引 + int rightStart = mid + 1 - left, rightEnd = right - left; + // i, j 分别指向左子数组、右子数组的首元素 + int i = leftStart, j = rightStart; + // 通过覆盖原数组 nums 来合并左子数组和右子数组 + for (int k = left; k <= right; k++) { + // 若“左子数组已全部合并完”,则选取右子数组元素,并且 j++ + if (i > leftEnd) + nums[k] = tmp[j++]; + // 否则,若“右子数组已全部合并完”或“左子数组元素 <= 右子数组元素”,则选取左子数组元素,并且 i++ + else if (j > rightEnd || tmp[i] <= tmp[j]) + nums[k] = tmp[i++]; + // 否则,若“左右子数组都未全部合并完”且“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++ + else + nums[k] = tmp[j++]; + } + } - [class]{}-[func]{mergeSort} + /* 归并排序 */ + 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); + } ``` === "Rust" ```rust title="merge_sort.rs" - [class]{}-[func]{merge} + /* 合并左子数组和右子数组 */ + // 左子数组区间 [left, mid] + // 右子数组区间 [mid + 1, right] + fn merge(nums: &mut [i32], left: usize, mid: usize, right: usize) { + // 初始化辅助数组 + let tmp: Vec = nums[left..right + 1].to_vec(); + // 左子数组的起始索引和结束索引 + let (left_start, left_end) = (left - left, mid - left); + // 右子数组的起始索引和结束索引 + let (right_start, right_end) = (mid + 1 - left, right-left); + // i, j 分别指向左子数组、右子数组的首元素 + let (mut l_corrent, mut r_corrent) = (left_start, right_start); + // 通过覆盖原数组 nums 来合并左子数组和右子数组 + for k in left..right + 1 { + // 若“左子数组已全部合并完”,则选取右子数组元素,并且 j++ + if l_corrent > left_end { + nums[k] = tmp[r_corrent]; + r_corrent += 1; + } + // 否则,若“右子数组已全部合并完”或“左子数组元素 <= 右子数组元素”,则选取左子数组元素,并且 i++ + else if r_corrent > right_end || tmp[l_corrent] <= tmp[r_corrent] { + nums[k] = tmp[l_corrent]; + l_corrent += 1; + } + // 否则,若“左右子数组都未全部合并完”且“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++ + else { + nums[k] = tmp[r_corrent]; + r_corrent += 1; + } + } + } - [class]{}-[func]{merge_sort} + /* 归并排序 */ + fn merge_sort(left: usize, right: usize, nums: &mut [i32]) { + // 终止条件 + if left >= right { return; } // 当子数组长度为 1 时终止递归 + // 划分阶段 + let mid = (left + right) / 2; // 计算中点 + merge_sort(left, mid, nums); // 递归左子数组 + merge_sort(mid + 1, right, nums); // 递归右子数组 + // 合并阶段 + merge(nums, left, mid, right); + } ``` === "C" ```c title="merge_sort.c" - [class]{}-[func]{merge} + /* 合并左子数组和右子数组 */ + // 左子数组区间 [left, mid] + // 右子数组区间 [mid + 1, right] + void merge(int *nums, int left, int mid, int right) { + int index; + // 初始化辅助数组 + int tmp[right + 1 - left]; + for (index = left; index < right + 1; index++) { + tmp[index - left] = nums[index]; + } + // 左子数组的起始索引和结束索引 + int leftStart = left - left, leftEnd = mid - left; + // 右子数组的起始索引和结束索引 + int rightStart = mid + 1 - left, rightEnd = right - left; + // i, j 分别指向左子数组、右子数组的首元素 + int i = leftStart, j = rightStart; + // 通过覆盖原数组 nums 来合并左子数组和右子数组 + for (int k = left; k <= right; k++) { + // 若“左子数组已全部合并完”,则选取右子数组元素,并且 j++ + if (i > leftEnd) + nums[k] = tmp[j++]; + // 否则,若“右子数组已全部合并完”或“左子数组元素 <= 右子数组元素”,则选取左子数组元素,并且 i++ + else if (j > rightEnd || tmp[i] <= tmp[j]) + nums[k] = tmp[i++]; + // 否则,若“左右子数组都未全部合并完”且“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++ + else + nums[k] = tmp[j++]; + } + } - [class]{}-[func]{mergeSort} + /* 归并排序 */ + 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); + } ``` === "Zig" ```zig title="merge_sort.zig" - [class]{}-[func]{merge} + // 合并左子数组和右子数组 + // 左子数组区间 [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]{mergeSort} + // 归并排序 + 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); + } ``` 实现合并函数 `merge()` 存在以下难点。 diff --git a/zh/chapter_sorting/quick_sort.md b/zh/chapter_sorting/quick_sort.md index 457e3d6e9..bd2c8256b 100755 --- a/zh/chapter_sorting/quick_sort.md +++ b/zh/chapter_sorting/quick_sort.md @@ -50,91 +50,314 @@ comments: true === "Python" ```python title="quick_sort.py" - [class]{QuickSort}-[func]{partition} + def partition(self, nums: list[int], left: int, right: int) -> int: + """哨兵划分""" + # 以 nums[left] 作为基准数 + i, j = left, right + while i < j: + while i < j and nums[j] >= nums[left]: + j -= 1 # 从右向左找首个小于基准数的元素 + while i < j and nums[i] <= nums[left]: + i += 1 # 从左向右找首个大于基准数的元素 + # 元素交换 + nums[i], nums[j] = nums[j], nums[i] + # 将基准数交换至两子数组的分界线 + nums[i], nums[left] = nums[left], nums[i] + return i # 返回基准数的索引 ``` === "C++" ```cpp title="quick_sort.cpp" - [class]{QuickSort}-[func]{swap} + /* 元素交换 */ + void swap(vector &nums, int i, int j) { + int tmp = nums[i]; + nums[i] = nums[j]; + nums[j] = tmp; + } - [class]{QuickSort}-[func]{partition} + /* 哨兵划分 */ + 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; // 返回基准数的索引 + } ``` === "Java" ```java title="quick_sort.java" - [class]{QuickSort}-[func]{swap} + /* 元素交换 */ + void swap(int[] nums, int i, int j) { + int tmp = nums[i]; + nums[i] = nums[j]; + nums[j] = tmp; + } - [class]{QuickSort}-[func]{partition} + /* 哨兵划分 */ + 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; // 返回基准数的索引 + } ``` === "C#" ```csharp title="quick_sort.cs" - [class]{QuickSort}-[func]{swap} + /* 元素交换 */ + void swap(int[] nums, int i, int j) { + int tmp = nums[i]; + nums[i] = nums[j]; + nums[j] = tmp; + } - [class]{QuickSort}-[func]{partition} + /* 哨兵划分 */ + 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; // 返回基准数的索引 + } ``` === "Go" ```go title="quick_sort.go" - [class]{quickSort}-[func]{partition} + /* 哨兵划分 */ + 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 // 返回基准数的索引 + } ``` === "Swift" ```swift title="quick_sort.swift" - [class]{}-[func]{swap} + /* 元素交换 */ + func swap(nums: inout [Int], i: Int, j: Int) { + let tmp = nums[i] + nums[i] = nums[j] + nums[j] = tmp + } - [class]{}-[func]{partition} + /* 哨兵划分 */ + 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 // 从左向右找首个大于基准数的元素 + } + swap(nums: &nums, i: i, j: j) // 交换这两个元素 + } + swap(nums: &nums, i: i, j: left) // 将基准数交换至两子数组的分界线 + return i // 返回基准数的索引 + } ``` === "JS" ```javascript title="quick_sort.js" - [class]{QuickSort}-[func]{swap} + /* 元素交换 */ + swap(nums, i, j) { + let tmp = nums[i]; + nums[i] = nums[j]; + nums[j] = tmp; + } - [class]{QuickSort}-[func]{partition} + /* 哨兵划分 */ + 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; // 返回基准数的索引 + } ``` === "TS" ```typescript title="quick_sort.ts" - [class]{QuickSort}-[func]{swap} + /* 元素交换 */ + swap(nums: number[], i: number, j: number): void { + let tmp = nums[i]; + nums[i] = nums[j]; + nums[j] = tmp; + } - [class]{QuickSort}-[func]{partition} + /* 哨兵划分 */ + 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; // 返回基准数的索引 + } ``` === "Dart" ```dart title="quick_sort.dart" - [class]{QuickSort}-[func]{_swap} + /* 元素交换 */ + void _swap(List nums, int i, int j) { + int tmp = nums[i]; + nums[i] = nums[j]; + nums[j] = tmp; + } - [class]{QuickSort}-[func]{_partition} + /* 哨兵划分 */ + 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; // 返回基准数的索引 + } ``` === "Rust" ```rust title="quick_sort.rs" - [class]{QuickSort}-[func]{partition} + /* 哨兵划分 */ + 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 // 返回基准数的索引 + } ``` === "C" ```c title="quick_sort.c" - [class]{}-[func]{swap} + /* 元素交换 */ + void swap(int nums[], int i, int j) { + int tmp = nums[i]; + nums[i] = nums[j]; + nums[j] = tmp; + } - [class]{}-[func]{partition} + /* 快速排序类 */ + // 快速排序类-哨兵划分 + 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; + } ``` === "Zig" ```zig title="quick_sort.zig" - [class]{QuickSort}-[func]{swap} + // 元素交换 + fn swap(nums: []i32, i: usize, j: usize) void { + var tmp = nums[i]; + nums[i] = nums[j]; + nums[j] = tmp; + } - [class]{QuickSort}-[func]{partition} + // 哨兵划分 + 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; // 返回基准数的索引 + } ``` ## 11.5.1   算法流程 @@ -152,73 +375,217 @@ comments: true === "Python" ```python title="quick_sort.py" - [class]{QuickSort}-[func]{quick_sort} + def quick_sort(self, nums: list[int], left: int, right: int): + """快速排序""" + # 子数组长度为 1 时终止递归 + if left >= right: + return + # 哨兵划分 + pivot = self.partition(nums, left, right) + # 递归左子数组、右子数组 + self.quick_sort(nums, left, pivot - 1) + self.quick_sort(nums, pivot + 1, right) ``` === "C++" ```cpp title="quick_sort.cpp" - [class]{QuickSort}-[func]{quickSort} + /* 快速排序 */ + 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); + } ``` === "Java" ```java title="quick_sort.java" - [class]{QuickSort}-[func]{quickSort} + /* 快速排序 */ + 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); + } ``` === "C#" ```csharp title="quick_sort.cs" - [class]{QuickSort}-[func]{quickSort} + /* 快速排序 */ + 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); + } ``` === "Go" ```go title="quick_sort.go" - [class]{quickSort}-[func]{quickSort} + /* 快速排序 */ + 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) + } ``` === "Swift" ```swift title="quick_sort.swift" - [class]{}-[func]{quickSort} + /* 快速排序 */ + 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) + } ``` === "JS" ```javascript title="quick_sort.js" - [class]{QuickSort}-[func]{quickSort} + /* 快速排序 */ + 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); + } ``` === "TS" ```typescript title="quick_sort.ts" - [class]{QuickSort}-[func]{quickSort} + /* 快速排序 */ + 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); + } ``` === "Dart" ```dart title="quick_sort.dart" - [class]{QuickSort}-[func]{quickSort} + /* 快速排序 */ + 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); + } ``` === "Rust" ```rust title="quick_sort.rs" - [class]{QuickSort}-[func]{quick_sort} + /* 快速排序 */ + 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); + } ``` === "C" ```c title="quick_sort.c" - [class]{}-[func]{quickSort} + /* 快速排序类 */ + // 快速排序类-哨兵划分 + 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; + } + + // 快速排序类-快速排序 + 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); + } ``` === "Zig" ```zig title="quick_sort.zig" - [class]{QuickSort}-[func]{quickSort} + // 快速排序 + 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); + } ``` ## 11.5.2   算法特性 @@ -248,97 +615,444 @@ comments: true === "Python" ```python title="quick_sort.py" - [class]{QuickSortMedian}-[func]{median_three} + def median_three(self, nums: list[int], left: int, mid: int, right: int) -> int: + """选取三个元素的中位数""" + # 此处使用异或运算来简化代码 + # 异或规则为 0 ^ 0 = 1 ^ 1 = 0, 0 ^ 1 = 1 ^ 0 = 1 + if (nums[left] < nums[mid]) ^ (nums[left] < nums[right]): + return left + elif (nums[mid] < nums[left]) ^ (nums[mid] < nums[right]): + return mid + return right - [class]{QuickSortMedian}-[func]{partition} + def partition(self, nums: list[int], left: int, right: int) -> int: + """哨兵划分(三数取中值)""" + # 以 nums[left] 作为基准数 + med = self.median_three(nums, left, (left + right) // 2, right) + # 将中位数交换至数组最左端 + nums[left], nums[med] = nums[med], nums[left] + # 以 nums[left] 作为基准数 + i, j = left, right + while i < j: + while i < j and nums[j] >= nums[left]: + j -= 1 # 从右向左找首个小于基准数的元素 + while i < j and nums[i] <= nums[left]: + i += 1 # 从左向右找首个大于基准数的元素 + # 元素交换 + nums[i], nums[j] = nums[j], nums[i] + # 将基准数交换至两子数组的分界线 + nums[i], nums[left] = nums[left], nums[i] + return i # 返回基准数的索引 ``` === "C++" ```cpp title="quick_sort.cpp" - [class]{QuickSortMedian}-[func]{medianThree} + /* 选取三个元素的中位数 */ + int medianThree(vector &nums, int left, int mid, int right) { + // 此处使用异或运算来简化代码 + // 异或规则为 0 ^ 0 = 1 ^ 1 = 0, 0 ^ 1 = 1 ^ 0 = 1 + if ((nums[left] < nums[mid]) ^ (nums[left] < nums[right])) + return left; + else if ((nums[mid] < nums[left]) ^ (nums[mid] < nums[right])) + return mid; + else + return right; + } - [class]{QuickSortMedian}-[func]{partition} + /* 哨兵划分(三数取中值) */ + 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; // 返回基准数的索引 + } ``` === "Java" ```java title="quick_sort.java" - [class]{QuickSortMedian}-[func]{medianThree} + /* 选取三个元素的中位数 */ + int medianThree(int[] nums, int left, int mid, int right) { + // 此处使用异或运算来简化代码 + // 异或规则为 0 ^ 0 = 1 ^ 1 = 0, 0 ^ 1 = 1 ^ 0 = 1 + if ((nums[left] < nums[mid]) ^ (nums[left] < nums[right])) + return left; + else if ((nums[mid] < nums[left]) ^ (nums[mid] < nums[right])) + return mid; + else + return right; + } - [class]{QuickSortMedian}-[func]{partition} + /* 哨兵划分(三数取中值) */ + 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; // 返回基准数的索引 + } ``` === "C#" ```csharp title="quick_sort.cs" - [class]{QuickSortMedian}-[func]{medianThree} + /* 选取三个元素的中位数 */ + int medianThree(int[] nums, int left, int mid, int right) { + // 此处使用异或运算来简化代码 + // 异或规则为 0 ^ 0 = 1 ^ 1 = 0, 0 ^ 1 = 1 ^ 0 = 1 + if ((nums[left] < nums[mid]) ^ (nums[left] < nums[right])) + return left; + else if ((nums[mid] < nums[left]) ^ (nums[mid] < nums[right])) + return mid; + else + return right; + } - [class]{QuickSortMedian}-[func]{partition} + /* 哨兵划分(三数取中值) */ + 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; // 返回基准数的索引 + } ``` === "Go" ```go title="quick_sort.go" - [class]{quickSortMedian}-[func]{medianThree} + /* 选取三个元素的中位数 */ + func (q *quickSortMedian) medianThree(nums []int, left, mid, right int) int { + // 此处使用异或运算来简化代码(!= 在这里起到异或的作用) + // 异或规则为 0 ^ 0 = 1 ^ 1 = 0, 0 ^ 1 = 1 ^ 0 = 1 + if (nums[left] < nums[mid]) != (nums[left] < nums[right]) { + return left + } else if (nums[mid] < nums[left]) != (nums[mid] < nums[right]) { + return mid + } + return right + } - [class]{quickSortMedian}-[func]{partition} + /* 哨兵划分(三数取中值)*/ + 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 //返回基准数的索引 + } ``` === "Swift" ```swift title="quick_sort.swift" - [class]{}-[func]{medianThree} + /* 选取三个元素的中位数 */ + func medianThree(nums: [Int], left: Int, mid: Int, right: Int) -> Int { + if (nums[left] < nums[mid]) != (nums[left] < nums[right]) { + return left + } else if (nums[mid] < nums[left]) != (nums[mid] < nums[right]) { + return mid + } else { + return right + } + } - [class]{}-[func]{partitionMedian} + /* 哨兵划分(三数取中值) */ + func partitionMedian(nums: inout [Int], left: Int, right: Int) -> Int { + // 选取三个候选元素的中位数 + let med = medianThree(nums: nums, left: left, mid: (left + right) / 2, right: right) + // 将中位数交换至数组最左端 + swap(nums: &nums, i: left, j: med) + return partition(nums: &nums, left: left, right: right) + } ``` === "JS" ```javascript title="quick_sort.js" - [class]{QuickSortMedian}-[func]{medianThree} + /* 选取三个元素的中位数 */ + medianThree(nums, left, mid, right) { + // 此处使用异或运算来简化代码 + // 异或规则为 0 ^ 0 = 1 ^ 1 = 0, 0 ^ 1 = 1 ^ 0 = 1 + if ((nums[left] < nums[mid]) ^ (nums[left] < nums[right])) return left; + else if ((nums[mid] < nums[left]) ^ (nums[mid] < nums[right])) + return mid; + else return right; + } - [class]{QuickSortMedian}-[func]{partition} + /* 哨兵划分(三数取中值) */ + 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; // 返回基准数的索引 + } ``` === "TS" ```typescript title="quick_sort.ts" - [class]{QuickSortMedian}-[func]{medianThree} + /* 选取三个元素的中位数 */ + medianThree( + nums: number[], + left: number, + mid: number, + right: number + ): number { + // 此处使用异或运算来简化代码 + // 异或规则为 0 ^ 0 = 1 ^ 1 = 0, 0 ^ 1 = 1 ^ 0 = 1 + if (Number(nums[left] < nums[mid]) ^ Number(nums[left] < nums[right])) { + return left; + } else if ( + Number(nums[mid] < nums[left]) ^ Number(nums[mid] < nums[right]) + ) { + return mid; + } else { + return right; + } + } - [class]{QuickSortMedian}-[func]{partition} + /* 哨兵划分(三数取中值) */ + 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; // 返回基准数的索引 + } ``` === "Dart" ```dart title="quick_sort.dart" - [class]{QuickSortMedian}-[func]{_medianThree} + /* 选取三个元素的中位数 */ + int _medianThree(List nums, int left, int mid, int right) { + // 此处使用异或运算来简化代码 + // 异或规则为 0 ^ 0 = 1 ^ 1 = 0, 0 ^ 1 = 1 ^ 0 = 1 + if ((nums[left] < nums[mid]) ^ (nums[left] < nums[right])) + return left; + else if ((nums[mid] < nums[left]) ^ (nums[mid] < nums[right])) + return mid; + else + return right; + } - [class]{QuickSortMedian}-[func]{_partition} + /* 哨兵划分(三数取中值) */ + 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; // 返回基准数的索引 + } ``` === "Rust" ```rust title="quick_sort.rs" - [class]{QuickSortMedian}-[func]{median_three} + /* 选取三个元素的中位数 */ + fn median_three(nums: &mut [i32], left: usize, mid: usize, right: usize) -> usize { + // 此处使用异或运算来简化代码 + // 异或规则为 0 ^ 0 = 1 ^ 1 = 0, 0 ^ 1 = 1 ^ 0 = 1 + if (nums[left] < nums[mid]) ^ (nums[left] < nums[right]) { + return left; + } else if (nums[mid] < nums[left]) ^ (nums[mid] < nums[right]) { + return mid; + } + right + } - [class]{QuickSortMedian}-[func]{partition} + /* 哨兵划分(三数取中值) */ + 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 // 返回基准数的索引 + } ``` === "C" ```c title="quick_sort.c" - [class]{}-[func]{medianThree} + /* 快速排序类(中位基准数优化) */ + // 选取三个元素的中位数 + int medianThree(int nums[], int left, int mid, int right) { + // 此处使用异或运算来简化代码 + // 异或规则为 0 ^ 0 = 1 ^ 1 = 0, 0 ^ 1 = 1 ^ 0 = 1 + if ((nums[left] < nums[mid]) ^ (nums[left] < nums[right])) + return left; + else if ((nums[mid] < nums[left]) ^ (nums[mid] < nums[right])) + return mid; + else + return right; + } - [class]{}-[func]{partitionMedian} + /* 快速排序类(中位基准数优化) */ + // 选取三个元素的中位数 + int medianThree(int nums[], int left, int mid, int right) { + // 此处使用异或运算来简化代码 + // 异或规则为 0 ^ 0 = 1 ^ 1 = 0, 0 ^ 1 = 1 ^ 0 = 1 + if ((nums[left] < nums[mid]) ^ (nums[left] < nums[right])) + return left; + else if ((nums[mid] < nums[left]) ^ (nums[mid] < nums[right])) + return mid; + else + return right; + } + + // 哨兵划分(三数取中值) + 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; // 返回基准数的索引 + } ``` === "Zig" ```zig title="quick_sort.zig" - [class]{QuickSortMedian}-[func]{medianThree} + // 选取三个元素的中位数 + fn medianThree(nums: []i32, left: usize, mid: usize, right: usize) usize { + // 此处使用异或运算来简化代码 + // 异或规则为 0 ^ 0 = 1 ^ 1 = 0, 0 ^ 1 = 1 ^ 0 = 1 + if ((nums[left] < nums[mid]) != (nums[left] < nums[right])) { + return left; + } else if ((nums[mid] < nums[left]) != (nums[mid] < nums[right])) { + return mid; + } else { + return right; + } + } - [class]{QuickSortMedian}-[func]{partition} + // 哨兵划分(三数取中值) + 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; // 返回基准数的索引 + } ``` ## 11.5.5   尾递归优化 @@ -350,71 +1064,253 @@ comments: true === "Python" ```python title="quick_sort.py" - [class]{QuickSortTailCall}-[func]{quick_sort} + def quick_sort(self, nums: list[int], left: int, right: int): + """快速排序(尾递归优化)""" + # 子数组长度为 1 时终止 + while left < right: + # 哨兵划分操作 + pivot = self.partition(nums, left, right) + # 对两个子数组中较短的那个执行快排 + if pivot - left < right - pivot: + self.quick_sort(nums, left, pivot - 1) # 递归排序左子数组 + left = pivot + 1 # 剩余未排序区间为 [pivot + 1, right] + else: + self.quick_sort(nums, pivot + 1, right) # 递归排序右子数组 + right = pivot - 1 # 剩余未排序区间为 [left, pivot - 1] ``` === "C++" ```cpp title="quick_sort.cpp" - [class]{QuickSortTailCall}-[func]{quickSort} + /* 快速排序(尾递归优化) */ + 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] + } + } + } ``` === "Java" ```java title="quick_sort.java" - [class]{QuickSortTailCall}-[func]{quickSort} + /* 快速排序(尾递归优化) */ + 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] + } + } + } ``` === "C#" ```csharp title="quick_sort.cs" - [class]{QuickSortTailCall}-[func]{quickSort} + /* 快速排序(尾递归优化) */ + 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] + } + } + } ``` === "Go" ```go title="quick_sort.go" - [class]{quickSortTailCall}-[func]{quickSort} + /* 快速排序(尾递归优化)*/ + 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] + } + } + } ``` === "Swift" ```swift title="quick_sort.swift" - [class]{}-[func]{quickSortTailCall} + /* 快速排序(尾递归优化) */ + 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] + } + } + } ``` === "JS" ```javascript title="quick_sort.js" - [class]{QuickSortTailCall}-[func]{quickSort} + /* 快速排序(尾递归优化) */ + 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] + } + } + } ``` === "TS" ```typescript title="quick_sort.ts" - [class]{QuickSortTailCall}-[func]{quickSort} + /* 快速排序(尾递归优化) */ + 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] + } + } + } ``` === "Dart" ```dart title="quick_sort.dart" - [class]{QuickSortTailCall}-[func]{quickSort} + /* 快速排序(尾递归优化) */ + 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] + } + } + } ``` === "Rust" ```rust title="quick_sort.rs" - [class]{QuickSortTailCall}-[func]{quick_sort} + /* 快速排序(尾递归优化) */ + 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] + } + } + } ``` === "C" ```c title="quick_sort.c" - [class]{}-[func]{quickSortTailCall} + /* 快速排序类(尾递归优化) */ + // 快速排序(尾递归优化) + 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); // 递归排序左子数组 + left = pivot + 1; // 剩余未排序区间为 [pivot + 1, right] + } else { + quickSortTailCall(nums, pivot + 1, right); // 递归排序右子数组 + right = pivot - 1; // 剩余未排序区间为 [left, pivot - 1] + } + } + } ``` === "Zig" ```zig title="quick_sort.zig" - [class]{QuickSortTailCall}-[func]{quickSort} + // 快速排序(尾递归优化) + 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] + } + } + } ``` diff --git a/zh/chapter_sorting/radix_sort.md b/zh/chapter_sorting/radix_sort.md index 02f8a72c3..cbb642444 100644 --- a/zh/chapter_sorting/radix_sort.md +++ b/zh/chapter_sorting/radix_sort.md @@ -33,121 +33,655 @@ $$ === "Python" ```python title="radix_sort.py" - [class]{}-[func]{digit} + def digit(num: int, exp: int) -> int: + """获取元素 num 的第 k 位,其中 exp = 10^(k-1)""" + # 传入 exp 而非 k 可以避免在此重复执行昂贵的次方计算 + return (num // exp) % 10 - [class]{}-[func]{counting_sort_digit} + def counting_sort_digit(nums: list[int], exp: int): + """计数排序(根据 nums 第 k 位排序)""" + # 十进制的位范围为 0~9 ,因此需要长度为 10 的桶 + counter = [0] * 10 + n = len(nums) + # 统计 0~9 各数字的出现次数 + for i in range(n): + d = digit(nums[i], exp) # 获取 nums[i] 第 k 位,记为 d + counter[d] += 1 # 统计数字 d 的出现次数 + # 求前缀和,将“出现个数”转换为“数组索引” + for i in range(1, 10): + counter[i] += counter[i - 1] + # 倒序遍历,根据桶内统计结果,将各元素填入 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 + for i in range(n): + nums[i] = res[i] - [class]{}-[func]{radix_sort} + def radix_sort(nums: list[int]): + """基数排序""" + # 获取数组的最大元素,用于判断最大位数 + m = max(nums) + # 按照从低位到高位的顺序遍历 + 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 ``` === "C++" ```cpp title="radix_sort.cpp" - [class]{}-[func]{digit} + /* 获取元素 num 的第 k 位,其中 exp = 10^(k-1) */ + int digit(int num, int exp) { + // 传入 exp 而非 k 可以避免在此重复执行昂贵的次方计算 + return (num / exp) % 10; + } - [class]{}-[func]{countingSortDigit} + /* 计数排序(根据 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]{radixSort} + /* 基数排序 */ + 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); + } ``` === "Java" ```java title="radix_sort.java" - [class]{radix_sort}-[func]{digit} + /* 获取元素 num 的第 k 位,其中 exp = 10^(k-1) */ + int digit(int num, int exp) { + // 传入 exp 而非 k 可以避免在此重复执行昂贵的次方计算 + return (num / exp) % 10; + } - [class]{radix_sort}-[func]{countingSortDigit} + /* 计数排序(根据 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]{radixSort} + /* 基数排序 */ + void radixSort(int[] nums) { + // 获取数组的最大元素,用于判断最大位数 + int m = Integer.MIN_VALUE; + for (int num : 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); + } ``` === "C#" ```csharp title="radix_sort.cs" - [class]{radix_sort}-[func]{digit} + /* 获取元素 num 的第 k 位,其中 exp = 10^(k-1) */ + int digit(int num, int exp) { + // 传入 exp 而非 k 可以避免在此重复执行昂贵的次方计算 + return (num / exp) % 10; + } - [class]{radix_sort}-[func]{countingSortDigit} + /* 计数排序(根据 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]{radixSort} + /* 基数排序 */ + 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); + } + } ``` === "Go" ```go title="radix_sort.go" - [class]{}-[func]{digit} + /* 获取元素 num 的第 k 位,其中 exp = 10^(k-1) */ + func digit(num, exp int) int { + // 传入 exp 而非 k 可以避免在此重复执行昂贵的次方计算 + return (num / exp) % 10 + } - [class]{}-[func]{countingSortDigit} + /* 计数排序(根据 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]{radixSort} + /* 基数排序 */ + 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) + } + } ``` === "Swift" ```swift title="radix_sort.swift" - [class]{}-[func]{digit} + /* 获取元素 num 的第 k 位,其中 exp = 10^(k-1) */ + func digit(num: Int, exp: Int) -> Int { + // 传入 exp 而非 k 可以避免在此重复执行昂贵的次方计算 + (num / exp) % 10 + } - [class]{}-[func]{countingSortDigit} + /* 计数排序(根据 nums 第 k 位排序) */ + func countingSortDigit(nums: inout [Int], exp: Int) { + // 十进制的位范围为 0~9 ,因此需要长度为 10 的桶 + var counter = Array(repeating: 0, count: 10) + let n = nums.count + // 统计 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: n) + for i in stride(from: n - 1, through: 0, by: -1) { + 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]{radixSort} + /* 基数排序 */ + 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) + } + } ``` === "JS" ```javascript title="radix_sort.js" - [class]{}-[func]{digit} + /* 获取元素 num 的第 k 位,其中 exp = 10^(k-1) */ + function digit(num, exp) { + // 传入 exp 而非 k 可以避免在此重复执行昂贵的次方计算 + return Math.floor(num / exp) % 10; + } - [class]{}-[func]{countingSortDigit} + /* 计数排序(根据 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]{radixSort} + /* 基数排序 */ + 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); + } + } ``` === "TS" ```typescript title="radix_sort.ts" - [class]{}-[func]{digit} + /* 获取元素 num 的第 k 位,其中 exp = 10^(k-1) */ + function digit(num: number, exp: number): number { + // 传入 exp 而非 k 可以避免在此重复执行昂贵的次方计算 + return Math.floor(num / exp) % 10; + } - [class]{}-[func]{countingSortDigit} + /* 计数排序(根据 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]{radixSort} + /* 基数排序 */ + 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); + } + } ``` === "Dart" ```dart title="radix_sort.dart" - [class]{}-[func]{digit} + /* 获取元素 num 的第 k 位,其中 exp = 10^(k-1) */ + int digit(int num, int exp) { + // 传入 exp 而非 k 可以避免在此重复执行昂贵的次方计算 + return (num ~/ exp) % 10; + } - [class]{}-[func]{countingSortDigit} + /* 计数排序(根据 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]{radixSort} + /* 基数排序 */ + 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); + } ``` === "Rust" ```rust title="radix_sort.rs" - [class]{}-[func]{digit} + /* 获取元素 num 的第 k 位,其中 exp = 10^(k-1) */ + fn digit(num: i32, exp: i32) -> usize { + // 传入 exp 而非 k 可以避免在此重复执行昂贵的次方计算 + return ((num / exp) % 10) as usize; + } - [class]{}-[func]{counting_sort_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]{radix_sort} + /* 基数排序 */ + 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; + } + } ``` === "C" ```c title="radix_sort.c" - [class]{}-[func]{digit} + /* 获取元素 num 的第 k 位,其中 exp = 10^(k-1) */ + int digit(int num, int exp) { + // 传入 exp 而非 k 可以避免在此重复执行昂贵的次方计算 + return (num / exp) % 10; + } - [class]{}-[func]{countingSortDigit} + /* 计数排序(根据 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]{radixSort} + /* 基数排序 */ + 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); + } ``` === "Zig" ```zig title="radix_sort.zig" - [class]{}-[func]{digit} + // 获取元素 num 的第 k 位,其中 exp = 10^(k-1) + fn digit(num: i32, exp: i32) i32 { + // 传入 exp 而非 k 可以避免在此重复执行昂贵的次方计算 + return @mod(@divFloor(num, exp), 10); + } - [class]{}-[func]{countingSortDigit} + // 计数排序(根据 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]{radixSort} + // 基数排序 + 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); + } + } ``` !!! question "为什么从最低位开始排序?" diff --git a/zh/chapter_sorting/selection_sort.md b/zh/chapter_sorting/selection_sort.md index c59583caa..a0817e8df 100644 --- a/zh/chapter_sorting/selection_sort.md +++ b/zh/chapter_sorting/selection_sort.md @@ -54,67 +54,228 @@ comments: true === "Python" ```python title="selection_sort.py" - [class]{}-[func]{selection_sort} + def selection_sort(nums: list[int]): + """选择排序""" + n = len(nums) + # 外循环:未排序区间为 [i, n-1] + for i in range(n - 1): + # 内循环:找到未排序区间内的最小元素 + k = i + for j in range(i + 1, n): + if nums[j] < nums[k]: + k = j # 记录最小元素的索引 + # 将该最小元素与未排序区间的首个元素交换 + nums[i], nums[k] = nums[k], nums[i] ``` === "C++" ```cpp title="selection_sort.cpp" - [class]{}-[func]{selectionSort} + /* 选择排序 */ + 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]); + } + } ``` === "Java" ```java title="selection_sort.java" - [class]{selection_sort}-[func]{selectionSort} + /* 选择排序 */ + 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; // 记录最小元素的索引 + } + // 将该最小元素与未排序区间的首个元素交换 + int temp = nums[i]; + nums[i] = nums[k]; + nums[k] = temp; + } + } ``` === "C#" ```csharp title="selection_sort.cs" - [class]{selection_sort}-[func]{selectionSort} + /* 选择排序 */ + 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]); + } + } ``` === "Go" ```go title="selection_sort.go" - [class]{}-[func]{selectionSort} + /* 选择排序 */ + 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] + + } + } ``` === "Swift" ```swift title="selection_sort.swift" - [class]{}-[func]{selectionSort} + /* 选择排序 */ + 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) + } + } ``` === "JS" ```javascript title="selection_sort.js" - [class]{}-[func]{selectionSort} + /* 选择排序 */ + 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]]; + } + } ``` === "TS" ```typescript title="selection_sort.ts" - [class]{}-[func]{selectionSort} + /* 选择排序 */ + 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]]; + } + } ``` === "Dart" ```dart title="selection_sort.dart" - [class]{}-[func]{selectionSort} + /* 选择排序 */ + 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; + } + } ``` === "Rust" ```rust title="selection_sort.rs" - [class]{}-[func]{selection_sort} + /* 选择排序 */ + fn selection_sort(nums: &mut [i32]) { + 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); + } + } ``` === "C" ```c title="selection_sort.c" - [class]{}-[func]{selectionSort} + /* 选择排序 */ + 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; + } + } ``` === "Zig" diff --git a/zh/chapter_stack_and_queue/deque.md b/zh/chapter_stack_and_queue/deque.md index 182bf4fd0..740611222 100644 --- a/zh/chapter_stack_and_queue/deque.md +++ b/zh/chapter_stack_and_queue/deque.md @@ -386,95 +386,1625 @@ comments: true === "Python" ```python title="linkedlist_deque.py" - [class]{ListNode}-[func]{} + class ListNode: + """双向链表节点""" - [class]{LinkedListDeque}-[func]{} + def __init__(self, val: int): + """构造方法""" + self.val: int = val + self.next: ListNode | None = None # 后继节点引用 + self.prev: ListNode | None = None # 前驱节点引用 + + class LinkedListDeque: + """基于双向链表实现的双向队列""" + + def __init__(self): + """构造方法""" + self.front: ListNode | None = None # 头节点 front + self.rear: ListNode | None = None # 尾节点 rear + self.__size: int = 0 # 双向队列的长度 + + def size(self) -> int: + """获取双向队列的长度""" + return self.__size + + def is_empty(self) -> bool: + """判断双向队列是否为空""" + return self.size() == 0 + + def push(self, num: int, is_front: bool): + """入队操作""" + node = ListNode(num) + # 若链表为空,则令 front, rear 都指向 node + if self.is_empty(): + self.front = self.rear = node + # 队首入队操作 + elif 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.__size += 1 # 更新队列长度 + + def push_first(self, num: int): + """队首入队""" + self.push(num, True) + + def push_last(self, num: int): + """队尾入队""" + self.push(num, False) + + def pop(self, is_front: bool) -> int: + """出队操作""" + if self.is_empty(): + raise IndexError("双向队列为空") + # 队首出队操作 + if is_front: + val: int = self.front.val # 暂存头节点值 + # 删除头节点 + fnext: ListNode | None = self.front.next + if fnext != None: + fnext.prev = None + self.front.next = None + self.front = fnext # 更新头节点 + # 队尾出队操作 + else: + val: int = self.rear.val # 暂存尾节点值 + # 删除尾节点 + rprev: ListNode | None = self.rear.prev + if rprev != None: + rprev.next = None + self.rear.prev = None + self.rear = rprev # 更新尾节点 + self.__size -= 1 # 更新队列长度 + return val + + def pop_first(self) -> int: + """队首出队""" + return self.pop(True) + + def pop_last(self) -> int: + """队尾出队""" + return self.pop(False) + + def peek_first(self) -> int: + """访问队首元素""" + if self.is_empty(): + raise IndexError("双向队列为空") + return self.front.val + + def peek_last(self) -> int: + """访问队尾元素""" + if self.is_empty(): + raise IndexError("双向队列为空") + return self.rear.val + + def to_array(self) -> list[int]: + """返回数组用于打印""" + node = self.front + res = [0] * self.size() + for i in range(self.size()): + res[i] = node.val + node = node.next + return res ``` === "C++" ```cpp title="linkedlist_deque.cpp" - [class]{DoublyListNode}-[func]{} + /* 双向链表节点 */ + struct DoublyListNode { + int val; // 节点值 + DoublyListNode *next; // 后继节点指针 + DoublyListNode *prev; // 前驱节点指针 + DoublyListNode(int val) : val(val), prev(nullptr), next(nullptr) { + } + }; - [class]{LinkedListDeque}-[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; + } + }; ``` === "Java" ```java title="linkedlist_deque.java" - [class]{ListNode}-[func]{} + /* 双向链表节点 */ + class ListNode { + int val; // 节点值 + ListNode next; // 后继节点引用 + ListNode prev; // 前驱节点引用 - [class]{LinkedListDeque}-[func]{} + ListNode(int val) { + this.val = val; + prev = next = null; + } + } + + /* 基于双向链表实现的双向队列 */ + class LinkedListDeque { + private ListNode front, rear; // 头节点 front ,尾节点 rear + private int queSize = 0; // 双向队列的长度 + + public LinkedListDeque() { + front = rear = null; + } + + /* 获取双向队列的长度 */ + public int size() { + return queSize; + } + + /* 判断双向队列是否为空 */ + public boolean isEmpty() { + return size() == 0; + } + + /* 入队操作 */ + private void push(int num, boolean isFront) { + ListNode node = new ListNode(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++; // 更新队列长度 + } + + /* 队首入队 */ + public void pushFirst(int num) { + push(num, true); + } + + /* 队尾入队 */ + public void pushLast(int num) { + push(num, false); + } + + /* 出队操作 */ + private int pop(boolean isFront) { + if (isEmpty()) + throw new IndexOutOfBoundsException(); + 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 IndexOutOfBoundsException(); + return front.val; + } + + /* 访问队尾元素 */ + public int peekLast() { + if (isEmpty()) + throw new IndexOutOfBoundsException(); + 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; + } + } ``` === "C#" ```csharp title="linkedlist_deque.cs" - [class]{ListNode}-[func]{} + /* 双向链表节点 */ + class ListNode { + public int val; // 节点值 + public ListNode? next; // 后继节点引用 + public ListNode? prev; // 前驱节点引用 - [class]{LinkedListDeque}-[func]{} + public ListNode(int val) { + this.val = val; + prev = null; + next = null; + } + } + + /* 基于双向链表实现的双向队列 */ + class LinkedListDeque { + private ListNode? front, rear; // 头节点 front, 尾节点 rear + private int queSize = 0; // 双向队列的长度 + + public LinkedListDeque() { + front = null; + rear = null; + } + + /* 获取双向队列的长度 */ + public int size() { + return queSize; + } + + /* 判断双向队列是否为空 */ + public bool isEmpty() { + return size() == 0; + } + + /* 入队操作 */ + private void push(int num, bool isFront) { + ListNode node = new ListNode(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); + } + + /* 出队操作 */ + private 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; + } + } ``` === "Go" ```go title="linkedlist_deque.go" - [class]{linkedListDeque}-[func]{} + /* 基于双向链表实现的双向队列 */ + 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 + } ``` === "Swift" ```swift title="linkedlist_deque.swift" - [class]{ListNode}-[func]{} + /* 双向链表节点 */ + class ListNode { + var val: Int // 节点值 + var next: ListNode? // 后继节点引用 + weak var prev: ListNode? // 前驱节点引用 - [class]{LinkedListDeque}-[func]{} + init(val: Int) { + self.val = val + } + } + + /* 基于双向链表实现的双向队列 */ + class LinkedListDeque { + private var front: ListNode? // 头节点 front + private var rear: ListNode? // 尾节点 rear + private var queSize: Int // 双向队列的长度 + + init() { + queSize = 0 + } + + /* 获取双向队列的长度 */ + func size() -> Int { + queSize + } + + /* 判断双向队列是否为空 */ + 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 // 更新尾节点 + } + queSize += 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 // 更新尾节点 + } + queSize -= 1 // 更新队列长度 + return val + } + + /* 队首出队 */ + func popFirst() -> Int { + pop(isFront: true) + } + + /* 队尾出队 */ + func popLast() -> Int { + pop(isFront: false) + } + + /* 访问队首元素 */ + func peekFirst() -> Int? { + isEmpty() ? nil : front?.val + } + + /* 访问队尾元素 */ + func peekLast() -> Int? { + isEmpty() ? nil : 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 + } + } ``` === "JS" ```javascript title="linkedlist_deque.js" - [class]{ListNode}-[func]{} + /* 双向链表节点 */ + class ListNode { + prev; // 前驱节点引用 (指针) + next; // 后继节点引用 (指针) + val; // 节点值 - [class]{LinkedListDeque}-[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(', ') + ']'); + } + } ``` === "TS" ```typescript title="linkedlist_deque.ts" - [class]{ListNode}-[func]{} + /* 双向链表节点 */ + class ListNode { + prev: ListNode; // 前驱节点引用 (指针) + next: ListNode; // 后继节点引用 (指针) + val: number; // 节点值 - [class]{LinkedListDeque}-[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(', ') + ']'); + } + } ``` === "Dart" ```dart title="linkedlist_deque.dart" - [class]{ListNode}-[func]{} + /* 双向链表节点 */ + class ListNode { + int val; // 节点值 + ListNode? next; // 后继节点引用 + ListNode? prev; // 前驱节点引用 - [class]{LinkedListDeque}-[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; + } + } ``` === "Rust" ```rust title="linkedlist_deque.rs" - [class]{ListNode}-[func]{} + /* 双向链表节点 */ + pub struct ListNode { + pub val: T, // 节点值 + pub next: Option>>>, // 后继节点指针 + pub prev: Option>>>, // 前驱节点指针 + } - [class]{LinkedListDeque}-[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(); + } + } ``` === "C" ```c title="linkedlist_deque.c" - [class]{doublyListNode}-[func]{} + /* 双向链表节点 */ + struct doublyListNode { + int val; // 节点值 + struct doublyListNode *next; // 后继节点 + struct doublyListNode *prev; // 前驱节点 + }; - [class]{linkedListDeque}-[func]{} + typedef struct doublyListNode doublyListNode; + + /* 构造函数 */ + 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); + } + + /* 基于双向链表实现的双向队列 */ + struct linkedListDeque { + doublyListNode *front, *rear; // 头节点 front ,尾节点 rear + int queSize; // 双向队列的长度 + }; + + typedef struct linkedListDeque 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[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); + } ``` === "Zig" ```zig title="linkedlist_deque.zig" - [class]{ListNode}-[func]{} + // 双向链表节点 + fn ListNode(comptime T: type) type { + return struct { + const Self = @This(); + + val: T = undefined, // 节点值 + next: ?*Self = null, // 后继节点指针 + prev: ?*Self = null, // 前驱节点指针 - [class]{LinkedListDeque}-[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; + } + }; + } ``` ### 2.   基于数组的实现 @@ -503,67 +2033,1207 @@ comments: true === "Python" ```python title="array_deque.py" - [class]{ArrayDeque}-[func]{} + class ArrayDeque: + """基于环形数组实现的双向队列""" + + def __init__(self, capacity: int): + """构造方法""" + self.__nums: list[int] = [0] * capacity + self.__front: int = 0 + self.__size: int = 0 + + def capacity(self) -> int: + """获取双向队列的容量""" + return len(self.__nums) + + def size(self) -> int: + """获取双向队列的长度""" + return self.__size + + def is_empty(self) -> bool: + """判断双向队列是否为空""" + return self.__size == 0 + + def index(self, i: int) -> int: + """计算环形数组索引""" + # 通过取余操作实现数组首尾相连 + # 当 i 越过数组尾部后,回到头部 + # 当 i 越过数组头部后,回到尾部 + return (i + self.capacity()) % self.capacity() + + def push_first(self, num: int): + """队首入队""" + if self.__size == self.capacity(): + print("双向队列已满") + return + # 队首指针向左移动一位 + # 通过取余操作,实现 front 越过数组头部后回到尾部 + self.__front = self.index(self.__front - 1) + # 将 num 添加至队首 + self.__nums[self.__front] = num + self.__size += 1 + + def push_last(self, num: int): + """队尾入队""" + if self.__size == self.capacity(): + print("双向队列已满") + return + # 计算尾指针,指向队尾索引 + 1 + rear = self.index(self.__front + self.__size) + # 将 num 添加至队尾 + self.__nums[rear] = num + self.__size += 1 + + def pop_first(self) -> int: + """队首出队""" + num = self.peek_first() + # 队首指针向后移动一位 + self.__front = self.index(self.__front + 1) + self.__size -= 1 + return num + + def pop_last(self) -> int: + """队尾出队""" + num = self.peek_last() + self.__size -= 1 + return num + + def peek_first(self) -> int: + """访问队首元素""" + if self.is_empty(): + raise IndexError("双向队列为空") + return self.__nums[self.__front] + + def peek_last(self) -> int: + """访问队尾元素""" + if self.is_empty(): + raise IndexError("双向队列为空") + # 计算尾元素索引 + last = self.index(self.__front + self.__size - 1) + return self.__nums[last] + + def to_array(self) -> list[int]: + """返回数组用于打印""" + # 仅转换有效长度范围内的列表元素 + res = [] + for i in range(self.__size): + res.append(self.__nums[self.index(self.__front + i)]) + return res ``` === "C++" ```cpp title="array_deque.cpp" - [class]{ArrayDeque}-[func]{} + /* 基于环形数组实现的双向队列 */ + class ArrayDeque { + private: + vector nums; // 用于存储双向队列元素的数组 + int front; // 队首指针,指向队首元素 + int queSize; // 双向队列长度 + + public: + /* 构造方法 */ + ArrayDeque(int capacity) { + nums.resize(capacity); + front = queSize = 0; + } + + /* 获取双向队列的容量 */ + int capacity() { + return nums.size(); + } + + /* 获取双向队列的长度 */ + int size() { + return queSize; + } + + /* 判断双向队列是否为空 */ + bool isEmpty() { + return queSize == 0; + } + + /* 计算环形数组索引 */ + int index(int i) { + // 通过取余操作实现数组首尾相连 + // 当 i 越过数组尾部后,回到头部 + // 当 i 越过数组头部后,回到尾部 + return (i + capacity()) % capacity(); + } + + /* 队首入队 */ + void pushFirst(int num) { + if (queSize == capacity()) { + cout << "双向队列已满" << endl; + return; + } + // 队首指针向左移动一位 + // 通过取余操作,实现 front 越过数组头部后回到尾部 + front = index(front - 1); + // 将 num 添加至队首 + nums[front] = num; + queSize++; + } + + /* 队尾入队 */ + void pushLast(int num) { + if (queSize == capacity()) { + cout << "双向队列已满" << endl; + return; + } + // 计算尾指针,指向队尾索引 + 1 + int rear = index(front + queSize); + // 将 num 添加至队尾 + nums[rear] = num; + queSize++; + } + + /* 队首出队 */ + int popFirst() { + int num = peekFirst(); + // 队首指针向后移动一位 + front = index(front + 1); + queSize--; + return num; + } + + /* 队尾出队 */ + int popLast() { + int num = peekLast(); + queSize--; + return num; + } + + /* 访问队首元素 */ + int peekFirst() { + if (isEmpty()) + throw out_of_range("双向队列为空"); + return nums[front]; + } + + /* 访问队尾元素 */ + int peekLast() { + if (isEmpty()) + throw out_of_range("双向队列为空"); + // 计算尾元素索引 + int last = index(front + queSize - 1); + return nums[last]; + } + + /* 返回数组用于打印 */ + vector toVector() { + // 仅转换有效长度范围内的列表元素 + vector res(queSize); + for (int i = 0, j = front; i < queSize; i++, j++) { + res[i] = nums[index(j)]; + } + return res; + } + }; ``` === "Java" ```java title="array_deque.java" - [class]{ArrayDeque}-[func]{} + /* 基于环形数组实现的双向队列 */ + class ArrayDeque { + private int[] nums; // 用于存储双向队列元素的数组 + private int front; // 队首指针,指向队首元素 + private int queSize; // 双向队列长度 + + /* 构造方法 */ + public ArrayDeque(int capacity) { + this.nums = new int[capacity]; + front = queSize = 0; + } + + /* 获取双向队列的容量 */ + public int capacity() { + return nums.length; + } + + /* 获取双向队列的长度 */ + public int size() { + return queSize; + } + + /* 判断双向队列是否为空 */ + public boolean isEmpty() { + return queSize == 0; + } + + /* 计算环形数组索引 */ + private int index(int i) { + // 通过取余操作实现数组首尾相连 + // 当 i 越过数组尾部后,回到头部 + // 当 i 越过数组头部后,回到尾部 + return (i + capacity()) % capacity(); + } + + /* 队首入队 */ + public void pushFirst(int num) { + if (queSize == capacity()) { + System.out.println("双向队列已满"); + return; + } + // 队首指针向左移动一位 + // 通过取余操作,实现 front 越过数组头部后回到尾部 + front = index(front - 1); + // 将 num 添加至队首 + nums[front] = num; + queSize++; + } + + /* 队尾入队 */ + public void pushLast(int num) { + if (queSize == capacity()) { + System.out.println("双向队列已满"); + return; + } + // 计算尾指针,指向队尾索引 + 1 + int rear = index(front + queSize); + // 将 num 添加至队尾 + nums[rear] = num; + queSize++; + } + + /* 队首出队 */ + public int popFirst() { + int num = peekFirst(); + // 队首指针向后移动一位 + front = index(front + 1); + queSize--; + return num; + } + + /* 队尾出队 */ + public int popLast() { + int num = peekLast(); + queSize--; + return num; + } + + /* 访问队首元素 */ + public int peekFirst() { + if (isEmpty()) + throw new IndexOutOfBoundsException(); + return nums[front]; + } + + /* 访问队尾元素 */ + public int peekLast() { + if (isEmpty()) + throw new IndexOutOfBoundsException(); + // 计算尾元素索引 + int last = index(front + queSize - 1); + return nums[last]; + } + + /* 返回数组用于打印 */ + public int[] toArray() { + // 仅转换有效长度范围内的列表元素 + int[] res = new int[queSize]; + for (int i = 0, j = front; i < queSize; i++, j++) { + res[i] = nums[index(j)]; + } + return res; + } + } ``` === "C#" ```csharp title="array_deque.cs" - [class]{ArrayDeque}-[func]{} + /* 基于环形数组实现的双向队列 */ + class ArrayDeque { + private readonly int[] nums; // 用于存储双向队列元素的数组 + private int front; // 队首指针,指向队首元素 + private int queSize; // 双向队列长度 + + /* 构造方法 */ + public ArrayDeque(int capacity) { + this.nums = new int[capacity]; + front = queSize = 0; + } + + /* 获取双向队列的容量 */ + public int capacity() { + return nums.Length; + } + + /* 获取双向队列的长度 */ + public int size() { + return queSize; + } + + /* 判断双向队列是否为空 */ + public bool isEmpty() { + return queSize == 0; + } + + /* 计算环形数组索引 */ + private int index(int i) { + // 通过取余操作实现数组首尾相连 + // 当 i 越过数组尾部后,回到头部 + // 当 i 越过数组头部后,回到尾部 + return (i + capacity()) % capacity(); + } + + /* 队首入队 */ + public void pushFirst(int num) { + if (queSize == capacity()) { + Console.WriteLine("双向队列已满"); + return; + } + // 队首指针向左移动一位 + // 通过取余操作,实现 front 越过数组头部后回到尾部 + front = index(front - 1); + // 将 num 添加至队首 + nums[front] = num; + queSize++; + } + + /* 队尾入队 */ + public void pushLast(int num) { + if (queSize == capacity()) { + Console.WriteLine("双向队列已满"); + return; + } + // 计算尾指针,指向队尾索引 + 1 + int rear = index(front + queSize); + // 将 num 添加至队尾 + nums[rear] = num; + queSize++; + } + + /* 队首出队 */ + public int popFirst() { + int num = peekFirst(); + // 队首指针向后移动一位 + front = index(front + 1); + queSize--; + return num; + } + + /* 队尾出队 */ + public int popLast() { + int num = peekLast(); + queSize--; + return num; + } + + /* 访问队首元素 */ + public int peekFirst() { + if (isEmpty()) { + throw new InvalidOperationException(); + } + return nums[front]; + } + + /* 访问队尾元素 */ + public int peekLast() { + if (isEmpty()) { + throw new InvalidOperationException(); + } + // 计算尾元素索引 + int last = index(front + queSize - 1); + return nums[last]; + } + + /* 返回数组用于打印 */ + public int[] toArray() { + // 仅转换有效长度范围内的列表元素 + int[] res = new int[queSize]; + for (int i = 0, j = front; i < queSize; i++, j++) { + res[i] = nums[index(j)]; + } + return res; + } + } ``` === "Go" ```go title="array_deque.go" - [class]{arrayDeque}-[func]{} + /* 基于环形数组实现的双向队列 */ + type arrayDeque struct { + nums []int // 用于存储双向队列元素的数组 + front int // 队首指针,指向队首元素 + queSize int // 双向队列长度 + queCapacity int // 队列容量(即最大容纳元素数量) + } + + /* 初始化队列 */ + func newArrayDeque(queCapacity int) *arrayDeque { + return &arrayDeque{ + nums: make([]int, queCapacity), + queCapacity: queCapacity, + front: 0, + queSize: 0, + } + } + + /* 获取双向队列的长度 */ + func (q *arrayDeque) size() int { + return q.queSize + } + + /* 判断双向队列是否为空 */ + func (q *arrayDeque) isEmpty() bool { + return q.queSize == 0 + } + + /* 计算环形数组索引 */ + func (q *arrayDeque) index(i int) int { + // 通过取余操作实现数组首尾相连 + // 当 i 越过数组尾部后,回到头部 + // 当 i 越过数组头部后,回到尾部 + return (i + q.queCapacity) % q.queCapacity + } + + /* 队首入队 */ + func (q *arrayDeque) pushFirst(num int) { + if q.queSize == q.queCapacity { + fmt.Println("双向队列已满") + return + } + // 队首指针向左移动一位 + // 通过取余操作,实现 front 越过数组头部后回到尾部 + q.front = q.index(q.front - 1) + // 将 num 添加至队首 + q.nums[q.front] = num + q.queSize++ + } + + /* 队尾入队 */ + func (q *arrayDeque) pushLast(num int) { + if q.queSize == q.queCapacity { + fmt.Println("双向队列已满") + return + } + // 计算尾指针,指向队尾索引 + 1 + rear := q.index(q.front + q.queSize) + // 将 num 添加至队首 + q.nums[rear] = num + q.queSize++ + } + + /* 队首出队 */ + func (q *arrayDeque) popFirst() any { + num := q.peekFirst() + // 队首指针向后移动一位 + q.front = q.index(q.front + 1) + q.queSize-- + return num + } + + /* 队尾出队 */ + func (q *arrayDeque) popLast() any { + num := q.peekLast() + q.queSize-- + return num + } + + /* 访问队首元素 */ + func (q *arrayDeque) peekFirst() any { + if q.isEmpty() { + return nil + } + return q.nums[q.front] + } + + /* 访问队尾元素 */ + func (q *arrayDeque) peekLast() any { + if q.isEmpty() { + return nil + } + // 计算尾元素索引 + last := q.index(q.front + q.queSize - 1) + return q.nums[last] + } + + /* 获取 Slice 用于打印 */ + func (q *arrayDeque) toSlice() []int { + // 仅转换有效长度范围内的列表元素 + res := make([]int, q.queSize) + for i, j := 0, q.front; i < q.queSize; i++ { + res[i] = q.nums[q.index(j)] + j++ + } + return res + } ``` === "Swift" ```swift title="array_deque.swift" - [class]{ArrayDeque}-[func]{} + /* 基于环形数组实现的双向队列 */ + class ArrayDeque { + private var nums: [Int] // 用于存储双向队列元素的数组 + private var front: Int // 队首指针,指向队首元素 + private var queSize: Int // 双向队列长度 + + /* 构造方法 */ + init(capacity: Int) { + nums = Array(repeating: 0, count: capacity) + front = 0 + queSize = 0 + } + + /* 获取双向队列的容量 */ + func capacity() -> Int { + nums.count + } + + /* 获取双向队列的长度 */ + func size() -> Int { + queSize + } + + /* 判断双向队列是否为空 */ + 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 + queSize += 1 + } + + /* 队尾入队 */ + func pushLast(num: Int) { + if size() == capacity() { + print("双向队列已满") + return + } + // 计算尾指针,指向队尾索引 + 1 + let rear = index(i: front + size()) + // 将 num 添加至队尾 + nums[rear] = num + queSize += 1 + } + + /* 队首出队 */ + func popFirst() -> Int { + let num = peekFirst() + // 队首指针向后移动一位 + front = index(i: front + 1) + queSize -= 1 + return num + } + + /* 队尾出队 */ + func popLast() -> Int { + let num = peekLast() + queSize -= 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] { + // 仅转换有效长度范围内的列表元素 + var res = Array(repeating: 0, count: size()) + for (i, j) in sequence(first: (0, front), next: { $0 < self.size() - 1 ? ($0 + 1, $1 + 1) : nil }) { + res[i] = nums[index(i: j)] + } + return res + } + } ``` === "JS" ```javascript title="array_deque.js" - [class]{ArrayDeque}-[func]{} + /* 基于环形数组实现的双向队列 */ + class ArrayDeque { + #nums; // 用于存储双向队列元素的数组 + #front; // 队首指针,指向队首元素 + #queSize; // 双向队列长度 + + /* 构造方法 */ + constructor(capacity) { + this.#nums = new Array(capacity); + this.#front = 0; + this.#queSize = 0; + } + + /* 获取双向队列的容量 */ + capacity() { + return this.#nums.length; + } + + /* 获取双向队列的长度 */ + size() { + return this.#queSize; + } + + /* 判断双向队列是否为空 */ + isEmpty() { + return this.#queSize === 0; + } + + /* 计算环形数组索引 */ + index(i) { + // 通过取余操作实现数组首尾相连 + // 当 i 越过数组尾部后,回到头部 + // 当 i 越过数组头部后,回到尾部 + return (i + this.capacity()) % this.capacity(); + } + + /* 队首入队 */ + pushFirst(num) { + if (this.#queSize === this.capacity()) { + console.log('双向队列已满'); + return; + } + // 队首指针向左移动一位 + // 通过取余操作,实现 front 越过数组头部后回到尾部 + this.#front = this.index(this.#front - 1); + // 将 num 添加至队首 + this.#nums[this.#front] = num; + this.#queSize++; + } + + /* 队尾入队 */ + pushLast(num) { + if (this.#queSize === this.capacity()) { + console.log('双向队列已满'); + return; + } + // 计算尾指针,指向队尾索引 + 1 + const rear = this.index(this.#front + this.#queSize); + // 将 num 添加至队尾 + this.#nums[rear] = num; + this.#queSize++; + } + + /* 队首出队 */ + popFirst() { + const num = this.peekFirst(); + // 队首指针向后移动一位 + this.#front = this.index(this.#front + 1); + this.#queSize--; + return num; + } + + /* 队尾出队 */ + popLast() { + const num = this.peekLast(); + this.#queSize--; + return num; + } + + /* 访问队首元素 */ + peekFirst() { + if (this.isEmpty()) throw new Error('The Deque Is Empty.'); + return this.#nums[this.#front]; + } + + /* 访问队尾元素 */ + peekLast() { + if (this.isEmpty()) throw new Error('The Deque Is Empty.'); + // 计算尾元素索引 + const last = this.index(this.#front + this.#queSize - 1); + return this.#nums[last]; + } + + /* 返回数组用于打印 */ + toArray() { + // 仅转换有效长度范围内的列表元素 + const res = []; + for (let i = 0, j = this.#front; i < this.#queSize; i++, j++) { + res[i] = this.#nums[this.index(j)]; + } + return res; + } + } ``` === "TS" ```typescript title="array_deque.ts" - [class]{ArrayDeque}-[func]{} + /* 基于环形数组实现的双向队列 */ + class ArrayDeque { + private nums: number[]; // 用于存储双向队列元素的数组 + private front: number; // 队首指针,指向队首元素 + private queSize: number; // 双向队列长度 + + /* 构造方法 */ + constructor(capacity: number) { + this.nums = new Array(capacity); + this.front = 0; + this.queSize = 0; + } + + /* 获取双向队列的容量 */ + capacity(): number { + return this.nums.length; + } + + /* 获取双向队列的长度 */ + size(): number { + return this.queSize; + } + + /* 判断双向队列是否为空 */ + isEmpty(): boolean { + return this.queSize === 0; + } + + /* 计算环形数组索引 */ + index(i: number): number { + // 通过取余操作实现数组首尾相连 + // 当 i 越过数组尾部后,回到头部 + // 当 i 越过数组头部后,回到尾部 + return (i + this.capacity()) % this.capacity(); + } + + /* 队首入队 */ + pushFirst(num: number): void { + if (this.queSize === this.capacity()) { + console.log('双向队列已满'); + return; + } + // 队首指针向左移动一位 + // 通过取余操作,实现 front 越过数组头部后回到尾部 + this.front = this.index(this.front - 1); + // 将 num 添加至队首 + this.nums[this.front] = num; + this.queSize++; + } + + /* 队尾入队 */ + pushLast(num: number): void { + if (this.queSize === this.capacity()) { + console.log('双向队列已满'); + return; + } + // 计算尾指针,指向队尾索引 + 1 + const rear: number = this.index(this.front + this.queSize); + // 将 num 添加至队尾 + this.nums[rear] = num; + this.queSize++; + } + + /* 队首出队 */ + popFirst(): number { + const num: number = this.peekFirst(); + // 队首指针向后移动一位 + this.front = this.index(this.front + 1); + this.queSize--; + return num; + } + + /* 队尾出队 */ + popLast(): number { + const num: number = this.peekLast(); + this.queSize--; + return num; + } + + /* 访问队首元素 */ + peekFirst(): number { + if (this.isEmpty()) throw new Error('The Deque Is Empty.'); + return this.nums[this.front]; + } + + /* 访问队尾元素 */ + peekLast(): number { + if (this.isEmpty()) throw new Error('The Deque Is Empty.'); + // 计算尾元素索引 + const last = this.index(this.front + this.queSize - 1); + return this.nums[last]; + } + + /* 返回数组用于打印 */ + toArray(): number[] { + // 仅转换有效长度范围内的列表元素 + const res: number[] = []; + for (let i = 0, j = this.front; i < this.queSize; i++, j++) { + res[i] = this.nums[this.index(j)]; + } + return res; + } + } ``` === "Dart" ```dart title="array_deque.dart" - [class]{ArrayDeque}-[func]{} + /* 基于环形数组实现的双向队列 */ + class ArrayDeque { + late List _nums; // 用于存储双向队列元素的数组 + late int _front; // 队首指针,指向队首元素 + late int _queSize; // 双向队列长度 + + /* 构造方法 */ + ArrayDeque(int capacity) { + this._nums = List.filled(capacity, 0); + this._front = this._queSize = 0; + } + + /* 获取双向队列的容量 */ + int capacity() { + return _nums.length; + } + + /* 获取双向队列的长度 */ + int size() { + return _queSize; + } + + /* 判断双向队列是否为空 */ + bool isEmpty() { + return _queSize == 0; + } + + /* 计算环形数组索引 */ + int index(int i) { + // 通过取余操作实现数组首尾相连 + // 当 i 越过数组尾部后,回到头部 + // 当 i 越过数组头部后,回到尾部 + return (i + capacity()) % capacity(); + } + + /* 队首入队 */ + void pushFirst(int num) { + if (_queSize == capacity()) { + throw Exception("双向队列已满"); + } + // 队首指针向左移动一位 + // 通过取余操作,实现 _front 越过数组头部后回到尾部 + _front = index(_front - 1); + // 将 num 添加至队首 + _nums[_front] = num; + _queSize++; + } + + /* 队尾入队 */ + void pushLast(int num) { + if (_queSize == capacity()) { + throw Exception("双向队列已满"); + } + // 计算尾指针,指向队尾索引 + 1 + int rear = index(_front + _queSize); + // 将 num 添加至队尾 + _nums[rear] = num; + _queSize++; + } + + /* 队首出队 */ + int popFirst() { + int num = peekFirst(); + // 队首指针向右移动一位 + _front = index(_front + 1); + _queSize--; + return num; + } + + /* 队尾出队 */ + int popLast() { + int num = peekLast(); + _queSize--; + return num; + } + + /* 访问队首元素 */ + int peekFirst() { + if (isEmpty()) { + throw Exception("双向队列为空"); + } + return _nums[_front]; + } + + /* 访问队尾元素 */ + int peekLast() { + if (isEmpty()) { + throw Exception("双向队列为空"); + } + // 计算尾元素索引 + int last = index(_front + _queSize - 1); + return _nums[last]; + } + + /* 返回数组用于打印 */ + List toArray() { + // 仅转换有效长度范围内的列表元素 + List res = List.filled(_queSize, 0); + for (int i = 0, j = _front; i < _queSize; i++, j++) { + res[i] = _nums[index(j)]; + } + return res; + } + } ``` === "Rust" ```rust title="array_deque.rs" - [class]{ArrayDeque}-[func]{} + /* 基于环形数组实现的双向队列 */ + struct ArrayDeque { + nums: Vec, // 用于存储双向队列元素的数组 + front: usize, // 队首指针,指向队首元素 + que_size: usize, // 双向队列长度 + } + + impl ArrayDeque { + /* 构造方法 */ + pub fn new(capacity: usize) -> Self { + Self { + nums: vec![0; capacity], + front: 0, + que_size: 0, + } + } + + /* 获取双向队列的容量 */ + pub fn capacity(&self) -> usize { + self.nums.len() + } + + /* 获取双向队列的长度 */ + pub fn size(&self) -> usize { + self.que_size + } + + /* 判断双向队列是否为空 */ + pub fn is_empty(&self) -> bool { + self.que_size == 0 + } + + /* 计算环形数组索引 */ + fn index(&self, i: i32) -> usize { + // 通过取余操作实现数组首尾相连 + // 当 i 越过数组尾部后,回到头部 + // 当 i 越过数组头部后,回到尾部 + return ((i + self.capacity() as i32) % self.capacity() as i32) as usize; + } + + /* 队首入队 */ + pub fn push_first(&mut self, num: i32) { + if self.que_size == self.capacity() { + println!("双向队列已满"); + return + } + // 队首指针向左移动一位 + // 通过取余操作,实现 front 越过数组头部后回到尾部 + self.front = self.index(self.front as i32 - 1); + // 将 num 添加至队首 + self.nums[self.front] = num; + self.que_size += 1; + } + + /* 队尾入队 */ + pub fn push_last(&mut self, num: i32) { + if self.que_size == self.capacity() { + println!("双向队列已满"); + return + } + // 计算尾指针,指向队尾索引 + 1 + let rear = self.index(self.front as i32 + self.que_size as i32); + // 将 num 添加至队尾 + self.nums[rear] = num; + self.que_size += 1; + } + + /* 队首出队 */ + fn pop_first(&mut self) -> i32 { + let num = self.peek_first(); + // 队首指针向后移动一位 + self.front = self.index(self.front as i32 + 1); + self.que_size -= 1; + num + } + + /* 队尾出队 */ + fn pop_last(&mut self) -> i32 { + let num = self.peek_last(); + self.que_size -= 1; + num + } + + /* 访问队首元素 */ + fn peek_first(&self) -> i32 { + if self.is_empty() { panic!("双向队列为空") }; + self.nums[self.front] + } + + /* 访问队尾元素 */ + fn peek_last(&self) -> i32 { + if self.is_empty() { panic!("双向队列为空") }; + // 计算尾元素索引 + let last = self.index(self.front as i32 + self.que_size as i32 - 1); + self.nums[last] + } + + /* 返回数组用于打印 */ + fn to_array(&self) -> Vec { + // 仅转换有效长度范围内的列表元素 + let mut res = vec![0; self.que_size]; + let mut j = self.front; + for i in 0..self.que_size { + res[i] = self.nums[self.index(j as i32)]; + j += 1; + } + res + } + } ``` === "C" ```c title="array_deque.c" - [class]{arrayDeque}-[func]{} + /* 基于环形数组实现的双向队列 */ + struct arrayDeque { + int *nums; // 用于存储队列元素的数组 + int front; // 队首指针,指向队首元素 + int queSize; // 尾指针,指向队尾 + 1 + int queCapacity; // 队列容量 + }; + + typedef struct arrayDeque 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); + deque->queCapacity = 0; + } + + /* 获取双向队列的容量 */ + 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; + } + + /* 打印队列 */ + void printArrayDeque(arrayDeque *deque) { + int arr[deque->queSize]; + // 拷贝 + for (int i = 0, j = deque->front; i < deque->queSize; i++, j++) { + arr[i] = deque->nums[j % deque->queCapacity]; + } + printArray(arr, deque->queSize); + } ``` === "Zig" diff --git a/zh/chapter_stack_and_queue/queue.md b/zh/chapter_stack_and_queue/queue.md index f837a23fb..d638cf9e0 100755 --- a/zh/chapter_stack_and_queue/queue.md +++ b/zh/chapter_stack_and_queue/queue.md @@ -340,73 +340,868 @@ comments: true === "Python" ```python title="linkedlist_queue.py" - [class]{LinkedListQueue}-[func]{} + class LinkedListQueue: + """基于链表实现的队列""" + + def __init__(self): + """构造方法""" + self.__front: ListNode | None = None # 头节点 front + self.__rear: ListNode | None = None # 尾节点 rear + self.__size: int = 0 + + def size(self) -> int: + """获取队列的长度""" + return self.__size + + def is_empty(self) -> bool: + """判断队列是否为空""" + return not self.__front + + def push(self, num: int): + """入队""" + # 尾节点后添加 num + node = ListNode(num) + # 如果队列为空,则令头、尾节点都指向该节点 + if self.__front is None: + self.__front = node + self.__rear = node + # 如果队列不为空,则将该节点添加到尾节点后 + else: + self.__rear.next = node + self.__rear = node + self.__size += 1 + + def pop(self) -> int: + """出队""" + num = self.peek() + # 删除头节点 + self.__front = self.__front.next + self.__size -= 1 + return num + + def peek(self) -> int: + """访问队首元素""" + if self.is_empty(): + raise IndexError("队列为空") + return self.__front.val + + def to_list(self) -> list[int]: + """转化为列表用于打印""" + queue = [] + temp = self.__front + while temp: + queue.append(temp.val) + temp = temp.next + return queue ``` === "C++" ```cpp title="linkedlist_queue.cpp" - [class]{LinkedListQueue}-[func]{} + /* 基于链表实现的队列 */ + 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++; + } + + /* 出队 */ + void pop() { + int num = peek(); + // 删除头节点 + ListNode *tmp = front; + front = front->next; + // 释放内存 + delete tmp; + queSize--; + } + + /* 访问队首元素 */ + 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; + } + }; ``` === "Java" ```java title="linkedlist_queue.java" - [class]{LinkedListQueue}-[func]{} + /* 基于链表实现的队列 */ + class LinkedListQueue { + private ListNode front, rear; // 头节点 front ,尾节点 rear + private int queSize = 0; + + public LinkedListQueue() { + front = null; + rear = null; + } + + /* 获取队列的长度 */ + public int size() { + return queSize; + } + + /* 判断队列是否为空 */ + public boolean isEmpty() { + return size() == 0; + } + + /* 入队 */ + public void push(int num) { + // 尾节点后添加 num + ListNode node = new ListNode(num); + // 如果队列为空,则令头、尾节点都指向该节点 + if (front == null) { + front = node; + rear = node; + // 如果队列不为空,则将该节点添加到尾节点后 + } else { + 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 IndexOutOfBoundsException(); + return front.val; + } + + /* 将链表转化为 Array 并返回 */ + 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; + } + } ``` === "C#" ```csharp title="linkedlist_queue.cs" - [class]{LinkedListQueue}-[func]{} + /* 基于链表实现的队列 */ + class LinkedListQueue { + private ListNode? front, rear; // 头节点 front ,尾节点 rear + private 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 ListNode(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 Array.Empty(); + + 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; + } + } ``` === "Go" ```go title="linkedlist_queue.go" - [class]{linkedListQueue}-[func]{} + /* 基于链表实现的队列 */ + 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 + } ``` === "Swift" ```swift title="linkedlist_queue.swift" - [class]{LinkedListQueue}-[func]{} + /* 基于链表实现的队列 */ + class LinkedListQueue { + private var front: ListNode? // 头节点 + private var rear: ListNode? // 尾节点 + private var _size = 0 + + init() {} + + /* 获取队列的长度 */ + 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 + } + } ``` === "JS" ```javascript title="linkedlist_queue.js" - [class]{LinkedListQueue}-[func]{} + /* 基于链表实现的队列 */ + 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; + } + } ``` === "TS" ```typescript title="linkedlist_queue.ts" - [class]{LinkedListQueue}-[func]{} + /* 基于链表实现的队列 */ + 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; + } + } ``` === "Dart" ```dart title="linkedlist_queue.dart" - [class]{LinkedListQueue}-[func]{} + /* 基于链表实现的队列 */ + 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; + } + } ``` === "Rust" ```rust title="linkedlist_queue.rs" - [class]{LinkedListQueue}-[func]{} + /* 基于链表实现的队列 */ + #[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(); + } + } ``` === "C" ```c title="linkedlist_queue.c" - [class]{linkedListQueue}-[func]{} + /* 基于链表实现的队列 */ + struct linkedListQueue { + ListNode *front, *rear; + int queSize; + }; + + typedef struct linkedListQueue linkedListQueue; + + /* 构造函数 */ + linkedListQueue *newLinkedListQueue() { + linkedListQueue *queue = (linkedListQueue *)malloc(sizeof(linkedListQueue)); + queue->front = NULL; + queue->rear = NULL; + queue->queSize = 0; + return queue; + } + + /* 析构函数 */ + void delLinkedListQueue(linkedListQueue *queue) { + // 释放所有节点 + for (int i = 0; i < queue->queSize && queue->front != NULL; i++) { + 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; + } + + /* 出队 */ + void pop(linkedListQueue *queue) { + int num = peek(queue); + ListNode *tmp = queue->front; + queue->front = queue->front->next; + free(tmp); + queue->queSize--; + } + + /* 打印队列 */ + void printLinkedListQueue(linkedListQueue *queue) { + int arr[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); + } ``` === "Zig" ```zig title="linkedlist_queue.zig" - [class]{LinkedListQueue}-[func]{} + // 基于链表实现的队列 + 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; + } + }; + } ``` ### 2.   基于数组的实现 @@ -440,73 +1235,894 @@ comments: true === "Python" ```python title="array_queue.py" - [class]{ArrayQueue}-[func]{} + class ArrayQueue: + """基于环形数组实现的队列""" + + def __init__(self, size: int): + """构造方法""" + self.__nums: list[int] = [0] * size # 用于存储队列元素的数组 + self.__front: int = 0 # 队首指针,指向队首元素 + self.__size: int = 0 # 队列长度 + + def capacity(self) -> int: + """获取队列的容量""" + return len(self.__nums) + + def size(self) -> int: + """获取队列的长度""" + return self.__size + + def is_empty(self) -> bool: + """判断队列是否为空""" + return self.__size == 0 + + def push(self, num: int): + """入队""" + if self.__size == self.capacity(): + raise IndexError("队列已满") + # 计算尾指针,指向队尾索引 + 1 + # 通过取余操作,实现 rear 越过数组尾部后回到头部 + rear: int = (self.__front + self.__size) % self.capacity() + # 将 num 添加至队尾 + self.__nums[rear] = num + self.__size += 1 + + def pop(self) -> int: + """出队""" + num: int = self.peek() + # 队首指针向后移动一位,若越过尾部则返回到数组头部 + self.__front = (self.__front + 1) % self.capacity() + self.__size -= 1 + return num + + def peek(self) -> int: + """访问队首元素""" + if self.is_empty(): + raise IndexError("队列为空") + return self.__nums[self.__front] + + def to_list(self) -> list[int]: + """返回列表用于打印""" + res = [0] * self.size() + j: int = self.__front + for i in range(self.size()): + res[i] = self.__nums[(j % self.capacity())] + j += 1 + return res ``` === "C++" ```cpp title="array_queue.cpp" - [class]{ArrayQueue}-[func]{} + /* 基于环形数组实现的队列 */ + 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++; + } + + /* 出队 */ + void pop() { + int num = peek(); + // 队首指针向后移动一位,若越过尾部则返回到数组头部 + front = (front + 1) % queCapacity; + queSize--; + } + + /* 访问队首元素 */ + 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; + } + }; ``` === "Java" ```java title="array_queue.java" - [class]{ArrayQueue}-[func]{} + /* 基于环形数组实现的队列 */ + class ArrayQueue { + private int[] nums; // 用于存储队列元素的数组 + private int front; // 队首指针,指向队首元素 + private int queSize; // 队列长度 + + public ArrayQueue(int capacity) { + nums = new int[capacity]; + front = queSize = 0; + } + + /* 获取队列的容量 */ + public int capacity() { + return nums.length; + } + + /* 获取队列的长度 */ + public int size() { + return queSize; + } + + /* 判断队列是否为空 */ + public boolean isEmpty() { + return queSize == 0; + } + + /* 入队 */ + public void push(int num) { + if (queSize == capacity()) { + System.out.println("队列已满"); + 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 IndexOutOfBoundsException(); + 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 % capacity()]; + } + return res; + } + } ``` === "C#" ```csharp title="array_queue.cs" - [class]{ArrayQueue}-[func]{} + /* 基于环形数组实现的队列 */ + class ArrayQueue { + private int[] nums; // 用于存储队列元素的数组 + private int front; // 队首指针,指向队首元素 + private int queSize; // 队列长度 + + public ArrayQueue(int capacity) { + nums = new int[capacity]; + front = queSize = 0; + } + + /* 获取队列的容量 */ + public 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; + } + } ``` === "Go" ```go title="array_queue.go" - [class]{arrayQueue}-[func]{} + /* 基于环形数组实现的队列 */ + 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] + } ``` === "Swift" ```swift title="array_queue.swift" - [class]{ArrayQueue}-[func]{} + /* 基于环形数组实现的队列 */ + class ArrayQueue { + private var nums: [Int] // 用于存储队列元素的数组 + private var front = 0 // 队首指针,指向队首元素 + private var queSize = 0 // 队列长度 + + init(capacity: Int) { + // 初始化数组 + nums = Array(repeating: 0, count: capacity) + } + + /* 获取队列的容量 */ + func capacity() -> Int { + nums.count + } + + /* 获取队列的长度 */ + func size() -> Int { + queSize + } + + /* 判断队列是否为空 */ + func isEmpty() -> Bool { + queSize == 0 + } + + /* 入队 */ + func push(num: Int) { + if size() == capacity() { + print("队列已满") + return + } + // 计算尾指针,指向队尾索引 + 1 + // 通过取余操作,实现 rear 越过数组尾部后回到头部 + let rear = (front + queSize) % capacity() + // 将 num 添加至队尾 + nums[rear] = num + queSize += 1 + } + + /* 出队 */ + @discardableResult + func pop() -> Int { + let num = peek() + // 队首指针向后移动一位,若越过尾部则返回到数组头部 + front = (front + 1) % capacity() + queSize -= 1 + return num + } + + /* 访问队首元素 */ + func peek() -> Int { + if isEmpty() { + fatalError("队列为空") + } + return nums[front] + } + + /* 返回数组 */ + func toArray() -> [Int] { + // 仅转换有效长度范围内的列表元素 + var res = Array(repeating: 0, count: queSize) + for (i, j) in sequence(first: (0, front), next: { $0 < self.queSize - 1 ? ($0 + 1, $1 + 1) : nil }) { + res[i] = nums[j % capacity()] + } + return res + } + } ``` === "JS" ```javascript title="array_queue.js" - [class]{ArrayQueue}-[func]{} + /* 基于环形数组实现的队列 */ + 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; + } + } ``` === "TS" ```typescript title="array_queue.ts" - [class]{ArrayQueue}-[func]{} + /* 基于环形数组实现的队列 */ + 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; + } + } ``` === "Dart" ```dart title="array_queue.dart" - [class]{ArrayQueue}-[func]{} + /* 基于环形数组实现的队列 */ + 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; + } + } ``` === "Rust" ```rust title="array_queue.rs" - [class]{ArrayQueue}-[func]{} + /* 基于环形数组实现的队列 */ + 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 + } + } ``` === "C" ```c title="array_queue.c" - [class]{arrayQueue}-[func]{} + /* 基于环形数组实现的队列 */ + struct arrayQueue { + int *nums; // 用于存储队列元素的数组 + int front; // 队首指针,指向队首元素 + int queSize; // 尾指针,指向队尾 + 1 + int queCapacity; // 队列容量 + }; + + typedef struct arrayQueue 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); + queue->queCapacity = 0; + } + + /* 获取队列的容量 */ + 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++; + } + + /* 出队 */ + void pop(arrayQueue *queue) { + int num = peek(queue); + // 队首指针向后移动一位,若越过尾部则返回到数组头部 + queue->front = (queue->front + 1) % queue->queCapacity; + queue->queSize--; + } + + /* 打印队列 */ + void printArrayQueue(arrayQueue *queue) { + int arr[queue->queSize]; + // 拷贝 + for (int i = 0, j = queue->front; i < queue->queSize; i++, j++) { + arr[i] = queue->nums[j % queue->queCapacity]; + } + printArray(arr, queue->queSize); + } ``` === "Zig" ```zig title="array_queue.zig" - [class]{ArrayQueue}-[func]{} + // 基于环形数组实现的队列 + 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; + } + }; + } ``` 以上实现的队列仍然具有局限性,即其长度不可变。然而,这个问题不难解决,我们可以将数组替换为动态数组,从而引入扩容机制。有兴趣的同学可以尝试自行实现。 diff --git a/zh/chapter_stack_and_queue/stack.md b/zh/chapter_stack_and_queue/stack.md index e40a70ad7..7261fed97 100755 --- a/zh/chapter_stack_and_queue/stack.md +++ b/zh/chapter_stack_and_queue/stack.md @@ -340,73 +340,749 @@ comments: true === "Python" ```python title="linkedlist_stack.py" - [class]{LinkedListStack}-[func]{} + class LinkedListStack: + """基于链表实现的栈""" + + def __init__(self): + """构造方法""" + self.__peek: ListNode | None = None + self.__size: int = 0 + + def size(self) -> int: + """获取栈的长度""" + return self.__size + + def is_empty(self) -> bool: + """判断栈是否为空""" + return not self.__peek + + def push(self, val: int): + """入栈""" + node = ListNode(val) + node.next = self.__peek + self.__peek = node + self.__size += 1 + + def pop(self) -> int: + """出栈""" + num = self.peek() + self.__peek = self.__peek.next + self.__size -= 1 + return num + + def peek(self) -> int: + """访问栈顶元素""" + if self.is_empty(): + raise IndexError("栈为空") + return self.__peek.val + + def to_list(self) -> list[int]: + """转化为列表用于打印""" + arr = [] + node = self.__peek + while node: + arr.append(node.val) + node = node.next + arr.reverse() + return arr ``` === "C++" ```cpp title="linkedlist_stack.cpp" - [class]{LinkedListStack}-[func]{} + /* 基于链表实现的栈 */ + 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++; + } + + /* 出栈 */ + void pop() { + int num = top(); + ListNode *tmp = stackTop; + stackTop = stackTop->next; + // 释放内存 + delete tmp; + stkSize--; + } + + /* 访问栈顶元素 */ + 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; + } + }; ``` === "Java" ```java title="linkedlist_stack.java" - [class]{LinkedListStack}-[func]{} + /* 基于链表实现的栈 */ + class LinkedListStack { + private ListNode stackPeek; // 将头节点作为栈顶 + private int stkSize = 0; // 栈的长度 + + public LinkedListStack() { + stackPeek = null; + } + + /* 获取栈的长度 */ + public int size() { + return stkSize; + } + + /* 判断栈是否为空 */ + public boolean isEmpty() { + return size() == 0; + } + + /* 入栈 */ + public void push(int num) { + ListNode node = new ListNode(num); + node.next = stackPeek; + stackPeek = node; + stkSize++; + } + + /* 出栈 */ + public int pop() { + int num = peek(); + stackPeek = stackPeek.next; + stkSize--; + return num; + } + + /* 访问栈顶元素 */ + public int peek() { + if (isEmpty()) + throw new IndexOutOfBoundsException(); + return stackPeek.val; + } + + /* 将 List 转化为 Array 并返回 */ + public int[] toArray() { + 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; + } + } ``` === "C#" ```csharp title="linkedlist_stack.cs" - [class]{LinkedListStack}-[func]{} + /* 基于链表实现的栈 */ + class LinkedListStack { + private ListNode? stackPeek; // 将头节点作为栈顶 + private 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 ListNode(num); + node.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 Array.Empty(); + + 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; + } + } ``` === "Go" ```go title="linkedlist_stack.go" - [class]{linkedListStack}-[func]{} + /* 基于链表实现的栈 */ + 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 + } ``` === "Swift" ```swift title="linkedlist_stack.swift" - [class]{LinkedListStack}-[func]{} + /* 基于链表实现的栈 */ + class LinkedListStack { + private var _peek: ListNode? // 将头节点作为栈顶 + private var _size = 0 // 栈的长度 + + init() {} + + /* 获取栈的长度 */ + 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 sequence(first: res.count - 1, next: { $0 >= 0 + 1 ? $0 - 1 : nil }) { + res[i] = node!.val + node = node?.next + } + return res + } + } ``` === "JS" ```javascript title="linkedlist_stack.js" - [class]{LinkedListStack}-[func]{} + /* 基于链表实现的栈 */ + 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; + } + } ``` === "TS" ```typescript title="linkedlist_stack.ts" - [class]{LinkedListStack}-[func]{} + /* 基于链表实现的栈 */ + 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; + } + } ``` === "Dart" ```dart title="linkedlist_stack.dart" - [class]{LinkedListStack}-[func]{} + /* 基于链表类实现的栈 */ + 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; + } + } ``` === "Rust" ```rust title="linkedlist_stack.rs" - [class]{LinkedListStack}-[func]{} + /* 基于链表实现的栈 */ + #[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(); + } + } ``` === "C" ```c title="linkedlist_stack.c" - [class]{linkedListStack}-[func]{} + /* 基于链表实现的栈 */ + struct linkedListStack { + ListNode *top; // 将头节点作为栈顶 + int size; // 栈的长度 + }; + + typedef struct linkedListStack 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) { + assert(s); + return s->size; + } + + /* 判断栈是否为空 */ + bool isEmpty(linkedListStack *s) { + assert(s); + return size(s) == 0; + } + + /* 访问栈顶元素 */ + int peek(linkedListStack *s) { + assert(s); + assert(size(s) != 0); + return s->top->val; + } + + /* 入栈 */ + void push(linkedListStack *s, int num) { + assert(s); + ListNode *node = (ListNode *)malloc(sizeof(ListNode)); + node->next = s->top; // 更新新加节点指针域 + node->val = num; // 更新新加节点数据域 + s->top = node; // 更新栈顶 + s->size++; // 更新栈大小 + } + + /* 出栈 */ + int pop(linkedListStack *s) { + if (s->size == 0) { + printf("stack is empty.\n"); + return INT_MAX; + } + assert(s); + int val = peek(s); + ListNode *tmp = s->top; + s->top = s->top->next; + // 释放内存 + free(tmp); + s->size--; + return val; + } ``` === "Zig" ```zig title="linkedlist_stack.zig" - [class]{LinkedListStack}-[func]{} + // 基于链表实现的栈 + 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; + } + }; + } ``` ### 2.   基于数组的实现 @@ -429,73 +1105,589 @@ comments: true === "Python" ```python title="array_stack.py" - [class]{ArrayStack}-[func]{} + class ArrayStack: + """基于数组实现的栈""" + + def __init__(self): + """构造方法""" + self.__stack: list[int] = [] + + def size(self) -> int: + """获取栈的长度""" + return len(self.__stack) + + def is_empty(self) -> bool: + """判断栈是否为空""" + return self.__stack == [] + + def push(self, item: int): + """入栈""" + self.__stack.append(item) + + def pop(self) -> int: + """出栈""" + if self.is_empty(): + raise IndexError("栈为空") + return self.__stack.pop() + + def peek(self) -> int: + """访问栈顶元素""" + if self.is_empty(): + raise IndexError("栈为空") + return self.__stack[-1] + + def to_list(self) -> list[int]: + """返回列表用于打印""" + return self.__stack ``` === "C++" ```cpp title="array_stack.cpp" - [class]{ArrayStack}-[func]{} + /* 基于数组实现的栈 */ + 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); + } + + /* 出栈 */ + void pop() { + int oldTop = top(); + stack.pop_back(); + } + + /* 访问栈顶元素 */ + int top() { + if (isEmpty()) + throw out_of_range("栈为空"); + return stack.back(); + } + + /* 返回 Vector */ + vector toVector() { + return stack; + } + }; ``` === "Java" ```java title="array_stack.java" - [class]{ArrayStack}-[func]{} + /* 基于数组实现的栈 */ + class ArrayStack { + private ArrayList stack; + + public ArrayStack() { + // 初始化列表(动态数组) + stack = new ArrayList<>(); + } + + /* 获取栈的长度 */ + public int size() { + return stack.size(); + } + + /* 判断栈是否为空 */ + public boolean isEmpty() { + return size() == 0; + } + + /* 入栈 */ + public void push(int num) { + stack.add(num); + } + + /* 出栈 */ + public int pop() { + if (isEmpty()) + throw new IndexOutOfBoundsException(); + return stack.remove(size() - 1); + } + + /* 访问栈顶元素 */ + public int peek() { + if (isEmpty()) + throw new IndexOutOfBoundsException(); + return stack.get(size() - 1); + } + + /* 将 List 转化为 Array 并返回 */ + public Object[] toArray() { + return stack.toArray(); + } + } ``` === "C#" ```csharp title="array_stack.cs" - [class]{ArrayStack}-[func]{} + /* 基于数组实现的栈 */ + class ArrayStack { + private List stack; + public ArrayStack() { + // 初始化列表(动态数组) + stack = new(); + } + + /* 获取栈的长度 */ + 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.ToArray(); + } + } ``` === "Go" ```go title="array_stack.go" - [class]{arrayStack}-[func]{} + /* 基于数组实现的栈 */ + 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 + } ``` === "Swift" ```swift title="array_stack.swift" - [class]{ArrayStack}-[func]{} + /* 基于数组实现的栈 */ + 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 + } + } ``` === "JS" ```javascript title="array_stack.js" - [class]{ArrayStack}-[func]{} + /* 基于数组实现的栈 */ + 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; + } + } ``` === "TS" ```typescript title="array_stack.ts" - [class]{ArrayStack}-[func]{} + /* 基于数组实现的栈 */ + 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; + } + } ``` === "Dart" ```dart title="array_stack.dart" - [class]{ArrayStack}-[func]{} + /* 基于数组实现的栈 */ + 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; + } ``` === "Rust" ```rust title="array_stack.rs" - [class]{ArrayStack}-[func]{} + /* 基于数组实现的栈 */ + 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 { + match self.stack.pop() { + Some(num) => Some(num), + None => None, + } + } + + /* 访问栈顶元素 */ + fn peek(&self) -> Option<&T> { + if self.is_empty() { panic!("栈为空") }; + self.stack.last() + } + + /* 返回 &Vec */ + fn to_array(&self) -> &Vec { + &self.stack + } + } ``` === "C" ```c title="array_stack.c" - [class]{arrayStack}-[func]{} + /* 基于数组实现的栈 */ + struct arrayStack { + int *data; + int size; + }; + + typedef struct arrayStack arrayStack; + + /* 构造函数 */ + arrayStack *newArrayStack() { + arrayStack *s = malloc(sizeof(arrayStack)); + // 初始化一个大容量,避免扩容 + s->data = malloc(sizeof(int) * MAX_SIZE); + s->size = 0; + return s; + } + + /* 获取栈的长度 */ + int size(arrayStack *s) { + return s->size; + } + + /* 判断栈是否为空 */ + bool isEmpty(arrayStack *s) { + return s->size == 0; + } + + /* 入栈 */ + void push(arrayStack *s, int num) { + if (s->size == MAX_SIZE) { + printf("stack is full.\n"); + return; + } + s->data[s->size] = num; + s->size++; + } + + /* 访问栈顶元素 */ + int peek(arrayStack *s) { + if (s->size == 0) { + printf("stack is empty.\n"); + return INT_MAX; + } + return s->data[s->size - 1]; + } + + /* 出栈 */ + int pop(arrayStack *s) { + if (s->size == 0) { + printf("stack is empty.\n"); + return INT_MAX; + } + int val = peek(s); + s->size--; + return val; + } ``` === "Zig" ```zig title="array_stack.zig" - [class]{ArrayStack}-[func]{} + // 基于数组实现的栈 + 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.?; + } + }; + } ``` ## 5.1.3   两种实现对比 diff --git a/zh/chapter_tree/array_representation_of_tree.md b/zh/chapter_tree/array_representation_of_tree.md index 9a7d4e14a..a3252e68f 100644 --- a/zh/chapter_tree/array_representation_of_tree.md +++ b/zh/chapter_tree/array_representation_of_tree.md @@ -146,67 +146,1011 @@ comments: true === "Python" ```python title="array_binary_tree.py" - [class]{ArrayBinaryTree}-[func]{} + class ArrayBinaryTree: + """数组表示下的二叉树类""" + + def __init__(self, arr: list[int | None]): + """构造方法""" + self.__tree = list(arr) + + def size(self): + """节点数量""" + return len(self.__tree) + + def val(self, i: int) -> int: + """获取索引为 i 节点的值""" + # 若索引越界,则返回 None ,代表空位 + if i < 0 or i >= self.size(): + return None + return self.__tree[i] + + def left(self, i: int) -> int | None: + """获取索引为 i 节点的左子节点的索引""" + return 2 * i + 1 + + def right(self, i: int) -> int | None: + """获取索引为 i 节点的右子节点的索引""" + return 2 * i + 2 + + def parent(self, i: int) -> int | None: + """获取索引为 i 节点的父节点的索引""" + return (i - 1) // 2 + + def level_order(self) -> list[int]: + """层序遍历""" + self.res = [] + # 直接遍历数组 + 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): + """深度优先遍历""" + if self.val(i) is None: + return + # 前序遍历 + if order == "pre": + self.res.append(self.val(i)) + self.__dfs(self.left(i), order) + # 中序遍历 + if order == "in": + self.res.append(self.val(i)) + self.__dfs(self.right(i), order) + # 后序遍历 + if order == "post": + self.res.append(self.val(i)) + + def pre_order(self) -> list[int]: + """前序遍历""" + self.res = [] + self.__dfs(0, order="pre") + return self.res + + def in_order(self) -> list[int]: + """中序遍历""" + self.res = [] + self.__dfs(0, order="in") + return self.res + + def post_order(self) -> list[int]: + """后序遍历""" + self.res = [] + self.__dfs(0, order="post") + return self.res ``` === "C++" ```cpp title="array_binary_tree.cpp" - [class]{ArrayBinaryTree}-[func]{} + /* 数组表示下的二叉树类 */ + 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)); + } + }; ``` === "Java" ```java title="array_binary_tree.java" - [class]{ArrayBinaryTree}-[func]{} + /* 数组表示下的二叉树类 */ + class ArrayBinaryTree { + private List tree; + + /* 构造方法 */ + public ArrayBinaryTree(List arr) { + tree = new ArrayList<>(arr); + } + + /* 节点数量 */ + public int size() { + return tree.size(); + } + + /* 获取索引为 i 节点的值 */ + public Integer val(int i) { + // 若索引越界,则返回 null ,代表空位 + if (i < 0 || i >= size()) + return null; + return tree.get(i); + } + + /* 获取索引为 i 节点的左子节点的索引 */ + public Integer left(int i) { + return 2 * i + 1; + } + + /* 获取索引为 i 节点的右子节点的索引 */ + public Integer right(int i) { + return 2 * i + 2; + } + + /* 获取索引为 i 节点的父节点的索引 */ + public Integer parent(int i) { + return (i - 1) / 2; + } + + /* 层序遍历 */ + public List levelOrder() { + List res = new ArrayList<>(); + // 直接遍历数组 + for (int i = 0; i < size(); i++) { + if (val(i) != null) + res.add(val(i)); + } + return res; + } + + /* 深度优先遍历 */ + private void dfs(Integer 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)); + } + + /* 前序遍历 */ + public List preOrder() { + List res = new ArrayList<>(); + dfs(0, "pre", res); + return res; + } + + /* 中序遍历 */ + public List inOrder() { + List res = new ArrayList<>(); + dfs(0, "in", res); + return res; + } + + /* 后序遍历 */ + public List postOrder() { + List res = new ArrayList<>(); + dfs(0, "post", res); + return res; + } + } ``` === "C#" ```csharp title="array_binary_tree.cs" - [class]{ArrayBinaryTree}-[func]{} + /* 数组表示下的二叉树类 */ + class ArrayBinaryTree { + private List tree; + + /* 构造方法 */ + public ArrayBinaryTree(List arr) { + tree = new List(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 = new List(); + // 直接遍历数组 + for (int i = 0; i < size(); i++) { + if (val(i).HasValue) + res.Add(val(i).Value); + } + return res; + } + + /* 深度优先遍历 */ + private 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 = new List(); + dfs(0, "pre", res); + return res; + } + + /* 中序遍历 */ + public List inOrder() { + List res = new List(); + dfs(0, "in", res); + return res; + } + + /* 后序遍历 */ + public List postOrder() { + List res = new List(); + dfs(0, "post", res); + return res; + } + } ``` === "Go" ```go title="array_binary_tree.go" - [class]{arrayBinaryTree}-[func]{} + /* 数组表示下的二叉树类 */ + 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 + } ``` === "Swift" ```swift title="array_binary_tree.swift" - [class]{ArrayBinaryTree}-[func]{} + /* 数组表示下的二叉树类 */ + 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 stride(from: 0, to: size(), by: 1) { + 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 + } + } ``` === "JS" ```javascript title="array_binary_tree.js" - [class]{ArrayBinaryTree}-[func]{} + /* 数组表示下的二叉树类 */ + 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 (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; + } + } ``` === "TS" ```typescript title="array_binary_tree.ts" - [class]{ArrayBinaryTree}-[func]{} + /* 数组表示下的二叉树类 */ + 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 (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; + } + } ``` === "Dart" ```dart title="array_binary_tree.dart" - [class]{ArrayBinaryTree}-[func]{} + /* 数组表示下的二叉树类 */ + 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; + } + } ``` === "Rust" ```rust title="array_binary_tree.rs" - [class]{ArrayBinaryTree}-[func]{} + /* 数组表示下的二叉树类 */ + 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 + } + } ``` === "C" ```c title="array_binary_tree.c" - [class]{arrayBinaryTree}-[func]{} + /* 数组表示下的二叉树类 */ + struct arrayBinaryTree { + vector *tree; + }; + + typedef struct arrayBinaryTree arrayBinaryTree; + + /* 构造函数 */ + arrayBinaryTree *newArrayBinaryTree(vector *arr) { + arrayBinaryTree *newABT = malloc(sizeof(arrayBinaryTree)); + newABT->tree = arr; + return newABT; + } + + /* 节点数量 */ + int size(arrayBinaryTree *abt) { + return abt->tree->size; + } + + /* 获取索引为 i 节点的值 */ + int val(arrayBinaryTree *abt, int i) { + // 若索引越界,则返回 INT_MAX ,代表空位 + if (i < 0 || i >= size(abt)) + return INT_MAX; + return *(int *)abt->tree->data[i]; + } + + /* 深度优先遍历 */ + void dfs(arrayBinaryTree *abt, int i, const char *order, vector *res) { + // 若为空位,则返回 + if (val(abt, i) == INT_MAX) + return; + // 前序遍历 + if (strcmp(order, "pre") == 0) { + int tmp = val(abt, i); + vectorPushback(res, &tmp, sizeof(tmp)); + } + dfs(abt, left(i), order, res); + // 中序遍历 + if (strcmp(order, "in") == 0) { + int tmp = val(abt, i); + vectorPushback(res, &tmp, sizeof(tmp)); + } + dfs(abt, right(i), order, res); + // 后序遍历 + if (strcmp(order, "post") == 0) { + int tmp = val(abt, i); + vectorPushback(res, &tmp, sizeof(tmp)); + } + } + + /* 层序遍历 */ + vector *levelOrder(arrayBinaryTree *abt) { + vector *res = newVector(); + // 直接遍历数组 + for (int i = 0; i < size(abt); i++) { + if (val(abt, i) != INT_MAX) { + int tmp = val(abt, i); + vectorPushback(res, &tmp, sizeof(int)); + } + } + return res; + } + + /* 前序遍历 */ + vector *preOrder(arrayBinaryTree *abt) { + vector *res = newVector(); + dfs(abt, 0, "pre", res); + return res; + } + + /* 中序遍历 */ + vector *inOrder(arrayBinaryTree *abt) { + vector *res = newVector(); + dfs(abt, 0, "in", res); + return res; + } + + /* 后序遍历 */ + vector *postOrder(arrayBinaryTree *abt) { + vector *res = newVector(); + dfs(abt, 0, "post", res); + return res; + } ``` === "Zig" diff --git a/zh/chapter_tree/avl_tree.md b/zh/chapter_tree/avl_tree.md index 8ab010da0..8ba5c3e13 100644 --- a/zh/chapter_tree/avl_tree.md +++ b/zh/chapter_tree/avl_tree.md @@ -222,97 +222,221 @@ AVL 树既是二叉搜索树也是平衡二叉树,同时满足这两类二叉 === "Python" ```python title="avl_tree.py" - [class]{AVLTree}-[func]{height} + def height(self, node: TreeNode | None) -> int: + """获取节点高度""" + # 空节点高度为 -1 ,叶节点高度为 0 + if node is not None: + return node.height + return -1 - [class]{AVLTree}-[func]{__update_height} + def __update_height(self, node: TreeNode | None): + """更新节点高度""" + # 节点高度等于最高子树高度 + 1 + node.height = max([self.height(node.left), self.height(node.right)]) + 1 ``` === "C++" ```cpp title="avl_tree.cpp" - [class]{AVLTree}-[func]{height} + /* 获取节点高度 */ + int height(TreeNode *node) { + // 空节点高度为 -1 ,叶节点高度为 0 + return node == nullptr ? -1 : node->height; + } - [class]{AVLTree}-[func]{updateHeight} + /* 更新节点高度 */ + void updateHeight(TreeNode *node) { + // 节点高度等于最高子树高度 + 1 + node->height = max(height(node->left), height(node->right)) + 1; + } ``` === "Java" ```java title="avl_tree.java" - [class]{AVLTree}-[func]{height} + /* 获取节点高度 */ + int height(TreeNode node) { + // 空节点高度为 -1 ,叶节点高度为 0 + return node == null ? -1 : node.height; + } - [class]{AVLTree}-[func]{updateHeight} + /* 更新节点高度 */ + void updateHeight(TreeNode node) { + // 节点高度等于最高子树高度 + 1 + node.height = Math.max(height(node.left), height(node.right)) + 1; + } ``` === "C#" ```csharp title="avl_tree.cs" - [class]{AVLTree}-[func]{height} + /* 获取节点高度 */ + int height(TreeNode? node) { + // 空节点高度为 -1 ,叶节点高度为 0 + return node == null ? -1 : node.height; + } - [class]{AVLTree}-[func]{updateHeight} + /* 更新节点高度 */ + void updateHeight(TreeNode node) { + // 节点高度等于最高子树高度 + 1 + node.height = Math.Max(height(node.left), height(node.right)) + 1; + } ``` === "Go" ```go title="avl_tree.go" - [class]{aVLTree}-[func]{height} + /* 获取节点高度 */ + func (t *aVLTree) height(node *TreeNode) int { + // 空节点高度为 -1 ,叶节点高度为 0 + if node != nil { + return node.Height + } + return -1 + } - [class]{aVLTree}-[func]{updateHeight} + /* 更新节点高度 */ + 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 + } + } ``` === "Swift" ```swift title="avl_tree.swift" - [class]{AVLTree}-[func]{height} + /* 获取节点高度 */ + func height(node: TreeNode?) -> Int { + // 空节点高度为 -1 ,叶节点高度为 0 + node == nil ? -1 : node!.height + } - [class]{AVLTree}-[func]{updateHeight} + /* 更新节点高度 */ + func updateHeight(node: TreeNode?) { + // 节点高度等于最高子树高度 + 1 + node?.height = max(height(node: node?.left), height(node: node?.right)) + 1 + } ``` === "JS" ```javascript title="avl_tree.js" - [class]{AVLTree}-[func]{height} + /* 获取节点高度 */ + height(node) { + // 空节点高度为 -1 ,叶节点高度为 0 + return node === null ? -1 : node.height; + } - [class]{AVLTree}-[func]{#updateHeight} + /* 更新节点高度 */ + #updateHeight(node) { + // 节点高度等于最高子树高度 + 1 + node.height = + Math.max(this.height(node.left), this.height(node.right)) + 1; + } ``` === "TS" ```typescript title="avl_tree.ts" - [class]{AVLTree}-[func]{height} + /* 获取节点高度 */ + height(node: TreeNode): number { + // 空节点高度为 -1 ,叶节点高度为 0 + return node === null ? -1 : node.height; + } - [class]{AVLTree}-[func]{updateHeight} + /* 更新节点高度 */ + updateHeight(node: TreeNode): void { + // 节点高度等于最高子树高度 + 1 + node.height = + Math.max(this.height(node.left), this.height(node.right)) + 1; + } ``` === "Dart" ```dart title="avl_tree.dart" - [class]{AVLTree}-[func]{height} + /* 获取节点高度 */ + int height(TreeNode? node) { + // 空节点高度为 -1 ,叶节点高度为 0 + return node == null ? -1 : node.height; + } - [class]{AVLTree}-[func]{updateHeight} + /* 更新节点高度 */ + void updateHeight(TreeNode? node) { + // 节点高度等于最高子树高度 + 1 + node!.height = max(height(node.left), height(node.right)) + 1; + } ``` === "Rust" ```rust title="avl_tree.rs" - [class]{AVLTree}-[func]{height} + /* 获取节点高度 */ + fn height(node: OptionTreeNodeRc) -> i32 { + // 空节点高度为 -1 ,叶节点高度为 0 + match node { + Some(node) => node.borrow().height, + None => -1, + } + } - [class]{AVLTree}-[func]{update_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; + } + } ``` === "C" ```c title="avl_tree.c" - [class]{}-[func]{height} + /* 获取节点高度 */ + int height(TreeNode *node) { + // 空节点高度为 -1 ,叶节点高度为 0 + if (node != NULL) { + return node->height; + } + return -1; + } - [class]{}-[func]{updateHeight} + /* 更新节点高度 */ + 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; + } + } ``` === "Zig" ```zig title="avl_tree.zig" - [class]{AVLTree}-[func]{height} + // 获取节点高度 + fn height(self: *Self, node: ?*inc.TreeNode(T)) i32 { + _ = self; + // 空节点高度为 -1 ,叶节点高度为 0 + return if (node == null) -1 else node.?.height; + } - [class]{AVLTree}-[func]{updateHeight} + // 更新节点高度 + fn updateHeight(self: *Self, node: ?*inc.TreeNode(T)) void { + // 节点高度等于最高子树高度 + 1 + node.?.height = @max(self.height(node.?.left), self.height(node.?.right)) + 1; + } ``` ### 2.   节点平衡因子 @@ -322,73 +446,155 @@ AVL 树既是二叉搜索树也是平衡二叉树,同时满足这两类二叉 === "Python" ```python title="avl_tree.py" - [class]{AVLTree}-[func]{balance_factor} + def balance_factor(self, node: TreeNode | None) -> int: + """获取平衡因子""" + # 空节点平衡因子为 0 + if node is None: + return 0 + # 节点平衡因子 = 左子树高度 - 右子树高度 + return self.height(node.left) - self.height(node.right) ``` === "C++" ```cpp title="avl_tree.cpp" - [class]{AVLTree}-[func]{balanceFactor} + /* 获取平衡因子 */ + int balanceFactor(TreeNode *node) { + // 空节点平衡因子为 0 + if (node == nullptr) + return 0; + // 节点平衡因子 = 左子树高度 - 右子树高度 + return height(node->left) - height(node->right); + } ``` === "Java" ```java title="avl_tree.java" - [class]{AVLTree}-[func]{balanceFactor} + /* 获取平衡因子 */ + int balanceFactor(TreeNode node) { + // 空节点平衡因子为 0 + if (node == null) + return 0; + // 节点平衡因子 = 左子树高度 - 右子树高度 + return height(node.left) - height(node.right); + } ``` === "C#" ```csharp title="avl_tree.cs" - [class]{AVLTree}-[func]{balanceFactor} + /* 获取平衡因子 */ + int balanceFactor(TreeNode? node) { + // 空节点平衡因子为 0 + if (node == null) return 0; + // 节点平衡因子 = 左子树高度 - 右子树高度 + return height(node.left) - height(node.right); + } ``` === "Go" ```go title="avl_tree.go" - [class]{aVLTree}-[func]{balanceFactor} + /* 获取平衡因子 */ + func (t *aVLTree) balanceFactor(node *TreeNode) int { + // 空节点平衡因子为 0 + if node == nil { + return 0 + } + // 节点平衡因子 = 左子树高度 - 右子树高度 + return t.height(node.Left) - t.height(node.Right) + } ``` === "Swift" ```swift title="avl_tree.swift" - [class]{AVLTree}-[func]{balanceFactor} + /* 获取平衡因子 */ + func balanceFactor(node: TreeNode?) -> Int { + // 空节点平衡因子为 0 + guard let node = node else { return 0 } + // 节点平衡因子 = 左子树高度 - 右子树高度 + return height(node: node.left) - height(node: node.right) + } ``` === "JS" ```javascript title="avl_tree.js" - [class]{AVLTree}-[func]{balanceFactor} + /* 获取平衡因子 */ + balanceFactor(node) { + // 空节点平衡因子为 0 + if (node === null) return 0; + // 节点平衡因子 = 左子树高度 - 右子树高度 + return this.height(node.left) - this.height(node.right); + } ``` === "TS" ```typescript title="avl_tree.ts" - [class]{AVLTree}-[func]{balanceFactor} + /* 获取平衡因子 */ + balanceFactor(node: TreeNode): number { + // 空节点平衡因子为 0 + if (node === null) return 0; + // 节点平衡因子 = 左子树高度 - 右子树高度 + return this.height(node.left) - this.height(node.right); + } ``` === "Dart" ```dart title="avl_tree.dart" - [class]{AVLTree}-[func]{balanceFactor} + /* 获取平衡因子 */ + int balanceFactor(TreeNode? node) { + // 空节点平衡因子为 0 + if (node == null) return 0; + // 节点平衡因子 = 左子树高度 - 右子树高度 + return height(node.left) - height(node.right); + } ``` === "Rust" ```rust title="avl_tree.rs" - [class]{AVLTree}-[func]{balance_factor} + /* 获取平衡因子 */ + 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()) + } + } + } ``` === "C" ```c title="avl_tree.c" - [class]{}-[func]{balanceFactor} + /* 获取平衡因子 */ + int balanceFactor(TreeNode *node) { + // 空节点平衡因子为 0 + if (node == NULL) { + return 0; + } + // 节点平衡因子 = 左子树高度 - 右子树高度 + return height(node->left) - height(node->right); + } ``` === "Zig" ```zig title="avl_tree.zig" - [class]{AVLTree}-[func]{balanceFactor} + // 获取平衡因子 + fn balanceFactor(self: *Self, node: ?*inc.TreeNode(T)) i32 { + // 空节点平衡因子为 0 + if (node == null) return 0; + // 节点平衡因子 = 左子树高度 - 右子树高度 + return self.height(node.?.left) - self.height(node.?.right); + } ``` !!! note @@ -430,73 +636,222 @@ AVL 树的特点在于“旋转”操作,它能够在不影响二叉树的中 === "Python" ```python title="avl_tree.py" - [class]{AVLTree}-[func]{__right_rotate} + def __right_rotate(self, node: TreeNode | None) -> TreeNode | None: + """右旋操作""" + child = node.left + grand_child = child.right + # 以 child 为原点,将 node 向右旋转 + child.right = node + node.left = grand_child + # 更新节点高度 + self.__update_height(node) + self.__update_height(child) + # 返回旋转后子树的根节点 + return child ``` === "C++" ```cpp title="avl_tree.cpp" - [class]{AVLTree}-[func]{rightRotate} + /* 右旋操作 */ + 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; + } ``` === "Java" ```java title="avl_tree.java" - [class]{AVLTree}-[func]{rightRotate} + /* 右旋操作 */ + 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; + } ``` === "C#" ```csharp title="avl_tree.cs" - [class]{AVLTree}-[func]{rightRotate} + /* 右旋操作 */ + 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; + } ``` === "Go" ```go title="avl_tree.go" - [class]{aVLTree}-[func]{rightRotate} + /* 右旋操作 */ + 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 + } ``` === "Swift" ```swift title="avl_tree.swift" - [class]{AVLTree}-[func]{rightRotate} + /* 右旋操作 */ + 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 + } ``` === "JS" ```javascript title="avl_tree.js" - [class]{AVLTree}-[func]{#rightRotate} + /* 右旋操作 */ + #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; + } ``` === "TS" ```typescript title="avl_tree.ts" - [class]{AVLTree}-[func]{rightRotate} + /* 右旋操作 */ + 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; + } ``` === "Dart" ```dart title="avl_tree.dart" - [class]{AVLTree}-[func]{rightRotate} + /* 右旋操作 */ + 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; + } ``` === "Rust" ```rust title="avl_tree.rs" - [class]{AVLTree}-[func]{right_rotate} + /* 右旋操作 */ + 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, + } + } ``` === "C" ```c title="avl_tree.c" - [class]{}-[func]{rightRotate} + /* 右旋操作 */ + 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; + } ``` === "Zig" ```zig title="avl_tree.zig" - [class]{AVLTree}-[func]{rightRotate} + // 右旋操作 + 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; + } ``` ### 2.   左旋 @@ -518,73 +873,222 @@ AVL 树的特点在于“旋转”操作,它能够在不影响二叉树的中 === "Python" ```python title="avl_tree.py" - [class]{AVLTree}-[func]{__left_rotate} + def __left_rotate(self, node: TreeNode | None) -> TreeNode | None: + """左旋操作""" + child = node.right + grand_child = child.left + # 以 child 为原点,将 node 向左旋转 + child.left = node + node.right = grand_child + # 更新节点高度 + self.__update_height(node) + self.__update_height(child) + # 返回旋转后子树的根节点 + return child ``` === "C++" ```cpp title="avl_tree.cpp" - [class]{AVLTree}-[func]{leftRotate} + /* 左旋操作 */ + 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; + } ``` === "Java" ```java title="avl_tree.java" - [class]{AVLTree}-[func]{leftRotate} + /* 左旋操作 */ + 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; + } ``` === "C#" ```csharp title="avl_tree.cs" - [class]{AVLTree}-[func]{leftRotate} + /* 左旋操作 */ + 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; + } ``` === "Go" ```go title="avl_tree.go" - [class]{aVLTree}-[func]{leftRotate} + /* 左旋操作 */ + 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 + } ``` === "Swift" ```swift title="avl_tree.swift" - [class]{AVLTree}-[func]{leftRotate} + /* 左旋操作 */ + 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 + } ``` === "JS" ```javascript title="avl_tree.js" - [class]{AVLTree}-[func]{#leftRotate} + /* 左旋操作 */ + #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; + } ``` === "TS" ```typescript title="avl_tree.ts" - [class]{AVLTree}-[func]{leftRotate} + /* 左旋操作 */ + 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; + } ``` === "Dart" ```dart title="avl_tree.dart" - [class]{AVLTree}-[func]{leftRotate} + /* 左旋操作 */ + 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; + } ``` === "Rust" ```rust title="avl_tree.rs" - [class]{AVLTree}-[func]{left_rotate} + /* 左旋操作 */ + 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, + } + } ``` === "C" ```c title="avl_tree.c" - [class]{}-[func]{leftRotate} + /* 左旋操作 */ + 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; + } ``` === "Zig" ```zig title="avl_tree.zig" - [class]{AVLTree}-[func]{leftRotate} + // 左旋操作 + 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; + } ``` ### 3.   先左旋后右旋 @@ -631,73 +1135,410 @@ AVL 树的特点在于“旋转”操作,它能够在不影响二叉树的中 === "Python" ```python title="avl_tree.py" - [class]{AVLTree}-[func]{__rotate} + def __rotate(self, node: TreeNode | None) -> TreeNode | None: + """执行旋转操作,使该子树重新恢复平衡""" + # 获取节点 node 的平衡因子 + balance_factor = self.balance_factor(node) + # 左偏树 + if balance_factor > 1: + if self.balance_factor(node.left) >= 0: + # 右旋 + return self.__right_rotate(node) + else: + # 先左旋后右旋 + node.left = self.__left_rotate(node.left) + return self.__right_rotate(node) + # 右偏树 + elif balance_factor < -1: + if self.balance_factor(node.right) <= 0: + # 左旋 + return self.__left_rotate(node) + else: + # 先右旋后左旋 + node.right = self.__right_rotate(node.right) + return self.__left_rotate(node) + # 平衡树,无须旋转,直接返回 + return node ``` === "C++" ```cpp title="avl_tree.cpp" - [class]{AVLTree}-[func]{rotate} + /* 执行旋转操作,使该子树重新恢复平衡 */ + 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; + } ``` === "Java" ```java title="avl_tree.java" - [class]{AVLTree}-[func]{rotate} + /* 执行旋转操作,使该子树重新恢复平衡 */ + 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; + } ``` === "C#" ```csharp title="avl_tree.cs" - [class]{AVLTree}-[func]{rotate} + /* 执行旋转操作,使该子树重新恢复平衡 */ + 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; + } ``` === "Go" ```go title="avl_tree.go" - [class]{aVLTree}-[func]{rotate} + /* 执行旋转操作,使该子树重新恢复平衡 */ + 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 + } ``` === "Swift" ```swift title="avl_tree.swift" - [class]{AVLTree}-[func]{rotate} + /* 执行旋转操作,使该子树重新恢复平衡 */ + 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 + } ``` === "JS" ```javascript title="avl_tree.js" - [class]{AVLTree}-[func]{#rotate} + /* 执行旋转操作,使该子树重新恢复平衡 */ + #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; + } ``` === "TS" ```typescript title="avl_tree.ts" - [class]{AVLTree}-[func]{rotate} + /* 执行旋转操作,使该子树重新恢复平衡 */ + 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; + } ``` === "Dart" ```dart title="avl_tree.dart" - [class]{AVLTree}-[func]{rotate} + /* 执行旋转操作,使该子树重新恢复平衡 */ + 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; + } ``` === "Rust" ```rust title="avl_tree.rs" - [class]{AVLTree}-[func]{rotate} + /* 执行旋转操作,使该子树重新恢复平衡 */ + 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 + } + } ``` === "C" ```c title="avl_tree.c" - [class]{}-[func]{rotate} + /* 执行旋转操作,使该子树重新恢复平衡 */ + 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; + } ``` === "Zig" ```zig title="avl_tree.zig" - [class]{AVLTree}-[func]{rotate} + // 执行旋转操作,使该子树重新恢复平衡 + 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; + } ``` ## 7.5.3   AVL 树常用操作 @@ -709,97 +1550,349 @@ AVL 树的节点插入操作与二叉搜索树在主体上类似。唯一的区 === "Python" ```python title="avl_tree.py" - [class]{AVLTree}-[func]{insert} + def insert(self, val): + """插入节点""" + self.root = self.__insert_helper(self.root, val) - [class]{AVLTree}-[func]{__insert_helper} + def __insert_helper(self, node: TreeNode | None, val: int) -> TreeNode: + """递归插入节点(辅助方法)""" + if node is None: + return TreeNode(val) + # 1. 查找插入位置,并插入节点 + 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: + # 重复节点不插入,直接返回 + return node + # 更新节点高度 + self.__update_height(node) + # 2. 执行旋转操作,使该子树重新恢复平衡 + return self.__rotate(node) ``` === "C++" ```cpp title="avl_tree.cpp" - [class]{AVLTree}-[func]{insert} + /* 插入节点 */ + void insert(int val) { + root = insertHelper(root, val); + } - [class]{AVLTree}-[func]{insertHelper} + /* 递归插入节点(辅助方法) */ + 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; + } ``` === "Java" ```java title="avl_tree.java" - [class]{AVLTree}-[func]{insert} + /* 插入节点 */ + void insert(int val) { + root = insertHelper(root, val); + } - [class]{AVLTree}-[func]{insertHelper} + /* 递归插入节点(辅助方法) */ + 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; + } ``` === "C#" ```csharp title="avl_tree.cs" - [class]{AVLTree}-[func]{insert} + /* 插入节点 */ + void insert(int val) { + root = insertHelper(root, val); + } - [class]{AVLTree}-[func]{insertHelper} + /* 递归插入节点(辅助方法) */ + 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; + } ``` === "Go" ```go title="avl_tree.go" - [class]{aVLTree}-[func]{insert} + /* 插入节点 */ + func (t *aVLTree) insert(val int) { + t.root = t.insertHelper(t.root, val) + } - [class]{aVLTree}-[func]{insertHelper} + /* 递归插入节点(辅助函数) */ + 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 + } ``` === "Swift" ```swift title="avl_tree.swift" - [class]{AVLTree}-[func]{insert} + /* 插入节点 */ + func insert(val: Int) { + root = insertHelper(node: root, val: val) + } - [class]{AVLTree}-[func]{insertHelper} + /* 递归插入节点(辅助方法) */ + 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 + } ``` === "JS" ```javascript title="avl_tree.js" - [class]{AVLTree}-[func]{insert} + /* 插入节点 */ + insert(val) { + this.root = this.#insertHelper(this.root, val); + } - [class]{AVLTree}-[func]{#insertHelper} + /* 递归插入节点(辅助方法) */ + #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; + } ``` === "TS" ```typescript title="avl_tree.ts" - [class]{AVLTree}-[func]{insert} + /* 插入节点 */ + insert(val: number): void { + this.root = this.insertHelper(this.root, val); + } - [class]{AVLTree}-[func]{insertHelper} + /* 递归插入节点(辅助方法) */ + 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; + } ``` === "Dart" ```dart title="avl_tree.dart" - [class]{AVLTree}-[func]{insert} + /* 插入节点 */ + void insert(int val) { + root = insertHelper(root, val); + } - [class]{AVLTree}-[func]{insertHelper} + /* 递归插入节点(辅助方法) */ + 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; + } ``` === "Rust" ```rust title="avl_tree.rs" - [class]{AVLTree}-[func]{insert} + /* 插入节点 */ + fn insert(&mut self, val: i32) { + self.root = Self::insert_helper(self.root.clone(), val); + } - [class]{AVLTree}-[func]{insert_helper} + /* 递归插入节点(辅助方法) */ + 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)), + } + } ``` === "C" ```c title="avl_tree.c" - [class]{aVLTree}-[func]{insert} + /* 插入节点 */ + void insert(aVLTree *tree, int val) { + tree->root = insertHelper(tree->root, val); + } - [class]{}-[func]{insertHelper} + /* 递归插入节点(辅助函数) */ + 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; + } ``` === "Zig" ```zig title="avl_tree.zig" - [class]{AVLTree}-[func]{insert} + // 插入节点 + fn insert(self: *Self, val: T) !void { + self.root = (try self.insertHelper(self.root, val)).?; + } - [class]{AVLTree}-[func]{insertHelper} + // 递归插入节点(辅助方法) + 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; + } ``` ### 2.   删除节点 @@ -809,97 +1902,556 @@ AVL 树的节点插入操作与二叉搜索树在主体上类似。唯一的区 === "Python" ```python title="avl_tree.py" - [class]{AVLTree}-[func]{remove} + def remove(self, val: int): + """删除节点""" + self.root = self.__remove_helper(self.root, val) - [class]{AVLTree}-[func]{__remove_helper} + def __remove_helper(self, node: TreeNode | None, val: int) -> TreeNode | None: + """递归删除节点(辅助方法)""" + if node is None: + return None + # 1. 查找节点,并删除之 + if val < node.val: + node.left = self.__remove_helper(node.left, val) + elif val > node.val: + node.right = self.__remove_helper(node.right, val) + else: + if node.left is None or node.right is None: + child = node.left or node.right + # 子节点数量 = 0 ,直接删除 node 并返回 + if child is None: + return None + # 子节点数量 = 1 ,直接删除 node + else: + node = child + else: + # 子节点数量 = 2 ,则将中序遍历的下个节点删除,并用该节点替换当前节点 + 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 + # 更新节点高度 + self.__update_height(node) + # 2. 执行旋转操作,使该子树重新恢复平衡 + return self.__rotate(node) ``` === "C++" ```cpp title="avl_tree.cpp" - [class]{AVLTree}-[func]{remove} + /* 删除节点 */ + void remove(int val) { + root = removeHelper(root, val); + } - [class]{AVLTree}-[func]{removeHelper} + /* 递归删除节点(辅助方法) */ + 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; + } ``` === "Java" ```java title="avl_tree.java" - [class]{AVLTree}-[func]{remove} + /* 删除节点 */ + void remove(int val) { + root = removeHelper(root, val); + } - [class]{AVLTree}-[func]{removeHelper} + /* 递归删除节点(辅助方法) */ + 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 != null ? 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; + } ``` === "C#" ```csharp title="avl_tree.cs" - [class]{AVLTree}-[func]{remove} + /* 删除节点 */ + void remove(int val) { + root = removeHelper(root, val); + } - [class]{AVLTree}-[func]{removeHelper} + /* 递归删除节点(辅助方法) */ + 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 != null ? 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; + } ``` === "Go" ```go title="avl_tree.go" - [class]{aVLTree}-[func]{remove} + /* 删除节点 */ + func (t *aVLTree) remove(val int) { + t.root = t.removeHelper(t.root, val) + } - [class]{aVLTree}-[func]{removeHelper} + /* 递归删除节点(辅助函数) */ + 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 + } ``` === "Swift" ```swift title="avl_tree.swift" - [class]{AVLTree}-[func]{remove} + /* 删除节点 */ + func remove(val: Int) { + root = removeHelper(node: root, val: val) + } - [class]{AVLTree}-[func]{removeHelper} + /* 递归删除节点(辅助方法) */ + 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 != nil ? 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 + } ``` === "JS" ```javascript title="avl_tree.js" - [class]{AVLTree}-[func]{remove} + /* 删除节点 */ + remove(val) { + this.root = this.#removeHelper(this.root, val); + } - [class]{AVLTree}-[func]{#removeHelper} + /* 递归删除节点(辅助方法) */ + #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; + } ``` === "TS" ```typescript title="avl_tree.ts" - [class]{AVLTree}-[func]{remove} + /* 删除节点 */ + remove(val: number): void { + this.root = this.removeHelper(this.root, val); + } - [class]{AVLTree}-[func]{removeHelper} + /* 递归删除节点(辅助方法) */ + 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; + } ``` === "Dart" ```dart title="avl_tree.dart" - [class]{AVLTree}-[func]{remove} + /* 删除节点 */ + void remove(int val) { + root = removeHelper(root, val); + } - [class]{AVLTree}-[func]{removeHelper} + /* 递归删除节点(辅助方法) */ + 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; + } ``` === "Rust" ```rust title="avl_tree.rs" - [class]{AVLTree}-[func]{remove} + /* 删除节点 */ + fn remove(&self, val: i32) { + Self::remove_helper(self.root.clone(), val); + } - [class]{AVLTree}-[func]{remove_helper} + /* 递归删除节点(辅助方法) */ + 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, + } + } ``` === "C" ```c title="avl_tree.c" - [class]{aVLTree}-[func]{removeNode} + /* 删除节点 */ + // 由于引入了 stdio.h ,此处无法使用 remove 关键词 + void removeNode(aVLTree *tree, int val) { + TreeNode *root = removeHelper(tree->root, val); + } - [class]{}-[func]{removeHelper} + /* 递归删除节点(辅助函数) */ + 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; + } ``` === "Zig" ```zig title="avl_tree.zig" - [class]{AVLTree}-[func]{remove} + // 删除节点 + fn remove(self: *Self, val: T) void { + self.root = self.removeHelper(self.root, val).?; + } - [class]{AVLTree}-[func]{removeHelper} + // 递归删除节点(辅助方法) + 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; + } ``` ### 3.   查找节点 diff --git a/zh/chapter_tree/binary_search_tree.md b/zh/chapter_tree/binary_search_tree.md index bfb8c39c2..cb8343e6b 100755 --- a/zh/chapter_tree/binary_search_tree.md +++ b/zh/chapter_tree/binary_search_tree.md @@ -44,73 +44,278 @@ comments: true === "Python" ```python title="binary_search_tree.py" - [class]{BinarySearchTree}-[func]{search} + def search(self, num: int) -> TreeNode | None: + """查找节点""" + cur = self.__root + # 循环查找,越过叶节点后跳出 + while cur is not None: + # 目标节点在 cur 的右子树中 + if cur.val < num: + cur = cur.right + # 目标节点在 cur 的左子树中 + elif cur.val > num: + cur = cur.left + # 找到目标节点,跳出循环 + else: + break + return cur ``` === "C++" ```cpp title="binary_search_tree.cpp" - [class]{BinarySearchTree}-[func]{search} + /* 查找节点 */ + 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; + } ``` === "Java" ```java title="binary_search_tree.java" - [class]{BinarySearchTree}-[func]{search} + /* 查找节点 */ + 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; + } ``` === "C#" ```csharp title="binary_search_tree.cs" - [class]{BinarySearchTree}-[func]{search} + /* 查找节点 */ + 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; + } ``` === "Go" ```go title="binary_search_tree.go" - [class]{binarySearchTree}-[func]{search} + /* 查找节点 */ + 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 + } ``` === "Swift" ```swift title="binary_search_tree.swift" - [class]{BinarySearchTree}-[func]{search} + /* 查找节点 */ + 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 + } ``` === "JS" ```javascript title="binary_search_tree.js" - [class]{BinarySearchTree}-[func]{search} + /* 查找节点 */ + 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; + } ``` === "TS" ```typescript title="binary_search_tree.ts" - [class]{BinarySearchTree}-[func]{search} + /* 查找节点 */ + 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; + } ``` === "Dart" ```dart title="binary_search_tree.dart" - [class]{BinarySearchTree}-[func]{search} + /* 查找节点 */ + 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; + } ``` === "Rust" ```rust title="binary_search_tree.rs" - [class]{BinarySearchTree}-[func]{search} + /* 查找节点 */ + pub fn search(&self, num: i32) -> Option { + let mut cur = self.root.clone(); + + // 循环查找,越过叶节点后跳出 + while let Some(node) = cur.clone() { + // 目标节点在 cur 的右子树中 + if node.borrow().val < num { + cur = node.borrow().right.clone(); + } + // 目标节点在 cur 的左子树中 + else if node.borrow().val > num { + cur = node.borrow().left.clone(); + } + // 找到目标节点,跳出循环 + else { + break; + } + } + // 返回目标节点 + cur + } ``` === "C" ```c title="binary_search_tree.c" - [class]{binarySearchTree}-[func]{search} + /* 查找节点 */ + 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; + } ``` === "Zig" ```zig title="binary_search_tree.zig" - [class]{BinarySearchTree}-[func]{search} + // 查找节点 + 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; + } ``` ### 2.   插入节点 @@ -132,73 +337,408 @@ comments: true === "Python" ```python title="binary_search_tree.py" - [class]{BinarySearchTree}-[func]{insert} + def insert(self, num: int): + """插入节点""" + # 若树为空,则初始化根节点 + if self.__root is None: + self.__root = TreeNode(num) + return + # 循环查找,越过叶节点后跳出 + cur, pre = self.__root, None + while cur is not None: + # 找到重复节点,直接返回 + if cur.val == num: + return + pre = cur + # 插入位置在 cur 的右子树中 + if cur.val < num: + cur = cur.right + # 插入位置在 cur 的左子树中 + else: + cur = cur.left + # 插入节点 + node = TreeNode(num) + if pre.val < num: + pre.right = node + else: + pre.left = node ``` === "C++" ```cpp title="binary_search_tree.cpp" - [class]{BinarySearchTree}-[func]{insert} + /* 插入节点 */ + 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; + } ``` === "Java" ```java title="binary_search_tree.java" - [class]{BinarySearchTree}-[func]{insert} + /* 插入节点 */ + 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 TreeNode(num); + if (pre.val < num) + pre.right = node; + else + pre.left = node; + } ``` === "C#" ```csharp title="binary_search_tree.cs" - [class]{BinarySearchTree}-[func]{insert} + /* 插入节点 */ + 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 TreeNode(num); + if (pre != null) { + if (pre.val < num) + pre.right = node; + else + pre.left = node; + } + } ``` === "Go" ```go title="binary_search_tree.go" - [class]{binarySearchTree}-[func]{insert} + /* 插入节点 */ + 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 + } + } ``` === "Swift" ```swift title="binary_search_tree.swift" - [class]{BinarySearchTree}-[func]{insert} + /* 插入节点 */ + 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 + } + } ``` === "JS" ```javascript title="binary_search_tree.js" - [class]{BinarySearchTree}-[func]{insert} + /* 插入节点 */ + 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; + } ``` === "TS" ```typescript title="binary_search_tree.ts" - [class]{BinarySearchTree}-[func]{insert} + /* 插入节点 */ + 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; + } ``` === "Dart" ```dart title="binary_search_tree.dart" - [class]{BinarySearchTree}-[func]{insert} + /* 插入节点 */ + 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; + } ``` === "Rust" ```rust title="binary_search_tree.rs" - [class]{BinarySearchTree}-[func]{insert} + /* 插入节点 */ + 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() { + // 找到重复节点,直接返回 + if node.borrow().val == num { + return; + } + // 插入位置在 cur 的右子树中 + pre = cur.clone(); + if node.borrow().val < num { + cur = node.borrow().right.clone(); + } + // 插入位置在 cur 的左子树中 + else { + cur = node.borrow().left.clone(); + } + } + // 插入节点 + let node = TreeNode::new(num); + let pre = pre.unwrap(); + if pre.borrow().val < num { + pre.borrow_mut().right = Some(Rc::clone(&node)); + } else { + pre.borrow_mut().left = Some(Rc::clone(&node)); + } + } ``` === "C" ```c title="binary_search_tree.c" - [class]{binarySearchTree}-[func]{insert} + /* 插入节点 */ + 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; + } + } ``` === "Zig" ```zig title="binary_search_tree.zig" - [class]{BinarySearchTree}-[func]{insert} + // 插入节点 + 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; + } + } ``` 与查找节点相同,插入节点使用 $O(\log n)$ 时间。 @@ -249,73 +789,671 @@ comments: true === "Python" ```python title="binary_search_tree.py" - [class]{BinarySearchTree}-[func]{remove} + def remove(self, num: int): + """删除节点""" + # 若树为空,直接提前返回 + if self.__root is None: + return + # 循环查找,越过叶节点后跳出 + cur, pre = self.__root, None + while cur is not None: + # 找到待删除节点,跳出循环 + if cur.val == num: + break + pre = cur + # 待删除节点在 cur 的右子树中 + if cur.val < num: + cur = cur.right + # 待删除节点在 cur 的左子树中 + else: + cur = cur.left + # 若无待删除节点,则直接返回 + if cur is None: + return + + # 子节点数量 = 0 or 1 + if cur.left is None or cur.right is None: + # 当子节点数量 = 0 / 1 时, child = null / 该子节点 + child = cur.left or cur.right + # 删除节点 cur + if cur != self.__root: + if pre.left == cur: + pre.left = child + else: + pre.right = child + else: + # 若删除节点为根节点,则重新指定根节点 + self.__root = child + # 子节点数量 = 2 + else: + # 获取中序遍历中 cur 的下一个节点 + tmp: TreeNode = cur.right + while tmp.left is not None: + tmp = tmp.left + # 递归删除节点 tmp + self.remove(tmp.val) + # 用 tmp 覆盖 cur + cur.val = tmp.val ``` === "C++" ```cpp title="binary_search_tree.cpp" - [class]{BinarySearchTree}-[func]{remove} + /* 删除节点 */ + 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; + } + } ``` === "Java" ```java title="binary_search_tree.java" - [class]{BinarySearchTree}-[func]{remove} + /* 删除节点 */ + 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 != null ? 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); + // 用 tmp 覆盖 cur + cur.val = tmp.val; + } + } ``` === "C#" ```csharp title="binary_search_tree.cs" - [class]{BinarySearchTree}-[func]{remove} + /* 删除节点 */ + 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 != null ? 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); + // 用 tmp 覆盖 cur + cur.val = tmp.val; + } + } ``` === "Go" ```go title="binary_search_tree.go" - [class]{binarySearchTree}-[func]{remove} + /* 删除节点 */ + 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 + } + } ``` === "Swift" ```swift title="binary_search_tree.swift" - [class]{BinarySearchTree}-[func]{remove} + /* 删除节点 */ + 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 != nil ? 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 + } + } ``` === "JS" ```javascript title="binary_search_tree.js" - [class]{BinarySearchTree}-[func]{remove} + /* 删除节点 */ + 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; + } + } ``` === "TS" ```typescript title="binary_search_tree.ts" - [class]{BinarySearchTree}-[func]{remove} + /* 删除节点 */ + 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; + } + } ``` === "Dart" ```dart title="binary_search_tree.dart" - [class]{BinarySearchTree}-[func]{remove} + /* 删除节点 */ + 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; + } + } ``` === "Rust" ```rust title="binary_search_tree.rs" - [class]{BinarySearchTree}-[func]{remove} + /* 删除节点 */ + 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() { + // 找到待删除节点,跳出循环 + if node.borrow().val == num { + break; + } + // 待删除节点在 cur 的右子树中 + pre = cur.clone(); + if node.borrow().val < num { + cur = node.borrow().right.clone(); + } + // 待删除节点在 cur 的左子树中 + else { + cur = node.borrow().left.clone(); + } + } + // 若无待删除节点,则直接返回 + if cur.is_none() { + return; + } + let cur = cur.unwrap(); + // 子节点数量 = 0 or 1 + if cur.borrow().left.is_none() || cur.borrow().right.is_none() { + // 当子节点数量 = 0 / 1 时, child = nullptr / 该子节点 + let child = cur.borrow().left.clone().or_else(|| cur.borrow().right.clone()); + let pre = pre.unwrap(); + let left = pre.borrow().left.clone().unwrap(); + // 删除节点 cur + if !Rc::ptr_eq(&cur, self.root.as_ref().unwrap()) { + if Rc::ptr_eq(&left, &cur) { + pre.borrow_mut().left = child; + } else { + pre.borrow_mut().right = child; + } + } else { + // 若删除节点为根节点,则重新指定根节点 + self.root = child; + } + } + // 子节点数量 = 2 + else { + // 获取中序遍历中 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; + } + } ``` === "C" ```c title="binary_search_tree.c" - [class]{binarySearchTree}-[func]{removeNode} + /* 删除节点 */ + // 由于引入了 stdio.h ,此处无法使用 remove 关键词 + void removeNode(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; + } + } else { + /* 子节点数量 = 2 */ + // 获取中序遍历中 cur 的下一个节点 + TreeNode *tmp = cur->right; + while (tmp->left != NULL) { + tmp = tmp->left; + } + int tmpVal = tmp->val; + // 递归删除节点 tmp + removeNode(bst, tmp->val); + // 用 tmp 覆盖 cur + cur->val = tmpVal; + } + } ``` === "Zig" ```zig title="binary_search_tree.zig" - [class]{BinarySearchTree}-[func]{remove} + // 删除节点 + 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; + } + } ``` ### 4.   中序遍历有序 diff --git a/zh/chapter_tree/binary_tree_traversal.md b/zh/chapter_tree/binary_tree_traversal.md index b0e3c0280..aa93bffc1 100755 --- a/zh/chapter_tree/binary_tree_traversal.md +++ b/zh/chapter_tree/binary_tree_traversal.md @@ -25,73 +25,303 @@ comments: true === "Python" ```python title="binary_tree_bfs.py" - [class]{}-[func]{level_order} + def level_order(root: TreeNode | None) -> list[int]: + """层序遍历""" + # 初始化队列,加入根节点 + queue: deque[TreeNode] = deque() + queue.append(root) + # 初始化一个列表,用于保存遍历序列 + res = [] + while queue: + node: TreeNode = queue.popleft() # 队列出队 + res.append(node.val) # 保存节点值 + if node.left is not None: + queue.append(node.left) # 左子节点入队 + if node.right is not None: + queue.append(node.right) # 右子节点入队 + return res ``` === "C++" ```cpp title="binary_tree_bfs.cpp" - [class]{}-[func]{levelOrder} + /* 层序遍历 */ + 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; + } ``` === "Java" ```java title="binary_tree_bfs.java" - [class]{binary_tree_bfs}-[func]{levelOrder} + /* 层序遍历 */ + List levelOrder(TreeNode root) { + // 初始化队列,加入根节点 + Queue queue = new LinkedList<>(); + queue.add(root); + // 初始化一个列表,用于保存遍历序列 + List list = new ArrayList<>(); + while (!queue.isEmpty()) { + TreeNode 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; + } ``` === "C#" ```csharp title="binary_tree_bfs.cs" - [class]{binary_tree_bfs}-[func]{levelOrder} + /* 层序遍历 */ + List levelOrder(TreeNode root) { + // 初始化队列,加入根节点 + Queue queue = new(); + queue.Enqueue(root); + // 初始化一个列表,用于保存遍历序列 + List list = new(); + while (queue.Count != 0) { + TreeNode node = queue.Dequeue(); // 队列出队 + list.Add(node.val); // 保存节点值 + if (node.left != null) + queue.Enqueue(node.left); // 左子节点入队 + if (node.right != null) + queue.Enqueue(node.right); // 右子节点入队 + } + return list; + } ``` === "Go" ```go title="binary_tree_bfs.go" - [class]{}-[func]{levelOrder} + /* 层序遍历 */ + 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 + } ``` === "Swift" ```swift title="binary_tree_bfs.swift" - [class]{}-[func]{levelOrder} + /* 层序遍历 */ + 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 + } ``` === "JS" ```javascript title="binary_tree_bfs.js" - [class]{}-[func]{levelOrder} + /* 层序遍历 */ + 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; + } ``` === "TS" ```typescript title="binary_tree_bfs.ts" - [class]{}-[func]{levelOrder} + /* 层序遍历 */ + 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; + } ``` === "Dart" ```dart title="binary_tree_bfs.dart" - [class]{}-[func]{levelOrder} + /* 层序遍历 */ + 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; + } ``` === "Rust" ```rust title="binary_tree_bfs.rs" - [class]{}-[func]{level_order} + /* 层序遍历 */ + fn level_order(root: &Rc>) -> Vec { + // 初始化队列,加入根结点 + let mut que = VecDeque::new(); + que.push_back(Rc::clone(&root)); + // 初始化一个列表,用于保存遍历序列 + 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(Rc::clone(left)); // 左子结点入队 + } + if let Some(right) = node.borrow().right.as_ref() { + que.push_back(Rc::clone(right)); // 右子结点入队 + }; + } + vec + } ``` === "C" ```c title="binary_tree_bfs.c" - [class]{}-[func]{levelOrder} + /* 层序遍历 */ + int *levelOrder(TreeNode *root, int *size) { + /* 辅助队列 */ + int front, rear; + int index, *arr; + TreeNode *node; + TreeNode **queue; + + /* 辅助队列 */ + queue = (TreeNode **)malloc(sizeof(TreeNode *) * MAX_NODE_SIZE); + // 队列指针 + front = 0, rear = 0; + // 加入根节点 + queue[rear++] = root; + // 初始化一个列表,用于保存遍历序列 + /* 辅助数组 */ + arr = (int *)malloc(sizeof(int) * MAX_NODE_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; + } ``` === "Zig" ```zig title="binary_tree_bfs.zig" - [class]{}-[func]{levelOrder} + // 层序遍历 + 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; + } ``` ### 2.   复杂度分析 @@ -116,121 +346,412 @@ comments: true === "Python" ```python title="binary_tree_dfs.py" - [class]{}-[func]{pre_order} + def pre_order(root: TreeNode | None): + """前序遍历""" + if root is None: + return + # 访问优先级:根节点 -> 左子树 -> 右子树 + res.append(root.val) + pre_order(root=root.left) + pre_order(root=root.right) - [class]{}-[func]{in_order} + def in_order(root: TreeNode | None): + """中序遍历""" + if root is None: + return + # 访问优先级:左子树 -> 根节点 -> 右子树 + in_order(root=root.left) + res.append(root.val) + in_order(root=root.right) - [class]{}-[func]{post_order} + def post_order(root: TreeNode | None): + """后序遍历""" + if root is None: + return + # 访问优先级:左子树 -> 右子树 -> 根节点 + post_order(root=root.left) + post_order(root=root.right) + res.append(root.val) ``` === "C++" ```cpp title="binary_tree_dfs.cpp" - [class]{}-[func]{preOrder} + /* 前序遍历 */ + void preOrder(TreeNode *root) { + if (root == nullptr) + return; + // 访问优先级:根节点 -> 左子树 -> 右子树 + vec.push_back(root->val); + preOrder(root->left); + preOrder(root->right); + } - [class]{}-[func]{inOrder} + /* 中序遍历 */ + void inOrder(TreeNode *root) { + if (root == nullptr) + return; + // 访问优先级:左子树 -> 根节点 -> 右子树 + inOrder(root->left); + vec.push_back(root->val); + inOrder(root->right); + } - [class]{}-[func]{postOrder} + /* 后序遍历 */ + void postOrder(TreeNode *root) { + if (root == nullptr) + return; + // 访问优先级:左子树 -> 右子树 -> 根节点 + postOrder(root->left); + postOrder(root->right); + vec.push_back(root->val); + } ``` === "Java" ```java title="binary_tree_dfs.java" - [class]{binary_tree_dfs}-[func]{preOrder} + /* 前序遍历 */ + void preOrder(TreeNode root) { + if (root == null) + return; + // 访问优先级:根节点 -> 左子树 -> 右子树 + list.add(root.val); + preOrder(root.left); + preOrder(root.right); + } - [class]{binary_tree_dfs}-[func]{inOrder} + /* 中序遍历 */ + void inOrder(TreeNode root) { + if (root == null) + return; + // 访问优先级:左子树 -> 根节点 -> 右子树 + inOrder(root.left); + list.add(root.val); + inOrder(root.right); + } - [class]{binary_tree_dfs}-[func]{postOrder} + /* 后序遍历 */ + void postOrder(TreeNode root) { + if (root == null) + return; + // 访问优先级:左子树 -> 右子树 -> 根节点 + postOrder(root.left); + postOrder(root.right); + list.add(root.val); + } ``` === "C#" ```csharp title="binary_tree_dfs.cs" - [class]{binary_tree_dfs}-[func]{preOrder} + /* 前序遍历 */ + void preOrder(TreeNode? root) { + if (root == null) return; + // 访问优先级:根节点 -> 左子树 -> 右子树 + list.Add(root.val); + preOrder(root.left); + preOrder(root.right); + } - [class]{binary_tree_dfs}-[func]{inOrder} + /* 中序遍历 */ + void inOrder(TreeNode? root) { + if (root == null) return; + // 访问优先级:左子树 -> 根节点 -> 右子树 + inOrder(root.left); + list.Add(root.val); + inOrder(root.right); + } - [class]{binary_tree_dfs}-[func]{postOrder} + /* 后序遍历 */ + void postOrder(TreeNode? root) { + if (root == null) return; + // 访问优先级:左子树 -> 右子树 -> 根节点 + postOrder(root.left); + postOrder(root.right); + list.Add(root.val); + } ``` === "Go" ```go title="binary_tree_dfs.go" - [class]{}-[func]{preOrder} + /* 前序遍历 */ + func preOrder(node *TreeNode) { + if node == nil { + return + } + // 访问优先级:根节点 -> 左子树 -> 右子树 + nums = append(nums, node.Val) + preOrder(node.Left) + preOrder(node.Right) + } - [class]{}-[func]{inOrder} + /* 中序遍历 */ + func inOrder(node *TreeNode) { + if node == nil { + return + } + // 访问优先级:左子树 -> 根节点 -> 右子树 + inOrder(node.Left) + nums = append(nums, node.Val) + inOrder(node.Right) + } - [class]{}-[func]{postOrder} + /* 后序遍历 */ + func postOrder(node *TreeNode) { + if node == nil { + return + } + // 访问优先级:左子树 -> 右子树 -> 根节点 + postOrder(node.Left) + postOrder(node.Right) + nums = append(nums, node.Val) + } ``` === "Swift" ```swift title="binary_tree_dfs.swift" - [class]{}-[func]{preOrder} + /* 前序遍历 */ + func preOrder(root: TreeNode?) { + guard let root = root else { + return + } + // 访问优先级:根节点 -> 左子树 -> 右子树 + list.append(root.val) + preOrder(root: root.left) + preOrder(root: root.right) + } - [class]{}-[func]{inOrder} + /* 中序遍历 */ + func inOrder(root: TreeNode?) { + guard let root = root else { + return + } + // 访问优先级:左子树 -> 根节点 -> 右子树 + inOrder(root: root.left) + list.append(root.val) + inOrder(root: root.right) + } - [class]{}-[func]{postOrder} + /* 后序遍历 */ + func postOrder(root: TreeNode?) { + guard let root = root else { + return + } + // 访问优先级:左子树 -> 右子树 -> 根节点 + postOrder(root: root.left) + postOrder(root: root.right) + list.append(root.val) + } ``` === "JS" ```javascript title="binary_tree_dfs.js" - [class]{}-[func]{preOrder} + /* 前序遍历 */ + function preOrder(root) { + if (root === null) return; + // 访问优先级:根节点 -> 左子树 -> 右子树 + list.push(root.val); + preOrder(root.left); + preOrder(root.right); + } - [class]{}-[func]{inOrder} + /* 中序遍历 */ + function inOrder(root) { + if (root === null) return; + // 访问优先级:左子树 -> 根节点 -> 右子树 + inOrder(root.left); + list.push(root.val); + inOrder(root.right); + } - [class]{}-[func]{postOrder} + /* 后序遍历 */ + function postOrder(root) { + if (root === null) return; + // 访问优先级:左子树 -> 右子树 -> 根节点 + postOrder(root.left); + postOrder(root.right); + list.push(root.val); + } ``` === "TS" ```typescript title="binary_tree_dfs.ts" - [class]{}-[func]{preOrder} + /* 前序遍历 */ + function preOrder(root: TreeNode | null): void { + if (root === null) { + return; + } + // 访问优先级:根节点 -> 左子树 -> 右子树 + list.push(root.val); + preOrder(root.left); + preOrder(root.right); + } - [class]{}-[func]{inOrder} + /* 中序遍历 */ + function inOrder(root: TreeNode | null): void { + if (root === null) { + return; + } + // 访问优先级:左子树 -> 根节点 -> 右子树 + inOrder(root.left); + list.push(root.val); + inOrder(root.right); + } - [class]{}-[func]{postOrder} + /* 后序遍历 */ + function postOrder(root: TreeNode | null): void { + if (root === null) { + return; + } + // 访问优先级:左子树 -> 右子树 -> 根节点 + postOrder(root.left); + postOrder(root.right); + list.push(root.val); + } ``` === "Dart" ```dart title="binary_tree_dfs.dart" - [class]{}-[func]{preOrder} + /* 前序遍历 */ + void preOrder(TreeNode? node) { + if (node == null) return; + // 访问优先级:根节点 -> 左子树 -> 右子树 + list.add(node.val); + preOrder(node.left); + preOrder(node.right); + } - [class]{}-[func]{inOrder} + /* 中序遍历 */ + void inOrder(TreeNode? node) { + if (node == null) return; + // 访问优先级:左子树 -> 根节点 -> 右子树 + inOrder(node.left); + list.add(node.val); + inOrder(node.right); + } - [class]{}-[func]{postOrder} + /* 后序遍历 */ + void postOrder(TreeNode? node) { + if (node == null) return; + // 访问优先级:左子树 -> 右子树 -> 根节点 + postOrder(node.left); + postOrder(node.right); + list.add(node.val); + } ``` === "Rust" ```rust title="binary_tree_dfs.rs" - [class]{}-[func]{pre_order} + /* 前序遍历 */ + fn pre_order(root: Option<&Rc>>) -> Vec { + let mut result = vec![]; - [class]{}-[func]{in_order} + if let Some(node) = root { + // 访问优先级:根结点 -> 左子树 -> 右子树 + result.push(node.borrow().val); + result.append(&mut pre_order(node.borrow().left.as_ref())); + result.append(&mut pre_order(node.borrow().right.as_ref())); + } + result + } - [class]{}-[func]{post_order} + /* 中序遍历 */ + fn in_order(root: Option<&Rc>>) -> Vec { + let mut result = vec![]; + + if let Some(node) = root { + // 访问优先级:左子树 -> 根结点 -> 右子树 + result.append(&mut in_order(node.borrow().left.as_ref())); + result.push(node.borrow().val); + result.append(&mut 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.append(&mut post_order(node.borrow().left.as_ref())); + result.append(&mut post_order(node.borrow().right.as_ref())); + result.push(node.borrow().val); + } + result + } ``` === "C" ```c title="binary_tree_dfs.c" - [class]{}-[func]{preOrder} + /* 前序遍历 */ + void preOrder(TreeNode *root, int *size) { + if (root == NULL) + return; + // 访问优先级:根节点 -> 左子树 -> 右子树 + arr[(*size)++] = root->val; + preOrder(root->left, size); + preOrder(root->right, size); + } - [class]{}-[func]{inOrder} + /* 中序遍历 */ + void inOrder(TreeNode *root, int *size) { + if (root == NULL) + return; + // 访问优先级:左子树 -> 根节点 -> 右子树 + inOrder(root->left, size); + arr[(*size)++] = root->val; + inOrder(root->right, size); + } - [class]{}-[func]{postOrder} + /* 后序遍历 */ + void postOrder(TreeNode *root, int *size) { + if (root == NULL) + return; + // 访问优先级:左子树 -> 右子树 -> 根节点 + postOrder(root->left, size); + postOrder(root->right, size); + arr[(*size)++] = root->val; + } ``` === "Zig" ```zig title="binary_tree_dfs.zig" - [class]{}-[func]{preOrder} + // 前序遍历 + 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]{inOrder} + // 中序遍历 + 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]{postOrder} + // 后序遍历 + 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); + } ``` !!! note