From 9c3b7b64229e0a21bd23e48895ce7e9838f44fc0 Mon Sep 17 00:00:00 2001 From: Yudong Jin Date: Mon, 4 Sep 2023 03:19:08 +0800 Subject: [PATCH] Sort the coding languages by applications. (#721) --- .../chapter_greedy/fractional_knapsack.swift | 4 +- docs/chapter_array_and_linkedlist/array.md | 442 +++--- .../linked_list.md | 772 ++++----- docs/chapter_array_and_linkedlist/list.md | 750 ++++----- .../backtracking_algorithm.md | 592 +++---- docs/chapter_backtracking/n_queens_problem.md | 80 +- .../permutations_problem.md | 160 +- .../subset_sum_problem.md | 240 +-- .../iteration_and_recursion.md | 420 ++--- .../space_complexity.md | 924 +++++------ .../time_complexity.md | 1392 ++++++++--------- .../basic_data_types.md | 112 +- .../binary_search_recur.md | 80 +- .../build_binary_tree_problem.md | 80 +- .../hanota_problem.md | 100 +- .../dp_problem_features.md | 180 +-- .../dp_solution_pipeline.md | 240 +-- .../edit_distance_problem.md | 120 +- .../intro_to_dynamic_programming.md | 360 ++--- .../knapsack_problem.md | 240 +-- .../unbounded_knapsack_problem.md | 360 ++--- docs/chapter_graph/graph_operations.md | 112 +- docs/chapter_graph/graph_traversal.md | 140 +- .../fractional_knapsack_problem.md | 76 +- docs/chapter_greedy/greedy_algorithm.md | 60 +- docs/chapter_greedy/max_capacity_problem.md | 60 +- .../max_product_cutting_problem.md | 60 +- docs/chapter_hashing/hash_algorithm.md | 250 +-- docs/chapter_hashing/hash_collision.md | 112 +- docs/chapter_hashing/hash_map.md | 410 ++--- docs/chapter_heap/build_heap.md | 60 +- docs/chapter_heap/heap.md | 556 +++---- docs/chapter_heap/top_k.md | 60 +- docs/chapter_preface/suggestions.md | 122 +- docs/chapter_searching/binary_search.md | 120 +- docs/chapter_searching/binary_search_edge.md | 120 +- .../binary_search_insertion.md | 120 +- .../replace_linear_by_hashing.md | 124 +- docs/chapter_sorting/bubble_sort.md | 120 +- docs/chapter_sorting/bucket_sort.md | 60 +- docs/chapter_sorting/counting_sort.md | 120 +- docs/chapter_sorting/heap_sort.md | 80 +- docs/chapter_sorting/insertion_sort.md | 60 +- docs/chapter_sorting/merge_sort.md | 80 +- docs/chapter_sorting/quick_sort.md | 272 ++-- docs/chapter_sorting/radix_sort.md | 100 +- docs/chapter_sorting/selection_sort.md | 60 +- docs/chapter_stack_and_queue/deque.md | 350 ++--- docs/chapter_stack_and_queue/queue.md | 320 ++-- docs/chapter_stack_and_queue/stack.md | 346 ++-- .../array_representation_of_tree.md | 136 +- docs/chapter_tree/avl_tree.md | 604 +++---- docs/chapter_tree/binary_search_tree.md | 168 +- docs/chapter_tree/binary_tree.md | 406 ++--- docs/chapter_tree/binary_tree_traversal.md | 160 +- 55 files changed, 6826 insertions(+), 6826 deletions(-) diff --git a/codes/swift/chapter_greedy/fractional_knapsack.swift b/codes/swift/chapter_greedy/fractional_knapsack.swift index 357ac689..e25b022d 100644 --- a/codes/swift/chapter_greedy/fractional_knapsack.swift +++ b/codes/swift/chapter_greedy/fractional_knapsack.swift @@ -4,8 +4,8 @@ * Author: nuomi1 (nuomi1@qq.com) */ -// 物品 -struct Item { +/* 物品 */ +class Item { var w: Int // 物品重量 var v: Int // 物品价值 diff --git a/docs/chapter_array_and_linkedlist/array.md b/docs/chapter_array_and_linkedlist/array.md index 5aff0885..56ff8dca 100755 --- a/docs/chapter_array_and_linkedlist/array.md +++ b/docs/chapter_array_and_linkedlist/array.md @@ -10,12 +10,12 @@ 我们可以根据需求选用数组的两种初始化方式:无初始值、给定初始值。在未指定初始值的情况下,大多数编程语言会将数组元素初始化为 $0$ 。 -=== "Java" +=== "Python" - ```java title="array.java" - /* 初始化数组 */ - int[] arr = new int[5]; // { 0, 0, 0, 0, 0 } - int[] nums = { 1, 3, 2, 5, 4 }; + ```python title="array.py" + # 初始化数组 + arr: list[int] = [0] * 5 # [ 0, 0, 0, 0, 0 ] + nums: list[int] = [1, 3, 2, 5, 4] ``` === "C++" @@ -30,12 +30,20 @@ int* nums1 = new int[5] { 1, 3, 2, 5, 4 }; ``` -=== "Python" +=== "Java" - ```python title="array.py" - # 初始化数组 - arr: list[int] = [0] * 5 # [ 0, 0, 0, 0, 0 ] - nums: list[int] = [1, 3, 2, 5, 4] + ```java title="array.java" + /* 初始化数组 */ + int[] arr = new int[5]; // { 0, 0, 0, 0, 0 } + int[] nums = { 1, 3, 2, 5, 4 }; + ``` + +=== "C#" + + ```csharp title="array.cs" + /* 初始化数组 */ + int[] arr = new int[5]; // { 0, 0, 0, 0, 0 } + int[] nums = { 1, 3, 2, 5, 4 }; ``` === "Go" @@ -49,6 +57,14 @@ nums := []int{1, 3, 2, 5, 4} ``` +=== "Swift" + + ```swift title="array.swift" + /* 初始化数组 */ + let arr = Array(repeating: 0, count: 5) // [0, 0, 0, 0, 0] + let nums = [1, 3, 2, 5, 4] + ``` + === "JS" ```javascript title="array.js" @@ -65,37 +81,6 @@ let nums: number[] = [1, 3, 2, 5, 4]; ``` -=== "C" - - ```c title="array.c" - int arr[5] = { 0 }; // { 0, 0, 0, 0, 0 } - int nums[5] = { 1, 3, 2, 5, 4 }; - ``` - -=== "C#" - - ```csharp title="array.cs" - /* 初始化数组 */ - int[] arr = new int[5]; // { 0, 0, 0, 0, 0 } - int[] nums = { 1, 3, 2, 5, 4 }; - ``` - -=== "Swift" - - ```swift title="array.swift" - /* 初始化数组 */ - let arr = Array(repeating: 0, count: 5) // [0, 0, 0, 0, 0] - let nums = [1, 3, 2, 5, 4] - ``` - -=== "Zig" - - ```zig title="array.zig" - // 初始化数组 - var arr = [_]i32{0} ** 5; // { 0, 0, 0, 0, 0 } - var nums = [_]i32{ 1, 3, 2, 5, 4 }; - ``` - === "Dart" ```dart title="array.dart" @@ -112,6 +97,21 @@ let nums: Vec = vec![1, 3, 2, 5, 4]; ``` +=== "C" + + ```c title="array.c" + int arr[5] = { 0 }; // { 0, 0, 0, 0, 0 } + int nums[5] = { 1, 3, 2, 5, 4 }; + ``` + +=== "Zig" + + ```zig title="array.zig" + // 初始化数组 + var arr = [_]i32{0} ** 5; // { 0, 0, 0, 0, 0 } + var nums = [_]i32{ 1, 3, 2, 5, 4 }; + ``` + ### 访问元素 数组元素被存储在连续的内存空间中,这意味着计算数组元素的内存地址非常容易。给定数组内存地址(即首元素内存地址)和某个元素的索引,我们可以使用下图所示的公式计算得到该元素的内存地址,从而直接访问此元素。 @@ -122,10 +122,10 @@ 在数组中访问元素是非常高效的,我们可以在 $O(1)$ 时间内随机访问数组中的任意一个元素。 -=== "Java" +=== "Python" - ```java title="array.java" - [class]{array}-[func]{randomAccess} + ```python title="array.py" + [class]{}-[func]{random_access} ``` === "C++" @@ -134,10 +134,16 @@ [class]{}-[func]{randomAccess} ``` -=== "Python" +=== "Java" - ```python title="array.py" - [class]{}-[func]{random_access} + ```java title="array.java" + [class]{array}-[func]{randomAccess} + ``` + +=== "C#" + + ```csharp title="array.cs" + [class]{array}-[func]{randomAccess} ``` === "Go" @@ -146,6 +152,12 @@ [class]{}-[func]{randomAccess} ``` +=== "Swift" + + ```swift title="array.swift" + [class]{}-[func]{randomAccess} + ``` + === "JS" ```javascript title="array.js" @@ -158,30 +170,6 @@ [class]{}-[func]{randomAccess} ``` -=== "C" - - ```c title="array.c" - [class]{}-[func]{randomAccess} - ``` - -=== "C#" - - ```csharp title="array.cs" - [class]{array}-[func]{randomAccess} - ``` - -=== "Swift" - - ```swift title="array.swift" - [class]{}-[func]{randomAccess} - ``` - -=== "Zig" - - ```zig title="array.zig" - [class]{}-[func]{randomAccess} - ``` - === "Dart" ```dart title="array.dart" @@ -194,6 +182,18 @@ [class]{}-[func]{random_access} ``` +=== "C" + + ```c title="array.c" + [class]{}-[func]{randomAccess} + ``` + +=== "Zig" + + ```zig title="array.zig" + [class]{}-[func]{randomAccess} + ``` + ### 插入元素 数组元素在内存中是“紧挨着的”,它们之间没有空间再存放任何数据。如下图所示,如果想要在数组中间插入一个元素,则需要将该元素之后的所有元素都向后移动一位,之后再把元素赋值给该索引。 @@ -202,10 +202,10 @@ 值得注意的是,由于数组的长度是固定的,因此插入一个元素必定会导致数组尾部元素的“丢失”。我们将这个问题的解决方案留在列表章节中讨论。 -=== "Java" +=== "Python" - ```java title="array.java" - [class]{array}-[func]{insert} + ```python title="array.py" + [class]{}-[func]{insert} ``` === "C++" @@ -214,10 +214,16 @@ [class]{}-[func]{insert} ``` -=== "Python" +=== "Java" - ```python title="array.py" - [class]{}-[func]{insert} + ```java title="array.java" + [class]{array}-[func]{insert} + ``` + +=== "C#" + + ```csharp title="array.cs" + [class]{array}-[func]{insert} ``` === "Go" @@ -226,6 +232,12 @@ [class]{}-[func]{insert} ``` +=== "Swift" + + ```swift title="array.swift" + [class]{}-[func]{insert} + ``` + === "JS" ```javascript title="array.js" @@ -238,30 +250,6 @@ [class]{}-[func]{insert} ``` -=== "C" - - ```c title="array.c" - [class]{}-[func]{insert} - ``` - -=== "C#" - - ```csharp title="array.cs" - [class]{array}-[func]{insert} - ``` - -=== "Swift" - - ```swift title="array.swift" - [class]{}-[func]{insert} - ``` - -=== "Zig" - - ```zig title="array.zig" - [class]{}-[func]{insert} - ``` - === "Dart" ```dart title="array.dart" @@ -274,6 +262,18 @@ [class]{}-[func]{insert} ``` +=== "C" + + ```c title="array.c" + [class]{}-[func]{insert} + ``` + +=== "Zig" + + ```zig title="array.zig" + [class]{}-[func]{insert} + ``` + ### 删除元素 同理,如下图所示,若想要删除索引 $i$ 处的元素,则需要把索引 $i$ 之后的元素都向前移动一位。 @@ -282,10 +282,10 @@ 请注意,删除元素完成后,原先末尾的元素变得“无意义”了,所以我们无须特意去修改它。 -=== "Java" +=== "Python" - ```java title="array.java" - [class]{array}-[func]{remove} + ```python title="array.py" + [class]{}-[func]{remove} ``` === "C++" @@ -294,10 +294,16 @@ [class]{}-[func]{remove} ``` -=== "Python" +=== "Java" - ```python title="array.py" - [class]{}-[func]{remove} + ```java title="array.java" + [class]{array}-[func]{remove} + ``` + +=== "C#" + + ```csharp title="array.cs" + [class]{array}-[func]{remove} ``` === "Go" @@ -306,6 +312,12 @@ [class]{}-[func]{remove} ``` +=== "Swift" + + ```swift title="array.swift" + [class]{}-[func]{remove} + ``` + === "JS" ```javascript title="array.js" @@ -318,30 +330,6 @@ [class]{}-[func]{remove} ``` -=== "C" - - ```c title="array.c" - [class]{}-[func]{removeItem} - ``` - -=== "C#" - - ```csharp title="array.cs" - [class]{array}-[func]{remove} - ``` - -=== "Swift" - - ```swift title="array.swift" - [class]{}-[func]{remove} - ``` - -=== "Zig" - - ```zig title="array.zig" - [class]{}-[func]{remove} - ``` - === "Dart" ```dart title="array.dart" @@ -354,6 +342,18 @@ [class]{}-[func]{remove} ``` +=== "C" + + ```c title="array.c" + [class]{}-[func]{removeItem} + ``` + +=== "Zig" + + ```zig title="array.zig" + [class]{}-[func]{remove} + ``` + 总的来看,数组的插入与删除操作有以下缺点。 - **时间复杂度高**:数组的插入和删除的平均时间复杂度均为 $O(n)$ ,其中 $n$ 为数组长度。 @@ -364,10 +364,10 @@ 在大多数编程语言中,我们既可以通过索引遍历数组,也可以直接遍历获取数组中的每个元素。 -=== "Java" +=== "Python" - ```java title="array.java" - [class]{array}-[func]{traverse} + ```python title="array.py" + [class]{}-[func]{traverse} ``` === "C++" @@ -376,10 +376,16 @@ [class]{}-[func]{traverse} ``` -=== "Python" +=== "Java" - ```python title="array.py" - [class]{}-[func]{traverse} + ```java title="array.java" + [class]{array}-[func]{traverse} + ``` + +=== "C#" + + ```csharp title="array.cs" + [class]{array}-[func]{traverse} ``` === "Go" @@ -388,6 +394,12 @@ [class]{}-[func]{traverse} ``` +=== "Swift" + + ```swift title="array.swift" + [class]{}-[func]{traverse} + ``` + === "JS" ```javascript title="array.js" @@ -400,30 +412,6 @@ [class]{}-[func]{traverse} ``` -=== "C" - - ```c title="array.c" - [class]{}-[func]{traverse} - ``` - -=== "C#" - - ```csharp title="array.cs" - [class]{array}-[func]{traverse} - ``` - -=== "Swift" - - ```swift title="array.swift" - [class]{}-[func]{traverse} - ``` - -=== "Zig" - - ```zig title="array.zig" - [class]{}-[func]{traverse} - ``` - === "Dart" ```dart title="array.dart" @@ -436,16 +424,28 @@ [class]{}-[func]{traverse} ``` +=== "C" + + ```c title="array.c" + [class]{}-[func]{traverse} + ``` + +=== "Zig" + + ```zig title="array.zig" + [class]{}-[func]{traverse} + ``` + ### 查找元素 在数组中查找指定元素需要遍历数组,每轮判断元素值是否匹配,若匹配则输出对应索引。 因为数组是线性数据结构,所以上述查找操作被称为“线性查找”。 -=== "Java" +=== "Python" - ```java title="array.java" - [class]{array}-[func]{find} + ```python title="array.py" + [class]{}-[func]{find} ``` === "C++" @@ -454,10 +454,16 @@ [class]{}-[func]{find} ``` -=== "Python" +=== "Java" - ```python title="array.py" - [class]{}-[func]{find} + ```java title="array.java" + [class]{array}-[func]{find} + ``` + +=== "C#" + + ```csharp title="array.cs" + [class]{array}-[func]{find} ``` === "Go" @@ -466,6 +472,12 @@ [class]{}-[func]{find} ``` +=== "Swift" + + ```swift title="array.swift" + [class]{}-[func]{find} + ``` + === "JS" ```javascript title="array.js" @@ -478,30 +490,6 @@ [class]{}-[func]{find} ``` -=== "C" - - ```c title="array.c" - [class]{}-[func]{find} - ``` - -=== "C#" - - ```csharp title="array.cs" - [class]{array}-[func]{find} - ``` - -=== "Swift" - - ```swift title="array.swift" - [class]{}-[func]{find} - ``` - -=== "Zig" - - ```zig title="array.zig" - [class]{}-[func]{find} - ``` - === "Dart" ```dart title="array.dart" @@ -514,16 +502,28 @@ [class]{}-[func]{find} ``` +=== "C" + + ```c title="array.c" + [class]{}-[func]{find} + ``` + +=== "Zig" + + ```zig title="array.zig" + [class]{}-[func]{find} + ``` + ### 扩容数组 在复杂的系统环境中,程序难以保证数组之后的内存空间是可用的,从而无法安全地扩展数组容量。因此在大多数编程语言中,**数组的长度是不可变的**。 如果我们希望扩容数组,则需重新建立一个更大的数组,然后把原数组元素依次拷贝到新数组。这是一个 $O(n)$ 的操作,在数组很大的情况下是非常耗时的。 -=== "Java" +=== "Python" - ```java title="array.java" - [class]{array}-[func]{extend} + ```python title="array.py" + [class]{}-[func]{extend} ``` === "C++" @@ -532,10 +532,16 @@ [class]{}-[func]{extend} ``` -=== "Python" +=== "Java" - ```python title="array.py" - [class]{}-[func]{extend} + ```java title="array.java" + [class]{array}-[func]{extend} + ``` + +=== "C#" + + ```csharp title="array.cs" + [class]{array}-[func]{extend} ``` === "Go" @@ -544,6 +550,12 @@ [class]{}-[func]{extend} ``` +=== "Swift" + + ```swift title="array.swift" + [class]{}-[func]{extend} + ``` + === "JS" ```javascript title="array.js" @@ -556,30 +568,6 @@ [class]{}-[func]{extend} ``` -=== "C" - - ```c title="array.c" - [class]{}-[func]{extend} - ``` - -=== "C#" - - ```csharp title="array.cs" - [class]{array}-[func]{extend} - ``` - -=== "Swift" - - ```swift title="array.swift" - [class]{}-[func]{extend} - ``` - -=== "Zig" - - ```zig title="array.zig" - [class]{}-[func]{extend} - ``` - === "Dart" ```dart title="array.dart" @@ -592,6 +580,18 @@ [class]{}-[func]{extend} ``` +=== "C" + + ```c title="array.c" + [class]{}-[func]{extend} + ``` + +=== "Zig" + + ```zig title="array.zig" + [class]{}-[func]{extend} + ``` + ## 数组优点与局限性 数组存储在连续的内存空间内,且元素类型相同。这种做法包含丰富的先验信息,系统可以利用这些信息来优化数据结构的操作效率。 diff --git a/docs/chapter_array_and_linkedlist/linked_list.md b/docs/chapter_array_and_linkedlist/linked_list.md index 645d9854..ad0f27c3 100755 --- a/docs/chapter_array_and_linkedlist/linked_list.md +++ b/docs/chapter_array_and_linkedlist/linked_list.md @@ -16,15 +16,14 @@ 如以下代码所示,链表节点 `ListNode` 除了包含值,还需额外保存一个引用(指针)。因此在相同数据量下,**链表比数组占用更多的内存空间**。 -=== "Java" +=== "Python" - ```java title="" - /* 链表节点类 */ - class ListNode { - int val; // 节点值 - ListNode next; // 指向下一节点的引用 - ListNode(int x) { val = x; } // 构造函数 - } + ```python title="" + class ListNode: + """链表节点类""" + def __init__(self, val: int): + self.val: int = val # 节点值 + self.next: Optional[ListNode] = None # 指向下一节点的引用 ``` === "C++" @@ -38,14 +37,26 @@ }; ``` -=== "Python" +=== "Java" - ```python title="" - class ListNode: - """链表节点类""" - def __init__(self, val: int): - self.val: int = val # 节点值 - self.next: Optional[ListNode] = None # 指向下一节点的引用 + ```java title="" + /* 链表节点类 */ + class ListNode { + int val; // 节点值 + ListNode next; // 指向下一节点的引用 + ListNode(int x) { val = x; } // 构造函数 + } + ``` + +=== "C#" + + ```csharp title="" + /* 链表节点类 */ + class ListNode { + int val; // 节点值 + ListNode next; // 指向下一节点的引用 + ListNode(int x) => val = x; //构造函数 + } ``` === "Go" @@ -66,6 +77,20 @@ } ``` +=== "Swift" + + ```swift title="" + /* 链表节点类 */ + class ListNode { + var val: Int // 节点值 + var next: ListNode? // 指向下一节点的引用 + + init(x: Int) { // 构造函数 + val = x + } + } + ``` + === "JS" ```javascript title="" @@ -94,72 +119,6 @@ } ``` -=== "C" - - ```c title="" - /* 链表节点结构体 */ - struct ListNode { - int val; // 节点值 - struct ListNode *next; // 指向下一节点的指针 - }; - - typedef struct ListNode ListNode; - - /* 构造函数 */ - ListNode *newListNode(int val) { - ListNode *node, *next; - node = (ListNode *) malloc(sizeof(ListNode)); - node->val = val; - node->next = NULL; - return node; - } - ``` - -=== "C#" - - ```csharp title="" - /* 链表节点类 */ - class ListNode { - int val; // 节点值 - ListNode next; // 指向下一节点的引用 - ListNode(int x) => val = x; //构造函数 - } - ``` - -=== "Swift" - - ```swift title="" - /* 链表节点类 */ - class ListNode { - var val: Int // 节点值 - var next: ListNode? // 指向下一节点的引用 - - init(x: Int) { // 构造函数 - val = x - } - } - ``` - -=== "Zig" - - ```zig title="" - // 链表节点类 - pub fn ListNode(comptime T: type) type { - return struct { - const Self = @This(); - - val: T = 0, // 节点值 - next: ?*Self = null, // 指向下一节点的指针 - - // 构造函数 - pub fn init(self: *Self, x: i32) void { - self.val = x; - self.next = null; - } - }; - } - ``` - === "Dart" ```dart title="" @@ -184,27 +143,68 @@ } ``` +=== "C" + + ```c title="" + /* 链表节点结构体 */ + struct ListNode { + int val; // 节点值 + struct ListNode *next; // 指向下一节点的指针 + }; + + typedef struct ListNode ListNode; + + /* 构造函数 */ + ListNode *newListNode(int val) { + ListNode *node, *next; + node = (ListNode *) malloc(sizeof(ListNode)); + node->val = val; + node->next = NULL; + return node; + } + ``` + +=== "Zig" + + ```zig title="" + // 链表节点类 + pub fn ListNode(comptime T: type) type { + return struct { + const Self = @This(); + + val: T = 0, // 节点值 + next: ?*Self = null, // 指向下一节点的指针 + + // 构造函数 + pub fn init(self: *Self, x: i32) void { + self.val = x; + self.next = null; + } + }; + } + ``` + ## 链表常用操作 ### 初始化链表 建立链表分为两步,第一步是初始化各个节点对象,第二步是构建引用指向关系。初始化完成后,我们就可以从链表的头节点出发,通过引用指向 `next` 依次访问所有节点。 -=== "Java" +=== "Python" - ```java title="linked_list.java" - /* 初始化链表 1 -> 3 -> 2 -> 5 -> 4 */ - // 初始化各个节点 - ListNode n0 = new ListNode(1); - ListNode n1 = new ListNode(3); - ListNode n2 = new ListNode(2); - ListNode n3 = new ListNode(5); - ListNode n4 = new ListNode(4); - // 构建引用指向 - n0.next = n1; - n1.next = n2; - n2.next = n3; - n3.next = n4; + ```python title="linked_list.py" + # 初始化链表 1 -> 3 -> 2 -> 5 -> 4 + # 初始化各个节点 + n0 = ListNode(1) + n1 = ListNode(3) + n2 = ListNode(2) + n3 = ListNode(5) + n4 = ListNode(4) + # 构建引用指向 + n0.next = n1 + n1.next = n2 + n2.next = n3 + n3.next = n4 ``` === "C++" @@ -224,21 +224,38 @@ n3->next = n4; ``` -=== "Python" +=== "Java" - ```python title="linked_list.py" - # 初始化链表 1 -> 3 -> 2 -> 5 -> 4 - # 初始化各个节点 - n0 = ListNode(1) - n1 = ListNode(3) - n2 = ListNode(2) - n3 = ListNode(5) - n4 = ListNode(4) - # 构建引用指向 - n0.next = n1 - n1.next = n2 - n2.next = n3 - n3.next = n4 + ```java title="linked_list.java" + /* 初始化链表 1 -> 3 -> 2 -> 5 -> 4 */ + // 初始化各个节点 + ListNode n0 = new ListNode(1); + ListNode n1 = new ListNode(3); + ListNode n2 = new ListNode(2); + ListNode n3 = new ListNode(5); + ListNode n4 = new ListNode(4); + // 构建引用指向 + n0.next = n1; + n1.next = n2; + n2.next = n3; + n3.next = n4; + ``` + +=== "C#" + + ```csharp title="linked_list.cs" + /* 初始化链表 1 -> 3 -> 2 -> 5 -> 4 */ + // 初始化各个节点 + ListNode n0 = new ListNode(1); + ListNode n1 = new ListNode(3); + ListNode n2 = new ListNode(2); + ListNode n3 = new ListNode(5); + ListNode n4 = new ListNode(4); + // 构建引用指向 + n0.next = n1; + n1.next = n2; + n2.next = n3; + n3.next = n4; ``` === "Go" @@ -258,6 +275,23 @@ n3.Next = n4 ``` +=== "Swift" + + ```swift title="linked_list.swift" + /* 初始化链表 1 -> 3 -> 2 -> 5 -> 4 */ + // 初始化各个节点 + let n0 = ListNode(x: 1) + let n1 = ListNode(x: 3) + let n2 = ListNode(x: 2) + let n3 = ListNode(x: 5) + let n4 = ListNode(x: 4) + // 构建引用指向 + n0.next = n1 + n1.next = n2 + n2.next = n3 + n3.next = n4 + ``` + === "JS" ```javascript title="linked_list.js" @@ -292,74 +326,6 @@ n3.next = n4; ``` -=== "C" - - ```c title="linked_list.c" - /* 初始化链表 1 -> 3 -> 2 -> 5 -> 4 */ - // 初始化各个节点 - ListNode* n0 = newListNode(1); - ListNode* n1 = newListNode(3); - ListNode* n2 = newListNode(2); - ListNode* n3 = newListNode(5); - ListNode* n4 = newListNode(4); - // 构建引用指向 - n0->next = n1; - n1->next = n2; - n2->next = n3; - n3->next = n4; - ``` - -=== "C#" - - ```csharp title="linked_list.cs" - /* 初始化链表 1 -> 3 -> 2 -> 5 -> 4 */ - // 初始化各个节点 - ListNode n0 = new ListNode(1); - ListNode n1 = new ListNode(3); - ListNode n2 = new ListNode(2); - ListNode n3 = new ListNode(5); - ListNode n4 = new ListNode(4); - // 构建引用指向 - n0.next = n1; - n1.next = n2; - n2.next = n3; - n3.next = n4; - ``` - -=== "Swift" - - ```swift title="linked_list.swift" - /* 初始化链表 1 -> 3 -> 2 -> 5 -> 4 */ - // 初始化各个节点 - let n0 = ListNode(x: 1) - let n1 = ListNode(x: 3) - let n2 = ListNode(x: 2) - let n3 = ListNode(x: 5) - let n4 = ListNode(x: 4) - // 构建引用指向 - n0.next = n1 - n1.next = n2 - n2.next = n3 - n3.next = n4 - ``` - -=== "Zig" - - ```zig title="linked_list.zig" - // 初始化链表 - // 初始化各个节点 - var n0 = inc.ListNode(i32){.val = 1}; - var n1 = inc.ListNode(i32){.val = 3}; - var n2 = inc.ListNode(i32){.val = 2}; - var n3 = inc.ListNode(i32){.val = 5}; - var n4 = inc.ListNode(i32){.val = 4}; - // 构建引用指向 - n0.next = &n1; - n1.next = &n2; - n2.next = &n3; - n3.next = &n4; - ``` - === "Dart" ```dart title="linked_list.dart" @@ -395,6 +361,40 @@ n3.borrow_mut().next = Some(n4.clone()); ``` +=== "C" + + ```c title="linked_list.c" + /* 初始化链表 1 -> 3 -> 2 -> 5 -> 4 */ + // 初始化各个节点 + ListNode* n0 = newListNode(1); + ListNode* n1 = newListNode(3); + ListNode* n2 = newListNode(2); + ListNode* n3 = newListNode(5); + ListNode* n4 = newListNode(4); + // 构建引用指向 + n0->next = n1; + n1->next = n2; + n2->next = n3; + n3->next = n4; + ``` + +=== "Zig" + + ```zig title="linked_list.zig" + // 初始化链表 + // 初始化各个节点 + var n0 = inc.ListNode(i32){.val = 1}; + var n1 = inc.ListNode(i32){.val = 3}; + var n2 = inc.ListNode(i32){.val = 2}; + var n3 = inc.ListNode(i32){.val = 5}; + var n4 = inc.ListNode(i32){.val = 4}; + // 构建引用指向 + n0.next = &n1; + n1.next = &n2; + n2.next = &n3; + n3.next = &n4; + ``` + 数组整体是一个变量,比如数组 `nums` 包含元素 `nums[0]` 和 `nums[1]` 等,而链表是由多个独立的节点对象组成的。**我们通常将头节点当作链表的代称**,比如以上代码中的链表可被记做链表 `n0` 。 ### 插入节点 @@ -405,10 +405,10 @@ ![链表插入节点示例](linked_list.assets/linkedlist_insert_node.png) -=== "Java" +=== "Python" - ```java title="linked_list.java" - [class]{linked_list}-[func]{insert} + ```python title="linked_list.py" + [class]{}-[func]{insert} ``` === "C++" @@ -417,10 +417,16 @@ [class]{}-[func]{insert} ``` -=== "Python" +=== "Java" - ```python title="linked_list.py" - [class]{}-[func]{insert} + ```java title="linked_list.java" + [class]{linked_list}-[func]{insert} + ``` + +=== "C#" + + ```csharp title="linked_list.cs" + [class]{linked_list}-[func]{insert} ``` === "Go" @@ -429,6 +435,12 @@ [class]{}-[func]{insertNode} ``` +=== "Swift" + + ```swift title="linked_list.swift" + [class]{}-[func]{insert} + ``` + === "JS" ```javascript title="linked_list.js" @@ -441,30 +453,6 @@ [class]{}-[func]{insert} ``` -=== "C" - - ```c title="linked_list.c" - [class]{}-[func]{insert} - ``` - -=== "C#" - - ```csharp title="linked_list.cs" - [class]{linked_list}-[func]{insert} - ``` - -=== "Swift" - - ```swift title="linked_list.swift" - [class]{}-[func]{insert} - ``` - -=== "Zig" - - ```zig title="linked_list.zig" - [class]{}-[func]{insert} - ``` - === "Dart" ```dart title="linked_list.dart" @@ -477,6 +465,18 @@ [class]{}-[func]{insert} ``` +=== "C" + + ```c title="linked_list.c" + [class]{}-[func]{insert} + ``` + +=== "Zig" + + ```zig title="linked_list.zig" + [class]{}-[func]{insert} + ``` + ### 删除节点 如下图所示,在链表中删除节点也非常方便,**只需改变一个节点的引用(指针)即可**。 @@ -485,10 +485,10 @@ ![链表删除节点](linked_list.assets/linkedlist_remove_node.png) -=== "Java" +=== "Python" - ```java title="linked_list.java" - [class]{linked_list}-[func]{remove} + ```python title="linked_list.py" + [class]{}-[func]{remove} ``` === "C++" @@ -497,10 +497,16 @@ [class]{}-[func]{remove} ``` -=== "Python" +=== "Java" - ```python title="linked_list.py" - [class]{}-[func]{remove} + ```java title="linked_list.java" + [class]{linked_list}-[func]{remove} + ``` + +=== "C#" + + ```csharp title="linked_list.cs" + [class]{linked_list}-[func]{remove} ``` === "Go" @@ -509,6 +515,12 @@ [class]{}-[func]{removeNode} ``` +=== "Swift" + + ```swift title="linked_list.swift" + [class]{}-[func]{remove} + ``` + === "JS" ```javascript title="linked_list.js" @@ -521,30 +533,6 @@ [class]{}-[func]{remove} ``` -=== "C" - - ```c title="linked_list.c" - [class]{}-[func]{removeNode} - ``` - -=== "C#" - - ```csharp title="linked_list.cs" - [class]{linked_list}-[func]{remove} - ``` - -=== "Swift" - - ```swift title="linked_list.swift" - [class]{}-[func]{remove} - ``` - -=== "Zig" - - ```zig title="linked_list.zig" - [class]{}-[func]{remove} - ``` - === "Dart" ```dart title="linked_list.dart" @@ -557,14 +545,26 @@ [class]{}-[func]{remove} ``` +=== "C" + + ```c title="linked_list.c" + [class]{}-[func]{removeNode} + ``` + +=== "Zig" + + ```zig title="linked_list.zig" + [class]{}-[func]{remove} + ``` + ### 访问节点 **在链表访问节点的效率较低**。如上节所述,我们可以在 $O(1)$ 时间下访问数组中的任意元素。链表则不然,程序需要从头节点出发,逐个向后遍历,直至找到目标节点。也就是说,访问链表的第 $i$ 个节点需要循环 $i - 1$ 轮,时间复杂度为 $O(n)$ 。 -=== "Java" +=== "Python" - ```java title="linked_list.java" - [class]{linked_list}-[func]{access} + ```python title="linked_list.py" + [class]{}-[func]{access} ``` === "C++" @@ -573,10 +573,16 @@ [class]{}-[func]{access} ``` -=== "Python" +=== "Java" - ```python title="linked_list.py" - [class]{}-[func]{access} + ```java title="linked_list.java" + [class]{linked_list}-[func]{access} + ``` + +=== "C#" + + ```csharp title="linked_list.cs" + [class]{linked_list}-[func]{access} ``` === "Go" @@ -585,6 +591,12 @@ [class]{}-[func]{access} ``` +=== "Swift" + + ```swift title="linked_list.swift" + [class]{}-[func]{access} + ``` + === "JS" ```javascript title="linked_list.js" @@ -597,30 +609,6 @@ [class]{}-[func]{access} ``` -=== "C" - - ```c title="linked_list.c" - [class]{}-[func]{access} - ``` - -=== "C#" - - ```csharp title="linked_list.cs" - [class]{linked_list}-[func]{access} - ``` - -=== "Swift" - - ```swift title="linked_list.swift" - [class]{}-[func]{access} - ``` - -=== "Zig" - - ```zig title="linked_list.zig" - [class]{}-[func]{access} - ``` - === "Dart" ```dart title="linked_list.dart" @@ -633,14 +621,26 @@ [class]{}-[func]{access} ``` +=== "C" + + ```c title="linked_list.c" + [class]{}-[func]{access} + ``` + +=== "Zig" + + ```zig title="linked_list.zig" + [class]{}-[func]{access} + ``` + ### 查找节点 遍历链表,查找链表内值为 `target` 的节点,输出节点在链表中的索引。此过程也属于线性查找。 -=== "Java" +=== "Python" - ```java title="linked_list.java" - [class]{linked_list}-[func]{find} + ```python title="linked_list.py" + [class]{}-[func]{find} ``` === "C++" @@ -649,10 +649,16 @@ [class]{}-[func]{find} ``` -=== "Python" +=== "Java" - ```python title="linked_list.py" - [class]{}-[func]{find} + ```java title="linked_list.java" + [class]{linked_list}-[func]{find} + ``` + +=== "C#" + + ```csharp title="linked_list.cs" + [class]{linked_list}-[func]{find} ``` === "Go" @@ -661,6 +667,12 @@ [class]{}-[func]{findNode} ``` +=== "Swift" + + ```swift title="linked_list.swift" + [class]{}-[func]{find} + ``` + === "JS" ```javascript title="linked_list.js" @@ -673,30 +685,6 @@ [class]{}-[func]{find} ``` -=== "C" - - ```c title="linked_list.c" - [class]{}-[func]{find} - ``` - -=== "C#" - - ```csharp title="linked_list.cs" - [class]{linked_list}-[func]{find} - ``` - -=== "Swift" - - ```swift title="linked_list.swift" - [class]{}-[func]{find} - ``` - -=== "Zig" - - ```zig title="linked_list.zig" - [class]{}-[func]{find} - ``` - === "Dart" ```dart title="linked_list.dart" @@ -709,6 +697,18 @@ [class]{}-[func]{find} ``` +=== "C" + + ```c title="linked_list.c" + [class]{}-[func]{find} + ``` + +=== "Zig" + + ```zig title="linked_list.zig" + [class]{}-[func]{find} + ``` + ## 数组 VS 链表 下表总结对比了数组和链表的各项特点与操作效率。由于它们采用两种相反的存储策略,因此各种性质和操作效率也呈现对立的特点。 @@ -733,16 +733,15 @@ - **环形链表**:如果我们令单向链表的尾节点指向头节点(即首尾相接),则得到一个环形链表。在环形链表中,任意节点都可以视作头节点。 - **双向链表**:与单向链表相比,双向链表记录了两个方向的引用。双向链表的节点定义同时包含指向后继节点(下一个节点)和前驱节点(上一个节点)的引用(指针)。相较于单向链表,双向链表更具灵活性,可以朝两个方向遍历链表,但相应地也需要占用更多的内存空间。 -=== "Java" +=== "Python" - ```java title="" - /* 双向链表节点类 */ - class ListNode { - int val; // 节点值 - ListNode next; // 指向后继节点的引用 - ListNode prev; // 指向前驱节点的引用 - ListNode(int x) { val = x; } // 构造函数 - } + ```python title="" + class ListNode: + """双向链表节点类""" + def __init__(self, val: int): + self.val: int = val # 节点值 + self.next: Optional[ListNode] = None # 指向后继节点的引用 + self.prev: Optional[ListNode] = None # 指向前驱节点的引用 ``` === "C++" @@ -757,15 +756,28 @@ }; ``` -=== "Python" +=== "Java" - ```python title="" - class ListNode: - """双向链表节点类""" - def __init__(self, val: int): - self.val: int = val # 节点值 - self.next: Optional[ListNode] = None # 指向后继节点的引用 - self.prev: Optional[ListNode] = None # 指向前驱节点的引用 + ```java title="" + /* 双向链表节点类 */ + class ListNode { + int val; // 节点值 + ListNode next; // 指向后继节点的引用 + ListNode prev; // 指向前驱节点的引用 + ListNode(int x) { val = x; } // 构造函数 + } + ``` + +=== "C#" + + ```csharp title="" + /* 双向链表节点类 */ + class ListNode { + int val; // 节点值 + ListNode next; // 指向后继节点的引用 + ListNode prev; // 指向前驱节点的引用 + ListNode(int x) => val = x; // 构造函数 + } ``` === "Go" @@ -788,6 +800,21 @@ } ``` +=== "Swift" + + ```swift title="" + /* 双向链表节点类 */ + class ListNode { + var val: Int // 节点值 + var next: ListNode? // 指向后继节点的引用 + var prev: ListNode? // 指向前驱节点的引用 + + init(x: Int) { // 构造函数 + val = x + } + } + ``` + === "JS" ```javascript title="" @@ -820,78 +847,6 @@ } ``` -=== "C" - - ```c title="" - /* 双向链表节点结构体 */ - struct ListNode { - int val; // 节点值 - struct ListNode *next; // 指向后继节点的指针 - struct ListNode *prev; // 指向前驱节点的指针 - }; - - typedef struct ListNode ListNode; - - /* 构造函数 */ - ListNode *newListNode(int val) { - ListNode *node, *next; - node = (ListNode *) malloc(sizeof(ListNode)); - node->val = val; - node->next = NULL; - node->prev = NULL; - return node; - } - ``` - -=== "C#" - - ```csharp title="" - /* 双向链表节点类 */ - class ListNode { - int val; // 节点值 - ListNode next; // 指向后继节点的引用 - ListNode prev; // 指向前驱节点的引用 - ListNode(int x) => val = x; // 构造函数 - } - ``` - -=== "Swift" - - ```swift title="" - /* 双向链表节点类 */ - class ListNode { - var val: Int // 节点值 - var next: ListNode? // 指向后继节点的引用 - var prev: ListNode? // 指向前驱节点的引用 - - init(x: Int) { // 构造函数 - val = x - } - } - ``` - -=== "Zig" - - ```zig title="" - // 双向链表节点类 - pub fn ListNode(comptime T: type) type { - return struct { - const Self = @This(); - - val: T = 0, // 节点值 - next: ?*Self = null, // 指向后继节点的指针 - prev: ?*Self = null, // 指向前驱节点的指针 - - // 构造函数 - pub fn init(self: *Self, x: i32) void { - self.val = x; - self.next = null; - self.prev = null; - } - }; - } - ``` - === "Dart" ```dart title="" @@ -930,6 +885,51 @@ } ``` +=== "C" + + ```c title="" + /* 双向链表节点结构体 */ + struct ListNode { + int val; // 节点值 + struct ListNode *next; // 指向后继节点的指针 + struct ListNode *prev; // 指向前驱节点的指针 + }; + + typedef struct ListNode ListNode; + + /* 构造函数 */ + ListNode *newListNode(int val) { + ListNode *node, *next; + node = (ListNode *) malloc(sizeof(ListNode)); + node->val = val; + node->next = NULL; + node->prev = NULL; + return node; + } + ``` + +=== "Zig" + + ```zig title="" + // 双向链表节点类 + pub fn ListNode(comptime T: type) type { + return struct { + const Self = @This(); + + val: T = 0, // 节点值 + next: ?*Self = null, // 指向后继节点的指针 + prev: ?*Self = null, // 指向前驱节点的指针 + + // 构造函数 + pub fn init(self: *Self, x: i32) void { + self.val = x; + self.next = null; + self.prev = null; + } + }; + } + ``` + ![常见链表种类](linked_list.assets/linkedlist_common_types.png) ## 链表典型应用 diff --git a/docs/chapter_array_and_linkedlist/list.md b/docs/chapter_array_and_linkedlist/list.md index 196d0186..965cfabc 100755 --- a/docs/chapter_array_and_linkedlist/list.md +++ b/docs/chapter_array_and_linkedlist/list.md @@ -10,15 +10,14 @@ 我们通常使用“无初始值”和“有初始值”这两种初始化方法。 -=== "Java" +=== "Python" - ```java title="list.java" - /* 初始化列表 */ - // 无初始值 - List list1 = new ArrayList<>(); - // 有初始值(注意数组的元素类型需为 int[] 的包装类 Integer[]) - Integer[] numbers = new Integer[] { 1, 3, 2, 5, 4 }; - List list = new ArrayList<>(Arrays.asList(numbers)); + ```python title="list.py" + # 初始化列表 + # 无初始值 + list1: list[int] = [] + # 有初始值 + list: list[int] = [1, 3, 2, 5, 4] ``` === "C++" @@ -32,14 +31,26 @@ vector list = { 1, 3, 2, 5, 4 }; ``` -=== "Python" +=== "Java" - ```python title="list.py" - # 初始化列表 - # 无初始值 - list1: list[int] = [] - # 有初始值 - list: list[int] = [1, 3, 2, 5, 4] + ```java title="list.java" + /* 初始化列表 */ + // 无初始值 + List list1 = new ArrayList<>(); + // 有初始值(注意数组的元素类型需为 int[] 的包装类 Integer[]) + Integer[] numbers = new Integer[] { 1, 3, 2, 5, 4 }; + List list = new ArrayList<>(Arrays.asList(numbers)); + ``` + +=== "C#" + + ```csharp title="list.cs" + /* 初始化列表 */ + // 无初始值 + List list1 = new (); + // 有初始值 + int[] numbers = new int[] { 1, 3, 2, 5, 4 }; + List list = numbers.ToList(); ``` === "Go" @@ -52,6 +63,16 @@ list := []int{1, 3, 2, 5, 4} ``` +=== "Swift" + + ```swift title="list.swift" + /* 初始化列表 */ + // 无初始值 + let list1: [Int] = [] + // 有初始值 + var list = [1, 3, 2, 5, 4] + ``` + === "JS" ```javascript title="list.js" @@ -72,42 +93,6 @@ const list: number[] = [1, 3, 2, 5, 4]; ``` -=== "C" - - ```c title="list.c" - // C 未提供内置动态数组 - ``` - -=== "C#" - - ```csharp title="list.cs" - /* 初始化列表 */ - // 无初始值 - List list1 = new (); - // 有初始值 - int[] numbers = new int[] { 1, 3, 2, 5, 4 }; - List list = numbers.ToList(); - ``` - -=== "Swift" - - ```swift title="list.swift" - /* 初始化列表 */ - // 无初始值 - let list1: [Int] = [] - // 有初始值 - var list = [1, 3, 2, 5, 4] - ``` - -=== "Zig" - - ```zig title="list.zig" - // 初始化列表 - var list = std.ArrayList(i32).init(std.heap.page_allocator); - defer list.deinit(); - try list.appendSlice(&[_]i32{ 1, 3, 2, 5, 4 }); - ``` - === "Dart" ```dart title="list.dart" @@ -128,18 +113,33 @@ let list2: Vec = vec![1, 3, 2, 5, 4]; ``` +=== "C" + + ```c title="list.c" + // C 未提供内置动态数组 + ``` + +=== "Zig" + + ```zig title="list.zig" + // 初始化列表 + var list = std.ArrayList(i32).init(std.heap.page_allocator); + defer list.deinit(); + try list.appendSlice(&[_]i32{ 1, 3, 2, 5, 4 }); + ``` + ### 访问元素 列表本质上是数组,因此可以在 $O(1)$ 时间内访问和更新元素,效率很高。 -=== "Java" +=== "Python" - ```java title="list.java" - /* 访问元素 */ - int num = list.get(1); // 访问索引 1 处的元素 + ```python title="list.py" + # 访问元素 + num: int = list[1] # 访问索引 1 处的元素 - /* 更新元素 */ - list.set(1, 0); // 将索引 1 处的元素更新为 0 + # 更新元素 + list[1] = 0 # 将索引 1 处的元素更新为 0 ``` === "C++" @@ -152,14 +152,24 @@ list[1] = 0; // 将索引 1 处的元素更新为 0 ``` -=== "Python" +=== "Java" - ```python title="list.py" - # 访问元素 - num: int = list[1] # 访问索引 1 处的元素 + ```java title="list.java" + /* 访问元素 */ + int num = list.get(1); // 访问索引 1 处的元素 - # 更新元素 - list[1] = 0 # 将索引 1 处的元素更新为 0 + /* 更新元素 */ + list.set(1, 0); // 将索引 1 处的元素更新为 0 + ``` + +=== "C#" + + ```csharp title="list.cs" + /* 访问元素 */ + int num = list[1]; // 访问索引 1 处的元素 + + /* 更新元素 */ + list[1] = 0; // 将索引 1 处的元素更新为 0 ``` === "Go" @@ -172,6 +182,16 @@ list[1] = 0 // 将索引 1 处的元素更新为 0 ``` +=== "Swift" + + ```swift title="list.swift" + /* 访问元素 */ + let num = list[1] // 访问索引 1 处的元素 + + /* 更新元素 */ + list[1] = 0 // 将索引 1 处的元素更新为 0 + ``` + === "JS" ```javascript title="list.js" @@ -192,42 +212,6 @@ list[1] = 0; // 将索引 1 处的元素更新为 0 ``` -=== "C" - - ```c title="list.c" - // C 未提供内置动态数组 - ``` - -=== "C#" - - ```csharp title="list.cs" - /* 访问元素 */ - int num = list[1]; // 访问索引 1 处的元素 - - /* 更新元素 */ - list[1] = 0; // 将索引 1 处的元素更新为 0 - ``` - -=== "Swift" - - ```swift title="list.swift" - /* 访问元素 */ - let num = list[1] // 访问索引 1 处的元素 - - /* 更新元素 */ - list[1] = 0 // 将索引 1 处的元素更新为 0 - ``` - -=== "Zig" - - ```zig title="list.zig" - // 访问元素 - var num = list.items[1]; // 访问索引 1 处的元素 - - // 更新元素 - list.items[1] = 0; // 将索引 1 处的元素更新为 0 - ``` - === "Dart" ```dart title="list.dart" @@ -247,28 +231,44 @@ list[1] = 0; // 将索引 1 处的元素更新为 0 ``` +=== "C" + + ```c title="list.c" + // C 未提供内置动态数组 + ``` + +=== "Zig" + + ```zig title="list.zig" + // 访问元素 + var num = list.items[1]; // 访问索引 1 处的元素 + + // 更新元素 + list.items[1] = 0; // 将索引 1 处的元素更新为 0 + ``` + ### 插入与删除元素 相较于数组,列表可以自由地添加与删除元素。在列表尾部添加元素的时间复杂度为 $O(1)$ ,但插入和删除元素的效率仍与数组相同,时间复杂度为 $O(n)$ 。 -=== "Java" +=== "Python" - ```java title="list.java" - /* 清空列表 */ - list.clear(); + ```python title="list.py" + # 清空列表 + list.clear() - /* 尾部添加元素 */ - list.add(1); - list.add(3); - list.add(2); - list.add(5); - list.add(4); + # 尾部添加元素 + list.append(1) + list.append(3) + list.append(2) + list.append(5) + list.append(4) - /* 中间插入元素 */ - list.add(3, 6); // 在索引 3 处插入数字 6 + # 中间插入元素 + list.insert(3, 6) # 在索引 3 处插入数字 6 - /* 删除元素 */ - list.remove(3); // 删除索引 3 处的元素 + # 删除元素 + list.pop(3) # 删除索引 3 处的元素 ``` === "C++" @@ -291,24 +291,44 @@ list.erase(list.begin() + 3); // 删除索引 3 处的元素 ``` -=== "Python" +=== "Java" - ```python title="list.py" - # 清空列表 - list.clear() + ```java title="list.java" + /* 清空列表 */ + list.clear(); - # 尾部添加元素 - list.append(1) - list.append(3) - list.append(2) - list.append(5) - list.append(4) + /* 尾部添加元素 */ + list.add(1); + list.add(3); + list.add(2); + list.add(5); + list.add(4); - # 中间插入元素 - list.insert(3, 6) # 在索引 3 处插入数字 6 + /* 中间插入元素 */ + list.add(3, 6); // 在索引 3 处插入数字 6 - # 删除元素 - list.pop(3) # 删除索引 3 处的元素 + /* 删除元素 */ + list.remove(3); // 删除索引 3 处的元素 + ``` + +=== "C#" + + ```csharp title="list.cs" + /* 清空列表 */ + list.Clear(); + + /* 尾部添加元素 */ + list.Add(1); + list.Add(3); + list.Add(2); + list.Add(5); + list.Add(4); + + /* 中间插入元素 */ + list.Insert(3, 6); + + /* 删除元素 */ + list.RemoveAt(3); ``` === "Go" @@ -331,6 +351,26 @@ list = append(list[:3], list[4:]...) // 删除索引 3 处的元素 ``` +=== "Swift" + + ```swift title="list.swift" + /* 清空列表 */ + list.removeAll() + + /* 尾部添加元素 */ + list.append(1) + list.append(3) + list.append(2) + list.append(5) + list.append(4) + + /* 中间插入元素 */ + list.insert(6, at: 3) // 在索引 3 处插入数字 6 + + /* 删除元素 */ + list.remove(at: 3) // 删除索引 3 处的元素 + ``` + === "JS" ```javascript title="list.js" @@ -371,72 +411,6 @@ list.splice(3, 1); ``` -=== "C" - - ```c title="list.c" - // C 未提供内置动态数组 - ``` - -=== "C#" - - ```csharp title="list.cs" - /* 清空列表 */ - list.Clear(); - - /* 尾部添加元素 */ - list.Add(1); - list.Add(3); - list.Add(2); - list.Add(5); - list.Add(4); - - /* 中间插入元素 */ - list.Insert(3, 6); - - /* 删除元素 */ - list.RemoveAt(3); - ``` - -=== "Swift" - - ```swift title="list.swift" - /* 清空列表 */ - list.removeAll() - - /* 尾部添加元素 */ - list.append(1) - list.append(3) - list.append(2) - list.append(5) - list.append(4) - - /* 中间插入元素 */ - list.insert(6, at: 3) // 在索引 3 处插入数字 6 - - /* 删除元素 */ - list.remove(at: 3) // 删除索引 3 处的元素 - ``` - -=== "Zig" - - ```zig title="list.zig" - // 清空列表 - list.clearRetainingCapacity(); - - // 尾部添加元素 - try list.append(1); - try list.append(3); - try list.append(2); - try list.append(5); - try list.append(4); - - // 中间插入元素 - try list.insert(3, 6); // 在索引 3 处插入数字 6 - - // 删除元素 - _ = list.orderedRemove(3); // 删除索引 3 处的元素 - ``` - === "Dart" ```dart title="list.dart" @@ -477,24 +451,48 @@ list.remove(3); // 删除索引 3 处的元素 ``` +=== "C" + + ```c title="list.c" + // C 未提供内置动态数组 + ``` + +=== "Zig" + + ```zig title="list.zig" + // 清空列表 + list.clearRetainingCapacity(); + + // 尾部添加元素 + try list.append(1); + try list.append(3); + try list.append(2); + try list.append(5); + try list.append(4); + + // 中间插入元素 + try list.insert(3, 6); // 在索引 3 处插入数字 6 + + // 删除元素 + _ = list.orderedRemove(3); // 删除索引 3 处的元素 + ``` + ### 遍历列表 与数组一样,列表可以根据索引遍历,也可以直接遍历各元素。 -=== "Java" +=== "Python" - ```java title="list.java" - /* 通过索引遍历列表 */ - int count = 0; - for (int i = 0; i < list.size(); i++) { - count++; - } + ```python title="list.py" + # 通过索引遍历列表 + count = 0 + for i in range(len(list)): + count += 1 - /* 直接遍历列表元素 */ - count = 0; - for (int n : list) { - count++; - } + # 直接遍历列表元素 + count = 0 + for n in list: + count += 1 ``` === "C++" @@ -513,18 +511,36 @@ } ``` -=== "Python" +=== "Java" - ```python title="list.py" - # 通过索引遍历列表 - count = 0 - for i in range(len(list)): - count += 1 + ```java title="list.java" + /* 通过索引遍历列表 */ + int count = 0; + for (int i = 0; i < list.size(); i++) { + count++; + } - # 直接遍历列表元素 - count = 0 - for n in list: - count += 1 + /* 直接遍历列表元素 */ + count = 0; + for (int n : list) { + count++; + } + ``` + +=== "C#" + + ```csharp title="list.cs" + /* 通过索引遍历列表 */ + int count = 0; + for (int i = 0; i < list.Count; i++) { + count++; + } + + /* 直接遍历列表元素 */ + count = 0; + foreach (int n in list) { + count++; + } ``` === "Go" @@ -543,6 +559,22 @@ } ``` +=== "Swift" + + ```swift title="list.swift" + /* 通过索引遍历列表 */ + var count = 0 + for _ in list.indices { + count += 1 + } + + /* 直接遍历列表元素 */ + count = 0 + for _ in list { + count += 1 + } + ``` + === "JS" ```javascript title="list.js" @@ -575,61 +607,6 @@ } ``` -=== "C" - - ```c title="list.c" - // C 未提供内置动态数组 - ``` - -=== "C#" - - ```csharp title="list.cs" - /* 通过索引遍历列表 */ - int count = 0; - for (int i = 0; i < list.Count; i++) { - count++; - } - - /* 直接遍历列表元素 */ - count = 0; - foreach (int n in list) { - count++; - } - ``` - -=== "Swift" - - ```swift title="list.swift" - /* 通过索引遍历列表 */ - var count = 0 - for _ in list.indices { - count += 1 - } - - /* 直接遍历列表元素 */ - count = 0 - for _ in list { - count += 1 - } - ``` - -=== "Zig" - - ```zig title="list.zig" - // 通过索引遍历列表 - var count: i32 = 0; - var i: i32 = 0; - while (i < list.items.len) : (i += 1) { - count += 1; - } - - // 直接遍历列表元素 - count = 0; - for (list.items) |_| { - count += 1; - } - ``` - === "Dart" ```dart title="list.dart" @@ -662,16 +639,39 @@ } ``` +=== "C" + + ```c title="list.c" + // C 未提供内置动态数组 + ``` + +=== "Zig" + + ```zig title="list.zig" + // 通过索引遍历列表 + var count: i32 = 0; + var i: i32 = 0; + while (i < list.items.len) : (i += 1) { + count += 1; + } + + // 直接遍历列表元素 + count = 0; + for (list.items) |_| { + count += 1; + } + ``` + ### 拼接列表 给定一个新列表 `list1` ,我们可以将该列表拼接到原列表的尾部。 -=== "Java" +=== "Python" - ```java title="list.java" - /* 拼接两个列表 */ - List list1 = new ArrayList<>(Arrays.asList(new Integer[] { 6, 8, 7, 10, 9 })); - list.addAll(list1); // 将列表 list1 拼接到 list 之后 + ```python title="list.py" + # 拼接两个列表 + list1: list[int] = [6, 8, 7, 10, 9] + list += list1 # 将列表 list1 拼接到 list 之后 ``` === "C++" @@ -683,12 +683,20 @@ list.insert(list.end(), list1.begin(), list1.end()); ``` -=== "Python" +=== "Java" - ```python title="list.py" - # 拼接两个列表 - list1: list[int] = [6, 8, 7, 10, 9] - list += list1 # 将列表 list1 拼接到 list 之后 + ```java title="list.java" + /* 拼接两个列表 */ + List list1 = new ArrayList<>(Arrays.asList(new Integer[] { 6, 8, 7, 10, 9 })); + list.addAll(list1); // 将列表 list1 拼接到 list 之后 + ``` + +=== "C#" + + ```csharp title="list.cs" + /* 拼接两个列表 */ + List list1 = new() { 6, 8, 7, 10, 9 }; + list.AddRange(list1); // 将列表 list1 拼接到 list 之后 ``` === "Go" @@ -699,6 +707,14 @@ list = append(list, list1...) // 将列表 list1 拼接到 list 之后 ``` +=== "Swift" + + ```swift title="list.swift" + /* 拼接两个列表 */ + let list1 = [6, 8, 7, 10, 9] + list.append(contentsOf: list1) // 将列表 list1 拼接到 list 之后 + ``` + === "JS" ```javascript title="list.js" @@ -715,38 +731,6 @@ list.push(...list1); // 将列表 list1 拼接到 list 之后 ``` -=== "C" - - ```c title="list.c" - // C 未提供内置动态数组 - ``` - -=== "C#" - - ```csharp title="list.cs" - /* 拼接两个列表 */ - List list1 = new() { 6, 8, 7, 10, 9 }; - list.AddRange(list1); // 将列表 list1 拼接到 list 之后 - ``` - -=== "Swift" - - ```swift title="list.swift" - /* 拼接两个列表 */ - let list1 = [6, 8, 7, 10, 9] - list.append(contentsOf: list1) // 将列表 list1 拼接到 list 之后 - ``` - -=== "Zig" - - ```zig title="list.zig" - // 拼接两个列表 - var list1 = std.ArrayList(i32).init(std.heap.page_allocator); - defer list1.deinit(); - try list1.appendSlice(&[_]i32{ 6, 8, 7, 10, 9 }); - try list.insertSlice(list.items.len, list1.items); // 将列表 list1 拼接到 list 之后 - ``` - === "Dart" ```dart title="list.dart" @@ -763,15 +747,31 @@ list.extend(list1); ``` +=== "C" + + ```c title="list.c" + // C 未提供内置动态数组 + ``` + +=== "Zig" + + ```zig title="list.zig" + // 拼接两个列表 + var list1 = std.ArrayList(i32).init(std.heap.page_allocator); + defer list1.deinit(); + try list1.appendSlice(&[_]i32{ 6, 8, 7, 10, 9 }); + try list.insertSlice(list.items.len, list1.items); // 将列表 list1 拼接到 list 之后 + ``` + ### 排序列表 完成列表排序后,我们便可以使用在数组类算法题中经常考察的“二分查找”和“双指针”算法。 -=== "Java" +=== "Python" - ```java title="list.java" - /* 排序列表 */ - Collections.sort(list); // 排序后,列表元素从小到大排列 + ```python title="list.py" + # 排序列表 + list.sort() # 排序后,列表元素从小到大排列 ``` === "C++" @@ -781,11 +781,18 @@ sort(list.begin(), list.end()); // 排序后,列表元素从小到大排列 ``` -=== "Python" +=== "Java" - ```python title="list.py" - # 排序列表 - list.sort() # 排序后,列表元素从小到大排列 + ```java title="list.java" + /* 排序列表 */ + Collections.sort(list); // 排序后,列表元素从小到大排列 + ``` + +=== "C#" + + ```csharp title="list.cs" + /* 排序列表 */ + list.Sort(); // 排序后,列表元素从小到大排列 ``` === "Go" @@ -795,6 +802,13 @@ sort.Ints(list) // 排序后,列表元素从小到大排列 ``` +=== "Swift" + + ```swift title="list.swift" + /* 排序列表 */ + list.sort() // 排序后,列表元素从小到大排列 + ``` + === "JS" ```javascript title="list.js" @@ -809,33 +823,6 @@ list.sort((a, b) => a - b); // 排序后,列表元素从小到大排列 ``` -=== "C" - - ```c title="list.c" - // C 未提供内置动态数组 - ``` - -=== "C#" - - ```csharp title="list.cs" - /* 排序列表 */ - list.Sort(); // 排序后,列表元素从小到大排列 - ``` - -=== "Swift" - - ```swift title="list.swift" - /* 排序列表 */ - list.sort() // 排序后,列表元素从小到大排列 - ``` - -=== "Zig" - - ```zig title="list.zig" - // 排序列表 - std.sort.sort(i32, list.items, {}, comptime std.sort.asc(i32)); - ``` - === "Dart" ```dart title="list.dart" @@ -850,6 +837,19 @@ list.sort(); // 排序后,列表元素从小到大排列 ``` +=== "C" + + ```c title="list.c" + // C 未提供内置动态数组 + ``` + +=== "Zig" + + ```zig title="list.zig" + // 排序列表 + std.sort.sort(i32, list.items, {}, comptime std.sort.asc(i32)); + ``` + ## 列表实现 许多编程语言都提供内置的列表,例如 Java、C++、Python 等。它们的实现比较复杂,各个参数的设定也非常有考究,例如初始容量、扩容倍数等。感兴趣的读者可以查阅源码进行学习。 @@ -860,9 +860,9 @@ - **数量记录**:声明一个变量 size,用于记录列表当前元素数量,并随着元素插入和删除实时更新。根据此变量,我们可以定位列表尾部,以及判断是否需要扩容。 - **扩容机制**:若插入元素时列表容量已满,则需要进行扩容。首先根据扩容倍数创建一个更大的数组,再将当前数组的所有元素依次移动至新数组。在本示例中,我们规定每次将数组扩容至之前的 2 倍。 -=== "Java" +=== "Python" - ```java title="my_list.java" + ```python title="my_list.py" [class]{MyList}-[func]{} ``` @@ -872,9 +872,15 @@ [class]{MyList}-[func]{} ``` -=== "Python" +=== "Java" - ```python title="my_list.py" + ```java title="my_list.java" + [class]{MyList}-[func]{} + ``` + +=== "C#" + + ```csharp title="my_list.cs" [class]{MyList}-[func]{} ``` @@ -884,6 +890,12 @@ [class]{myList}-[func]{} ``` +=== "Swift" + + ```swift title="my_list.swift" + [class]{MyList}-[func]{} + ``` + === "JS" ```javascript title="my_list.js" @@ -896,30 +908,6 @@ [class]{MyList}-[func]{} ``` -=== "C" - - ```c title="my_list.c" - [class]{myList}-[func]{} - ``` - -=== "C#" - - ```csharp title="my_list.cs" - [class]{MyList}-[func]{} - ``` - -=== "Swift" - - ```swift title="my_list.swift" - [class]{MyList}-[func]{} - ``` - -=== "Zig" - - ```zig title="my_list.zig" - [class]{MyList}-[func]{} - ``` - === "Dart" ```dart title="my_list.dart" @@ -931,3 +919,15 @@ ```rust title="my_list.rs" [class]{MyList}-[func]{} ``` + +=== "C" + + ```c title="my_list.c" + [class]{myList}-[func]{} + ``` + +=== "Zig" + + ```zig title="my_list.zig" + [class]{MyList}-[func]{} + ``` diff --git a/docs/chapter_backtracking/backtracking_algorithm.md b/docs/chapter_backtracking/backtracking_algorithm.md index 135e045f..fcde06c2 100644 --- a/docs/chapter_backtracking/backtracking_algorithm.md +++ b/docs/chapter_backtracking/backtracking_algorithm.md @@ -10,10 +10,10 @@ 对于此题,我们前序遍历这颗树,并判断当前节点的值是否为 $7$ ,若是则将该节点的值加入到结果列表 `res` 之中。相关过程实现如下图和以下代码所示。 -=== "Java" +=== "Python" - ```java title="preorder_traversal_i_compact.java" - [class]{preorder_traversal_i_compact}-[func]{preOrder} + ```python title="preorder_traversal_i_compact.py" + [class]{}-[func]{pre_order} ``` === "C++" @@ -22,10 +22,16 @@ [class]{}-[func]{preOrder} ``` -=== "Python" +=== "Java" - ```python title="preorder_traversal_i_compact.py" - [class]{}-[func]{pre_order} + ```java title="preorder_traversal_i_compact.java" + [class]{preorder_traversal_i_compact}-[func]{preOrder} + ``` + +=== "C#" + + ```csharp title="preorder_traversal_i_compact.cs" + [class]{preorder_traversal_i_compact}-[func]{preOrder} ``` === "Go" @@ -34,6 +40,12 @@ [class]{}-[func]{preOrderI} ``` +=== "Swift" + + ```swift title="preorder_traversal_i_compact.swift" + [class]{}-[func]{preOrder} + ``` + === "JS" ```javascript title="preorder_traversal_i_compact.js" @@ -46,30 +58,6 @@ [class]{}-[func]{preOrder} ``` -=== "C" - - ```c title="preorder_traversal_i_compact.c" - [class]{}-[func]{preOrder} - ``` - -=== "C#" - - ```csharp title="preorder_traversal_i_compact.cs" - [class]{preorder_traversal_i_compact}-[func]{preOrder} - ``` - -=== "Swift" - - ```swift title="preorder_traversal_i_compact.swift" - [class]{}-[func]{preOrder} - ``` - -=== "Zig" - - ```zig title="preorder_traversal_i_compact.zig" - [class]{}-[func]{preOrder} - ``` - === "Dart" ```dart title="preorder_traversal_i_compact.dart" @@ -82,6 +70,18 @@ [class]{}-[func]{pre_order} ``` +=== "C" + + ```c title="preorder_traversal_i_compact.c" + [class]{}-[func]{preOrder} + ``` + +=== "Zig" + + ```zig title="preorder_traversal_i_compact.zig" + [class]{}-[func]{preOrder} + ``` + ![在前序遍历中搜索节点](backtracking_algorithm.assets/preorder_find_nodes.png) ## 尝试与回退 @@ -98,10 +98,10 @@ 在例题一代码的基础上,我们需要借助一个列表 `path` 记录访问过的节点路径。当访问到值为 $7$ 的节点时,则复制 `path` 并添加进结果列表 `res` 。遍历完成后,`res` 中保存的就是所有的解。 -=== "Java" +=== "Python" - ```java title="preorder_traversal_ii_compact.java" - [class]{preorder_traversal_ii_compact}-[func]{preOrder} + ```python title="preorder_traversal_ii_compact.py" + [class]{}-[func]{pre_order} ``` === "C++" @@ -110,10 +110,16 @@ [class]{}-[func]{preOrder} ``` -=== "Python" +=== "Java" - ```python title="preorder_traversal_ii_compact.py" - [class]{}-[func]{pre_order} + ```java title="preorder_traversal_ii_compact.java" + [class]{preorder_traversal_ii_compact}-[func]{preOrder} + ``` + +=== "C#" + + ```csharp title="preorder_traversal_ii_compact.cs" + [class]{preorder_traversal_ii_compact}-[func]{preOrder} ``` === "Go" @@ -122,6 +128,12 @@ [class]{}-[func]{preOrderII} ``` +=== "Swift" + + ```swift title="preorder_traversal_ii_compact.swift" + [class]{}-[func]{preOrder} + ``` + === "JS" ```javascript title="preorder_traversal_ii_compact.js" @@ -134,30 +146,6 @@ [class]{}-[func]{preOrder} ``` -=== "C" - - ```c title="preorder_traversal_ii_compact.c" - [class]{}-[func]{preOrder} - ``` - -=== "C#" - - ```csharp title="preorder_traversal_ii_compact.cs" - [class]{preorder_traversal_ii_compact}-[func]{preOrder} - ``` - -=== "Swift" - - ```swift title="preorder_traversal_ii_compact.swift" - [class]{}-[func]{preOrder} - ``` - -=== "Zig" - - ```zig title="preorder_traversal_ii_compact.zig" - [class]{}-[func]{preOrder} - ``` - === "Dart" ```dart title="preorder_traversal_ii_compact.dart" @@ -170,6 +158,18 @@ [class]{}-[func]{pre_order} ``` +=== "C" + + ```c title="preorder_traversal_ii_compact.c" + [class]{}-[func]{preOrder} + ``` + +=== "Zig" + + ```zig title="preorder_traversal_ii_compact.zig" + [class]{}-[func]{preOrder} + ``` + 在每次“尝试”中,我们通过将当前节点添加进 `path` 来记录路径;而在“回退”前,我们需要将该节点从 `path` 中弹出,**以恢复本次尝试之前的状态**。 观察下图所示的过程,**我们可以将尝试和回退理解为“前进”与“撤销”**,两个操作是互为逆向的。 @@ -217,10 +217,10 @@ 为了满足以上约束条件,**我们需要添加剪枝操作**:在搜索过程中,若遇到值为 $3$ 的节点,则提前返回,停止继续搜索。 -=== "Java" +=== "Python" - ```java title="preorder_traversal_iii_compact.java" - [class]{preorder_traversal_iii_compact}-[func]{preOrder} + ```python title="preorder_traversal_iii_compact.py" + [class]{}-[func]{pre_order} ``` === "C++" @@ -229,10 +229,16 @@ [class]{}-[func]{preOrder} ``` -=== "Python" +=== "Java" - ```python title="preorder_traversal_iii_compact.py" - [class]{}-[func]{pre_order} + ```java title="preorder_traversal_iii_compact.java" + [class]{preorder_traversal_iii_compact}-[func]{preOrder} + ``` + +=== "C#" + + ```csharp title="preorder_traversal_iii_compact.cs" + [class]{preorder_traversal_iii_compact}-[func]{preOrder} ``` === "Go" @@ -241,6 +247,12 @@ [class]{}-[func]{preOrderIII} ``` +=== "Swift" + + ```swift title="preorder_traversal_iii_compact.swift" + [class]{}-[func]{preOrder} + ``` + === "JS" ```javascript title="preorder_traversal_iii_compact.js" @@ -253,30 +265,6 @@ [class]{}-[func]{preOrder} ``` -=== "C" - - ```c title="preorder_traversal_iii_compact.c" - [class]{}-[func]{preOrder} - ``` - -=== "C#" - - ```csharp title="preorder_traversal_iii_compact.cs" - [class]{preorder_traversal_iii_compact}-[func]{preOrder} - ``` - -=== "Swift" - - ```swift title="preorder_traversal_iii_compact.swift" - [class]{}-[func]{preOrder} - ``` - -=== "Zig" - - ```zig title="preorder_traversal_iii_compact.zig" - [class]{}-[func]{preOrder} - ``` - === "Dart" ```dart title="preorder_traversal_iii_compact.dart" @@ -289,6 +277,18 @@ [class]{}-[func]{pre_order} ``` +=== "C" + + ```c title="preorder_traversal_iii_compact.c" + [class]{}-[func]{preOrder} + ``` + +=== "Zig" + + ```zig title="preorder_traversal_iii_compact.zig" + [class]{}-[func]{preOrder} + ``` + 剪枝是一个非常形象的名词。如下图所示,在搜索过程中,**我们“剪掉”了不满足约束条件的搜索分支**,避免许多无意义的尝试,从而提高了搜索效率。 ![根据约束条件剪枝](backtracking_algorithm.assets/preorder_find_constrained_paths.png) @@ -299,30 +299,26 @@ 在以下框架代码中,`state` 表示问题的当前状态,`choices` 表示当前状态下可以做出的选择。 -=== "Java" +=== "Python" - ```java title="" - /* 回溯算法框架 */ - void backtrack(State state, List choices, List res) { - // 判断是否为解 - if (isSolution(state)) { - // 记录解 - recordSolution(state, res); - // 停止继续搜索 - return; - } - // 遍历所有选择 - for (Choice choice : choices) { - // 剪枝:判断选择是否合法 - if (isValid(state, choice)) { - // 尝试:做出选择,更新状态 - makeChoice(state, choice); - backtrack(state, choices, res); - // 回退:撤销选择,恢复到之前的状态 - undoChoice(state, choice); - } - } - } + ```python title="" + def backtrack(state: State, choices: list[choice], res: list[state]): + """回溯算法框架""" + # 判断是否为解 + if is_solution(state): + # 记录解 + record_solution(state, res) + # 停止继续搜索 + return + # 遍历所有选择 + for choice in choices: + # 剪枝:判断选择是否合法 + if is_valid(state, choice): + # 尝试:做出选择,更新状态 + make_choice(state, choice) + backtrack(state, choices, res) + # 回退:撤销选择,恢复到之前的状态 + undo_choice(state, choice) ``` === "C++" @@ -351,26 +347,56 @@ } ``` -=== "Python" +=== "Java" - ```python title="" - def backtrack(state: State, choices: list[choice], res: list[state]): - """回溯算法框架""" - # 判断是否为解 - if is_solution(state): - # 记录解 - record_solution(state, res) - # 停止继续搜索 - return - # 遍历所有选择 - for choice in choices: - # 剪枝:判断选择是否合法 - if is_valid(state, choice): - # 尝试:做出选择,更新状态 - make_choice(state, choice) - backtrack(state, choices, res) - # 回退:撤销选择,恢复到之前的状态 - undo_choice(state, choice) + ```java title="" + /* 回溯算法框架 */ + void backtrack(State state, List choices, List res) { + // 判断是否为解 + if (isSolution(state)) { + // 记录解 + recordSolution(state, res); + // 停止继续搜索 + return; + } + // 遍历所有选择 + for (Choice choice : choices) { + // 剪枝:判断选择是否合法 + if (isValid(state, choice)) { + // 尝试:做出选择,更新状态 + makeChoice(state, choice); + backtrack(state, choices, res); + // 回退:撤销选择,恢复到之前的状态 + undoChoice(state, choice); + } + } + } + ``` + +=== "C#" + + ```csharp title="" + /* 回溯算法框架 */ + void backtrack(State state, List choices, List res) { + // 判断是否为解 + if (isSolution(state)) { + // 记录解 + recordSolution(state, res); + // 停止继续搜索 + return; + } + // 遍历所有选择 + foreach (Choice choice in choices) { + // 剪枝:判断选择是否合法 + if (isValid(state, choice)) { + // 尝试:做出选择,更新状态 + makeChoice(state, choice); + backtrack(state, choices, res); + // 回退:撤销选择,恢复到之前的状态 + undoChoice(state, choice); + } + } + } ``` === "Go" @@ -399,6 +425,32 @@ } ``` +=== "Swift" + + ```swift title="" + /* 回溯算法框架 */ + func backtrack(state: inout State, choices: [Choice], res: inout [State]) { + // 判断是否为解 + if isSolution(state: state) { + // 记录解 + recordSolution(state: state, res: &res) + // 停止继续搜索 + return + } + // 遍历所有选择 + for choice in choices { + // 剪枝:判断选择是否合法 + if isValid(state: state, choice: choice) { + // 尝试:做出选择,更新状态 + makeChoice(state: &state, choice: choice) + backtrack(state: &state, choices: choices, res: &res) + // 回退:撤销选择,恢复到之前的状态 + undoChoice(state: &state, choice: choice) + } + } + } + ``` + === "JS" ```javascript title="" @@ -451,90 +503,6 @@ } ``` -=== "C" - - ```c title="" - /* 回溯算法框架 */ - void backtrack(State *state, Choice *choices, int numChoices, State *res, int numRes) { - // 判断是否为解 - if (isSolution(state)) { - // 记录解 - recordSolution(state, res, numRes); - // 停止继续搜索 - return; - } - // 遍历所有选择 - for (int i = 0; i < numChoices; i++) { - // 剪枝:判断选择是否合法 - if (isValid(state, &choices[i])) { - // 尝试:做出选择,更新状态 - makeChoice(state, &choices[i]); - backtrack(state, choices, numChoices, res, numRes); - // 回退:撤销选择,恢复到之前的状态 - undoChoice(state, &choices[i]); - } - } - } - ``` - -=== "C#" - - ```csharp title="" - /* 回溯算法框架 */ - void backtrack(State state, List choices, List res) { - // 判断是否为解 - if (isSolution(state)) { - // 记录解 - recordSolution(state, res); - // 停止继续搜索 - return; - } - // 遍历所有选择 - foreach (Choice choice in choices) { - // 剪枝:判断选择是否合法 - if (isValid(state, choice)) { - // 尝试:做出选择,更新状态 - makeChoice(state, choice); - backtrack(state, choices, res); - // 回退:撤销选择,恢复到之前的状态 - undoChoice(state, choice); - } - } - } - ``` - -=== "Swift" - - ```swift title="" - /* 回溯算法框架 */ - func backtrack(state: inout State, choices: [Choice], res: inout [State]) { - // 判断是否为解 - if isSolution(state: state) { - // 记录解 - recordSolution(state: state, res: &res) - // 停止继续搜索 - return - } - // 遍历所有选择 - for choice in choices { - // 剪枝:判断选择是否合法 - if isValid(state: state, choice: choice) { - // 尝试:做出选择,更新状态 - makeChoice(state: &state, choice: choice) - backtrack(state: &state, choices: choices, res: &res) - // 回退:撤销选择,恢复到之前的状态 - undoChoice(state: &state, choice: choice) - } - } - } - ``` - -=== "Zig" - - ```zig title="" - - ``` - === "Dart" ```dart title="" @@ -567,22 +535,54 @@ ``` +=== "C" + + ```c title="" + /* 回溯算法框架 */ + void backtrack(State *state, Choice *choices, int numChoices, State *res, int numRes) { + // 判断是否为解 + if (isSolution(state)) { + // 记录解 + recordSolution(state, res, numRes); + // 停止继续搜索 + return; + } + // 遍历所有选择 + for (int i = 0; i < numChoices; i++) { + // 剪枝:判断选择是否合法 + if (isValid(state, &choices[i])) { + // 尝试:做出选择,更新状态 + makeChoice(state, &choices[i]); + backtrack(state, choices, numChoices, res, numRes); + // 回退:撤销选择,恢复到之前的状态 + undoChoice(state, &choices[i]); + } + } + } + ``` + +=== "Zig" + + ```zig title="" + + ``` + 接下来,我们基于框架代码来解决例题三。状态 `state` 为节点遍历路径,选择 `choices` 为当前节点的左子节点和右子节点,结果 `res` 是路径列表。 -=== "Java" +=== "Python" - ```java title="preorder_traversal_iii_template.java" - [class]{preorder_traversal_iii_template}-[func]{isSolution} + ```python title="preorder_traversal_iii_template.py" + [class]{}-[func]{is_solution} - [class]{preorder_traversal_iii_template}-[func]{recordSolution} + [class]{}-[func]{record_solution} - [class]{preorder_traversal_iii_template}-[func]{isValid} + [class]{}-[func]{is_valid} - [class]{preorder_traversal_iii_template}-[func]{makeChoice} + [class]{}-[func]{make_choice} - [class]{preorder_traversal_iii_template}-[func]{undoChoice} + [class]{}-[func]{undo_choice} - [class]{preorder_traversal_iii_template}-[func]{backtrack} + [class]{}-[func]{backtrack} ``` === "C++" @@ -601,20 +601,36 @@ [class]{}-[func]{backtrack} ``` -=== "Python" +=== "Java" - ```python title="preorder_traversal_iii_template.py" - [class]{}-[func]{is_solution} + ```java title="preorder_traversal_iii_template.java" + [class]{preorder_traversal_iii_template}-[func]{isSolution} - [class]{}-[func]{record_solution} + [class]{preorder_traversal_iii_template}-[func]{recordSolution} - [class]{}-[func]{is_valid} + [class]{preorder_traversal_iii_template}-[func]{isValid} - [class]{}-[func]{make_choice} + [class]{preorder_traversal_iii_template}-[func]{makeChoice} - [class]{}-[func]{undo_choice} + [class]{preorder_traversal_iii_template}-[func]{undoChoice} - [class]{}-[func]{backtrack} + [class]{preorder_traversal_iii_template}-[func]{backtrack} + ``` + +=== "C#" + + ```csharp title="preorder_traversal_iii_template.cs" + [class]{preorder_traversal_iii_template}-[func]{isSolution} + + [class]{preorder_traversal_iii_template}-[func]{recordSolution} + + [class]{preorder_traversal_iii_template}-[func]{isValid} + + [class]{preorder_traversal_iii_template}-[func]{makeChoice} + + [class]{preorder_traversal_iii_template}-[func]{undoChoice} + + [class]{preorder_traversal_iii_template}-[func]{backtrack} ``` === "Go" @@ -633,6 +649,22 @@ [class]{}-[func]{backtrackIII} ``` +=== "Swift" + + ```swift title="preorder_traversal_iii_template.swift" + [class]{}-[func]{isSolution} + + [class]{}-[func]{recordSolution} + + [class]{}-[func]{isValid} + + [class]{}-[func]{makeChoice} + + [class]{}-[func]{undoChoice} + + [class]{}-[func]{backtrack} + ``` + === "JS" ```javascript title="preorder_traversal_iii_template.js" @@ -665,70 +697,6 @@ [class]{}-[func]{backtrack} ``` -=== "C" - - ```c title="preorder_traversal_iii_template.c" - [class]{}-[func]{isSolution} - - [class]{}-[func]{recordSolution} - - [class]{}-[func]{isValid} - - [class]{}-[func]{makeChoice} - - [class]{}-[func]{undoChoice} - - [class]{}-[func]{backtrack} - ``` - -=== "C#" - - ```csharp title="preorder_traversal_iii_template.cs" - [class]{preorder_traversal_iii_template}-[func]{isSolution} - - [class]{preorder_traversal_iii_template}-[func]{recordSolution} - - [class]{preorder_traversal_iii_template}-[func]{isValid} - - [class]{preorder_traversal_iii_template}-[func]{makeChoice} - - [class]{preorder_traversal_iii_template}-[func]{undoChoice} - - [class]{preorder_traversal_iii_template}-[func]{backtrack} - ``` - -=== "Swift" - - ```swift title="preorder_traversal_iii_template.swift" - [class]{}-[func]{isSolution} - - [class]{}-[func]{recordSolution} - - [class]{}-[func]{isValid} - - [class]{}-[func]{makeChoice} - - [class]{}-[func]{undoChoice} - - [class]{}-[func]{backtrack} - ``` - -=== "Zig" - - ```zig title="preorder_traversal_iii_template.zig" - [class]{}-[func]{isSolution} - - [class]{}-[func]{recordSolution} - - [class]{}-[func]{isValid} - - [class]{}-[func]{makeChoice} - - [class]{}-[func]{undoChoice} - - [class]{}-[func]{backtrack} - ``` - === "Dart" ```dart title="preorder_traversal_iii_template.dart" @@ -761,6 +729,38 @@ [class]{}-[func]{backtrack} ``` +=== "C" + + ```c title="preorder_traversal_iii_template.c" + [class]{}-[func]{isSolution} + + [class]{}-[func]{recordSolution} + + [class]{}-[func]{isValid} + + [class]{}-[func]{makeChoice} + + [class]{}-[func]{undoChoice} + + [class]{}-[func]{backtrack} + ``` + +=== "Zig" + + ```zig title="preorder_traversal_iii_template.zig" + [class]{}-[func]{isSolution} + + [class]{}-[func]{recordSolution} + + [class]{}-[func]{isValid} + + [class]{}-[func]{makeChoice} + + [class]{}-[func]{undoChoice} + + [class]{}-[func]{backtrack} + ``` + 根据题意,我们在找到值为 7 的节点后应该继续搜索,**因此需要将记录解之后的 `return` 语句删除**。下图对比了保留或删除 `return` 语句的搜索过程。 ![保留与删除 return 的搜索过程对比](backtracking_algorithm.assets/backtrack_remove_return_or_not.png) diff --git a/docs/chapter_backtracking/n_queens_problem.md b/docs/chapter_backtracking/n_queens_problem.md index f7269dcc..49d60448 100644 --- a/docs/chapter_backtracking/n_queens_problem.md +++ b/docs/chapter_backtracking/n_queens_problem.md @@ -40,12 +40,12 @@ 请注意,$n$ 维方阵中 $row - col$ 的范围是 $[-n + 1, n - 1]$ ,$row + col$ 的范围是 $[0, 2n - 2]$ ,所以主对角线和次对角线的数量都为 $2n - 1$ ,即数组 `diag1` 和 `diag2` 的长度都为 $2n - 1$ 。 -=== "Java" +=== "Python" - ```java title="n_queens.java" - [class]{n_queens}-[func]{backtrack} + ```python title="n_queens.py" + [class]{}-[func]{backtrack} - [class]{n_queens}-[func]{nQueens} + [class]{}-[func]{n_queens} ``` === "C++" @@ -56,12 +56,20 @@ [class]{}-[func]{nQueens} ``` -=== "Python" +=== "Java" - ```python title="n_queens.py" - [class]{}-[func]{backtrack} + ```java title="n_queens.java" + [class]{n_queens}-[func]{backtrack} - [class]{}-[func]{n_queens} + [class]{n_queens}-[func]{nQueens} + ``` + +=== "C#" + + ```csharp title="n_queens.cs" + [class]{n_queens}-[func]{backtrack} + + [class]{n_queens}-[func]{nQueens} ``` === "Go" @@ -72,6 +80,14 @@ [class]{}-[func]{nQueens} ``` +=== "Swift" + + ```swift title="n_queens.swift" + [class]{}-[func]{backtrack} + + [class]{}-[func]{nQueens} + ``` + === "JS" ```javascript title="n_queens.js" @@ -88,38 +104,6 @@ [class]{}-[func]{nQueens} ``` -=== "C" - - ```c title="n_queens.c" - [class]{}-[func]{backtrack} - - [class]{}-[func]{nQueens} - ``` - -=== "C#" - - ```csharp title="n_queens.cs" - [class]{n_queens}-[func]{backtrack} - - [class]{n_queens}-[func]{nQueens} - ``` - -=== "Swift" - - ```swift title="n_queens.swift" - [class]{}-[func]{backtrack} - - [class]{}-[func]{nQueens} - ``` - -=== "Zig" - - ```zig title="n_queens.zig" - [class]{}-[func]{backtrack} - - [class]{}-[func]{nQueens} - ``` - === "Dart" ```dart title="n_queens.dart" @@ -136,6 +120,22 @@ [class]{}-[func]{n_queens} ``` +=== "C" + + ```c title="n_queens.c" + [class]{}-[func]{backtrack} + + [class]{}-[func]{nQueens} + ``` + +=== "Zig" + + ```zig title="n_queens.zig" + [class]{}-[func]{backtrack} + + [class]{}-[func]{nQueens} + ``` + 逐行放置 $n$ 次,考虑列约束,则从第一行到最后一行分别有 $n$、$n-1$、$\dots$、$2$、$1$ 个选择,**因此时间复杂度为 $O(n!)$** 。实际上,根据对角线约束的剪枝也能够大幅地缩小搜索空间,因而搜索效率往往优于以上时间复杂度。 数组 `state` 使用 $O(n^2)$ 空间,数组 `cols`、`diags1` 和 `diags2` 皆使用 $O(n)$ 空间。最大递归深度为 $n$ ,使用 $O(n)$ 栈帧空间。因此,**空间复杂度为 $O(n^2)$** 。 diff --git a/docs/chapter_backtracking/permutations_problem.md b/docs/chapter_backtracking/permutations_problem.md index 06a0453c..59c3b672 100644 --- a/docs/chapter_backtracking/permutations_problem.md +++ b/docs/chapter_backtracking/permutations_problem.md @@ -43,12 +43,12 @@ 想清楚以上信息之后,我们就可以在框架代码中做“完形填空”了。为了缩短代码行数,我们不单独实现框架代码中的各个函数,而是将他们展开在 `backtrack()` 函数中。 -=== "Java" +=== "Python" - ```java title="permutations_i.java" - [class]{permutations_i}-[func]{backtrack} + ```python title="permutations_i.py" + [class]{}-[func]{backtrack} - [class]{permutations_i}-[func]{permutationsI} + [class]{}-[func]{permutations_i} ``` === "C++" @@ -59,12 +59,20 @@ [class]{}-[func]{permutationsI} ``` -=== "Python" +=== "Java" - ```python title="permutations_i.py" - [class]{}-[func]{backtrack} + ```java title="permutations_i.java" + [class]{permutations_i}-[func]{backtrack} - [class]{}-[func]{permutations_i} + [class]{permutations_i}-[func]{permutationsI} + ``` + +=== "C#" + + ```csharp title="permutations_i.cs" + [class]{permutations_i}-[func]{backtrack} + + [class]{permutations_i}-[func]{permutationsI} ``` === "Go" @@ -75,6 +83,14 @@ [class]{}-[func]{permutationsI} ``` +=== "Swift" + + ```swift title="permutations_i.swift" + [class]{}-[func]{backtrack} + + [class]{}-[func]{permutationsI} + ``` + === "JS" ```javascript title="permutations_i.js" @@ -91,38 +107,6 @@ [class]{}-[func]{permutationsI} ``` -=== "C" - - ```c title="permutations_i.c" - [class]{}-[func]{backtrack} - - [class]{}-[func]{permutationsI} - ``` - -=== "C#" - - ```csharp title="permutations_i.cs" - [class]{permutations_i}-[func]{backtrack} - - [class]{permutations_i}-[func]{permutationsI} - ``` - -=== "Swift" - - ```swift title="permutations_i.swift" - [class]{}-[func]{backtrack} - - [class]{}-[func]{permutationsI} - ``` - -=== "Zig" - - ```zig title="permutations_i.zig" - [class]{}-[func]{backtrack} - - [class]{}-[func]{permutationsI} - ``` - === "Dart" ```dart title="permutations_i.dart" @@ -139,6 +123,22 @@ [class]{}-[func]{permutations_i} ``` +=== "C" + + ```c title="permutations_i.c" + [class]{}-[func]{backtrack} + + [class]{}-[func]{permutationsI} + ``` + +=== "Zig" + + ```zig title="permutations_i.zig" + [class]{}-[func]{backtrack} + + [class]{}-[func]{permutationsI} + ``` + ## 考虑相等元素的情况 !!! question @@ -167,12 +167,12 @@ 在上一题的代码的基础上,我们考虑在每一轮选择中开启一个哈希表 `duplicated` ,用于记录该轮中已经尝试过的元素,并将重复元素剪枝。 -=== "Java" +=== "Python" - ```java title="permutations_ii.java" - [class]{permutations_ii}-[func]{backtrack} + ```python title="permutations_ii.py" + [class]{}-[func]{backtrack} - [class]{permutations_ii}-[func]{permutationsII} + [class]{}-[func]{permutations_ii} ``` === "C++" @@ -183,12 +183,20 @@ [class]{}-[func]{permutationsII} ``` -=== "Python" +=== "Java" - ```python title="permutations_ii.py" - [class]{}-[func]{backtrack} + ```java title="permutations_ii.java" + [class]{permutations_ii}-[func]{backtrack} - [class]{}-[func]{permutations_ii} + [class]{permutations_ii}-[func]{permutationsII} + ``` + +=== "C#" + + ```csharp title="permutations_ii.cs" + [class]{permutations_ii}-[func]{backtrack} + + [class]{permutations_ii}-[func]{permutationsII} ``` === "Go" @@ -199,6 +207,14 @@ [class]{}-[func]{permutationsII} ``` +=== "Swift" + + ```swift title="permutations_ii.swift" + [class]{}-[func]{backtrack} + + [class]{}-[func]{permutationsII} + ``` + === "JS" ```javascript title="permutations_ii.js" @@ -215,38 +231,6 @@ [class]{}-[func]{permutationsII} ``` -=== "C" - - ```c title="permutations_ii.c" - [class]{}-[func]{backtrack} - - [class]{}-[func]{permutationsII} - ``` - -=== "C#" - - ```csharp title="permutations_ii.cs" - [class]{permutations_ii}-[func]{backtrack} - - [class]{permutations_ii}-[func]{permutationsII} - ``` - -=== "Swift" - - ```swift title="permutations_ii.swift" - [class]{}-[func]{backtrack} - - [class]{}-[func]{permutationsII} - ``` - -=== "Zig" - - ```zig title="permutations_ii.zig" - [class]{}-[func]{backtrack} - - [class]{}-[func]{permutationsII} - ``` - === "Dart" ```dart title="permutations_ii.dart" @@ -263,6 +247,22 @@ [class]{}-[func]{permutations_ii} ``` +=== "C" + + ```c title="permutations_ii.c" + [class]{}-[func]{backtrack} + + [class]{}-[func]{permutationsII} + ``` + +=== "Zig" + + ```zig title="permutations_ii.zig" + [class]{}-[func]{backtrack} + + [class]{}-[func]{permutationsII} + ``` + 假设元素两两之间互不相同,则 $n$ 个元素共有 $n!$ 种排列(阶乘);在记录结果时,需要复制长度为 $n$ 的列表,使用 $O(n)$ 时间。**因此时间复杂度为 $O(n!n)$** 。 最大递归深度为 $n$ ,使用 $O(n)$ 栈帧空间。`selected` 使用 $O(n)$ 空间。同一时刻最多共有 $n$ 个 `duplicated` ,使用 $O(n^2)$ 空间。**因此空间复杂度为 $O(n^2)$** 。 diff --git a/docs/chapter_backtracking/subset_sum_problem.md b/docs/chapter_backtracking/subset_sum_problem.md index 3e28fdb7..31684da1 100644 --- a/docs/chapter_backtracking/subset_sum_problem.md +++ b/docs/chapter_backtracking/subset_sum_problem.md @@ -17,12 +17,12 @@ 而与全排列问题不同的是,**本题集合中的元素可以被无限次选取**,因此无须借助 `selected` 布尔列表来记录元素是否已被选择。我们可以对全排列代码进行小幅修改,初步得到解题代码。 -=== "Java" +=== "Python" - ```java title="subset_sum_i_naive.java" - [class]{subset_sum_i_naive}-[func]{backtrack} + ```python title="subset_sum_i_naive.py" + [class]{}-[func]{backtrack} - [class]{subset_sum_i_naive}-[func]{subsetSumINaive} + [class]{}-[func]{subset_sum_i_naive} ``` === "C++" @@ -33,12 +33,20 @@ [class]{}-[func]{subsetSumINaive} ``` -=== "Python" +=== "Java" - ```python title="subset_sum_i_naive.py" - [class]{}-[func]{backtrack} + ```java title="subset_sum_i_naive.java" + [class]{subset_sum_i_naive}-[func]{backtrack} - [class]{}-[func]{subset_sum_i_naive} + [class]{subset_sum_i_naive}-[func]{subsetSumINaive} + ``` + +=== "C#" + + ```csharp title="subset_sum_i_naive.cs" + [class]{subset_sum_i_naive}-[func]{backtrack} + + [class]{subset_sum_i_naive}-[func]{subsetSumINaive} ``` === "Go" @@ -49,6 +57,14 @@ [class]{}-[func]{subsetSumINaive} ``` +=== "Swift" + + ```swift title="subset_sum_i_naive.swift" + [class]{}-[func]{backtrack} + + [class]{}-[func]{subsetSumINaive} + ``` + === "JS" ```javascript title="subset_sum_i_naive.js" @@ -65,38 +81,6 @@ [class]{}-[func]{subsetSumINaive} ``` -=== "C" - - ```c title="subset_sum_i_naive.c" - [class]{}-[func]{backtrack} - - [class]{}-[func]{subsetSumINaive} - ``` - -=== "C#" - - ```csharp title="subset_sum_i_naive.cs" - [class]{subset_sum_i_naive}-[func]{backtrack} - - [class]{subset_sum_i_naive}-[func]{subsetSumINaive} - ``` - -=== "Swift" - - ```swift title="subset_sum_i_naive.swift" - [class]{}-[func]{backtrack} - - [class]{}-[func]{subsetSumINaive} - ``` - -=== "Zig" - - ```zig title="subset_sum_i_naive.zig" - [class]{}-[func]{backtrack} - - [class]{}-[func]{subsetSumINaive} - ``` - === "Dart" ```dart title="subset_sum_i_naive.dart" @@ -113,6 +97,22 @@ [class]{}-[func]{subset_sum_i_naive} ``` +=== "C" + + ```c title="subset_sum_i_naive.c" + [class]{}-[func]{backtrack} + + [class]{}-[func]{subsetSumINaive} + ``` + +=== "Zig" + + ```zig title="subset_sum_i_naive.zig" + [class]{}-[func]{backtrack} + + [class]{}-[func]{subsetSumINaive} + ``` + 向以上代码输入数组 $[3, 4, 5]$ 和目标元素 $9$ ,输出结果为 $[3, 3, 3], [4, 5], [5, 4]$ 。**虽然成功找出了所有和为 $9$ 的子集,但其中存在重复的子集 $[4, 5]$ 和 $[5, 4]$** 。 这是因为搜索过程是区分选择顺序的,然而子集不区分选择顺序。如下图所示,先选 $4$ 后选 $5$ 与先选 $5$ 后选 $4$ 是两个不同的分支,但两者对应同一个子集。 @@ -150,12 +150,12 @@ - 在开启搜索前,先将数组 `nums` 排序。在遍历所有选择时,**当子集和超过 `target` 时直接结束循环**,因为后边的元素更大,其子集和都一定会超过 `target` 。 - 省去元素和变量 `total`,**通过在 `target` 上执行减法来统计元素和**,当 `target` 等于 $0$ 时记录解。 -=== "Java" +=== "Python" - ```java title="subset_sum_i.java" - [class]{subset_sum_i}-[func]{backtrack} + ```python title="subset_sum_i.py" + [class]{}-[func]{backtrack} - [class]{subset_sum_i}-[func]{subsetSumI} + [class]{}-[func]{subset_sum_i} ``` === "C++" @@ -166,12 +166,20 @@ [class]{}-[func]{subsetSumI} ``` -=== "Python" +=== "Java" - ```python title="subset_sum_i.py" - [class]{}-[func]{backtrack} + ```java title="subset_sum_i.java" + [class]{subset_sum_i}-[func]{backtrack} - [class]{}-[func]{subset_sum_i} + [class]{subset_sum_i}-[func]{subsetSumI} + ``` + +=== "C#" + + ```csharp title="subset_sum_i.cs" + [class]{subset_sum_i}-[func]{backtrack} + + [class]{subset_sum_i}-[func]{subsetSumI} ``` === "Go" @@ -182,6 +190,14 @@ [class]{}-[func]{subsetSumI} ``` +=== "Swift" + + ```swift title="subset_sum_i.swift" + [class]{}-[func]{backtrack} + + [class]{}-[func]{subsetSumI} + ``` + === "JS" ```javascript title="subset_sum_i.js" @@ -198,38 +214,6 @@ [class]{}-[func]{subsetSumI} ``` -=== "C" - - ```c title="subset_sum_i.c" - [class]{}-[func]{backtrack} - - [class]{}-[func]{subsetSumI} - ``` - -=== "C#" - - ```csharp title="subset_sum_i.cs" - [class]{subset_sum_i}-[func]{backtrack} - - [class]{subset_sum_i}-[func]{subsetSumI} - ``` - -=== "Swift" - - ```swift title="subset_sum_i.swift" - [class]{}-[func]{backtrack} - - [class]{}-[func]{subsetSumI} - ``` - -=== "Zig" - - ```zig title="subset_sum_i.zig" - [class]{}-[func]{backtrack} - - [class]{}-[func]{subsetSumI} - ``` - === "Dart" ```dart title="subset_sum_i.dart" @@ -246,6 +230,22 @@ [class]{}-[func]{subset_sum_i} ``` +=== "C" + + ```c title="subset_sum_i.c" + [class]{}-[func]{backtrack} + + [class]{}-[func]{subsetSumI} + ``` + +=== "Zig" + + ```zig title="subset_sum_i.zig" + [class]{}-[func]{backtrack} + + [class]{}-[func]{subsetSumI} + ``` + 如下图所示,为将数组 $[3, 4, 5]$ 和目标元素 $9$ 输入到以上代码后的整体回溯过程。 ![子集和 I 回溯过程](subset_sum_problem.assets/subset_sum_i.png) @@ -270,12 +270,12 @@ ### 代码实现 -=== "Java" +=== "Python" - ```java title="subset_sum_ii.java" - [class]{subset_sum_ii}-[func]{backtrack} + ```python title="subset_sum_ii.py" + [class]{}-[func]{backtrack} - [class]{subset_sum_ii}-[func]{subsetSumII} + [class]{}-[func]{subset_sum_ii} ``` === "C++" @@ -286,12 +286,20 @@ [class]{}-[func]{subsetSumII} ``` -=== "Python" +=== "Java" - ```python title="subset_sum_ii.py" - [class]{}-[func]{backtrack} + ```java title="subset_sum_ii.java" + [class]{subset_sum_ii}-[func]{backtrack} - [class]{}-[func]{subset_sum_ii} + [class]{subset_sum_ii}-[func]{subsetSumII} + ``` + +=== "C#" + + ```csharp title="subset_sum_ii.cs" + [class]{subset_sum_ii}-[func]{backtrack} + + [class]{subset_sum_ii}-[func]{subsetSumII} ``` === "Go" @@ -302,6 +310,14 @@ [class]{}-[func]{subsetSumII} ``` +=== "Swift" + + ```swift title="subset_sum_ii.swift" + [class]{}-[func]{backtrack} + + [class]{}-[func]{subsetSumII} + ``` + === "JS" ```javascript title="subset_sum_ii.js" @@ -318,38 +334,6 @@ [class]{}-[func]{subsetSumII} ``` -=== "C" - - ```c title="subset_sum_ii.c" - [class]{}-[func]{backtrack} - - [class]{}-[func]{subsetSumII} - ``` - -=== "C#" - - ```csharp title="subset_sum_ii.cs" - [class]{subset_sum_ii}-[func]{backtrack} - - [class]{subset_sum_ii}-[func]{subsetSumII} - ``` - -=== "Swift" - - ```swift title="subset_sum_ii.swift" - [class]{}-[func]{backtrack} - - [class]{}-[func]{subsetSumII} - ``` - -=== "Zig" - - ```zig title="subset_sum_ii.zig" - [class]{}-[func]{backtrack} - - [class]{}-[func]{subsetSumII} - ``` - === "Dart" ```dart title="subset_sum_ii.dart" @@ -366,6 +350,22 @@ [class]{}-[func]{subset_sum_ii} ``` +=== "C" + + ```c title="subset_sum_ii.c" + [class]{}-[func]{backtrack} + + [class]{}-[func]{subsetSumII} + ``` + +=== "Zig" + + ```zig title="subset_sum_ii.zig" + [class]{}-[func]{backtrack} + + [class]{}-[func]{subsetSumII} + ``` + 下图展示了数组 $[4, 4, 5]$ 和目标元素 $9$ 的回溯过程,共包含四种剪枝操作。请你将图示与代码注释相结合,理解整个搜索过程,以及每种剪枝操作是如何工作的。 ![子集和 II 回溯过程](subset_sum_problem.assets/subset_sum_ii.png) diff --git a/docs/chapter_computational_complexity/iteration_and_recursion.md b/docs/chapter_computational_complexity/iteration_and_recursion.md index efbb968d..cdbb8e5e 100644 --- a/docs/chapter_computational_complexity/iteration_and_recursion.md +++ b/docs/chapter_computational_complexity/iteration_and_recursion.md @@ -12,10 +12,10 @@ 以下函数基于 `for` 循环实现了求和 $1 + 2 + \dots + n$ ,求和结果使用变量 `res` 记录。需要注意的是,Python 中 `range(a, b)` 对应的区间是“左闭右开”的,对应的遍历范围为 $a, a + 1, \dots, b-1$ 。 -=== "Java" +=== "Python" - ```java title="iteration.java" - [class]{iteration}-[func]{forLoop} + ```python title="iteration.py" + [class]{}-[func]{for_loop} ``` === "C++" @@ -24,10 +24,16 @@ [class]{}-[func]{forLoop} ``` -=== "Python" +=== "Java" - ```python title="iteration.py" - [class]{}-[func]{for_loop} + ```java title="iteration.java" + [class]{iteration}-[func]{forLoop} + ``` + +=== "C#" + + ```csharp title="iteration.cs" + [class]{iteration}-[func]{forLoop} ``` === "Go" @@ -36,6 +42,12 @@ [class]{}-[func]{forLoop} ``` +=== "Swift" + + ```swift title="iteration.swift" + [class]{}-[func]{forLoop} + ``` + === "JS" ```javascript title="iteration.js" @@ -48,30 +60,6 @@ [class]{}-[func]{forLoop} ``` -=== "C" - - ```c title="iteration.c" - [class]{}-[func]{forLoop} - ``` - -=== "C#" - - ```csharp title="iteration.cs" - [class]{iteration}-[func]{forLoop} - ``` - -=== "Swift" - - ```swift title="iteration.swift" - [class]{}-[func]{forLoop} - ``` - -=== "Zig" - - ```zig title="iteration.zig" - [class]{}-[func]{forLoop} - ``` - === "Dart" ```dart title="iteration.dart" @@ -84,6 +72,18 @@ [class]{}-[func]{for_loop} ``` +=== "C" + + ```c title="iteration.c" + [class]{}-[func]{forLoop} + ``` + +=== "Zig" + + ```zig title="iteration.zig" + [class]{}-[func]{forLoop} + ``` + 下图展示了该求和函数的流程框图。 ![求和函数的流程框图](iteration_and_recursion.assets/iteration.png) @@ -96,10 +96,10 @@ 下面,我们用 `while` 循环来实现求和 $1 + 2 + \dots + n$ 。 -=== "Java" +=== "Python" - ```java title="iteration.java" - [class]{iteration}-[func]{whileLoop} + ```python title="iteration.py" + [class]{}-[func]{while_loop} ``` === "C++" @@ -108,10 +108,16 @@ [class]{}-[func]{whileLoop} ``` -=== "Python" +=== "Java" - ```python title="iteration.py" - [class]{}-[func]{while_loop} + ```java title="iteration.java" + [class]{iteration}-[func]{whileLoop} + ``` + +=== "C#" + + ```csharp title="iteration.cs" + [class]{iteration}-[func]{whileLoop} ``` === "Go" @@ -120,6 +126,12 @@ [class]{}-[func]{whileLoop} ``` +=== "Swift" + + ```swift title="iteration.swift" + [class]{}-[func]{whileLoop} + ``` + === "JS" ```javascript title="iteration.js" @@ -132,30 +144,6 @@ [class]{}-[func]{whileLoop} ``` -=== "C" - - ```c title="iteration.c" - [class]{}-[func]{whileLoop} - ``` - -=== "C#" - - ```csharp title="iteration.cs" - [class]{iteration}-[func]{whileLoop} - ``` - -=== "Swift" - - ```swift title="iteration.swift" - [class]{}-[func]{whileLoop} - ``` - -=== "Zig" - - ```zig title="iteration.zig" - [class]{}-[func]{whileLoop} - ``` - === "Dart" ```dart title="iteration.dart" @@ -168,14 +156,26 @@ [class]{}-[func]{while_loop} ``` +=== "C" + + ```c title="iteration.c" + [class]{}-[func]{whileLoop} + ``` + +=== "Zig" + + ```zig title="iteration.zig" + [class]{}-[func]{whileLoop} + ``` + 在 `while` 循环中,由于初始化和更新条件变量的步骤是独立在循环结构之外的,**因此它比 `for` 循环的自由度更高**。 例如在以下代码中,条件变量 $i$ 每轮进行了两次更新,这种情况就不太方便用 `for` 循环实现。 -=== "Java" +=== "Python" - ```java title="iteration.java" - [class]{iteration}-[func]{whileLoopII} + ```python title="iteration.py" + [class]{}-[func]{while_loop_ii} ``` === "C++" @@ -184,10 +184,16 @@ [class]{}-[func]{whileLoopII} ``` -=== "Python" +=== "Java" - ```python title="iteration.py" - [class]{}-[func]{while_loop_ii} + ```java title="iteration.java" + [class]{iteration}-[func]{whileLoopII} + ``` + +=== "C#" + + ```csharp title="iteration.cs" + [class]{iteration}-[func]{whileLoopII} ``` === "Go" @@ -196,6 +202,12 @@ [class]{}-[func]{whileLoopII} ``` +=== "Swift" + + ```swift title="iteration.swift" + [class]{}-[func]{whileLoopII} + ``` + === "JS" ```javascript title="iteration.js" @@ -208,30 +220,6 @@ [class]{}-[func]{whileLoopII} ``` -=== "C" - - ```c title="iteration.c" - [class]{}-[func]{whileLoopII} - ``` - -=== "C#" - - ```csharp title="iteration.cs" - [class]{iteration}-[func]{whileLoopII} - ``` - -=== "Swift" - - ```swift title="iteration.swift" - [class]{}-[func]{whileLoopII} - ``` - -=== "Zig" - - ```zig title="iteration.zig" - [class]{}-[func]{whileLoopII} - ``` - === "Dart" ```dart title="iteration.dart" @@ -244,16 +232,28 @@ [class]{}-[func]{while_loop_ii} ``` +=== "C" + + ```c title="iteration.c" + [class]{}-[func]{whileLoopII} + ``` + +=== "Zig" + + ```zig title="iteration.zig" + [class]{}-[func]{whileLoopII} + ``` + 总的来说,**`for` 循环的代码更加紧凑,`while` 循环更加灵活**,两者都可以实现迭代结构。选择使用哪一个应该根据特定问题的需求来决定。 ### 嵌套循环 我们可以在一个循环结构内嵌套另一个循环结构,以 `for` 循环为例: -=== "Java" +=== "Python" - ```java title="iteration.java" - [class]{iteration}-[func]{nestedForLoop} + ```python title="iteration.py" + [class]{}-[func]{nested_for_loop} ``` === "C++" @@ -262,10 +262,16 @@ [class]{}-[func]{nestedForLoop} ``` -=== "Python" +=== "Java" - ```python title="iteration.py" - [class]{}-[func]{nested_for_loop} + ```java title="iteration.java" + [class]{iteration}-[func]{nestedForLoop} + ``` + +=== "C#" + + ```csharp title="iteration.cs" + [class]{iteration}-[func]{nestedForLoop} ``` === "Go" @@ -274,6 +280,12 @@ [class]{}-[func]{nestedForLoop} ``` +=== "Swift" + + ```swift title="iteration.swift" + [class]{}-[func]{nestedForLoop} + ``` + === "JS" ```javascript title="iteration.js" @@ -286,30 +298,6 @@ [class]{}-[func]{nestedForLoop} ``` -=== "C" - - ```c title="iteration.c" - [class]{}-[func]{nestedForLoop} - ``` - -=== "C#" - - ```csharp title="iteration.cs" - [class]{iteration}-[func]{nestedForLoop} - ``` - -=== "Swift" - - ```swift title="iteration.swift" - [class]{}-[func]{nestedForLoop} - ``` - -=== "Zig" - - ```zig title="iteration.zig" - [class]{}-[func]{nestedForLoop} - ``` - === "Dart" ```dart title="iteration.dart" @@ -322,6 +310,18 @@ [class]{}-[func]{nested_for_loop} ``` +=== "C" + + ```c title="iteration.c" + [class]{}-[func]{nestedForLoop} + ``` + +=== "Zig" + + ```zig title="iteration.zig" + [class]{}-[func]{nestedForLoop} + ``` + 下图给出了该嵌套循环的流程框图。 ![嵌套循环的流程框图](iteration_and_recursion.assets/nested_iteration.png) @@ -345,10 +345,10 @@ 观察以下代码,我们只需调用函数 `recur(n)` ,就可以完成 $1 + 2 + \dots + n$ 的计算: -=== "Java" +=== "Python" - ```java title="recursion.java" - [class]{recursion}-[func]{recur} + ```python title="recursion.py" + [class]{}-[func]{recur} ``` === "C++" @@ -357,10 +357,16 @@ [class]{}-[func]{recur} ``` -=== "Python" +=== "Java" - ```python title="recursion.py" - [class]{}-[func]{recur} + ```java title="recursion.java" + [class]{recursion}-[func]{recur} + ``` + +=== "C#" + + ```csharp title="recursion.cs" + [class]{recursion}-[func]{recur} ``` === "Go" @@ -369,6 +375,12 @@ [class]{}-[func]{recur} ``` +=== "Swift" + + ```swift title="recursion.swift" + [class]{}-[func]{recur} + ``` + === "JS" ```javascript title="recursion.js" @@ -381,30 +393,6 @@ [class]{}-[func]{recur} ``` -=== "C" - - ```c title="recursion.c" - [class]{}-[func]{recur} - ``` - -=== "C#" - - ```csharp title="recursion.cs" - [class]{recursion}-[func]{recur} - ``` - -=== "Swift" - - ```swift title="recursion.swift" - [class]{}-[func]{recur} - ``` - -=== "Zig" - - ```zig title="recursion.zig" - [class]{}-[func]{recur} - ``` - === "Dart" ```dart title="recursion.dart" @@ -417,6 +405,18 @@ [class]{}-[func]{recur} ``` +=== "C" + + ```c title="recursion.c" + [class]{}-[func]{recur} + ``` + +=== "Zig" + + ```zig title="recursion.zig" + [class]{}-[func]{recur} + ``` + 下图展示了该函数的递归过程。 ![求和函数的递归过程](iteration_and_recursion.assets/recursion_sum.png) @@ -453,10 +453,10 @@ 以计算 $1 + 2 + \dots + n$ 为例,我们可以将结果变量 `res` 设为函数参数,从而实现尾递归。 -=== "Java" +=== "Python" - ```java title="recursion.java" - [class]{recursion}-[func]{tailRecur} + ```python title="recursion.py" + [class]{}-[func]{tail_recur} ``` === "C++" @@ -465,10 +465,16 @@ [class]{}-[func]{tailRecur} ``` -=== "Python" +=== "Java" - ```python title="recursion.py" - [class]{}-[func]{tail_recur} + ```java title="recursion.java" + [class]{recursion}-[func]{tailRecur} + ``` + +=== "C#" + + ```csharp title="recursion.cs" + [class]{recursion}-[func]{tailRecur} ``` === "Go" @@ -477,6 +483,12 @@ [class]{}-[func]{tailRecur} ``` +=== "Swift" + + ```swift title="recursion.swift" + [class]{}-[func]{tailRecur} + ``` + === "JS" ```javascript title="recursion.js" @@ -489,30 +501,6 @@ [class]{}-[func]{tailRecur} ``` -=== "C" - - ```c title="recursion.c" - [class]{}-[func]{tailRecur} - ``` - -=== "C#" - - ```csharp title="recursion.cs" - [class]{recursion}-[func]{tailRecur} - ``` - -=== "Swift" - - ```swift title="recursion.swift" - [class]{}-[func]{tailRecur} - ``` - -=== "Zig" - - ```zig title="recursion.zig" - [class]{}-[func]{tailRecur} - ``` - === "Dart" ```dart title="recursion.dart" @@ -525,6 +513,18 @@ [class]{}-[func]{tail_recur} ``` +=== "C" + + ```c title="recursion.c" + [class]{}-[func]{tailRecur} + ``` + +=== "Zig" + + ```zig title="recursion.zig" + [class]{}-[func]{tailRecur} + ``` + 两种递归的过程对比如下图所示。 - **普通递归**:求和操作是在“归”的过程中执行的,每层返回后都要再执行一次求和操作。 @@ -549,10 +549,10 @@ 按照递推关系进行递归调用,将前两个数字作为终止条件,便可写出递归代码。调用 `fib(n)` 即可得到斐波那契数列的第 $n$ 个数字。 -=== "Java" +=== "Python" - ```java title="recursion.java" - [class]{recursion}-[func]{fib} + ```python title="recursion.py" + [class]{}-[func]{fib} ``` === "C++" @@ -561,10 +561,16 @@ [class]{}-[func]{fib} ``` -=== "Python" +=== "Java" - ```python title="recursion.py" - [class]{}-[func]{fib} + ```java title="recursion.java" + [class]{recursion}-[func]{fib} + ``` + +=== "C#" + + ```csharp title="recursion.cs" + [class]{recursion}-[func]{fib} ``` === "Go" @@ -573,6 +579,12 @@ [class]{}-[func]{fib} ``` +=== "Swift" + + ```swift title="recursion.swift" + [class]{}-[func]{fib} + ``` + === "JS" ```javascript title="recursion.js" @@ -585,30 +597,6 @@ [class]{}-[func]{fib} ``` -=== "C" - - ```c title="recursion.c" - [class]{}-[func]{fib} - ``` - -=== "C#" - - ```csharp title="recursion.cs" - [class]{recursion}-[func]{fib} - ``` - -=== "Swift" - - ```swift title="recursion.swift" - [class]{}-[func]{fib} - ``` - -=== "Zig" - - ```zig title="recursion.zig" - [class]{}-[func]{fib} - ``` - === "Dart" ```dart title="recursion.dart" @@ -621,6 +609,18 @@ [class]{}-[func]{fib} ``` +=== "C" + + ```c title="recursion.c" + [class]{}-[func]{fib} + ``` + +=== "Zig" + + ```zig title="recursion.zig" + [class]{}-[func]{fib} + ``` + 观察以上代码,我们在函数内递归调用了两个函数,**这意味着从一个调用产生了两个调用分支**。如下图所示,这样不断递归调用下去,最终将产生一个层数为 $n$ 的「递归树 recursion tree」。 ![斐波那契数列的递归树](iteration_and_recursion.assets/recursion_tree.png) diff --git a/docs/chapter_computational_complexity/space_complexity.md b/docs/chapter_computational_complexity/space_complexity.md index 59ae22ea..8a7311dc 100755 --- a/docs/chapter_computational_complexity/space_complexity.md +++ b/docs/chapter_computational_complexity/space_complexity.md @@ -22,29 +22,26 @@ ![算法使用的相关空间](space_complexity.assets/space_types.png) -=== "Java" +=== "Python" - ```java title="" - /* 类 */ - class Node { - int val; - Node next; - Node(int x) { val = x; } - } - - /* 函数 */ - int function() { - // 执行某些操作... - return 0; - } - - int algorithm(int n) { // 输入数据 - final int a = 0; // 暂存数据(常量) - int b = 0; // 暂存数据(变量) - Node node = new Node(0); // 暂存数据(对象) - int c = function(); // 栈帧空间(调用函数) - return a + b + c; // 输出数据 - } + ```python title="" + class Node: + """类""" + def __init__(self, x: int): + self.val: int = x # 节点值 + self.next: Optional[Node] = None # 指向下一节点的引用 + + def function() -> int: + """函数""" + # 执行某些操作... + return 0 + + def algorithm(n) -> int: # 输入数据 + A = 0 # 暂存数据(常量,一般用大写字母表示) + b = 0 # 暂存数据(变量) + node = Node(0) # 暂存数据(对象) + c = function() # 栈帧空间(调用函数) + return A + b + c # 输出数据 ``` === "C++" @@ -72,26 +69,54 @@ } ``` -=== "Python" +=== "Java" - ```python title="" - class Node: - """类""" - def __init__(self, x: int): - self.val: int = x # 节点值 - self.next: Optional[Node] = None # 指向下一节点的引用 + ```java title="" + /* 类 */ + class Node { + int val; + Node next; + Node(int x) { val = x; } + } + + /* 函数 */ + int function() { + // 执行某些操作... + return 0; + } + + int algorithm(int n) { // 输入数据 + final int a = 0; // 暂存数据(常量) + int b = 0; // 暂存数据(变量) + Node node = new Node(0); // 暂存数据(对象) + int c = function(); // 栈帧空间(调用函数) + return a + b + c; // 输出数据 + } + ``` - def function() -> int: - """函数""" - # 执行某些操作... - return 0 +=== "C#" - def algorithm(n) -> int: # 输入数据 - A = 0 # 暂存数据(常量,一般用大写字母表示) - b = 0 # 暂存数据(变量) - node = Node(0) # 暂存数据(对象) - c = function() # 栈帧空间(调用函数) - return A + b + c # 输出数据 + ```csharp title="" + /* 类 */ + class Node { + int val; + Node next; + Node(int x) { val = x; } + } + + /* 函数 */ + int function() { + // 执行某些操作... + return 0; + } + + int algorithm(int n) { // 输入数据 + const int a = 0; // 暂存数据(常量) + int b = 0; // 暂存数据(变量) + Node node = new Node(0); // 暂存数据(对象) + int c = function(); // 栈帧空间(调用函数) + return a + b + c; // 输出数据 + } ``` === "Go" @@ -123,6 +148,34 @@ } ``` +=== "Swift" + + ```swift title="" + /* 类 */ + class Node { + var val: Int + var next: Node? + + init(x: Int) { + val = x + } + } + + /* 函数 */ + func function() -> Int { + // 执行某些操作... + return 0 + } + + func algorithm(n: Int) -> Int { // 输入数据 + let a = 0 // 暂存数据(常量) + var b = 0 // 暂存数据(变量) + let node = Node(x: 0) // 暂存数据(对象) + let c = function() // 栈帧空间(调用函数) + return a + b + c // 输出数据 + } + ``` + === "JS" ```javascript title="" @@ -179,82 +232,6 @@ } ``` -=== "C" - - ```c title="" - /* 函数 */ - int func() { - // 执行某些操作... - return 0; - } - - int algorithm(int n) { // 输入数据 - const int a = 0; // 暂存数据(常量) - int b = 0; // 暂存数据(变量) - int c = func(); // 栈帧空间(调用函数) - return a + b + c; // 输出数据 - } - ``` - -=== "C#" - - ```csharp title="" - /* 类 */ - class Node { - int val; - Node next; - Node(int x) { val = x; } - } - - /* 函数 */ - int function() { - // 执行某些操作... - return 0; - } - - int algorithm(int n) { // 输入数据 - const int a = 0; // 暂存数据(常量) - int b = 0; // 暂存数据(变量) - Node node = new Node(0); // 暂存数据(对象) - int c = function(); // 栈帧空间(调用函数) - return a + b + c; // 输出数据 - } - ``` - -=== "Swift" - - ```swift title="" - /* 类 */ - class Node { - var val: Int - var next: Node? - - init(x: Int) { - val = x - } - } - - /* 函数 */ - func function() -> Int { - // 执行某些操作... - return 0 - } - - func algorithm(n: Int) -> Int { // 输入数据 - let a = 0 // 暂存数据(常量) - var b = 0 // 暂存数据(变量) - let node = Node(x: 0) // 暂存数据(对象) - let c = function() // 栈帧空间(调用函数) - return a + b + c // 输出数据 - } - ``` - -=== "Zig" - - ```zig title="" - - ``` - === "Dart" ```dart title="" @@ -286,6 +263,29 @@ ``` +=== "C" + + ```c title="" + /* 函数 */ + int func() { + // 执行某些操作... + return 0; + } + + int algorithm(int n) { // 输入数据 + const int a = 0; // 暂存数据(常量) + int b = 0; // 暂存数据(变量) + int c = func(); // 栈帧空间(调用函数) + return a + b + c; // 输出数据 + } + ``` + +=== "Zig" + + ```zig title="" + + ``` + ## 推算方法 空间复杂度的推算方法与时间复杂度大致相同,只需将统计对象从“操作数量”转为“使用空间大小”。 @@ -297,15 +297,14 @@ 1. **以最差输入数据为准**:当 $n < 10$ 时,空间复杂度为 $O(1)$ ;但当 $n > 10$ 时,初始化的数组 `nums` 占用 $O(n)$ 空间;因此最差空间复杂度为 $O(n)$ 。 2. **以算法运行中的峰值内存为准**:例如,程序在执行最后一行之前,占用 $O(1)$ 空间;当初始化数组 `nums` 时,程序占用 $O(n)$ 空间;因此最差空间复杂度为 $O(n)$ 。 -=== "Java" +=== "Python" - ```java title="" - void algorithm(int n) { - int a = 0; // O(1) - int[] b = new int[10000]; // O(1) - if (n > 10) - int[] nums = new int[n]; // O(n) - } + ```python title="" + def algorithm(n: int): + a = 0 # O(1) + b = [0] * 10000 # O(1) + if n > 10: + nums = [0] * n # O(n) ``` === "C++" @@ -319,14 +318,27 @@ } ``` -=== "Python" +=== "Java" - ```python title="" - def algorithm(n: int): - a = 0 # O(1) - b = [0] * 10000 # O(1) - if n > 10: - nums = [0] * n # O(n) + ```java title="" + void algorithm(int n) { + int a = 0; // O(1) + int[] b = new int[10000]; // O(1) + if (n > 10) + int[] nums = new int[n]; // O(n) + } + ``` + +=== "C#" + + ```csharp title="" + void algorithm(int n) { + int a = 0; // O(1) + int[] b = new int[10000]; // O(1) + if (n > 10) { + int[] nums = new int[n]; // O(n) + } + } ``` === "Go" @@ -343,6 +355,18 @@ } ``` +=== "Swift" + + ```swift title="" + func algorithm(n: Int) { + let a = 0 // O(1) + let b = Array(repeating: 0, count: 10000) // O(1) + if n > 10 { + let nums = Array(repeating: 0, count: n) // O(n) + } + } + ``` + === "JS" ```javascript title="" @@ -367,47 +391,6 @@ } ``` -=== "C" - - ```c title="" - void algorithm(int n) { - int a = 0; // O(1) - int b[10000]; // O(1) - if (n > 10) - int nums[n] = {0}; // O(n) - } - ``` - -=== "C#" - - ```csharp title="" - void algorithm(int n) { - int a = 0; // O(1) - int[] b = new int[10000]; // O(1) - if (n > 10) { - int[] nums = new int[n]; // O(n) - } - } - ``` - -=== "Swift" - - ```swift title="" - func algorithm(n: Int) { - let a = 0 // O(1) - let b = Array(repeating: 0, count: 10000) // O(1) - if n > 10 { - let nums = Array(repeating: 0, count: n) // O(n) - } - } - ``` - -=== "Zig" - - ```zig title="" - - ``` - === "Dart" ```dart title="" @@ -426,29 +409,44 @@ ``` +=== "C" + + ```c title="" + void algorithm(int n) { + int a = 0; // O(1) + int b[10000]; // O(1) + if (n > 10) + int nums[n] = {0}; // O(n) + } + ``` + +=== "Zig" + + ```zig title="" + + ``` + **在递归函数中,需要注意统计栈帧空间**。例如在以下代码中: - 函数 `loop()` 在循环中调用了 $n$ 次 `function()` ,每轮中的 `function()` 都返回并释放了栈帧空间,因此空间复杂度仍为 $O(1)$ 。 - 递归函数 `recur()` 在运行过程中会同时存在 $n$ 个未返回的 `recur()` ,从而占用 $O(n)$ 的栈帧空间。 -=== "Java" +=== "Python" - ```java title="" - int function() { - // 执行某些操作 - return 0; - } - /* 循环 O(1) */ - void loop(int n) { - for (int i = 0; i < n; i++) { - function(); - } - } - /* 递归 O(n) */ - void recur(int n) { - if (n == 1) return; - return recur(n - 1); - } + ```python title="" + def function() -> int: + # 执行某些操作 + return 0 + + def loop(n: int): + """循环 O(1)""" + for _ in range(n): + function() + + def recur(n: int) -> int: + """递归 O(n)""" + if n == 1: return + return recur(n - 1) ``` === "C++" @@ -471,22 +469,44 @@ } ``` -=== "Python" +=== "Java" - ```python title="" - def function() -> int: - # 执行某些操作 - return 0 + ```java title="" + int function() { + // 执行某些操作 + return 0; + } + /* 循环 O(1) */ + void loop(int n) { + for (int i = 0; i < n; i++) { + function(); + } + } + /* 递归 O(n) */ + void recur(int n) { + if (n == 1) return; + return recur(n - 1); + } + ``` - def loop(n: int): - """循环 O(1)""" - for _ in range(n): - function() +=== "C#" - def recur(n: int) -> int: - """递归 O(n)""" - if n == 1: return - return recur(n - 1) + ```csharp title="" + int function() { + // 执行某些操作 + return 0; + } + /* 循环 O(1) */ + void loop(int n) { + for (int i = 0; i < n; i++) { + function(); + } + } + /* 递归 O(n) */ + int recur(int n) { + if (n == 1) return 1; + return recur(n - 1); + } ``` === "Go" @@ -513,6 +533,31 @@ } ``` +=== "Swift" + + ```swift title="" + @discardableResult + func function() -> Int { + // 执行某些操作 + return 0 + } + + /* 循环 O(1) */ + func loop(n: Int) { + for _ in 0 ..< n { + function() + } + } + + /* 递归 O(n) */ + func recur(n: Int) { + if n == 1 { + return + } + recur(n: n - 1) + } + ``` + === "JS" ```javascript title="" @@ -553,77 +598,6 @@ } ``` -=== "C" - - ```c title="" - int func() { - // 执行某些操作 - return 0; - } - /* 循环 O(1) */ - void loop(int n) { - for (int i = 0; i < n; i++) { - func(); - } - } - /* 递归 O(n) */ - void recur(int n) { - if (n == 1) return; - return recur(n - 1); - } - ``` - -=== "C#" - - ```csharp title="" - int function() { - // 执行某些操作 - return 0; - } - /* 循环 O(1) */ - void loop(int n) { - for (int i = 0; i < n; i++) { - function(); - } - } - /* 递归 O(n) */ - int recur(int n) { - if (n == 1) return 1; - return recur(n - 1); - } - ``` - -=== "Swift" - - ```swift title="" - @discardableResult - func function() -> Int { - // 执行某些操作 - return 0 - } - - /* 循环 O(1) */ - func loop(n: Int) { - for _ in 0 ..< n { - function() - } - } - - /* 递归 O(n) */ - func recur(n: Int) { - if n == 1 { - return - } - recur(n: n - 1) - } - ``` - -=== "Zig" - - ```zig title="" - - ``` - === "Dart" ```dart title="" @@ -650,6 +624,32 @@ ``` +=== "C" + + ```c title="" + int func() { + // 执行某些操作 + return 0; + } + /* 循环 O(1) */ + void loop(int n) { + for (int i = 0; i < n; i++) { + func(); + } + } + /* 递归 O(n) */ + void recur(int n) { + if (n == 1) return; + return recur(n - 1); + } + ``` + +=== "Zig" + + ```zig title="" + + ``` + ## 常见类型 设输入数据大小为 $n$ ,下图展示了常见的空间复杂度类型(从低到高排列)。 @@ -669,12 +669,12 @@ $$ 需要注意的是,在循环中初始化变量或调用函数而占用的内存,在进入下一循环后就会被释放,因此不会累积占用空间,空间复杂度仍为 $O(1)$ : -=== "Java" +=== "Python" - ```java title="space_complexity.java" - [class]{space_complexity}-[func]{function} + ```python title="space_complexity.py" + [class]{}-[func]{function} - [class]{space_complexity}-[func]{constant} + [class]{}-[func]{constant} ``` === "C++" @@ -685,12 +685,20 @@ $$ [class]{}-[func]{constant} ``` -=== "Python" +=== "Java" - ```python title="space_complexity.py" - [class]{}-[func]{function} + ```java title="space_complexity.java" + [class]{space_complexity}-[func]{function} - [class]{}-[func]{constant} + [class]{space_complexity}-[func]{constant} + ``` + +=== "C#" + + ```csharp title="space_complexity.cs" + [class]{space_complexity}-[func]{function} + + [class]{space_complexity}-[func]{constant} ``` === "Go" @@ -701,6 +709,14 @@ $$ [class]{}-[func]{spaceConstant} ``` +=== "Swift" + + ```swift title="space_complexity.swift" + [class]{}-[func]{function} + + [class]{}-[func]{constant} + ``` + === "JS" ```javascript title="space_complexity.js" @@ -717,38 +733,6 @@ $$ [class]{}-[func]{constant} ``` -=== "C" - - ```c title="space_complexity.c" - [class]{}-[func]{func} - - [class]{}-[func]{constant} - ``` - -=== "C#" - - ```csharp title="space_complexity.cs" - [class]{space_complexity}-[func]{function} - - [class]{space_complexity}-[func]{constant} - ``` - -=== "Swift" - - ```swift title="space_complexity.swift" - [class]{}-[func]{function} - - [class]{}-[func]{constant} - ``` - -=== "Zig" - - ```zig title="space_complexity.zig" - [class]{}-[func]{function} - - [class]{}-[func]{constant} - ``` - === "Dart" ```dart title="space_complexity.dart" @@ -765,14 +749,30 @@ $$ [class]{}-[func]{constant} ``` +=== "C" + + ```c title="space_complexity.c" + [class]{}-[func]{func} + + [class]{}-[func]{constant} + ``` + +=== "Zig" + + ```zig title="space_complexity.zig" + [class]{}-[func]{function} + + [class]{}-[func]{constant} + ``` + ### 线性阶 $O(n)$ 线性阶常见于元素数量与 $n$ 成正比的数组、链表、栈、队列等: -=== "Java" +=== "Python" - ```java title="space_complexity.java" - [class]{space_complexity}-[func]{linear} + ```python title="space_complexity.py" + [class]{}-[func]{linear} ``` === "C++" @@ -781,10 +781,16 @@ $$ [class]{}-[func]{linear} ``` -=== "Python" +=== "Java" - ```python title="space_complexity.py" - [class]{}-[func]{linear} + ```java title="space_complexity.java" + [class]{space_complexity}-[func]{linear} + ``` + +=== "C#" + + ```csharp title="space_complexity.cs" + [class]{space_complexity}-[func]{linear} ``` === "Go" @@ -793,6 +799,12 @@ $$ [class]{}-[func]{spaceLinear} ``` +=== "Swift" + + ```swift title="space_complexity.swift" + [class]{}-[func]{linear} + ``` + === "JS" ```javascript title="space_complexity.js" @@ -805,6 +817,18 @@ $$ [class]{}-[func]{linear} ``` +=== "Dart" + + ```dart title="space_complexity.dart" + [class]{}-[func]{linear} + ``` + +=== "Rust" + + ```rust title="space_complexity.rs" + [class]{}-[func]{linear} + ``` + === "C" ```c title="space_complexity.c" @@ -813,42 +837,18 @@ $$ [class]{}-[func]{linear} ``` -=== "C#" - - ```csharp title="space_complexity.cs" - [class]{space_complexity}-[func]{linear} - ``` - -=== "Swift" - - ```swift title="space_complexity.swift" - [class]{}-[func]{linear} - ``` - === "Zig" ```zig title="space_complexity.zig" [class]{}-[func]{linear} ``` -=== "Dart" - - ```dart title="space_complexity.dart" - [class]{}-[func]{linear} - ``` - -=== "Rust" - - ```rust title="space_complexity.rs" - [class]{}-[func]{linear} - ``` - 如下图所示,此函数的递归深度为 $n$ ,即同时存在 $n$ 个未返回的 `linear_recur()` 函数,使用 $O(n)$ 大小的栈帧空间: -=== "Java" +=== "Python" - ```java title="space_complexity.java" - [class]{space_complexity}-[func]{linearRecur} + ```python title="space_complexity.py" + [class]{}-[func]{linear_recur} ``` === "C++" @@ -857,10 +857,16 @@ $$ [class]{}-[func]{linearRecur} ``` -=== "Python" +=== "Java" - ```python title="space_complexity.py" - [class]{}-[func]{linear_recur} + ```java title="space_complexity.java" + [class]{space_complexity}-[func]{linearRecur} + ``` + +=== "C#" + + ```csharp title="space_complexity.cs" + [class]{space_complexity}-[func]{linearRecur} ``` === "Go" @@ -869,6 +875,12 @@ $$ [class]{}-[func]{spaceLinearRecur} ``` +=== "Swift" + + ```swift title="space_complexity.swift" + [class]{}-[func]{linearRecur} + ``` + === "JS" ```javascript title="space_complexity.js" @@ -881,30 +893,6 @@ $$ [class]{}-[func]{linearRecur} ``` -=== "C" - - ```c title="space_complexity.c" - [class]{}-[func]{linearRecur} - ``` - -=== "C#" - - ```csharp title="space_complexity.cs" - [class]{space_complexity}-[func]{linearRecur} - ``` - -=== "Swift" - - ```swift title="space_complexity.swift" - [class]{}-[func]{linearRecur} - ``` - -=== "Zig" - - ```zig title="space_complexity.zig" - [class]{}-[func]{linearRecur} - ``` - === "Dart" ```dart title="space_complexity.dart" @@ -917,16 +905,28 @@ $$ [class]{}-[func]{linear_recur} ``` +=== "C" + + ```c title="space_complexity.c" + [class]{}-[func]{linearRecur} + ``` + +=== "Zig" + + ```zig title="space_complexity.zig" + [class]{}-[func]{linearRecur} + ``` + ![递归函数产生的线性阶空间复杂度](space_complexity.assets/space_complexity_recursive_linear.png) ### 平方阶 $O(n^2)$ 平方阶常见于矩阵和图,元素数量与 $n$ 成平方关系: -=== "Java" +=== "Python" - ```java title="space_complexity.java" - [class]{space_complexity}-[func]{quadratic} + ```python title="space_complexity.py" + [class]{}-[func]{quadratic} ``` === "C++" @@ -935,10 +935,16 @@ $$ [class]{}-[func]{quadratic} ``` -=== "Python" +=== "Java" - ```python title="space_complexity.py" - [class]{}-[func]{quadratic} + ```java title="space_complexity.java" + [class]{space_complexity}-[func]{quadratic} + ``` + +=== "C#" + + ```csharp title="space_complexity.cs" + [class]{space_complexity}-[func]{quadratic} ``` === "Go" @@ -947,6 +953,12 @@ $$ [class]{}-[func]{spaceQuadratic} ``` +=== "Swift" + + ```swift title="space_complexity.swift" + [class]{}-[func]{quadratic} + ``` + === "JS" ```javascript title="space_complexity.js" @@ -959,30 +971,6 @@ $$ [class]{}-[func]{quadratic} ``` -=== "C" - - ```c title="space_complexity.c" - [class]{}-[func]{quadratic} - ``` - -=== "C#" - - ```csharp title="space_complexity.cs" - [class]{space_complexity}-[func]{quadratic} - ``` - -=== "Swift" - - ```swift title="space_complexity.swift" - [class]{}-[func]{quadratic} - ``` - -=== "Zig" - - ```zig title="space_complexity.zig" - [class]{}-[func]{quadratic} - ``` - === "Dart" ```dart title="space_complexity.dart" @@ -995,12 +983,24 @@ $$ [class]{}-[func]{quadratic} ``` +=== "C" + + ```c title="space_complexity.c" + [class]{}-[func]{quadratic} + ``` + +=== "Zig" + + ```zig title="space_complexity.zig" + [class]{}-[func]{quadratic} + ``` + 如下图所示,该函数的递归深度为 $n$ ,在每个递归函数中都初始化了一个数组,长度分别为 $n$、$n-1$、$\dots$、$2$、$1$ ,平均长度为 $n / 2$ ,因此总体占用 $O(n^2)$ 空间: -=== "Java" +=== "Python" - ```java title="space_complexity.java" - [class]{space_complexity}-[func]{quadraticRecur} + ```python title="space_complexity.py" + [class]{}-[func]{quadratic_recur} ``` === "C++" @@ -1009,10 +1009,16 @@ $$ [class]{}-[func]{quadraticRecur} ``` -=== "Python" +=== "Java" - ```python title="space_complexity.py" - [class]{}-[func]{quadratic_recur} + ```java title="space_complexity.java" + [class]{space_complexity}-[func]{quadraticRecur} + ``` + +=== "C#" + + ```csharp title="space_complexity.cs" + [class]{space_complexity}-[func]{quadraticRecur} ``` === "Go" @@ -1021,6 +1027,12 @@ $$ [class]{}-[func]{spaceQuadraticRecur} ``` +=== "Swift" + + ```swift title="space_complexity.swift" + [class]{}-[func]{quadraticRecur} + ``` + === "JS" ```javascript title="space_complexity.js" @@ -1033,30 +1045,6 @@ $$ [class]{}-[func]{quadraticRecur} ``` -=== "C" - - ```c title="space_complexity.c" - [class]{}-[func]{quadraticRecur} - ``` - -=== "C#" - - ```csharp title="space_complexity.cs" - [class]{space_complexity}-[func]{quadraticRecur} - ``` - -=== "Swift" - - ```swift title="space_complexity.swift" - [class]{}-[func]{quadraticRecur} - ``` - -=== "Zig" - - ```zig title="space_complexity.zig" - [class]{}-[func]{quadraticRecur} - ``` - === "Dart" ```dart title="space_complexity.dart" @@ -1069,16 +1057,28 @@ $$ [class]{}-[func]{quadratic_recur} ``` +=== "C" + + ```c title="space_complexity.c" + [class]{}-[func]{quadraticRecur} + ``` + +=== "Zig" + + ```zig title="space_complexity.zig" + [class]{}-[func]{quadraticRecur} + ``` + ![递归函数产生的平方阶空间复杂度](space_complexity.assets/space_complexity_recursive_quadratic.png) ### 指数阶 $O(2^n)$ 指数阶常见于二叉树。观察下图,高度为 $n$ 的“满二叉树”的节点数量为 $2^n - 1$ ,占用 $O(2^n)$ 空间: -=== "Java" +=== "Python" - ```java title="space_complexity.java" - [class]{space_complexity}-[func]{buildTree} + ```python title="space_complexity.py" + [class]{}-[func]{build_tree} ``` === "C++" @@ -1087,10 +1087,16 @@ $$ [class]{}-[func]{buildTree} ``` -=== "Python" +=== "Java" - ```python title="space_complexity.py" - [class]{}-[func]{build_tree} + ```java title="space_complexity.java" + [class]{space_complexity}-[func]{buildTree} + ``` + +=== "C#" + + ```csharp title="space_complexity.cs" + [class]{space_complexity}-[func]{buildTree} ``` === "Go" @@ -1099,6 +1105,12 @@ $$ [class]{}-[func]{buildTree} ``` +=== "Swift" + + ```swift title="space_complexity.swift" + [class]{}-[func]{buildTree} + ``` + === "JS" ```javascript title="space_complexity.js" @@ -1111,30 +1123,6 @@ $$ [class]{}-[func]{buildTree} ``` -=== "C" - - ```c title="space_complexity.c" - [class]{}-[func]{buildTree} - ``` - -=== "C#" - - ```csharp title="space_complexity.cs" - [class]{space_complexity}-[func]{buildTree} - ``` - -=== "Swift" - - ```swift title="space_complexity.swift" - [class]{}-[func]{buildTree} - ``` - -=== "Zig" - - ```zig title="space_complexity.zig" - [class]{}-[func]{buildTree} - ``` - === "Dart" ```dart title="space_complexity.dart" @@ -1147,6 +1135,18 @@ $$ [class]{}-[func]{build_tree} ``` +=== "C" + + ```c title="space_complexity.c" + [class]{}-[func]{buildTree} + ``` + +=== "Zig" + + ```zig title="space_complexity.zig" + [class]{}-[func]{buildTree} + ``` + ![满二叉树产生的指数阶空间复杂度](space_complexity.assets/space_complexity_exponential.png) ### 对数阶 $O(\log n)$ diff --git a/docs/chapter_computational_complexity/time_complexity.md b/docs/chapter_computational_complexity/time_complexity.md index 7a752e33..4af1fd61 100755 --- a/docs/chapter_computational_complexity/time_complexity.md +++ b/docs/chapter_computational_complexity/time_complexity.md @@ -8,19 +8,17 @@ 例如在以下代码中,输入数据大小为 $n$ : -=== "Java" +=== "Python" - ```java title="" - // 在某运行平台下 - void algorithm(int n) { - int a = 2; // 1 ns - a = a + 1; // 1 ns - a = a * 2; // 10 ns - // 循环 n 次 - for (int i = 0; i < n; i++) { // 1 ns ,每轮都要执行 i++ - System.out.println(0); // 5 ns - } - } + ```python title="" + # 在某运行平台下 + def algorithm(n: int): + a = 2 # 1 ns + a = a + 1 # 1 ns + a = a * 2 # 10 ns + # 循环 n 次 + for _ in range(n): # 1 ns + print(0) # 5 ns ``` === "C++" @@ -38,17 +36,34 @@ } ``` -=== "Python" +=== "Java" - ```python title="" - # 在某运行平台下 - def algorithm(n: int): - a = 2 # 1 ns - a = a + 1 # 1 ns - a = a * 2 # 10 ns - # 循环 n 次 - for _ in range(n): # 1 ns - print(0) # 5 ns + ```java title="" + // 在某运行平台下 + void algorithm(int n) { + int a = 2; // 1 ns + a = a + 1; // 1 ns + a = a * 2; // 10 ns + // 循环 n 次 + for (int i = 0; i < n; i++) { // 1 ns ,每轮都要执行 i++ + System.out.println(0); // 5 ns + } + } + ``` + +=== "C#" + + ```csharp title="" + // 在某运行平台下 + void algorithm(int n) { + int a = 2; // 1 ns + a = a + 1; // 1 ns + a = a * 2; // 10 ns + // 循环 n 次 + for (int i = 0; i < n; i++) { // 1 ns ,每轮都要执行 i++ + Console.WriteLine(0); // 5 ns + } + } ``` === "Go" @@ -66,6 +81,21 @@ } ``` +=== "Swift" + + ```swift title="" + // 在某运行平台下 + func algorithm(n: Int) { + var a = 2 // 1 ns + a = a + 1 // 1 ns + a = a * 2 // 10 ns + // 循环 n 次 + for _ in 0 ..< n { // 1 ns + print(0) // 5 ns + } + } + ``` + === "JS" ```javascript title="" @@ -96,57 +126,6 @@ } ``` -=== "C" - - ```c title="" - // 在某运行平台下 - void algorithm(int n) { - int a = 2; // 1 ns - a = a + 1; // 1 ns - a = a * 2; // 10 ns - // 循环 n 次 - for (int i = 0; i < n; i++) { // 1 ns ,每轮都要执行 i++ - printf("%d", 0); // 5 ns - } - } - ``` - -=== "C#" - - ```csharp title="" - // 在某运行平台下 - void algorithm(int n) { - int a = 2; // 1 ns - a = a + 1; // 1 ns - a = a * 2; // 10 ns - // 循环 n 次 - for (int i = 0; i < n; i++) { // 1 ns ,每轮都要执行 i++ - Console.WriteLine(0); // 5 ns - } - } - ``` - -=== "Swift" - - ```swift title="" - // 在某运行平台下 - func algorithm(n: Int) { - var a = 2 // 1 ns - a = a + 1 // 1 ns - a = a * 2 // 10 ns - // 循环 n 次 - for _ in 0 ..< n { // 1 ns - print(0) // 5 ns - } - } - ``` - -=== "Zig" - - ```zig title="" - - ``` - === "Dart" ```dart title="" @@ -177,6 +156,27 @@ } ``` +=== "C" + + ```c title="" + // 在某运行平台下 + void algorithm(int n) { + int a = 2; // 1 ns + a = a + 1; // 1 ns + a = a * 2; // 10 ns + // 循环 n 次 + for (int i = 0; i < n; i++) { // 1 ns ,每轮都要执行 i++ + printf("%d", 0); // 5 ns + } + } + ``` + +=== "Zig" + + ```zig title="" + + ``` + 根据以上方法,可以得到算法运行时间为 $6n + 12$ ns : $$ @@ -191,25 +191,20 @@ $$ “时间增长趋势”这个概念比较抽象,我们通过一个例子来加以理解。假设输入数据大小为 $n$ ,给定三个算法函数 `A`、`B` 和 `C` : -=== "Java" +=== "Python" - ```java title="" - // 算法 A 的时间复杂度:常数阶 - void algorithm_A(int n) { - System.out.println(0); - } - // 算法 B 的时间复杂度:线性阶 - void algorithm_B(int n) { - for (int i = 0; i < n; i++) { - System.out.println(0); - } - } - // 算法 C 的时间复杂度:常数阶 - void algorithm_C(int n) { - for (int i = 0; i < 1000000; i++) { - System.out.println(0); - } - } + ```python title="" + # 算法 A 的时间复杂度:常数阶 + def algorithm_A(n: int): + print(0) + # 算法 B 的时间复杂度:线性阶 + def algorithm_B(n: int): + for _ in range(n): + print(0) + # 算法 C 的时间复杂度:常数阶 + def algorithm_C(n: int): + for _ in range(1000000): + print(0) ``` === "C++" @@ -233,20 +228,46 @@ $$ } ``` -=== "Python" +=== "Java" - ```python title="" - # 算法 A 的时间复杂度:常数阶 - def algorithm_A(n: int): - print(0) - # 算法 B 的时间复杂度:线性阶 - def algorithm_B(n: int): - for _ in range(n): - print(0) - # 算法 C 的时间复杂度:常数阶 - def algorithm_C(n: int): - for _ in range(1000000): - print(0) + ```java title="" + // 算法 A 的时间复杂度:常数阶 + void algorithm_A(int n) { + System.out.println(0); + } + // 算法 B 的时间复杂度:线性阶 + void algorithm_B(int n) { + for (int i = 0; i < n; i++) { + System.out.println(0); + } + } + // 算法 C 的时间复杂度:常数阶 + void algorithm_C(int n) { + for (int i = 0; i < 1000000; i++) { + System.out.println(0); + } + } + ``` + +=== "C#" + + ```csharp title="" + // 算法 A 的时间复杂度:常数阶 + void algorithm_A(int n) { + Console.WriteLine(0); + } + // 算法 B 的时间复杂度:线性阶 + void algorithm_B(int n) { + for (int i = 0; i < n; i++) { + Console.WriteLine(0); + } + } + // 算法 C 的时间复杂度:常数阶 + void algorithm_C(int n) { + for (int i = 0; i < 1000000; i++) { + Console.WriteLine(0); + } + } ``` === "Go" @@ -270,6 +291,29 @@ $$ } ``` +=== "Swift" + + ```swift title="" + // 算法 A 的时间复杂度:常数阶 + func algorithmA(n: Int) { + print(0) + } + + // 算法 B 的时间复杂度:线性阶 + func algorithmB(n: Int) { + for _ in 0 ..< n { + print(0) + } + } + + // 算法 C 的时间复杂度:常数阶 + func algorithmC(n: Int) { + for _ in 0 ..< 1000000 { + print(0) + } + } + ``` + === "JS" ```javascript title="" @@ -313,77 +357,6 @@ $$ } ``` -=== "C" - - ```c title="" - // 算法 A 的时间复杂度:常数阶 - void algorithm_A(int n) { - printf("%d", 0); - } - // 算法 B 的时间复杂度:线性阶 - void algorithm_B(int n) { - for (int i = 0; i < n; i++) { - printf("%d", 0); - } - } - // 算法 C 的时间复杂度:常数阶 - void algorithm_C(int n) { - for (int i = 0; i < 1000000; i++) { - printf("%d", 0); - } - } - ``` - -=== "C#" - - ```csharp title="" - // 算法 A 的时间复杂度:常数阶 - void algorithm_A(int n) { - Console.WriteLine(0); - } - // 算法 B 的时间复杂度:线性阶 - void algorithm_B(int n) { - for (int i = 0; i < n; i++) { - Console.WriteLine(0); - } - } - // 算法 C 的时间复杂度:常数阶 - void algorithm_C(int n) { - for (int i = 0; i < 1000000; i++) { - Console.WriteLine(0); - } - } - ``` - -=== "Swift" - - ```swift title="" - // 算法 A 的时间复杂度:常数阶 - func algorithmA(n: Int) { - print(0) - } - - // 算法 B 的时间复杂度:线性阶 - func algorithmB(n: Int) { - for _ in 0 ..< n { - print(0) - } - } - - // 算法 C 的时间复杂度:常数阶 - func algorithmC(n: Int) { - for _ in 0 ..< 1000000 { - print(0) - } - } - ``` - -=== "Zig" - - ```zig title="" - - ``` - === "Dart" ```dart title="" @@ -426,6 +399,33 @@ $$ } ``` +=== "C" + + ```c title="" + // 算法 A 的时间复杂度:常数阶 + void algorithm_A(int n) { + printf("%d", 0); + } + // 算法 B 的时间复杂度:线性阶 + void algorithm_B(int n) { + for (int i = 0; i < n; i++) { + printf("%d", 0); + } + } + // 算法 C 的时间复杂度:常数阶 + void algorithm_C(int n) { + for (int i = 0; i < 1000000; i++) { + printf("%d", 0); + } + } + ``` + +=== "Zig" + + ```zig title="" + + ``` + 下图展示了以上三个算法函数的时间复杂度。 - 算法 `A` 只有 $1$ 个打印操作,算法运行时间不随着 $n$ 增大而增长。我们称此算法的时间复杂度为“常数阶”。 @@ -444,18 +444,16 @@ $$ 给定一个输入大小为 $n$ 的函数: -=== "Java" +=== "Python" - ```java title="" - void algorithm(int n) { - int a = 1; // +1 - a = a + 1; // +1 - a = a * 2; // +1 - // 循环 n 次 - for (int i = 0; i < n; i++) { // +1(每轮都执行 i ++) - System.out.println(0); // +1 - } - } + ```python title="" + def algorithm(n: int): + a = 1 # +1 + a = a + 1 # +1 + a = a * 2 # +1 + # 循环 n 次 + for i in range(n): # +1 + print(0) # +1 ``` === "C++" @@ -472,16 +470,32 @@ $$ } ``` -=== "Python" +=== "Java" - ```python title="" - def algorithm(n: int): - a = 1 # +1 - a = a + 1 # +1 - a = a * 2 # +1 - # 循环 n 次 - for i in range(n): # +1 - print(0) # +1 + ```java title="" + void algorithm(int n) { + int a = 1; // +1 + a = a + 1; // +1 + a = a * 2; // +1 + // 循环 n 次 + for (int i = 0; i < n; i++) { // +1(每轮都执行 i ++) + System.out.println(0); // +1 + } + } + ``` + +=== "C#" + + ```csharp title="" + void algorithm(int n) { + int a = 1; // +1 + a = a + 1; // +1 + a = a * 2; // +1 + // 循环 n 次 + for (int i = 0; i < n; i++) { // +1(每轮都执行 i ++) + Console.WriteLine(0); // +1 + } + } ``` === "Go" @@ -498,6 +512,20 @@ $$ } ``` +=== "Swift" + + ```swift title="" + func algorithm(n: Int) { + var a = 1 // +1 + a = a + 1 // +1 + a = a * 2 // +1 + // 循环 n 次 + for _ in 0 ..< n { // +1 + print(0) // +1 + } + } + ``` + === "JS" ```javascript title="" @@ -526,54 +554,6 @@ $$ } ``` -=== "C" - - ```c title="" - void algorithm(int n) { - int a = 1; // +1 - a = a + 1; // +1 - a = a * 2; // +1 - // 循环 n 次 - for (int i = 0; i < n; i++) { // +1(每轮都执行 i ++) - printf("%d", 0); // +1 - } - } - ``` - -=== "C#" - - ```csharp title="" - void algorithm(int n) { - int a = 1; // +1 - a = a + 1; // +1 - a = a * 2; // +1 - // 循环 n 次 - for (int i = 0; i < n; i++) { // +1(每轮都执行 i ++) - Console.WriteLine(0); // +1 - } - } - ``` - -=== "Swift" - - ```swift title="" - func algorithm(n: Int) { - var a = 1 // +1 - a = a + 1 // +1 - a = a * 2 // +1 - // 循环 n 次 - for _ in 0 ..< n { // +1 - print(0) // +1 - } - } - ``` - -=== "Zig" - - ```zig title="" - - ``` - === "Dart" ```dart title="" @@ -603,6 +583,26 @@ $$ } ``` +=== "C" + + ```c title="" + void algorithm(int n) { + int a = 1; // +1 + a = a + 1; // +1 + a = a * 2; // +1 + // 循环 n 次 + for (int i = 0; i < n; i++) { // +1(每轮都执行 i ++) + printf("%d", 0); // +1 + } + } + ``` + +=== "Zig" + + ```zig title="" + + ``` + 设算法的操作数量是一个关于输入数据大小 $n$ 的函数,记为 $T(n)$ ,则以上函数的的操作数量为: $$ @@ -639,23 +639,19 @@ $T(n)$ 是一次函数,说明其运行时间的增长趋势是线性的,因 给定一个函数,我们可以用上述技巧来统计操作数量。 -=== "Java" +=== "Python" - ```java title="" - void algorithm(int n) { - int a = 1; // +0(技巧 1) - a = a + n; // +0(技巧 1) - // +n(技巧 2) - for (int i = 0; i < 5 * n + 1; i++) { - System.out.println(0); - } - // +n*n(技巧 3) - for (int i = 0; i < 2 * n; i++) { - for (int j = 0; j < n + 1; j++) { - System.out.println(0); - } - } - } + ```python title="" + def algorithm(n: int): + a = 1 # +0(技巧 1) + a = a + n # +0(技巧 1) + # +n(技巧 2) + for i in range(5 * n + 1): + print(0) + # +n*n(技巧 3) + for i in range(2 * n): + for j in range(n + 1): + print(0) ``` === "C++" @@ -677,19 +673,42 @@ $T(n)$ 是一次函数,说明其运行时间的增长趋势是线性的,因 } ``` -=== "Python" +=== "Java" - ```python title="" - def algorithm(n: int): - a = 1 # +0(技巧 1) - a = a + n # +0(技巧 1) - # +n(技巧 2) - for i in range(5 * n + 1): - print(0) - # +n*n(技巧 3) - for i in range(2 * n): - for j in range(n + 1): - print(0) + ```java title="" + void algorithm(int n) { + int a = 1; // +0(技巧 1) + a = a + n; // +0(技巧 1) + // +n(技巧 2) + for (int i = 0; i < 5 * n + 1; i++) { + System.out.println(0); + } + // +n*n(技巧 3) + for (int i = 0; i < 2 * n; i++) { + for (int j = 0; j < n + 1; j++) { + System.out.println(0); + } + } + } + ``` + +=== "C#" + + ```csharp title="" + void algorithm(int n) { + int a = 1; // +0(技巧 1) + a = a + n; // +0(技巧 1) + // +n(技巧 2) + for (int i = 0; i < 5 * n + 1; i++) { + Console.WriteLine(0); + } + // +n*n(技巧 3) + for (int i = 0; i < 2 * n; i++) { + for (int j = 0; j < n + 1; j++) { + Console.WriteLine(0); + } + } + } ``` === "Go" @@ -711,6 +730,25 @@ $T(n)$ 是一次函数,说明其运行时间的增长趋势是线性的,因 } ``` +=== "Swift" + + ```swift title="" + func algorithm(n: Int) { + var a = 1 // +0(技巧 1) + a = a + n // +0(技巧 1) + // +n(技巧 2) + for _ in 0 ..< (5 * n + 1) { + print(0) + } + // +n*n(技巧 3) + for _ in 0 ..< (2 * n) { + for _ in 0 ..< (n + 1) { + print(0) + } + } + } + ``` + === "JS" ```javascript title="" @@ -749,69 +787,6 @@ $T(n)$ 是一次函数,说明其运行时间的增长趋势是线性的,因 } ``` -=== "C" - - ```c title="" - void algorithm(int n) { - int a = 1; // +0(技巧 1) - a = a + n; // +0(技巧 1) - // +n(技巧 2) - for (int i = 0; i < 5 * n + 1; i++) { - printf("%d", 0); - } - // +n*n(技巧 3) - for (int i = 0; i < 2 * n; i++) { - for (int j = 0; j < n + 1; j++) { - printf("%d", 0); - } - } - } - ``` - -=== "C#" - - ```csharp title="" - void algorithm(int n) { - int a = 1; // +0(技巧 1) - a = a + n; // +0(技巧 1) - // +n(技巧 2) - for (int i = 0; i < 5 * n + 1; i++) { - Console.WriteLine(0); - } - // +n*n(技巧 3) - for (int i = 0; i < 2 * n; i++) { - for (int j = 0; j < n + 1; j++) { - Console.WriteLine(0); - } - } - } - ``` - -=== "Swift" - - ```swift title="" - func algorithm(n: Int) { - var a = 1 // +0(技巧 1) - a = a + n // +0(技巧 1) - // +n(技巧 2) - for _ in 0 ..< (5 * n + 1) { - print(0) - } - // +n*n(技巧 3) - for _ in 0 ..< (2 * n) { - for _ in 0 ..< (n + 1) { - print(0) - } - } - } - ``` - -=== "Zig" - - ```zig title="" - - ``` - === "Dart" ```dart title="" @@ -852,6 +827,31 @@ $T(n)$ 是一次函数,说明其运行时间的增长趋势是线性的,因 } ``` +=== "C" + + ```c title="" + void algorithm(int n) { + int a = 1; // +0(技巧 1) + a = a + n; // +0(技巧 1) + // +n(技巧 2) + for (int i = 0; i < 5 * n + 1; i++) { + printf("%d", 0); + } + // +n*n(技巧 3) + for (int i = 0; i < 2 * n; i++) { + for (int j = 0; j < n + 1; j++) { + printf("%d", 0); + } + } + } + ``` + +=== "Zig" + + ```zig title="" + + ``` + 以下公式展示了使用上述技巧前后的统计结果,两者推出的时间复杂度都为 $O(n^2)$ 。 $$ @@ -897,10 +897,10 @@ $$ 在以下函数中,尽管操作数量 `size` 可能很大,但由于其与输入数据大小 $n$ 无关,因此时间复杂度仍为 $O(1)$ : -=== "Java" +=== "Python" - ```java title="time_complexity.java" - [class]{time_complexity}-[func]{constant} + ```python title="time_complexity.py" + [class]{}-[func]{constant} ``` === "C++" @@ -909,10 +909,16 @@ $$ [class]{}-[func]{constant} ``` -=== "Python" +=== "Java" - ```python title="time_complexity.py" - [class]{}-[func]{constant} + ```java title="time_complexity.java" + [class]{time_complexity}-[func]{constant} + ``` + +=== "C#" + + ```csharp title="time_complexity.cs" + [class]{time_complexity}-[func]{constant} ``` === "Go" @@ -921,6 +927,12 @@ $$ [class]{}-[func]{constant} ``` +=== "Swift" + + ```swift title="time_complexity.swift" + [class]{}-[func]{constant} + ``` + === "JS" ```javascript title="time_complexity.js" @@ -933,30 +945,6 @@ $$ [class]{}-[func]{constant} ``` -=== "C" - - ```c title="time_complexity.c" - [class]{}-[func]{constant} - ``` - -=== "C#" - - ```csharp title="time_complexity.cs" - [class]{time_complexity}-[func]{constant} - ``` - -=== "Swift" - - ```swift title="time_complexity.swift" - [class]{}-[func]{constant} - ``` - -=== "Zig" - - ```zig title="time_complexity.zig" - [class]{}-[func]{constant} - ``` - === "Dart" ```dart title="time_complexity.dart" @@ -969,14 +957,26 @@ $$ [class]{}-[func]{constant} ``` +=== "C" + + ```c title="time_complexity.c" + [class]{}-[func]{constant} + ``` + +=== "Zig" + + ```zig title="time_complexity.zig" + [class]{}-[func]{constant} + ``` + ### 线性阶 $O(n)$ 线性阶的操作数量相对于输入数据大小 $n$ 以线性级别增长。线性阶通常出现在单层循环中: -=== "Java" +=== "Python" - ```java title="time_complexity.java" - [class]{time_complexity}-[func]{linear} + ```python title="time_complexity.py" + [class]{}-[func]{linear} ``` === "C++" @@ -985,10 +985,16 @@ $$ [class]{}-[func]{linear} ``` -=== "Python" +=== "Java" - ```python title="time_complexity.py" - [class]{}-[func]{linear} + ```java title="time_complexity.java" + [class]{time_complexity}-[func]{linear} + ``` + +=== "C#" + + ```csharp title="time_complexity.cs" + [class]{time_complexity}-[func]{linear} ``` === "Go" @@ -997,6 +1003,12 @@ $$ [class]{}-[func]{linear} ``` +=== "Swift" + + ```swift title="time_complexity.swift" + [class]{}-[func]{linear} + ``` + === "JS" ```javascript title="time_complexity.js" @@ -1009,30 +1021,6 @@ $$ [class]{}-[func]{linear} ``` -=== "C" - - ```c title="time_complexity.c" - [class]{}-[func]{linear} - ``` - -=== "C#" - - ```csharp title="time_complexity.cs" - [class]{time_complexity}-[func]{linear} - ``` - -=== "Swift" - - ```swift title="time_complexity.swift" - [class]{}-[func]{linear} - ``` - -=== "Zig" - - ```zig title="time_complexity.zig" - [class]{}-[func]{linear} - ``` - === "Dart" ```dart title="time_complexity.dart" @@ -1045,12 +1033,24 @@ $$ [class]{}-[func]{linear} ``` +=== "C" + + ```c title="time_complexity.c" + [class]{}-[func]{linear} + ``` + +=== "Zig" + + ```zig title="time_complexity.zig" + [class]{}-[func]{linear} + ``` + 遍历数组和遍历链表等操作的时间复杂度均为 $O(n)$ ,其中 $n$ 为数组或链表的长度: -=== "Java" +=== "Python" - ```java title="time_complexity.java" - [class]{time_complexity}-[func]{arrayTraversal} + ```python title="time_complexity.py" + [class]{}-[func]{array_traversal} ``` === "C++" @@ -1059,10 +1059,16 @@ $$ [class]{}-[func]{arrayTraversal} ``` -=== "Python" +=== "Java" - ```python title="time_complexity.py" - [class]{}-[func]{array_traversal} + ```java title="time_complexity.java" + [class]{time_complexity}-[func]{arrayTraversal} + ``` + +=== "C#" + + ```csharp title="time_complexity.cs" + [class]{time_complexity}-[func]{arrayTraversal} ``` === "Go" @@ -1071,6 +1077,12 @@ $$ [class]{}-[func]{arrayTraversal} ``` +=== "Swift" + + ```swift title="time_complexity.swift" + [class]{}-[func]{arrayTraversal} + ``` + === "JS" ```javascript title="time_complexity.js" @@ -1083,30 +1095,6 @@ $$ [class]{}-[func]{arrayTraversal} ``` -=== "C" - - ```c title="time_complexity.c" - [class]{}-[func]{arrayTraversal} - ``` - -=== "C#" - - ```csharp title="time_complexity.cs" - [class]{time_complexity}-[func]{arrayTraversal} - ``` - -=== "Swift" - - ```swift title="time_complexity.swift" - [class]{}-[func]{arrayTraversal} - ``` - -=== "Zig" - - ```zig title="time_complexity.zig" - [class]{}-[func]{arrayTraversal} - ``` - === "Dart" ```dart title="time_complexity.dart" @@ -1119,16 +1107,28 @@ $$ [class]{}-[func]{array_traversal} ``` +=== "C" + + ```c title="time_complexity.c" + [class]{}-[func]{arrayTraversal} + ``` + +=== "Zig" + + ```zig title="time_complexity.zig" + [class]{}-[func]{arrayTraversal} + ``` + 值得注意的是,**输入数据大小 $n$ 需根据输入数据的类型来具体确定**。比如在第一个示例中,变量 $n$ 为输入数据大小;在第二个示例中,数组长度 $n$ 为数据大小。 ### 平方阶 $O(n^2)$ 平方阶的操作数量相对于输入数据大小 $n$ 以平方级别增长。平方阶通常出现在嵌套循环中,外层循环和内层循环都为 $O(n)$ ,因此总体为 $O(n^2)$ : -=== "Java" +=== "Python" - ```java title="time_complexity.java" - [class]{time_complexity}-[func]{quadratic} + ```python title="time_complexity.py" + [class]{}-[func]{quadratic} ``` === "C++" @@ -1137,10 +1137,16 @@ $$ [class]{}-[func]{quadratic} ``` -=== "Python" +=== "Java" - ```python title="time_complexity.py" - [class]{}-[func]{quadratic} + ```java title="time_complexity.java" + [class]{time_complexity}-[func]{quadratic} + ``` + +=== "C#" + + ```csharp title="time_complexity.cs" + [class]{time_complexity}-[func]{quadratic} ``` === "Go" @@ -1149,6 +1155,12 @@ $$ [class]{}-[func]{quadratic} ``` +=== "Swift" + + ```swift title="time_complexity.swift" + [class]{}-[func]{quadratic} + ``` + === "JS" ```javascript title="time_complexity.js" @@ -1161,30 +1173,6 @@ $$ [class]{}-[func]{quadratic} ``` -=== "C" - - ```c title="time_complexity.c" - [class]{}-[func]{quadratic} - ``` - -=== "C#" - - ```csharp title="time_complexity.cs" - [class]{time_complexity}-[func]{quadratic} - ``` - -=== "Swift" - - ```swift title="time_complexity.swift" - [class]{}-[func]{quadratic} - ``` - -=== "Zig" - - ```zig title="time_complexity.zig" - [class]{}-[func]{quadratic} - ``` - === "Dart" ```dart title="time_complexity.dart" @@ -1197,16 +1185,28 @@ $$ [class]{}-[func]{quadratic} ``` +=== "C" + + ```c title="time_complexity.c" + [class]{}-[func]{quadratic} + ``` + +=== "Zig" + + ```zig title="time_complexity.zig" + [class]{}-[func]{quadratic} + ``` + 下图对比了常数阶、线性阶和平方阶三种时间复杂度。 ![常数阶、线性阶和平方阶的时间复杂度](time_complexity.assets/time_complexity_constant_linear_quadratic.png) 以冒泡排序为例,外层循环执行 $n - 1$ 次,内层循环执行 $n-1$、$n-2$、$\dots$、$2$、$1$ 次,平均为 $n / 2$ 次,因此时间复杂度为 $O((n - 1) n / 2) = O(n^2)$ 。 -=== "Java" +=== "Python" - ```java title="time_complexity.java" - [class]{time_complexity}-[func]{bubbleSort} + ```python title="time_complexity.py" + [class]{}-[func]{bubble_sort} ``` === "C++" @@ -1215,10 +1215,16 @@ $$ [class]{}-[func]{bubbleSort} ``` -=== "Python" +=== "Java" - ```python title="time_complexity.py" - [class]{}-[func]{bubble_sort} + ```java title="time_complexity.java" + [class]{time_complexity}-[func]{bubbleSort} + ``` + +=== "C#" + + ```csharp title="time_complexity.cs" + [class]{time_complexity}-[func]{bubbleSort} ``` === "Go" @@ -1227,6 +1233,12 @@ $$ [class]{}-[func]{bubbleSort} ``` +=== "Swift" + + ```swift title="time_complexity.swift" + [class]{}-[func]{bubbleSort} + ``` + === "JS" ```javascript title="time_complexity.js" @@ -1239,30 +1251,6 @@ $$ [class]{}-[func]{bubbleSort} ``` -=== "C" - - ```c title="time_complexity.c" - [class]{}-[func]{bubbleSort} - ``` - -=== "C#" - - ```csharp title="time_complexity.cs" - [class]{time_complexity}-[func]{bubbleSort} - ``` - -=== "Swift" - - ```swift title="time_complexity.swift" - [class]{}-[func]{bubbleSort} - ``` - -=== "Zig" - - ```zig title="time_complexity.zig" - [class]{}-[func]{bubbleSort} - ``` - === "Dart" ```dart title="time_complexity.dart" @@ -1275,16 +1263,28 @@ $$ [class]{}-[func]{bubble_sort} ``` +=== "C" + + ```c title="time_complexity.c" + [class]{}-[func]{bubbleSort} + ``` + +=== "Zig" + + ```zig title="time_complexity.zig" + [class]{}-[func]{bubbleSort} + ``` + ### 指数阶 $O(2^n)$ 生物学的“细胞分裂”是指数阶增长的典型例子:初始状态为 $1$ 个细胞,分裂一轮后变为 $2$ 个,分裂两轮后变为 $4$ 个,以此类推,分裂 $n$ 轮后有 $2^n$ 个细胞。 下图和以下代码模拟了细胞分裂的过程,时间复杂度为 $O(2^n)$ 。 -=== "Java" +=== "Python" - ```java title="time_complexity.java" - [class]{time_complexity}-[func]{exponential} + ```python title="time_complexity.py" + [class]{}-[func]{exponential} ``` === "C++" @@ -1293,10 +1293,16 @@ $$ [class]{}-[func]{exponential} ``` -=== "Python" +=== "Java" - ```python title="time_complexity.py" - [class]{}-[func]{exponential} + ```java title="time_complexity.java" + [class]{time_complexity}-[func]{exponential} + ``` + +=== "C#" + + ```csharp title="time_complexity.cs" + [class]{time_complexity}-[func]{exponential} ``` === "Go" @@ -1305,6 +1311,12 @@ $$ [class]{}-[func]{exponential} ``` +=== "Swift" + + ```swift title="time_complexity.swift" + [class]{}-[func]{exponential} + ``` + === "JS" ```javascript title="time_complexity.js" @@ -1317,30 +1329,6 @@ $$ [class]{}-[func]{exponential} ``` -=== "C" - - ```c title="time_complexity.c" - [class]{}-[func]{exponential} - ``` - -=== "C#" - - ```csharp title="time_complexity.cs" - [class]{time_complexity}-[func]{exponential} - ``` - -=== "Swift" - - ```swift title="time_complexity.swift" - [class]{}-[func]{exponential} - ``` - -=== "Zig" - - ```zig title="time_complexity.zig" - [class]{}-[func]{exponential} - ``` - === "Dart" ```dart title="time_complexity.dart" @@ -1353,14 +1341,26 @@ $$ [class]{}-[func]{exponential} ``` +=== "C" + + ```c title="time_complexity.c" + [class]{}-[func]{exponential} + ``` + +=== "Zig" + + ```zig title="time_complexity.zig" + [class]{}-[func]{exponential} + ``` + ![指数阶的时间复杂度](time_complexity.assets/time_complexity_exponential.png) 在实际算法中,指数阶常出现于递归函数中。例如在以下代码中,其递归地一分为二,经过 $n$ 次分裂后停止: -=== "Java" +=== "Python" - ```java title="time_complexity.java" - [class]{time_complexity}-[func]{expRecur} + ```python title="time_complexity.py" + [class]{}-[func]{exp_recur} ``` === "C++" @@ -1369,10 +1369,16 @@ $$ [class]{}-[func]{expRecur} ``` -=== "Python" +=== "Java" - ```python title="time_complexity.py" - [class]{}-[func]{exp_recur} + ```java title="time_complexity.java" + [class]{time_complexity}-[func]{expRecur} + ``` + +=== "C#" + + ```csharp title="time_complexity.cs" + [class]{time_complexity}-[func]{expRecur} ``` === "Go" @@ -1381,6 +1387,12 @@ $$ [class]{}-[func]{expRecur} ``` +=== "Swift" + + ```swift title="time_complexity.swift" + [class]{}-[func]{expRecur} + ``` + === "JS" ```javascript title="time_complexity.js" @@ -1393,30 +1405,6 @@ $$ [class]{}-[func]{expRecur} ``` -=== "C" - - ```c title="time_complexity.c" - [class]{}-[func]{expRecur} - ``` - -=== "C#" - - ```csharp title="time_complexity.cs" - [class]{time_complexity}-[func]{expRecur} - ``` - -=== "Swift" - - ```swift title="time_complexity.swift" - [class]{}-[func]{expRecur} - ``` - -=== "Zig" - - ```zig title="time_complexity.zig" - [class]{}-[func]{expRecur} - ``` - === "Dart" ```dart title="time_complexity.dart" @@ -1429,6 +1417,18 @@ $$ [class]{}-[func]{exp_recur} ``` +=== "C" + + ```c title="time_complexity.c" + [class]{}-[func]{expRecur} + ``` + +=== "Zig" + + ```zig title="time_complexity.zig" + [class]{}-[func]{expRecur} + ``` + 指数阶增长非常迅速,在穷举法(暴力搜索、回溯等)中比较常见。对于数据规模较大的问题,指数阶是不可接受的,通常需要使用动态规划或贪心等算法来解决。 ### 对数阶 $O(\log n)$ @@ -1437,10 +1437,10 @@ $$ 下图和以下代码模拟了“每轮缩减到一半”的过程,时间复杂度为 $O(\log_2 n)$ ,简记为 $O(\log n)$ 。 -=== "Java" +=== "Python" - ```java title="time_complexity.java" - [class]{time_complexity}-[func]{logarithmic} + ```python title="time_complexity.py" + [class]{}-[func]{logarithmic} ``` === "C++" @@ -1449,10 +1449,16 @@ $$ [class]{}-[func]{logarithmic} ``` -=== "Python" +=== "Java" - ```python title="time_complexity.py" - [class]{}-[func]{logarithmic} + ```java title="time_complexity.java" + [class]{time_complexity}-[func]{logarithmic} + ``` + +=== "C#" + + ```csharp title="time_complexity.cs" + [class]{time_complexity}-[func]{logarithmic} ``` === "Go" @@ -1461,6 +1467,12 @@ $$ [class]{}-[func]{logarithmic} ``` +=== "Swift" + + ```swift title="time_complexity.swift" + [class]{}-[func]{logarithmic} + ``` + === "JS" ```javascript title="time_complexity.js" @@ -1473,30 +1485,6 @@ $$ [class]{}-[func]{logarithmic} ``` -=== "C" - - ```c title="time_complexity.c" - [class]{}-[func]{logarithmic} - ``` - -=== "C#" - - ```csharp title="time_complexity.cs" - [class]{time_complexity}-[func]{logarithmic} - ``` - -=== "Swift" - - ```swift title="time_complexity.swift" - [class]{}-[func]{logarithmic} - ``` - -=== "Zig" - - ```zig title="time_complexity.zig" - [class]{}-[func]{logarithmic} - ``` - === "Dart" ```dart title="time_complexity.dart" @@ -1509,14 +1497,26 @@ $$ [class]{}-[func]{logarithmic} ``` +=== "C" + + ```c title="time_complexity.c" + [class]{}-[func]{logarithmic} + ``` + +=== "Zig" + + ```zig title="time_complexity.zig" + [class]{}-[func]{logarithmic} + ``` + ![对数阶的时间复杂度](time_complexity.assets/time_complexity_logarithmic.png) 与指数阶类似,对数阶也常出现于递归函数中。以下代码形成了一个高度为 $\log_2 n$ 的递归树: -=== "Java" +=== "Python" - ```java title="time_complexity.java" - [class]{time_complexity}-[func]{logRecur} + ```python title="time_complexity.py" + [class]{}-[func]{log_recur} ``` === "C++" @@ -1525,10 +1525,16 @@ $$ [class]{}-[func]{logRecur} ``` -=== "Python" +=== "Java" - ```python title="time_complexity.py" - [class]{}-[func]{log_recur} + ```java title="time_complexity.java" + [class]{time_complexity}-[func]{logRecur} + ``` + +=== "C#" + + ```csharp title="time_complexity.cs" + [class]{time_complexity}-[func]{logRecur} ``` === "Go" @@ -1537,6 +1543,12 @@ $$ [class]{}-[func]{logRecur} ``` +=== "Swift" + + ```swift title="time_complexity.swift" + [class]{}-[func]{logRecur} + ``` + === "JS" ```javascript title="time_complexity.js" @@ -1549,30 +1561,6 @@ $$ [class]{}-[func]{logRecur} ``` -=== "C" - - ```c title="time_complexity.c" - [class]{}-[func]{logRecur} - ``` - -=== "C#" - - ```csharp title="time_complexity.cs" - [class]{time_complexity}-[func]{logRecur} - ``` - -=== "Swift" - - ```swift title="time_complexity.swift" - [class]{}-[func]{logRecur} - ``` - -=== "Zig" - - ```zig title="time_complexity.zig" - [class]{}-[func]{logRecur} - ``` - === "Dart" ```dart title="time_complexity.dart" @@ -1585,6 +1573,18 @@ $$ [class]{}-[func]{log_recur} ``` +=== "C" + + ```c title="time_complexity.c" + [class]{}-[func]{logRecur} + ``` + +=== "Zig" + + ```zig title="time_complexity.zig" + [class]{}-[func]{logRecur} + ``` + 对数阶常出现于基于分治策略的算法中,体现了“一分为多”和“化繁为简”的算法思想。它增长缓慢,是仅次于常数阶的理想的时间复杂度。 !!! tip "$O(\log n)$ 的底数是多少?" @@ -1601,10 +1601,10 @@ $$ 线性对数阶常出现于嵌套循环中,两层循环的时间复杂度分别为 $O(\log n)$ 和 $O(n)$ 。相关代码如下: -=== "Java" +=== "Python" - ```java title="time_complexity.java" - [class]{time_complexity}-[func]{linearLogRecur} + ```python title="time_complexity.py" + [class]{}-[func]{linear_log_recur} ``` === "C++" @@ -1613,10 +1613,16 @@ $$ [class]{}-[func]{linearLogRecur} ``` -=== "Python" +=== "Java" - ```python title="time_complexity.py" - [class]{}-[func]{linear_log_recur} + ```java title="time_complexity.java" + [class]{time_complexity}-[func]{linearLogRecur} + ``` + +=== "C#" + + ```csharp title="time_complexity.cs" + [class]{time_complexity}-[func]{linearLogRecur} ``` === "Go" @@ -1625,6 +1631,12 @@ $$ [class]{}-[func]{linearLogRecur} ``` +=== "Swift" + + ```swift title="time_complexity.swift" + [class]{}-[func]{linearLogRecur} + ``` + === "JS" ```javascript title="time_complexity.js" @@ -1637,30 +1649,6 @@ $$ [class]{}-[func]{linearLogRecur} ``` -=== "C" - - ```c title="time_complexity.c" - [class]{}-[func]{linearLogRecur} - ``` - -=== "C#" - - ```csharp title="time_complexity.cs" - [class]{time_complexity}-[func]{linearLogRecur} - ``` - -=== "Swift" - - ```swift title="time_complexity.swift" - [class]{}-[func]{linearLogRecur} - ``` - -=== "Zig" - - ```zig title="time_complexity.zig" - [class]{}-[func]{linearLogRecur} - ``` - === "Dart" ```dart title="time_complexity.dart" @@ -1673,6 +1661,18 @@ $$ [class]{}-[func]{linear_log_recur} ``` +=== "C" + + ```c title="time_complexity.c" + [class]{}-[func]{linearLogRecur} + ``` + +=== "Zig" + + ```zig title="time_complexity.zig" + [class]{}-[func]{linearLogRecur} + ``` + 下图展示了线性对数阶的生成方式。二叉树的每一层的操作总数都为 $n$ ,树共有 $\log_2 n + 1$ 层,因此时间复杂度为 $O(n \log n)$ 。 ![线性对数阶的时间复杂度](time_complexity.assets/time_complexity_logarithmic_linear.png) @@ -1689,10 +1689,10 @@ $$ 阶乘通常使用递归实现。如下图和以下代码所示,第一层分裂出 $n$ 个,第二层分裂出 $n - 1$ 个,以此类推,直至第 $n$ 层时停止分裂: -=== "Java" +=== "Python" - ```java title="time_complexity.java" - [class]{time_complexity}-[func]{factorialRecur} + ```python title="time_complexity.py" + [class]{}-[func]{factorial_recur} ``` === "C++" @@ -1701,10 +1701,16 @@ $$ [class]{}-[func]{factorialRecur} ``` -=== "Python" +=== "Java" - ```python title="time_complexity.py" - [class]{}-[func]{factorial_recur} + ```java title="time_complexity.java" + [class]{time_complexity}-[func]{factorialRecur} + ``` + +=== "C#" + + ```csharp title="time_complexity.cs" + [class]{time_complexity}-[func]{factorialRecur} ``` === "Go" @@ -1713,6 +1719,12 @@ $$ [class]{}-[func]{factorialRecur} ``` +=== "Swift" + + ```swift title="time_complexity.swift" + [class]{}-[func]{factorialRecur} + ``` + === "JS" ```javascript title="time_complexity.js" @@ -1725,30 +1737,6 @@ $$ [class]{}-[func]{factorialRecur} ``` -=== "C" - - ```c title="time_complexity.c" - [class]{}-[func]{factorialRecur} - ``` - -=== "C#" - - ```csharp title="time_complexity.cs" - [class]{time_complexity}-[func]{factorialRecur} - ``` - -=== "Swift" - - ```swift title="time_complexity.swift" - [class]{}-[func]{factorialRecur} - ``` - -=== "Zig" - - ```zig title="time_complexity.zig" - [class]{}-[func]{factorialRecur} - ``` - === "Dart" ```dart title="time_complexity.dart" @@ -1761,6 +1749,18 @@ $$ [class]{}-[func]{factorial_recur} ``` +=== "C" + + ```c title="time_complexity.c" + [class]{}-[func]{factorialRecur} + ``` + +=== "Zig" + + ```zig title="time_complexity.zig" + [class]{}-[func]{factorialRecur} + ``` + ![阶乘阶的时间复杂度](time_complexity.assets/time_complexity_factorial.png) 请注意,因为当 $n \geq 4$ 时恒有 $n! > 2^n$ ,所以阶乘阶比指数阶增长得更快,在 $n$ 较大时也是不可接受的。 @@ -1774,12 +1774,12 @@ $$ “最差时间复杂度”对应函数渐近上界,使用大 $O$ 记号表示。相应地,“最佳时间复杂度”对应函数渐近下界,用 $\Omega$ 记号表示: -=== "Java" +=== "Python" - ```java title="worst_best_time_complexity.java" - [class]{worst_best_time_complexity}-[func]{randomNumbers} + ```python title="worst_best_time_complexity.py" + [class]{}-[func]{random_numbers} - [class]{worst_best_time_complexity}-[func]{findOne} + [class]{}-[func]{find_one} ``` === "C++" @@ -1790,12 +1790,20 @@ $$ [class]{}-[func]{findOne} ``` -=== "Python" +=== "Java" - ```python title="worst_best_time_complexity.py" - [class]{}-[func]{random_numbers} + ```java title="worst_best_time_complexity.java" + [class]{worst_best_time_complexity}-[func]{randomNumbers} - [class]{}-[func]{find_one} + [class]{worst_best_time_complexity}-[func]{findOne} + ``` + +=== "C#" + + ```csharp title="worst_best_time_complexity.cs" + [class]{worst_best_time_complexity}-[func]{randomNumbers} + + [class]{worst_best_time_complexity}-[func]{findOne} ``` === "Go" @@ -1806,6 +1814,14 @@ $$ [class]{}-[func]{findOne} ``` +=== "Swift" + + ```swift title="worst_best_time_complexity.swift" + [class]{}-[func]{randomNumbers} + + [class]{}-[func]{findOne} + ``` + === "JS" ```javascript title="worst_best_time_complexity.js" @@ -1822,25 +1838,25 @@ $$ [class]{}-[func]{findOne} ``` -=== "C" +=== "Dart" - ```c title="worst_best_time_complexity.c" + ```dart title="worst_best_time_complexity.dart" [class]{}-[func]{randomNumbers} [class]{}-[func]{findOne} ``` -=== "C#" +=== "Rust" - ```csharp title="worst_best_time_complexity.cs" - [class]{worst_best_time_complexity}-[func]{randomNumbers} + ```rust title="worst_best_time_complexity.rs" + [class]{}-[func]{random_numbers} - [class]{worst_best_time_complexity}-[func]{findOne} + [class]{}-[func]{find_one} ``` -=== "Swift" +=== "C" - ```swift title="worst_best_time_complexity.swift" + ```c title="worst_best_time_complexity.c" [class]{}-[func]{randomNumbers} [class]{}-[func]{findOne} @@ -1873,22 +1889,6 @@ $$ } ``` -=== "Dart" - - ```dart title="worst_best_time_complexity.dart" - [class]{}-[func]{randomNumbers} - - [class]{}-[func]{findOne} - ``` - -=== "Rust" - - ```rust title="worst_best_time_complexity.rs" - [class]{}-[func]{random_numbers} - - [class]{}-[func]{find_one} - ``` - 值得说明的是,我们在实际中很少使用最佳时间复杂度,因为通常只有在很小概率下才能达到,可能会带来一定的误导性。**而最差时间复杂度更为实用,因为它给出了一个效率安全值**,让我们可以放心地使用算法。 从上述示例可以看出,最差或最佳时间复杂度只出现于“特殊的数据分布”,这些情况的出现概率可能很小,并不能真实地反映算法运行效率。相比之下,**平均时间复杂度可以体现算法在随机输入数据下的运行效率**,用 $\Theta$ 记号来表示。 diff --git a/docs/chapter_data_structure/basic_data_types.md b/docs/chapter_data_structure/basic_data_types.md index 0cf61f43..3c83da12 100644 --- a/docs/chapter_data_structure/basic_data_types.md +++ b/docs/chapter_data_structure/basic_data_types.md @@ -43,26 +43,6 @@ 换句话说,**基本数据类型提供了数据的“内容类型”,而数据结构提供了数据的“组织方式”**。例如以下代码,我们用相同的数据结构(数组)来存储与表示不同的基本数据类型,包括 `int`、`float`、`char`、`bool` 等。 -=== "Java" - - ```java title="" - // 使用多种基本数据类型来初始化数组 - int[] numbers = new int[5]; - float[] decimals = new float[5]; - char[] characters = new char[5]; - boolean[] bools = new boolean[5]; - ``` - -=== "C++" - - ```cpp title="" - // 使用多种基本数据类型来初始化数组 - int numbers[5]; - float decimals[5]; - char characters[5]; - bool bools[5]; - ``` - === "Python" ```python title="" @@ -76,6 +56,36 @@ data = [0, 0.0, 'a', False, ListNode(0)] ``` +=== "C++" + + ```cpp title="" + // 使用多种基本数据类型来初始化数组 + int numbers[5]; + float decimals[5]; + char characters[5]; + bool bools[5]; + ``` + +=== "Java" + + ```java title="" + // 使用多种基本数据类型来初始化数组 + int[] numbers = new int[5]; + float[] decimals = new float[5]; + char[] characters = new char[5]; + boolean[] bools = new boolean[5]; + ``` + +=== "C#" + + ```csharp title="" + // 使用多种基本数据类型来初始化数组 + int[] numbers = new int[5]; + float[] decimals = new float[5]; + char[] characters = new char[5]; + bool[] bools = new bool[5]; + ``` + === "Go" ```go title="" @@ -86,6 +96,16 @@ var bools = [5]bool{} ``` +=== "Swift" + + ```swift title="" + // 使用多种基本数据类型来初始化数组 + let numbers = Array(repeating: Int(), count: 5) + let decimals = Array(repeating: Double(), count: 5) + let characters = Array(repeating: Character("a"), count: 5) + let bools = Array(repeating: Bool(), count: 5) + ``` + === "JS" ```javascript title="" @@ -102,42 +122,6 @@ const bools: boolean[] = []; ``` -=== "C" - - ```c title="" - // 使用多种基本数据类型来初始化数组 - int numbers[10]; - float decimals[10]; - char characters[10]; - bool bools[10]; - ``` - -=== "C#" - - ```csharp title="" - // 使用多种基本数据类型来初始化数组 - int[] numbers = new int[5]; - float[] decimals = new float[5]; - char[] characters = new char[5]; - bool[] bools = new bool[5]; - ``` - -=== "Swift" - - ```swift title="" - // 使用多种基本数据类型来初始化数组 - let numbers = Array(repeating: Int(), count: 5) - let decimals = Array(repeating: Double(), count: 5) - let characters = Array(repeating: Character("a"), count: 5) - let bools = Array(repeating: Bool(), count: 5) - ``` - -=== "Zig" - - ```zig title="" - - ``` - === "Dart" ```dart title="" @@ -153,3 +137,19 @@ ```rust title="" ``` + +=== "C" + + ```c title="" + // 使用多种基本数据类型来初始化数组 + int numbers[10]; + float decimals[10]; + char characters[10]; + bool bools[10]; + ``` + +=== "Zig" + + ```zig title="" + + ``` diff --git a/docs/chapter_divide_and_conquer/binary_search_recur.md b/docs/chapter_divide_and_conquer/binary_search_recur.md index 15ebdd21..5e51550c 100644 --- a/docs/chapter_divide_and_conquer/binary_search_recur.md +++ b/docs/chapter_divide_and_conquer/binary_search_recur.md @@ -40,12 +40,12 @@ 在实现代码中,我们声明一个递归函数 `dfs()` 来求解问题 $f(i, j)$ 。 -=== "Java" +=== "Python" - ```java title="binary_search_recur.java" - [class]{binary_search_recur}-[func]{dfs} + ```python title="binary_search_recur.py" + [class]{}-[func]{dfs} - [class]{binary_search_recur}-[func]{binarySearch} + [class]{}-[func]{binary_search} ``` === "C++" @@ -56,12 +56,20 @@ [class]{}-[func]{binarySearch} ``` -=== "Python" +=== "Java" - ```python title="binary_search_recur.py" - [class]{}-[func]{dfs} + ```java title="binary_search_recur.java" + [class]{binary_search_recur}-[func]{dfs} - [class]{}-[func]{binary_search} + [class]{binary_search_recur}-[func]{binarySearch} + ``` + +=== "C#" + + ```csharp title="binary_search_recur.cs" + [class]{binary_search_recur}-[func]{dfs} + + [class]{binary_search_recur}-[func]{binarySearch} ``` === "Go" @@ -72,6 +80,14 @@ [class]{}-[func]{binarySearch} ``` +=== "Swift" + + ```swift title="binary_search_recur.swift" + [class]{}-[func]{dfs} + + [class]{}-[func]{binarySearch} + ``` + === "JS" ```javascript title="binary_search_recur.js" @@ -88,38 +104,6 @@ [class]{}-[func]{binarySearch} ``` -=== "C" - - ```c title="binary_search_recur.c" - [class]{}-[func]{dfs} - - [class]{}-[func]{binarySearch} - ``` - -=== "C#" - - ```csharp title="binary_search_recur.cs" - [class]{binary_search_recur}-[func]{dfs} - - [class]{binary_search_recur}-[func]{binarySearch} - ``` - -=== "Swift" - - ```swift title="binary_search_recur.swift" - [class]{}-[func]{dfs} - - [class]{}-[func]{binarySearch} - ``` - -=== "Zig" - - ```zig title="binary_search_recur.zig" - [class]{}-[func]{dfs} - - [class]{}-[func]{binarySearch} - ``` - === "Dart" ```dart title="binary_search_recur.dart" @@ -135,3 +119,19 @@ [class]{}-[func]{binary_search} ``` + +=== "C" + + ```c title="binary_search_recur.c" + [class]{}-[func]{dfs} + + [class]{}-[func]{binarySearch} + ``` + +=== "Zig" + + ```zig title="binary_search_recur.zig" + [class]{}-[func]{dfs} + + [class]{}-[func]{binarySearch} + ``` diff --git a/docs/chapter_divide_and_conquer/build_binary_tree_problem.md b/docs/chapter_divide_and_conquer/build_binary_tree_problem.md index a7a264b9..ec6cf1ad 100644 --- a/docs/chapter_divide_and_conquer/build_binary_tree_problem.md +++ b/docs/chapter_divide_and_conquer/build_binary_tree_problem.md @@ -57,12 +57,12 @@ 为了提升查询 $m$ 的效率,我们借助一个哈希表 `hmap` 来存储数组 `inorder` 中元素到索引的映射。 -=== "Java" +=== "Python" - ```java title="build_tree.java" - [class]{build_tree}-[func]{dfs} + ```python title="build_tree.py" + [class]{}-[func]{dfs} - [class]{build_tree}-[func]{buildTree} + [class]{}-[func]{build_tree} ``` === "C++" @@ -73,12 +73,20 @@ [class]{}-[func]{buildTree} ``` -=== "Python" +=== "Java" - ```python title="build_tree.py" - [class]{}-[func]{dfs} + ```java title="build_tree.java" + [class]{build_tree}-[func]{dfs} - [class]{}-[func]{build_tree} + [class]{build_tree}-[func]{buildTree} + ``` + +=== "C#" + + ```csharp title="build_tree.cs" + [class]{build_tree}-[func]{dfs} + + [class]{build_tree}-[func]{buildTree} ``` === "Go" @@ -89,6 +97,14 @@ [class]{}-[func]{buildTree} ``` +=== "Swift" + + ```swift title="build_tree.swift" + [class]{}-[func]{dfs} + + [class]{}-[func]{buildTree} + ``` + === "JS" ```javascript title="build_tree.js" @@ -105,38 +121,6 @@ [class]{}-[func]{buildTree} ``` -=== "C" - - ```c title="build_tree.c" - [class]{}-[func]{dfs} - - [class]{}-[func]{buildTree} - ``` - -=== "C#" - - ```csharp title="build_tree.cs" - [class]{build_tree}-[func]{dfs} - - [class]{build_tree}-[func]{buildTree} - ``` - -=== "Swift" - - ```swift title="build_tree.swift" - [class]{}-[func]{dfs} - - [class]{}-[func]{buildTree} - ``` - -=== "Zig" - - ```zig title="build_tree.zig" - [class]{}-[func]{dfs} - - [class]{}-[func]{buildTree} - ``` - === "Dart" ```dart title="build_tree.dart" @@ -153,6 +137,22 @@ [class]{}-[func]{build_tree} ``` +=== "C" + + ```c title="build_tree.c" + [class]{}-[func]{dfs} + + [class]{}-[func]{buildTree} + ``` + +=== "Zig" + + ```zig title="build_tree.zig" + [class]{}-[func]{dfs} + + [class]{}-[func]{buildTree} + ``` + 下图展示了构建二叉树的递归过程,各个节点是在向下“递”的过程中建立的,而各条边(即引用)是在向上“归”的过程中建立的。 === "<1>" diff --git a/docs/chapter_divide_and_conquer/hanota_problem.md b/docs/chapter_divide_and_conquer/hanota_problem.md index 4e129ff8..5959ab14 100644 --- a/docs/chapter_divide_and_conquer/hanota_problem.md +++ b/docs/chapter_divide_and_conquer/hanota_problem.md @@ -82,14 +82,14 @@ 在代码中,我们声明一个递归函数 `dfs(i, src, buf, tar)` ,它的作用是将柱 `src` 顶部的 $i$ 个圆盘借助缓冲柱 `buf` 移动至目标柱 `tar` 。 -=== "Java" +=== "Python" - ```java title="hanota.java" - [class]{hanota}-[func]{move} + ```python title="hanota.py" + [class]{}-[func]{move} - [class]{hanota}-[func]{dfs} + [class]{}-[func]{dfs} - [class]{hanota}-[func]{solveHanota} + [class]{}-[func]{solve_hanota} ``` === "C++" @@ -102,14 +102,24 @@ [class]{}-[func]{solveHanota} ``` -=== "Python" +=== "Java" - ```python title="hanota.py" - [class]{}-[func]{move} + ```java title="hanota.java" + [class]{hanota}-[func]{move} - [class]{}-[func]{dfs} + [class]{hanota}-[func]{dfs} - [class]{}-[func]{solve_hanota} + [class]{hanota}-[func]{solveHanota} + ``` + +=== "C#" + + ```csharp title="hanota.cs" + [class]{hanota}-[func]{move} + + [class]{hanota}-[func]{dfs} + + [class]{hanota}-[func]{solveHanota} ``` === "Go" @@ -122,6 +132,16 @@ [class]{}-[func]{solveHanota} ``` +=== "Swift" + + ```swift title="hanota.swift" + [class]{}-[func]{move} + + [class]{}-[func]{dfs} + + [class]{}-[func]{solveHanota} + ``` + === "JS" ```javascript title="hanota.js" @@ -142,46 +162,6 @@ [class]{}-[func]{solveHanota} ``` -=== "C" - - ```c title="hanota.c" - [class]{}-[func]{move} - - [class]{}-[func]{dfs} - - [class]{}-[func]{solveHanota} - ``` - -=== "C#" - - ```csharp title="hanota.cs" - [class]{hanota}-[func]{move} - - [class]{hanota}-[func]{dfs} - - [class]{hanota}-[func]{solveHanota} - ``` - -=== "Swift" - - ```swift title="hanota.swift" - [class]{}-[func]{move} - - [class]{}-[func]{dfs} - - [class]{}-[func]{solveHanota} - ``` - -=== "Zig" - - ```zig title="hanota.zig" - [class]{}-[func]{move} - - [class]{}-[func]{dfs} - - [class]{}-[func]{solveHanota} - ``` - === "Dart" ```dart title="hanota.dart" @@ -202,6 +182,26 @@ [class]{}-[func]{solve_hanota} ``` +=== "C" + + ```c title="hanota.c" + [class]{}-[func]{move} + + [class]{}-[func]{dfs} + + [class]{}-[func]{solveHanota} + ``` + +=== "Zig" + + ```zig title="hanota.zig" + [class]{}-[func]{move} + + [class]{}-[func]{dfs} + + [class]{}-[func]{solveHanota} + ``` + 如下图所示,汉诺塔问题形成一个高度为 $n$ 的递归树,每个节点代表一个子问题、对应一个开启的 `dfs()` 函数,**因此时间复杂度为 $O(2^n)$ ,空间复杂度为 $O(n)$** 。 ![汉诺塔问题的递归树](hanota_problem.assets/hanota_recursive_tree.png) diff --git a/docs/chapter_dynamic_programming/dp_problem_features.md b/docs/chapter_dynamic_programming/dp_problem_features.md index 7c735ac1..ac4402a6 100644 --- a/docs/chapter_dynamic_programming/dp_problem_features.md +++ b/docs/chapter_dynamic_programming/dp_problem_features.md @@ -34,10 +34,10 @@ $$ 根据状态转移方程,以及初始状态 $dp[1] = cost[1]$ 和 $dp[2] = cost[2]$ ,我们就可以得到动态规划代码。 -=== "Java" +=== "Python" - ```java title="min_cost_climbing_stairs_dp.java" - [class]{min_cost_climbing_stairs_dp}-[func]{minCostClimbingStairsDP} + ```python title="min_cost_climbing_stairs_dp.py" + [class]{}-[func]{min_cost_climbing_stairs_dp} ``` === "C++" @@ -46,10 +46,16 @@ $$ [class]{}-[func]{minCostClimbingStairsDP} ``` -=== "Python" +=== "Java" - ```python title="min_cost_climbing_stairs_dp.py" - [class]{}-[func]{min_cost_climbing_stairs_dp} + ```java title="min_cost_climbing_stairs_dp.java" + [class]{min_cost_climbing_stairs_dp}-[func]{minCostClimbingStairsDP} + ``` + +=== "C#" + + ```csharp title="min_cost_climbing_stairs_dp.cs" + [class]{min_cost_climbing_stairs_dp}-[func]{minCostClimbingStairsDP} ``` === "Go" @@ -58,6 +64,12 @@ $$ [class]{}-[func]{minCostClimbingStairsDP} ``` +=== "Swift" + + ```swift title="min_cost_climbing_stairs_dp.swift" + [class]{}-[func]{minCostClimbingStairsDP} + ``` + === "JS" ```javascript title="min_cost_climbing_stairs_dp.js" @@ -70,30 +82,6 @@ $$ [class]{}-[func]{minCostClimbingStairsDP} ``` -=== "C" - - ```c title="min_cost_climbing_stairs_dp.c" - [class]{}-[func]{minCostClimbingStairsDP} - ``` - -=== "C#" - - ```csharp title="min_cost_climbing_stairs_dp.cs" - [class]{min_cost_climbing_stairs_dp}-[func]{minCostClimbingStairsDP} - ``` - -=== "Swift" - - ```swift title="min_cost_climbing_stairs_dp.swift" - [class]{}-[func]{minCostClimbingStairsDP} - ``` - -=== "Zig" - - ```zig title="min_cost_climbing_stairs_dp.zig" - [class]{}-[func]{minCostClimbingStairsDP} - ``` - === "Dart" ```dart title="min_cost_climbing_stairs_dp.dart" @@ -106,16 +94,28 @@ $$ [class]{}-[func]{min_cost_climbing_stairs_dp} ``` +=== "C" + + ```c title="min_cost_climbing_stairs_dp.c" + [class]{}-[func]{minCostClimbingStairsDP} + ``` + +=== "Zig" + + ```zig title="min_cost_climbing_stairs_dp.zig" + [class]{}-[func]{minCostClimbingStairsDP} + ``` + 下图展示了以上代码的动态规划过程。 ![爬楼梯最小代价的动态规划过程](dp_problem_features.assets/min_cost_cs_dp.png) 本题也可以进行空间优化,将一维压缩至零维,使得空间复杂度从 $O(n)$ 降低至 $O(1)$ 。 -=== "Java" +=== "Python" - ```java title="min_cost_climbing_stairs_dp.java" - [class]{min_cost_climbing_stairs_dp}-[func]{minCostClimbingStairsDPComp} + ```python title="min_cost_climbing_stairs_dp.py" + [class]{}-[func]{min_cost_climbing_stairs_dp_comp} ``` === "C++" @@ -124,10 +124,16 @@ $$ [class]{}-[func]{minCostClimbingStairsDPComp} ``` -=== "Python" +=== "Java" - ```python title="min_cost_climbing_stairs_dp.py" - [class]{}-[func]{min_cost_climbing_stairs_dp_comp} + ```java title="min_cost_climbing_stairs_dp.java" + [class]{min_cost_climbing_stairs_dp}-[func]{minCostClimbingStairsDPComp} + ``` + +=== "C#" + + ```csharp title="min_cost_climbing_stairs_dp.cs" + [class]{min_cost_climbing_stairs_dp}-[func]{minCostClimbingStairsDPComp} ``` === "Go" @@ -136,6 +142,12 @@ $$ [class]{}-[func]{minCostClimbingStairsDPComp} ``` +=== "Swift" + + ```swift title="min_cost_climbing_stairs_dp.swift" + [class]{}-[func]{minCostClimbingStairsDPComp} + ``` + === "JS" ```javascript title="min_cost_climbing_stairs_dp.js" @@ -148,30 +160,6 @@ $$ [class]{}-[func]{minCostClimbingStairsDPComp} ``` -=== "C" - - ```c title="min_cost_climbing_stairs_dp.c" - [class]{}-[func]{minCostClimbingStairsDPComp} - ``` - -=== "C#" - - ```csharp title="min_cost_climbing_stairs_dp.cs" - [class]{min_cost_climbing_stairs_dp}-[func]{minCostClimbingStairsDPComp} - ``` - -=== "Swift" - - ```swift title="min_cost_climbing_stairs_dp.swift" - [class]{}-[func]{minCostClimbingStairsDPComp} - ``` - -=== "Zig" - - ```zig title="min_cost_climbing_stairs_dp.zig" - [class]{}-[func]{minCostClimbingStairsDPComp} - ``` - === "Dart" ```dart title="min_cost_climbing_stairs_dp.dart" @@ -184,6 +172,18 @@ $$ [class]{}-[func]{min_cost_climbing_stairs_dp_comp} ``` +=== "C" + + ```c title="min_cost_climbing_stairs_dp.c" + [class]{}-[func]{minCostClimbingStairsDPComp} + ``` + +=== "Zig" + + ```zig title="min_cost_climbing_stairs_dp.zig" + [class]{}-[func]{minCostClimbingStairsDPComp} + ``` + ## 无后效性 无后效性是动态规划能够有效解决问题的重要特性之一,定义为:**给定一个确定的状态,它的未来发展只与当前状态有关,而与当前状态过去所经历过的所有状态无关**。 @@ -222,10 +222,10 @@ $$ 最终,返回 $dp[n, 1] + dp[n, 2]$ 即可,两者之和代表爬到第 $n$ 阶的方案总数。 -=== "Java" +=== "Python" - ```java title="climbing_stairs_constraint_dp.java" - [class]{climbing_stairs_constraint_dp}-[func]{climbingStairsConstraintDP} + ```python title="climbing_stairs_constraint_dp.py" + [class]{}-[func]{climbing_stairs_constraint_dp} ``` === "C++" @@ -234,10 +234,16 @@ $$ [class]{}-[func]{climbingStairsConstraintDP} ``` -=== "Python" +=== "Java" - ```python title="climbing_stairs_constraint_dp.py" - [class]{}-[func]{climbing_stairs_constraint_dp} + ```java title="climbing_stairs_constraint_dp.java" + [class]{climbing_stairs_constraint_dp}-[func]{climbingStairsConstraintDP} + ``` + +=== "C#" + + ```csharp title="climbing_stairs_constraint_dp.cs" + [class]{climbing_stairs_constraint_dp}-[func]{climbingStairsConstraintDP} ``` === "Go" @@ -246,6 +252,12 @@ $$ [class]{}-[func]{climbingStairsConstraintDP} ``` +=== "Swift" + + ```swift title="climbing_stairs_constraint_dp.swift" + [class]{}-[func]{climbingStairsConstraintDP} + ``` + === "JS" ```javascript title="climbing_stairs_constraint_dp.js" @@ -258,30 +270,6 @@ $$ [class]{}-[func]{climbingStairsConstraintDP} ``` -=== "C" - - ```c title="climbing_stairs_constraint_dp.c" - [class]{}-[func]{climbingStairsConstraintDP} - ``` - -=== "C#" - - ```csharp title="climbing_stairs_constraint_dp.cs" - [class]{climbing_stairs_constraint_dp}-[func]{climbingStairsConstraintDP} - ``` - -=== "Swift" - - ```swift title="climbing_stairs_constraint_dp.swift" - [class]{}-[func]{climbingStairsConstraintDP} - ``` - -=== "Zig" - - ```zig title="climbing_stairs_constraint_dp.zig" - [class]{}-[func]{climbingStairsConstraintDP} - ``` - === "Dart" ```dart title="climbing_stairs_constraint_dp.dart" @@ -294,6 +282,18 @@ $$ [class]{}-[func]{climbing_stairs_constraint_dp} ``` +=== "C" + + ```c title="climbing_stairs_constraint_dp.c" + [class]{}-[func]{climbingStairsConstraintDP} + ``` + +=== "Zig" + + ```zig title="climbing_stairs_constraint_dp.zig" + [class]{}-[func]{climbingStairsConstraintDP} + ``` + 在上面的案例中,由于仅需多考虑前面一个状态,我们仍然可以通过扩展状态定义,使得问题重新满足无后效性。然而,某些问题具有非常严重的“有后效性”。 !!! question "爬楼梯与障碍生成" diff --git a/docs/chapter_dynamic_programming/dp_solution_pipeline.md b/docs/chapter_dynamic_programming/dp_solution_pipeline.md index ef462952..a5762b13 100644 --- a/docs/chapter_dynamic_programming/dp_solution_pipeline.md +++ b/docs/chapter_dynamic_programming/dp_solution_pipeline.md @@ -98,10 +98,10 @@ $$ - **终止条件**:当 $i = 0$ 且 $j = 0$ 时,返回代价 $grid[0, 0]$ 。 - **剪枝**:当 $i < 0$ 时或 $j < 0$ 时索引越界,此时返回代价 $+\infty$ ,代表不可行。 -=== "Java" +=== "Python" - ```java title="min_path_sum.java" - [class]{min_path_sum}-[func]{minPathSumDFS} + ```python title="min_path_sum.py" + [class]{}-[func]{min_path_sum_dfs} ``` === "C++" @@ -110,10 +110,16 @@ $$ [class]{}-[func]{minPathSumDFS} ``` -=== "Python" +=== "Java" - ```python title="min_path_sum.py" - [class]{}-[func]{min_path_sum_dfs} + ```java title="min_path_sum.java" + [class]{min_path_sum}-[func]{minPathSumDFS} + ``` + +=== "C#" + + ```csharp title="min_path_sum.cs" + [class]{min_path_sum}-[func]{minPathSumDFS} ``` === "Go" @@ -122,6 +128,12 @@ $$ [class]{}-[func]{minPathSumDFS} ``` +=== "Swift" + + ```swift title="min_path_sum.swift" + [class]{}-[func]{minPathSumDFS} + ``` + === "JS" ```javascript title="min_path_sum.js" @@ -134,30 +146,6 @@ $$ [class]{}-[func]{minPathSumDFS} ``` -=== "C" - - ```c title="min_path_sum.c" - [class]{}-[func]{minPathSumDFS} - ``` - -=== "C#" - - ```csharp title="min_path_sum.cs" - [class]{min_path_sum}-[func]{minPathSumDFS} - ``` - -=== "Swift" - - ```swift title="min_path_sum.swift" - [class]{}-[func]{minPathSumDFS} - ``` - -=== "Zig" - - ```zig title="min_path_sum.zig" - [class]{}-[func]{minPathSumDFS} - ``` - === "Dart" ```dart title="min_path_sum.dart" @@ -170,6 +158,18 @@ $$ [class]{}-[func]{min_path_sum_dfs} ``` +=== "C" + + ```c title="min_path_sum.c" + [class]{}-[func]{minPathSumDFS} + ``` + +=== "Zig" + + ```zig title="min_path_sum.zig" + [class]{}-[func]{minPathSumDFS} + ``` + 下图给出了以 $dp[2, 1]$ 为根节点的递归树,其中包含一些重叠子问题,其数量会随着网格 `grid` 的尺寸变大而急剧增多。 本质上看,造成重叠子问题的原因为:**存在多条路径可以从左上角到达某一单元格**。 @@ -182,10 +182,10 @@ $$ 我们引入一个和网格 `grid` 相同尺寸的记忆列表 `mem` ,用于记录各个子问题的解,并将重叠子问题进行剪枝。 -=== "Java" +=== "Python" - ```java title="min_path_sum.java" - [class]{min_path_sum}-[func]{minPathSumDFSMem} + ```python title="min_path_sum.py" + [class]{}-[func]{min_path_sum_dfs_mem} ``` === "C++" @@ -194,10 +194,16 @@ $$ [class]{}-[func]{minPathSumDFSMem} ``` -=== "Python" +=== "Java" - ```python title="min_path_sum.py" - [class]{}-[func]{min_path_sum_dfs_mem} + ```java title="min_path_sum.java" + [class]{min_path_sum}-[func]{minPathSumDFSMem} + ``` + +=== "C#" + + ```csharp title="min_path_sum.cs" + [class]{min_path_sum}-[func]{minPathSumDFSMem} ``` === "Go" @@ -206,6 +212,12 @@ $$ [class]{}-[func]{minPathSumDFSMem} ``` +=== "Swift" + + ```swift title="min_path_sum.swift" + [class]{}-[func]{minPathSumDFSMem} + ``` + === "JS" ```javascript title="min_path_sum.js" @@ -218,30 +230,6 @@ $$ [class]{}-[func]{minPathSumDFSMem} ``` -=== "C" - - ```c title="min_path_sum.c" - [class]{}-[func]{minPathSumDFSMem} - ``` - -=== "C#" - - ```csharp title="min_path_sum.cs" - [class]{min_path_sum}-[func]{minPathSumDFSMem} - ``` - -=== "Swift" - - ```swift title="min_path_sum.swift" - [class]{}-[func]{minPathSumDFSMem} - ``` - -=== "Zig" - - ```zig title="min_path_sum.zig" - [class]{}-[func]{minPathSumDFSMem} - ``` - === "Dart" ```dart title="min_path_sum.dart" @@ -254,6 +242,18 @@ $$ [class]{}-[func]{min_path_sum_dfs_mem} ``` +=== "C" + + ```c title="min_path_sum.c" + [class]{}-[func]{minPathSumDFSMem} + ``` + +=== "Zig" + + ```zig title="min_path_sum.zig" + [class]{}-[func]{minPathSumDFSMem} + ``` + 如下图所示,在引入记忆化后,所有子问题的解只需计算一次,因此时间复杂度取决于状态总数,即网格尺寸 $O(nm)$ 。 ![记忆化搜索递归树](dp_solution_pipeline.assets/min_path_sum_dfs_mem.png) @@ -262,10 +262,10 @@ $$ 基于迭代实现动态规划解法。 -=== "Java" +=== "Python" - ```java title="min_path_sum.java" - [class]{min_path_sum}-[func]{minPathSumDP} + ```python title="min_path_sum.py" + [class]{}-[func]{min_path_sum_dp} ``` === "C++" @@ -274,10 +274,16 @@ $$ [class]{}-[func]{minPathSumDP} ``` -=== "Python" +=== "Java" - ```python title="min_path_sum.py" - [class]{}-[func]{min_path_sum_dp} + ```java title="min_path_sum.java" + [class]{min_path_sum}-[func]{minPathSumDP} + ``` + +=== "C#" + + ```csharp title="min_path_sum.cs" + [class]{min_path_sum}-[func]{minPathSumDP} ``` === "Go" @@ -286,6 +292,12 @@ $$ [class]{}-[func]{minPathSumDP} ``` +=== "Swift" + + ```swift title="min_path_sum.swift" + [class]{}-[func]{minPathSumDP} + ``` + === "JS" ```javascript title="min_path_sum.js" @@ -298,30 +310,6 @@ $$ [class]{}-[func]{minPathSumDP} ``` -=== "C" - - ```c title="min_path_sum.c" - [class]{}-[func]{minPathSumDP} - ``` - -=== "C#" - - ```csharp title="min_path_sum.cs" - [class]{min_path_sum}-[func]{minPathSumDP} - ``` - -=== "Swift" - - ```swift title="min_path_sum.swift" - [class]{}-[func]{minPathSumDP} - ``` - -=== "Zig" - - ```zig title="min_path_sum.zig" - [class]{}-[func]{minPathSumDP} - ``` - === "Dart" ```dart title="min_path_sum.dart" @@ -334,6 +322,18 @@ $$ [class]{}-[func]{min_path_sum_dp} ``` +=== "C" + + ```c title="min_path_sum.c" + [class]{}-[func]{minPathSumDP} + ``` + +=== "Zig" + + ```zig title="min_path_sum.zig" + [class]{}-[func]{minPathSumDP} + ``` + 下图展示了最小路径和的状态转移过程,其遍历了整个网格,**因此时间复杂度为 $O(nm)$** 。 数组 `dp` 大小为 $n \times m$ ,**因此空间复杂度为 $O(nm)$** 。 @@ -380,10 +380,10 @@ $$ 请注意,因为数组 `dp` 只能表示一行的状态,所以我们无法提前初始化首列状态,而是在遍历每行中更新它。 -=== "Java" +=== "Python" - ```java title="min_path_sum.java" - [class]{min_path_sum}-[func]{minPathSumDPComp} + ```python title="min_path_sum.py" + [class]{}-[func]{min_path_sum_dp_comp} ``` === "C++" @@ -392,10 +392,16 @@ $$ [class]{}-[func]{minPathSumDPComp} ``` -=== "Python" +=== "Java" - ```python title="min_path_sum.py" - [class]{}-[func]{min_path_sum_dp_comp} + ```java title="min_path_sum.java" + [class]{min_path_sum}-[func]{minPathSumDPComp} + ``` + +=== "C#" + + ```csharp title="min_path_sum.cs" + [class]{min_path_sum}-[func]{minPathSumDPComp} ``` === "Go" @@ -404,6 +410,12 @@ $$ [class]{}-[func]{minPathSumDPComp} ``` +=== "Swift" + + ```swift title="min_path_sum.swift" + [class]{}-[func]{minPathSumDPComp} + ``` + === "JS" ```javascript title="min_path_sum.js" @@ -416,30 +428,6 @@ $$ [class]{}-[func]{minPathSumDPComp} ``` -=== "C" - - ```c title="min_path_sum.c" - [class]{}-[func]{minPathSumDPComp} - ``` - -=== "C#" - - ```csharp title="min_path_sum.cs" - [class]{min_path_sum}-[func]{minPathSumDPComp} - ``` - -=== "Swift" - - ```swift title="min_path_sum.swift" - [class]{}-[func]{minPathSumDPComp} - ``` - -=== "Zig" - - ```zig title="min_path_sum.zig" - [class]{}-[func]{minPathSumDPComp} - ``` - === "Dart" ```dart title="min_path_sum.dart" @@ -451,3 +439,15 @@ $$ ```rust title="min_path_sum.rs" [class]{}-[func]{min_path_sum_dp_comp} ``` + +=== "C" + + ```c title="min_path_sum.c" + [class]{}-[func]{minPathSumDPComp} + ``` + +=== "Zig" + + ```zig title="min_path_sum.zig" + [class]{}-[func]{minPathSumDPComp} + ``` diff --git a/docs/chapter_dynamic_programming/edit_distance_problem.md b/docs/chapter_dynamic_programming/edit_distance_problem.md index 4c2a0234..924a8945 100644 --- a/docs/chapter_dynamic_programming/edit_distance_problem.md +++ b/docs/chapter_dynamic_programming/edit_distance_problem.md @@ -67,10 +67,10 @@ $$ ### 代码实现 -=== "Java" +=== "Python" - ```java title="edit_distance.java" - [class]{edit_distance}-[func]{editDistanceDP} + ```python title="edit_distance.py" + [class]{}-[func]{edit_distance_dp} ``` === "C++" @@ -79,10 +79,16 @@ $$ [class]{}-[func]{editDistanceDP} ``` -=== "Python" +=== "Java" - ```python title="edit_distance.py" - [class]{}-[func]{edit_distance_dp} + ```java title="edit_distance.java" + [class]{edit_distance}-[func]{editDistanceDP} + ``` + +=== "C#" + + ```csharp title="edit_distance.cs" + [class]{edit_distance}-[func]{editDistanceDP} ``` === "Go" @@ -91,6 +97,12 @@ $$ [class]{}-[func]{editDistanceDP} ``` +=== "Swift" + + ```swift title="edit_distance.swift" + [class]{}-[func]{editDistanceDP} + ``` + === "JS" ```javascript title="edit_distance.js" @@ -103,30 +115,6 @@ $$ [class]{}-[func]{editDistanceDP} ``` -=== "C" - - ```c title="edit_distance.c" - [class]{}-[func]{editDistanceDP} - ``` - -=== "C#" - - ```csharp title="edit_distance.cs" - [class]{edit_distance}-[func]{editDistanceDP} - ``` - -=== "Swift" - - ```swift title="edit_distance.swift" - [class]{}-[func]{editDistanceDP} - ``` - -=== "Zig" - - ```zig title="edit_distance.zig" - [class]{}-[func]{editDistanceDP} - ``` - === "Dart" ```dart title="edit_distance.dart" @@ -139,6 +127,18 @@ $$ [class]{}-[func]{edit_distance_dp} ``` +=== "C" + + ```c title="edit_distance.c" + [class]{}-[func]{editDistanceDP} + ``` + +=== "Zig" + + ```zig title="edit_distance.zig" + [class]{}-[func]{editDistanceDP} + ``` + 如下图所示,编辑距离问题的状态转移过程与背包问题非常类似,都可以看作是填写一个二维网格的过程。 === "<1>" @@ -192,10 +192,10 @@ $$ 为此,我们可以使用一个变量 `leftup` 来暂存左上方的解 $dp[i-1, j-1]$ ,从而只需考虑左方和上方的解。此时的情况与完全背包问题相同,可使用正序遍历。 -=== "Java" +=== "Python" - ```java title="edit_distance.java" - [class]{edit_distance}-[func]{editDistanceDPComp} + ```python title="edit_distance.py" + [class]{}-[func]{edit_distance_dp_comp} ``` === "C++" @@ -204,10 +204,16 @@ $$ [class]{}-[func]{editDistanceDPComp} ``` -=== "Python" +=== "Java" - ```python title="edit_distance.py" - [class]{}-[func]{edit_distance_dp_comp} + ```java title="edit_distance.java" + [class]{edit_distance}-[func]{editDistanceDPComp} + ``` + +=== "C#" + + ```csharp title="edit_distance.cs" + [class]{edit_distance}-[func]{editDistanceDPComp} ``` === "Go" @@ -216,6 +222,12 @@ $$ [class]{}-[func]{editDistanceDPComp} ``` +=== "Swift" + + ```swift title="edit_distance.swift" + [class]{}-[func]{editDistanceDPComp} + ``` + === "JS" ```javascript title="edit_distance.js" @@ -228,30 +240,6 @@ $$ [class]{}-[func]{editDistanceDPComp} ``` -=== "C" - - ```c title="edit_distance.c" - [class]{}-[func]{editDistanceDPComp} - ``` - -=== "C#" - - ```csharp title="edit_distance.cs" - [class]{edit_distance}-[func]{editDistanceDPComp} - ``` - -=== "Swift" - - ```swift title="edit_distance.swift" - [class]{}-[func]{editDistanceDPComp} - ``` - -=== "Zig" - - ```zig title="edit_distance.zig" - [class]{}-[func]{editDistanceDPComp} - ``` - === "Dart" ```dart title="edit_distance.dart" @@ -263,3 +251,15 @@ $$ ```rust title="edit_distance.rs" [class]{}-[func]{edit_distance_dp_comp} ``` + +=== "C" + + ```c title="edit_distance.c" + [class]{}-[func]{editDistanceDPComp} + ``` + +=== "Zig" + + ```zig title="edit_distance.zig" + [class]{}-[func]{editDistanceDPComp} + ``` diff --git a/docs/chapter_dynamic_programming/intro_to_dynamic_programming.md b/docs/chapter_dynamic_programming/intro_to_dynamic_programming.md index 323ce3f7..e2341760 100644 --- a/docs/chapter_dynamic_programming/intro_to_dynamic_programming.md +++ b/docs/chapter_dynamic_programming/intro_to_dynamic_programming.md @@ -14,12 +14,12 @@ 本题的目标是求解方案数量,**我们可以考虑通过回溯来穷举所有可能性**。具体来说,将爬楼梯想象为一个多轮选择的过程:从地面出发,每轮选择上 $1$ 阶或 $2$ 阶,每当到达楼梯顶部时就将方案数量加 $1$ ,当越过楼梯顶部时就将其剪枝。 -=== "Java" +=== "Python" - ```java title="climbing_stairs_backtrack.java" - [class]{climbing_stairs_backtrack}-[func]{backtrack} + ```python title="climbing_stairs_backtrack.py" + [class]{}-[func]{backtrack} - [class]{climbing_stairs_backtrack}-[func]{climbingStairsBacktrack} + [class]{}-[func]{climbing_stairs_backtrack} ``` === "C++" @@ -30,12 +30,20 @@ [class]{}-[func]{climbingStairsBacktrack} ``` -=== "Python" +=== "Java" - ```python title="climbing_stairs_backtrack.py" - [class]{}-[func]{backtrack} + ```java title="climbing_stairs_backtrack.java" + [class]{climbing_stairs_backtrack}-[func]{backtrack} - [class]{}-[func]{climbing_stairs_backtrack} + [class]{climbing_stairs_backtrack}-[func]{climbingStairsBacktrack} + ``` + +=== "C#" + + ```csharp title="climbing_stairs_backtrack.cs" + [class]{climbing_stairs_backtrack}-[func]{backtrack} + + [class]{climbing_stairs_backtrack}-[func]{climbingStairsBacktrack} ``` === "Go" @@ -46,6 +54,14 @@ [class]{}-[func]{climbingStairsBacktrack} ``` +=== "Swift" + + ```swift title="climbing_stairs_backtrack.swift" + [class]{}-[func]{backtrack} + + [class]{}-[func]{climbingStairsBacktrack} + ``` + === "JS" ```javascript title="climbing_stairs_backtrack.js" @@ -62,38 +78,6 @@ [class]{}-[func]{climbingStairsBacktrack} ``` -=== "C" - - ```c title="climbing_stairs_backtrack.c" - [class]{}-[func]{backtrack} - - [class]{}-[func]{climbingStairsBacktrack} - ``` - -=== "C#" - - ```csharp title="climbing_stairs_backtrack.cs" - [class]{climbing_stairs_backtrack}-[func]{backtrack} - - [class]{climbing_stairs_backtrack}-[func]{climbingStairsBacktrack} - ``` - -=== "Swift" - - ```swift title="climbing_stairs_backtrack.swift" - [class]{}-[func]{backtrack} - - [class]{}-[func]{climbingStairsBacktrack} - ``` - -=== "Zig" - - ```zig title="climbing_stairs_backtrack.zig" - [class]{}-[func]{backtrack} - - [class]{}-[func]{climbingStairsBacktrack} - ``` - === "Dart" ```dart title="climbing_stairs_backtrack.dart" @@ -110,6 +94,22 @@ [class]{}-[func]{climbing_stairs_backtrack} ``` +=== "C" + + ```c title="climbing_stairs_backtrack.c" + [class]{}-[func]{backtrack} + + [class]{}-[func]{climbingStairsBacktrack} + ``` + +=== "Zig" + + ```zig title="climbing_stairs_backtrack.zig" + [class]{}-[func]{backtrack} + + [class]{}-[func]{climbingStairsBacktrack} + ``` + ## 方法一:暴力搜索 回溯算法通常并不显式地对问题进行拆解,而是将问题看作一系列决策步骤,通过试探和剪枝,搜索所有可能的解。 @@ -136,12 +136,12 @@ $$ 观察以下代码,它和标准回溯代码都属于深度优先搜索,但更加简洁。 -=== "Java" +=== "Python" - ```java title="climbing_stairs_dfs.java" - [class]{climbing_stairs_dfs}-[func]{dfs} + ```python title="climbing_stairs_dfs.py" + [class]{}-[func]{dfs} - [class]{climbing_stairs_dfs}-[func]{climbingStairsDFS} + [class]{}-[func]{climbing_stairs_dfs} ``` === "C++" @@ -152,12 +152,20 @@ $$ [class]{}-[func]{climbingStairsDFS} ``` -=== "Python" +=== "Java" - ```python title="climbing_stairs_dfs.py" - [class]{}-[func]{dfs} + ```java title="climbing_stairs_dfs.java" + [class]{climbing_stairs_dfs}-[func]{dfs} - [class]{}-[func]{climbing_stairs_dfs} + [class]{climbing_stairs_dfs}-[func]{climbingStairsDFS} + ``` + +=== "C#" + + ```csharp title="climbing_stairs_dfs.cs" + [class]{climbing_stairs_dfs}-[func]{dfs} + + [class]{climbing_stairs_dfs}-[func]{climbingStairsDFS} ``` === "Go" @@ -168,6 +176,14 @@ $$ [class]{}-[func]{climbingStairsDFS} ``` +=== "Swift" + + ```swift title="climbing_stairs_dfs.swift" + [class]{}-[func]{dfs} + + [class]{}-[func]{climbingStairsDFS} + ``` + === "JS" ```javascript title="climbing_stairs_dfs.js" @@ -184,38 +200,6 @@ $$ [class]{}-[func]{climbingStairsDFS} ``` -=== "C" - - ```c title="climbing_stairs_dfs.c" - [class]{}-[func]{dfs} - - [class]{}-[func]{climbingStairsDFS} - ``` - -=== "C#" - - ```csharp title="climbing_stairs_dfs.cs" - [class]{climbing_stairs_dfs}-[func]{dfs} - - [class]{climbing_stairs_dfs}-[func]{climbingStairsDFS} - ``` - -=== "Swift" - - ```swift title="climbing_stairs_dfs.swift" - [class]{}-[func]{dfs} - - [class]{}-[func]{climbingStairsDFS} - ``` - -=== "Zig" - - ```zig title="climbing_stairs_dfs.zig" - [class]{}-[func]{dfs} - - [class]{}-[func]{climbingStairsDFS} - ``` - === "Dart" ```dart title="climbing_stairs_dfs.dart" @@ -232,6 +216,22 @@ $$ [class]{}-[func]{climbing_stairs_dfs} ``` +=== "C" + + ```c title="climbing_stairs_dfs.c" + [class]{}-[func]{dfs} + + [class]{}-[func]{climbingStairsDFS} + ``` + +=== "Zig" + + ```zig title="climbing_stairs_dfs.zig" + [class]{}-[func]{dfs} + + [class]{}-[func]{climbingStairsDFS} + ``` + 下图展示了暴力搜索形成的递归树。对于问题 $dp[n]$ ,其递归树的深度为 $n$ ,时间复杂度为 $O(2^n)$ 。指数阶属于爆炸式增长,如果我们输入一个比较大的 $n$ ,则会陷入漫长的等待之中。 ![爬楼梯对应递归树](intro_to_dynamic_programming.assets/climbing_stairs_dfs_tree.png) @@ -247,12 +247,12 @@ $$ 1. 当首次计算 $dp[i]$ 时,我们将其记录至 `mem[i]` ,以便之后使用。 2. 当再次需要计算 $dp[i]$ 时,我们便可直接从 `mem[i]` 中获取结果,从而避免重复计算该子问题。 -=== "Java" +=== "Python" - ```java title="climbing_stairs_dfs_mem.java" - [class]{climbing_stairs_dfs_mem}-[func]{dfs} + ```python title="climbing_stairs_dfs_mem.py" + [class]{}-[func]{dfs} - [class]{climbing_stairs_dfs_mem}-[func]{climbingStairsDFSMem} + [class]{}-[func]{climbing_stairs_dfs_mem} ``` === "C++" @@ -263,12 +263,20 @@ $$ [class]{}-[func]{climbingStairsDFSMem} ``` -=== "Python" +=== "Java" - ```python title="climbing_stairs_dfs_mem.py" - [class]{}-[func]{dfs} + ```java title="climbing_stairs_dfs_mem.java" + [class]{climbing_stairs_dfs_mem}-[func]{dfs} - [class]{}-[func]{climbing_stairs_dfs_mem} + [class]{climbing_stairs_dfs_mem}-[func]{climbingStairsDFSMem} + ``` + +=== "C#" + + ```csharp title="climbing_stairs_dfs_mem.cs" + [class]{climbing_stairs_dfs_mem}-[func]{dfs} + + [class]{climbing_stairs_dfs_mem}-[func]{climbingStairsDFSMem} ``` === "Go" @@ -279,6 +287,14 @@ $$ [class]{}-[func]{climbingStairsDFSMem} ``` +=== "Swift" + + ```swift title="climbing_stairs_dfs_mem.swift" + [class]{}-[func]{dfs} + + [class]{}-[func]{climbingStairsDFSMem} + ``` + === "JS" ```javascript title="climbing_stairs_dfs_mem.js" @@ -295,38 +311,6 @@ $$ [class]{}-[func]{climbingStairsDFSMem} ``` -=== "C" - - ```c title="climbing_stairs_dfs_mem.c" - [class]{}-[func]{dfs} - - [class]{}-[func]{climbingStairsDFSMem} - ``` - -=== "C#" - - ```csharp title="climbing_stairs_dfs_mem.cs" - [class]{climbing_stairs_dfs_mem}-[func]{dfs} - - [class]{climbing_stairs_dfs_mem}-[func]{climbingStairsDFSMem} - ``` - -=== "Swift" - - ```swift title="climbing_stairs_dfs_mem.swift" - [class]{}-[func]{dfs} - - [class]{}-[func]{climbingStairsDFSMem} - ``` - -=== "Zig" - - ```zig title="climbing_stairs_dfs_mem.zig" - [class]{}-[func]{dfs} - - [class]{}-[func]{climbingStairsDFSMem} - ``` - === "Dart" ```dart title="climbing_stairs_dfs_mem.dart" @@ -343,6 +327,22 @@ $$ [class]{}-[func]{climbing_stairs_dfs_mem} ``` +=== "C" + + ```c title="climbing_stairs_dfs_mem.c" + [class]{}-[func]{dfs} + + [class]{}-[func]{climbingStairsDFSMem} + ``` + +=== "Zig" + + ```zig title="climbing_stairs_dfs_mem.zig" + [class]{}-[func]{dfs} + + [class]{}-[func]{climbingStairsDFSMem} + ``` + 观察下图,**经过记忆化处理后,所有重叠子问题都只需被计算一次,时间复杂度被优化至 $O(n)$** ,这是一个巨大的飞跃。 ![记忆化搜索对应递归树](intro_to_dynamic_programming.assets/climbing_stairs_dfs_memo_tree.png) @@ -355,10 +355,10 @@ $$ 由于动态规划不包含回溯过程,因此只需使用循环迭代实现,无须使用递归。在以下代码中,我们初始化一个数组 `dp` 来存储子问题的解,它起到了记忆化搜索中数组 `mem` 相同的记录作用。 -=== "Java" +=== "Python" - ```java title="climbing_stairs_dp.java" - [class]{climbing_stairs_dp}-[func]{climbingStairsDP} + ```python title="climbing_stairs_dp.py" + [class]{}-[func]{climbing_stairs_dp} ``` === "C++" @@ -367,10 +367,16 @@ $$ [class]{}-[func]{climbingStairsDP} ``` -=== "Python" +=== "Java" - ```python title="climbing_stairs_dp.py" - [class]{}-[func]{climbing_stairs_dp} + ```java title="climbing_stairs_dp.java" + [class]{climbing_stairs_dp}-[func]{climbingStairsDP} + ``` + +=== "C#" + + ```csharp title="climbing_stairs_dp.cs" + [class]{climbing_stairs_dp}-[func]{climbingStairsDP} ``` === "Go" @@ -379,6 +385,12 @@ $$ [class]{}-[func]{climbingStairsDP} ``` +=== "Swift" + + ```swift title="climbing_stairs_dp.swift" + [class]{}-[func]{climbingStairsDP} + ``` + === "JS" ```javascript title="climbing_stairs_dp.js" @@ -391,30 +403,6 @@ $$ [class]{}-[func]{climbingStairsDP} ``` -=== "C" - - ```c title="climbing_stairs_dp.c" - [class]{}-[func]{climbingStairsDP} - ``` - -=== "C#" - - ```csharp title="climbing_stairs_dp.cs" - [class]{climbing_stairs_dp}-[func]{climbingStairsDP} - ``` - -=== "Swift" - - ```swift title="climbing_stairs_dp.swift" - [class]{}-[func]{climbingStairsDP} - ``` - -=== "Zig" - - ```zig title="climbing_stairs_dp.zig" - [class]{}-[func]{climbingStairsDP} - ``` - === "Dart" ```dart title="climbing_stairs_dp.dart" @@ -427,6 +415,18 @@ $$ [class]{}-[func]{climbing_stairs_dp} ``` +=== "C" + + ```c title="climbing_stairs_dp.c" + [class]{}-[func]{climbingStairsDP} + ``` + +=== "Zig" + + ```zig title="climbing_stairs_dp.zig" + [class]{}-[func]{climbingStairsDP} + ``` + 下图模拟了以上代码的执行过程。 ![爬楼梯的动态规划过程](intro_to_dynamic_programming.assets/climbing_stairs_dp.png) @@ -443,10 +443,10 @@ $$ 细心的你可能发现,**由于 $dp[i]$ 只与 $dp[i-1]$ 和 $dp[i-2]$ 有关,因此我们无须使用一个数组 `dp` 来存储所有子问题的解**,而只需两个变量滚动前进即可。 -=== "Java" +=== "Python" - ```java title="climbing_stairs_dp.java" - [class]{climbing_stairs_dp}-[func]{climbingStairsDPComp} + ```python title="climbing_stairs_dp.py" + [class]{}-[func]{climbing_stairs_dp_comp} ``` === "C++" @@ -455,10 +455,16 @@ $$ [class]{}-[func]{climbingStairsDPComp} ``` -=== "Python" +=== "Java" - ```python title="climbing_stairs_dp.py" - [class]{}-[func]{climbing_stairs_dp_comp} + ```java title="climbing_stairs_dp.java" + [class]{climbing_stairs_dp}-[func]{climbingStairsDPComp} + ``` + +=== "C#" + + ```csharp title="climbing_stairs_dp.cs" + [class]{climbing_stairs_dp}-[func]{climbingStairsDPComp} ``` === "Go" @@ -467,6 +473,12 @@ $$ [class]{}-[func]{climbingStairsDPComp} ``` +=== "Swift" + + ```swift title="climbing_stairs_dp.swift" + [class]{}-[func]{climbingStairsDPComp} + ``` + === "JS" ```javascript title="climbing_stairs_dp.js" @@ -479,30 +491,6 @@ $$ [class]{}-[func]{climbingStairsDPComp} ``` -=== "C" - - ```c title="climbing_stairs_dp.c" - [class]{}-[func]{climbingStairsDPComp} - ``` - -=== "C#" - - ```csharp title="climbing_stairs_dp.cs" - [class]{climbing_stairs_dp}-[func]{climbingStairsDPComp} - ``` - -=== "Swift" - - ```swift title="climbing_stairs_dp.swift" - [class]{}-[func]{climbingStairsDPComp} - ``` - -=== "Zig" - - ```zig title="climbing_stairs_dp.zig" - [class]{}-[func]{climbingStairsDPComp} - ``` - === "Dart" ```dart title="climbing_stairs_dp.dart" @@ -515,6 +503,18 @@ $$ [class]{}-[func]{climbing_stairs_dp_comp} ``` +=== "C" + + ```c title="climbing_stairs_dp.c" + [class]{}-[func]{climbingStairsDPComp} + ``` + +=== "Zig" + + ```zig title="climbing_stairs_dp.zig" + [class]{}-[func]{climbingStairsDPComp} + ``` + 观察以上代码,由于省去了数组 `dp` 占用的空间,因此空间复杂度从 $O(n)$ 降低至 $O(1)$ 。 在动态规划问题中,当前状态往往仅与前面有限个状态有关,这时我们可以只保留必要的状态,通过“降维”来节省内存空间。**这种空间优化技巧被称为“滚动变量”或“滚动数组”**。 diff --git a/docs/chapter_dynamic_programming/knapsack_problem.md b/docs/chapter_dynamic_programming/knapsack_problem.md index 11b5fd79..8843acc3 100644 --- a/docs/chapter_dynamic_programming/knapsack_problem.md +++ b/docs/chapter_dynamic_programming/knapsack_problem.md @@ -56,10 +56,10 @@ $$ - **终止条件**:当物品编号越界 $i = 0$ 或背包剩余容量为 $0$ 时,终止递归并返回价值 $0$ 。 - **剪枝**:若当前物品重量超出背包剩余容量,则只能不放入背包。 -=== "Java" +=== "Python" - ```java title="knapsack.java" - [class]{knapsack}-[func]{knapsackDFS} + ```python title="knapsack.py" + [class]{}-[func]{knapsack_dfs} ``` === "C++" @@ -68,10 +68,16 @@ $$ [class]{}-[func]{knapsackDFS} ``` -=== "Python" +=== "Java" - ```python title="knapsack.py" - [class]{}-[func]{knapsack_dfs} + ```java title="knapsack.java" + [class]{knapsack}-[func]{knapsackDFS} + ``` + +=== "C#" + + ```csharp title="knapsack.cs" + [class]{knapsack}-[func]{knapsackDFS} ``` === "Go" @@ -80,6 +86,12 @@ $$ [class]{}-[func]{knapsackDFS} ``` +=== "Swift" + + ```swift title="knapsack.swift" + [class]{}-[func]{knapsackDFS} + ``` + === "JS" ```javascript title="knapsack.js" @@ -92,30 +104,6 @@ $$ [class]{}-[func]{knapsackDFS} ``` -=== "C" - - ```c title="knapsack.c" - [class]{}-[func]{knapsackDFS} - ``` - -=== "C#" - - ```csharp title="knapsack.cs" - [class]{knapsack}-[func]{knapsackDFS} - ``` - -=== "Swift" - - ```swift title="knapsack.swift" - [class]{}-[func]{knapsackDFS} - ``` - -=== "Zig" - - ```zig title="knapsack.zig" - [class]{}-[func]{knapsackDFS} - ``` - === "Dart" ```dart title="knapsack.dart" @@ -128,6 +116,18 @@ $$ [class]{}-[func]{knapsack_dfs} ``` +=== "C" + + ```c title="knapsack.c" + [class]{}-[func]{knapsackDFS} + ``` + +=== "Zig" + + ```zig title="knapsack.zig" + [class]{}-[func]{knapsackDFS} + ``` + 如下图所示,由于每个物品都会产生不选和选两条搜索分支,因此时间复杂度为 $O(2^n)$ 。 观察递归树,容易发现其中存在重叠子问题,例如 $dp[1, 10]$ 等。而当物品较多、背包容量较大,尤其是相同重量的物品较多时,重叠子问题的数量将会大幅增多。 @@ -140,10 +140,10 @@ $$ 引入记忆化之后,**时间复杂度取决于子问题数量**,也就是 $O(n \times cap)$ 。 -=== "Java" +=== "Python" - ```java title="knapsack.java" - [class]{knapsack}-[func]{knapsackDFSMem} + ```python title="knapsack.py" + [class]{}-[func]{knapsack_dfs_mem} ``` === "C++" @@ -152,10 +152,16 @@ $$ [class]{}-[func]{knapsackDFSMem} ``` -=== "Python" +=== "Java" - ```python title="knapsack.py" - [class]{}-[func]{knapsack_dfs_mem} + ```java title="knapsack.java" + [class]{knapsack}-[func]{knapsackDFSMem} + ``` + +=== "C#" + + ```csharp title="knapsack.cs" + [class]{knapsack}-[func]{knapsackDFSMem} ``` === "Go" @@ -164,6 +170,12 @@ $$ [class]{}-[func]{knapsackDFSMem} ``` +=== "Swift" + + ```swift title="knapsack.swift" + [class]{}-[func]{knapsackDFSMem} + ``` + === "JS" ```javascript title="knapsack.js" @@ -176,30 +188,6 @@ $$ [class]{}-[func]{knapsackDFSMem} ``` -=== "C" - - ```c title="knapsack.c" - [class]{}-[func]{knapsackDFSMem} - ``` - -=== "C#" - - ```csharp title="knapsack.cs" - [class]{knapsack}-[func]{knapsackDFSMem} - ``` - -=== "Swift" - - ```swift title="knapsack.swift" - [class]{}-[func]{knapsackDFSMem} - ``` - -=== "Zig" - - ```zig title="knapsack.zig" - [class]{}-[func]{knapsackDFSMem} - ``` - === "Dart" ```dart title="knapsack.dart" @@ -212,6 +200,18 @@ $$ [class]{}-[func]{knapsack_dfs_mem} ``` +=== "C" + + ```c title="knapsack.c" + [class]{}-[func]{knapsackDFSMem} + ``` + +=== "Zig" + + ```zig title="knapsack.zig" + [class]{}-[func]{knapsackDFSMem} + ``` + 下图展示了在记忆化递归中被剪掉的搜索分支。 ![0-1 背包的记忆化搜索递归树](knapsack_problem.assets/knapsack_dfs_mem.png) @@ -220,10 +220,10 @@ $$ 动态规划实质上就是在状态转移中填充 $dp$ 表的过程,代码如下所示。 -=== "Java" +=== "Python" - ```java title="knapsack.java" - [class]{knapsack}-[func]{knapsackDP} + ```python title="knapsack.py" + [class]{}-[func]{knapsack_dp} ``` === "C++" @@ -232,10 +232,16 @@ $$ [class]{}-[func]{knapsackDP} ``` -=== "Python" +=== "Java" - ```python title="knapsack.py" - [class]{}-[func]{knapsack_dp} + ```java title="knapsack.java" + [class]{knapsack}-[func]{knapsackDP} + ``` + +=== "C#" + + ```csharp title="knapsack.cs" + [class]{knapsack}-[func]{knapsackDP} ``` === "Go" @@ -244,6 +250,12 @@ $$ [class]{}-[func]{knapsackDP} ``` +=== "Swift" + + ```swift title="knapsack.swift" + [class]{}-[func]{knapsackDP} + ``` + === "JS" ```javascript title="knapsack.js" @@ -256,30 +268,6 @@ $$ [class]{}-[func]{knapsackDP} ``` -=== "C" - - ```c title="knapsack.c" - [class]{}-[func]{knapsackDP} - ``` - -=== "C#" - - ```csharp title="knapsack.cs" - [class]{knapsack}-[func]{knapsackDP} - ``` - -=== "Swift" - - ```swift title="knapsack.swift" - [class]{}-[func]{knapsackDP} - ``` - -=== "Zig" - - ```zig title="knapsack.zig" - [class]{}-[func]{knapsackDP} - ``` - === "Dart" ```dart title="knapsack.dart" @@ -292,6 +280,18 @@ $$ [class]{}-[func]{knapsack_dp} ``` +=== "C" + + ```c title="knapsack.c" + [class]{}-[func]{knapsackDP} + ``` + +=== "Zig" + + ```zig title="knapsack.zig" + [class]{}-[func]{knapsackDP} + ``` + 如下图所示,时间复杂度和空间复杂度都由数组 `dp` 大小决定,即 $O(n \times cap)$ 。 === "<1>" @@ -367,10 +367,10 @@ $$ 在代码实现中,我们仅需将数组 `dp` 的第一维 $i$ 直接删除,并且把内循环更改为倒序遍历即可。 -=== "Java" +=== "Python" - ```java title="knapsack.java" - [class]{knapsack}-[func]{knapsackDPComp} + ```python title="knapsack.py" + [class]{}-[func]{knapsack_dp_comp} ``` === "C++" @@ -379,10 +379,16 @@ $$ [class]{}-[func]{knapsackDPComp} ``` -=== "Python" +=== "Java" - ```python title="knapsack.py" - [class]{}-[func]{knapsack_dp_comp} + ```java title="knapsack.java" + [class]{knapsack}-[func]{knapsackDPComp} + ``` + +=== "C#" + + ```csharp title="knapsack.cs" + [class]{knapsack}-[func]{knapsackDPComp} ``` === "Go" @@ -391,6 +397,12 @@ $$ [class]{}-[func]{knapsackDPComp} ``` +=== "Swift" + + ```swift title="knapsack.swift" + [class]{}-[func]{knapsackDPComp} + ``` + === "JS" ```javascript title="knapsack.js" @@ -403,30 +415,6 @@ $$ [class]{}-[func]{knapsackDPComp} ``` -=== "C" - - ```c title="knapsack.c" - [class]{}-[func]{knapsackDPComp} - ``` - -=== "C#" - - ```csharp title="knapsack.cs" - [class]{knapsack}-[func]{knapsackDPComp} - ``` - -=== "Swift" - - ```swift title="knapsack.swift" - [class]{}-[func]{knapsackDPComp} - ``` - -=== "Zig" - - ```zig title="knapsack.zig" - [class]{}-[func]{knapsackDPComp} - ``` - === "Dart" ```dart title="knapsack.dart" @@ -438,3 +426,15 @@ $$ ```rust title="knapsack.rs" [class]{}-[func]{knapsack_dp_comp} ``` + +=== "C" + + ```c title="knapsack.c" + [class]{}-[func]{knapsackDPComp} + ``` + +=== "Zig" + + ```zig title="knapsack.zig" + [class]{}-[func]{knapsackDPComp} + ``` diff --git a/docs/chapter_dynamic_programming/unbounded_knapsack_problem.md b/docs/chapter_dynamic_programming/unbounded_knapsack_problem.md index e18a5001..50fd0577 100644 --- a/docs/chapter_dynamic_programming/unbounded_knapsack_problem.md +++ b/docs/chapter_dynamic_programming/unbounded_knapsack_problem.md @@ -32,10 +32,10 @@ $$ 对比两道题目的代码,状态转移中有一处从 $i-1$ 变为 $i$ ,其余完全一致。 -=== "Java" +=== "Python" - ```java title="unbounded_knapsack.java" - [class]{unbounded_knapsack}-[func]{unboundedKnapsackDP} + ```python title="unbounded_knapsack.py" + [class]{}-[func]{unbounded_knapsack_dp} ``` === "C++" @@ -44,10 +44,16 @@ $$ [class]{}-[func]{unboundedKnapsackDP} ``` -=== "Python" +=== "Java" - ```python title="unbounded_knapsack.py" - [class]{}-[func]{unbounded_knapsack_dp} + ```java title="unbounded_knapsack.java" + [class]{unbounded_knapsack}-[func]{unboundedKnapsackDP} + ``` + +=== "C#" + + ```csharp title="unbounded_knapsack.cs" + [class]{unbounded_knapsack}-[func]{unboundedKnapsackDP} ``` === "Go" @@ -56,6 +62,12 @@ $$ [class]{}-[func]{unboundedKnapsackDP} ``` +=== "Swift" + + ```swift title="unbounded_knapsack.swift" + [class]{}-[func]{unboundedKnapsackDP} + ``` + === "JS" ```javascript title="unbounded_knapsack.js" @@ -68,30 +80,6 @@ $$ [class]{}-[func]{unboundedKnapsackDP} ``` -=== "C" - - ```c title="unbounded_knapsack.c" - [class]{}-[func]{unboundedKnapsackDP} - ``` - -=== "C#" - - ```csharp title="unbounded_knapsack.cs" - [class]{unbounded_knapsack}-[func]{unboundedKnapsackDP} - ``` - -=== "Swift" - - ```swift title="unbounded_knapsack.swift" - [class]{}-[func]{unboundedKnapsackDP} - ``` - -=== "Zig" - - ```zig title="unbounded_knapsack.zig" - [class]{}-[func]{unboundedKnapsackDP} - ``` - === "Dart" ```dart title="unbounded_knapsack.dart" @@ -104,6 +92,18 @@ $$ [class]{}-[func]{unbounded_knapsack_dp} ``` +=== "C" + + ```c title="unbounded_knapsack.c" + [class]{}-[func]{unboundedKnapsackDP} + ``` + +=== "Zig" + + ```zig title="unbounded_knapsack.zig" + [class]{}-[func]{unboundedKnapsackDP} + ``` + ### 空间优化 由于当前状态是从左边和上边的状态转移而来,**因此空间优化后应该对 $dp$ 表中的每一行采取正序遍历**。 @@ -130,10 +130,10 @@ $$ 代码实现比较简单,仅需将数组 `dp` 的第一维删除。 -=== "Java" +=== "Python" - ```java title="unbounded_knapsack.java" - [class]{unbounded_knapsack}-[func]{unboundedKnapsackDPComp} + ```python title="unbounded_knapsack.py" + [class]{}-[func]{unbounded_knapsack_dp_comp} ``` === "C++" @@ -142,10 +142,16 @@ $$ [class]{}-[func]{unboundedKnapsackDPComp} ``` -=== "Python" +=== "Java" - ```python title="unbounded_knapsack.py" - [class]{}-[func]{unbounded_knapsack_dp_comp} + ```java title="unbounded_knapsack.java" + [class]{unbounded_knapsack}-[func]{unboundedKnapsackDPComp} + ``` + +=== "C#" + + ```csharp title="unbounded_knapsack.cs" + [class]{unbounded_knapsack}-[func]{unboundedKnapsackDPComp} ``` === "Go" @@ -154,6 +160,12 @@ $$ [class]{}-[func]{unboundedKnapsackDPComp} ``` +=== "Swift" + + ```swift title="unbounded_knapsack.swift" + [class]{}-[func]{unboundedKnapsackDPComp} + ``` + === "JS" ```javascript title="unbounded_knapsack.js" @@ -166,30 +178,6 @@ $$ [class]{}-[func]{unboundedKnapsackDPComp} ``` -=== "C" - - ```c title="unbounded_knapsack.c" - [class]{}-[func]{unboundedKnapsackDPComp} - ``` - -=== "C#" - - ```csharp title="unbounded_knapsack.cs" - [class]{unbounded_knapsack}-[func]{unboundedKnapsackDPComp} - ``` - -=== "Swift" - - ```swift title="unbounded_knapsack.swift" - [class]{}-[func]{unboundedKnapsackDPComp} - ``` - -=== "Zig" - - ```zig title="unbounded_knapsack.zig" - [class]{}-[func]{unboundedKnapsackDPComp} - ``` - === "Dart" ```dart title="unbounded_knapsack.dart" @@ -202,6 +190,18 @@ $$ [class]{}-[func]{unbounded_knapsack_dp_comp} ``` +=== "C" + + ```c title="unbounded_knapsack.c" + [class]{}-[func]{unboundedKnapsackDPComp} + ``` + +=== "Zig" + + ```zig title="unbounded_knapsack.zig" + [class]{}-[func]{unboundedKnapsackDPComp} + ``` + ## 零钱兑换问题 背包问题是一大类动态规划问题的代表,其拥有很多的变种,例如零钱兑换问题。 @@ -251,10 +251,10 @@ $$ 最后返回前,判断 $dp[n, amt]$ 是否等于 $amt + 1$ ,若是则返回 $-1$ ,代表无法凑出目标金额。 -=== "Java" +=== "Python" - ```java title="coin_change.java" - [class]{coin_change}-[func]{coinChangeDP} + ```python title="coin_change.py" + [class]{}-[func]{coin_change_dp} ``` === "C++" @@ -263,10 +263,16 @@ $$ [class]{}-[func]{coinChangeDP} ``` -=== "Python" +=== "Java" - ```python title="coin_change.py" - [class]{}-[func]{coin_change_dp} + ```java title="coin_change.java" + [class]{coin_change}-[func]{coinChangeDP} + ``` + +=== "C#" + + ```csharp title="coin_change.cs" + [class]{coin_change}-[func]{coinChangeDP} ``` === "Go" @@ -275,6 +281,12 @@ $$ [class]{}-[func]{coinChangeDP} ``` +=== "Swift" + + ```swift title="coin_change.swift" + [class]{}-[func]{coinChangeDP} + ``` + === "JS" ```javascript title="coin_change.js" @@ -287,30 +299,6 @@ $$ [class]{}-[func]{coinChangeDP} ``` -=== "C" - - ```c title="coin_change.c" - [class]{}-[func]{coinChangeDP} - ``` - -=== "C#" - - ```csharp title="coin_change.cs" - [class]{coin_change}-[func]{coinChangeDP} - ``` - -=== "Swift" - - ```swift title="coin_change.swift" - [class]{}-[func]{coinChangeDP} - ``` - -=== "Zig" - - ```zig title="coin_change.zig" - [class]{}-[func]{coinChangeDP} - ``` - === "Dart" ```dart title="coin_change.dart" @@ -323,6 +311,18 @@ $$ [class]{}-[func]{coin_change_dp} ``` +=== "C" + + ```c title="coin_change.c" + [class]{}-[func]{coinChangeDP} + ``` + +=== "Zig" + + ```zig title="coin_change.zig" + [class]{}-[func]{coinChangeDP} + ``` + 下图展示了零钱兑换的动态规划过程,和完全背包非常相似。 === "<1>" @@ -374,10 +374,10 @@ $$ 零钱兑换的空间优化的处理方式和完全背包一致。 -=== "Java" +=== "Python" - ```java title="coin_change.java" - [class]{coin_change}-[func]{coinChangeDPComp} + ```python title="coin_change.py" + [class]{}-[func]{coin_change_dp_comp} ``` === "C++" @@ -386,10 +386,16 @@ $$ [class]{}-[func]{coinChangeDPComp} ``` -=== "Python" +=== "Java" - ```python title="coin_change.py" - [class]{}-[func]{coin_change_dp_comp} + ```java title="coin_change.java" + [class]{coin_change}-[func]{coinChangeDPComp} + ``` + +=== "C#" + + ```csharp title="coin_change.cs" + [class]{coin_change}-[func]{coinChangeDPComp} ``` === "Go" @@ -398,6 +404,12 @@ $$ [class]{}-[func]{coinChangeDPComp} ``` +=== "Swift" + + ```swift title="coin_change.swift" + [class]{}-[func]{coinChangeDPComp} + ``` + === "JS" ```javascript title="coin_change.js" @@ -410,30 +422,6 @@ $$ [class]{}-[func]{coinChangeDPComp} ``` -=== "C" - - ```c title="coin_change.c" - [class]{}-[func]{coinChangeDPComp} - ``` - -=== "C#" - - ```csharp title="coin_change.cs" - [class]{coin_change}-[func]{coinChangeDPComp} - ``` - -=== "Swift" - - ```swift title="coin_change.swift" - [class]{}-[func]{coinChangeDPComp} - ``` - -=== "Zig" - - ```zig title="coin_change.zig" - [class]{}-[func]{coinChangeDPComp} - ``` - === "Dart" ```dart title="coin_change.dart" @@ -446,6 +434,18 @@ $$ [class]{}-[func]{coin_change_dp_comp} ``` +=== "C" + + ```c title="coin_change.c" + [class]{}-[func]{coinChangeDPComp} + ``` + +=== "Zig" + + ```zig title="coin_change.zig" + [class]{}-[func]{coinChangeDPComp} + ``` + ## 零钱兑换问题 II !!! question @@ -468,10 +468,10 @@ $$ ### 代码实现 -=== "Java" +=== "Python" - ```java title="coin_change_ii.java" - [class]{coin_change_ii}-[func]{coinChangeIIDP} + ```python title="coin_change_ii.py" + [class]{}-[func]{coin_change_ii_dp} ``` === "C++" @@ -480,10 +480,16 @@ $$ [class]{}-[func]{coinChangeIIDP} ``` -=== "Python" +=== "Java" - ```python title="coin_change_ii.py" - [class]{}-[func]{coin_change_ii_dp} + ```java title="coin_change_ii.java" + [class]{coin_change_ii}-[func]{coinChangeIIDP} + ``` + +=== "C#" + + ```csharp title="coin_change_ii.cs" + [class]{coin_change_ii}-[func]{coinChangeIIDP} ``` === "Go" @@ -492,6 +498,12 @@ $$ [class]{}-[func]{coinChangeIIDP} ``` +=== "Swift" + + ```swift title="coin_change_ii.swift" + [class]{}-[func]{coinChangeIIDP} + ``` + === "JS" ```javascript title="coin_change_ii.js" @@ -504,30 +516,6 @@ $$ [class]{}-[func]{coinChangeIIDP} ``` -=== "C" - - ```c title="coin_change_ii.c" - [class]{}-[func]{coinChangeIIDP} - ``` - -=== "C#" - - ```csharp title="coin_change_ii.cs" - [class]{coin_change_ii}-[func]{coinChangeIIDP} - ``` - -=== "Swift" - - ```swift title="coin_change_ii.swift" - [class]{}-[func]{coinChangeIIDP} - ``` - -=== "Zig" - - ```zig title="coin_change_ii.zig" - [class]{}-[func]{coinChangeIIDP} - ``` - === "Dart" ```dart title="coin_change_ii.dart" @@ -540,14 +528,26 @@ $$ [class]{}-[func]{coin_change_ii_dp} ``` +=== "C" + + ```c title="coin_change_ii.c" + [class]{}-[func]{coinChangeIIDP} + ``` + +=== "Zig" + + ```zig title="coin_change_ii.zig" + [class]{}-[func]{coinChangeIIDP} + ``` + ### 空间优化 空间优化处理方式相同,删除硬币维度即可。 -=== "Java" +=== "Python" - ```java title="coin_change_ii.java" - [class]{coin_change_ii}-[func]{coinChangeIIDPComp} + ```python title="coin_change_ii.py" + [class]{}-[func]{coin_change_ii_dp_comp} ``` === "C++" @@ -556,10 +556,16 @@ $$ [class]{}-[func]{coinChangeIIDPComp} ``` -=== "Python" +=== "Java" - ```python title="coin_change_ii.py" - [class]{}-[func]{coin_change_ii_dp_comp} + ```java title="coin_change_ii.java" + [class]{coin_change_ii}-[func]{coinChangeIIDPComp} + ``` + +=== "C#" + + ```csharp title="coin_change_ii.cs" + [class]{coin_change_ii}-[func]{coinChangeIIDPComp} ``` === "Go" @@ -568,6 +574,12 @@ $$ [class]{}-[func]{coinChangeIIDPComp} ``` +=== "Swift" + + ```swift title="coin_change_ii.swift" + [class]{}-[func]{coinChangeIIDPComp} + ``` + === "JS" ```javascript title="coin_change_ii.js" @@ -580,30 +592,6 @@ $$ [class]{}-[func]{coinChangeIIDPComp} ``` -=== "C" - - ```c title="coin_change_ii.c" - [class]{}-[func]{coinChangeIIDPComp} - ``` - -=== "C#" - - ```csharp title="coin_change_ii.cs" - [class]{coin_change_ii}-[func]{coinChangeIIDPComp} - ``` - -=== "Swift" - - ```swift title="coin_change_ii.swift" - [class]{}-[func]{coinChangeIIDPComp} - ``` - -=== "Zig" - - ```zig title="coin_change_ii.zig" - [class]{}-[func]{coinChangeIIDPComp} - ``` - === "Dart" ```dart title="coin_change_ii.dart" @@ -615,3 +603,15 @@ $$ ```rust title="coin_change_ii.rs" [class]{}-[func]{coin_change_ii_dp_comp} ``` + +=== "C" + + ```c title="coin_change_ii.c" + [class]{}-[func]{coinChangeIIDPComp} + ``` + +=== "Zig" + + ```zig title="coin_change_ii.zig" + [class]{}-[func]{coinChangeIIDPComp} + ``` diff --git a/docs/chapter_graph/graph_operations.md b/docs/chapter_graph/graph_operations.md index 182a46dd..b0769814 100644 --- a/docs/chapter_graph/graph_operations.md +++ b/docs/chapter_graph/graph_operations.md @@ -28,9 +28,9 @@ 以下是基于邻接矩阵表示图的实现代码。 -=== "Java" +=== "Python" - ```java title="graph_adjacency_matrix.java" + ```python title="graph_adjacency_matrix.py" [class]{GraphAdjMat}-[func]{} ``` @@ -40,9 +40,15 @@ [class]{GraphAdjMat}-[func]{} ``` -=== "Python" +=== "Java" - ```python title="graph_adjacency_matrix.py" + ```java title="graph_adjacency_matrix.java" + [class]{GraphAdjMat}-[func]{} + ``` + +=== "C#" + + ```csharp title="graph_adjacency_matrix.cs" [class]{GraphAdjMat}-[func]{} ``` @@ -52,6 +58,12 @@ [class]{graphAdjMat}-[func]{} ``` +=== "Swift" + + ```swift title="graph_adjacency_matrix.swift" + [class]{GraphAdjMat}-[func]{} + ``` + === "JS" ```javascript title="graph_adjacency_matrix.js" @@ -64,30 +76,6 @@ [class]{GraphAdjMat}-[func]{} ``` -=== "C" - - ```c title="graph_adjacency_matrix.c" - [class]{graphAdjMat}-[func]{} - ``` - -=== "C#" - - ```csharp title="graph_adjacency_matrix.cs" - [class]{GraphAdjMat}-[func]{} - ``` - -=== "Swift" - - ```swift title="graph_adjacency_matrix.swift" - [class]{GraphAdjMat}-[func]{} - ``` - -=== "Zig" - - ```zig title="graph_adjacency_matrix.zig" - - ``` - === "Dart" ```dart title="graph_adjacency_matrix.dart" @@ -100,6 +88,18 @@ [class]{GraphAdjMat}-[func]{} ``` +=== "C" + + ```c title="graph_adjacency_matrix.c" + [class]{graphAdjMat}-[func]{} + ``` + +=== "Zig" + + ```zig title="graph_adjacency_matrix.zig" + + ``` + ## 基于邻接表的实现 设无向图的顶点总数为 $n$、边总数为 $m$ ,则可根据下图所示的方法实现各种操作。 @@ -131,9 +131,9 @@ 2. 如果类似邻接矩阵那样,使用顶点列表索引来区分不同顶点。那么,假设我们想要删除索引为 $i$ 的顶点,则需要遍历整个邻接表,将其中 $> i$ 的索引全部减 $1$ ,这样操作效率较低。 3. 因此我们考虑引入顶点类 `Vertex` ,使得每个顶点都是唯一的对象,此时删除顶点时就无须改动其余顶点了。 -=== "Java" +=== "Python" - ```java title="graph_adjacency_list.java" + ```python title="graph_adjacency_list.py" [class]{GraphAdjList}-[func]{} ``` @@ -143,9 +143,15 @@ [class]{GraphAdjList}-[func]{} ``` -=== "Python" +=== "Java" - ```python title="graph_adjacency_list.py" + ```java title="graph_adjacency_list.java" + [class]{GraphAdjList}-[func]{} + ``` + +=== "C#" + + ```csharp title="graph_adjacency_list.cs" [class]{GraphAdjList}-[func]{} ``` @@ -155,6 +161,12 @@ [class]{graphAdjList}-[func]{} ``` +=== "Swift" + + ```swift title="graph_adjacency_list.swift" + [class]{GraphAdjList}-[func]{} + ``` + === "JS" ```javascript title="graph_adjacency_list.js" @@ -167,30 +179,6 @@ [class]{GraphAdjList}-[func]{} ``` -=== "C" - - ```c title="graph_adjacency_list.c" - [class]{graphAdjList}-[func]{} - ``` - -=== "C#" - - ```csharp title="graph_adjacency_list.cs" - [class]{GraphAdjList}-[func]{} - ``` - -=== "Swift" - - ```swift title="graph_adjacency_list.swift" - [class]{GraphAdjList}-[func]{} - ``` - -=== "Zig" - - ```zig title="graph_adjacency_list.zig" - [class]{GraphAdjList}-[func]{} - ``` - === "Dart" ```dart title="graph_adjacency_list.dart" @@ -203,6 +191,18 @@ [class]{GraphAdjList}-[func]{} ``` +=== "C" + + ```c title="graph_adjacency_list.c" + [class]{graphAdjList}-[func]{} + ``` + +=== "Zig" + + ```zig title="graph_adjacency_list.zig" + [class]{GraphAdjList}-[func]{} + ``` + ## 效率对比 设图中共有 $n$ 个顶点和 $m$ 条边,下表对比了邻接矩阵和邻接表的时间和空间效率。 diff --git a/docs/chapter_graph/graph_traversal.md b/docs/chapter_graph/graph_traversal.md index b29ef7d0..450eb77d 100644 --- a/docs/chapter_graph/graph_traversal.md +++ b/docs/chapter_graph/graph_traversal.md @@ -20,10 +20,10 @@ BFS 通常借助队列来实现。队列具有“先入先出”的性质,这 为了防止重复遍历顶点,我们需要借助一个哈希表 `visited` 来记录哪些节点已被访问。 -=== "Java" +=== "Python" - ```java title="graph_bfs.java" - [class]{graph_bfs}-[func]{graphBFS} + ```python title="graph_bfs.py" + [class]{}-[func]{graph_bfs} ``` === "C++" @@ -32,10 +32,16 @@ BFS 通常借助队列来实现。队列具有“先入先出”的性质,这 [class]{}-[func]{graphBFS} ``` -=== "Python" +=== "Java" - ```python title="graph_bfs.py" - [class]{}-[func]{graph_bfs} + ```java title="graph_bfs.java" + [class]{graph_bfs}-[func]{graphBFS} + ``` + +=== "C#" + + ```csharp title="graph_bfs.cs" + [class]{graph_bfs}-[func]{graphBFS} ``` === "Go" @@ -44,6 +50,12 @@ BFS 通常借助队列来实现。队列具有“先入先出”的性质,这 [class]{}-[func]{graphBFS} ``` +=== "Swift" + + ```swift title="graph_bfs.swift" + [class]{}-[func]{graphBFS} + ``` + === "JS" ```javascript title="graph_bfs.js" @@ -56,30 +68,6 @@ BFS 通常借助队列来实现。队列具有“先入先出”的性质,这 [class]{}-[func]{graphBFS} ``` -=== "C" - - ```c title="graph_bfs.c" - [class]{}-[func]{graphBFS} - ``` - -=== "C#" - - ```csharp title="graph_bfs.cs" - [class]{graph_bfs}-[func]{graphBFS} - ``` - -=== "Swift" - - ```swift title="graph_bfs.swift" - [class]{}-[func]{graphBFS} - ``` - -=== "Zig" - - ```zig title="graph_bfs.zig" - [class]{}-[func]{graphBFS} - ``` - === "Dart" ```dart title="graph_bfs.dart" @@ -92,6 +80,18 @@ BFS 通常借助队列来实现。队列具有“先入先出”的性质,这 [class]{}-[func]{graph_bfs} ``` +=== "C" + + ```c title="graph_bfs.c" + [class]{}-[func]{graphBFS} + ``` + +=== "Zig" + + ```zig title="graph_bfs.zig" + [class]{}-[func]{graphBFS} + ``` + 代码相对抽象,建议对照下图来加深理解。 === "<1>" @@ -147,12 +147,12 @@ BFS 通常借助队列来实现。队列具有“先入先出”的性质,这 这种“走到尽头再返回”的算法范式通常基于递归来实现。与广度优先遍历类似,在深度优先遍历中我们也需要借助一个哈希表 `visited` 来记录已被访问的顶点,以避免重复访问顶点。 -=== "Java" +=== "Python" - ```java title="graph_dfs.java" - [class]{graph_dfs}-[func]{dfs} + ```python title="graph_dfs.py" + [class]{}-[func]{dfs} - [class]{graph_dfs}-[func]{graphDFS} + [class]{}-[func]{graph_dfs} ``` === "C++" @@ -163,12 +163,20 @@ BFS 通常借助队列来实现。队列具有“先入先出”的性质,这 [class]{}-[func]{graphDFS} ``` -=== "Python" +=== "Java" - ```python title="graph_dfs.py" - [class]{}-[func]{dfs} + ```java title="graph_dfs.java" + [class]{graph_dfs}-[func]{dfs} - [class]{}-[func]{graph_dfs} + [class]{graph_dfs}-[func]{graphDFS} + ``` + +=== "C#" + + ```csharp title="graph_dfs.cs" + [class]{graph_dfs}-[func]{dfs} + + [class]{graph_dfs}-[func]{graphDFS} ``` === "Go" @@ -179,6 +187,14 @@ BFS 通常借助队列来实现。队列具有“先入先出”的性质,这 [class]{}-[func]{graphDFS} ``` +=== "Swift" + + ```swift title="graph_dfs.swift" + [class]{}-[func]{dfs} + + [class]{}-[func]{graphDFS} + ``` + === "JS" ```javascript title="graph_dfs.js" @@ -195,38 +211,6 @@ BFS 通常借助队列来实现。队列具有“先入先出”的性质,这 [class]{}-[func]{graphDFS} ``` -=== "C" - - ```c title="graph_dfs.c" - [class]{}-[func]{dfs} - - [class]{}-[func]{graphDFS} - ``` - -=== "C#" - - ```csharp title="graph_dfs.cs" - [class]{graph_dfs}-[func]{dfs} - - [class]{graph_dfs}-[func]{graphDFS} - ``` - -=== "Swift" - - ```swift title="graph_dfs.swift" - [class]{}-[func]{dfs} - - [class]{}-[func]{graphDFS} - ``` - -=== "Zig" - - ```zig title="graph_dfs.zig" - [class]{}-[func]{dfs} - - [class]{}-[func]{graphDFS} - ``` - === "Dart" ```dart title="graph_dfs.dart" @@ -243,6 +227,22 @@ BFS 通常借助队列来实现。队列具有“先入先出”的性质,这 [class]{}-[func]{graph_dfs} ``` +=== "C" + + ```c title="graph_dfs.c" + [class]{}-[func]{dfs} + + [class]{}-[func]{graphDFS} + ``` + +=== "Zig" + + ```zig title="graph_dfs.zig" + [class]{}-[func]{dfs} + + [class]{}-[func]{graphDFS} + ``` + 深度优先遍历的算法流程如下图所示。 - **直虚线代表向下递推**,表示开启了一个新的递归方法来访问新顶点。 diff --git a/docs/chapter_greedy/fractional_knapsack_problem.md b/docs/chapter_greedy/fractional_knapsack_problem.md index 23675b03..0594f3da 100644 --- a/docs/chapter_greedy/fractional_knapsack_problem.md +++ b/docs/chapter_greedy/fractional_knapsack_problem.md @@ -29,12 +29,12 @@ 我们建立了一个物品类 `Item` ,以便将物品按照单位价值进行排序。循环进行贪心选择,当背包已满时跳出并返回解。 -=== "Java" +=== "Python" - ```java title="fractional_knapsack.java" + ```python title="fractional_knapsack.py" [class]{Item}-[func]{} - [class]{fractional_knapsack}-[func]{fractionalKnapsack} + [class]{}-[func]{fractional_knapsack} ``` === "C++" @@ -45,12 +45,20 @@ [class]{}-[func]{fractionalKnapsack} ``` -=== "Python" +=== "Java" - ```python title="fractional_knapsack.py" + ```java title="fractional_knapsack.java" [class]{Item}-[func]{} - [class]{}-[func]{fractional_knapsack} + [class]{fractional_knapsack}-[func]{fractionalKnapsack} + ``` + +=== "C#" + + ```csharp title="fractional_knapsack.cs" + [class]{Item}-[func]{} + + [class]{fractional_knapsack}-[func]{fractionalKnapsack} ``` === "Go" @@ -61,6 +69,14 @@ [class]{}-[func]{fractionalKnapsack} ``` +=== "Swift" + + ```swift title="fractional_knapsack.swift" + [class]{Item}-[func]{} + + [class]{}-[func]{fractionalKnapsack} + ``` + === "JS" ```javascript title="fractional_knapsack.js" @@ -77,38 +93,6 @@ [class]{}-[func]{fractionalKnapsack} ``` -=== "C" - - ```c title="fractional_knapsack.c" - [class]{Item}-[func]{} - - [class]{}-[func]{fractionalKnapsack} - ``` - -=== "C#" - - ```csharp title="fractional_knapsack.cs" - [class]{Item}-[func]{} - - [class]{fractional_knapsack}-[func]{fractionalKnapsack} - ``` - -=== "Swift" - - ```swift title="fractional_knapsack.swift" - [class]{Item}-[func]{} - - [class]{}-[func]{fractionalKnapsack} - ``` - -=== "Zig" - - ```zig title="fractional_knapsack.zig" - [class]{Item}-[func]{} - - [class]{}-[func]{fractionalKnapsack} - ``` - === "Dart" ```dart title="fractional_knapsack.dart" @@ -125,6 +109,22 @@ [class]{}-[func]{fractional_knapsack} ``` +=== "C" + + ```c title="fractional_knapsack.c" + [class]{Item}-[func]{} + + [class]{}-[func]{fractionalKnapsack} + ``` + +=== "Zig" + + ```zig title="fractional_knapsack.zig" + [class]{Item}-[func]{} + + [class]{}-[func]{fractionalKnapsack} + ``` + 最差情况下,需要遍历整个物品列表,**因此时间复杂度为 $O(n)$** ,其中 $n$ 为物品数量。 由于初始化了一个 `Item` 对象列表,**因此空间复杂度为 $O(n)$** 。 diff --git a/docs/chapter_greedy/greedy_algorithm.md b/docs/chapter_greedy/greedy_algorithm.md index de018e64..f2721273 100644 --- a/docs/chapter_greedy/greedy_algorithm.md +++ b/docs/chapter_greedy/greedy_algorithm.md @@ -19,10 +19,10 @@ 实现代码如下所示。你可能会不由地发出感叹:So Clean !贪心算法仅用十行代码就解决了零钱兑换问题。 -=== "Java" +=== "Python" - ```java title="coin_change_greedy.java" - [class]{coin_change_greedy}-[func]{coinChangeGreedy} + ```python title="coin_change_greedy.py" + [class]{}-[func]{coin_change_greedy} ``` === "C++" @@ -31,10 +31,16 @@ [class]{}-[func]{coinChangeGreedy} ``` -=== "Python" +=== "Java" - ```python title="coin_change_greedy.py" - [class]{}-[func]{coin_change_greedy} + ```java title="coin_change_greedy.java" + [class]{coin_change_greedy}-[func]{coinChangeGreedy} + ``` + +=== "C#" + + ```csharp title="coin_change_greedy.cs" + [class]{coin_change_greedy}-[func]{coinChangeGreedy} ``` === "Go" @@ -43,6 +49,12 @@ [class]{}-[func]{coinChangeGreedy} ``` +=== "Swift" + + ```swift title="coin_change_greedy.swift" + [class]{}-[func]{coinChangeGreedy} + ``` + === "JS" ```javascript title="coin_change_greedy.js" @@ -55,30 +67,6 @@ [class]{}-[func]{coinChangeGreedy} ``` -=== "C" - - ```c title="coin_change_greedy.c" - [class]{}-[func]{coinChangeGreedy} - ``` - -=== "C#" - - ```csharp title="coin_change_greedy.cs" - [class]{coin_change_greedy}-[func]{coinChangeGreedy} - ``` - -=== "Swift" - - ```swift title="coin_change_greedy.swift" - [class]{}-[func]{coinChangeGreedy} - ``` - -=== "Zig" - - ```zig title="coin_change_greedy.zig" - [class]{}-[func]{coinChangeGreedy} - ``` - === "Dart" ```dart title="coin_change_greedy.dart" @@ -91,6 +79,18 @@ [class]{}-[func]{coin_change_greedy} ``` +=== "C" + + ```c title="coin_change_greedy.c" + [class]{}-[func]{coinChangeGreedy} + ``` + +=== "Zig" + + ```zig title="coin_change_greedy.zig" + [class]{}-[func]{coinChangeGreedy} + ``` + ## 贪心优点与局限性 **贪心算法不仅操作直接、实现简单,而且通常效率也很高**。在以上代码中,记硬币最小面值为 $\min(coins)$ ,则贪心选择最多循环 $amt / \min(coins)$ 次,时间复杂度为 $O(amt / \min(coins))$ 。这比动态规划解法的时间复杂度 $O(n \times amt)$ 提升了一个数量级。 diff --git a/docs/chapter_greedy/max_capacity_problem.md b/docs/chapter_greedy/max_capacity_problem.md index c07282b1..6ace805e 100644 --- a/docs/chapter_greedy/max_capacity_problem.md +++ b/docs/chapter_greedy/max_capacity_problem.md @@ -78,10 +78,10 @@ $$ 变量 $i$、$j$、$res$ 使用常数大小额外空间,**因此空间复杂度为 $O(1)$** 。 -=== "Java" +=== "Python" - ```java title="max_capacity.java" - [class]{max_capacity}-[func]{maxCapacity} + ```python title="max_capacity.py" + [class]{}-[func]{max_capacity} ``` === "C++" @@ -90,10 +90,16 @@ $$ [class]{}-[func]{maxCapacity} ``` -=== "Python" +=== "Java" - ```python title="max_capacity.py" - [class]{}-[func]{max_capacity} + ```java title="max_capacity.java" + [class]{max_capacity}-[func]{maxCapacity} + ``` + +=== "C#" + + ```csharp title="max_capacity.cs" + [class]{max_capacity}-[func]{maxCapacity} ``` === "Go" @@ -102,6 +108,12 @@ $$ [class]{}-[func]{maxCapacity} ``` +=== "Swift" + + ```swift title="max_capacity.swift" + [class]{}-[func]{maxCapacity} + ``` + === "JS" ```javascript title="max_capacity.js" @@ -114,30 +126,6 @@ $$ [class]{}-[func]{maxCapacity} ``` -=== "C" - - ```c title="max_capacity.c" - [class]{}-[func]{maxCapacity} - ``` - -=== "C#" - - ```csharp title="max_capacity.cs" - [class]{max_capacity}-[func]{maxCapacity} - ``` - -=== "Swift" - - ```swift title="max_capacity.swift" - [class]{}-[func]{maxCapacity} - ``` - -=== "Zig" - - ```zig title="max_capacity.zig" - [class]{}-[func]{maxCapacity} - ``` - === "Dart" ```dart title="max_capacity.dart" @@ -150,6 +138,18 @@ $$ [class]{}-[func]{max_capacity} ``` +=== "C" + + ```c title="max_capacity.c" + [class]{}-[func]{maxCapacity} + ``` + +=== "Zig" + + ```zig title="max_capacity.zig" + [class]{}-[func]{maxCapacity} + ``` + ### 正确性证明 之所以贪心比穷举更快,是因为每轮的贪心选择都会“跳过”一些状态。 diff --git a/docs/chapter_greedy/max_product_cutting_problem.md b/docs/chapter_greedy/max_product_cutting_problem.md index 9d44a254..2ea773a4 100644 --- a/docs/chapter_greedy/max_product_cutting_problem.md +++ b/docs/chapter_greedy/max_product_cutting_problem.md @@ -63,10 +63,10 @@ $$ 请注意,对于 $n \leq 3$ 的边界情况,必须拆分出一个 $1$ ,乘积为 $1 \times (n - 1)$ 。 -=== "Java" +=== "Python" - ```java title="max_product_cutting.java" - [class]{max_product_cutting}-[func]{maxProductCutting} + ```python title="max_product_cutting.py" + [class]{}-[func]{max_product_cutting} ``` === "C++" @@ -75,10 +75,16 @@ $$ [class]{}-[func]{maxProductCutting} ``` -=== "Python" +=== "Java" - ```python title="max_product_cutting.py" - [class]{}-[func]{max_product_cutting} + ```java title="max_product_cutting.java" + [class]{max_product_cutting}-[func]{maxProductCutting} + ``` + +=== "C#" + + ```csharp title="max_product_cutting.cs" + [class]{max_product_cutting}-[func]{maxProductCutting} ``` === "Go" @@ -87,6 +93,12 @@ $$ [class]{}-[func]{maxProductCutting} ``` +=== "Swift" + + ```swift title="max_product_cutting.swift" + [class]{}-[func]{maxProductCutting} + ``` + === "JS" ```javascript title="max_product_cutting.js" @@ -99,30 +111,6 @@ $$ [class]{}-[func]{maxProductCutting} ``` -=== "C" - - ```c title="max_product_cutting.c" - [class]{}-[func]{maxProductCutting} - ``` - -=== "C#" - - ```csharp title="max_product_cutting.cs" - [class]{max_product_cutting}-[func]{maxProductCutting} - ``` - -=== "Swift" - - ```swift title="max_product_cutting.swift" - [class]{}-[func]{maxProductCutting} - ``` - -=== "Zig" - - ```zig title="max_product_cutting.zig" - [class]{}-[func]{maxProductCutting} - ``` - === "Dart" ```dart title="max_product_cutting.dart" @@ -135,6 +123,18 @@ $$ [class]{}-[func]{max_product_cutting} ``` +=== "C" + + ```c title="max_product_cutting.c" + [class]{}-[func]{maxProductCutting} + ``` + +=== "Zig" + + ```zig title="max_product_cutting.zig" + [class]{}-[func]{maxProductCutting} + ``` + ![最大切分乘积的计算方法](max_product_cutting_problem.assets/max_product_cutting_greedy_calculation.png) **时间复杂度取决于编程语言的幂运算的实现方法**。以 Python 为例,常用的幂计算函数有三种。 diff --git a/docs/chapter_hashing/hash_algorithm.md b/docs/chapter_hashing/hash_algorithm.md index 16ec1790..9f1fb152 100644 --- a/docs/chapter_hashing/hash_algorithm.md +++ b/docs/chapter_hashing/hash_algorithm.md @@ -45,16 +45,16 @@ index = hash(key) % capacity - **异或哈希**:将输入数据的每个元素通过异或操作累积到一个哈希值中。 - **旋转哈希**:将每个字符的 ASCII 码累积到一个哈希值中,每次累积之前都会对哈希值进行旋转操作。 -=== "Java" +=== "Python" - ```java title="simple_hash.java" - [class]{simple_hash}-[func]{addHash} + ```python title="simple_hash.py" + [class]{}-[func]{add_hash} - [class]{simple_hash}-[func]{mulHash} + [class]{}-[func]{mul_hash} - [class]{simple_hash}-[func]{xorHash} + [class]{}-[func]{xor_hash} - [class]{simple_hash}-[func]{rotHash} + [class]{}-[func]{rot_hash} ``` === "C++" @@ -69,16 +69,28 @@ index = hash(key) % capacity [class]{}-[func]{rotHash} ``` -=== "Python" +=== "Java" - ```python title="simple_hash.py" - [class]{}-[func]{add_hash} + ```java title="simple_hash.java" + [class]{simple_hash}-[func]{addHash} - [class]{}-[func]{mul_hash} + [class]{simple_hash}-[func]{mulHash} - [class]{}-[func]{xor_hash} + [class]{simple_hash}-[func]{xorHash} - [class]{}-[func]{rot_hash} + [class]{simple_hash}-[func]{rotHash} + ``` + +=== "C#" + + ```csharp title="simple_hash.cs" + [class]{simple_hash}-[func]{addHash} + + [class]{simple_hash}-[func]{mulHash} + + [class]{simple_hash}-[func]{xorHash} + + [class]{simple_hash}-[func]{rotHash} ``` === "Go" @@ -93,6 +105,18 @@ index = hash(key) % capacity [class]{}-[func]{rotHash} ``` +=== "Swift" + + ```swift title="simple_hash.swift" + [class]{}-[func]{addHash} + + [class]{}-[func]{mulHash} + + [class]{}-[func]{xorHash} + + [class]{}-[func]{rotHash} + ``` + === "JS" ```javascript title="simple_hash.js" @@ -117,54 +141,6 @@ index = hash(key) % capacity [class]{}-[func]{rotHash} ``` -=== "C" - - ```c title="simple_hash.c" - [class]{}-[func]{addHash} - - [class]{}-[func]{mulHash} - - [class]{}-[func]{xorHash} - - [class]{}-[func]{rotHash} - ``` - -=== "C#" - - ```csharp title="simple_hash.cs" - [class]{simple_hash}-[func]{addHash} - - [class]{simple_hash}-[func]{mulHash} - - [class]{simple_hash}-[func]{xorHash} - - [class]{simple_hash}-[func]{rotHash} - ``` - -=== "Swift" - - ```swift title="simple_hash.swift" - [class]{}-[func]{addHash} - - [class]{}-[func]{mulHash} - - [class]{}-[func]{xorHash} - - [class]{}-[func]{rotHash} - ``` - -=== "Zig" - - ```zig title="simple_hash.zig" - [class]{}-[func]{addHash} - - [class]{}-[func]{mulHash} - - [class]{}-[func]{xorHash} - - [class]{}-[func]{rotHash} - ``` - === "Dart" ```dart title="simple_hash.dart" @@ -189,6 +165,30 @@ index = hash(key) % capacity [class]{}-[func]{rot_hash} ``` +=== "C" + + ```c title="simple_hash.c" + [class]{}-[func]{addHash} + + [class]{}-[func]{mulHash} + + [class]{}-[func]{xorHash} + + [class]{}-[func]{rotHash} + ``` + +=== "Zig" + + ```zig title="simple_hash.zig" + [class]{}-[func]{addHash} + + [class]{}-[func]{mulHash} + + [class]{}-[func]{xorHash} + + [class]{}-[func]{rotHash} + ``` + 观察发现,每种哈希算法的最后一步都是对大质数 $1000000007$ 取模,以确保哈希值在合适的范围内。值得思考的是,为什么要强调对质数取模,或者说对合数取模的弊端是什么?这是一个有趣的问题。 先抛出结论:**当我们使用大质数作为模数时,可以最大化地保证哈希值的均匀分布**。因为质数不会与其他数字存在公约数,可以减少因取模操作而产生的周期性模式,从而避免哈希冲突。 @@ -252,57 +252,6 @@ $$ 请注意,不同编程语言的内置哈希值计算函数的定义和方法不同。 -=== "Java" - - ```java title="built_in_hash.java" - int num = 3; - int hashNum = Integer.hashCode(num); - // 整数 3 的哈希值为 3 - - boolean bol = true; - int hashBol = Boolean.hashCode(bol); - // 布尔量 true 的哈希值为 1231 - - double dec = 3.14159; - int hashDec = Double.hashCode(dec); - // 小数 3.14159 的哈希值为 -1340954729 - - String str = "Hello 算法"; - int hashStr = str.hashCode(); - // 字符串 Hello 算法 的哈希值为 -727081396 - - Object[] arr = { 12836, "小哈" }; - int hashTup = Arrays.hashCode(arr); - // 数组 [12836, 小哈] 的哈希值为 1151158 - - ListNode obj = new ListNode(0); - int hashObj = obj.hashCode(); - // 节点对象 utils.ListNode@7dc5e7b4 的哈希值为 2110121908 - ``` - -=== "C++" - - ```cpp title="built_in_hash.cpp" - int num = 3; - size_t hashNum = hash()(num); - // 整数 3 的哈希值为 3 - - bool bol = true; - size_t hashBol = hash()(bol); - // 布尔量 1 的哈希值为 1 - - double dec = 3.14159; - size_t hashDec = hash()(dec); - // 小数 3.14159 的哈希值为 4614256650576692846 - - string str = "Hello 算法"; - size_t hashStr = hash()(str); - // 字符串 Hello 算法 的哈希值为 15466937326284535026 - - // 在 C++ 中,内置 std:hash() 仅提供基本数据类型的哈希值计算 - // 数组、对象的哈希值计算需要自行实现 - ``` - === "Python" ```python title="built_in_hash.py" @@ -331,28 +280,55 @@ $$ # 节点对象 的哈希值为 274267521 ``` -=== "Go" +=== "C++" - ```go title="built_in_hash.go" + ```cpp title="built_in_hash.cpp" + int num = 3; + size_t hashNum = hash()(num); + // 整数 3 的哈希值为 3 + bool bol = true; + size_t hashBol = hash()(bol); + // 布尔量 1 的哈希值为 1 + + double dec = 3.14159; + size_t hashDec = hash()(dec); + // 小数 3.14159 的哈希值为 4614256650576692846 + + string str = "Hello 算法"; + size_t hashStr = hash()(str); + // 字符串 Hello 算法 的哈希值为 15466937326284535026 + + // 在 C++ 中,内置 std:hash() 仅提供基本数据类型的哈希值计算 + // 数组、对象的哈希值计算需要自行实现 ``` -=== "JS" +=== "Java" - ```javascript title="built_in_hash.js" - // JavaScript 未提供内置 hash code 函数 - ``` + ```java title="built_in_hash.java" + int num = 3; + int hashNum = Integer.hashCode(num); + // 整数 3 的哈希值为 3 -=== "TS" + boolean bol = true; + int hashBol = Boolean.hashCode(bol); + // 布尔量 true 的哈希值为 1231 - ```typescript title="built_in_hash.ts" - // TypeScript 未提供内置 hash code 函数 - ``` + double dec = 3.14159; + int hashDec = Double.hashCode(dec); + // 小数 3.14159 的哈希值为 -1340954729 -=== "C" + String str = "Hello 算法"; + int hashStr = str.hashCode(); + // 字符串 Hello 算法 的哈希值为 -727081396 - ```c title="built_in_hash.c" + Object[] arr = { 12836, "小哈" }; + int hashTup = Arrays.hashCode(arr); + // 数组 [12836, 小哈] 的哈希值为 1151158 + ListNode obj = new ListNode(0); + int hashObj = obj.hashCode(); + // 节点对象 utils.ListNode@7dc5e7b4 的哈希值为 2110121908 ``` === "C#" @@ -383,6 +359,12 @@ $$ // 节点对象 0 的哈希值为 39053774; ``` +=== "Go" + + ```go title="built_in_hash.go" + + ``` + === "Swift" ```swift title="built_in_hash.swift" @@ -411,10 +393,16 @@ $$ // 节点对象 utils.ListNode 的哈希值为 -2434780518035996159 ``` -=== "Zig" +=== "JS" - ```zig title="built_in_hash.zig" + ```javascript title="built_in_hash.js" + // JavaScript 未提供内置 hash code 函数 + ``` +=== "TS" + + ```typescript title="built_in_hash.ts" + // TypeScript 未提供内置 hash code 函数 ``` === "Dart" @@ -451,6 +439,18 @@ $$ ``` +=== "C" + + ```c title="built_in_hash.c" + + ``` + +=== "Zig" + + ```zig title="built_in_hash.zig" + + ``` + 在许多编程语言中,**只有不可变对象才可作为哈希表的 `key`** 。假如我们将列表(动态数组)作为 `key` ,当列表的内容发生变化时,它的哈希值也随之改变,我们就无法在哈希表中查询到原先的 `value` 了。 虽然自定义对象(比如链表节点)的成员变量是可变的,但它是可哈希的。**这是因为对象的哈希值通常是基于内存地址生成的**,即使对象的内容发生了变化,但它的内存地址不变,哈希值仍然是不变的。 diff --git a/docs/chapter_hashing/hash_collision.md b/docs/chapter_hashing/hash_collision.md index af01248b..dd352262 100644 --- a/docs/chapter_hashing/hash_collision.md +++ b/docs/chapter_hashing/hash_collision.md @@ -31,9 +31,9 @@ - 使用列表(动态数组)代替链表,从而简化代码。在这种设定下,哈希表(数组)包含多个桶,每个桶都是一个列表。 - 以下实现包含哈希表扩容方法。当负载因子超过 $0.75$ 时,我们将哈希表扩容至 $2$ 倍。 -=== "Java" +=== "Python" - ```java title="hash_map_chaining.java" + ```python title="hash_map_chaining.py" [class]{HashMapChaining}-[func]{} ``` @@ -43,9 +43,15 @@ [class]{HashMapChaining}-[func]{} ``` -=== "Python" +=== "Java" - ```python title="hash_map_chaining.py" + ```java title="hash_map_chaining.java" + [class]{HashMapChaining}-[func]{} + ``` + +=== "C#" + + ```csharp title="hash_map_chaining.cs" [class]{HashMapChaining}-[func]{} ``` @@ -55,6 +61,12 @@ [class]{hashMapChaining}-[func]{} ``` +=== "Swift" + + ```swift title="hash_map_chaining.swift" + [class]{HashMapChaining}-[func]{} + ``` + === "JS" ```javascript title="hash_map_chaining.js" @@ -67,30 +79,6 @@ [class]{HashMapChaining}-[func]{} ``` -=== "C" - - ```c title="hash_map_chaining.c" - [class]{hashMapChaining}-[func]{} - ``` - -=== "C#" - - ```csharp title="hash_map_chaining.cs" - [class]{HashMapChaining}-[func]{} - ``` - -=== "Swift" - - ```swift title="hash_map_chaining.swift" - [class]{HashMapChaining}-[func]{} - ``` - -=== "Zig" - - ```zig title="hash_map_chaining.zig" - [class]{HashMapChaining}-[func]{} - ``` - === "Dart" ```dart title="hash_map_chaining.dart" @@ -103,6 +91,18 @@ [class]{HashMapChaining}-[func]{} ``` +=== "C" + + ```c title="hash_map_chaining.c" + [class]{hashMapChaining}-[func]{} + ``` + +=== "Zig" + + ```zig title="hash_map_chaining.zig" + [class]{HashMapChaining}-[func]{} + ``` + 值得注意的是,当链表很长时,查询效率 $O(n)$ 很差。**此时可以将链表转换为“AVL 树”或“红黑树”**,从而将查询操作的时间复杂度优化至 $O(\log n)$ 。 ## 开放寻址 @@ -130,9 +130,9 @@ - 我们使用一个固定的键值对实例 `removed` 来标记已删除元素。也就是说,当一个桶内的元素为 $\text{None}$ 或 `removed` 时,说明这个桶是空的,可用于放置键值对。 - 在线性探测时,我们从当前索引 `index` 向后遍历;而当越过数组尾部时,需要回到头部继续遍历。 -=== "Java" +=== "Python" - ```java title="hash_map_open_addressing.java" + ```python title="hash_map_open_addressing.py" [class]{HashMapOpenAddressing}-[func]{} ``` @@ -142,9 +142,15 @@ [class]{HashMapOpenAddressing}-[func]{} ``` -=== "Python" +=== "Java" - ```python title="hash_map_open_addressing.py" + ```java title="hash_map_open_addressing.java" + [class]{HashMapOpenAddressing}-[func]{} + ``` + +=== "C#" + + ```csharp title="hash_map_open_addressing.cs" [class]{HashMapOpenAddressing}-[func]{} ``` @@ -154,6 +160,12 @@ [class]{hashMapOpenAddressing}-[func]{} ``` +=== "Swift" + + ```swift title="hash_map_open_addressing.swift" + [class]{HashMapOpenAddressing}-[func]{} + ``` + === "JS" ```javascript title="hash_map_open_addressing.js" @@ -166,30 +178,6 @@ [class]{HashMapOpenAddressing}-[func]{} ``` -=== "C" - - ```c title="hash_map_open_addressing.c" - [class]{hashMapOpenAddressing}-[func]{} - ``` - -=== "C#" - - ```csharp title="hash_map_open_addressing.cs" - [class]{HashMapOpenAddressing}-[func]{} - ``` - -=== "Swift" - - ```swift title="hash_map_open_addressing.swift" - [class]{HashMapOpenAddressing}-[func]{} - ``` - -=== "Zig" - - ```zig title="hash_map_open_addressing.zig" - [class]{HashMapOpenAddressing}-[func]{} - ``` - === "Dart" ```dart title="hash_map_open_addressing.dart" @@ -202,6 +190,18 @@ [class]{HashMapOpenAddressing}-[func]{} ``` +=== "C" + + ```c title="hash_map_open_addressing.c" + [class]{hashMapOpenAddressing}-[func]{} + ``` + +=== "Zig" + + ```zig title="hash_map_open_addressing.zig" + [class]{HashMapOpenAddressing}-[func]{} + ``` + ### 多次哈希 顾名思义,多次哈希方法是使用多个哈希函数 $f_1(x)$、$f_2(x)$、$f_3(x)$、$\dots$ 进行探测。 diff --git a/docs/chapter_hashing/hash_map.md b/docs/chapter_hashing/hash_map.md index d5224d7a..9f1b9b52 100755 --- a/docs/chapter_hashing/hash_map.md +++ b/docs/chapter_hashing/hash_map.md @@ -26,27 +26,27 @@ 哈希表的常见操作包括:初始化、查询操作、添加键值对和删除键值对等。 -=== "Java" +=== "Python" - ```java title="hash_map.java" - /* 初始化哈希表 */ - Map map = new HashMap<>(); + ```python title="hash_map.py" + # 初始化哈希表 + hmap: dict = {} - /* 添加操作 */ - // 在哈希表中添加键值对 (key, value) - map.put(12836, "小哈"); - map.put(15937, "小啰"); - map.put(16750, "小算"); - map.put(13276, "小法"); - map.put(10583, "小鸭"); + # 添加操作 + # 在哈希表中添加键值对 (key, value) + hmap[12836] = "小哈" + hmap[15937] = "小啰" + hmap[16750] = "小算" + hmap[13276] = "小法" + hmap[10583] = "小鸭" - /* 查询操作 */ - // 向哈希表输入键 key ,得到值 value - String name = map.get(15937); + # 查询操作 + # 向哈希表输入键 key ,得到值 value + name: str = hmap[15937] - /* 删除操作 */ - // 在哈希表中删除键值对 (key, value) - map.remove(10583); + # 删除操作 + # 在哈希表中删除键值对 (key, value) + hmap.pop(10583) ``` === "C++" @@ -72,27 +72,50 @@ map.erase(10583); ``` -=== "Python" +=== "Java" - ```python title="hash_map.py" - # 初始化哈希表 - hmap: Dict = {} + ```java title="hash_map.java" + /* 初始化哈希表 */ + Map map = new HashMap<>(); - # 添加操作 - # 在哈希表中添加键值对 (key, value) - hmap[12836] = "小哈" - hmap[15937] = "小啰" - hmap[16750] = "小算" - hmap[13276] = "小法" - hmap[10583] = "小鸭" + /* 添加操作 */ + // 在哈希表中添加键值对 (key, value) + map.put(12836, "小哈"); + map.put(15937, "小啰"); + map.put(16750, "小算"); + map.put(13276, "小法"); + map.put(10583, "小鸭"); - # 查询操作 - # 向哈希表输入键 key ,得到值 value - name: str = hmap[15937] + /* 查询操作 */ + // 向哈希表输入键 key ,得到值 value + String name = map.get(15937); - # 删除操作 - # 在哈希表中删除键值对 (key, value) - hmap.pop(10583) + /* 删除操作 */ + // 在哈希表中删除键值对 (key, value) + map.remove(10583); + ``` + +=== "C#" + + ```csharp title="hash_map.cs" + /* 初始化哈希表 */ + Dictionary map = new (); + + /* 添加操作 */ + // 在哈希表中添加键值对 (key, value) + map.Add(12836, "小哈"); + map.Add(15937, "小啰"); + map.Add(16750, "小算"); + map.Add(13276, "小法"); + map.Add(10583, "小鸭"); + + /* 查询操作 */ + // 向哈希表输入键 key ,得到值 value + String name = map[15937]; + + /* 删除操作 */ + // 在哈希表中删除键值对 (key, value) + map.Remove(10583); ``` === "Go" @@ -118,6 +141,29 @@ delete(hmap, 10583) ``` +=== "Swift" + + ```swift title="hash_map.swift" + /* 初始化哈希表 */ + var map: [Int: String] = [:] + + /* 添加操作 */ + // 在哈希表中添加键值对 (key, value) + map[12836] = "小哈" + map[15937] = "小啰" + map[16750] = "小算" + map[13276] = "小法" + map[10583] = "小鸭" + + /* 查询操作 */ + // 向哈希表输入键 key ,得到值 value + let name = map[15937]! + + /* 删除操作 */ + // 在哈希表中删除键值对 (key, value) + map.removeValue(forKey: 10583) + ``` + === "JS" ```javascript title="hash_map.js" @@ -167,64 +213,6 @@ console.info(map); ``` -=== "C" - - ```c title="hash_map.c" - // C 未提供内置哈希表 - ``` - -=== "C#" - - ```csharp title="hash_map.cs" - /* 初始化哈希表 */ - Dictionary map = new (); - - /* 添加操作 */ - // 在哈希表中添加键值对 (key, value) - map.Add(12836, "小哈"); - map.Add(15937, "小啰"); - map.Add(16750, "小算"); - map.Add(13276, "小法"); - map.Add(10583, "小鸭"); - - /* 查询操作 */ - // 向哈希表输入键 key ,得到值 value - String name = map[15937]; - - /* 删除操作 */ - // 在哈希表中删除键值对 (key, value) - map.Remove(10583); - ``` - -=== "Swift" - - ```swift title="hash_map.swift" - /* 初始化哈希表 */ - var map: [Int: String] = [:] - - /* 添加操作 */ - // 在哈希表中添加键值对 (key, value) - map[12836] = "小哈" - map[15937] = "小啰" - map[16750] = "小算" - map[13276] = "小法" - map[10583] = "小鸭" - - /* 查询操作 */ - // 向哈希表输入键 key ,得到值 value - let name = map[15937]! - - /* 删除操作 */ - // 在哈希表中删除键值对 (key, value) - map.removeValue(forKey: 10583) - ``` - -=== "Zig" - - ```zig title="hash_map.zig" - - ``` - === "Dart" ```dart title="hash_map.dart" @@ -254,24 +242,33 @@ ``` +=== "C" + + ```c title="hash_map.c" + // C 未提供内置哈希表 + ``` + +=== "Zig" + + ```zig title="hash_map.zig" + + ``` + 哈希表有三种常用遍历方式:遍历键值对、遍历键和遍历值。 -=== "Java" +=== "Python" - ```java title="hash_map.java" - /* 遍历哈希表 */ - // 遍历键值对 key->value - for (Map.Entry kv: map.entrySet()) { - System.out.println(kv.getKey() + " -> " + kv.getValue()); - } - // 单独遍历键 key - for (int key: map.keySet()) { - System.out.println(key); - } - // 单独遍历值 value - for (String val: map.values()) { - System.out.println(val); - } + ```python title="hash_map.py" + # 遍历哈希表 + # 遍历键值对 key->value + for key, value in hmap.items(): + print(key, "->", value) + # 单独遍历键 key + for key in hmap.keys(): + print(key) + # 单独遍历值 value + for value in hmap.values(): + print(value) ``` === "C++" @@ -292,19 +289,40 @@ } ``` -=== "Python" +=== "Java" - ```python title="hash_map.py" - # 遍历哈希表 - # 遍历键值对 key->value - for key, value in hmap.items(): - print(key, "->", value) - # 单独遍历键 key - for key in hmap.keys(): - print(key) - # 单独遍历值 value - for value in hmap.values(): - print(value) + ```java title="hash_map.java" + /* 遍历哈希表 */ + // 遍历键值对 key->value + for (Map.Entry kv: map.entrySet()) { + System.out.println(kv.getKey() + " -> " + kv.getValue()); + } + // 单独遍历键 key + for (int key: map.keySet()) { + System.out.println(key); + } + // 单独遍历值 value + for (String val: map.values()) { + System.out.println(val); + } + ``` + +=== "C#" + + ```csharp title="hash_map.cs" + /* 遍历哈希表 */ + // 遍历键值对 Key->Value + foreach (var kv in map) { + Console.WriteLine(kv.Key + " -> " + kv.Value); + } + // 单独遍历键 key + foreach (int key in map.Keys) { + Console.WriteLine(key); + } + // 单独遍历值 value + foreach (String val in map.Values) { + Console.WriteLine(val); + } ``` === "Go" @@ -325,6 +343,24 @@ } ``` +=== "Swift" + + ```swift title="hash_map.swift" + /* 遍历哈希表 */ + // 遍历键值对 Key->Value + for (key, value) in map { + print("\(key) -> \(value)") + } + // 单独遍历键 Key + for key in map.keys { + print(key) + } + // 单独遍历值 Value + for value in map.values { + print(value) + } + ``` + === "JS" ```javascript title="hash_map.js" @@ -361,54 +397,6 @@ } ``` -=== "C" - - ```c title="hash_map.c" - // C 未提供内置哈希表 - ``` - -=== "C#" - - ```csharp title="hash_map.cs" - /* 遍历哈希表 */ - // 遍历键值对 Key->Value - foreach (var kv in map) { - Console.WriteLine(kv.Key + " -> " + kv.Value); - } - // 单独遍历键 key - foreach (int key in map.Keys) { - Console.WriteLine(key); - } - // 单独遍历值 value - foreach (String val in map.Values) { - Console.WriteLine(val); - } - ``` - -=== "Swift" - - ```swift title="hash_map.swift" - /* 遍历哈希表 */ - // 遍历键值对 Key->Value - for (key, value) in map { - print("\(key) -> \(value)") - } - // 单独遍历键 Key - for key in map.keys { - print(key) - } - // 单独遍历值 Value - for value in map.values { - print(value) - } - ``` - -=== "Zig" - - ```zig title="hash_map.zig" - - ``` - === "Dart" ```dart title="hash_map.dart" @@ -435,6 +423,18 @@ ``` +=== "C" + + ```c title="hash_map.c" + // C 未提供内置哈希表 + ``` + +=== "Zig" + + ```zig title="hash_map.zig" + + ``` + ## 哈希表简单实现 我们先考虑最简单的情况,**仅用一个数组来实现哈希表**。在哈希表中,我们将数组中的每个空位称为「桶 bucket」,每个桶可存储一个键值对。因此,查询操作就是找到 `key` 对应的桶,并在桶中获取 `value` 。 @@ -458,9 +458,9 @@ index = hash(key) % capacity 以下代码实现了一个简单哈希表。其中,我们将 `key` 和 `value` 封装成一个类 `Pair` ,以表示键值对。 -=== "Java" +=== "Python" - ```java title="array_hash_map.java" + ```python title="array_hash_map.py" [class]{Pair}-[func]{} [class]{ArrayHashMap}-[func]{} @@ -474,9 +474,17 @@ index = hash(key) % capacity [class]{ArrayHashMap}-[func]{} ``` -=== "Python" +=== "Java" - ```python title="array_hash_map.py" + ```java title="array_hash_map.java" + [class]{Pair}-[func]{} + + [class]{ArrayHashMap}-[func]{} + ``` + +=== "C#" + + ```csharp title="array_hash_map.cs" [class]{Pair}-[func]{} [class]{ArrayHashMap}-[func]{} @@ -490,38 +498,6 @@ index = hash(key) % capacity [class]{arrayHashMap}-[func]{} ``` -=== "JS" - - ```javascript title="array_hash_map.js" - [class]{Pair}-[func]{} - - [class]{ArrayHashMap}-[func]{} - ``` - -=== "TS" - - ```typescript title="array_hash_map.ts" - [class]{Pair}-[func]{} - - [class]{ArrayHashMap}-[func]{} - ``` - -=== "C" - - ```c title="array_hash_map.c" - [class]{pair}-[func]{} - - [class]{arrayHashMap}-[func]{} - ``` - -=== "C#" - - ```csharp title="array_hash_map.cs" - [class]{Pair}-[func]{} - - [class]{ArrayHashMap}-[func]{} - ``` - === "Swift" ```swift title="array_hash_map.swift" @@ -539,9 +515,17 @@ index = hash(key) % capacity [class]{ArrayHashMap}-[func]{} ``` -=== "Zig" +=== "JS" - ```zig title="array_hash_map.zig" + ```javascript title="array_hash_map.js" + [class]{Pair}-[func]{} + + [class]{ArrayHashMap}-[func]{} + ``` + +=== "TS" + + ```typescript title="array_hash_map.ts" [class]{Pair}-[func]{} [class]{ArrayHashMap}-[func]{} @@ -563,6 +547,22 @@ index = hash(key) % capacity [class]{ArrayHashMap}-[func]{} ``` +=== "C" + + ```c title="array_hash_map.c" + [class]{pair}-[func]{} + + [class]{arrayHashMap}-[func]{} + ``` + +=== "Zig" + + ```zig title="array_hash_map.zig" + [class]{Pair}-[func]{} + + [class]{ArrayHashMap}-[func]{} + ``` + ## 哈希冲突与扩容 本质上看,哈希函数的作用是将所有 `key` 构成的输入空间映射到数组所有索引构成的输出空间,而输入空间往往远大于输出空间。因此,**理论上一定存在“多个输入对应相同输出”的情况**。 diff --git a/docs/chapter_heap/build_heap.md b/docs/chapter_heap/build_heap.md index cbe228b5..b044e7a7 100644 --- a/docs/chapter_heap/build_heap.md +++ b/docs/chapter_heap/build_heap.md @@ -22,10 +22,10 @@ - 由于叶节点没有子节点,因此无需对它们执行堆化。最后一个节点的父节点是最后一个非叶节点。 - 在倒序遍历中,我们能够保证当前节点之下的子树已经完成堆化(已经是合法的堆),而这是堆化当前节点的前置条件。 -=== "Java" +=== "Python" - ```java title="my_heap.java" - [class]{MaxHeap}-[func]{MaxHeap} + ```python title="my_heap.py" + [class]{MaxHeap}-[func]{__init__} ``` === "C++" @@ -34,10 +34,16 @@ [class]{MaxHeap}-[func]{MaxHeap} ``` -=== "Python" +=== "Java" - ```python title="my_heap.py" - [class]{MaxHeap}-[func]{__init__} + ```java title="my_heap.java" + [class]{MaxHeap}-[func]{MaxHeap} + ``` + +=== "C#" + + ```csharp title="my_heap.cs" + [class]{MaxHeap}-[func]{MaxHeap} ``` === "Go" @@ -46,6 +52,12 @@ [class]{maxHeap}-[func]{newMaxHeap} ``` +=== "Swift" + + ```swift title="my_heap.swift" + [class]{MaxHeap}-[func]{init} + ``` + === "JS" ```javascript title="my_heap.js" @@ -58,30 +70,6 @@ [class]{MaxHeap}-[func]{constructor} ``` -=== "C" - - ```c title="my_heap.c" - [class]{maxHeap}-[func]{newMaxHeap} - ``` - -=== "C#" - - ```csharp title="my_heap.cs" - [class]{MaxHeap}-[func]{MaxHeap} - ``` - -=== "Swift" - - ```swift title="my_heap.swift" - [class]{MaxHeap}-[func]{init} - ``` - -=== "Zig" - - ```zig title="my_heap.zig" - [class]{MaxHeap}-[func]{init} - ``` - === "Dart" ```dart title="my_heap.dart" @@ -94,6 +82,18 @@ [class]{MaxHeap}-[func]{new} ``` +=== "C" + + ```c title="my_heap.c" + [class]{maxHeap}-[func]{newMaxHeap} + ``` + +=== "Zig" + + ```zig title="my_heap.zig" + [class]{MaxHeap}-[func]{init} + ``` + ## 复杂度分析 下面,我们来尝试推算第二种建堆方法的时间复杂度。 diff --git a/docs/chapter_heap/heap.md b/docs/chapter_heap/heap.md index a732ae67..6c5c831e 100644 --- a/docs/chapter_heap/heap.md +++ b/docs/chapter_heap/heap.md @@ -37,81 +37,6 @@ 类似于排序算法中的“从小到大排列”和“从大到小排列”,我们可以通过修改 Comparator 来实现“小顶堆”与“大顶堆”之间的转换。 -=== "Java" - - ```java title="heap.java" - /* 初始化堆 */ - // 初始化小顶堆 - Queue minHeap = new PriorityQueue<>(); - // 初始化大顶堆(使用 lambda 表达式修改 Comparator 即可) - Queue maxHeap = new PriorityQueue<>((a, b) -> b - a); - - /* 元素入堆 */ - maxHeap.offer(1); - maxHeap.offer(3); - maxHeap.offer(2); - maxHeap.offer(5); - maxHeap.offer(4); - - /* 获取堆顶元素 */ - int peek = maxHeap.peek(); // 5 - - /* 堆顶元素出堆 */ - // 出堆元素会形成一个从大到小的序列 - peek = maxHeap.poll(); // 5 - peek = maxHeap.poll(); // 4 - peek = maxHeap.poll(); // 3 - peek = maxHeap.poll(); // 2 - peek = maxHeap.poll(); // 1 - - /* 获取堆大小 */ - int size = maxHeap.size(); - - /* 判断堆是否为空 */ - boolean isEmpty = maxHeap.isEmpty(); - - /* 输入列表并建堆 */ - minHeap = new PriorityQueue<>(Arrays.asList(1, 3, 2, 5, 4)); - ``` - -=== "C++" - - ```cpp title="heap.cpp" - /* 初始化堆 */ - // 初始化小顶堆 - priority_queue, greater> minHeap; - // 初始化大顶堆 - priority_queue, less> maxHeap; - - /* 元素入堆 */ - maxHeap.push(1); - maxHeap.push(3); - maxHeap.push(2); - maxHeap.push(5); - maxHeap.push(4); - - /* 获取堆顶元素 */ - int peek = maxHeap.top(); // 5 - - /* 堆顶元素出堆 */ - // 出堆元素会形成一个从大到小的序列 - maxHeap.pop(); // 5 - maxHeap.pop(); // 4 - maxHeap.pop(); // 3 - maxHeap.pop(); // 2 - maxHeap.pop(); // 1 - - /* 获取堆大小 */ - int size = maxHeap.size(); - - /* 判断堆是否为空 */ - bool isEmpty = maxHeap.empty(); - - /* 输入列表并建堆 */ - vector input{1, 3, 2, 5, 4}; - priority_queue, greater> minHeap(input.begin(), input.end()); - ``` - === "Python" ```python title="heap.py" @@ -153,6 +78,118 @@ heapq.heapify(min_heap) ``` +=== "C++" + + ```cpp title="heap.cpp" + /* 初始化堆 */ + // 初始化小顶堆 + priority_queue, greater> minHeap; + // 初始化大顶堆 + priority_queue, less> maxHeap; + + /* 元素入堆 */ + maxHeap.push(1); + maxHeap.push(3); + maxHeap.push(2); + maxHeap.push(5); + maxHeap.push(4); + + /* 获取堆顶元素 */ + int peek = maxHeap.top(); // 5 + + /* 堆顶元素出堆 */ + // 出堆元素会形成一个从大到小的序列 + maxHeap.pop(); // 5 + maxHeap.pop(); // 4 + maxHeap.pop(); // 3 + maxHeap.pop(); // 2 + maxHeap.pop(); // 1 + + /* 获取堆大小 */ + int size = maxHeap.size(); + + /* 判断堆是否为空 */ + bool isEmpty = maxHeap.empty(); + + /* 输入列表并建堆 */ + vector input{1, 3, 2, 5, 4}; + priority_queue, greater> minHeap(input.begin(), input.end()); + ``` + +=== "Java" + + ```java title="heap.java" + /* 初始化堆 */ + // 初始化小顶堆 + Queue minHeap = new PriorityQueue<>(); + // 初始化大顶堆(使用 lambda 表达式修改 Comparator 即可) + Queue maxHeap = new PriorityQueue<>((a, b) -> b - a); + + /* 元素入堆 */ + maxHeap.offer(1); + maxHeap.offer(3); + maxHeap.offer(2); + maxHeap.offer(5); + maxHeap.offer(4); + + /* 获取堆顶元素 */ + int peek = maxHeap.peek(); // 5 + + /* 堆顶元素出堆 */ + // 出堆元素会形成一个从大到小的序列 + peek = maxHeap.poll(); // 5 + peek = maxHeap.poll(); // 4 + peek = maxHeap.poll(); // 3 + peek = maxHeap.poll(); // 2 + peek = maxHeap.poll(); // 1 + + /* 获取堆大小 */ + int size = maxHeap.size(); + + /* 判断堆是否为空 */ + boolean isEmpty = maxHeap.isEmpty(); + + /* 输入列表并建堆 */ + minHeap = new PriorityQueue<>(Arrays.asList(1, 3, 2, 5, 4)); + ``` + +=== "C#" + + ```csharp title="heap.cs" + /* 初始化堆 */ + // 初始化小顶堆 + PriorityQueue minHeap = new PriorityQueue(); + // 初始化大顶堆(使用 lambda 表达式修改 Comparator 即可) + PriorityQueue maxHeap = new PriorityQueue(Comparer.Create((x, y) => y - x)); + + /* 元素入堆 */ + maxHeap.Enqueue(1, 1); + maxHeap.Enqueue(3, 3); + maxHeap.Enqueue(2, 2); + maxHeap.Enqueue(5, 5); + maxHeap.Enqueue(4, 4); + + /* 获取堆顶元素 */ + int peek = maxHeap.Peek();//5 + + /* 堆顶元素出堆 */ + // 出堆元素会形成一个从大到小的序列 + peek = maxHeap.Dequeue(); // 5 + peek = maxHeap.Dequeue(); // 4 + peek = maxHeap.Dequeue(); // 3 + peek = maxHeap.Dequeue(); // 2 + peek = maxHeap.Dequeue(); // 1 + + /* 获取堆大小 */ + int size = maxHeap.Count; + + /* 判断堆是否为空 */ + bool isEmpty = maxHeap.Count == 0; + + /* 输入列表并建堆 */ + minHeap = new PriorityQueue(new List<(int, int)> { (1, 1), (3, 3), (2, 2), (5, 5), (4, 4), }); + ``` + === "Go" ```go title="heap.go" @@ -232,6 +269,12 @@ } ``` +=== "Swift" + + ```swift title="heap.swift" + // Swift 未提供内置 Heap 类 + ``` + === "JS" ```javascript title="heap.js" @@ -244,61 +287,6 @@ // TypeScript 未提供内置 Heap 类 ``` -=== "C" - - ```c title="heap.c" - // C 未提供内置 Heap 类 - ``` - -=== "C#" - - ```csharp title="heap.cs" - /* 初始化堆 */ - // 初始化小顶堆 - PriorityQueue minHeap = new PriorityQueue(); - // 初始化大顶堆(使用 lambda 表达式修改 Comparator 即可) - PriorityQueue maxHeap = new PriorityQueue(Comparer.Create((x, y) => y - x)); - - /* 元素入堆 */ - maxHeap.Enqueue(1, 1); - maxHeap.Enqueue(3, 3); - maxHeap.Enqueue(2, 2); - maxHeap.Enqueue(5, 5); - maxHeap.Enqueue(4, 4); - - /* 获取堆顶元素 */ - int peek = maxHeap.Peek();//5 - - /* 堆顶元素出堆 */ - // 出堆元素会形成一个从大到小的序列 - peek = maxHeap.Dequeue(); // 5 - peek = maxHeap.Dequeue(); // 4 - peek = maxHeap.Dequeue(); // 3 - peek = maxHeap.Dequeue(); // 2 - peek = maxHeap.Dequeue(); // 1 - - /* 获取堆大小 */ - int size = maxHeap.Count; - - /* 判断堆是否为空 */ - bool isEmpty = maxHeap.Count == 0; - - /* 输入列表并建堆 */ - minHeap = new PriorityQueue(new List<(int, int)> { (1, 1), (3, 3), (2, 2), (5, 5), (4, 4), }); - ``` - -=== "Swift" - - ```swift title="heap.swift" - // Swift 未提供内置 Heap 类 - ``` - -=== "Zig" - - ```zig title="heap.zig" - - ``` - === "Dart" ```dart title="heap.dart" @@ -311,6 +299,18 @@ ``` +=== "C" + + ```c title="heap.c" + // C 未提供内置 Heap 类 + ``` + +=== "Zig" + + ```zig title="heap.zig" + + ``` + ## 堆的实现 下文实现的是大顶堆。若要将其转换为小顶堆,只需将所有大小逻辑判断取逆(例如,将 $\geq$ 替换为 $\leq$ )。感兴趣的读者可以自行实现。 @@ -327,9 +327,9 @@ 我们可以将索引映射公式封装成函数,方便后续使用。 -=== "Java" +=== "Python" - ```java title="my_heap.java" + ```python title="my_heap.py" [class]{MaxHeap}-[func]{left} [class]{MaxHeap}-[func]{right} @@ -347,9 +347,19 @@ [class]{MaxHeap}-[func]{parent} ``` -=== "Python" +=== "Java" - ```python title="my_heap.py" + ```java title="my_heap.java" + [class]{MaxHeap}-[func]{left} + + [class]{MaxHeap}-[func]{right} + + [class]{MaxHeap}-[func]{parent} + ``` + +=== "C#" + + ```csharp title="my_heap.cs" [class]{MaxHeap}-[func]{left} [class]{MaxHeap}-[func]{right} @@ -367,6 +377,16 @@ [class]{maxHeap}-[func]{parent} ``` +=== "Swift" + + ```swift title="my_heap.swift" + [class]{MaxHeap}-[func]{left} + + [class]{MaxHeap}-[func]{right} + + [class]{MaxHeap}-[func]{parent} + ``` + === "JS" ```javascript title="my_heap.js" @@ -387,46 +407,6 @@ [class]{MaxHeap}-[func]{parent} ``` -=== "C" - - ```c title="my_heap.c" - [class]{maxHeap}-[func]{left} - - [class]{maxHeap}-[func]{right} - - [class]{maxHeap}-[func]{parent} - ``` - -=== "C#" - - ```csharp title="my_heap.cs" - [class]{MaxHeap}-[func]{left} - - [class]{MaxHeap}-[func]{right} - - [class]{MaxHeap}-[func]{parent} - ``` - -=== "Swift" - - ```swift title="my_heap.swift" - [class]{MaxHeap}-[func]{left} - - [class]{MaxHeap}-[func]{right} - - [class]{MaxHeap}-[func]{parent} - ``` - -=== "Zig" - - ```zig title="my_heap.zig" - [class]{MaxHeap}-[func]{left} - - [class]{MaxHeap}-[func]{right} - - [class]{MaxHeap}-[func]{parent} - ``` - === "Dart" ```dart title="my_heap.dart" @@ -447,13 +427,33 @@ [class]{MaxHeap}-[func]{parent} ``` +=== "C" + + ```c title="my_heap.c" + [class]{maxHeap}-[func]{left} + + [class]{maxHeap}-[func]{right} + + [class]{maxHeap}-[func]{parent} + ``` + +=== "Zig" + + ```zig title="my_heap.zig" + [class]{MaxHeap}-[func]{left} + + [class]{MaxHeap}-[func]{right} + + [class]{MaxHeap}-[func]{parent} + ``` + ### 访问堆顶元素 堆顶元素即为二叉树的根节点,也就是列表的首个元素。 -=== "Java" +=== "Python" - ```java title="my_heap.java" + ```python title="my_heap.py" [class]{MaxHeap}-[func]{peek} ``` @@ -463,9 +463,15 @@ [class]{MaxHeap}-[func]{peek} ``` -=== "Python" +=== "Java" - ```python title="my_heap.py" + ```java title="my_heap.java" + [class]{MaxHeap}-[func]{peek} + ``` + +=== "C#" + + ```csharp title="my_heap.cs" [class]{MaxHeap}-[func]{peek} ``` @@ -475,6 +481,12 @@ [class]{maxHeap}-[func]{peek} ``` +=== "Swift" + + ```swift title="my_heap.swift" + [class]{MaxHeap}-[func]{peek} + ``` + === "JS" ```javascript title="my_heap.js" @@ -487,30 +499,6 @@ [class]{MaxHeap}-[func]{peek} ``` -=== "C" - - ```c title="my_heap.c" - [class]{maxHeap}-[func]{peek} - ``` - -=== "C#" - - ```csharp title="my_heap.cs" - [class]{MaxHeap}-[func]{peek} - ``` - -=== "Swift" - - ```swift title="my_heap.swift" - [class]{MaxHeap}-[func]{peek} - ``` - -=== "Zig" - - ```zig title="my_heap.zig" - [class]{MaxHeap}-[func]{peek} - ``` - === "Dart" ```dart title="my_heap.dart" @@ -523,6 +511,18 @@ [class]{MaxHeap}-[func]{peek} ``` +=== "C" + + ```c title="my_heap.c" + [class]{maxHeap}-[func]{peek} + ``` + +=== "Zig" + + ```zig title="my_heap.zig" + [class]{MaxHeap}-[func]{peek} + ``` + ### 元素入堆 给定元素 `val` ,我们首先将其添加到堆底。添加之后,由于 val 可能大于堆中其他元素,堆的成立条件可能已被破坏。因此,**需要修复从插入节点到根节点的路径上的各个节点**,这个操作被称为「堆化 heapify」。 @@ -558,12 +558,12 @@ 设节点总数为 $n$ ,则树的高度为 $O(\log n)$ 。由此可知,堆化操作的循环轮数最多为 $O(\log n)$ ,**元素入堆操作的时间复杂度为 $O(\log n)$** 。 -=== "Java" +=== "Python" - ```java title="my_heap.java" + ```python title="my_heap.py" [class]{MaxHeap}-[func]{push} - [class]{MaxHeap}-[func]{siftUp} + [class]{MaxHeap}-[func]{sift_up} ``` === "C++" @@ -574,12 +574,20 @@ [class]{MaxHeap}-[func]{siftUp} ``` -=== "Python" +=== "Java" - ```python title="my_heap.py" + ```java title="my_heap.java" [class]{MaxHeap}-[func]{push} - [class]{MaxHeap}-[func]{sift_up} + [class]{MaxHeap}-[func]{siftUp} + ``` + +=== "C#" + + ```csharp title="my_heap.cs" + [class]{MaxHeap}-[func]{push} + + [class]{MaxHeap}-[func]{siftUp} ``` === "Go" @@ -590,6 +598,14 @@ [class]{maxHeap}-[func]{siftUp} ``` +=== "Swift" + + ```swift title="my_heap.swift" + [class]{MaxHeap}-[func]{push} + + [class]{MaxHeap}-[func]{siftUp} + ``` + === "JS" ```javascript title="my_heap.js" @@ -606,38 +622,6 @@ [class]{MaxHeap}-[func]{siftUp} ``` -=== "C" - - ```c title="my_heap.c" - [class]{maxHeap}-[func]{push} - - [class]{maxHeap}-[func]{siftUp} - ``` - -=== "C#" - - ```csharp title="my_heap.cs" - [class]{MaxHeap}-[func]{push} - - [class]{MaxHeap}-[func]{siftUp} - ``` - -=== "Swift" - - ```swift title="my_heap.swift" - [class]{MaxHeap}-[func]{push} - - [class]{MaxHeap}-[func]{siftUp} - ``` - -=== "Zig" - - ```zig title="my_heap.zig" - [class]{MaxHeap}-[func]{push} - - [class]{MaxHeap}-[func]{siftUp} - ``` - === "Dart" ```dart title="my_heap.dart" @@ -654,6 +638,22 @@ [class]{MaxHeap}-[func]{sift_up} ``` +=== "C" + + ```c title="my_heap.c" + [class]{maxHeap}-[func]{push} + + [class]{maxHeap}-[func]{siftUp} + ``` + +=== "Zig" + + ```zig title="my_heap.zig" + [class]{MaxHeap}-[func]{push} + + [class]{MaxHeap}-[func]{siftUp} + ``` + ### 堆顶元素出堆 堆顶元素是二叉树的根节点,即列表首元素。如果我们直接从列表中删除首元素,那么二叉树中所有节点的索引都会发生变化,这将使得后续使用堆化修复变得困难。为了尽量减少元素索引的变动,我们采用以下操作步骤。 @@ -696,12 +696,12 @@ 与元素入堆操作相似,堆顶元素出堆操作的时间复杂度也为 $O(\log n)$ 。 -=== "Java" +=== "Python" - ```java title="my_heap.java" + ```python title="my_heap.py" [class]{MaxHeap}-[func]{pop} - [class]{MaxHeap}-[func]{siftDown} + [class]{MaxHeap}-[func]{sift_down} ``` === "C++" @@ -712,12 +712,20 @@ [class]{MaxHeap}-[func]{siftDown} ``` -=== "Python" +=== "Java" - ```python title="my_heap.py" + ```java title="my_heap.java" [class]{MaxHeap}-[func]{pop} - [class]{MaxHeap}-[func]{sift_down} + [class]{MaxHeap}-[func]{siftDown} + ``` + +=== "C#" + + ```csharp title="my_heap.cs" + [class]{MaxHeap}-[func]{pop} + + [class]{MaxHeap}-[func]{siftDown} ``` === "Go" @@ -728,6 +736,14 @@ [class]{maxHeap}-[func]{siftDown} ``` +=== "Swift" + + ```swift title="my_heap.swift" + [class]{MaxHeap}-[func]{pop} + + [class]{MaxHeap}-[func]{siftDown} + ``` + === "JS" ```javascript title="my_heap.js" @@ -744,38 +760,6 @@ [class]{MaxHeap}-[func]{siftDown} ``` -=== "C" - - ```c title="my_heap.c" - [class]{maxHeap}-[func]{pop} - - [class]{maxHeap}-[func]{siftDown} - ``` - -=== "C#" - - ```csharp title="my_heap.cs" - [class]{MaxHeap}-[func]{pop} - - [class]{MaxHeap}-[func]{siftDown} - ``` - -=== "Swift" - - ```swift title="my_heap.swift" - [class]{MaxHeap}-[func]{pop} - - [class]{MaxHeap}-[func]{siftDown} - ``` - -=== "Zig" - - ```zig title="my_heap.zig" - [class]{MaxHeap}-[func]{pop} - - [class]{MaxHeap}-[func]{siftDown} - ``` - === "Dart" ```dart title="my_heap.dart" @@ -792,6 +776,22 @@ [class]{MaxHeap}-[func]{sift_down} ``` +=== "C" + + ```c title="my_heap.c" + [class]{maxHeap}-[func]{pop} + + [class]{maxHeap}-[func]{siftDown} + ``` + +=== "Zig" + + ```zig title="my_heap.zig" + [class]{MaxHeap}-[func]{pop} + + [class]{MaxHeap}-[func]{siftDown} + ``` + ## 堆常见应用 - **优先队列**:堆通常作为实现优先队列的首选数据结构,其入队和出队操作的时间复杂度均为 $O(\log n)$ ,而建队操作为 $O(n)$ ,这些操作都非常高效。 diff --git a/docs/chapter_heap/top_k.md b/docs/chapter_heap/top_k.md index f1089cdb..1fa6a06c 100644 --- a/docs/chapter_heap/top_k.md +++ b/docs/chapter_heap/top_k.md @@ -66,10 +66,10 @@ 另外,该方法适用于动态数据流的使用场景。在不断加入数据时,我们可以持续维护堆内的元素,从而实现最大 $k$ 个元素的动态更新。 -=== "Java" +=== "Python" - ```java title="top_k.java" - [class]{top_k}-[func]{topKHeap} + ```python title="top_k.py" + [class]{}-[func]{top_k_heap} ``` === "C++" @@ -78,10 +78,16 @@ [class]{}-[func]{topKHeap} ``` -=== "Python" +=== "Java" - ```python title="top_k.py" - [class]{}-[func]{top_k_heap} + ```java title="top_k.java" + [class]{top_k}-[func]{topKHeap} + ``` + +=== "C#" + + ```csharp title="top_k.cs" + [class]{top_k}-[func]{topKHeap} ``` === "Go" @@ -90,6 +96,12 @@ [class]{}-[func]{topKHeap} ``` +=== "Swift" + + ```swift title="top_k.swift" + [class]{}-[func]{topKHeap} + ``` + === "JS" ```javascript title="top_k.js" @@ -102,30 +114,6 @@ [class]{}-[func]{topKHeap} ``` -=== "C" - - ```c title="top_k.c" - [class]{}-[func]{topKHeap} - ``` - -=== "C#" - - ```csharp title="top_k.cs" - [class]{top_k}-[func]{topKHeap} - ``` - -=== "Swift" - - ```swift title="top_k.swift" - [class]{}-[func]{topKHeap} - ``` - -=== "Zig" - - ```zig title="top_k.zig" - [class]{}-[func]{topKHeap} - ``` - === "Dart" ```dart title="top_k.dart" @@ -137,3 +125,15 @@ ```rust title="top_k.rs" [class]{}-[func]{top_k_heap} ``` + +=== "C" + + ```c title="top_k.c" + [class]{}-[func]{topKHeap} + ``` + +=== "Zig" + + ```zig title="top_k.zig" + [class]{}-[func]{topKHeap} + ``` diff --git a/docs/chapter_preface/suggestions.md b/docs/chapter_preface/suggestions.md index 47427a1c..1cae45ec 100644 --- a/docs/chapter_preface/suggestions.md +++ b/docs/chapter_preface/suggestions.md @@ -13,17 +13,17 @@ - 当涉及到编程语言之间不一致的名词时,本书均以 Python 为准,例如使用 $\text{None}$ 来表示“空”。 - 本书部分放弃了编程语言的注释规范,以换取更加紧凑的内容排版。注释主要分为三种类型:标题注释、内容注释、多行注释。 -=== "Java" +=== "Python" - ```java title="" - /* 标题注释,用于标注函数、类、测试样例等 */ + ```python title="" + """标题注释,用于标注函数、类、测试样例等""" - // 内容注释,用于详解代码 + # 内容注释,用于详解代码 - /** - * 多行 - * 注释 - */ + """ + 多行 + 注释 + """ ``` === "C++" @@ -39,17 +39,30 @@ */ ``` -=== "Python" +=== "Java" - ```python title="" - """标题注释,用于标注函数、类、测试样例等""" + ```java title="" + /* 标题注释,用于标注函数、类、测试样例等 */ - # 内容注释,用于详解代码 + // 内容注释,用于详解代码 - """ - 多行 - 注释 - """ + /** + * 多行 + * 注释 + */ + ``` + +=== "C#" + + ```csharp title="" + /* 标题注释,用于标注函数、类、测试样例等 */ + + // 内容注释,用于详解代码 + + /** + * 多行 + * 注释 + */ ``` === "Go" @@ -65,6 +78,19 @@ */ ``` +=== "Swift" + + ```swift title="" + /* 标题注释,用于标注函数、类、测试样例等 */ + + // 内容注释,用于详解代码 + + /** + * 多行 + * 注释 + */ + ``` + === "JS" ```javascript title="" @@ -91,6 +117,25 @@ */ ``` +=== "Dart" + + ```dart title="" + /* 标题注释,用于标注函数、类、测试样例等 */ + + // 内容注释,用于详解代码 + + /** + * 多行 + * 注释 + */ + ``` + +=== "Rust" + + ```rust title="" + + ``` + === "C" ```c title="" @@ -104,32 +149,6 @@ */ ``` -=== "C#" - - ```csharp title="" - /* 标题注释,用于标注函数、类、测试样例等 */ - - // 内容注释,用于详解代码 - - /** - * 多行 - * 注释 - */ - ``` - -=== "Swift" - - ```swift title="" - /* 标题注释,用于标注函数、类、测试样例等 */ - - // 内容注释,用于详解代码 - - /** - * 多行 - * 注释 - */ - ``` - === "Zig" ```zig title="" @@ -141,25 +160,6 @@ // 注释 ``` -=== "Dart" - - ```dart title="" - /* 标题注释,用于标注函数、类、测试样例等 */ - - // 内容注释,用于详解代码 - - /** - * 多行 - * 注释 - */ - ``` - -=== "Rust" - - ```rust title="" - - ``` - ## 在动画图解中高效学习 相较于文字,视频和图片具有更高的信息密度和结构化程度,更易于理解。在本书中,**重点和难点知识将主要通过动画和图解形式展示**,而文字则作为动画和图片的解释与补充。 diff --git a/docs/chapter_searching/binary_search.md b/docs/chapter_searching/binary_search.md index 3715fe03..47171148 100755 --- a/docs/chapter_searching/binary_search.md +++ b/docs/chapter_searching/binary_search.md @@ -43,10 +43,10 @@ 值得注意的是,由于 $i$ 和 $j$ 都是 `int` 类型,**因此 $i + j$ 可能会超出 `int` 类型的取值范围**。为了避免大数越界,我们通常采用公式 $m = \lfloor {i + (j - i) / 2} \rfloor$ 来计算中点。 -=== "Java" +=== "Python" - ```java title="binary_search.java" - [class]{binary_search}-[func]{binarySearch} + ```python title="binary_search.py" + [class]{}-[func]{binary_search} ``` === "C++" @@ -55,10 +55,16 @@ [class]{}-[func]{binarySearch} ``` -=== "Python" +=== "Java" - ```python title="binary_search.py" - [class]{}-[func]{binary_search} + ```java title="binary_search.java" + [class]{binary_search}-[func]{binarySearch} + ``` + +=== "C#" + + ```csharp title="binary_search.cs" + [class]{binary_search}-[func]{binarySearch} ``` === "Go" @@ -67,6 +73,12 @@ [class]{}-[func]{binarySearch} ``` +=== "Swift" + + ```swift title="binary_search.swift" + [class]{}-[func]{binarySearch} + ``` + === "JS" ```javascript title="binary_search.js" @@ -79,30 +91,6 @@ [class]{}-[func]{binarySearch} ``` -=== "C" - - ```c title="binary_search.c" - [class]{}-[func]{binarySearch} - ``` - -=== "C#" - - ```csharp title="binary_search.cs" - [class]{binary_search}-[func]{binarySearch} - ``` - -=== "Swift" - - ```swift title="binary_search.swift" - [class]{}-[func]{binarySearch} - ``` - -=== "Zig" - - ```zig title="binary_search.zig" - [class]{}-[func]{binarySearch} - ``` - === "Dart" ```dart title="binary_search.dart" @@ -115,6 +103,18 @@ [class]{}-[func]{binary_search} ``` +=== "C" + + ```c title="binary_search.c" + [class]{}-[func]{binarySearch} + ``` + +=== "Zig" + + ```zig title="binary_search.zig" + [class]{}-[func]{binarySearch} + ``` + **时间复杂度 $O(\log n)$** :在二分循环中,区间每轮缩小一半,循环次数为 $\log_2 n$ 。 **空间复杂度 $O(1)$** :指针 $i$ 和 $j$ 使用常数大小空间。 @@ -125,10 +125,10 @@ 我们可以基于该表示实现具有相同功能的二分查找算法。 -=== "Java" +=== "Python" - ```java title="binary_search.java" - [class]{binary_search}-[func]{binarySearchLCRO} + ```python title="binary_search.py" + [class]{}-[func]{binary_search_lcro} ``` === "C++" @@ -137,10 +137,16 @@ [class]{}-[func]{binarySearchLCRO} ``` -=== "Python" +=== "Java" - ```python title="binary_search.py" - [class]{}-[func]{binary_search_lcro} + ```java title="binary_search.java" + [class]{binary_search}-[func]{binarySearchLCRO} + ``` + +=== "C#" + + ```csharp title="binary_search.cs" + [class]{binary_search}-[func]{binarySearchLCRO} ``` === "Go" @@ -149,6 +155,12 @@ [class]{}-[func]{binarySearchLCRO} ``` +=== "Swift" + + ```swift title="binary_search.swift" + [class]{}-[func]{binarySearchLCRO} + ``` + === "JS" ```javascript title="binary_search.js" @@ -161,30 +173,6 @@ [class]{}-[func]{binarySearchLCRO} ``` -=== "C" - - ```c title="binary_search.c" - [class]{}-[func]{binarySearchLCRO} - ``` - -=== "C#" - - ```csharp title="binary_search.cs" - [class]{binary_search}-[func]{binarySearchLCRO} - ``` - -=== "Swift" - - ```swift title="binary_search.swift" - [class]{}-[func]{binarySearchLCRO} - ``` - -=== "Zig" - - ```zig title="binary_search.zig" - [class]{}-[func]{binarySearchLCRO} - ``` - === "Dart" ```dart title="binary_search.dart" @@ -197,6 +185,18 @@ [class]{}-[func]{binary_search_lcro} ``` +=== "C" + + ```c title="binary_search.c" + [class]{}-[func]{binarySearchLCRO} + ``` + +=== "Zig" + + ```zig title="binary_search.zig" + [class]{}-[func]{binarySearchLCRO} + ``` + 如下图所示,在两种区间表示下,二分查找算法的初始化、循环条件和缩小区间操作皆有所不同。 由于“双闭区间”表示中的左右边界都被定义为闭区间,因此指针 $i$ 和 $j$ 缩小区间操作也是对称的。这样更不容易出错,**因此一般建议采用“双闭区间”的写法**。 diff --git a/docs/chapter_searching/binary_search_edge.md b/docs/chapter_searching/binary_search_edge.md index 9075b113..04256667 100644 --- a/docs/chapter_searching/binary_search_edge.md +++ b/docs/chapter_searching/binary_search_edge.md @@ -15,10 +15,10 @@ 当遇到以上两种情况时,直接返回 $-1$ 即可。 -=== "Java" +=== "Python" - ```java title="binary_search_edge.java" - [class]{binary_search_edge}-[func]{binarySearchLeftEdge} + ```python title="binary_search_edge.py" + [class]{}-[func]{binary_search_left_edge} ``` === "C++" @@ -27,10 +27,16 @@ [class]{}-[func]{binarySearchLeftEdge} ``` -=== "Python" +=== "Java" - ```python title="binary_search_edge.py" - [class]{}-[func]{binary_search_left_edge} + ```java title="binary_search_edge.java" + [class]{binary_search_edge}-[func]{binarySearchLeftEdge} + ``` + +=== "C#" + + ```csharp title="binary_search_edge.cs" + [class]{binary_search_edge}-[func]{binarySearchLeftEdge} ``` === "Go" @@ -39,6 +45,12 @@ [class]{}-[func]{binarySearchLeftEdge} ``` +=== "Swift" + + ```swift title="binary_search_edge.swift" + [class]{}-[func]{binarySearchLeftEdge} + ``` + === "JS" ```javascript title="binary_search_edge.js" @@ -51,30 +63,6 @@ [class]{}-[func]{binarySearchLeftEdge} ``` -=== "C" - - ```c title="binary_search_edge.c" - [class]{}-[func]{binarySearchLeftEdge} - ``` - -=== "C#" - - ```csharp title="binary_search_edge.cs" - [class]{binary_search_edge}-[func]{binarySearchLeftEdge} - ``` - -=== "Swift" - - ```swift title="binary_search_edge.swift" - [class]{}-[func]{binarySearchLeftEdge} - ``` - -=== "Zig" - - ```zig title="binary_search_edge.zig" - [class]{}-[func]{binarySearchLeftEdge} - ``` - === "Dart" ```dart title="binary_search_edge.dart" @@ -87,6 +75,18 @@ [class]{}-[func]{binary_search_left_edge} ``` +=== "C" + + ```c title="binary_search_edge.c" + [class]{}-[func]{binarySearchLeftEdge} + ``` + +=== "Zig" + + ```zig title="binary_search_edge.zig" + [class]{}-[func]{binarySearchLeftEdge} + ``` + ## 查找右边界 那么如何查找最右一个 `target` 呢?最直接的方式是修改代码,替换在 `nums[m] == target` 情况下的指针收缩操作。代码在此省略,有兴趣的同学可以自行实现。 @@ -103,10 +103,10 @@ 请注意,返回的插入点是 $i$ ,因此需要将其减 $1$ ,从而获得 $j$ 。 -=== "Java" +=== "Python" - ```java title="binary_search_edge.java" - [class]{binary_search_edge}-[func]{binarySearchRightEdge} + ```python title="binary_search_edge.py" + [class]{}-[func]{binary_search_right_edge} ``` === "C++" @@ -115,10 +115,16 @@ [class]{}-[func]{binarySearchRightEdge} ``` -=== "Python" +=== "Java" - ```python title="binary_search_edge.py" - [class]{}-[func]{binary_search_right_edge} + ```java title="binary_search_edge.java" + [class]{binary_search_edge}-[func]{binarySearchRightEdge} + ``` + +=== "C#" + + ```csharp title="binary_search_edge.cs" + [class]{binary_search_edge}-[func]{binarySearchRightEdge} ``` === "Go" @@ -127,6 +133,12 @@ [class]{}-[func]{binarySearchRightEdge} ``` +=== "Swift" + + ```swift title="binary_search_edge.swift" + [class]{}-[func]{binarySearchRightEdge} + ``` + === "JS" ```javascript title="binary_search_edge.js" @@ -139,30 +151,6 @@ [class]{}-[func]{binarySearchRightEdge} ``` -=== "C" - - ```c title="binary_search_edge.c" - [class]{}-[func]{binarySearchRightEdge} - ``` - -=== "C#" - - ```csharp title="binary_search_edge.cs" - [class]{binary_search_edge}-[func]{binarySearchRightEdge} - ``` - -=== "Swift" - - ```swift title="binary_search_edge.swift" - [class]{}-[func]{binarySearchRightEdge} - ``` - -=== "Zig" - - ```zig title="binary_search_edge.zig" - [class]{}-[func]{binarySearchRightEdge} - ``` - === "Dart" ```dart title="binary_search_edge.dart" @@ -175,6 +163,18 @@ [class]{}-[func]{binary_search_right_edge} ``` +=== "C" + + ```c title="binary_search_edge.c" + [class]{}-[func]{binarySearchRightEdge} + ``` + +=== "Zig" + + ```zig title="binary_search_edge.zig" + [class]{}-[func]{binarySearchRightEdge} + ``` + ### 转化为查找元素 我们知道,当数组不包含 `target` 时,最终 $i$ 和 $j$ 会分别指向首个大于、小于 `target` 的元素。 diff --git a/docs/chapter_searching/binary_search_insertion.md b/docs/chapter_searching/binary_search_insertion.md index d42c2f74..82c46d7b 100644 --- a/docs/chapter_searching/binary_search_insertion.md +++ b/docs/chapter_searching/binary_search_insertion.md @@ -22,10 +22,10 @@ 因此二分结束时一定有:$i$ 指向首个大于 `target` 的元素,$j$ 指向首个小于 `target` 的元素。**易得当数组不包含 `target` 时,插入索引为 $i$** 。 -=== "Java" +=== "Python" - ```java title="binary_search_insertion.java" - [class]{binary_search_insertion}-[func]{binarySearchInsertionSimple} + ```python title="binary_search_insertion.py" + [class]{}-[func]{binary_search_insertion_simple} ``` === "C++" @@ -34,10 +34,16 @@ [class]{}-[func]{binarySearchInsertionSimple} ``` -=== "Python" +=== "Java" - ```python title="binary_search_insertion.py" - [class]{}-[func]{binary_search_insertion_simple} + ```java title="binary_search_insertion.java" + [class]{binary_search_insertion}-[func]{binarySearchInsertionSimple} + ``` + +=== "C#" + + ```csharp title="binary_search_insertion.cs" + [class]{binary_search_insertion}-[func]{binarySearchInsertionSimple} ``` === "Go" @@ -46,6 +52,12 @@ [class]{}-[func]{binarySearchInsertionSimple} ``` +=== "Swift" + + ```swift title="binary_search_insertion.swift" + [class]{}-[func]{binarySearchInsertionSimple} + ``` + === "JS" ```javascript title="binary_search_insertion.js" @@ -58,30 +70,6 @@ [class]{}-[func]{binarySearchInsertionSimple} ``` -=== "C" - - ```c title="binary_search_insertion.c" - [class]{}-[func]{binarySearchInsertionSimple} - ``` - -=== "C#" - - ```csharp title="binary_search_insertion.cs" - [class]{binary_search_insertion}-[func]{binarySearchInsertionSimple} - ``` - -=== "Swift" - - ```swift title="binary_search_insertion.swift" - [class]{}-[func]{binarySearchInsertionSimple} - ``` - -=== "Zig" - - ```zig title="binary_search_insertion.zig" - [class]{}-[func]{binarySearchInsertionSimple} - ``` - === "Dart" ```dart title="binary_search_insertion.dart" @@ -94,6 +82,18 @@ [class]{}-[func]{binary_search_insertion} ``` +=== "C" + + ```c title="binary_search_insertion.c" + [class]{}-[func]{binarySearchInsertionSimple} + ``` + +=== "Zig" + + ```zig title="binary_search_insertion.zig" + [class]{}-[func]{binarySearchInsertionSimple} + ``` + ## 存在重复元素的情况 !!! question @@ -146,10 +146,10 @@ 即便如此,我们仍然可以将判断条件保持展开,因为其逻辑更加清晰、可读性更好。 -=== "Java" +=== "Python" - ```java title="binary_search_insertion.java" - [class]{binary_search_insertion}-[func]{binarySearchInsertion} + ```python title="binary_search_insertion.py" + [class]{}-[func]{binary_search_insertion} ``` === "C++" @@ -158,10 +158,16 @@ [class]{}-[func]{binarySearchInsertion} ``` -=== "Python" +=== "Java" - ```python title="binary_search_insertion.py" - [class]{}-[func]{binary_search_insertion} + ```java title="binary_search_insertion.java" + [class]{binary_search_insertion}-[func]{binarySearchInsertion} + ``` + +=== "C#" + + ```csharp title="binary_search_insertion.cs" + [class]{binary_search_insertion}-[func]{binarySearchInsertion} ``` === "Go" @@ -170,6 +176,12 @@ [class]{}-[func]{binarySearchInsertion} ``` +=== "Swift" + + ```swift title="binary_search_insertion.swift" + [class]{}-[func]{binarySearchInsertion} + ``` + === "JS" ```javascript title="binary_search_insertion.js" @@ -182,30 +194,6 @@ [class]{}-[func]{binarySearchInsertion} ``` -=== "C" - - ```c title="binary_search_insertion.c" - [class]{}-[func]{binarySearchInsertion} - ``` - -=== "C#" - - ```csharp title="binary_search_insertion.cs" - [class]{binary_search_insertion}-[func]{binarySearchInsertion} - ``` - -=== "Swift" - - ```swift title="binary_search_insertion.swift" - [class]{}-[func]{binarySearchInsertion} - ``` - -=== "Zig" - - ```zig title="binary_search_insertion.zig" - [class]{}-[func]{binarySearchInsertion} - ``` - === "Dart" ```dart title="binary_search_insertion.dart" @@ -218,6 +206,18 @@ [class]{}-[func]{binary_search_insertion} ``` +=== "C" + + ```c title="binary_search_insertion.c" + [class]{}-[func]{binarySearchInsertion} + ``` + +=== "Zig" + + ```zig title="binary_search_insertion.zig" + [class]{}-[func]{binarySearchInsertion} + ``` + !!! tip 本节的代码都是“双闭区间”写法。有兴趣的读者可以自行实现“左闭右开”写法。 diff --git a/docs/chapter_searching/replace_linear_by_hashing.md b/docs/chapter_searching/replace_linear_by_hashing.md index 1a543d59..6ef322a2 100755 --- a/docs/chapter_searching/replace_linear_by_hashing.md +++ b/docs/chapter_searching/replace_linear_by_hashing.md @@ -12,10 +12,10 @@ ![线性查找求解两数之和](replace_linear_by_hashing.assets/two_sum_brute_force.png) -=== "Java" +=== "Python" - ```java title="two_sum.java" - [class]{two_sum}-[func]{twoSumBruteForce} + ```python title="two_sum.py" + [class]{}-[func]{two_sum_brute_force} ``` === "C++" @@ -24,10 +24,16 @@ [class]{}-[func]{twoSumBruteForce} ``` -=== "Python" +=== "Java" - ```python title="two_sum.py" - [class]{}-[func]{two_sum_brute_force} + ```java title="two_sum.java" + [class]{two_sum}-[func]{twoSumBruteForce} + ``` + +=== "C#" + + ```csharp title="two_sum.cs" + [class]{two_sum}-[func]{twoSumBruteForce} ``` === "Go" @@ -36,6 +42,12 @@ [class]{}-[func]{twoSumBruteForce} ``` +=== "Swift" + + ```swift title="two_sum.swift" + [class]{}-[func]{twoSumBruteForce} + ``` + === "JS" ```javascript title="two_sum.js" @@ -48,30 +60,6 @@ [class]{}-[func]{twoSumBruteForce} ``` -=== "C" - - ```c title="two_sum.c" - [class]{}-[func]{twoSumBruteForce} - ``` - -=== "C#" - - ```csharp title="two_sum.cs" - [class]{two_sum}-[func]{twoSumBruteForce} - ``` - -=== "Swift" - - ```swift title="two_sum.swift" - [class]{}-[func]{twoSumBruteForce} - ``` - -=== "Zig" - - ```zig title="two_sum.zig" - [class]{}-[func]{twoSumBruteForce} - ``` - === "Dart" ```dart title="two_sum.dart" @@ -84,6 +72,18 @@ [class]{}-[func]{two_sum_brute_force} ``` +=== "C" + + ```c title="two_sum.c" + [class]{}-[func]{twoSumBruteForce} + ``` + +=== "Zig" + + ```zig title="two_sum.zig" + [class]{}-[func]{twoSumBruteForce} + ``` + 此方法的时间复杂度为 $O(n^2)$ ,空间复杂度为 $O(1)$ ,在大数据量下非常耗时。 ## 哈希查找:以空间换时间 @@ -104,10 +104,10 @@ 实现代码如下所示,仅需单层循环即可。 -=== "Java" +=== "Python" - ```java title="two_sum.java" - [class]{two_sum}-[func]{twoSumHashTable} + ```python title="two_sum.py" + [class]{}-[func]{two_sum_hash_table} ``` === "C++" @@ -116,10 +116,16 @@ [class]{}-[func]{twoSumHashTable} ``` -=== "Python" +=== "Java" - ```python title="two_sum.py" - [class]{}-[func]{two_sum_hash_table} + ```java title="two_sum.java" + [class]{two_sum}-[func]{twoSumHashTable} + ``` + +=== "C#" + + ```csharp title="two_sum.cs" + [class]{two_sum}-[func]{twoSumHashTable} ``` === "Go" @@ -128,6 +134,12 @@ [class]{}-[func]{twoSumHashTable} ``` +=== "Swift" + + ```swift title="two_sum.swift" + [class]{}-[func]{twoSumHashTable} + ``` + === "JS" ```javascript title="two_sum.js" @@ -140,32 +152,6 @@ [class]{}-[func]{twoSumHashTable} ``` -=== "C" - - ```c title="two_sum.c" - [class]{hashTable}-[func]{} - - [class]{}-[func]{twoSumHashTable} - ``` - -=== "C#" - - ```csharp title="two_sum.cs" - [class]{two_sum}-[func]{twoSumHashTable} - ``` - -=== "Swift" - - ```swift title="two_sum.swift" - [class]{}-[func]{twoSumHashTable} - ``` - -=== "Zig" - - ```zig title="two_sum.zig" - [class]{}-[func]{twoSumHashTable} - ``` - === "Dart" ```dart title="two_sum.dart" @@ -178,6 +164,20 @@ [class]{}-[func]{two_sum_hash_table} ``` +=== "C" + + ```c title="two_sum.c" + [class]{hashTable}-[func]{} + + [class]{}-[func]{twoSumHashTable} + ``` + +=== "Zig" + + ```zig title="two_sum.zig" + [class]{}-[func]{twoSumHashTable} + ``` + 此方法通过哈希查找将时间复杂度从 $O(n^2)$ 降低至 $O(n)$ ,大幅提升运行效率。 由于需要维护一个额外的哈希表,因此空间复杂度为 $O(n)$ 。**尽管如此,该方法的整体时空效率更为均衡,因此它是本题的最优解法**。 diff --git a/docs/chapter_sorting/bubble_sort.md b/docs/chapter_sorting/bubble_sort.md index 90a33ed1..6995375a 100755 --- a/docs/chapter_sorting/bubble_sort.md +++ b/docs/chapter_sorting/bubble_sort.md @@ -36,10 +36,10 @@ ![冒泡排序流程](bubble_sort.assets/bubble_sort_overview.png) -=== "Java" +=== "Python" - ```java title="bubble_sort.java" - [class]{bubble_sort}-[func]{bubbleSort} + ```python title="bubble_sort.py" + [class]{}-[func]{bubble_sort} ``` === "C++" @@ -48,10 +48,16 @@ [class]{}-[func]{bubbleSort} ``` -=== "Python" +=== "Java" - ```python title="bubble_sort.py" - [class]{}-[func]{bubble_sort} + ```java title="bubble_sort.java" + [class]{bubble_sort}-[func]{bubbleSort} + ``` + +=== "C#" + + ```csharp title="bubble_sort.cs" + [class]{bubble_sort}-[func]{bubbleSort} ``` === "Go" @@ -60,6 +66,12 @@ [class]{}-[func]{bubbleSort} ``` +=== "Swift" + + ```swift title="bubble_sort.swift" + [class]{}-[func]{bubbleSort} + ``` + === "JS" ```javascript title="bubble_sort.js" @@ -72,30 +84,6 @@ [class]{}-[func]{bubbleSort} ``` -=== "C" - - ```c title="bubble_sort.c" - [class]{}-[func]{bubbleSort} - ``` - -=== "C#" - - ```csharp title="bubble_sort.cs" - [class]{bubble_sort}-[func]{bubbleSort} - ``` - -=== "Swift" - - ```swift title="bubble_sort.swift" - [class]{}-[func]{bubbleSort} - ``` - -=== "Zig" - - ```zig title="bubble_sort.zig" - [class]{}-[func]{bubbleSort} - ``` - === "Dart" ```dart title="bubble_sort.dart" @@ -108,16 +96,28 @@ [class]{}-[func]{bubble_sort} ``` +=== "C" + + ```c title="bubble_sort.c" + [class]{}-[func]{bubbleSort} + ``` + +=== "Zig" + + ```zig title="bubble_sort.zig" + [class]{}-[func]{bubbleSort} + ``` + ## 效率优化 我们发现,如果某轮“冒泡”中没有执行任何交换操作,说明数组已经完成排序,可直接返回结果。因此,可以增加一个标志位 `flag` 来监测这种情况,一旦出现就立即返回。 经过优化,冒泡排序的最差和平均时间复杂度仍为 $O(n^2)$ ;但当输入数组完全有序时,可达到最佳时间复杂度 $O(n)$ 。 -=== "Java" +=== "Python" - ```java title="bubble_sort.java" - [class]{bubble_sort}-[func]{bubbleSortWithFlag} + ```python title="bubble_sort.py" + [class]{}-[func]{bubble_sort_with_flag} ``` === "C++" @@ -126,10 +126,16 @@ [class]{}-[func]{bubbleSortWithFlag} ``` -=== "Python" +=== "Java" - ```python title="bubble_sort.py" - [class]{}-[func]{bubble_sort_with_flag} + ```java title="bubble_sort.java" + [class]{bubble_sort}-[func]{bubbleSortWithFlag} + ``` + +=== "C#" + + ```csharp title="bubble_sort.cs" + [class]{bubble_sort}-[func]{bubbleSortWithFlag} ``` === "Go" @@ -138,6 +144,12 @@ [class]{}-[func]{bubbleSortWithFlag} ``` +=== "Swift" + + ```swift title="bubble_sort.swift" + [class]{}-[func]{bubbleSortWithFlag} + ``` + === "JS" ```javascript title="bubble_sort.js" @@ -150,30 +162,6 @@ [class]{}-[func]{bubbleSortWithFlag} ``` -=== "C" - - ```c title="bubble_sort.c" - [class]{}-[func]{bubbleSortWithFlag} - ``` - -=== "C#" - - ```csharp title="bubble_sort.cs" - [class]{bubble_sort}-[func]{bubbleSortWithFlag} - ``` - -=== "Swift" - - ```swift title="bubble_sort.swift" - [class]{}-[func]{bubbleSortWithFlag} - ``` - -=== "Zig" - - ```zig title="bubble_sort.zig" - [class]{}-[func]{bubbleSortWithFlag} - ``` - === "Dart" ```dart title="bubble_sort.dart" @@ -186,6 +174,18 @@ [class]{}-[func]{bubble_sort_with_flag} ``` +=== "C" + + ```c title="bubble_sort.c" + [class]{}-[func]{bubbleSortWithFlag} + ``` + +=== "Zig" + + ```zig title="bubble_sort.zig" + [class]{}-[func]{bubbleSortWithFlag} + ``` + ## 算法特性 - **时间复杂度为 $O(n^2)$、自适应排序**:各轮“冒泡”遍历的数组长度依次为 $n - 1$、$n - 2$、$\dots$、$2$、$1$ ,总和为 $(n - 1) n / 2$ 。在引入 `flag` 优化后,最佳时间复杂度可达到 $O(n)$ 。 diff --git a/docs/chapter_sorting/bucket_sort.md b/docs/chapter_sorting/bucket_sort.md index cbfe7fe5..5e3b1f13 100644 --- a/docs/chapter_sorting/bucket_sort.md +++ b/docs/chapter_sorting/bucket_sort.md @@ -14,10 +14,10 @@ ![桶排序算法流程](bucket_sort.assets/bucket_sort_overview.png) -=== "Java" +=== "Python" - ```java title="bucket_sort.java" - [class]{bucket_sort}-[func]{bucketSort} + ```python title="bucket_sort.py" + [class]{}-[func]{bucket_sort} ``` === "C++" @@ -26,10 +26,16 @@ [class]{}-[func]{bucketSort} ``` -=== "Python" +=== "Java" - ```python title="bucket_sort.py" - [class]{}-[func]{bucket_sort} + ```java title="bucket_sort.java" + [class]{bucket_sort}-[func]{bucketSort} + ``` + +=== "C#" + + ```csharp title="bucket_sort.cs" + [class]{bucket_sort}-[func]{bucketSort} ``` === "Go" @@ -38,6 +44,12 @@ [class]{}-[func]{bucketSort} ``` +=== "Swift" + + ```swift title="bucket_sort.swift" + [class]{}-[func]{bucketSort} + ``` + === "JS" ```javascript title="bucket_sort.js" @@ -50,30 +62,6 @@ [class]{}-[func]{bucketSort} ``` -=== "C" - - ```c title="bucket_sort.c" - [class]{}-[func]{bucketSort} - ``` - -=== "C#" - - ```csharp title="bucket_sort.cs" - [class]{bucket_sort}-[func]{bucketSort} - ``` - -=== "Swift" - - ```swift title="bucket_sort.swift" - [class]{}-[func]{bucketSort} - ``` - -=== "Zig" - - ```zig title="bucket_sort.zig" - [class]{}-[func]{bucketSort} - ``` - === "Dart" ```dart title="bucket_sort.dart" @@ -86,6 +74,18 @@ [class]{}-[func]{bucket_sort} ``` +=== "C" + + ```c title="bucket_sort.c" + [class]{}-[func]{bucketSort} + ``` + +=== "Zig" + + ```zig title="bucket_sort.zig" + [class]{}-[func]{bucketSort} + ``` + ## 算法特性 桶排序适用于处理体量很大的数据。例如,输入数据包含 100 万个元素,由于空间限制,系统内存无法一次性加载所有数据。此时,可以将数据分成 1000 个桶,然后分别对每个桶进行排序,最后将结果合并。 diff --git a/docs/chapter_sorting/counting_sort.md b/docs/chapter_sorting/counting_sort.md index f249c421..62ba3abf 100644 --- a/docs/chapter_sorting/counting_sort.md +++ b/docs/chapter_sorting/counting_sort.md @@ -12,10 +12,10 @@ ![计数排序流程](counting_sort.assets/counting_sort_overview.png) -=== "Java" +=== "Python" - ```java title="counting_sort.java" - [class]{counting_sort}-[func]{countingSortNaive} + ```python title="counting_sort.py" + [class]{}-[func]{counting_sort_naive} ``` === "C++" @@ -24,10 +24,16 @@ [class]{}-[func]{countingSortNaive} ``` -=== "Python" +=== "Java" - ```python title="counting_sort.py" - [class]{}-[func]{counting_sort_naive} + ```java title="counting_sort.java" + [class]{counting_sort}-[func]{countingSortNaive} + ``` + +=== "C#" + + ```csharp title="counting_sort.cs" + [class]{counting_sort}-[func]{countingSortNaive} ``` === "Go" @@ -36,6 +42,12 @@ [class]{}-[func]{countingSortNaive} ``` +=== "Swift" + + ```swift title="counting_sort.swift" + [class]{}-[func]{countingSortNaive} + ``` + === "JS" ```javascript title="counting_sort.js" @@ -48,30 +60,6 @@ [class]{}-[func]{countingSortNaive} ``` -=== "C" - - ```c title="counting_sort.c" - [class]{}-[func]{countingSortNaive} - ``` - -=== "C#" - - ```csharp title="counting_sort.cs" - [class]{counting_sort}-[func]{countingSortNaive} - ``` - -=== "Swift" - - ```swift title="counting_sort.swift" - [class]{}-[func]{countingSortNaive} - ``` - -=== "Zig" - - ```zig title="counting_sort.zig" - [class]{}-[func]{countingSortNaive} - ``` - === "Dart" ```dart title="counting_sort.dart" @@ -84,6 +72,18 @@ [class]{}-[func]{counting_sort_naive} ``` +=== "C" + + ```c title="counting_sort.c" + [class]{}-[func]{countingSortNaive} + ``` + +=== "Zig" + + ```zig title="counting_sort.zig" + [class]{}-[func]{countingSortNaive} + ``` + !!! note "计数排序与桶排序的联系" 从桶排序的角度看,我们可以将计数排序中的计数数组 `counter` 的每个索引视为一个桶,将统计数量的过程看作是将各个元素分配到对应的桶中。本质上,计数排序是桶排序在整型数据下的一个特例。 @@ -131,10 +131,10 @@ $$ 计数排序的实现代码如下所示。 -=== "Java" +=== "Python" - ```java title="counting_sort.java" - [class]{counting_sort}-[func]{countingSort} + ```python title="counting_sort.py" + [class]{}-[func]{counting_sort} ``` === "C++" @@ -143,10 +143,16 @@ $$ [class]{}-[func]{countingSort} ``` -=== "Python" +=== "Java" - ```python title="counting_sort.py" - [class]{}-[func]{counting_sort} + ```java title="counting_sort.java" + [class]{counting_sort}-[func]{countingSort} + ``` + +=== "C#" + + ```csharp title="counting_sort.cs" + [class]{counting_sort}-[func]{countingSort} ``` === "Go" @@ -155,6 +161,12 @@ $$ [class]{}-[func]{countingSort} ``` +=== "Swift" + + ```swift title="counting_sort.swift" + [class]{}-[func]{countingSort} + ``` + === "JS" ```javascript title="counting_sort.js" @@ -167,30 +179,6 @@ $$ [class]{}-[func]{countingSort} ``` -=== "C" - - ```c title="counting_sort.c" - [class]{}-[func]{countingSort} - ``` - -=== "C#" - - ```csharp title="counting_sort.cs" - [class]{counting_sort}-[func]{countingSort} - ``` - -=== "Swift" - - ```swift title="counting_sort.swift" - [class]{}-[func]{countingSort} - ``` - -=== "Zig" - - ```zig title="counting_sort.zig" - [class]{}-[func]{countingSort} - ``` - === "Dart" ```dart title="counting_sort.dart" @@ -203,6 +191,18 @@ $$ [class]{}-[func]{counting_sort} ``` +=== "C" + + ```c title="counting_sort.c" + [class]{}-[func]{countingSort} + ``` + +=== "Zig" + + ```zig title="counting_sort.zig" + [class]{}-[func]{countingSort} + ``` + ## 算法特性 - **时间复杂度 $O(n + m)$** :涉及遍历 `nums` 和遍历 `counter` ,都使用线性时间。一般情况下 $n \gg m$ ,时间复杂度趋于 $O(n)$ 。 diff --git a/docs/chapter_sorting/heap_sort.md b/docs/chapter_sorting/heap_sort.md index f048c8f9..58e459b1 100644 --- a/docs/chapter_sorting/heap_sort.md +++ b/docs/chapter_sorting/heap_sort.md @@ -62,12 +62,12 @@ 在代码实现中,我们使用了与堆章节相同的从顶至底堆化 `sift_down()` 函数。值得注意的是,由于堆的长度会随着提取最大元素而减小,因此我们需要给 `sift_down()` 函数添加一个长度参数 $n$ ,用于指定堆的当前有效长度。 -=== "Java" +=== "Python" - ```java title="heap_sort.java" - [class]{heap_sort}-[func]{siftDown} + ```python title="heap_sort.py" + [class]{}-[func]{sift_down} - [class]{heap_sort}-[func]{heapSort} + [class]{}-[func]{heap_sort} ``` === "C++" @@ -78,12 +78,20 @@ [class]{}-[func]{heapSort} ``` -=== "Python" +=== "Java" - ```python title="heap_sort.py" - [class]{}-[func]{sift_down} + ```java title="heap_sort.java" + [class]{heap_sort}-[func]{siftDown} - [class]{}-[func]{heap_sort} + [class]{heap_sort}-[func]{heapSort} + ``` + +=== "C#" + + ```csharp title="heap_sort.cs" + [class]{heap_sort}-[func]{siftDown} + + [class]{heap_sort}-[func]{heapSort} ``` === "Go" @@ -94,6 +102,14 @@ [class]{}-[func]{heapSort} ``` +=== "Swift" + + ```swift title="heap_sort.swift" + [class]{}-[func]{siftDown} + + [class]{}-[func]{heapSort} + ``` + === "JS" ```javascript title="heap_sort.js" @@ -110,38 +126,6 @@ [class]{}-[func]{heapSort} ``` -=== "C" - - ```c title="heap_sort.c" - [class]{}-[func]{siftDown} - - [class]{}-[func]{heapSort} - ``` - -=== "C#" - - ```csharp title="heap_sort.cs" - [class]{heap_sort}-[func]{siftDown} - - [class]{heap_sort}-[func]{heapSort} - ``` - -=== "Swift" - - ```swift title="heap_sort.swift" - [class]{}-[func]{siftDown} - - [class]{}-[func]{heapSort} - ``` - -=== "Zig" - - ```zig title="heap_sort.zig" - [class]{}-[func]{siftDown} - - [class]{}-[func]{heapSort} - ``` - === "Dart" ```dart title="heap_sort.dart" @@ -158,6 +142,22 @@ [class]{}-[func]{heap_sort} ``` +=== "C" + + ```c title="heap_sort.c" + [class]{}-[func]{siftDown} + + [class]{}-[func]{heapSort} + ``` + +=== "Zig" + + ```zig title="heap_sort.zig" + [class]{}-[func]{siftDown} + + [class]{}-[func]{heapSort} + ``` + ## 算法特性 - **时间复杂度 $O(n \log n)$、非自适应排序**:建堆操作使用 $O(n)$ 时间。从堆中提取最大元素的时间复杂度为 $O(\log n)$ ,共循环 $n - 1$ 轮。 diff --git a/docs/chapter_sorting/insertion_sort.md b/docs/chapter_sorting/insertion_sort.md index 6865538b..a7f6e4e8 100755 --- a/docs/chapter_sorting/insertion_sort.md +++ b/docs/chapter_sorting/insertion_sort.md @@ -19,10 +19,10 @@ ![插入排序流程](insertion_sort.assets/insertion_sort_overview.png) -=== "Java" +=== "Python" - ```java title="insertion_sort.java" - [class]{insertion_sort}-[func]{insertionSort} + ```python title="insertion_sort.py" + [class]{}-[func]{insertion_sort} ``` === "C++" @@ -31,10 +31,16 @@ [class]{}-[func]{insertionSort} ``` -=== "Python" +=== "Java" - ```python title="insertion_sort.py" - [class]{}-[func]{insertion_sort} + ```java title="insertion_sort.java" + [class]{insertion_sort}-[func]{insertionSort} + ``` + +=== "C#" + + ```csharp title="insertion_sort.cs" + [class]{insertion_sort}-[func]{insertionSort} ``` === "Go" @@ -43,6 +49,12 @@ [class]{}-[func]{insertionSort} ``` +=== "Swift" + + ```swift title="insertion_sort.swift" + [class]{}-[func]{insertionSort} + ``` + === "JS" ```javascript title="insertion_sort.js" @@ -55,30 +67,6 @@ [class]{}-[func]{insertionSort} ``` -=== "C" - - ```c title="insertion_sort.c" - [class]{}-[func]{insertionSort} - ``` - -=== "C#" - - ```csharp title="insertion_sort.cs" - [class]{insertion_sort}-[func]{insertionSort} - ``` - -=== "Swift" - - ```swift title="insertion_sort.swift" - [class]{}-[func]{insertionSort} - ``` - -=== "Zig" - - ```zig title="insertion_sort.zig" - [class]{}-[func]{insertionSort} - ``` - === "Dart" ```dart title="insertion_sort.dart" @@ -91,6 +79,18 @@ [class]{}-[func]{insertion_sort} ``` +=== "C" + + ```c title="insertion_sort.c" + [class]{}-[func]{insertionSort} + ``` + +=== "Zig" + + ```zig title="insertion_sort.zig" + [class]{}-[func]{insertionSort} + ``` + ## 算法特性 - **时间复杂度 $O(n^2)$、自适应排序**:最差情况下,每次插入操作分别需要循环 $n - 1$、$n-2$、$\dots$、$2$、$1$ 次,求和得到 $(n - 1) n / 2$ ,因此时间复杂度为 $O(n^2)$ 。在遇到有序数据时,插入操作会提前终止。当输入数组完全有序时,插入排序达到最佳时间复杂度 $O(n)$ 。 diff --git a/docs/chapter_sorting/merge_sort.md b/docs/chapter_sorting/merge_sort.md index 20eeb639..ab14a73f 100755 --- a/docs/chapter_sorting/merge_sort.md +++ b/docs/chapter_sorting/merge_sort.md @@ -51,12 +51,12 @@ - **后序遍历**:先递归左子树,再递归右子树,最后处理根节点。 - **归并排序**:先递归左子数组,再递归右子数组,最后处理合并。 -=== "Java" +=== "Python" - ```java title="merge_sort.java" - [class]{merge_sort}-[func]{merge} + ```python title="merge_sort.py" + [class]{}-[func]{merge} - [class]{merge_sort}-[func]{mergeSort} + [class]{}-[func]{merge_sort} ``` === "C++" @@ -67,12 +67,20 @@ [class]{}-[func]{mergeSort} ``` -=== "Python" +=== "Java" - ```python title="merge_sort.py" - [class]{}-[func]{merge} + ```java title="merge_sort.java" + [class]{merge_sort}-[func]{merge} - [class]{}-[func]{merge_sort} + [class]{merge_sort}-[func]{mergeSort} + ``` + +=== "C#" + + ```csharp title="merge_sort.cs" + [class]{merge_sort}-[func]{merge} + + [class]{merge_sort}-[func]{mergeSort} ``` === "Go" @@ -83,6 +91,14 @@ [class]{}-[func]{mergeSort} ``` +=== "Swift" + + ```swift title="merge_sort.swift" + [class]{}-[func]{merge} + + [class]{}-[func]{mergeSort} + ``` + === "JS" ```javascript title="merge_sort.js" @@ -99,38 +115,6 @@ [class]{}-[func]{mergeSort} ``` -=== "C" - - ```c title="merge_sort.c" - [class]{}-[func]{merge} - - [class]{}-[func]{mergeSort} - ``` - -=== "C#" - - ```csharp title="merge_sort.cs" - [class]{merge_sort}-[func]{merge} - - [class]{merge_sort}-[func]{mergeSort} - ``` - -=== "Swift" - - ```swift title="merge_sort.swift" - [class]{}-[func]{merge} - - [class]{}-[func]{mergeSort} - ``` - -=== "Zig" - - ```zig title="merge_sort.zig" - [class]{}-[func]{merge} - - [class]{}-[func]{mergeSort} - ``` - === "Dart" ```dart title="merge_sort.dart" @@ -147,6 +131,22 @@ [class]{}-[func]{merge_sort} ``` +=== "C" + + ```c title="merge_sort.c" + [class]{}-[func]{merge} + + [class]{}-[func]{mergeSort} + ``` + +=== "Zig" + + ```zig title="merge_sort.zig" + [class]{}-[func]{merge} + + [class]{}-[func]{mergeSort} + ``` + 实现合并函数 `merge()` 存在以下难点。 - **需要特别注意各个变量的含义**。`nums` 的待合并区间为 `[left, right]` ,但由于 `tmp` 仅复制了 `nums` 该区间的元素,因此 `tmp` 对应区间为 `[0, right - left]` 。 diff --git a/docs/chapter_sorting/quick_sort.md b/docs/chapter_sorting/quick_sort.md index f1a3dc16..cb7ba2bd 100755 --- a/docs/chapter_sorting/quick_sort.md +++ b/docs/chapter_sorting/quick_sort.md @@ -41,11 +41,9 @@ 哨兵划分的实质是将一个较长数组的排序问题简化为两个较短数组的排序问题。 -=== "Java" - - ```java title="quick_sort.java" - [class]{QuickSort}-[func]{swap} +=== "Python" + ```python title="quick_sort.py" [class]{QuickSort}-[func]{partition} ``` @@ -57,9 +55,19 @@ [class]{QuickSort}-[func]{partition} ``` -=== "Python" +=== "Java" + + ```java title="quick_sort.java" + [class]{QuickSort}-[func]{swap} + + [class]{QuickSort}-[func]{partition} + ``` + +=== "C#" + + ```csharp title="quick_sort.cs" + [class]{QuickSort}-[func]{swap} - ```python title="quick_sort.py" [class]{QuickSort}-[func]{partition} ``` @@ -69,6 +77,14 @@ [class]{quickSort}-[func]{partition} ``` +=== "Swift" + + ```swift title="quick_sort.swift" + [class]{}-[func]{swap} + + [class]{}-[func]{partition} + ``` + === "JS" ```javascript title="quick_sort.js" @@ -85,38 +101,6 @@ [class]{QuickSort}-[func]{partition} ``` -=== "C" - - ```c title="quick_sort.c" - [class]{}-[func]{swap} - - [class]{}-[func]{partition} - ``` - -=== "C#" - - ```csharp title="quick_sort.cs" - [class]{QuickSort}-[func]{swap} - - [class]{QuickSort}-[func]{partition} - ``` - -=== "Swift" - - ```swift title="quick_sort.swift" - [class]{}-[func]{swap} - - [class]{}-[func]{partition} - ``` - -=== "Zig" - - ```zig title="quick_sort.zig" - [class]{QuickSort}-[func]{swap} - - [class]{QuickSort}-[func]{partition} - ``` - === "Dart" ```dart title="quick_sort.dart" @@ -131,6 +115,22 @@ [class]{QuickSort}-[func]{partition} ``` +=== "C" + + ```c title="quick_sort.c" + [class]{}-[func]{swap} + + [class]{}-[func]{partition} + ``` + +=== "Zig" + + ```zig title="quick_sort.zig" + [class]{QuickSort}-[func]{swap} + + [class]{QuickSort}-[func]{partition} + ``` + ## 算法流程 快速排序的整体流程如下图所示。 @@ -141,10 +141,10 @@ ![快速排序流程](quick_sort.assets/quick_sort_overview.png) -=== "Java" +=== "Python" - ```java title="quick_sort.java" - [class]{QuickSort}-[func]{quickSort} + ```python title="quick_sort.py" + [class]{QuickSort}-[func]{quick_sort} ``` === "C++" @@ -153,10 +153,16 @@ [class]{QuickSort}-[func]{quickSort} ``` -=== "Python" +=== "Java" - ```python title="quick_sort.py" - [class]{QuickSort}-[func]{quick_sort} + ```java title="quick_sort.java" + [class]{QuickSort}-[func]{quickSort} + ``` + +=== "C#" + + ```csharp title="quick_sort.cs" + [class]{QuickSort}-[func]{quickSort} ``` === "Go" @@ -165,6 +171,12 @@ [class]{quickSort}-[func]{quickSort} ``` +=== "Swift" + + ```swift title="quick_sort.swift" + [class]{}-[func]{quickSort} + ``` + === "JS" ```javascript title="quick_sort.js" @@ -177,30 +189,6 @@ [class]{QuickSort}-[func]{quickSort} ``` -=== "C" - - ```c title="quick_sort.c" - [class]{}-[func]{quickSort} - ``` - -=== "C#" - - ```csharp title="quick_sort.cs" - [class]{QuickSort}-[func]{quickSort} - ``` - -=== "Swift" - - ```swift title="quick_sort.swift" - [class]{}-[func]{quickSort} - ``` - -=== "Zig" - - ```zig title="quick_sort.zig" - [class]{QuickSort}-[func]{quickSort} - ``` - === "Dart" ```dart title="quick_sort.dart" @@ -213,6 +201,18 @@ [class]{QuickSort}-[func]{quick_sort} ``` +=== "C" + + ```c title="quick_sort.c" + [class]{}-[func]{quickSort} + ``` + +=== "Zig" + + ```zig title="quick_sort.zig" + [class]{QuickSort}-[func]{quickSort} + ``` + ## 算法特性 - **时间复杂度 $O(n \log n)$、自适应排序**:在平均情况下,哨兵划分的递归层数为 $\log n$ ,每层中的总循环数为 $n$ ,总体使用 $O(n \log n)$ 时间。在最差情况下,每轮哨兵划分操作都将长度为 $n$ 的数组划分为长度为 $0$ 和 $n - 1$ 的两个子数组,此时递归层数达到 $n$ 层,每层中的循环数为 $n$ ,总体使用 $O(n^2)$ 时间。 @@ -237,10 +237,10 @@ 为了进一步改进,我们可以在数组中选取三个候选元素(通常为数组的首、尾、中点元素),**并将这三个候选元素的中位数作为基准数**。这样一来,基准数“既不太小也不太大”的概率将大幅提升。当然,我们还可以选取更多候选元素,以进一步提高算法的稳健性。采用这种方法后,时间复杂度劣化至 $O(n^2)$ 的概率大大降低。 -=== "Java" +=== "Python" - ```java title="quick_sort.java" - [class]{QuickSortMedian}-[func]{medianThree} + ```python title="quick_sort.py" + [class]{QuickSortMedian}-[func]{median_three} [class]{QuickSortMedian}-[func]{partition} ``` @@ -253,10 +253,18 @@ [class]{QuickSortMedian}-[func]{partition} ``` -=== "Python" +=== "Java" - ```python title="quick_sort.py" - [class]{QuickSortMedian}-[func]{median_three} + ```java title="quick_sort.java" + [class]{QuickSortMedian}-[func]{medianThree} + + [class]{QuickSortMedian}-[func]{partition} + ``` + +=== "C#" + + ```csharp title="quick_sort.cs" + [class]{QuickSortMedian}-[func]{medianThree} [class]{QuickSortMedian}-[func]{partition} ``` @@ -269,6 +277,14 @@ [class]{quickSortMedian}-[func]{partition} ``` +=== "Swift" + + ```swift title="quick_sort.swift" + [class]{}-[func]{medianThree} + + [class]{}-[func]{partitionMedian} + ``` + === "JS" ```javascript title="quick_sort.js" @@ -285,38 +301,6 @@ [class]{QuickSortMedian}-[func]{partition} ``` -=== "C" - - ```c title="quick_sort.c" - [class]{}-[func]{medianThree} - - [class]{}-[func]{partitionMedian} - ``` - -=== "C#" - - ```csharp title="quick_sort.cs" - [class]{QuickSortMedian}-[func]{medianThree} - - [class]{QuickSortMedian}-[func]{partition} - ``` - -=== "Swift" - - ```swift title="quick_sort.swift" - [class]{}-[func]{medianThree} - - [class]{}-[func]{partitionMedian} - ``` - -=== "Zig" - - ```zig title="quick_sort.zig" - [class]{QuickSortMedian}-[func]{medianThree} - - [class]{QuickSortMedian}-[func]{partition} - ``` - === "Dart" ```dart title="quick_sort.dart" @@ -333,16 +317,32 @@ [class]{QuickSortMedian}-[func]{partition} ``` +=== "C" + + ```c title="quick_sort.c" + [class]{}-[func]{medianThree} + + [class]{}-[func]{partitionMedian} + ``` + +=== "Zig" + + ```zig title="quick_sort.zig" + [class]{QuickSortMedian}-[func]{medianThree} + + [class]{QuickSortMedian}-[func]{partition} + ``` + ## 尾递归优化 **在某些输入下,快速排序可能占用空间较多**。以完全倒序的输入数组为例,由于每轮哨兵划分后右子数组长度为 $0$ ,递归树的高度会达到 $n - 1$ ,此时需要占用 $O(n)$ 大小的栈帧空间。 为了防止栈帧空间的累积,我们可以在每轮哨兵排序完成后,比较两个子数组的长度,**仅对较短的子数组进行递归**。由于较短子数组的长度不会超过 $n / 2$ ,因此这种方法能确保递归深度不超过 $\log n$ ,从而将最差空间复杂度优化至 $O(\log n)$ 。 -=== "Java" +=== "Python" - ```java title="quick_sort.java" - [class]{QuickSortTailCall}-[func]{quickSort} + ```python title="quick_sort.py" + [class]{QuickSortTailCall}-[func]{quick_sort} ``` === "C++" @@ -351,10 +351,16 @@ [class]{QuickSortTailCall}-[func]{quickSort} ``` -=== "Python" +=== "Java" - ```python title="quick_sort.py" - [class]{QuickSortTailCall}-[func]{quick_sort} + ```java title="quick_sort.java" + [class]{QuickSortTailCall}-[func]{quickSort} + ``` + +=== "C#" + + ```csharp title="quick_sort.cs" + [class]{QuickSortTailCall}-[func]{quickSort} ``` === "Go" @@ -363,6 +369,12 @@ [class]{quickSortTailCall}-[func]{quickSort} ``` +=== "Swift" + + ```swift title="quick_sort.swift" + [class]{}-[func]{quickSortTailCall} + ``` + === "JS" ```javascript title="quick_sort.js" @@ -375,30 +387,6 @@ [class]{QuickSortTailCall}-[func]{quickSort} ``` -=== "C" - - ```c title="quick_sort.c" - [class]{}-[func]{quickSortTailCall} - ``` - -=== "C#" - - ```csharp title="quick_sort.cs" - [class]{QuickSortTailCall}-[func]{quickSort} - ``` - -=== "Swift" - - ```swift title="quick_sort.swift" - [class]{}-[func]{quickSortTailCall} - ``` - -=== "Zig" - - ```zig title="quick_sort.zig" - [class]{QuickSortTailCall}-[func]{quickSort} - ``` - === "Dart" ```dart title="quick_sort.dart" @@ -410,3 +398,15 @@ ```rust title="quick_sort.rs" [class]{QuickSortTailCall}-[func]{quick_sort} ``` + +=== "C" + + ```c title="quick_sort.c" + [class]{}-[func]{quickSortTailCall} + ``` + +=== "Zig" + + ```zig title="quick_sort.zig" + [class]{QuickSortTailCall}-[func]{quickSort} + ``` diff --git a/docs/chapter_sorting/radix_sort.md b/docs/chapter_sorting/radix_sort.md index f392c74a..a2a9b14a 100644 --- a/docs/chapter_sorting/radix_sort.md +++ b/docs/chapter_sorting/radix_sort.md @@ -24,14 +24,14 @@ $$ 此外,我们需要小幅改动计数排序代码,使之可以根据数字的第 $k$ 位进行排序。 -=== "Java" +=== "Python" - ```java title="radix_sort.java" - [class]{radix_sort}-[func]{digit} + ```python title="radix_sort.py" + [class]{}-[func]{digit} - [class]{radix_sort}-[func]{countingSortDigit} + [class]{}-[func]{counting_sort_digit} - [class]{radix_sort}-[func]{radixSort} + [class]{}-[func]{radix_sort} ``` === "C++" @@ -44,14 +44,24 @@ $$ [class]{}-[func]{radixSort} ``` -=== "Python" +=== "Java" - ```python title="radix_sort.py" - [class]{}-[func]{digit} + ```java title="radix_sort.java" + [class]{radix_sort}-[func]{digit} - [class]{}-[func]{counting_sort_digit} + [class]{radix_sort}-[func]{countingSortDigit} - [class]{}-[func]{radix_sort} + [class]{radix_sort}-[func]{radixSort} + ``` + +=== "C#" + + ```csharp title="radix_sort.cs" + [class]{radix_sort}-[func]{digit} + + [class]{radix_sort}-[func]{countingSortDigit} + + [class]{radix_sort}-[func]{radixSort} ``` === "Go" @@ -64,6 +74,16 @@ $$ [class]{}-[func]{radixSort} ``` +=== "Swift" + + ```swift title="radix_sort.swift" + [class]{}-[func]{digit} + + [class]{}-[func]{countingSortDigit} + + [class]{}-[func]{radixSort} + ``` + === "JS" ```javascript title="radix_sort.js" @@ -84,46 +104,6 @@ $$ [class]{}-[func]{radixSort} ``` -=== "C" - - ```c title="radix_sort.c" - [class]{}-[func]{digit} - - [class]{}-[func]{countingSortDigit} - - [class]{}-[func]{radixSort} - ``` - -=== "C#" - - ```csharp title="radix_sort.cs" - [class]{radix_sort}-[func]{digit} - - [class]{radix_sort}-[func]{countingSortDigit} - - [class]{radix_sort}-[func]{radixSort} - ``` - -=== "Swift" - - ```swift title="radix_sort.swift" - [class]{}-[func]{digit} - - [class]{}-[func]{countingSortDigit} - - [class]{}-[func]{radixSort} - ``` - -=== "Zig" - - ```zig title="radix_sort.zig" - [class]{}-[func]{digit} - - [class]{}-[func]{countingSortDigit} - - [class]{}-[func]{radixSort} - ``` - === "Dart" ```dart title="radix_sort.dart" @@ -144,6 +124,26 @@ $$ [class]{}-[func]{radix_sort} ``` +=== "C" + + ```c title="radix_sort.c" + [class]{}-[func]{digit} + + [class]{}-[func]{countingSortDigit} + + [class]{}-[func]{radixSort} + ``` + +=== "Zig" + + ```zig title="radix_sort.zig" + [class]{}-[func]{digit} + + [class]{}-[func]{countingSortDigit} + + [class]{}-[func]{radixSort} + ``` + !!! question "为什么从最低位开始排序?" 在连续的排序轮次中,后一轮排序会覆盖前一轮排序的结果。举例来说,如果第一轮排序结果 $a < b$ ,而第二轮排序结果 $a > b$ ,那么第二轮的结果将取代第一轮的结果。由于数字的高位优先级高于低位,我们应该先排序低位再排序高位。 diff --git a/docs/chapter_sorting/selection_sort.md b/docs/chapter_sorting/selection_sort.md index 43074e91..c6a8c023 100644 --- a/docs/chapter_sorting/selection_sort.md +++ b/docs/chapter_sorting/selection_sort.md @@ -45,10 +45,10 @@ 在代码中,我们用 $k$ 来记录未排序区间内的最小元素。 -=== "Java" +=== "Python" - ```java title="selection_sort.java" - [class]{selection_sort}-[func]{selectionSort} + ```python title="selection_sort.py" + [class]{}-[func]{selection_sort} ``` === "C++" @@ -57,10 +57,16 @@ [class]{}-[func]{selectionSort} ``` -=== "Python" +=== "Java" - ```python title="selection_sort.py" - [class]{}-[func]{selection_sort} + ```java title="selection_sort.java" + [class]{selection_sort}-[func]{selectionSort} + ``` + +=== "C#" + + ```csharp title="selection_sort.cs" + [class]{selection_sort}-[func]{selectionSort} ``` === "Go" @@ -69,6 +75,12 @@ [class]{}-[func]{selectionSort} ``` +=== "Swift" + + ```swift title="selection_sort.swift" + [class]{}-[func]{selectionSort} + ``` + === "JS" ```javascript title="selection_sort.js" @@ -81,30 +93,6 @@ [class]{}-[func]{selectionSort} ``` -=== "C" - - ```c title="selection_sort.c" - [class]{}-[func]{selectionSort} - ``` - -=== "C#" - - ```csharp title="selection_sort.cs" - [class]{selection_sort}-[func]{selectionSort} - ``` - -=== "Swift" - - ```swift title="selection_sort.swift" - [class]{}-[func]{selectionSort} - ``` - -=== "Zig" - - ```zig title="selection_sort.zig" - [class]{}-[func]{selectionSort} - ``` - === "Dart" ```dart title="selection_sort.dart" @@ -117,6 +105,18 @@ [class]{}-[func]{selection_sort} ``` +=== "C" + + ```c title="selection_sort.c" + [class]{}-[func]{selectionSort} + ``` + +=== "Zig" + + ```zig title="selection_sort.zig" + [class]{}-[func]{selectionSort} + ``` + ## 算法特性 - **时间复杂度为 $O(n^2)$、非自适应排序**:外循环共 $n - 1$ 轮,第一轮的未排序区间长度为 $n$ ,最后一轮的未排序区间长度为 $2$ ,即各轮外循环分别包含 $n$、$n - 1$、$\dots$、$3$、$2$ 轮内循环,求和为 $\frac{(n - 1)(n + 2)}{2}$ 。 diff --git a/docs/chapter_stack_and_queue/deque.md b/docs/chapter_stack_and_queue/deque.md index 7eb70d39..da55dd12 100644 --- a/docs/chapter_stack_and_queue/deque.md +++ b/docs/chapter_stack_and_queue/deque.md @@ -21,32 +21,32 @@ 同样地,我们可以直接使用编程语言中已实现的双向队列类。 -=== "Java" +=== "Python" - ```java title="deque.java" - /* 初始化双向队列 */ - Deque deque = new LinkedList<>(); + ```python title="deque.py" + # 初始化双向队列 + deque: deque[int] = collections.deque() - /* 元素入队 */ - deque.offerLast(2); // 添加至队尾 - deque.offerLast(5); - deque.offerLast(4); - deque.offerFirst(3); // 添加至队首 - deque.offerFirst(1); + # 元素入队 + deque.append(2) # 添加至队尾 + deque.append(5) + deque.append(4) + deque.appendleft(3) # 添加至队首 + deque.appendleft(1) - /* 访问元素 */ - int peekFirst = deque.peekFirst(); // 队首元素 - int peekLast = deque.peekLast(); // 队尾元素 + # 访问元素 + front: int = deque[0] # 队首元素 + rear: int = deque[-1] # 队尾元素 - /* 元素出队 */ - int popFirst = deque.pollFirst(); // 队首元素出队 - int popLast = deque.pollLast(); // 队尾元素出队 + # 元素出队 + pop_front: int = deque.popleft() # 队首元素出队 + pop_rear: int = deque.pop() # 队尾元素出队 - /* 获取双向队列的长度 */ - int size = deque.size(); + # 获取双向队列的长度 + size: int = len(deque) - /* 判断双向队列是否为空 */ - boolean isEmpty = deque.isEmpty(); + # 判断双向队列是否为空 + is_empty: bool = len(deque) == 0 ``` === "C++" @@ -77,32 +77,61 @@ bool empty = deque.empty(); ``` -=== "Python" +=== "Java" - ```python title="deque.py" - # 初始化双向队列 - deque: Deque[int] = collections.deque() + ```java title="deque.java" + /* 初始化双向队列 */ + Deque deque = new LinkedList<>(); - # 元素入队 - deque.append(2) # 添加至队尾 - deque.append(5) - deque.append(4) - deque.appendleft(3) # 添加至队首 - deque.appendleft(1) + /* 元素入队 */ + deque.offerLast(2); // 添加至队尾 + deque.offerLast(5); + deque.offerLast(4); + deque.offerFirst(3); // 添加至队首 + deque.offerFirst(1); - # 访问元素 - front: int = deque[0] # 队首元素 - rear: int = deque[-1] # 队尾元素 + /* 访问元素 */ + int peekFirst = deque.peekFirst(); // 队首元素 + int peekLast = deque.peekLast(); // 队尾元素 - # 元素出队 - pop_front: int = deque.popleft() # 队首元素出队 - pop_rear: int = deque.pop() # 队尾元素出队 + /* 元素出队 */ + int popFirst = deque.pollFirst(); // 队首元素出队 + int popLast = deque.pollLast(); // 队尾元素出队 - # 获取双向队列的长度 - size: int = len(deque) + /* 获取双向队列的长度 */ + int size = deque.size(); - # 判断双向队列是否为空 - is_empty: bool = len(deque) == 0 + /* 判断双向队列是否为空 */ + boolean isEmpty = deque.isEmpty(); + ``` + +=== "C#" + + ```csharp title="deque.cs" + /* 初始化双向队列 */ + // 在 C# 中,将链表 LinkedList 看作双向队列来使用 + LinkedList deque = new LinkedList(); + + /* 元素入队 */ + deque.AddLast(2); // 添加至队尾 + deque.AddLast(5); + deque.AddLast(4); + deque.AddFirst(3); // 添加至队首 + deque.AddFirst(1); + + /* 访问元素 */ + int peekFirst = deque.First.Value; // 队首元素 + int peekLast = deque.Last.Value; // 队尾元素 + + /* 元素出队 */ + deque.RemoveFirst(); // 队首元素出队 + deque.RemoveLast(); // 队尾元素出队 + + /* 获取双向队列的长度 */ + int size = deque.Count; + + /* 判断双向队列是否为空 */ + bool isEmpty = deque.Count == 0; ``` === "Go" @@ -134,6 +163,36 @@ isEmpty := deque.Len() == 0 ``` +=== "Swift" + + ```swift title="deque.swift" + /* 初始化双向队列 */ + // Swift 没有内置的双向队列类,可以把 Array 当作双向队列来使用 + var deque: [Int] = [] + + /* 元素入队 */ + deque.append(2) // 添加至队尾 + deque.append(5) + deque.append(4) + deque.insert(3, at: 0) // 添加至队首 + deque.insert(1, at: 0) + + /* 访问元素 */ + let peekFirst = deque.first! // 队首元素 + let peekLast = deque.last! // 队尾元素 + + /* 元素出队 */ + // 使用 Array 模拟时 popFirst 的复杂度为 O(n) + let popFirst = deque.removeFirst() // 队首元素出队 + let popLast = deque.removeLast() // 队尾元素出队 + + /* 获取双向队列的长度 */ + let size = deque.count + + /* 判断双向队列是否为空 */ + let isEmpty = deque.isEmpty + ``` + === "JS" ```javascript title="deque.js" @@ -210,77 +269,6 @@ console.log("双向队列是否为空 = " + isEmpty); ``` -=== "C" - - ```c title="deque.c" - // C 未提供内置双向队列 - ``` - -=== "C#" - - ```csharp title="deque.cs" - /* 初始化双向队列 */ - // 在 C# 中,将链表 LinkedList 看作双向队列来使用 - LinkedList deque = new LinkedList(); - - /* 元素入队 */ - deque.AddLast(2); // 添加至队尾 - deque.AddLast(5); - deque.AddLast(4); - deque.AddFirst(3); // 添加至队首 - deque.AddFirst(1); - - /* 访问元素 */ - int peekFirst = deque.First.Value; // 队首元素 - int peekLast = deque.Last.Value; // 队尾元素 - - /* 元素出队 */ - deque.RemoveFirst(); // 队首元素出队 - deque.RemoveLast(); // 队尾元素出队 - - /* 获取双向队列的长度 */ - int size = deque.Count; - - /* 判断双向队列是否为空 */ - bool isEmpty = deque.Count == 0; - ``` - -=== "Swift" - - ```swift title="deque.swift" - /* 初始化双向队列 */ - // Swift 没有内置的双向队列类,可以把 Array 当作双向队列来使用 - var deque: [Int] = [] - - /* 元素入队 */ - deque.append(2) // 添加至队尾 - deque.append(5) - deque.append(4) - deque.insert(3, at: 0) // 添加至队首 - deque.insert(1, at: 0) - - /* 访问元素 */ - let peekFirst = deque.first! // 队首元素 - let peekLast = deque.last! // 队尾元素 - - /* 元素出队 */ - // 使用 Array 模拟时 popFirst 的复杂度为 O(n) - let popFirst = deque.removeFirst() // 队首元素出队 - let popLast = deque.removeLast() // 队尾元素出队 - - /* 获取双向队列的长度 */ - let size = deque.count - - /* 判断双向队列是否为空 */ - let isEmpty = deque.isEmpty - ``` - -=== "Zig" - - ```zig title="deque.zig" - - ``` - === "Dart" ```dart title="deque.dart" @@ -316,6 +304,18 @@ ``` +=== "C" + + ```c title="deque.c" + // C 未提供内置双向队列 + ``` + +=== "Zig" + + ```zig title="deque.zig" + + ``` + ## 双向队列实现 * 双向队列的实现与队列类似,可以选择链表或数组作为底层数据结构。 @@ -345,9 +345,9 @@ 实现代码如下所示。 -=== "Java" +=== "Python" - ```java title="linkedlist_deque.java" + ```python title="linkedlist_deque.py" [class]{ListNode}-[func]{} [class]{LinkedListDeque}-[func]{} @@ -361,9 +361,17 @@ [class]{LinkedListDeque}-[func]{} ``` -=== "Python" +=== "Java" - ```python title="linkedlist_deque.py" + ```java title="linkedlist_deque.java" + [class]{ListNode}-[func]{} + + [class]{LinkedListDeque}-[func]{} + ``` + +=== "C#" + + ```csharp title="linkedlist_deque.cs" [class]{ListNode}-[func]{} [class]{LinkedListDeque}-[func]{} @@ -375,6 +383,14 @@ [class]{linkedListDeque}-[func]{} ``` +=== "Swift" + + ```swift title="linkedlist_deque.swift" + [class]{ListNode}-[func]{} + + [class]{LinkedListDeque}-[func]{} + ``` + === "JS" ```javascript title="linkedlist_deque.js" @@ -391,38 +407,6 @@ [class]{LinkedListDeque}-[func]{} ``` -=== "C" - - ```c title="linkedlist_deque.c" - [class]{doublyListNode}-[func]{} - - [class]{linkedListDeque}-[func]{} - ``` - -=== "C#" - - ```csharp title="linkedlist_deque.cs" - [class]{ListNode}-[func]{} - - [class]{LinkedListDeque}-[func]{} - ``` - -=== "Swift" - - ```swift title="linkedlist_deque.swift" - [class]{ListNode}-[func]{} - - [class]{LinkedListDeque}-[func]{} - ``` - -=== "Zig" - - ```zig title="linkedlist_deque.zig" - [class]{ListNode}-[func]{} - - [class]{LinkedListDeque}-[func]{} - ``` - === "Dart" ```dart title="linkedlist_deque.dart" @@ -439,6 +423,22 @@ [class]{LinkedListDeque}-[func]{} ``` +=== "C" + + ```c title="linkedlist_deque.c" + [class]{doublyListNode}-[func]{} + + [class]{linkedListDeque}-[func]{} + ``` + +=== "Zig" + + ```zig title="linkedlist_deque.zig" + [class]{ListNode}-[func]{} + + [class]{LinkedListDeque}-[func]{} + ``` + ### 基于数组的实现 如下图所示,与基于数组实现队列类似,我们也可以使用环形数组来实现双向队列。 @@ -460,9 +460,9 @@ 在队列的实现基础上,仅需增加“队首入队”和“队尾出队”的方法。 -=== "Java" +=== "Python" - ```java title="array_deque.java" + ```python title="array_deque.py" [class]{ArrayDeque}-[func]{} ``` @@ -472,9 +472,15 @@ [class]{ArrayDeque}-[func]{} ``` -=== "Python" +=== "Java" - ```python title="array_deque.py" + ```java title="array_deque.java" + [class]{ArrayDeque}-[func]{} + ``` + +=== "C#" + + ```csharp title="array_deque.cs" [class]{ArrayDeque}-[func]{} ``` @@ -484,6 +490,12 @@ [class]{arrayDeque}-[func]{} ``` +=== "Swift" + + ```swift title="array_deque.swift" + [class]{ArrayDeque}-[func]{} + ``` + === "JS" ```javascript title="array_deque.js" @@ -496,30 +508,6 @@ [class]{ArrayDeque}-[func]{} ``` -=== "C" - - ```c title="array_deque.c" - [class]{arrayDeque}-[func]{} - ``` - -=== "C#" - - ```csharp title="array_deque.cs" - [class]{ArrayDeque}-[func]{} - ``` - -=== "Swift" - - ```swift title="array_deque.swift" - [class]{ArrayDeque}-[func]{} - ``` - -=== "Zig" - - ```zig title="array_deque.zig" - [class]{ArrayDeque}-[func]{} - ``` - === "Dart" ```dart title="array_deque.dart" @@ -532,6 +520,18 @@ [class]{ArrayDeque}-[func]{} ``` +=== "C" + + ```c title="array_deque.c" + [class]{arrayDeque}-[func]{} + ``` + +=== "Zig" + + ```zig title="array_deque.zig" + [class]{ArrayDeque}-[func]{} + ``` + ## 双向队列应用 双向队列兼具栈与队列的逻辑,**因此它可以实现这两者的所有应用场景,同时提供更高的自由度**。 diff --git a/docs/chapter_stack_and_queue/queue.md b/docs/chapter_stack_and_queue/queue.md index 72356673..17e5f3a7 100755 --- a/docs/chapter_stack_and_queue/queue.md +++ b/docs/chapter_stack_and_queue/queue.md @@ -20,30 +20,32 @@ 我们可以直接使用编程语言中现成的队列类。 -=== "Java" +=== "Python" - ```java title="queue.java" - /* 初始化队列 */ - Queue queue = new LinkedList<>(); + ```python title="queue.py" + # 初始化队列 + # 在 Python 中,我们一般将双向队列类 deque 看作队列使用 + # 虽然 queue.Queue() 是纯正的队列类,但不太好用,因此不建议 + que: deque[int] = collections.deque() - /* 元素入队 */ - queue.offer(1); - queue.offer(3); - queue.offer(2); - queue.offer(5); - queue.offer(4); + # 元素入队 + que.append(1) + que.append(3) + que.append(2) + que.append(5) + que.append(4) - /* 访问队首元素 */ - int peek = queue.peek(); + # 访问队首元素 + front: int = que[0]; - /* 元素出队 */ - int pop = queue.poll(); + # 元素出队 + pop: int = que.popleft() - /* 获取队列的长度 */ - int size = queue.size(); + # 获取队列的长度 + size: int = len(que) - /* 判断队列是否为空 */ - boolean isEmpty = queue.isEmpty(); + # 判断队列是否为空 + is_empty: bool = len(que) == 0 ``` === "C++" @@ -72,32 +74,56 @@ bool empty = queue.empty(); ``` -=== "Python" +=== "Java" - ```python title="queue.py" - # 初始化队列 - # 在 Python 中,我们一般将双向队列类 deque 看作队列使用 - # 虽然 queue.Queue() 是纯正的队列类,但不太好用,因此不建议 - que: Deque[int] = collections.deque() + ```java title="queue.java" + /* 初始化队列 */ + Queue queue = new LinkedList<>(); - # 元素入队 - que.append(1) - que.append(3) - que.append(2) - que.append(5) - que.append(4) + /* 元素入队 */ + queue.offer(1); + queue.offer(3); + queue.offer(2); + queue.offer(5); + queue.offer(4); - # 访问队首元素 - front: int = que[0]; + /* 访问队首元素 */ + int peek = queue.peek(); - # 元素出队 - pop: int = que.popleft() + /* 元素出队 */ + int pop = queue.poll(); - # 获取队列的长度 - size: int = len(que) + /* 获取队列的长度 */ + int size = queue.size(); - # 判断队列是否为空 - is_empty: bool = len(que) == 0 + /* 判断队列是否为空 */ + boolean isEmpty = queue.isEmpty(); + ``` + +=== "C#" + + ```csharp title="queue.cs" + /* 初始化队列 */ + Queue queue = new(); + + /* 元素入队 */ + queue.Enqueue(1); + queue.Enqueue(3); + queue.Enqueue(2); + queue.Enqueue(5); + queue.Enqueue(4); + + /* 访问队首元素 */ + int peek = queue.Peek(); + + /* 元素出队 */ + int pop = queue.Dequeue(); + + /* 获取队列的长度 */ + int size = queue.Count; + + /* 判断队列是否为空 */ + bool isEmpty = queue.Count == 0; ``` === "Go" @@ -128,6 +154,34 @@ isEmpty := queue.Len() == 0 ``` +=== "Swift" + + ```swift title="queue.swift" + /* 初始化队列 */ + // Swift 没有内置的队列类,可以把 Array 当作队列来使用 + var queue: [Int] = [] + + /* 元素入队 */ + queue.append(1) + queue.append(3) + queue.append(2) + queue.append(5) + queue.append(4) + + /* 访问队首元素 */ + let peek = queue.first! + + /* 元素出队 */ + // 由于是数组,因此 removeFirst 的复杂度为 O(n) + let pool = queue.removeFirst() + + /* 获取队列的长度 */ + let size = queue.count + + /* 判断队列是否为空 */ + let isEmpty = queue.isEmpty + ``` + === "JS" ```javascript title="queue.js" @@ -184,72 +238,6 @@ const empty = queue.length === 0; ``` -=== "C" - - ```c title="queue.c" - // C 未提供内置队列 - ``` - -=== "C#" - - ```csharp title="queue.cs" - /* 初始化队列 */ - Queue queue = new(); - - /* 元素入队 */ - queue.Enqueue(1); - queue.Enqueue(3); - queue.Enqueue(2); - queue.Enqueue(5); - queue.Enqueue(4); - - /* 访问队首元素 */ - int peek = queue.Peek(); - - /* 元素出队 */ - int pop = queue.Dequeue(); - - /* 获取队列的长度 */ - int size = queue.Count; - - /* 判断队列是否为空 */ - bool isEmpty = queue.Count == 0; - ``` - -=== "Swift" - - ```swift title="queue.swift" - /* 初始化队列 */ - // Swift 没有内置的队列类,可以把 Array 当作队列来使用 - var queue: [Int] = [] - - /* 元素入队 */ - queue.append(1) - queue.append(3) - queue.append(2) - queue.append(5) - queue.append(4) - - /* 访问队首元素 */ - let peek = queue.first! - - /* 元素出队 */ - // 由于是数组,因此 removeFirst 的复杂度为 O(n) - let pool = queue.removeFirst() - - /* 获取队列的长度 */ - let size = queue.count - - /* 判断队列是否为空 */ - let isEmpty = queue.isEmpty - ``` - -=== "Zig" - - ```zig title="queue.zig" - - ``` - === "Dart" ```dart title="queue.dart" @@ -283,6 +271,18 @@ ``` +=== "C" + + ```c title="queue.c" + // C 未提供内置队列 + ``` + +=== "Zig" + + ```zig title="queue.zig" + + ``` + ## 队列实现 为了实现队列,我们需要一种数据结构,可以在一端添加元素,并在另一端删除元素。因此,链表和数组都可以用来实现队列。 @@ -302,9 +302,9 @@ 以下是用链表实现队列的代码。 -=== "Java" +=== "Python" - ```java title="linkedlist_queue.java" + ```python title="linkedlist_queue.py" [class]{LinkedListQueue}-[func]{} ``` @@ -314,9 +314,15 @@ [class]{LinkedListQueue}-[func]{} ``` -=== "Python" +=== "Java" - ```python title="linkedlist_queue.py" + ```java title="linkedlist_queue.java" + [class]{LinkedListQueue}-[func]{} + ``` + +=== "C#" + + ```csharp title="linkedlist_queue.cs" [class]{LinkedListQueue}-[func]{} ``` @@ -326,6 +332,12 @@ [class]{linkedListQueue}-[func]{} ``` +=== "Swift" + + ```swift title="linkedlist_queue.swift" + [class]{LinkedListQueue}-[func]{} + ``` + === "JS" ```javascript title="linkedlist_queue.js" @@ -338,30 +350,6 @@ [class]{LinkedListQueue}-[func]{} ``` -=== "C" - - ```c title="linkedlist_queue.c" - [class]{linkedListQueue}-[func]{} - ``` - -=== "C#" - - ```csharp title="linkedlist_queue.cs" - [class]{LinkedListQueue}-[func]{} - ``` - -=== "Swift" - - ```swift title="linkedlist_queue.swift" - [class]{LinkedListQueue}-[func]{} - ``` - -=== "Zig" - - ```zig title="linkedlist_queue.zig" - [class]{LinkedListQueue}-[func]{} - ``` - === "Dart" ```dart title="linkedlist_queue.dart" @@ -374,6 +362,18 @@ [class]{LinkedListQueue}-[func]{} ``` +=== "C" + + ```c title="linkedlist_queue.c" + [class]{linkedListQueue}-[func]{} + ``` + +=== "Zig" + + ```zig title="linkedlist_queue.zig" + [class]{LinkedListQueue}-[func]{} + ``` + ### 基于数组的实现 由于数组删除首元素的时间复杂度为 $O(n)$ ,这会导致出队操作效率较低。然而,我们可以采用以下巧妙方法来避免这个问题。 @@ -400,9 +400,9 @@ 对于环形数组,我们需要让 `front` 或 `rear` 在越过数组尾部时,直接回到数组头部继续遍历。这种周期性规律可以通过“取余操作”来实现,代码如下所示。 -=== "Java" +=== "Python" - ```java title="array_queue.java" + ```python title="array_queue.py" [class]{ArrayQueue}-[func]{} ``` @@ -412,9 +412,15 @@ [class]{ArrayQueue}-[func]{} ``` -=== "Python" +=== "Java" - ```python title="array_queue.py" + ```java title="array_queue.java" + [class]{ArrayQueue}-[func]{} + ``` + +=== "C#" + + ```csharp title="array_queue.cs" [class]{ArrayQueue}-[func]{} ``` @@ -424,6 +430,12 @@ [class]{arrayQueue}-[func]{} ``` +=== "Swift" + + ```swift title="array_queue.swift" + [class]{ArrayQueue}-[func]{} + ``` + === "JS" ```javascript title="array_queue.js" @@ -436,30 +448,6 @@ [class]{ArrayQueue}-[func]{} ``` -=== "C" - - ```c title="array_queue.c" - [class]{arrayQueue}-[func]{} - ``` - -=== "C#" - - ```csharp title="array_queue.cs" - [class]{ArrayQueue}-[func]{} - ``` - -=== "Swift" - - ```swift title="array_queue.swift" - [class]{ArrayQueue}-[func]{} - ``` - -=== "Zig" - - ```zig title="array_queue.zig" - [class]{ArrayQueue}-[func]{} - ``` - === "Dart" ```dart title="array_queue.dart" @@ -472,6 +460,18 @@ [class]{ArrayQueue}-[func]{} ``` +=== "C" + + ```c title="array_queue.c" + [class]{arrayQueue}-[func]{} + ``` + +=== "Zig" + + ```zig title="array_queue.zig" + [class]{ArrayQueue}-[func]{} + ``` + 以上实现的队列仍然具有局限性,即其长度不可变。然而,这个问题不难解决,我们可以将数组替换为动态数组,从而引入扩容机制。有兴趣的同学可以尝试自行实现。 两种实现的对比结论与栈一致,在此不再赘述。 diff --git a/docs/chapter_stack_and_queue/stack.md b/docs/chapter_stack_and_queue/stack.md index 287eda6a..e4bd884c 100755 --- a/docs/chapter_stack_and_queue/stack.md +++ b/docs/chapter_stack_and_queue/stack.md @@ -22,58 +22,6 @@ 通常情况下,我们可以直接使用编程语言内置的栈类。然而,某些语言可能没有专门提供栈类,这时我们可以将该语言的“数组”或“链表”视作栈来使用,并在程序逻辑上忽略与栈无关的操作。 -=== "Java" - - ```java title="stack.java" - /* 初始化栈 */ - Stack stack = new Stack<>(); - - /* 元素入栈 */ - stack.push(1); - stack.push(3); - stack.push(2); - stack.push(5); - stack.push(4); - - /* 访问栈顶元素 */ - int peek = stack.peek(); - - /* 元素出栈 */ - int pop = stack.pop(); - - /* 获取栈的长度 */ - int size = stack.size(); - - /* 判断是否为空 */ - boolean isEmpty = stack.isEmpty(); - ``` - -=== "C++" - - ```cpp title="stack.cpp" - /* 初始化栈 */ - stack stack; - - /* 元素入栈 */ - stack.push(1); - stack.push(3); - stack.push(2); - stack.push(5); - stack.push(4); - - /* 访问栈顶元素 */ - int top = stack.top(); - - /* 元素出栈 */ - stack.pop(); // 无返回值 - - /* 获取栈的长度 */ - int size = stack.size(); - - /* 判断是否为空 */ - bool empty = stack.empty(); - ``` - === "Python" ```python title="stack.py" @@ -101,6 +49,84 @@ is_empty: bool = len(stack) == 0 ``` +=== "C++" + + ```cpp title="stack.cpp" + /* 初始化栈 */ + stack stack; + + /* 元素入栈 */ + stack.push(1); + stack.push(3); + stack.push(2); + stack.push(5); + stack.push(4); + + /* 访问栈顶元素 */ + int top = stack.top(); + + /* 元素出栈 */ + stack.pop(); // 无返回值 + + /* 获取栈的长度 */ + int size = stack.size(); + + /* 判断是否为空 */ + bool empty = stack.empty(); + ``` + +=== "Java" + + ```java title="stack.java" + /* 初始化栈 */ + Stack stack = new Stack<>(); + + /* 元素入栈 */ + stack.push(1); + stack.push(3); + stack.push(2); + stack.push(5); + stack.push(4); + + /* 访问栈顶元素 */ + int peek = stack.peek(); + + /* 元素出栈 */ + int pop = stack.pop(); + + /* 获取栈的长度 */ + int size = stack.size(); + + /* 判断是否为空 */ + boolean isEmpty = stack.isEmpty(); + ``` + +=== "C#" + + ```csharp title="stack.cs" + /* 初始化栈 */ + Stack stack = new (); + + /* 元素入栈 */ + stack.Push(1); + stack.Push(3); + stack.Push(2); + stack.Push(5); + stack.Push(4); + + /* 访问栈顶元素 */ + int peek = stack.Peek(); + + /* 元素出栈 */ + int pop = stack.Pop(); + + /* 获取栈的长度 */ + int size = stack.Count; + + /* 判断是否为空 */ + bool isEmpty = stack.Count == 0; + ``` + === "Go" ```go title="stack_test.go" @@ -129,6 +155,33 @@ isEmpty := len(stack) == 0 ``` +=== "Swift" + + ```swift title="stack.swift" + /* 初始化栈 */ + // Swift 没有内置的栈类,可以把 Array 当作栈来使用 + var stack: [Int] = [] + + /* 元素入栈 */ + stack.append(1) + stack.append(3) + stack.append(2) + stack.append(5) + stack.append(4) + + /* 访问栈顶元素 */ + let peek = stack.last! + + /* 元素出栈 */ + let pop = stack.removeLast() + + /* 获取栈的长度 */ + let size = stack.count + + /* 判断是否为空 */ + let isEmpty = stack.isEmpty + ``` + === "JS" ```javascript title="stack.js" @@ -183,71 +236,6 @@ const is_empty = stack.length === 0; ``` -=== "C" - - ```c title="stack.c" - // C 未提供内置栈 - ``` - -=== "C#" - - ```csharp title="stack.cs" - /* 初始化栈 */ - Stack stack = new (); - - /* 元素入栈 */ - stack.Push(1); - stack.Push(3); - stack.Push(2); - stack.Push(5); - stack.Push(4); - - /* 访问栈顶元素 */ - int peek = stack.Peek(); - - /* 元素出栈 */ - int pop = stack.Pop(); - - /* 获取栈的长度 */ - int size = stack.Count; - - /* 判断是否为空 */ - bool isEmpty = stack.Count == 0; - ``` - -=== "Swift" - - ```swift title="stack.swift" - /* 初始化栈 */ - // Swift 没有内置的栈类,可以把 Array 当作栈来使用 - var stack: [Int] = [] - - /* 元素入栈 */ - stack.append(1) - stack.append(3) - stack.append(2) - stack.append(5) - stack.append(4) - - /* 访问栈顶元素 */ - let peek = stack.last! - - /* 元素出栈 */ - let pop = stack.removeLast() - - /* 获取栈的长度 */ - let size = stack.count - - /* 判断是否为空 */ - let isEmpty = stack.isEmpty - ``` - -=== "Zig" - - ```zig title="stack.zig" - - ``` - === "Dart" ```dart title="stack.dart" @@ -281,6 +269,18 @@ ``` +=== "C" + + ```c title="stack.c" + // C 未提供内置栈 + ``` + +=== "Zig" + + ```zig title="stack.zig" + + ``` + ## 栈的实现 为了深入了解栈的运行机制,我们来尝试自己实现一个栈类。 @@ -304,9 +304,9 @@ 以下是基于链表实现栈的示例代码。 -=== "Java" +=== "Python" - ```java title="linkedlist_stack.java" + ```python title="linkedlist_stack.py" [class]{LinkedListStack}-[func]{} ``` @@ -316,9 +316,15 @@ [class]{LinkedListStack}-[func]{} ``` -=== "Python" +=== "Java" - ```python title="linkedlist_stack.py" + ```java title="linkedlist_stack.java" + [class]{LinkedListStack}-[func]{} + ``` + +=== "C#" + + ```csharp title="linkedlist_stack.cs" [class]{LinkedListStack}-[func]{} ``` @@ -328,6 +334,12 @@ [class]{linkedListStack}-[func]{} ``` +=== "Swift" + + ```swift title="linkedlist_stack.swift" + [class]{LinkedListStack}-[func]{} + ``` + === "JS" ```javascript title="linkedlist_stack.js" @@ -340,30 +352,6 @@ [class]{LinkedListStack}-[func]{} ``` -=== "C" - - ```c title="linkedlist_stack.c" - [class]{linkedListStack}-[func]{} - ``` - -=== "C#" - - ```csharp title="linkedlist_stack.cs" - [class]{LinkedListStack}-[func]{} - ``` - -=== "Swift" - - ```swift title="linkedlist_stack.swift" - [class]{LinkedListStack}-[func]{} - ``` - -=== "Zig" - - ```zig title="linkedlist_stack.zig" - [class]{LinkedListStack}-[func]{} - ``` - === "Dart" ```dart title="linkedlist_stack.dart" @@ -376,6 +364,18 @@ [class]{LinkedListStack}-[func]{} ``` +=== "C" + + ```c title="linkedlist_stack.c" + [class]{linkedListStack}-[func]{} + ``` + +=== "Zig" + + ```zig title="linkedlist_stack.zig" + [class]{LinkedListStack}-[func]{} + ``` + ### 基于数组的实现 使用数组实现栈时,我们可以将数组的尾部作为栈顶。如下图所示,入栈与出栈操作分别对应在数组尾部添加元素与删除元素,时间复杂度都为 $O(1)$ 。 @@ -391,9 +391,9 @@ 由于入栈的元素可能会源源不断地增加,因此我们可以使用动态数组,这样就无须自行处理数组扩容问题。以下为示例代码。 -=== "Java" +=== "Python" - ```java title="array_stack.java" + ```python title="array_stack.py" [class]{ArrayStack}-[func]{} ``` @@ -403,9 +403,15 @@ [class]{ArrayStack}-[func]{} ``` -=== "Python" +=== "Java" - ```python title="array_stack.py" + ```java title="array_stack.java" + [class]{ArrayStack}-[func]{} + ``` + +=== "C#" + + ```csharp title="array_stack.cs" [class]{ArrayStack}-[func]{} ``` @@ -415,6 +421,12 @@ [class]{arrayStack}-[func]{} ``` +=== "Swift" + + ```swift title="array_stack.swift" + [class]{ArrayStack}-[func]{} + ``` + === "JS" ```javascript title="array_stack.js" @@ -427,30 +439,6 @@ [class]{ArrayStack}-[func]{} ``` -=== "C" - - ```c title="array_stack.c" - [class]{arrayStack}-[func]{} - ``` - -=== "C#" - - ```csharp title="array_stack.cs" - [class]{ArrayStack}-[func]{} - ``` - -=== "Swift" - - ```swift title="array_stack.swift" - [class]{ArrayStack}-[func]{} - ``` - -=== "Zig" - - ```zig title="array_stack.zig" - [class]{ArrayStack}-[func]{} - ``` - === "Dart" ```dart title="array_stack.dart" @@ -463,6 +451,18 @@ [class]{ArrayStack}-[func]{} ``` +=== "C" + + ```c title="array_stack.c" + [class]{arrayStack}-[func]{} + ``` + +=== "Zig" + + ```zig title="array_stack.zig" + [class]{ArrayStack}-[func]{} + ``` + ## 两种实现对比 **支持操作** diff --git a/docs/chapter_tree/array_representation_of_tree.md b/docs/chapter_tree/array_representation_of_tree.md index 3dca2d7b..df4086e1 100644 --- a/docs/chapter_tree/array_representation_of_tree.md +++ b/docs/chapter_tree/array_representation_of_tree.md @@ -24,12 +24,12 @@ 为了解决此问题,**我们可以考虑在层序遍历序列中显式地写出所有 $\text{None}$** 。如下图所示,这样处理后,层序遍历序列就可以唯一表示二叉树了。 -=== "Java" +=== "Python" - ```java title="" - /* 二叉树的数组表示 */ - // 使用 int 的包装类 Integer ,就可以使用 null 来标记空位 - Integer[] tree = { 1, 2, 3, 4, null, 6, 7, 8, 9, null, null, 12, null, null, 15 }; + ```python title="" + # 二叉树的数组表示 + # 使用 None 来表示空位 + tree = [1, 2, 3, 4, None, 6, 7, 8, 9, None, None, 12, None, None, 15] ``` === "C++" @@ -40,12 +40,20 @@ vector tree = {1, 2, 3, 4, INT_MAX, 6, 7, 8, 9, INT_MAX, INT_MAX, 12, INT_MAX, INT_MAX, 15}; ``` -=== "Python" +=== "Java" - ```python title="" - # 二叉树的数组表示 - # 使用 None 来表示空位 - tree = [1, 2, 3, 4, None, 6, 7, 8, 9, None, None, 12, None, None, 15] + ```java title="" + /* 二叉树的数组表示 */ + // 使用 int 的包装类 Integer ,就可以使用 null 来标记空位 + Integer[] tree = { 1, 2, 3, 4, null, 6, 7, 8, 9, null, null, 12, null, null, 15 }; + ``` + +=== "C#" + + ```csharp title="" + /* 二叉树的数组表示 */ + // 使用 int? 可空类型 ,就可以使用 null 来标记空位 + int?[] tree = { 1, 2, 3, 4, null, 6, 7, 8, 9, null, null, 12, null, null, 15 }; ``` === "Go" @@ -56,6 +64,14 @@ tree := []any{1, 2, 3, 4, nil, 6, 7, 8, 9, nil, nil, 12, nil, nil, 15} ``` +=== "Swift" + + ```swift title="" + /* 二叉树的数组表示 */ + // 使用 Int? 可空类型 ,就可以使用 nil 来标记空位 + let tree: [Int?] = [1, 2, 3, 4, nil, 6, 7, 8, 9, nil, nil, 12, nil, nil, 15] + ``` + === "JS" ```javascript title="" @@ -72,36 +88,6 @@ let tree: (number | null)[] = [1, 2, 3, 4, null, 6, 7, 8, 9, null, null, 12, null, null, 15]; ``` -=== "C" - - ```c title="" - /* 二叉树的数组表示 */ - // 使用 int 最大值标记空位,因此要求节点值不能为 INT_MAX - int tree[] = {1, 2, 3, 4, INT_MAX, 6, 7, 8, 9, INT_MAX, INT_MAX, 12, INT_MAX, INT_MAX, 15}; - ``` - -=== "C#" - - ```csharp title="" - /* 二叉树的数组表示 */ - // 使用 int? 可空类型 ,就可以使用 null 来标记空位 - int?[] tree = { 1, 2, 3, 4, null, 6, 7, 8, 9, null, null, 12, null, null, 15 }; - ``` - -=== "Swift" - - ```swift title="" - /* 二叉树的数组表示 */ - // 使用 Int? 可空类型 ,就可以使用 nil 来标记空位 - let tree: [Int?] = [1, 2, 3, 4, nil, 6, 7, 8, 9, nil, nil, 12, nil, nil, 15] - ``` - -=== "Zig" - - ```zig title="" - - ``` - === "Dart" ```dart title="" @@ -116,6 +102,20 @@ ``` +=== "C" + + ```c title="" + /* 二叉树的数组表示 */ + // 使用 int 最大值标记空位,因此要求节点值不能为 INT_MAX + int tree[] = {1, 2, 3, 4, INT_MAX, 6, 7, 8, 9, INT_MAX, INT_MAX, 12, INT_MAX, INT_MAX, 15}; + ``` + +=== "Zig" + + ```zig title="" + + ``` + ![任意类型二叉树的数组表示](array_representation_of_tree.assets/array_representation_with_empty.png) 值得说明的是,**完全二叉树非常适合使用数组来表示**。回顾完全二叉树的定义,$\text{None}$ 只出现在最底层且靠右的位置,**因此所有 $\text{None}$ 一定出现在层序遍历序列的末尾**。 @@ -129,9 +129,9 @@ - 给定某节点,获取它的值、左(右)子节点、父节点。 - 获取前序遍历、中序遍历、后序遍历、层序遍历序列。 -=== "Java" +=== "Python" - ```java title="array_binary_tree.java" + ```python title="array_binary_tree.py" [class]{ArrayBinaryTree}-[func]{} ``` @@ -141,9 +141,15 @@ [class]{ArrayBinaryTree}-[func]{} ``` -=== "Python" +=== "Java" - ```python title="array_binary_tree.py" + ```java title="array_binary_tree.java" + [class]{ArrayBinaryTree}-[func]{} + ``` + +=== "C#" + + ```csharp title="array_binary_tree.cs" [class]{ArrayBinaryTree}-[func]{} ``` @@ -153,6 +159,12 @@ [class]{arrayBinaryTree}-[func]{} ``` +=== "Swift" + + ```swift title="array_binary_tree.swift" + [class]{ArrayBinaryTree}-[func]{} + ``` + === "JS" ```javascript title="array_binary_tree.js" @@ -165,30 +177,6 @@ [class]{ArrayBinaryTree}-[func]{} ``` -=== "C" - - ```c title="array_binary_tree.c" - [class]{arrayBinaryTree}-[func]{} - ``` - -=== "C#" - - ```csharp title="array_binary_tree.cs" - [class]{ArrayBinaryTree}-[func]{} - ``` - -=== "Swift" - - ```swift title="array_binary_tree.swift" - [class]{ArrayBinaryTree}-[func]{} - ``` - -=== "Zig" - - ```zig title="array_binary_tree.zig" - [class]{ArrayBinaryTree}-[func]{} - ``` - === "Dart" ```dart title="array_binary_tree.dart" @@ -201,6 +189,18 @@ [class]{ArrayBinaryTree}-[func]{} ``` +=== "C" + + ```c title="array_binary_tree.c" + [class]{arrayBinaryTree}-[func]{} + ``` + +=== "Zig" + + ```zig title="array_binary_tree.zig" + [class]{ArrayBinaryTree}-[func]{} + ``` + ## 优势与局限性 二叉树的数组表示主要有以下优点。 diff --git a/docs/chapter_tree/avl_tree.md b/docs/chapter_tree/avl_tree.md index fb016575..903658b8 100644 --- a/docs/chapter_tree/avl_tree.md +++ b/docs/chapter_tree/avl_tree.md @@ -20,17 +20,16 @@ AVL 树既是二叉搜索树也是平衡二叉树,同时满足这两类二叉 由于 AVL 树的相关操作需要获取节点高度,因此我们需要为节点类添加 `height` 变量。 -=== "Java" +=== "Python" - ```java title="" - /* AVL 树节点类 */ - class TreeNode { - public int val; // 节点值 - public int height; // 节点高度 - public TreeNode left; // 左子节点 - public TreeNode right; // 右子节点 - public TreeNode(int x) { val = x; } - } + ```python title="" + class TreeNode: + """AVL 树节点类""" + def __init__(self, val: int): + self.val: int = val # 节点值 + self.height: int = 0 # 节点高度 + self.left: Optional[TreeNode] = None # 左子节点引用 + self.right: Optional[TreeNode] = None # 右子节点引用 ``` === "C++" @@ -47,16 +46,30 @@ AVL 树既是二叉搜索树也是平衡二叉树,同时满足这两类二叉 }; ``` -=== "Python" +=== "Java" - ```python title="" - class TreeNode: - """AVL 树节点类""" - def __init__(self, val: int): - self.val: int = val # 节点值 - self.height: int = 0 # 节点高度 - self.left: Optional[TreeNode] = None # 左子节点引用 - self.right: Optional[TreeNode] = None # 右子节点引用 + ```java title="" + /* AVL 树节点类 */ + class TreeNode { + public int val; // 节点值 + public int height; // 节点高度 + public TreeNode left; // 左子节点 + public TreeNode right; // 右子节点 + public TreeNode(int x) { val = x; } + } + ``` + +=== "C#" + + ```csharp title="" + /* AVL 树节点类 */ + class TreeNode { + public int val; // 节点值 + public int height; // 节点高度 + public TreeNode? left; // 左子节点 + public TreeNode? right; // 右子节点 + public TreeNode(int x) { val = x; } + } ``` === "Go" @@ -71,6 +84,23 @@ AVL 树既是二叉搜索树也是平衡二叉树,同时满足这两类二叉 } ``` +=== "Swift" + + ```swift title="" + /* AVL 树节点类 */ + class TreeNode { + var val: Int // 节点值 + var height: Int // 节点高度 + var left: TreeNode? // 左子节点 + var right: TreeNode? // 右子节点 + + init(x: Int) { + val = x + height = 0 + } + } + ``` + === "JS" ```javascript title="" @@ -107,6 +137,25 @@ AVL 树既是二叉搜索树也是平衡二叉树,同时满足这两类二叉 } ``` +=== "Dart" + + ```dart title="" + /* AVL 树节点类 */ + class TreeNode { + int val; // 节点值 + int height; // 节点高度 + TreeNode? left; // 左子节点 + TreeNode? right; // 右子节点 + TreeNode(this.val, [this.height = 0, this.left, this.right]); + } + ``` + +=== "Rust" + + ```rust title="" + + ``` + === "C" ```c title="" @@ -133,69 +182,20 @@ AVL 树既是二叉搜索树也是平衡二叉树,同时满足这两类二叉 } ``` -=== "C#" - - ```csharp title="" - /* AVL 树节点类 */ - class TreeNode { - public int val; // 节点值 - public int height; // 节点高度 - public TreeNode? left; // 左子节点 - public TreeNode? right; // 右子节点 - public TreeNode(int x) { val = x; } - } - ``` - -=== "Swift" - - ```swift title="" - /* AVL 树节点类 */ - class TreeNode { - var val: Int // 节点值 - var height: Int // 节点高度 - var left: TreeNode? // 左子节点 - var right: TreeNode? // 右子节点 - - init(x: Int) { - val = x - height = 0 - } - } - ``` - === "Zig" ```zig title="" ``` -=== "Dart" - - ```dart title="" - /* AVL 树节点类 */ - class TreeNode { - int val; // 节点值 - int height; // 节点高度 - TreeNode? left; // 左子节点 - TreeNode? right; // 右子节点 - TreeNode(this.val, [this.height = 0, this.left, this.right]); - } - ``` - -=== "Rust" - - ```rust title="" - - ``` - “节点高度”是指从该节点到最远叶节点的距离,即所经过的“边”的数量。需要特别注意的是,叶节点的高度为 0 ,而空节点的高度为 -1 。我们将创建两个工具函数,分别用于获取和更新节点的高度。 -=== "Java" +=== "Python" - ```java title="avl_tree.java" + ```python title="avl_tree.py" [class]{AVLTree}-[func]{height} - [class]{AVLTree}-[func]{updateHeight} + [class]{AVLTree}-[func]{__update_height} ``` === "C++" @@ -206,12 +206,20 @@ AVL 树既是二叉搜索树也是平衡二叉树,同时满足这两类二叉 [class]{AVLTree}-[func]{updateHeight} ``` -=== "Python" +=== "Java" - ```python title="avl_tree.py" + ```java title="avl_tree.java" [class]{AVLTree}-[func]{height} - [class]{AVLTree}-[func]{__update_height} + [class]{AVLTree}-[func]{updateHeight} + ``` + +=== "C#" + + ```csharp title="avl_tree.cs" + [class]{AVLTree}-[func]{height} + + [class]{AVLTree}-[func]{updateHeight} ``` === "Go" @@ -222,6 +230,14 @@ AVL 树既是二叉搜索树也是平衡二叉树,同时满足这两类二叉 [class]{aVLTree}-[func]{updateHeight} ``` +=== "Swift" + + ```swift title="avl_tree.swift" + [class]{AVLTree}-[func]{height} + + [class]{AVLTree}-[func]{updateHeight} + ``` + === "JS" ```javascript title="avl_tree.js" @@ -238,38 +254,6 @@ AVL 树既是二叉搜索树也是平衡二叉树,同时满足这两类二叉 [class]{AVLTree}-[func]{updateHeight} ``` -=== "C" - - ```c title="avl_tree.c" - [class]{}-[func]{height} - - [class]{}-[func]{updateHeight} - ``` - -=== "C#" - - ```csharp title="avl_tree.cs" - [class]{AVLTree}-[func]{height} - - [class]{AVLTree}-[func]{updateHeight} - ``` - -=== "Swift" - - ```swift title="avl_tree.swift" - [class]{AVLTree}-[func]{height} - - [class]{AVLTree}-[func]{updateHeight} - ``` - -=== "Zig" - - ```zig title="avl_tree.zig" - [class]{AVLTree}-[func]{height} - - [class]{AVLTree}-[func]{updateHeight} - ``` - === "Dart" ```dart title="avl_tree.dart" @@ -286,14 +270,30 @@ AVL 树既是二叉搜索树也是平衡二叉树,同时满足这两类二叉 [class]{AVLTree}-[func]{update_height} ``` +=== "C" + + ```c title="avl_tree.c" + [class]{}-[func]{height} + + [class]{}-[func]{updateHeight} + ``` + +=== "Zig" + + ```zig title="avl_tree.zig" + [class]{AVLTree}-[func]{height} + + [class]{AVLTree}-[func]{updateHeight} + ``` + ### 节点平衡因子 节点的「平衡因子 balance factor」定义为节点左子树的高度减去右子树的高度,同时规定空节点的平衡因子为 0 。我们同样将获取节点平衡因子的功能封装成函数,方便后续使用。 -=== "Java" +=== "Python" - ```java title="avl_tree.java" - [class]{AVLTree}-[func]{balanceFactor} + ```python title="avl_tree.py" + [class]{AVLTree}-[func]{balance_factor} ``` === "C++" @@ -302,10 +302,16 @@ AVL 树既是二叉搜索树也是平衡二叉树,同时满足这两类二叉 [class]{AVLTree}-[func]{balanceFactor} ``` -=== "Python" +=== "Java" - ```python title="avl_tree.py" - [class]{AVLTree}-[func]{balance_factor} + ```java title="avl_tree.java" + [class]{AVLTree}-[func]{balanceFactor} + ``` + +=== "C#" + + ```csharp title="avl_tree.cs" + [class]{AVLTree}-[func]{balanceFactor} ``` === "Go" @@ -314,6 +320,12 @@ AVL 树既是二叉搜索树也是平衡二叉树,同时满足这两类二叉 [class]{aVLTree}-[func]{balanceFactor} ``` +=== "Swift" + + ```swift title="avl_tree.swift" + [class]{AVLTree}-[func]{balanceFactor} + ``` + === "JS" ```javascript title="avl_tree.js" @@ -326,30 +338,6 @@ AVL 树既是二叉搜索树也是平衡二叉树,同时满足这两类二叉 [class]{AVLTree}-[func]{balanceFactor} ``` -=== "C" - - ```c title="avl_tree.c" - [class]{}-[func]{balanceFactor} - ``` - -=== "C#" - - ```csharp title="avl_tree.cs" - [class]{AVLTree}-[func]{balanceFactor} - ``` - -=== "Swift" - - ```swift title="avl_tree.swift" - [class]{AVLTree}-[func]{balanceFactor} - ``` - -=== "Zig" - - ```zig title="avl_tree.zig" - [class]{AVLTree}-[func]{balanceFactor} - ``` - === "Dart" ```dart title="avl_tree.dart" @@ -362,6 +350,18 @@ AVL 树既是二叉搜索树也是平衡二叉树,同时满足这两类二叉 [class]{AVLTree}-[func]{balance_factor} ``` +=== "C" + + ```c title="avl_tree.c" + [class]{}-[func]{balanceFactor} + ``` + +=== "Zig" + + ```zig title="avl_tree.zig" + [class]{AVLTree}-[func]{balanceFactor} + ``` + !!! note 设平衡因子为 $f$ ,则一棵 AVL 树的任意节点的平衡因子皆满足 $-1 \le f \le 1$ 。 @@ -394,10 +394,10 @@ AVL 树的特点在于“旋转”操作,它能够在不影响二叉树的中 “向右旋转”是一种形象化的说法,实际上需要通过修改节点指针来实现,代码如下所示。 -=== "Java" +=== "Python" - ```java title="avl_tree.java" - [class]{AVLTree}-[func]{rightRotate} + ```python title="avl_tree.py" + [class]{AVLTree}-[func]{__right_rotate} ``` === "C++" @@ -406,10 +406,16 @@ AVL 树的特点在于“旋转”操作,它能够在不影响二叉树的中 [class]{AVLTree}-[func]{rightRotate} ``` -=== "Python" +=== "Java" - ```python title="avl_tree.py" - [class]{AVLTree}-[func]{__right_rotate} + ```java title="avl_tree.java" + [class]{AVLTree}-[func]{rightRotate} + ``` + +=== "C#" + + ```csharp title="avl_tree.cs" + [class]{AVLTree}-[func]{rightRotate} ``` === "Go" @@ -418,6 +424,12 @@ AVL 树的特点在于“旋转”操作,它能够在不影响二叉树的中 [class]{aVLTree}-[func]{rightRotate} ``` +=== "Swift" + + ```swift title="avl_tree.swift" + [class]{AVLTree}-[func]{rightRotate} + ``` + === "JS" ```javascript title="avl_tree.js" @@ -430,30 +442,6 @@ AVL 树的特点在于“旋转”操作,它能够在不影响二叉树的中 [class]{AVLTree}-[func]{rightRotate} ``` -=== "C" - - ```c title="avl_tree.c" - [class]{}-[func]{rightRotate} - ``` - -=== "C#" - - ```csharp title="avl_tree.cs" - [class]{AVLTree}-[func]{rightRotate} - ``` - -=== "Swift" - - ```swift title="avl_tree.swift" - [class]{AVLTree}-[func]{rightRotate} - ``` - -=== "Zig" - - ```zig title="avl_tree.zig" - [class]{AVLTree}-[func]{rightRotate} - ``` - === "Dart" ```dart title="avl_tree.dart" @@ -466,6 +454,18 @@ AVL 树的特点在于“旋转”操作,它能够在不影响二叉树的中 [class]{AVLTree}-[func]{right_rotate} ``` +=== "C" + + ```c title="avl_tree.c" + [class]{}-[func]{rightRotate} + ``` + +=== "Zig" + + ```zig title="avl_tree.zig" + [class]{AVLTree}-[func]{rightRotate} + ``` + ### 左旋 相应的,如果考虑上述失衡二叉树的“镜像”,则需要执行下图所示的“左旋”操作。 @@ -478,10 +478,10 @@ AVL 树的特点在于“旋转”操作,它能够在不影响二叉树的中 可以观察到,**右旋和左旋操作在逻辑上是镜像对称的,它们分别解决的两种失衡情况也是对称的**。基于对称性,我们只需将右旋的实现代码中的所有的 `left` 替换为 `right` ,将所有的 `right` 替换为 `left` ,即可得到左旋的实现代码。 -=== "Java" +=== "Python" - ```java title="avl_tree.java" - [class]{AVLTree}-[func]{leftRotate} + ```python title="avl_tree.py" + [class]{AVLTree}-[func]{__left_rotate} ``` === "C++" @@ -490,10 +490,16 @@ AVL 树的特点在于“旋转”操作,它能够在不影响二叉树的中 [class]{AVLTree}-[func]{leftRotate} ``` -=== "Python" +=== "Java" - ```python title="avl_tree.py" - [class]{AVLTree}-[func]{__left_rotate} + ```java title="avl_tree.java" + [class]{AVLTree}-[func]{leftRotate} + ``` + +=== "C#" + + ```csharp title="avl_tree.cs" + [class]{AVLTree}-[func]{leftRotate} ``` === "Go" @@ -502,6 +508,12 @@ AVL 树的特点在于“旋转”操作,它能够在不影响二叉树的中 [class]{aVLTree}-[func]{leftRotate} ``` +=== "Swift" + + ```swift title="avl_tree.swift" + [class]{AVLTree}-[func]{leftRotate} + ``` + === "JS" ```javascript title="avl_tree.js" @@ -514,30 +526,6 @@ AVL 树的特点在于“旋转”操作,它能够在不影响二叉树的中 [class]{AVLTree}-[func]{leftRotate} ``` -=== "C" - - ```c title="avl_tree.c" - [class]{}-[func]{leftRotate} - ``` - -=== "C#" - - ```csharp title="avl_tree.cs" - [class]{AVLTree}-[func]{leftRotate} - ``` - -=== "Swift" - - ```swift title="avl_tree.swift" - [class]{AVLTree}-[func]{leftRotate} - ``` - -=== "Zig" - - ```zig title="avl_tree.zig" - [class]{AVLTree}-[func]{leftRotate} - ``` - === "Dart" ```dart title="avl_tree.dart" @@ -550,6 +538,18 @@ AVL 树的特点在于“旋转”操作,它能够在不影响二叉树的中 [class]{AVLTree}-[func]{left_rotate} ``` +=== "C" + + ```c title="avl_tree.c" + [class]{}-[func]{leftRotate} + ``` + +=== "Zig" + + ```zig title="avl_tree.zig" + [class]{AVLTree}-[func]{leftRotate} + ``` + ### 先左旋后右旋 对于下图中的失衡节点 3 ,仅使用左旋或右旋都无法使子树恢复平衡。此时需要先对 `child` 执行“左旋”,再对 `node` 执行“右旋”。 @@ -581,10 +581,10 @@ AVL 树的特点在于“旋转”操作,它能够在不影响二叉树的中 为了便于使用,我们将旋转操作封装成一个函数。**有了这个函数,我们就能对各种失衡情况进行旋转,使失衡节点重新恢复平衡**。 -=== "Java" +=== "Python" - ```java title="avl_tree.java" - [class]{AVLTree}-[func]{rotate} + ```python title="avl_tree.py" + [class]{AVLTree}-[func]{__rotate} ``` === "C++" @@ -593,10 +593,16 @@ AVL 树的特点在于“旋转”操作,它能够在不影响二叉树的中 [class]{AVLTree}-[func]{rotate} ``` -=== "Python" +=== "Java" - ```python title="avl_tree.py" - [class]{AVLTree}-[func]{__rotate} + ```java title="avl_tree.java" + [class]{AVLTree}-[func]{rotate} + ``` + +=== "C#" + + ```csharp title="avl_tree.cs" + [class]{AVLTree}-[func]{rotate} ``` === "Go" @@ -605,6 +611,12 @@ AVL 树的特点在于“旋转”操作,它能够在不影响二叉树的中 [class]{aVLTree}-[func]{rotate} ``` +=== "Swift" + + ```swift title="avl_tree.swift" + [class]{AVLTree}-[func]{rotate} + ``` + === "JS" ```javascript title="avl_tree.js" @@ -617,30 +629,6 @@ AVL 树的特点在于“旋转”操作,它能够在不影响二叉树的中 [class]{AVLTree}-[func]{rotate} ``` -=== "C" - - ```c title="avl_tree.c" - [class]{}-[func]{rotate} - ``` - -=== "C#" - - ```csharp title="avl_tree.cs" - [class]{AVLTree}-[func]{rotate} - ``` - -=== "Swift" - - ```swift title="avl_tree.swift" - [class]{AVLTree}-[func]{rotate} - ``` - -=== "Zig" - - ```zig title="avl_tree.zig" - [class]{AVLTree}-[func]{rotate} - ``` - === "Dart" ```dart title="avl_tree.dart" @@ -653,18 +641,30 @@ AVL 树的特点在于“旋转”操作,它能够在不影响二叉树的中 [class]{AVLTree}-[func]{rotate} ``` +=== "C" + + ```c title="avl_tree.c" + [class]{}-[func]{rotate} + ``` + +=== "Zig" + + ```zig title="avl_tree.zig" + [class]{AVLTree}-[func]{rotate} + ``` + ## AVL 树常用操作 ### 插入节点 AVL 树的节点插入操作与二叉搜索树在主体上类似。唯一的区别在于,在 AVL 树中插入节点后,从该节点到根节点的路径上可能会出现一系列失衡节点。因此,**我们需要从这个节点开始,自底向上执行旋转操作,使所有失衡节点恢复平衡**。 -=== "Java" +=== "Python" - ```java title="avl_tree.java" + ```python title="avl_tree.py" [class]{AVLTree}-[func]{insert} - [class]{AVLTree}-[func]{insertHelper} + [class]{AVLTree}-[func]{__insert_helper} ``` === "C++" @@ -675,12 +675,20 @@ AVL 树的节点插入操作与二叉搜索树在主体上类似。唯一的区 [class]{AVLTree}-[func]{insertHelper} ``` -=== "Python" +=== "Java" - ```python title="avl_tree.py" + ```java title="avl_tree.java" [class]{AVLTree}-[func]{insert} - [class]{AVLTree}-[func]{__insert_helper} + [class]{AVLTree}-[func]{insertHelper} + ``` + +=== "C#" + + ```csharp title="avl_tree.cs" + [class]{AVLTree}-[func]{insert} + + [class]{AVLTree}-[func]{insertHelper} ``` === "Go" @@ -691,6 +699,14 @@ AVL 树的节点插入操作与二叉搜索树在主体上类似。唯一的区 [class]{aVLTree}-[func]{insertHelper} ``` +=== "Swift" + + ```swift title="avl_tree.swift" + [class]{AVLTree}-[func]{insert} + + [class]{AVLTree}-[func]{insertHelper} + ``` + === "JS" ```javascript title="avl_tree.js" @@ -707,38 +723,6 @@ AVL 树的节点插入操作与二叉搜索树在主体上类似。唯一的区 [class]{AVLTree}-[func]{insertHelper} ``` -=== "C" - - ```c title="avl_tree.c" - [class]{aVLTree}-[func]{insert} - - [class]{}-[func]{insertHelper} - ``` - -=== "C#" - - ```csharp title="avl_tree.cs" - [class]{AVLTree}-[func]{insert} - - [class]{AVLTree}-[func]{insertHelper} - ``` - -=== "Swift" - - ```swift title="avl_tree.swift" - [class]{AVLTree}-[func]{insert} - - [class]{AVLTree}-[func]{insertHelper} - ``` - -=== "Zig" - - ```zig title="avl_tree.zig" - [class]{AVLTree}-[func]{insert} - - [class]{AVLTree}-[func]{insertHelper} - ``` - === "Dart" ```dart title="avl_tree.dart" @@ -755,16 +739,32 @@ AVL 树的节点插入操作与二叉搜索树在主体上类似。唯一的区 [class]{AVLTree}-[func]{insert_helper} ``` +=== "C" + + ```c title="avl_tree.c" + [class]{aVLTree}-[func]{insert} + + [class]{}-[func]{insertHelper} + ``` + +=== "Zig" + + ```zig title="avl_tree.zig" + [class]{AVLTree}-[func]{insert} + + [class]{AVLTree}-[func]{insertHelper} + ``` + ### 删除节点 类似地,在二叉搜索树的删除节点方法的基础上,需要从底至顶地执行旋转操作,使所有失衡节点恢复平衡。 -=== "Java" +=== "Python" - ```java title="avl_tree.java" + ```python title="avl_tree.py" [class]{AVLTree}-[func]{remove} - [class]{AVLTree}-[func]{removeHelper} + [class]{AVLTree}-[func]{__remove_helper} ``` === "C++" @@ -775,12 +775,20 @@ AVL 树的节点插入操作与二叉搜索树在主体上类似。唯一的区 [class]{AVLTree}-[func]{removeHelper} ``` -=== "Python" +=== "Java" - ```python title="avl_tree.py" + ```java title="avl_tree.java" [class]{AVLTree}-[func]{remove} - [class]{AVLTree}-[func]{__remove_helper} + [class]{AVLTree}-[func]{removeHelper} + ``` + +=== "C#" + + ```csharp title="avl_tree.cs" + [class]{AVLTree}-[func]{remove} + + [class]{AVLTree}-[func]{removeHelper} ``` === "Go" @@ -791,6 +799,14 @@ AVL 树的节点插入操作与二叉搜索树在主体上类似。唯一的区 [class]{aVLTree}-[func]{removeHelper} ``` +=== "Swift" + + ```swift title="avl_tree.swift" + [class]{AVLTree}-[func]{remove} + + [class]{AVLTree}-[func]{removeHelper} + ``` + === "JS" ```javascript title="avl_tree.js" @@ -807,38 +823,6 @@ AVL 树的节点插入操作与二叉搜索树在主体上类似。唯一的区 [class]{AVLTree}-[func]{removeHelper} ``` -=== "C" - - ```c title="avl_tree.c" - [class]{aVLTree}-[func]{removeNode} - - [class]{}-[func]{removeHelper} - ``` - -=== "C#" - - ```csharp title="avl_tree.cs" - [class]{AVLTree}-[func]{remove} - - [class]{AVLTree}-[func]{removeHelper} - ``` - -=== "Swift" - - ```swift title="avl_tree.swift" - [class]{AVLTree}-[func]{remove} - - [class]{AVLTree}-[func]{removeHelper} - ``` - -=== "Zig" - - ```zig title="avl_tree.zig" - [class]{AVLTree}-[func]{remove} - - [class]{AVLTree}-[func]{removeHelper} - ``` - === "Dart" ```dart title="avl_tree.dart" @@ -855,6 +839,22 @@ AVL 树的节点插入操作与二叉搜索树在主体上类似。唯一的区 [class]{AVLTree}-[func]{remove_helper} ``` +=== "C" + + ```c title="avl_tree.c" + [class]{aVLTree}-[func]{removeNode} + + [class]{}-[func]{removeHelper} + ``` + +=== "Zig" + + ```zig title="avl_tree.zig" + [class]{AVLTree}-[func]{remove} + + [class]{AVLTree}-[func]{removeHelper} + ``` + ### 查找节点 AVL 树的节点查找操作与二叉搜索树一致,在此不再赘述。 diff --git a/docs/chapter_tree/binary_search_tree.md b/docs/chapter_tree/binary_search_tree.md index af134c00..2688b3e6 100755 --- a/docs/chapter_tree/binary_search_tree.md +++ b/docs/chapter_tree/binary_search_tree.md @@ -33,9 +33,9 @@ 二叉搜索树的查找操作与二分查找算法的工作原理一致,都是每轮排除一半情况。循环次数最多为二叉树的高度,当二叉树平衡时,使用 $O(\log n)$ 时间。 -=== "Java" +=== "Python" - ```java title="binary_search_tree.java" + ```python title="binary_search_tree.py" [class]{BinarySearchTree}-[func]{search} ``` @@ -45,9 +45,15 @@ [class]{BinarySearchTree}-[func]{search} ``` -=== "Python" +=== "Java" - ```python title="binary_search_tree.py" + ```java title="binary_search_tree.java" + [class]{BinarySearchTree}-[func]{search} + ``` + +=== "C#" + + ```csharp title="binary_search_tree.cs" [class]{BinarySearchTree}-[func]{search} ``` @@ -57,6 +63,12 @@ [class]{binarySearchTree}-[func]{search} ``` +=== "Swift" + + ```swift title="binary_search_tree.swift" + [class]{BinarySearchTree}-[func]{search} + ``` + === "JS" ```javascript title="binary_search_tree.js" @@ -69,30 +81,6 @@ [class]{BinarySearchTree}-[func]{search} ``` -=== "C" - - ```c title="binary_search_tree.c" - [class]{binarySearchTree}-[func]{search} - ``` - -=== "C#" - - ```csharp title="binary_search_tree.cs" - [class]{BinarySearchTree}-[func]{search} - ``` - -=== "Swift" - - ```swift title="binary_search_tree.swift" - [class]{BinarySearchTree}-[func]{search} - ``` - -=== "Zig" - - ```zig title="binary_search_tree.zig" - [class]{BinarySearchTree}-[func]{search} - ``` - === "Dart" ```dart title="binary_search_tree.dart" @@ -105,6 +93,18 @@ [class]{BinarySearchTree}-[func]{search} ``` +=== "C" + + ```c title="binary_search_tree.c" + [class]{binarySearchTree}-[func]{search} + ``` + +=== "Zig" + + ```zig title="binary_search_tree.zig" + [class]{BinarySearchTree}-[func]{search} + ``` + ### 插入节点 给定一个待插入元素 `num` ,为了保持二叉搜索树“左子树 < 根节点 < 右子树”的性质,插入操作流程如下图所示。 @@ -119,9 +119,9 @@ - 二叉搜索树不允许存在重复节点,否则将违反其定义。因此,若待插入节点在树中已存在,则不执行插入,直接返回。 - 为了实现插入节点,我们需要借助节点 `pre` 保存上一轮循环的节点。这样在遍历至 $\text{None}$ 时,我们可以获取到其父节点,从而完成节点插入操作。 -=== "Java" +=== "Python" - ```java title="binary_search_tree.java" + ```python title="binary_search_tree.py" [class]{BinarySearchTree}-[func]{insert} ``` @@ -131,9 +131,15 @@ [class]{BinarySearchTree}-[func]{insert} ``` -=== "Python" +=== "Java" - ```python title="binary_search_tree.py" + ```java title="binary_search_tree.java" + [class]{BinarySearchTree}-[func]{insert} + ``` + +=== "C#" + + ```csharp title="binary_search_tree.cs" [class]{BinarySearchTree}-[func]{insert} ``` @@ -143,6 +149,12 @@ [class]{binarySearchTree}-[func]{insert} ``` +=== "Swift" + + ```swift title="binary_search_tree.swift" + [class]{BinarySearchTree}-[func]{insert} + ``` + === "JS" ```javascript title="binary_search_tree.js" @@ -155,30 +167,6 @@ [class]{BinarySearchTree}-[func]{insert} ``` -=== "C" - - ```c title="binary_search_tree.c" - [class]{binarySearchTree}-[func]{insert} - ``` - -=== "C#" - - ```csharp title="binary_search_tree.cs" - [class]{BinarySearchTree}-[func]{insert} - ``` - -=== "Swift" - - ```swift title="binary_search_tree.swift" - [class]{BinarySearchTree}-[func]{insert} - ``` - -=== "Zig" - - ```zig title="binary_search_tree.zig" - [class]{BinarySearchTree}-[func]{insert} - ``` - === "Dart" ```dart title="binary_search_tree.dart" @@ -191,6 +179,18 @@ [class]{BinarySearchTree}-[func]{insert} ``` +=== "C" + + ```c title="binary_search_tree.c" + [class]{binarySearchTree}-[func]{insert} + ``` + +=== "Zig" + + ```zig title="binary_search_tree.zig" + [class]{BinarySearchTree}-[func]{insert} + ``` + 与查找节点相同,插入节点使用 $O(\log n)$ 时间。 ### 删除节点 @@ -230,9 +230,9 @@ 删除节点操作同样使用 $O(\log n)$ 时间,其中查找待删除节点需要 $O(\log n)$ 时间,获取中序遍历后继节点需要 $O(\log n)$ 时间。 -=== "Java" +=== "Python" - ```java title="binary_search_tree.java" + ```python title="binary_search_tree.py" [class]{BinarySearchTree}-[func]{remove} ``` @@ -242,9 +242,15 @@ [class]{BinarySearchTree}-[func]{remove} ``` -=== "Python" +=== "Java" - ```python title="binary_search_tree.py" + ```java title="binary_search_tree.java" + [class]{BinarySearchTree}-[func]{remove} + ``` + +=== "C#" + + ```csharp title="binary_search_tree.cs" [class]{BinarySearchTree}-[func]{remove} ``` @@ -254,6 +260,12 @@ [class]{binarySearchTree}-[func]{remove} ``` +=== "Swift" + + ```swift title="binary_search_tree.swift" + [class]{BinarySearchTree}-[func]{remove} + ``` + === "JS" ```javascript title="binary_search_tree.js" @@ -266,30 +278,6 @@ [class]{BinarySearchTree}-[func]{remove} ``` -=== "C" - - ```c title="binary_search_tree.c" - [class]{binarySearchTree}-[func]{removeNode} - ``` - -=== "C#" - - ```csharp title="binary_search_tree.cs" - [class]{BinarySearchTree}-[func]{remove} - ``` - -=== "Swift" - - ```swift title="binary_search_tree.swift" - [class]{BinarySearchTree}-[func]{remove} - ``` - -=== "Zig" - - ```zig title="binary_search_tree.zig" - [class]{BinarySearchTree}-[func]{remove} - ``` - === "Dart" ```dart title="binary_search_tree.dart" @@ -302,6 +290,18 @@ [class]{BinarySearchTree}-[func]{remove} ``` +=== "C" + + ```c title="binary_search_tree.c" + [class]{binarySearchTree}-[func]{removeNode} + ``` + +=== "Zig" + + ```zig title="binary_search_tree.zig" + [class]{BinarySearchTree}-[func]{remove} + ``` + ### 中序遍历有序 如下图所示,二叉树的中序遍历遵循“左 $\rightarrow$ 根 $\rightarrow$ 右”的遍历顺序,而二叉搜索树满足“左子节点 $<$ 根节点 $<$ 右子节点”的大小关系。 diff --git a/docs/chapter_tree/binary_tree.md b/docs/chapter_tree/binary_tree.md index 7f969f1f..fc9e6e4e 100644 --- a/docs/chapter_tree/binary_tree.md +++ b/docs/chapter_tree/binary_tree.md @@ -2,16 +2,15 @@ 「二叉树 binary tree」是一种非线性数据结构,代表着祖先与后代之间的派生关系,体现着“一分为二”的分治逻辑。与链表类似,二叉树的基本单元是节点,每个节点包含:值、左子节点引用、右子节点引用。 -=== "Java" +=== "Python" - ```java title="" - /* 二叉树节点类 */ - class TreeNode { - int val; // 节点值 - TreeNode left; // 左子节点引用 - TreeNode right; // 右子节点引用 - TreeNode(int x) { val = x; } - } + ```python title="" + class TreeNode: + """二叉树节点类""" + def __init__(self, val: int): + self.val: int = val # 节点值 + self.left: Optional[TreeNode] = None # 左子节点引用 + self.right: Optional[TreeNode] = None # 右子节点引用 ``` === "C++" @@ -26,15 +25,28 @@ }; ``` -=== "Python" +=== "Java" - ```python title="" - class TreeNode: - """二叉树节点类""" - def __init__(self, val: int): - self.val: int = val # 节点值 - self.left: Optional[TreeNode] = None # 左子节点引用 - self.right: Optional[TreeNode] = None # 右子节点引用 + ```java title="" + /* 二叉树节点类 */ + class TreeNode { + int val; // 节点值 + TreeNode left; // 左子节点引用 + TreeNode right; // 右子节点引用 + TreeNode(int x) { val = x; } + } + ``` + +=== "C#" + + ```csharp title="" + /* 二叉树节点类 */ + class TreeNode { + int val; // 节点值 + TreeNode? left; // 左子节点引用 + TreeNode? right; // 右子节点引用 + TreeNode(int x) { val = x; } + } ``` === "Go" @@ -56,6 +68,21 @@ } ``` +=== "Swift" + + ```swift title="" + /* 二叉树节点类 */ + class TreeNode { + var val: Int // 节点值 + var left: TreeNode? // 左子节点引用 + var right: TreeNode? // 右子节点引用 + + init(x: Int) { + val = x + } + } + ``` + === "JS" ```javascript title="" @@ -84,6 +111,24 @@ } ``` +=== "Dart" + + ```dart title="" + /* 二叉树节点类 */ + class TreeNode { + int val; // 节点值 + TreeNode? left; // 左子节点引用 + TreeNode? right; // 右子节点引用 + TreeNode(this.val, [this.left, this.right]); + } + ``` + +=== "Rust" + + ```rust title="" + + ``` + === "C" ```c title="" @@ -110,57 +155,12 @@ } ``` -=== "C#" - - ```csharp title="" - /* 二叉树节点类 */ - class TreeNode { - int val; // 节点值 - TreeNode? left; // 左子节点引用 - TreeNode? right; // 右子节点引用 - TreeNode(int x) { val = x; } - } - ``` - -=== "Swift" - - ```swift title="" - /* 二叉树节点类 */ - class TreeNode { - var val: Int // 节点值 - var left: TreeNode? // 左子节点引用 - var right: TreeNode? // 右子节点引用 - - init(x: Int) { - val = x - } - } - ``` - === "Zig" ```zig title="" ``` -=== "Dart" - - ```dart title="" - /* 二叉树节点类 */ - class TreeNode { - int val; // 节点值 - TreeNode? left; // 左子节点引用 - TreeNode? right; // 右子节点引用 - TreeNode(this.val, [this.left, this.right]); - } - ``` - -=== "Rust" - - ```rust title="" - - ``` - 每个节点都有两个引用(指针),分别指向「左子节点 left-child node」和「右子节点 right-child node」,该节点被称为这两个子节点的「父节点 parent node」。当给定一个二叉树的节点时,我们将该节点的左子节点及其以下节点形成的树称为该节点的「左子树 left subtree」,同理可得「右子树 right subtree」。 **在二叉树中,除叶节点外,其他所有节点都包含子节点和非空子树**。如下图所示,如果将“节点 2”视为父节点,则其左子节点和右子节点分别是“节点 4”和“节点 5”,左子树是“节点 4 及其以下节点形成的树”,右子树是“节点 5 及其以下节点形成的树”。 @@ -192,20 +192,21 @@ 与链表类似,首先初始化节点,然后构建引用(指针)。 -=== "Java" +=== "Python" - ```java title="binary_tree.java" - // 初始化节点 - TreeNode n1 = new TreeNode(1); - TreeNode n2 = new TreeNode(2); - TreeNode n3 = new TreeNode(3); - TreeNode n4 = new TreeNode(4); - TreeNode n5 = new TreeNode(5); - // 构建引用指向(即指针) - n1.left = n2; - n1.right = n3; - n2.left = n4; - n2.right = n5; + ```python title="binary_tree.py" + # 初始化二叉树 + # 初始化节点 + n1 = TreeNode(val=1) + n2 = TreeNode(val=2) + n3 = TreeNode(val=3) + n4 = TreeNode(val=4) + n5 = TreeNode(val=5) + # 构建引用指向(即指针) + n1.left = n2 + n1.right = n3 + n2.left = n4 + n2.right = n5 ``` === "C++" @@ -225,21 +226,37 @@ n2->right = n5; ``` -=== "Python" +=== "Java" - ```python title="binary_tree.py" - # 初始化二叉树 - # 初始化节点 - n1 = TreeNode(val=1) - n2 = TreeNode(val=2) - n3 = TreeNode(val=3) - n4 = TreeNode(val=4) - n5 = TreeNode(val=5) - # 构建引用指向(即指针) - n1.left = n2 - n1.right = n3 - n2.left = n4 - n2.right = n5 + ```java title="binary_tree.java" + // 初始化节点 + TreeNode n1 = new TreeNode(1); + TreeNode n2 = new TreeNode(2); + TreeNode n3 = new TreeNode(3); + TreeNode n4 = new TreeNode(4); + TreeNode n5 = new TreeNode(5); + // 构建引用指向(即指针) + n1.left = n2; + n1.right = n3; + n2.left = n4; + n2.right = n5; + ``` + +=== "C#" + + ```csharp title="binary_tree.cs" + /* 初始化二叉树 */ + // 初始化节点 + TreeNode n1 = new TreeNode(1); + TreeNode n2 = new TreeNode(2); + TreeNode n3 = new TreeNode(3); + TreeNode n4 = new TreeNode(4); + TreeNode n5 = new TreeNode(5); + // 构建引用指向(即指针) + n1.left = n2; + n1.right = n3; + n2.left = n4; + n2.right = n5; ``` === "Go" @@ -259,6 +276,22 @@ n2.Right = n5 ``` +=== "Swift" + + ```swift title="binary_tree.swift" + // 初始化节点 + let n1 = TreeNode(x: 1) + let n2 = TreeNode(x: 2) + let n3 = TreeNode(x: 3) + let n4 = TreeNode(x: 4) + let n5 = TreeNode(x: 5) + // 构建引用指向(即指针) + n1.left = n2 + n1.right = n3 + n2.left = n4 + n2.right = n5 + ``` + === "JS" ```javascript title="binary_tree.js" @@ -293,62 +326,6 @@ n2.right = n5; ``` -=== "C" - - ```c title="binary_tree.c" - /* 初始化二叉树 */ - // 初始化节点 - TreeNode *n1 = newTreeNode(1); - TreeNode *n2 = newTreeNode(2); - TreeNode *n3 = newTreeNode(3); - TreeNode *n4 = newTreeNode(4); - TreeNode *n5 = newTreeNode(5); - // 构建引用指向(即指针) - n1->left = n2; - n1->right = n3; - n2->left = n4; - n2->right = n5; - ``` - -=== "C#" - - ```csharp title="binary_tree.cs" - /* 初始化二叉树 */ - // 初始化节点 - TreeNode n1 = new TreeNode(1); - TreeNode n2 = new TreeNode(2); - TreeNode n3 = new TreeNode(3); - TreeNode n4 = new TreeNode(4); - TreeNode n5 = new TreeNode(5); - // 构建引用指向(即指针) - n1.left = n2; - n1.right = n3; - n2.left = n4; - n2.right = n5; - ``` - -=== "Swift" - - ```swift title="binary_tree.swift" - // 初始化节点 - let n1 = TreeNode(x: 1) - let n2 = TreeNode(x: 2) - let n3 = TreeNode(x: 3) - let n4 = TreeNode(x: 4) - let n5 = TreeNode(x: 5) - // 构建引用指向(即指针) - n1.left = n2 - n1.right = n3 - n2.left = n4 - n2.right = n5 - ``` - -=== "Zig" - - ```zig title="binary_tree.zig" - - ``` - === "Dart" ```dart title="binary_tree.dart" @@ -372,21 +349,45 @@ ``` +=== "C" + + ```c title="binary_tree.c" + /* 初始化二叉树 */ + // 初始化节点 + TreeNode *n1 = newTreeNode(1); + TreeNode *n2 = newTreeNode(2); + TreeNode *n3 = newTreeNode(3); + TreeNode *n4 = newTreeNode(4); + TreeNode *n5 = newTreeNode(5); + // 构建引用指向(即指针) + n1->left = n2; + n1->right = n3; + n2->left = n4; + n2->right = n5; + ``` + +=== "Zig" + + ```zig title="binary_tree.zig" + + ``` + ### 插入与删除节点 与链表类似,在二叉树中插入与删除节点可以通过修改指针来实现。下图给出了一个示例。 ![在二叉树中插入与删除节点](binary_tree.assets/binary_tree_add_remove.png) -=== "Java" +=== "Python" - ```java title="binary_tree.java" - TreeNode P = new TreeNode(0); - // 在 n1 -> n2 中间插入节点 P - n1.left = P; - P.left = n2; - // 删除节点 P - n1.left = n2; + ```python title="binary_tree.py" + # 插入与删除节点 + p = TreeNode(0) + # 在 n1 -> n2 中间插入节点 P + n1.left = p + p.left = n2 + # 删除节点 P + n1.left = n2 ``` === "C++" @@ -401,16 +402,27 @@ n1->left = n2; ``` -=== "Python" +=== "Java" - ```python title="binary_tree.py" - # 插入与删除节点 - p = TreeNode(0) - # 在 n1 -> n2 中间插入节点 P - n1.left = p - p.left = n2 - # 删除节点 P - n1.left = n2 + ```java title="binary_tree.java" + TreeNode P = new TreeNode(0); + // 在 n1 -> n2 中间插入节点 P + n1.left = P; + P.left = n2; + // 删除节点 P + n1.left = n2; + ``` + +=== "C#" + + ```csharp title="binary_tree.cs" + /* 插入与删除节点 */ + TreeNode P = new TreeNode(0); + // 在 n1 -> n2 中间插入节点 P + n1.left = P; + P.left = n2; + // 删除节点 P + n1.left = n2; ``` === "Go" @@ -425,6 +437,17 @@ n1.Left = n2 ``` +=== "Swift" + + ```swift title="binary_tree.swift" + let P = TreeNode(x: 0) + // 在 n1 -> n2 中间插入节点 P + n1.left = P + P.left = n2 + // 删除节点 P + n1.left = n2 + ``` + === "JS" ```javascript title="binary_tree.js" @@ -449,47 +472,6 @@ n1.left = n2; ``` -=== "C" - - ```c title="binary_tree.c" - /* 插入与删除节点 */ - TreeNode *P = newTreeNode(0); - // 在 n1 -> n2 中间插入节点 P - n1->left = P; - P->left = n2; - // 删除节点 P - n1->left = n2; - ``` - -=== "C#" - - ```csharp title="binary_tree.cs" - /* 插入与删除节点 */ - TreeNode P = new TreeNode(0); - // 在 n1 -> n2 中间插入节点 P - n1.left = P; - P.left = n2; - // 删除节点 P - n1.left = n2; - ``` - -=== "Swift" - - ```swift title="binary_tree.swift" - let P = TreeNode(x: 0) - // 在 n1 -> n2 中间插入节点 P - n1.left = P - P.left = n2 - // 删除节点 P - n1.left = n2 - ``` - -=== "Zig" - - ```zig title="binary_tree.zig" - - ``` - === "Dart" ```dart title="binary_tree.dart" @@ -508,6 +490,24 @@ ``` +=== "C" + + ```c title="binary_tree.c" + /* 插入与删除节点 */ + TreeNode *P = newTreeNode(0); + // 在 n1 -> n2 中间插入节点 P + n1->left = P; + P->left = n2; + // 删除节点 P + n1->left = n2; + ``` + +=== "Zig" + + ```zig title="binary_tree.zig" + + ``` + !!! note 需要注意的是,插入节点可能会改变二叉树的原有逻辑结构,而删除节点通常意味着删除该节点及其所有子树。因此,在二叉树中,插入与删除操作通常是由一套操作配合完成的,以实现有实际意义的操作。 diff --git a/docs/chapter_tree/binary_tree_traversal.md b/docs/chapter_tree/binary_tree_traversal.md index 1a988d80..d9ebd252 100755 --- a/docs/chapter_tree/binary_tree_traversal.md +++ b/docs/chapter_tree/binary_tree_traversal.md @@ -16,10 +16,10 @@ 广度优先遍历通常借助“队列”来实现。队列遵循“先进先出”的规则,而广度优先遍历则遵循“逐层推进”的规则,两者背后的思想是一致的。 -=== "Java" +=== "Python" - ```java title="binary_tree_bfs.java" - [class]{binary_tree_bfs}-[func]{levelOrder} + ```python title="binary_tree_bfs.py" + [class]{}-[func]{level_order} ``` === "C++" @@ -28,10 +28,16 @@ [class]{}-[func]{levelOrder} ``` -=== "Python" +=== "Java" - ```python title="binary_tree_bfs.py" - [class]{}-[func]{level_order} + ```java title="binary_tree_bfs.java" + [class]{binary_tree_bfs}-[func]{levelOrder} + ``` + +=== "C#" + + ```csharp title="binary_tree_bfs.cs" + [class]{binary_tree_bfs}-[func]{levelOrder} ``` === "Go" @@ -40,6 +46,12 @@ [class]{}-[func]{levelOrder} ``` +=== "Swift" + + ```swift title="binary_tree_bfs.swift" + [class]{}-[func]{levelOrder} + ``` + === "JS" ```javascript title="binary_tree_bfs.js" @@ -52,30 +64,6 @@ [class]{}-[func]{levelOrder} ``` -=== "C" - - ```c title="binary_tree_bfs.c" - [class]{}-[func]{levelOrder} - ``` - -=== "C#" - - ```csharp title="binary_tree_bfs.cs" - [class]{binary_tree_bfs}-[func]{levelOrder} - ``` - -=== "Swift" - - ```swift title="binary_tree_bfs.swift" - [class]{}-[func]{levelOrder} - ``` - -=== "Zig" - - ```zig title="binary_tree_bfs.zig" - [class]{}-[func]{levelOrder} - ``` - === "Dart" ```dart title="binary_tree_bfs.dart" @@ -88,6 +76,18 @@ [class]{}-[func]{level_order} ``` +=== "C" + + ```c title="binary_tree_bfs.c" + [class]{}-[func]{levelOrder} + ``` + +=== "Zig" + + ```zig title="binary_tree_bfs.zig" + [class]{}-[func]{levelOrder} + ``` + ### 复杂度分析 - **时间复杂度 $O(n)$** :所有节点被访问一次,使用 $O(n)$ 时间,其中 $n$ 为节点数量。 @@ -105,14 +105,14 @@ 深度优先搜索通常基于递归实现: -=== "Java" +=== "Python" - ```java title="binary_tree_dfs.java" - [class]{binary_tree_dfs}-[func]{preOrder} + ```python title="binary_tree_dfs.py" + [class]{}-[func]{pre_order} - [class]{binary_tree_dfs}-[func]{inOrder} + [class]{}-[func]{in_order} - [class]{binary_tree_dfs}-[func]{postOrder} + [class]{}-[func]{post_order} ``` === "C++" @@ -125,14 +125,24 @@ [class]{}-[func]{postOrder} ``` -=== "Python" +=== "Java" - ```python title="binary_tree_dfs.py" - [class]{}-[func]{pre_order} + ```java title="binary_tree_dfs.java" + [class]{binary_tree_dfs}-[func]{preOrder} - [class]{}-[func]{in_order} + [class]{binary_tree_dfs}-[func]{inOrder} - [class]{}-[func]{post_order} + [class]{binary_tree_dfs}-[func]{postOrder} + ``` + +=== "C#" + + ```csharp title="binary_tree_dfs.cs" + [class]{binary_tree_dfs}-[func]{preOrder} + + [class]{binary_tree_dfs}-[func]{inOrder} + + [class]{binary_tree_dfs}-[func]{postOrder} ``` === "Go" @@ -145,6 +155,16 @@ [class]{}-[func]{postOrder} ``` +=== "Swift" + + ```swift title="binary_tree_dfs.swift" + [class]{}-[func]{preOrder} + + [class]{}-[func]{inOrder} + + [class]{}-[func]{postOrder} + ``` + === "JS" ```javascript title="binary_tree_dfs.js" @@ -165,46 +185,6 @@ [class]{}-[func]{postOrder} ``` -=== "C" - - ```c title="binary_tree_dfs.c" - [class]{}-[func]{preOrder} - - [class]{}-[func]{inOrder} - - [class]{}-[func]{postOrder} - ``` - -=== "C#" - - ```csharp title="binary_tree_dfs.cs" - [class]{binary_tree_dfs}-[func]{preOrder} - - [class]{binary_tree_dfs}-[func]{inOrder} - - [class]{binary_tree_dfs}-[func]{postOrder} - ``` - -=== "Swift" - - ```swift title="binary_tree_dfs.swift" - [class]{}-[func]{preOrder} - - [class]{}-[func]{inOrder} - - [class]{}-[func]{postOrder} - ``` - -=== "Zig" - - ```zig title="binary_tree_dfs.zig" - [class]{}-[func]{preOrder} - - [class]{}-[func]{inOrder} - - [class]{}-[func]{postOrder} - ``` - === "Dart" ```dart title="binary_tree_dfs.dart" @@ -225,6 +205,26 @@ [class]{}-[func]{post_order} ``` +=== "C" + + ```c title="binary_tree_dfs.c" + [class]{}-[func]{preOrder} + + [class]{}-[func]{inOrder} + + [class]{}-[func]{postOrder} + ``` + +=== "Zig" + + ```zig title="binary_tree_dfs.zig" + [class]{}-[func]{preOrder} + + [class]{}-[func]{inOrder} + + [class]{}-[func]{postOrder} + ``` + !!! note 深度优先搜索也可以基于迭代实现,有兴趣的同学可以自行研究。