From 15efaca85d6e2904c6d2dbf7cdbbe7ff643b49e1 Mon Sep 17 00:00:00 2001 From: sjinzh <99076655+sjinzh@users.noreply.github.com> Date: Fri, 3 Feb 2023 19:15:34 +0800 Subject: [PATCH] =?UTF-8?q?copy=20zig=20codes=20of=20chapter=5Farray=5Fand?= =?UTF-8?q?=5Flinkedlist=20and=20chapter=5Fcomputatio=E2=80=A6=20(#319)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * copy zig codes of chapter_array_and_linkedlist and chapter_computational_complexity to markdown files * Update time_complexity.md --------- Co-authored-by: Yudong Jin --- .../chapter_array_and_linkedlist/array.zig | 19 ++- .../zig/chapter_array_and_linkedlist/list.zig | 3 +- docs/chapter_array_and_linkedlist/array.md | 65 ++++++- .../linked_list.md | 84 +++++++++- docs/chapter_array_and_linkedlist/list.md | 158 +++++++++++++++++- .../space_complexity.md | 89 +++++++++- .../space_time_tradeoff.md | 37 +++- .../time_complexity.md | 156 +++++++++++++++-- 8 files changed, 568 insertions(+), 43 deletions(-) diff --git a/codes/zig/chapter_array_and_linkedlist/array.zig b/codes/zig/chapter_array_and_linkedlist/array.zig index 8c830cf4d..eebb46119 100644 --- a/codes/zig/chapter_array_and_linkedlist/array.zig +++ b/codes/zig/chapter_array_and_linkedlist/array.zig @@ -70,26 +70,27 @@ pub fn find(nums: []i32, target: i32) i32 { // Driver Code pub fn main() !void { + // 初始化内存分配器 + var mem_arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + defer mem_arena.deinit(); + const mem_allocator = mem_arena.allocator(); + // 初始化数组 - const size: i32 = 5; - var arr = [_]i32{0} ** size; + var arr = [_]i32{0} ** 5; std.debug.print("数组 arr = ", .{}); inc.PrintUtil.printArray(i32, &arr); var array = [_]i32{ 1, 3, 2, 5, 4 }; + var known_at_runtime_zero: usize = 0; + var nums = array[known_at_runtime_zero..]; std.debug.print("\n数组 nums = ", .{}); - inc.PrintUtil.printArray(i32, &array); + inc.PrintUtil.printArray(i32, nums); // 随机访问 - var randomNum = randomAccess(&array); + var randomNum = randomAccess(nums); std.debug.print("\n在 nums 中获取随机元素 {}", .{randomNum}); // 长度扩展 - var known_at_runtime_zero: usize = 0; - var nums: []i32 = array[known_at_runtime_zero..array.len]; - var mem_arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); - defer mem_arena.deinit(); - const mem_allocator = mem_arena.allocator(); nums = try extend(mem_allocator, nums, 3); std.debug.print("\n将数组长度扩展至 8 ,得到 nums = ", .{}); inc.PrintUtil.printArray(i32, nums); diff --git a/codes/zig/chapter_array_and_linkedlist/list.zig b/codes/zig/chapter_array_and_linkedlist/list.zig index 608bce96c..32fab973b 100644 --- a/codes/zig/chapter_array_and_linkedlist/list.zig +++ b/codes/zig/chapter_array_and_linkedlist/list.zig @@ -44,8 +44,7 @@ pub fn main() !void { inc.PrintUtil.printList(i32, list); // 删除元素 - var value = list.orderedRemove(3); - _ = value; + _ = list.orderedRemove(3); std.debug.print("\n删除索引 3 处的元素,得到 list = ", .{}); inc.PrintUtil.printList(i32, list); diff --git a/docs/chapter_array_and_linkedlist/array.md b/docs/chapter_array_and_linkedlist/array.md index 01b13840a..df60c834b 100644 --- a/docs/chapter_array_and_linkedlist/array.md +++ b/docs/chapter_array_and_linkedlist/array.md @@ -92,7 +92,9 @@ comments: true === "Zig" ```zig title="array.zig" - + // 初始化数组 + var arr = [_]i32{0} ** 5; // { 0, 0, 0, 0, 0 } + var nums = [_]i32{ 1, 3, 2, 5, 4 }; ``` ## 4.1.1. 数组优点 @@ -226,7 +228,14 @@ elementAddr = firtstElementAddr + elementLength * elementIndex === "Zig" ```zig title="array.zig" - + // 随机返回一个数组元素 + pub fn randomAccess(nums: []i32) i32 { + // 在区间 [0, nums.len) 中随机抽取一个整数 + var randomIndex = std.crypto.random.intRangeLessThan(usize, 0, nums.len); + // 获取并返回随机元素 + var randomNum = nums[randomIndex]; + return randomNum; + } ``` ## 4.1.2. 数组缺点 @@ -374,7 +383,16 @@ elementAddr = firtstElementAddr + elementLength * elementIndex === "Zig" ```zig title="array.zig" - + // 扩展数组长度 + pub fn extend(mem_allocator: std.mem.Allocator, nums: []i32, enlarge: usize) ![]i32 { + // 初始化一个扩展长度后的数组 + var res = try mem_allocator.alloc(i32, nums.len + enlarge); + std.mem.set(i32, res, 0); + // 将原数组中的所有元素复制到新数组 + std.mem.copy(i32, res, nums); + // 返回扩展后的新数组 + return res; + } ``` **数组中插入或删除元素效率低下**。假设我们想要在数组中间某位置插入一个元素,由于数组元素在内存中是“紧挨着的”,它们之间没有空间再放任何数据。因此,我们不得不将此索引之后的所有元素都向后移动一位,然后再把元素赋值给该索引。删除元素也是类似,需要把此索引之后的元素都向前移动一位。总体看有以下缺点: @@ -572,7 +590,25 @@ elementAddr = firtstElementAddr + elementLength * elementIndex === "Zig" ```zig title="array.zig" + // 在数组的索引 index 处插入元素 num + pub fn insert(nums: []i32, num: i32, index: usize) void { + // 把索引 index 以及之后的所有元素向后移动一位 + var i = nums.len - 1; + while (i > index) : (i -= 1) { + nums[i] = nums[i - 1]; + } + // 将 num 赋给 index 处元素 + nums[index] = num; + } + // 删除索引 index 处元素 + pub fn remove(nums: []i32, index: usize) void { + // 把索引 index 之后的所有元素向前移动一位 + var i = index; + while (i < nums.len - 1) : (i += 1) { + nums[i] = nums[i + 1]; + } + } ``` ## 4.1.3. 数组常用操作 @@ -720,7 +756,20 @@ elementAddr = firtstElementAddr + elementLength * elementIndex === "Zig" ```zig title="array.zig" - + // 遍历数组 + pub fn traverse(nums: []i32) void { + var count: i32 = 0; + // 通过索引遍历数组 + var i: i32 = 0; + while (i < nums.len) : (i += 1) { + count += 1; + } + count = 0; + // 直接遍历数组 + for (nums) |_| { + count += 1; + } + } ``` **数组查找**。通过遍历数组,查找数组内的指定元素,并输出对应索引。 @@ -842,7 +891,13 @@ elementAddr = firtstElementAddr + elementLength * elementIndex === "Zig" ```zig title="array.zig" - + // 在数组中查找指定元素 + pub fn find(nums: []i32, target: i32) i32 { + for (nums) |num, i| { + if (num == target) return @intCast(i32, i); + } + return -1; + } ``` ## 4.1.4. 数组典型应用 diff --git a/docs/chapter_array_and_linkedlist/linked_list.md b/docs/chapter_array_and_linkedlist/linked_list.md index d3cacd6b4..0e17954a5 100644 --- a/docs/chapter_array_and_linkedlist/linked_list.md +++ b/docs/chapter_array_and_linkedlist/linked_list.md @@ -129,7 +129,21 @@ comments: true === "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; + } + }; + } ``` **尾结点指向什么?** 我们一般将链表的最后一个结点称为「尾结点」,其指向的是「空」,在 Java / C++ / Python 中分别记为 `null` / `nullptr` / `None` 。在不引起歧义下,本书都使用 `null` 来表示空。 @@ -286,7 +300,18 @@ comments: true === "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; ``` ## 4.2.1. 链表优点 @@ -480,7 +505,21 @@ comments: true === "Zig" ```zig title="linked_list.zig" + // 在链表的结点 n0 之后插入结点 P + pub fn insert(n0: ?*inc.ListNode(i32), P: ?*inc.ListNode(i32)) void { + var n1 = n0.?.next; + n0.?.next = P; + P.?.next = n1; + } + // 删除链表的结点 n0 之后的首个结点 + pub fn remove(n0: ?*inc.ListNode(i32)) void { + if (n0.?.next == null) return; + // n0 -> P -> n1 + var P = n0.?.next; + var n1 = P.?.next; + n0.?.next = n1; + } ``` ## 4.2.2. 链表缺点 @@ -612,7 +651,16 @@ comments: true === "Zig" ```zig title="linked_list.zig" - + // 访问链表中索引为 index 的结点 + pub fn access(node: ?*inc.ListNode(i32), index: i32) ?*inc.ListNode(i32) { + var head = node; + var i: i32 = 0; + while (i < index) : (i += 1) { + head = head.?.next; + if (head == null) return null; + } + return head; + } ``` **链表的内存占用多**。链表以结点为单位,每个结点除了保存值外,还需额外保存指针(引用)。这意味着同样数据量下,链表比数组需要占用更多内存空间。 @@ -763,7 +811,17 @@ comments: true === "Zig" ```zig title="linked_list.zig" - + // 在链表中查找值为 target 的首个结点 + pub fn find(node: ?*inc.ListNode(i32), target: i32) i32 { + var head = node; + var index: i32 = 0; + while (head != null) { + if (head.?.val == target) return index; + head = head.?.next; + index += 1; + } + return -1; + } ``` ## 4.2.4. 常见链表类型 @@ -897,7 +955,23 @@ comments: true === "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; + } + }; + } ``` ![linkedlist_common_types](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 9ffa6fce9..e7ec18658 100644 --- a/docs/chapter_array_and_linkedlist/list.md +++ b/docs/chapter_array_and_linkedlist/list.md @@ -104,7 +104,10 @@ comments: true === "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)$ 时间内访问与更新元素,效率很高。 @@ -198,7 +201,11 @@ comments: true === "Zig" ```zig title="list.zig" + // 访问元素 + var num = list.items[1]; // 访问索引 1 处的元素 + // 更新元素 + list.items[1] = 0; // 将索引 1 处的元素更新为 0 ``` **在列表中添加、插入、删除元素**。相对于数组,列表可以自由地添加与删除元素。在列表尾部添加元素的时间复杂度为 $O(1)$ ,但是插入与删除元素的效率仍与数组一样低,时间复杂度为 $O(N)$ 。 @@ -372,7 +379,21 @@ comments: true === "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 处的元素 ``` **遍历列表**。与数组一样,列表可以使用索引遍历,也可以使用 `for-each` 直接遍历。 @@ -514,7 +535,18 @@ comments: true === "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` ,我们可以将其中一个列表拼接到另一个的尾部。 @@ -593,7 +625,11 @@ comments: true === "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 之后 ``` **排序列表**。排序也是常用的方法之一,完成列表排序后,我们就可以使用在数组类算法题中经常考察的「二分查找」和「双指针」算法了。 @@ -663,7 +699,8 @@ comments: true === "Zig" ```zig title="list.zig" - + // 排序列表 + std.sort.sort(i32, list.items, {}, comptime std.sort.asc(i32)); ``` ## 4.3.2. 列表简易实现 * @@ -1439,5 +1476,120 @@ comments: true === "Zig" ```zig title="my_list.zig" + // 列表类简易实现 + pub fn MyList(comptime T: type) type { + return struct { + const Self = @This(); + + nums: []T = undefined, // 数组(存储列表元素) + numsCapacity: usize = 10, // 列表容量 + numSize: usize = 0, // 列表长度(即当前元素数量) + extendRatio: usize = 2, // 每次列表扩容的倍数 + mem_arena: ?std.heap.ArenaAllocator = null, + mem_allocator: std.mem.Allocator = undefined, // 内存分配器 + + // 构造函数(分配内存+初始化列表) + pub fn init(self: *Self, allocator: std.mem.Allocator) !void { + if (self.mem_arena == null) { + self.mem_arena = std.heap.ArenaAllocator.init(allocator); + self.mem_allocator = self.mem_arena.?.allocator(); + } + self.nums = try self.mem_allocator.alloc(T, self.numsCapacity); + std.mem.set(T, self.nums, @as(T, 0)); + } + // 析构函数(释放内存) + pub fn deinit(self: *Self) void { + if (self.mem_arena == null) return; + self.mem_arena.?.deinit(); + } + + // 获取列表长度(即当前元素数量) + pub fn size(self: *Self) usize { + return self.numSize; + } + + // 获取列表容量 + pub fn capacity(self: *Self) usize { + return self.numsCapacity; + } + + // 访问元素 + pub fn get(self: *Self, index: usize) T { + // 索引如果越界则抛出异常,下同 + if (index < 0 or index >= self.size()) @panic("索引越界"); + return self.nums[index]; + } + + // 更新元素 + pub fn set(self: *Self, index: usize, num: T) void { + // 索引如果越界则抛出异常,下同 + if (index < 0 or index >= self.size()) @panic("索引越界"); + self.nums[index] = num; + } + + // 尾部添加元素 + pub fn add(self: *Self, num: T) !void { + // 元素数量超出容量时,触发扩容机制 + if (self.size() == self.capacity()) try self.extendCapacity(); + self.nums[self.size()] = num; + // 更新元素数量 + self.numSize += 1; + } + + // 中间插入元素 + pub fn insert(self: *Self, index: usize, num: T) !void { + if (index < 0 or index >= self.size()) @panic("索引越界"); + // 元素数量超出容量时,触发扩容机制 + if (self.size() == self.capacity()) try self.extendCapacity(); + // 索引 i 以及之后的元素都向后移动一位 + var j = self.size() - 1; + while (j >= index) : (j -= 1) { + self.nums[j + 1] = self.nums[j]; + } + self.nums[index] = num; + // 更新元素数量 + self.numSize += 1; + } + + // 删除元素 + pub fn remove(self: *Self, index: usize) T { + if (index < 0 or index >= self.size()) @panic("索引越界"); + var num = self.nums[index]; + // 索引 i 之后的元素都向前移动一位 + var j = index; + while (j < self.size() - 1) : (j += 1) { + self.nums[j] = self.nums[j + 1]; + } + // 更新元素数量 + self.numSize -= 1; + // 返回被删除元素 + return num; + } + + // 列表扩容 + pub fn extendCapacity(self: *Self) !void { + // 新建一个长度为 size * extendRatio 的数组,并将原数组拷贝到新数组 + var newCapacity = self.capacity() * self.extendRatio; + var extend = try self.mem_allocator.alloc(T, newCapacity); + std.mem.set(T, extend, @as(T, 0)); + // 将原数组中的所有元素复制到新数组 + std.mem.copy(T, extend, self.nums); + self.nums = extend; + // 更新列表容量 + self.numsCapacity = newCapacity; + } + + // 将列表转换为数组 + pub fn toArray(self: *Self) ![]T { + // 仅转换有效长度范围内的列表元素 + var nums = try self.mem_allocator.alloc(T, self.size()); + std.mem.set(T, nums, @as(T, 0)); + for (nums) |*num, i| { + num.* = self.get(i); + } + return nums; + } + }; + } ``` diff --git a/docs/chapter_computational_complexity/space_complexity.md b/docs/chapter_computational_complexity/space_complexity.md index 5d998c63f..9185c1e2f 100644 --- a/docs/chapter_computational_complexity/space_complexity.md +++ b/docs/chapter_computational_complexity/space_complexity.md @@ -643,7 +643,29 @@ $$ === "Zig" ```zig title="space_complexity.zig" - + // 常数阶 + fn constant(n: i32) void { + // 常量、变量、对象占用 O(1) 空间 + const a: i32 = 0; + var b: i32 = 0; + var nums = [_]i32{0}**10000; + var node = inc.ListNode(i32){.val = 0}; + var i: i32 = 0; + // 循环中的变量占用 O(1) 空间 + while (i < n) : (i += 1) { + var c: i32 = 0; + _ = c; + } + // 循环中的函数占用 O(1) 空间 + i = 0; + while (i < n) : (i += 1) { + _ = function(); + } + _ = a; + _ = b; + _ = nums; + _ = node; + } ``` ### 线性阶 $O(n)$ @@ -781,7 +803,28 @@ $$ === "Zig" ```zig title="space_complexity.zig" - + // 线性阶 + fn linear(comptime n: i32) !void { + // 长度为 n 的数组占用 O(n) 空间 + var nums = [_]i32{0}**n; + // 长度为 n 的列表占用 O(n) 空间 + var nodes = std.ArrayList(i32).init(std.heap.page_allocator); + defer nodes.deinit(); + var i: i32 = 0; + while (i < n) : (i += 1) { + try nodes.append(i); + } + // 长度为 n 的哈希表占用 O(n) 空间 + var map = std.AutoArrayHashMap(i32, []const u8).init(std.heap.page_allocator); + defer map.deinit(); + var j: i32 = 0; + while (j < n) : (j += 1) { + const string = try std.fmt.allocPrint(std.heap.page_allocator, "{d}", .{j}); + defer std.heap.page_allocator.free(string); + try map.put(i, string); + } + _ = nums; + } ``` 以下递归函数会同时存在 $n$ 个未返回的 `algorithm()` 函数,使用 $O(n)$ 大小的栈帧空间。 @@ -877,7 +920,12 @@ $$ === "Zig" ```zig title="space_complexity.zig" - + // 线性阶(递归实现) + fn linearRecur(comptime n: i32) void { + std.debug.print("递归 n = {}\n", .{n}); + if (n == 1) return; + linearRecur(n - 1); + } ``` ![space_complexity_recursive_linear](space_complexity.assets/space_complexity_recursive_linear.png) @@ -1000,7 +1048,22 @@ $$ === "Zig" ```zig title="space_complexity.zig" - + // 平方阶 + fn quadratic(n: i32) !void { + // 二维列表占用 O(n^2) 空间 + var nodes = std.ArrayList(std.ArrayList(i32)).init(std.heap.page_allocator); + defer nodes.deinit(); + var i: i32 = 0; + while (i < n) : (i += 1) { + var tmp = std.ArrayList(i32).init(std.heap.page_allocator); + defer tmp.deinit(); + var j: i32 = 0; + while (j < n) : (j += 1) { + try tmp.append(0); + } + try nodes.append(tmp); + } + } ``` 在以下递归函数中,同时存在 $n$ 个未返回的 `algorithm()` ,并且每个函数中都初始化了一个数组,长度分别为 $n, n-1, n-2, ..., 2, 1$ ,平均长度为 $\frac{n}{2}$ ,因此总体使用 $O(n^2)$ 空间。 @@ -1103,7 +1166,13 @@ $$ === "Zig" ```zig title="space_complexity.zig" - + // 平方阶(递归实现) + fn quadraticRecur(comptime n: i32) i32 { + if (n <= 0) return 0; + var nums = [_]i32{0}**n; + std.debug.print("递归 n = {} 中的 nums 长度 = {}\n", .{n, nums.len}); + return quadraticRecur(n - 1); + } ``` ![space_complexity_recursive_quadratic](space_complexity.assets/space_complexity_recursive_quadratic.png) @@ -1217,7 +1286,15 @@ $$ === "Zig" ```zig title="space_complexity.zig" - + // 指数阶(建立满二叉树) + fn buildTree(mem_allocator: std.mem.Allocator, n: i32) !?*inc.TreeNode(i32) { + if (n == 0) return null; + const root = try mem_allocator.create(inc.TreeNode(i32)); + root.init(0); + root.left = try buildTree(mem_allocator, n - 1); + root.right = try buildTree(mem_allocator, n - 1); + return root; + } ``` ![space_complexity_exponential](space_complexity.assets/space_complexity_exponential.png) diff --git a/docs/chapter_computational_complexity/space_time_tradeoff.md b/docs/chapter_computational_complexity/space_time_tradeoff.md index ddf178730..84dc1afc7 100644 --- a/docs/chapter_computational_complexity/space_time_tradeoff.md +++ b/docs/chapter_computational_complexity/space_time_tradeoff.md @@ -178,7 +178,23 @@ comments: true === "Zig" ```zig title="leetcode_two_sum.zig" - + const SolutionBruteForce = struct { + pub fn twoSum(self: *SolutionBruteForce, nums: []i32, target: i32) [2]i32 { + _ = self; + var size: usize = nums.len; + var i: usize = 0; + // 两层循环,时间复杂度 O(n^2) + while (i < size - 1) : (i += 1) { + var j = i + 1; + while (j < size) : (j += 1) { + if (nums[i] + nums[j] == target) { + return [_]i32{@intCast(i32, i), @intCast(i32, j)}; + } + } + } + return undefined; + } + }; ``` ### 方法二:辅助哈希表 @@ -347,5 +363,22 @@ comments: true === "Zig" ```zig title="leetcode_two_sum.zig" - + const SolutionHashMap = struct { + pub fn twoSum(self: *SolutionHashMap, nums: []i32, target: i32) ![2]i32 { + _ = self; + var size: usize = nums.len; + // 辅助哈希表,空间复杂度 O(n) + var dic = std.AutoHashMap(i32, i32).init(std.heap.page_allocator); + defer dic.deinit(); + var i: usize = 0; + // 单层循环,时间复杂度 O(n) + while (i < size) : (i += 1) { + if (dic.contains(target - nums[i])) { + return [_]i32{dic.get(target - nums[i]).?, @intCast(i32, i)}; + } + try dic.put(nums[i], @intCast(i32, i)); + } + return undefined; + } + }; ``` diff --git a/docs/chapter_computational_complexity/time_complexity.md b/docs/chapter_computational_complexity/time_complexity.md index c7eaa5336..3989f6219 100644 --- a/docs/chapter_computational_complexity/time_complexity.md +++ b/docs/chapter_computational_complexity/time_complexity.md @@ -914,7 +914,17 @@ $$ === "Zig" ```zig title="time_complexity.zig" - + // 常数阶 + fn constant(n: i32) i32 { + _ = n; + var count: i32 = 0; + const size: i32 = 100_000; + var i: i32 = 0; + while(i 0) : (i -= 1) { + var j: usize = 0; + // 内循环:冒泡操作 + while (j < i) : (j += 1) { + if (nums[j] > nums[j + 1]) { + // 交换 nums[j] 与 nums[j + 1] + var tmp = nums[j]; + nums[j] = nums[j + 1]; + nums[j + 1] = tmp; + count += 3; // 元素交换包含 3 个单元操作 + } + } + } + return count; + } ``` ### 指数阶 $O(2^n)$ @@ -1732,7 +1789,22 @@ $$ === "Zig" ```zig title="time_complexity.zig" - + // 指数阶(循环实现) + fn exponential(n: i32) i32{ + var count: i32 = 0; + var bas: i32 = 1; + var i: i32 = 0; + // cell 每轮一分为二,形成数列 1, 2, 4, 8, ..., 2^(n-1) + while (i < n) : (i += 1) { + var j: i32 = 0; + while (j < bas) : (j += 1) { + count += 1; + } + bas *= 2; + } + // count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1 + return count; + } ``` ![time_complexity_exponential](time_complexity.assets/time_complexity_exponential.png) @@ -1839,7 +1911,11 @@ $$ === "Zig" ```zig title="time_complexity.zig" - + // 指数阶(递归实现) + fn expRecur(n: i32) i32{ + if (n == 1) return 1; + return expRecur(n - 1) + expRecur(n - 1) + 1; + } ``` ### 对数阶 $O(\log n)$ @@ -1980,7 +2056,18 @@ $$ === "Zig" ```zig title="time_complexity.zig" - + // 对数阶(循环实现) + fn logarithmic(n: f32) i32 + { + var count: i32 = 0; + var n_var = n; + while (n_var > 1) + { + n_var = n_var / 2; + count +=1; + } + return count; + } ``` ![time_complexity_logarithmic](time_complexity.assets/time_complexity_logarithmic.png) @@ -2086,7 +2173,12 @@ $$ === "Zig" ```zig title="time_complexity.zig" - + // 对数阶(递归实现) + fn logRecur(n: f32) i32 + { + if (n <= 1) return 0; + return logRecur(n / 2) + 1; + } ``` ### 线性对数阶 $O(n \log n)$ @@ -2234,7 +2326,18 @@ $$ === "Zig" ```zig title="time_complexity.zig" - + // 线性对数阶 + fn linearLogRecur(n: f32) i32 + { + if (n <= 1) return 1; + var count: i32 = linearLogRecur(n / 2) + + linearLogRecur(n / 2); + var i: f32 = 0; + while (i < n) : (i += 1) { + count += 1; + } + return count; + } ``` ![time_complexity_logarithmic_linear](time_complexity.assets/time_complexity_logarithmic_linear.png) @@ -2392,7 +2495,17 @@ $$ === "Zig" ```zig title="time_complexity.zig" - + // 阶乘阶(递归实现) + fn factorialRecur(n: i32) i32 { + if (n == 0) return 1; + var count: i32 = 0; + var i: i32 = 0; + // 从 1 个分裂出 n 个 + while (i < n) : (i += 1) { + count += factorialRecur(n - 1); + } + return count; + } ``` ![time_complexity_factorial](time_complexity.assets/time_complexity_factorial.png) @@ -2687,7 +2800,28 @@ $$ === "Zig" ```zig title="worst_best_time_complexity.zig" + // 生成一个数组,元素为 { 1, 2, ..., n },顺序被打乱 + pub fn randomNumbers(comptime n: usize) [n]i32 { + var nums: [n]i32 = undefined; + // 生成数组 nums = { 1, 2, 3, ..., n } + for (nums) |*num, i| { + num.* = @intCast(i32, i) + 1; + } + // 随机打乱数组元素 + const rand = std.crypto.random; + rand.shuffle(i32, &nums); + return nums; + } + // 查找数组 nums 中数字 1 所在索引 + pub fn findOne(nums: []i32) i32 { + for (nums) |num, i| { + // 当元素 1 在数组头部时,达到最佳时间复杂度 O(1) + // 当元素 1 在数组尾部时,达到最差时间复杂度 O(n) + if (num == 1) return @intCast(i32, i); + } + return -1; + } ``` !!! tip