mirror of
https://github.com/krahets/hello-algo.git
synced 2024-12-25 02:06:28 +08:00
copy zig codes of chapter_array_and_linkedlist and chapter_computatio… (#319)
* copy zig codes of chapter_array_and_linkedlist and chapter_computational_complexity to markdown files * Update time_complexity.md --------- Co-authored-by: Yudong Jin <krahets@163.com>
This commit is contained in:
parent
b39b84acba
commit
15efaca85d
8 changed files with 566 additions and 41 deletions
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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. 数组典型应用
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
}
|
||||
```
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
|
|
@ -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<size) : (i += 1) {
|
||||
count += 1;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
```
|
||||
|
||||
### 线性阶 $O(n)$
|
||||
|
@ -1033,7 +1043,15 @@ $$
|
|||
=== "Zig"
|
||||
|
||||
```zig title="time_complexity.zig"
|
||||
|
||||
// 线性阶
|
||||
fn linear(n: i32) i32 {
|
||||
var count: i32 = 0;
|
||||
var i: i32 = 0;
|
||||
while (i < n) : (i += 1) {
|
||||
count += 1;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
```
|
||||
|
||||
「遍历数组」和「遍历链表」等操作,时间复杂度都为 $O(n)$ ,其中 $n$ 为数组或链表的长度。
|
||||
|
@ -1171,7 +1189,15 @@ $$
|
|||
=== "Zig"
|
||||
|
||||
```zig title="time_complexity.zig"
|
||||
|
||||
// 线性阶(遍历数组)
|
||||
fn arrayTraversal(nums: []i32) i32 {
|
||||
var count: i32 = 0;
|
||||
// 循环次数与数组长度成正比
|
||||
for (nums) |_| {
|
||||
count += 1;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
```
|
||||
|
||||
### 平方阶 $O(n^2)$
|
||||
|
@ -1325,7 +1351,19 @@ $$
|
|||
=== "Zig"
|
||||
|
||||
```zig title="time_complexity.zig"
|
||||
|
||||
// 平方阶
|
||||
fn quadratic(n: i32) i32 {
|
||||
var count: i32 = 0;
|
||||
var i: i32 = 0;
|
||||
// 循环次数与数组长度成平方关系
|
||||
while (i < n) : (i += 1) {
|
||||
var j: i32 = 0;
|
||||
while (j < n) : (j += 1) {
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
```
|
||||
|
||||
![time_complexity_constant_linear_quadratic](time_complexity.assets/time_complexity_constant_linear_quadratic.png)
|
||||
|
@ -1551,7 +1589,26 @@ $$
|
|||
=== "Zig"
|
||||
|
||||
```zig title="time_complexity.zig"
|
||||
|
||||
// 平方阶(冒泡排序)
|
||||
fn bubbleSort(nums: []i32) i32 {
|
||||
var count: i32 = 0; // 计数器
|
||||
// 外循环:待排序元素数量为 n-1, n-2, ..., 1
|
||||
var i: i32 = @intCast(i32, nums.len ) - 1;
|
||||
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
|
||||
|
|
Loading…
Reference in a new issue