From 80c5a0472af1c57e930ccde1238700179931067e Mon Sep 17 00:00:00 2001 From: xiongsp <37035570+xiongsp@users.noreply.github.com> Date: Sun, 8 Jan 2023 23:22:01 +0800 Subject: [PATCH 01/23] update Docker --- Dockerfile | 20 ++++++++++++++++++++ docker-compose.yml | 7 +++++++ 2 files changed, 27 insertions(+) create mode 100644 Dockerfile create mode 100644 docker-compose.yml diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..5d437c846 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,20 @@ +FROM python:3.9.0-buster + +RUN sed -i 's/deb.debian.org/mirrors.aliyun.com/g' /etc/apt/sources.list +RUN sed -i 's/security.debian.org/mirrors.aliyun.com/g' /etc/apt/sources.list + +RUN pip install --upgrade pip && pip install mkdocs mkdocs-material + +WORKDIR /app + +COPY codes /app/codes + +COPY docs /app/docs + +COPY mkdocs.yml /app/mkdocs.yml + +RUN mkdir ./docs/overrides && mkdocs build + +EXPOSE 8000 + +CMD ["mkdocs", "serve", "-a", "0.0.0.0:8000"] \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 000000000..6a472b173 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,7 @@ +version: '3' +services: + hello-algo: + build: . + ports: + - "8000:8000" + \ No newline at end of file From 777370a5f8ffb3e417f51699a1077cc4b679daad Mon Sep 17 00:00:00 2001 From: xiongsp <37035570+xiongsp@users.noreply.github.com> Date: Tue, 10 Jan 2023 12:12:10 +0800 Subject: [PATCH 02/23] Add instruction --- Dockerfile | 5 +---- README.md | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index 5d437c846..9d566fbe2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,9 +1,6 @@ FROM python:3.9.0-buster -RUN sed -i 's/deb.debian.org/mirrors.aliyun.com/g' /etc/apt/sources.list -RUN sed -i 's/security.debian.org/mirrors.aliyun.com/g' /etc/apt/sources.list - -RUN pip install --upgrade pip && pip install mkdocs mkdocs-material +RUN pip install -i https://pypi.tuna.tsinghua.edu.cn/simple --upgrade pip && pip install -i https://pypi.tuna.tsinghua.edu.cn/simple mkdocs mkdocs-material WORKDIR /app diff --git a/README.md b/README.md index 560e73953..8bb7b345d 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,28 @@ 如果感觉本书对你有所帮助,请点个 Star :star: 支持一下,谢谢! +### 本地部署 + +#### Docker + +使用本教程前,请确保Docker已经安装并启动。 + +根据如下命令离线部署。 + +```bash +git clone https://github.com/krahets/hello-algo.git +cd hello-algo +docker-compose up -d +``` + +稍等片刻,请访问`http://localhost:8000`即可访问本教程。 + +使用如下命令删除部署。 + +```bash +docker-compose down +``` + ## 推荐语 > “一本通俗易懂的数据结构与算法入门书,引导读者手脑并用地学习,强烈推荐算法初学者阅读。” From 38a3d913edf621ba3c968266d60832a5723b0daf Mon Sep 17 00:00:00 2001 From: xiongsp <37035570+xiongsp@users.noreply.github.com> Date: Tue, 10 Jan 2023 12:47:40 +0800 Subject: [PATCH 03/23] optimize the Dockerfile --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 9d566fbe2..813b4a37f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.9.0-buster +FROM python:3.9.0-alpine RUN pip install -i https://pypi.tuna.tsinghua.edu.cn/simple --upgrade pip && pip install -i https://pypi.tuna.tsinghua.edu.cn/simple mkdocs mkdocs-material From d1315f5cdcfcf2c0e760a61acf4cf84a33b2f74d Mon Sep 17 00:00:00 2001 From: Yudong Jin Date: Wed, 11 Jan 2023 03:37:14 +0800 Subject: [PATCH 04/23] Update Dockerfile --- Dockerfile | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index 813b4a37f..f699e33ea 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,17 +1,16 @@ FROM python:3.9.0-alpine -RUN pip install -i https://pypi.tuna.tsinghua.edu.cn/simple --upgrade pip && pip install -i https://pypi.tuna.tsinghua.edu.cn/simple mkdocs mkdocs-material +RUN pip install -i https://pypi.tuna.tsinghua.edu.cn/simple --upgrade pip +RUN pip install -i https://pypi.tuna.tsinghua.edu.cn/simple mkdocs-material==9.0.2 WORKDIR /app COPY codes /app/codes - COPY docs /app/docs - COPY mkdocs.yml /app/mkdocs.yml RUN mkdir ./docs/overrides && mkdocs build EXPOSE 8000 -CMD ["mkdocs", "serve", "-a", "0.0.0.0:8000"] \ No newline at end of file +CMD ["mkdocs", "serve", "-a", "0.0.0.0:8000"] From a1579f6f7e9294f34dcb8aa8b2bbebad8206fd5b Mon Sep 17 00:00:00 2001 From: sjinzh <99076655+sjinzh@users.noreply.github.com> Date: Wed, 11 Jan 2023 17:39:28 +0800 Subject: [PATCH 05/23] add zig codes for Section 'Array', 'LinkedList' and 'List' --- codes/zig/build.zig | 55 ++++++ .../chapter_array_and_linkedlist/array.zig | 149 ++++++++++++++ .../linked_list.zig | 89 +++++++++ .../zig/chapter_array_and_linkedlist/list.zig | 85 ++++++++ .../chapter_array_and_linkedlist/my_list.zig | 182 ++++++++++++++++++ codes/zig/include/PrintUtil.zig | 32 ++- 6 files changed, 589 insertions(+), 3 deletions(-) create mode 100644 codes/zig/chapter_array_and_linkedlist/array.zig create mode 100644 codes/zig/chapter_array_and_linkedlist/linked_list.zig create mode 100644 codes/zig/chapter_array_and_linkedlist/list.zig create mode 100644 codes/zig/chapter_array_and_linkedlist/my_list.zig diff --git a/codes/zig/build.zig b/codes/zig/build.zig index 1a9fb0fba..b9bbddce0 100644 --- a/codes/zig/build.zig +++ b/codes/zig/build.zig @@ -64,4 +64,59 @@ pub fn build(b: *std.build.Builder) void { if (b.args) |args| run_cmd_leetcode_two_sum.addArgs(args); const run_step_leetcode_two_sum = b.step("run_leetcode_two_sum", "Run leetcode_two_sum"); run_step_leetcode_two_sum.dependOn(&run_cmd_leetcode_two_sum.step); + + // Section: "Array" + // Source File: "chapter_array_and_linkedlist/array.zig" + // Run Command: zig build run_array + const exe_array = b.addExecutable("array", "chapter_array_and_linkedlist/array.zig"); + exe_array.addPackagePath("include", "include/include.zig"); + exe_array.setTarget(target); + exe_array.setBuildMode(mode); + exe_array.install(); + const run_cmd_array = exe_array.run(); + run_cmd_array.step.dependOn(b.getInstallStep()); + if (b.args) |args| run_cmd_array.addArgs(args); + const run_step_array = b.step("run_array", "Run array"); + run_step_array.dependOn(&run_cmd_array.step); + + // Section: "LinkedList" + // Source File: "chapter_array_and_linkedlist/linked_list.zig" + // Run Command: zig build run_linked_list + const exe_linked_list = b.addExecutable("linked_list", "chapter_array_and_linkedlist/linked_list.zig"); + exe_linked_list.addPackagePath("include", "include/include.zig"); + exe_linked_list.setTarget(target); + exe_linked_list.setBuildMode(mode); + exe_linked_list.install(); + const run_cmd_linked_list = exe_linked_list.run(); + run_cmd_linked_list.step.dependOn(b.getInstallStep()); + if (b.args) |args| run_cmd_linked_list.addArgs(args); + const run_step_linked_list = b.step("run_linked_list", "Run linked_list"); + run_step_linked_list.dependOn(&run_cmd_linked_list.step); + + // Section: "List" + // Source File: "chapter_array_and_linkedlist/list.zig" + // Run Command: zig build run_list + const exe_list = b.addExecutable("list", "chapter_array_and_linkedlist/list.zig"); + exe_list.addPackagePath("include", "include/include.zig"); + exe_list.setTarget(target); + exe_list.setBuildMode(mode); + exe_list.install(); + const run_cmd_list = exe_list.run(); + run_cmd_list.step.dependOn(b.getInstallStep()); + if (b.args) |args| run_cmd_list.addArgs(args); + const run_step_list = b.step("run_list", "Run list"); + run_step_list.dependOn(&run_cmd_list.step); + + // Source File: "chapter_array_and_linkedlist/my_list.zig" + // Run Command: zig build run_my_list + const exe_my_list = b.addExecutable("my_list", "chapter_array_and_linkedlist/my_list.zig"); + exe_my_list.addPackagePath("include", "include/include.zig"); + exe_my_list.setTarget(target); + exe_my_list.setBuildMode(mode); + exe_my_list.install(); + const run_cmd_my_list = exe_my_list.run(); + run_cmd_my_list.step.dependOn(b.getInstallStep()); + if (b.args) |args| run_cmd_my_list.addArgs(args); + const run_step_my_list = b.step("run_my_list", "Run my_list"); + run_step_my_list.dependOn(&run_cmd_my_list.step); } diff --git a/codes/zig/chapter_array_and_linkedlist/array.zig b/codes/zig/chapter_array_and_linkedlist/array.zig new file mode 100644 index 000000000..1206168e5 --- /dev/null +++ b/codes/zig/chapter_array_and_linkedlist/array.zig @@ -0,0 +1,149 @@ +// File: array.zig +// Created Time: 2023-01-07 +// Author: sjinzh (sjinzh@gmail.com) + +const std = @import("std"); +const inc = @import("include"); + +// 随机返回一个数组元素 +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; +} + +// 扩展数组长度(运行时方法) +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; +} + +// 扩展数组长度(编译期方法A:初始化新数组并进行元素拷贝) +pub fn extendComptimeA(comptime nums: anytype, comptime enlarge: i32) [nums.len + enlarge]i32 { + // 初始化一个扩展长度后的数组 + var res = [_]i32{0} ** (nums.len + enlarge); + // 将原数组中的所有元素复制到新数组 + for (nums) |num, i| { + res[i] = num; + } + // 返回扩展后的新数组 + return res; +} + +// 扩展数组长度(编译期方法B: 通过数组拼接运算符“++”) +pub fn extendComptimeB(comptime nums: anytype, comptime enlarge: i32) [nums.len + enlarge]i32 { + // 数组拼接操作 + var res = nums ++ [_]i32{0} ** enlarge; + // 返回拼接后的新数组 + return res; +} + +// 在数组的索引 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]; + } +} + +// 遍历数组 +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; + } +} + +// 在数组中查找指定元素 +pub fn find(nums: []i32, target: i32) i32 { + for (nums) |num, i| { + if (num == target) return @intCast(i32, i); + } + return -1; +} + +// Driver Code +pub fn main() !void { + // 查看本地CPU架构和操作系统信息 + var native_target_info = try std.zig.system.NativeTargetInfo.detect(std.zig.CrossTarget{}); + std.debug.print("Native Info: CPU Arch = {}, OS = {}\n", .{native_target_info.target.cpu.arch, native_target_info.target.os.tag}); + + // 初始化数组 + const size: i32 = 5; + var arr = [_]i32{0} ** size; + std.debug.print("数组 arr = ", .{}); + inc.PrintUtil.printArray(i32, &arr); + + var array = [_]i32{ 1, 3, 2, 5, 4 }; + std.debug.print("\n数组 nums = ", .{}); + inc.PrintUtil.printArray(i32, &array); + + // 随机访问 + var randomNum = randomAccess(&array); + 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); + // { + // // 长度扩展(编译期方法) + // comptime var array_comptime = [_]i32{ 1, 3, 2, 5, 4 }; + // var nums_comptime = extendComptimeA(array_comptime, 3); + // // var nums_comptime = extendComptimeB(array_comptime, 3); + // std.debug.print("\n将数组长度扩展至 8 ,得到 nums_comptime = ", .{}); + // inc.PrintUtil.printArray(i32, &nums_comptime); + // } + + // 插入元素 + insert(nums, 6, 3); + std.debug.print("\n在索引 3 处插入数字 6 ,得到 nums = ", .{}); + inc.PrintUtil.printArray(i32, nums); + + // 删除元素 + remove(nums, 2); + std.debug.print("\n删除索引 2 处的元素,得到 nums = ", .{}); + inc.PrintUtil.printArray(i32, nums); + + // 遍历数组 + traverse(nums); + + // 查找元素 + var index = find(nums, 3); + std.debug.print("\n在 nums_ext 中查找元素 3 ,得到索引 = {}\n", .{index}); + + const getchar = try std.io.getStdIn().reader().readByte(); + _ = getchar; +} + diff --git a/codes/zig/chapter_array_and_linkedlist/linked_list.zig b/codes/zig/chapter_array_and_linkedlist/linked_list.zig new file mode 100644 index 000000000..495ec3c66 --- /dev/null +++ b/codes/zig/chapter_array_and_linkedlist/linked_list.zig @@ -0,0 +1,89 @@ +// File: linked_list.zig +// Created Time: 2023-01-07 +// Author: sjinzh (sjinzh@gmail.com) + +const std = @import("std"); +const inc = @import("include"); + +// 在链表的结点 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; +} + +// 访问链表中索引为 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; +} + +// 在链表中查找值为 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; +} + +// Driver Code +pub fn main() !void { + // 查看本地CPU架构和操作系统信息 + var native_target_info = try std.zig.system.NativeTargetInfo.detect(std.zig.CrossTarget{}); + std.debug.print("Native Info: CPU Arch = {}, OS = {}\n", .{native_target_info.target.cpu.arch, native_target_info.target.os.tag}); + + // 初始化链表 + // 初始化各个结点 + 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; + std.debug.print("初始化的链表为", .{}); + try inc.PrintUtil.printLinkedList(i32, &n0); + + // 插入结点 + var tmp = inc.ListNode(i32){.val = 0}; + insert(&n0, &tmp); + std.debug.print("插入结点后的链表为", .{}); + try inc.PrintUtil.printLinkedList(i32, &n0); + + // 删除结点 + remove(&n0); + std.debug.print("删除结点后的链表为", .{}); + try inc.PrintUtil.printLinkedList(i32, &n0); + + // 访问结点 + var node = access(&n0, 3); + std.debug.print("链表中索引 3 处的结点的值 = {}\n", .{node.?.val}); + + // 查找结点 + var index = find(&n0, 2); + std.debug.print("链表中值为 2 的结点的索引 = {}\n", .{index}); + + const getchar = try std.io.getStdIn().reader().readByte(); + _ = getchar; +} \ No newline at end of file diff --git a/codes/zig/chapter_array_and_linkedlist/list.zig b/codes/zig/chapter_array_and_linkedlist/list.zig new file mode 100644 index 000000000..c27a641bd --- /dev/null +++ b/codes/zig/chapter_array_and_linkedlist/list.zig @@ -0,0 +1,85 @@ +// File: list.zig +// Created Time: 2023-01-07 +// Author: sjinzh (sjinzh@gmail.com) + +const std = @import("std"); +const inc = @import("include"); + +// Driver Code +pub fn main() !void { + // 查看本地CPU架构和操作系统信息 + var native_target_info = try std.zig.system.NativeTargetInfo.detect(std.zig.CrossTarget{}); + std.debug.print("Native Info: CPU Arch = {}, OS = {}\n", .{native_target_info.target.cpu.arch, native_target_info.target.os.tag}); + + // 初始化列表 + var list = std.ArrayList(i32).init(std.heap.page_allocator); + // 延迟释放内存 + defer list.deinit(); + try list.appendSlice(&[_]i32{ 1, 3, 2, 5, 4 }); + std.debug.print("列表 list = ", .{}); + inc.PrintUtil.printList(i32, list); + + // 访问元素 + var num = list.items[1]; + std.debug.print("\n访问索引 1 处的元素,得到 num = {}", .{num}); + + // 更新元素 + list.items[1] = 0; + std.debug.print("\n将索引 1 处的元素更新为 0 ,得到 list = ", .{}); + inc.PrintUtil.printList(i32, list); + + // 清空列表 + list.clearRetainingCapacity(); + std.debug.print("\n清空列表后 list = ", .{}); + inc.PrintUtil.printList(i32, list); + + // 尾部添加元素 + try list.append(1); + try list.append(3); + try list.append(2); + try list.append(5); + try list.append(4); + std.debug.print("\n添加元素后 list = ", .{}); + inc.PrintUtil.printList(i32, list); + + // 中间插入元素 + try list.insert(3, 6); + std.debug.print("\n在索引 3 处插入数字 6 ,得到 list = ", .{}); + inc.PrintUtil.printList(i32, list); + + // 删除元素 + var value = list.orderedRemove(3); + _ = value; + std.debug.print("\n删除索引 3 处的元素,得到 list = ", .{}); + inc.PrintUtil.printList(i32, list); + + // 通过索引遍历列表 + var count: i32 = 0; + var i: i32 = 0; + while (i < list.items.len) : (i += 1) { + count += 1; + } + + // 直接遍历列表元素 + count = 0; + for (list.items) |_| { + count += 1; + } + + // 拼接两个列表 + 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); + std.debug.print("\n将列表 list1 拼接到 list 之后,得到 list = ", .{}); + inc.PrintUtil.printList(i32, list); + + // 排序列表 + std.sort.sort(i32, list.items, {}, comptime std.sort.asc(i32)); + std.debug.print("\n排序列表后 list = ", .{}); + inc.PrintUtil.printList(i32, list); + + const getchar = try std.io.getStdIn().reader().readByte(); + _ = getchar; +} + diff --git a/codes/zig/chapter_array_and_linkedlist/my_list.zig b/codes/zig/chapter_array_and_linkedlist/my_list.zig new file mode 100644 index 000000000..21aa921f3 --- /dev/null +++ b/codes/zig/chapter_array_and_linkedlist/my_list.zig @@ -0,0 +1,182 @@ +// File: my_list.zig +// Created Time: 2023-01-08 +// Author: sjinzh (sjinzh@gmail.com) + +const std = @import("std"); +const inc = @import("include"); + +// 列表类简易实现 +// 编译期泛型 +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 >= self.size()) @panic("索引越界"); + return self.nums[index]; + } + + // 更新元素 + pub fn set(self: *Self, index: usize, num: T) void { + // 索引如果越界则抛出异常,下同 + if (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 >= 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 >= 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; + } + + }; +} + +// Driver Code +pub fn main() !void { + // 查看本地CPU架构和操作系统信息 + var native_target_info = try std.zig.system.NativeTargetInfo.detect(std.zig.CrossTarget{}); + std.debug.print("Native Info: CPU Arch = {}, OS = {}\n", .{native_target_info.target.cpu.arch, native_target_info.target.os.tag}); + + // 初始化列表 + var list = MyList(i32){}; + try list.init(std.heap.page_allocator); + // 延迟释放内存 + defer list.deinit(); + + // 尾部添加元素 + try list.add(1); + try list.add(3); + try list.add(2); + try list.add(5); + try list.add(4); + std.debug.print("列表 list = ", .{}); + inc.PrintUtil.printArray(i32, try list.toArray()); + std.debug.print(" ,容量 = {} ,长度 = {}", .{list.capacity(), list.size()}); + + // 中间插入元素 + try list.insert(3, 6); + std.debug.print("\n在索引 3 处插入数字 6 ,得到 list = ", .{}); + inc.PrintUtil.printArray(i32, try list.toArray()); + + // 删除元素 + _ = list.remove(3); + std.debug.print("\n删除索引 3 处的元素,得到 list = ", .{}); + inc.PrintUtil.printArray(i32, try list.toArray()); + + // 访问元素 + var num = list.get(1); + std.debug.print("\n访问索引 1 处的元素,得到 num = {}", .{num}); + + // 更新元素 + list.set(1, 0); + std.debug.print("\n将索引 1 处的元素更新为 0 ,得到 list = ", .{}); + inc.PrintUtil.printArray(i32, try list.toArray()); + + // 测试扩容机制 + list.set(1, 0); + var i: i32 = 0; + while (i < 10) : (i += 1) { + // 在 i = 5 时,列表长度将超出列表容量,此时触发扩容机制 + try list.add(i); + } + std.debug.print("\n扩容后的列表 list = ", .{}); + inc.PrintUtil.printArray(i32, try list.toArray()); + std.debug.print(" ,容量 = {} ,长度 = {}\n", .{list.capacity(), list.size()}); + + const getchar = try std.io.getStdIn().reader().readByte(); + _ = getchar; +} + diff --git a/codes/zig/include/PrintUtil.zig b/codes/zig/include/PrintUtil.zig index 9e6b18b05..4d2d20893 100644 --- a/codes/zig/include/PrintUtil.zig +++ b/codes/zig/include/PrintUtil.zig @@ -7,16 +7,42 @@ const ListNode = @import("ListNode.zig").ListNode; const TreeNode = @import("TreeNode.zig").TreeNode; // Print an array -// 编译期泛型 pub fn printArray(comptime T: type, nums: []T) void { std.debug.print("[", .{}); if (nums.len > 0) { for (nums) |num, j| { - std.debug.print("{}{s}", .{num, if (j == nums.len-1) "]\n" else ", " }); + std.debug.print("{}{s}", .{num, if (j == nums.len-1) "]" else ", " }); } } else { std.debug.print("]", .{}); - std.debug.print("\n", .{}); + } +} + +// Print a list +pub fn printList(comptime T: type, list: std.ArrayList(T)) void { + std.debug.print("[", .{}); + if (list.items.len > 0) { + for (list.items) |value, i| { + std.debug.print("{}{s}", .{value, if (i == list.items.len-1) "]" else ", " }); + } + } else { + std.debug.print("]", .{}); + } + +} + +// Print a linked list +pub fn printLinkedList(comptime T: type, node: ?*ListNode(T)) !void { + if (node == null) return; + var list = std.ArrayList(i32).init(std.heap.page_allocator); + defer list.deinit(); + var head = node; + while (head != null) { + try list.append(head.?.val); + head = head.?.next; + } + for (list.items) |value, i| { + std.debug.print("{}{s}", .{value, if (i == list.items.len-1) "\n" else "->" }); } } From 82f9542abe8cee81289c5adc1aac5dfaf82329b0 Mon Sep 17 00:00:00 2001 From: sjinzh <99076655+sjinzh@users.noreply.github.com> Date: Wed, 11 Jan 2023 17:43:52 +0800 Subject: [PATCH 06/23] add zig codes for Section 'Array', 'LinkedList' and 'List' --- codes/zig/chapter_array_and_linkedlist/array.zig | 4 ---- codes/zig/chapter_array_and_linkedlist/linked_list.zig | 4 ---- codes/zig/chapter_array_and_linkedlist/list.zig | 4 ---- codes/zig/chapter_array_and_linkedlist/my_list.zig | 4 ---- 4 files changed, 16 deletions(-) diff --git a/codes/zig/chapter_array_and_linkedlist/array.zig b/codes/zig/chapter_array_and_linkedlist/array.zig index 1206168e5..55000bf9b 100644 --- a/codes/zig/chapter_array_and_linkedlist/array.zig +++ b/codes/zig/chapter_array_and_linkedlist/array.zig @@ -90,10 +90,6 @@ pub fn find(nums: []i32, target: i32) i32 { // Driver Code pub fn main() !void { - // 查看本地CPU架构和操作系统信息 - var native_target_info = try std.zig.system.NativeTargetInfo.detect(std.zig.CrossTarget{}); - std.debug.print("Native Info: CPU Arch = {}, OS = {}\n", .{native_target_info.target.cpu.arch, native_target_info.target.os.tag}); - // 初始化数组 const size: i32 = 5; var arr = [_]i32{0} ** size; diff --git a/codes/zig/chapter_array_and_linkedlist/linked_list.zig b/codes/zig/chapter_array_and_linkedlist/linked_list.zig index 495ec3c66..fbaa3f8d7 100644 --- a/codes/zig/chapter_array_and_linkedlist/linked_list.zig +++ b/codes/zig/chapter_array_and_linkedlist/linked_list.zig @@ -46,10 +46,6 @@ pub fn find(node: ?*inc.ListNode(i32), target: i32) i32 { // Driver Code pub fn main() !void { - // 查看本地CPU架构和操作系统信息 - var native_target_info = try std.zig.system.NativeTargetInfo.detect(std.zig.CrossTarget{}); - std.debug.print("Native Info: CPU Arch = {}, OS = {}\n", .{native_target_info.target.cpu.arch, native_target_info.target.os.tag}); - // 初始化链表 // 初始化各个结点 var n0 = inc.ListNode(i32){.val = 1}; diff --git a/codes/zig/chapter_array_and_linkedlist/list.zig b/codes/zig/chapter_array_and_linkedlist/list.zig index c27a641bd..608bce96c 100644 --- a/codes/zig/chapter_array_and_linkedlist/list.zig +++ b/codes/zig/chapter_array_and_linkedlist/list.zig @@ -7,10 +7,6 @@ const inc = @import("include"); // Driver Code pub fn main() !void { - // 查看本地CPU架构和操作系统信息 - var native_target_info = try std.zig.system.NativeTargetInfo.detect(std.zig.CrossTarget{}); - std.debug.print("Native Info: CPU Arch = {}, OS = {}\n", .{native_target_info.target.cpu.arch, native_target_info.target.os.tag}); - // 初始化列表 var list = std.ArrayList(i32).init(std.heap.page_allocator); // 延迟释放内存 diff --git a/codes/zig/chapter_array_and_linkedlist/my_list.zig b/codes/zig/chapter_array_and_linkedlist/my_list.zig index 21aa921f3..fccd5ac65 100644 --- a/codes/zig/chapter_array_and_linkedlist/my_list.zig +++ b/codes/zig/chapter_array_and_linkedlist/my_list.zig @@ -126,10 +126,6 @@ pub fn MyList(comptime T: type) type { // Driver Code pub fn main() !void { - // 查看本地CPU架构和操作系统信息 - var native_target_info = try std.zig.system.NativeTargetInfo.detect(std.zig.CrossTarget{}); - std.debug.print("Native Info: CPU Arch = {}, OS = {}\n", .{native_target_info.target.cpu.arch, native_target_info.target.os.tag}); - // 初始化列表 var list = MyList(i32){}; try list.init(std.heap.page_allocator); From 2367318a28f33c45e77cdeae034a32b09c371f30 Mon Sep 17 00:00:00 2001 From: sjinzh <99076655+sjinzh@users.noreply.github.com> Date: Wed, 11 Jan 2023 18:05:42 +0800 Subject: [PATCH 07/23] add zig codes for Section 'Array', 'LinkedList' and 'List' --- codes/zig/chapter_array_and_linkedlist/array.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codes/zig/chapter_array_and_linkedlist/array.zig b/codes/zig/chapter_array_and_linkedlist/array.zig index 55000bf9b..52ebc8606 100644 --- a/codes/zig/chapter_array_and_linkedlist/array.zig +++ b/codes/zig/chapter_array_and_linkedlist/array.zig @@ -137,7 +137,7 @@ pub fn main() !void { // 查找元素 var index = find(nums, 3); - std.debug.print("\n在 nums_ext 中查找元素 3 ,得到索引 = {}\n", .{index}); + std.debug.print("\n在 nums 中查找元素 3 ,得到索引 = {}\n", .{index}); const getchar = try std.io.getStdIn().reader().readByte(); _ = getchar; From d75a3582b06ddd76494bfbb54a017ee18df02b63 Mon Sep 17 00:00:00 2001 From: sjinzh <99076655+sjinzh@users.noreply.github.com> Date: Wed, 11 Jan 2023 19:56:25 +0800 Subject: [PATCH 08/23] add zig codes for Section 'Array', 'LinkedList' and 'List' --- codes/zig/chapter_array_and_linkedlist/my_list.zig | 1 - 1 file changed, 1 deletion(-) diff --git a/codes/zig/chapter_array_and_linkedlist/my_list.zig b/codes/zig/chapter_array_and_linkedlist/my_list.zig index fccd5ac65..f018e61ca 100644 --- a/codes/zig/chapter_array_and_linkedlist/my_list.zig +++ b/codes/zig/chapter_array_and_linkedlist/my_list.zig @@ -120,7 +120,6 @@ pub fn MyList(comptime T: type) type { } return nums; } - }; } From 99f149493946e6c1bdef9c533273db527bc901e6 Mon Sep 17 00:00:00 2001 From: nuomi1 Date: Wed, 11 Jan 2023 23:48:10 +0800 Subject: [PATCH 09/23] feat: add Swift codes for queue article --- codes/swift/Package.swift | 6 + .../chapter_stack_and_queue/array_queue.swift | 116 ++++++++++++++++ .../linkedlist_queue.swift | 105 ++++++++++++++ .../swift/chapter_stack_and_queue/queue.swift | 39 ++++++ docs/chapter_stack_and_queue/queue.md | 129 ++++++++++++++++++ 5 files changed, 395 insertions(+) create mode 100644 codes/swift/chapter_stack_and_queue/array_queue.swift create mode 100644 codes/swift/chapter_stack_and_queue/linkedlist_queue.swift create mode 100644 codes/swift/chapter_stack_and_queue/queue.swift diff --git a/codes/swift/Package.swift b/codes/swift/Package.swift index 6d6eaa8a5..89c03d9f6 100644 --- a/codes/swift/Package.swift +++ b/codes/swift/Package.swift @@ -16,6 +16,9 @@ let package = Package( .executable(name: "stack", targets: ["stack"]), .executable(name: "linkedlist_stack", targets: ["linkedlist_stack"]), .executable(name: "array_stack", targets: ["array_stack"]), + .executable(name: "queue", targets: ["queue"]), + .executable(name: "linkedlist_queue", targets: ["linkedlist_queue"]), + .executable(name: "array_queue", targets: ["array_queue"]), ], targets: [ .target(name: "utils", path: "utils"), @@ -30,5 +33,8 @@ let package = Package( .executableTarget(name: "stack", path: "chapter_stack_and_queue", sources: ["stack.swift"]), .executableTarget(name: "linkedlist_stack", dependencies: ["utils"], path: "chapter_stack_and_queue", sources: ["linkedlist_stack.swift"]), .executableTarget(name: "array_stack", path: "chapter_stack_and_queue", sources: ["array_stack.swift"]), + .executableTarget(name: "queue", path: "chapter_stack_and_queue", sources: ["queue.swift"]), + .executableTarget(name: "linkedlist_queue", dependencies: ["utils"], path: "chapter_stack_and_queue", sources: ["linkedlist_queue.swift"]), + .executableTarget(name: "array_queue", path: "chapter_stack_and_queue", sources: ["array_queue.swift"]), ] ) diff --git a/codes/swift/chapter_stack_and_queue/array_queue.swift b/codes/swift/chapter_stack_and_queue/array_queue.swift new file mode 100644 index 000000000..d76dbc9c5 --- /dev/null +++ b/codes/swift/chapter_stack_and_queue/array_queue.swift @@ -0,0 +1,116 @@ +/** + * File: array_queue.swift + * Created Time: 2023-01-11 + * Author: nuomi1 (nuomi1@qq.com) + */ + +/* 基于环形数组实现的队列 */ +class ArrayQueue { + private var nums: [Int] // 用于存储队列元素的数组 + private var front = 0 // 头指针,指向队首 + private var rear = 0 // 尾指针,指向队尾 + 1 + + init(capacity: Int) { + // 初始化数组 + nums = Array(repeating: 0, count: capacity) + } + + /* 获取队列的容量 */ + func capacity() -> Int { + nums.count + } + + /* 获取队列的长度 */ + func size() -> Int { + let capacity = capacity() + // 由于将数组看作为环形,可能 rear < front ,因此需要取余数 + return (capacity + rear - front) % capacity + } + + /* 判断队列是否为空 */ + func isEmpty() -> Bool { + rear - front == 0 + } + + /* 入队 */ + func offer(num: Int) { + if size() == capacity() { + print("队列已满") + return + } + // 尾结点后添加 num + nums[rear] = num + // 尾指针向后移动一位,越过尾部后返回到数组头部 + rear = (rear + 1) % capacity() + } + + /* 出队 */ + @discardableResult + func poll() -> Int { + let num = peek() + // 队头指针向后移动一位,若越过尾部则返回到数组头部 + front = (front + 1) % capacity() + return num + } + + /* 访问队首元素 */ + func peek() -> Int { + if isEmpty() { + fatalError("队列为空") + } + return nums[front] + } + + /* 返回数组 */ + func toArray() -> [Int] { + let size = size() + let capacity = capacity() + // 仅转换有效长度范围内的列表元素 + var res = Array(repeating: 0, count: size) + for (i, j) in sequence(first: (0, front), next: { $0 < size - 1 ? ($0 + 1, $1 + 1) : nil }) { + res[i] = nums[j % capacity] + } + return res + } +} + +@main +enum _ArrayQueue { + /* Driver Code */ + static func main() { + /* 初始化队列 */ + let capacity = 10 + let queue = ArrayQueue(capacity: capacity) + + /* 元素入队 */ + queue.offer(num: 1) + queue.offer(num: 3) + queue.offer(num: 2) + queue.offer(num: 5) + queue.offer(num: 4) + print("队列 queue = \(queue.toArray())") + + /* 访问队首元素 */ + let peek = queue.peek() + print("队首元素 peek = \(peek)") + + /* 元素出队 */ + let poll = queue.poll() + print("出队元素 poll = \(poll),出队后 queue = \(queue.toArray())") + + /* 获取队列的长度 */ + let size = queue.size() + print("队列长度 size = \(size)") + + /* 判断队列是否为空 */ + let isEmpty = queue.isEmpty() + print("队列是否为空 = \(isEmpty)") + + /* 测试环形数组 */ + for i in 0 ..< 10 { + queue.offer(num: i) + queue.poll() + print("第 \(i) 轮入队 + 出队后 queue = \(queue.toArray())") + } + } +} diff --git a/codes/swift/chapter_stack_and_queue/linkedlist_queue.swift b/codes/swift/chapter_stack_and_queue/linkedlist_queue.swift new file mode 100644 index 000000000..1d671742b --- /dev/null +++ b/codes/swift/chapter_stack_and_queue/linkedlist_queue.swift @@ -0,0 +1,105 @@ +/** + * File: linkedlist_queue.swift + * Created Time: 2023-01-11 + * Author: nuomi1 (nuomi1@qq.com) + */ + +import utils + +/* 基于链表实现的队列 */ +class LinkedListQueue { + private var front: ListNode? // 头结点 + private var rear: ListNode? // 尾结点 + private var _size = 0 + + init() {} + + /* 获取队列的长度 */ + func size() -> Int { + _size + } + + /* 判断队列是否为空 */ + func isEmpty() -> Bool { + size() == 0 + } + + /* 入队 */ + func offer(num: Int) { + // 尾结点后添加 num + let node = ListNode(x: num) + // 如果队列为空,则令头、尾结点都指向该结点 + if front == nil { + front = node + rear = node + } + // 如果队列不为空,则将该结点添加到尾结点后 + else { + rear?.next = node + rear = node + } + _size += 1 + } + + /* 出队 */ + @discardableResult + func poll() -> Int { + let num = peek() + // 删除头结点 + front = front?.next + _size -= 1 + return num + } + + /* 访问队首元素 */ + func peek() -> Int { + if isEmpty() { + fatalError("队列为空") + } + return front!.val + } + + /* 将链表转化为 Array 并返回 */ + func toArray() -> [Int] { + var node = front + var res = Array(repeating: 0, count: size()) + for i in res.indices { + res[i] = node!.val + node = node?.next + } + return res + } +} + +@main +enum _LinkedListQueue { + /* Driver Code */ + static func main() { + /* 初始化队列 */ + let queue = LinkedListQueue() + + /* 元素入队 */ + queue.offer(num: 1) + queue.offer(num: 3) + queue.offer(num: 2) + queue.offer(num: 5) + queue.offer(num: 4) + print("队列 queue = \(queue.toArray())") + + /* 访问队首元素 */ + let peek = queue.peek() + print("队首元素 peek = \(peek)") + + /* 元素出队 */ + let poll = queue.poll() + print("出队元素 poll = \(poll),出队后 queue = \(queue.toArray())") + + /* 获取队列的长度 */ + let size = queue.size() + print("队列长度 size = \(size)") + + /* 判断队列是否为空 */ + let isEmpty = queue.isEmpty() + print("队列是否为空 = \(isEmpty)") + } +} diff --git a/codes/swift/chapter_stack_and_queue/queue.swift b/codes/swift/chapter_stack_and_queue/queue.swift new file mode 100644 index 000000000..f13af36f5 --- /dev/null +++ b/codes/swift/chapter_stack_and_queue/queue.swift @@ -0,0 +1,39 @@ +/** + * File: queue.swift + * Created Time: 2023-01-11 + * Author: nuomi1 (nuomi1@qq.com) + */ + +@main +enum Queue { + /* Driver Code */ + static func main() { + /* 初始化队列 */ + // Swift 没有内置的队列类,可以把 Array 当作队列来使用 + var queue: [Int] = [] + + /* 元素入队 */ + queue.append(1) + queue.append(3) + queue.append(2) + queue.append(5) + queue.append(4) + print("队列 queue = \(queue)") + + /* 访问队首元素 */ + let peek = queue.first! + print("队首元素 peek = \(peek)") + + /* 元素出队 */ + let pool = queue.removeFirst() + print("出队元素 poll = \(pool),出队后 queue = \(queue)") + + /* 获取队列的长度 */ + let size = queue.count + print("队列长度 size = \(size)") + + /* 判断队列是否为空 */ + let isEmpty = queue.isEmpty + print("队列是否为空 = \(isEmpty)") + } +} diff --git a/docs/chapter_stack_and_queue/queue.md b/docs/chapter_stack_and_queue/queue.md index 0c589fa69..08cf76d78 100644 --- a/docs/chapter_stack_and_queue/queue.md +++ b/docs/chapter_stack_and_queue/queue.md @@ -231,7 +231,28 @@ comments: true === "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! + + /* 元素出队 */ + let pool = queue.removeFirst() + + /* 获取队列的长度 */ + let size = queue.count + + /* 判断队列是否为空 */ + let isEmpty = queue.isEmpty ``` ## 队列实现 @@ -627,7 +648,59 @@ comments: true === "Swift" ```swift title="linkedlist_queue.swift" + /* 基于链表实现的队列 */ + class LinkedListQueue { + private var front: ListNode? // 头结点 + private var rear: ListNode? // 尾结点 + private var _size = 0 + init() {} + + /* 获取队列的长度 */ + func size() -> Int { + _size + } + + /* 判断队列是否为空 */ + func isEmpty() -> Bool { + size() == 0 + } + + /* 入队 */ + func offer(num: Int) { + // 尾结点后添加 num + let node = ListNode(x: num) + // 如果队列为空,则令头、尾结点都指向该结点 + if front == nil { + front = node + rear = node + } + // 如果队列不为空,则将该结点添加到尾结点后 + else { + rear?.next = node + rear = node + } + _size += 1 + } + + /* 出队 */ + @discardableResult + func poll() -> Int { + let num = peek() + // 删除头结点 + front = front?.next + _size -= 1 + return num + } + + /* 访问队首元素 */ + func peek() -> Int { + if isEmpty() { + fatalError("队列为空") + } + return front!.val + } + } ``` ### 基于数组的实现 @@ -1042,7 +1115,63 @@ comments: true === "Swift" ```swift title="array_queue.swift" + /* 基于环形数组实现的队列 */ + class ArrayQueue { + private var nums: [Int] // 用于存储队列元素的数组 + private var front = 0 // 头指针,指向队首 + private var rear = 0 // 尾指针,指向队尾 + 1 + init(capacity: Int) { + // 初始化数组 + nums = Array(repeating: 0, count: capacity) + } + + /* 获取队列的容量 */ + func capacity() -> Int { + nums.count + } + + /* 获取队列的长度 */ + func size() -> Int { + let capacity = capacity() + // 由于将数组看作为环形,可能 rear < front ,因此需要取余数 + return (capacity + rear - front) % capacity + } + + /* 判断队列是否为空 */ + func isEmpty() -> Bool { + rear - front == 0 + } + + /* 入队 */ + func offer(num: Int) { + if size() == capacity() { + print("队列已满") + return + } + // 尾结点后添加 num + nums[rear] = num + // 尾指针向后移动一位,越过尾部后返回到数组头部 + rear = (rear + 1) % capacity() + } + + /* 出队 */ + @discardableResult + func poll() -> Int { + let num = peek() + // 队头指针向后移动一位,若越过尾部则返回到数组头部 + front = (front + 1) % capacity() + return num + } + + /* 访问队首元素 */ + func peek() -> Int { + if isEmpty() { + fatalError("队列为空") + } + return nums[front] + } + } ``` ## 队列典型应用 From ac7d26c131c834d10fe951cd3615264a97a5f4fc Mon Sep 17 00:00:00 2001 From: nuomi1 Date: Wed, 11 Jan 2023 23:54:04 +0800 Subject: [PATCH 10/23] refactor: use internal method and remove toArray() method --- .../chapter_stack_and_queue/array_stack.swift | 5 ++-- .../linkedlist_stack.swift | 5 ++-- docs/chapter_stack_and_queue/stack.md | 26 +++++-------------- 3 files changed, 12 insertions(+), 24 deletions(-) diff --git a/codes/swift/chapter_stack_and_queue/array_stack.swift b/codes/swift/chapter_stack_and_queue/array_stack.swift index faeeaa173..144ebec95 100644 --- a/codes/swift/chapter_stack_and_queue/array_stack.swift +++ b/codes/swift/chapter_stack_and_queue/array_stack.swift @@ -29,8 +29,9 @@ class ArrayStack { } /* 出栈 */ + @discardableResult func pop() -> Int { - if stack.isEmpty { + if isEmpty() { fatalError("栈为空") } return stack.removeLast() @@ -38,7 +39,7 @@ class ArrayStack { /* 访问栈顶元素 */ func peek() -> Int { - if stack.isEmpty { + if isEmpty() { fatalError("栈为空") } return stack.last! diff --git a/codes/swift/chapter_stack_and_queue/linkedlist_stack.swift b/codes/swift/chapter_stack_and_queue/linkedlist_stack.swift index 9d3c98b01..97f7dabcc 100644 --- a/codes/swift/chapter_stack_and_queue/linkedlist_stack.swift +++ b/codes/swift/chapter_stack_and_queue/linkedlist_stack.swift @@ -20,7 +20,7 @@ class LinkedListStack { /* 判断栈是否为空 */ func isEmpty() -> Bool { - _size == 0 + size() == 0 } /* 入栈 */ @@ -32,6 +32,7 @@ class LinkedListStack { } /* 出栈 */ + @discardableResult func pop() -> Int { let num = peek() _peek = _peek?.next @@ -41,7 +42,7 @@ class LinkedListStack { /* 访问栈顶元素 */ func peek() -> Int { - if _size == 0 { + if isEmpty() { fatalError("栈为空") } return _peek!.val diff --git a/docs/chapter_stack_and_queue/stack.md b/docs/chapter_stack_and_queue/stack.md index 04825695c..7ff3f15bf 100644 --- a/docs/chapter_stack_and_queue/stack.md +++ b/docs/chapter_stack_and_queue/stack.md @@ -641,7 +641,7 @@ comments: true /* 判断栈是否为空 */ func isEmpty() -> Bool { - _size == 0 + size() == 0 } /* 入栈 */ @@ -653,6 +653,7 @@ comments: true } /* 出栈 */ + @discardableResult func pop() -> Int { let num = peek() _peek = _peek?.next @@ -662,22 +663,11 @@ comments: true /* 访问栈顶元素 */ func peek() -> Int { - if _size == 0 { + if isEmpty() { fatalError("栈为空") } return _peek!.val } - - /* 将 List 转化为 Array 并返回 */ - func toArray() -> [Int] { - var node = _peek - var res = Array(repeating: 0, count: _size) - for i in sequence(first: res.count - 1, next: { $0 >= 0 + 1 ? $0 - 1 : nil }) { - res[i] = node!.val - node = node?.next - } - return res - } } ``` @@ -994,8 +984,9 @@ comments: true } /* 出栈 */ + @discardableResult func pop() -> Int { - if stack.isEmpty { + if isEmpty() { fatalError("栈为空") } return stack.removeLast() @@ -1003,16 +994,11 @@ comments: true /* 访问栈顶元素 */ func peek() -> Int { - if stack.isEmpty { + if isEmpty() { fatalError("栈为空") } return stack.last! } - - /* 将 List 转化为 Array 并返回 */ - func toArray() -> [Int] { - stack - } } ``` From e6d1f2fd10fc67bb6b9ed722c95043e2b56c6e48 Mon Sep 17 00:00:00 2001 From: xiongsp <37035570+xiongsp@users.noreply.github.com> Date: Thu, 12 Jan 2023 10:36:54 +0800 Subject: [PATCH 11/23] Move tutorial to docs --- README.md | 22 ---------------------- docs/chapter_preface/contribution.md | 22 ++++++++++++++++++++++ 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 8bb7b345d..560e73953 100644 --- a/README.md +++ b/README.md @@ -41,28 +41,6 @@ 如果感觉本书对你有所帮助,请点个 Star :star: 支持一下,谢谢! -### 本地部署 - -#### Docker - -使用本教程前,请确保Docker已经安装并启动。 - -根据如下命令离线部署。 - -```bash -git clone https://github.com/krahets/hello-algo.git -cd hello-algo -docker-compose up -d -``` - -稍等片刻,请访问`http://localhost:8000`即可访问本教程。 - -使用如下命令删除部署。 - -```bash -docker-compose down -``` - ## 推荐语 > “一本通俗易懂的数据结构与算法入门书,引导读者手脑并用地学习,强烈推荐算法初学者阅读。” diff --git a/docs/chapter_preface/contribution.md b/docs/chapter_preface/contribution.md index c710e6de5..892da0b4c 100644 --- a/docs/chapter_preface/contribution.md +++ b/docs/chapter_preface/contribution.md @@ -41,4 +41,26 @@ comments: true 非常欢迎您和我一同来创作本书! +## 离线部署 + +### Docker + +使用本教程前,请确保Docker已经安装并启动。 + +根据如下命令离线部署。 + +```bash +git clone https://github.com/krahets/hello-algo.git +cd hello-algo +docker-compose up -d +``` + +稍等片刻,请访问`http://localhost:8000`即可访问本教程。 + +使用如下命令删除部署。 + +```bash +docker-compose down +``` + (TODO:教学视频) From dea3d0fa921a495bc2e9f8f4b537635e49d7e727 Mon Sep 17 00:00:00 2001 From: fbigm Date: Thu, 12 Jan 2023 11:32:32 +0800 Subject: [PATCH 12/23] =?UTF-8?q?=E6=8F=8F=E8=BF=B0=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 若将上图的「结点 2」看作父结点-->若将下图的「结点 2」看作父结点 --- docs/chapter_tree/binary_tree.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/chapter_tree/binary_tree.md b/docs/chapter_tree/binary_tree.md index 1061e019d..0201f6a2a 100644 --- a/docs/chapter_tree/binary_tree.md +++ b/docs/chapter_tree/binary_tree.md @@ -114,7 +114,7 @@ comments: true 结点的两个指针分别指向「左子结点 Left Child Node」和「右子结点 Right Child Node」,并且称该结点为两个子结点的「父结点 Parent Node」。给定二叉树某结点,将左子结点以下的树称为该结点的「左子树 Left Subtree」,右子树同理。 -除了叶结点外,每个结点都有子结点和子树。例如,若将上图的「结点 2」看作父结点,那么其左子结点和右子结点分别为「结点 4」和「结点 5」,左子树和右子树分别为「结点 4 及其以下结点形成的树」和「结点 5 及其以下结点形成的树」。 +除了叶结点外,每个结点都有子结点和子树。例如,若将下图的「结点 2」看作父结点,那么其左子结点和右子结点分别为「结点 4」和「结点 5」,左子树和右子树分别为「结点 4 及其以下结点形成的树」和「结点 5 及其以下结点形成的树」。 ![binary_tree_definition](binary_tree.assets/binary_tree_definition.png) From ebf049f2c6ebb903f67cd1e8c7cb47edfc5a58ef Mon Sep 17 00:00:00 2001 From: Yudong Jin Date: Thu, 12 Jan 2023 13:50:02 +0800 Subject: [PATCH 13/23] Update contribution.md --- docs/chapter_preface/contribution.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/chapter_preface/contribution.md b/docs/chapter_preface/contribution.md index 892da0b4c..4e5f3dc85 100644 --- a/docs/chapter_preface/contribution.md +++ b/docs/chapter_preface/contribution.md @@ -45,7 +45,7 @@ comments: true ### Docker -使用本教程前,请确保Docker已经安装并启动。 +使用本教程前,请确保 Docker 已经安装并启动。 根据如下命令离线部署。 @@ -55,7 +55,7 @@ cd hello-algo docker-compose up -d ``` -稍等片刻,请访问`http://localhost:8000`即可访问本教程。 +稍等片刻,即可通过浏览器打开 `http://localhost:8000` 访问本教程。 使用如下命令删除部署。 From 8325ce44748bea5a366e10619c8828083ced959c Mon Sep 17 00:00:00 2001 From: sjinzh <99076655+sjinzh@users.noreply.github.com> Date: Thu, 12 Jan 2023 17:08:33 +0800 Subject: [PATCH 14/23] update zig codes for Section 'Array', 'LinkedList', 'List' and 'Stack' --- .../chapter_array_and_linkedlist/array.zig | 32 ++----------------- 1 file changed, 2 insertions(+), 30 deletions(-) diff --git a/codes/zig/chapter_array_and_linkedlist/array.zig b/codes/zig/chapter_array_and_linkedlist/array.zig index 52ebc8606..8c830cf4d 100644 --- a/codes/zig/chapter_array_and_linkedlist/array.zig +++ b/codes/zig/chapter_array_and_linkedlist/array.zig @@ -14,7 +14,7 @@ pub fn randomAccess(nums: []i32) i32 { return randomNum; } -// 扩展数组长度(运行时方法) +// 扩展数组长度 pub fn extend(mem_allocator: std.mem.Allocator, nums: []i32, enlarge: usize) ![]i32 { // 初始化一个扩展长度后的数组 var res = try mem_allocator.alloc(i32, nums.len + enlarge); @@ -25,26 +25,6 @@ pub fn extend(mem_allocator: std.mem.Allocator, nums: []i32, enlarge: usize) ![] return res; } -// 扩展数组长度(编译期方法A:初始化新数组并进行元素拷贝) -pub fn extendComptimeA(comptime nums: anytype, comptime enlarge: i32) [nums.len + enlarge]i32 { - // 初始化一个扩展长度后的数组 - var res = [_]i32{0} ** (nums.len + enlarge); - // 将原数组中的所有元素复制到新数组 - for (nums) |num, i| { - res[i] = num; - } - // 返回扩展后的新数组 - return res; -} - -// 扩展数组长度(编译期方法B: 通过数组拼接运算符“++”) -pub fn extendComptimeB(comptime nums: anytype, comptime enlarge: i32) [nums.len + enlarge]i32 { - // 数组拼接操作 - var res = nums ++ [_]i32{0} ** enlarge; - // 返回拼接后的新数组 - return res; -} - // 在数组的索引 index 处插入元素 num pub fn insert(nums: []i32, num: i32, index: usize) void { // 把索引 index 以及之后的所有元素向后移动一位 @@ -104,7 +84,7 @@ pub fn main() !void { var randomNum = randomAccess(&array); 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); @@ -113,14 +93,6 @@ pub fn main() !void { nums = try extend(mem_allocator, nums, 3); std.debug.print("\n将数组长度扩展至 8 ,得到 nums = ", .{}); inc.PrintUtil.printArray(i32, nums); - // { - // // 长度扩展(编译期方法) - // comptime var array_comptime = [_]i32{ 1, 3, 2, 5, 4 }; - // var nums_comptime = extendComptimeA(array_comptime, 3); - // // var nums_comptime = extendComptimeB(array_comptime, 3); - // std.debug.print("\n将数组长度扩展至 8 ,得到 nums_comptime = ", .{}); - // inc.PrintUtil.printArray(i32, &nums_comptime); - // } // 插入元素 insert(nums, 6, 3); From 8726934f94079ee87252281fe1bf3ad7470c8a9b Mon Sep 17 00:00:00 2001 From: sjinzh <99076655+sjinzh@users.noreply.github.com> Date: Thu, 12 Jan 2023 21:51:11 +0800 Subject: [PATCH 15/23] add zig codes for Section 'Stack', 'Bubble Sort' and 'Insertion Sort' --- codes/zig/build.zig | 68 ++++++++++ codes/zig/chapter_sorting/bubble_sort.zig | 62 +++++++++ codes/zig/chapter_sorting/insertion_sort.zig | 33 +++++ .../chapter_stack_and_queue/array_stack.zig | 99 +++++++++++++++ .../linkedlist_stack.zig | 120 ++++++++++++++++++ codes/zig/chapter_stack_and_queue/stack.zig | 44 +++++++ 6 files changed, 426 insertions(+) create mode 100644 codes/zig/chapter_sorting/bubble_sort.zig create mode 100644 codes/zig/chapter_sorting/insertion_sort.zig create mode 100644 codes/zig/chapter_stack_and_queue/array_stack.zig create mode 100644 codes/zig/chapter_stack_and_queue/linkedlist_stack.zig create mode 100644 codes/zig/chapter_stack_and_queue/stack.zig diff --git a/codes/zig/build.zig b/codes/zig/build.zig index b9bbddce0..a8738fae9 100644 --- a/codes/zig/build.zig +++ b/codes/zig/build.zig @@ -119,4 +119,72 @@ pub fn build(b: *std.build.Builder) void { if (b.args) |args| run_cmd_my_list.addArgs(args); const run_step_my_list = b.step("run_my_list", "Run my_list"); run_step_my_list.dependOn(&run_cmd_my_list.step); + + // Section: "Stack" + // Source File: "chapter_stack_and_queue/stack.zig" + // Run Command: zig build run_stack + const exe_stack = b.addExecutable("stack", "chapter_stack_and_queue/stack.zig"); + exe_stack.addPackagePath("include", "include/include.zig"); + exe_stack.setTarget(target); + exe_stack.setBuildMode(mode); + exe_stack.install(); + const run_cmd_stack = exe_stack.run(); + run_cmd_stack.step.dependOn(b.getInstallStep()); + if (b.args) |args| run_cmd_stack.addArgs(args); + const run_step_stack = b.step("run_stack", "Run stack"); + run_step_stack.dependOn(&run_cmd_stack.step); + + // Source File: "chapter_stack_and_queue/linkedlist_stack.zig" + // Run Command: zig build run_linkedlist_stack + const exe_linkedlist_stack = b.addExecutable("linkedlist_stack", "chapter_stack_and_queue/linkedlist_stack.zig"); + exe_linkedlist_stack.addPackagePath("include", "include/include.zig"); + exe_linkedlist_stack.setTarget(target); + exe_linkedlist_stack.setBuildMode(mode); + exe_linkedlist_stack.install(); + const run_cmd_linkedlist_stack = exe_linkedlist_stack.run(); + run_cmd_linkedlist_stack.step.dependOn(b.getInstallStep()); + if (b.args) |args| run_cmd_linkedlist_stack.addArgs(args); + const run_step_linkedlist_stack = b.step("run_linkedlist_stack", "Run linkedlist_stack"); + run_step_linkedlist_stack.dependOn(&run_cmd_linkedlist_stack.step); + + // Source File: "chapter_stack_and_queue/array_stack.zig" + // Run Command: zig build run_array_stack + const exe_array_stack = b.addExecutable("array_stack", "chapter_stack_and_queue/array_stack.zig"); + exe_array_stack.addPackagePath("include", "include/include.zig"); + exe_array_stack.setTarget(target); + exe_array_stack.setBuildMode(mode); + exe_array_stack.install(); + const run_cmd_array_stack = exe_linkedlist_stack.run(); + run_cmd_array_stack.step.dependOn(b.getInstallStep()); + if (b.args) |args| run_cmd_array_stack.addArgs(args); + const run_step_array_stack = b.step("run_array_stack", "Run array_stack"); + run_step_array_stack.dependOn(&run_cmd_array_stack.step); + + // Section: "Bubble Sort" + // Source File: "chapter_sorting/bubble_sort.zig" + // Run Command: zig build run_bubble_sort + const exe_bubble_sort = b.addExecutable("bubble_sort", "chapter_sorting/bubble_sort.zig"); + exe_bubble_sort.addPackagePath("include", "include/include.zig"); + exe_bubble_sort.setTarget(target); + exe_bubble_sort.setBuildMode(mode); + exe_bubble_sort.install(); + const run_cmd_bubble_sort = exe_bubble_sort.run(); + run_cmd_bubble_sort.step.dependOn(b.getInstallStep()); + if (b.args) |args| run_cmd_bubble_sort.addArgs(args); + const run_step_bubble_sort = b.step("run_bubble_sort", "Run bubble_sort"); + run_step_bubble_sort.dependOn(&run_cmd_bubble_sort.step); + + // Section: "Insertion Sort" + // Source File: "chapter_sorting/insertion_sort.zig" + // Run Command: zig build run_insertion_sort + const exe_insertion_sort = b.addExecutable("insertion_sort", "chapter_sorting/insertion_sort.zig"); + exe_insertion_sort.addPackagePath("include", "include/include.zig"); + exe_insertion_sort.setTarget(target); + exe_insertion_sort.setBuildMode(mode); + exe_insertion_sort.install(); + const run_cmd_insertion_sort = exe_insertion_sort.run(); + run_cmd_insertion_sort.step.dependOn(b.getInstallStep()); + if (b.args) |args| run_cmd_insertion_sort.addArgs(args); + const run_step_insertion_sort = b.step("run_insertion_sort", "Run insertion_sort"); + run_step_insertion_sort.dependOn(&run_cmd_insertion_sort.step); } diff --git a/codes/zig/chapter_sorting/bubble_sort.zig b/codes/zig/chapter_sorting/bubble_sort.zig new file mode 100644 index 000000000..863190963 --- /dev/null +++ b/codes/zig/chapter_sorting/bubble_sort.zig @@ -0,0 +1,62 @@ +// File: time_complexity.zig +// Created Time: 2023-01-08 +// Author: sjinzh (sjinzh@gmail.com) + +const std = @import("std"); +const inc = @import("include"); + +// 冒泡排序 +fn bubbleSort(nums: []i32) void { + // 外循环:待排序元素数量为 n-1, n-2, ..., 1 + var i: usize = 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; + } + } + } +} + +// 冒泡排序(标志优化) +fn bubbleSortWithFlag(nums: []i32) void { + // 外循环:待排序元素数量为 n-1, n-2, ..., 1 + var i: usize = nums.len - 1; + while (i > 0) : (i -= 1) { + var flag = false; // 初始化标志位 + 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; + flag = true; + } + } + if (!flag) break; // 此轮冒泡未交换任何元素,直接跳出 + } +} + +// Driver Code +pub fn main() !void { + var nums = [_]i32{ 4, 1, 3, 1, 5, 2 }; + bubbleSort(&nums); + std.debug.print("冒泡排序完成后 nums = ", .{}); + inc.PrintUtil.printArray(i32, &nums); + + var nums1 = [_]i32{ 4, 1, 3, 1, 5, 2 }; + bubbleSortWithFlag(&nums1); + std.debug.print("\n冒泡排序完成后 nums1 = ", .{}); + inc.PrintUtil.printArray(i32, &nums1); + + const getchar = try std.io.getStdIn().reader().readByte(); + _ = getchar; +} + diff --git a/codes/zig/chapter_sorting/insertion_sort.zig b/codes/zig/chapter_sorting/insertion_sort.zig new file mode 100644 index 000000000..3c7594c24 --- /dev/null +++ b/codes/zig/chapter_sorting/insertion_sort.zig @@ -0,0 +1,33 @@ +// File: time_complexity.zig +// Created Time: 2023-01-08 +// Author: sjinzh (sjinzh@gmail.com) + +const std = @import("std"); +const inc = @import("include"); + +// 插入排序 +fn insertionSort(nums: []i32) void { + // 外循环:base = nums[1], nums[2], ..., nums[n-1] + var i: usize = 1; + while (i < nums.len) : (i += 1) { + var base = nums[i]; + var j: usize = i; + // 内循环:将 base 插入到左边的正确位置 + while (j >= 1 and nums[j - 1] > base) : (j -= 1) { + nums[j] = nums[j - 1]; // 1. 将 nums[j] 向右移动一位 + } + nums[j] = base; // 2. 将 base 赋值到正确位置 + } +} + +// Driver Code +pub fn main() !void { + var nums = [_]i32{ 4, 1, 3, 1, 5, 2 }; + insertionSort(&nums); + std.debug.print("插入排序完成后 nums = ", .{}); + inc.PrintUtil.printArray(i32, &nums); + + const getchar = try std.io.getStdIn().reader().readByte(); + _ = getchar; +} + diff --git a/codes/zig/chapter_stack_and_queue/array_stack.zig b/codes/zig/chapter_stack_and_queue/array_stack.zig new file mode 100644 index 000000000..868d9d94d --- /dev/null +++ b/codes/zig/chapter_stack_and_queue/array_stack.zig @@ -0,0 +1,99 @@ +// File: stack.zig +// Created Time: 2023-01-08 +// Author: sjinzh (sjinzh@gmail.com) + +const std = @import("std"); +const inc = @import("include"); + +// 基于数组实现的栈 +// 编译期泛型 +pub fn ArrayStack(comptime T: type) type { + return struct { + const Self = @This(); + + stack: ?std.ArrayList(T) = null, + + // 构造函数(分配内存+初始化栈) + pub fn init(self: *Self, allocator: std.mem.Allocator) void { + if (self.stack == null) { + self.stack = std.ArrayList(T).init(allocator); + } + } + + // 析构函数(释放内存) + pub fn deinit(self: *Self) void { + if (self.stack == null) return; + self.stack.?.deinit(); + } + + // 获取栈的长度 + pub fn size(self: *Self) usize { + return self.stack.?.items.len; + } + + // 判断栈是否为空 + pub fn empty(self: *Self) bool { + return self.size() == 0; + } + + // 访问栈顶元素 + pub fn top(self: *Self) T { + if (self.size() == 0) @panic("栈为空"); + return self.stack.?.items[self.size() - 1]; + } + + // 入栈 + pub fn push(self: *Self, num: T) !void { + try self.stack.?.append(num); + } + + // 出栈 + pub fn pop(self: *Self) T { + var num = self.stack.?.pop(); + return num; + } + + // 返回 ArrayList + pub fn toList(self: *Self) std.ArrayList(T) { + return self.stack.?; + } + }; +} + +// Driver Code +pub fn main() !void { + // 初始化栈 + var stack = ArrayStack(i32){}; + stack.init(std.heap.page_allocator); + // 延迟释放内存 + defer stack.deinit(); + + // 元素入栈 + try stack.push(1); + try stack.push(3); + try stack.push(2); + try stack.push(5); + try stack.push(4); + std.debug.print("栈 stack = ", .{}); + inc.PrintUtil.printList(i32, stack.toList()); + + // 访问栈顶元素 + var top = stack.top(); + std.debug.print("\n栈顶元素 top = {}", .{top}); + + // 元素出栈 + top = stack.pop(); + std.debug.print("\n出栈元素 pop = {},出栈后 stack = ", .{top}); + inc.PrintUtil.printList(i32, stack.toList()); + + // 获取栈的长度 + var size = stack.size(); + std.debug.print("\n栈的长度 size = {}", .{size}); + + // 判断栈是否为空 + var empty = stack.empty(); + std.debug.print("\n栈是否为空 = {}", .{empty}); + + const getchar = try std.io.getStdIn().reader().readByte(); + _ = getchar; +} diff --git a/codes/zig/chapter_stack_and_queue/linkedlist_stack.zig b/codes/zig/chapter_stack_and_queue/linkedlist_stack.zig new file mode 100644 index 000000000..633583b45 --- /dev/null +++ b/codes/zig/chapter_stack_and_queue/linkedlist_stack.zig @@ -0,0 +1,120 @@ +// File: linkedlist_stack.zig +// Created Time: 2023-01-08 +// Author: sjinzh (sjinzh@gmail.com) + +const std = @import("std"); +const inc = @import("include"); + +// 基于链表实现的栈 +// 编译期泛型 +pub fn LinkedListStack(comptime T: type) type { + return struct { + const Self = @This(); + + stackTop: ?*inc.ListNode(T) = null, // 将头结点作为栈顶 + stkSize: usize = 0, // 栈的长度 + mem_arena: ?std.heap.ArenaAllocator = null, + mem_allocator: std.mem.Allocator = undefined, // 内存分配器 + + // 构造函数(分配内存+初始化栈) + pub fn init(self: *Self, allocator: std.mem.Allocator) !void { + if (self.mem_arena == null) { + self.mem_arena = std.heap.ArenaAllocator.init(allocator); + self.mem_allocator = self.mem_arena.?.allocator(); + } + self.stackTop = null; + self.stkSize = 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.stkSize; + } + + // 判断栈是否为空 + pub fn empty(self: *Self) bool { + return self.size() == 0; + } + + // 访问栈顶元素 + pub fn top(self: *Self) T { + if (self.size() == 0) @panic("栈为空"); + return self.stackTop.?.val; + } + + // 入栈 + pub fn push(self: *Self, num: T) !void { + var node = try self.mem_allocator.create(inc.ListNode(T)); + node.init(num); + node.next = self.stackTop; + self.stackTop = node; + self.stkSize += 1; + } + + // 出栈 + pub fn pop(self: *Self) T { + var num = self.top(); + self.stackTop = self.stackTop.?.next; + self.stkSize -= 1; + return num; + } + + // 将栈转换为数组 + pub fn toArray(self: *Self) ![]T { + var node = self.stackTop; + var res = try self.mem_allocator.alloc(T, self.size()); + std.mem.set(T, res, @as(T, 0)); + var i: usize = 0; + while (i < res.len) : (i += 1) { + res[res.len - i - 1] = node.?.val; + node = node.?.next; + } + return res; + } + }; +} + +// Driver Code +pub fn main() !void { + // 初始化栈 + var stack = LinkedListStack(i32){}; + try stack.init(std.heap.page_allocator); + // 延迟释放内存 + defer stack.deinit(); + + // 元素入栈 + try stack.push(1); + try stack.push(3); + try stack.push(2); + try stack.push(5); + try stack.push(4); + std.debug.print("栈 stack = ", .{}); + inc.PrintUtil.printArray(i32, try stack.toArray()); + + // 访问栈顶元素 + var top = stack.top(); + std.debug.print("\n栈顶元素 top = {}", .{top}); + + // 元素出栈 + top = stack.pop(); + std.debug.print("\n出栈元素 pop = {},出栈后 stack = ", .{top}); + inc.PrintUtil.printArray(i32, try stack.toArray()); + + // 获取栈的长度 + var size = stack.size(); + std.debug.print("\n栈的长度 size = {}", .{size}); + + // 判断栈是否为空 + var empty = stack.empty(); + std.debug.print("\n栈是否为空 = {}", .{empty}); + + const getchar = try std.io.getStdIn().reader().readByte(); + _ = getchar; +} + diff --git a/codes/zig/chapter_stack_and_queue/stack.zig b/codes/zig/chapter_stack_and_queue/stack.zig new file mode 100644 index 000000000..95ed427b0 --- /dev/null +++ b/codes/zig/chapter_stack_and_queue/stack.zig @@ -0,0 +1,44 @@ +// File: stack.zig +// Created Time: 2023-01-08 +// Author: sjinzh (sjinzh@gmail.com) + +const std = @import("std"); +const inc = @import("include"); + +// Driver Code +pub fn main() !void { + // 初始化栈 + // 在 zig 中,推荐将 ArrayList 当作栈来使用 + var stack = std.ArrayList(i32).init(std.heap.page_allocator); + // 延迟释放内存 + defer stack.deinit(); + + // 元素入栈 + try stack.append(1); + try stack.append(3); + try stack.append(2); + try stack.append(5); + try stack.append(4); + std.debug.print("栈 stack = ", .{}); + inc.PrintUtil.printList(i32, stack); + + // 访问栈顶元素 + var top = stack.items[stack.items.len - 1]; + std.debug.print("\n栈顶元素 top = {}", .{top}); + + // 元素出栈 + top = stack.pop(); + std.debug.print("\n出栈元素 pop = {},出栈后 stack = ", .{top}); + inc.PrintUtil.printList(i32, stack); + + // 获取栈的长度 + var size = stack.items.len; + std.debug.print("\n栈的长度 size = {}", .{size}); + + // 判断栈是否为空 + var empty = if (stack.items.len == 0) true else false; + std.debug.print("\n栈是否为空 = {}", .{empty}); + + const getchar = try std.io.getStdIn().reader().readByte(); + _ = getchar; +} From d90f3f828216c69b79270c107b552c91aac689a1 Mon Sep 17 00:00:00 2001 From: Yudong Jin Date: Fri, 13 Jan 2023 01:08:29 +0800 Subject: [PATCH 16/23] Update deployment guidance (Docker) --- docs/chapter_preface/contribution.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/docs/chapter_preface/contribution.md b/docs/chapter_preface/contribution.md index 4e5f3dc85..498002c72 100644 --- a/docs/chapter_preface/contribution.md +++ b/docs/chapter_preface/contribution.md @@ -41,23 +41,22 @@ comments: true 非常欢迎您和我一同来创作本书! -## 离线部署 +## 本地部署 hello-algo ### Docker -使用本教程前,请确保 Docker 已经安装并启动。 +请确保 Docker 已经安装并启动,并根据如下命令离线部署。 -根据如下命令离线部署。 +稍等片刻,即可使用浏览器打开 `http://localhost:8000` 访问本项目。 ```bash git clone https://github.com/krahets/hello-algo.git cd hello-algo + docker-compose up -d ``` -稍等片刻,即可通过浏览器打开 `http://localhost:8000` 访问本教程。 - -使用如下命令删除部署。 +使用如下命令即可删除部署。 ```bash docker-compose down From 32962fb7a14d143d36fece4bdc315af2eb50e4b2 Mon Sep 17 00:00:00 2001 From: reanon <793584285@qq.com> Date: Thu, 12 Jan 2023 20:32:06 +0800 Subject: [PATCH 17/23] feat(heap): add go codes --- codes/go/chapter_heap/heap.go | 46 ++++++++++ codes/go/chapter_heap/heap_test.go | 90 +++++++++++++++++++ codes/go/chapter_heap/my_heap.go | 139 +++++++++++++++++++++++++++++ codes/go/pkg/print_utils.go | 23 +++-- 4 files changed, 291 insertions(+), 7 deletions(-) create mode 100644 codes/go/chapter_heap/heap.go create mode 100644 codes/go/chapter_heap/heap_test.go create mode 100644 codes/go/chapter_heap/my_heap.go diff --git a/codes/go/chapter_heap/heap.go b/codes/go/chapter_heap/heap.go new file mode 100644 index 000000000..d28d2ba7e --- /dev/null +++ b/codes/go/chapter_heap/heap.go @@ -0,0 +1,46 @@ +// File: intHeap.go +// Created Time: 2023-01-12 +// Author: Reanon (793584285@qq.com) + +package chapter_heap + +// intHeap 是一个由整数组成的最大堆 +// 通过实现 heap.Interface 来构建堆 +type intHeap []any + +// Len sort.Interface 的方法 +func (h *intHeap) Len() int { + return len(*h) +} + +// Less sort.Interface 的方法 +func (h *intHeap) Less(i, j int) bool { + // 如果实现小顶堆,则需要调整为小于号 + return (*h)[i].(int) > (*h)[j].(int) +} + +// Swap sort.Interface 的方法 +func (h *intHeap) Swap(i, j int) { + (*h)[i], (*h)[j] = (*h)[j], (*h)[i] +} + +// Push heap.Interface 的方法 + +func (h *intHeap) Push(x any) { + // Push 和 Pop 使用 pointer receiver 作为参数 + // 因为它们不仅会对切片的内容进行调整,还会修改切片的长度。 + *h = append(*h, x.(int)) +} + +// Top 获取堆顶元素 +func (h *intHeap) Top() int { + return (*h)[0].(int) +} + +// Pop heap.Interface 的方法,实现弹出堆顶元素 +func (h *intHeap) Pop() any { + // 待出堆元素存放在最后 + last := (*h)[len(*h)-1] + *h = (*h)[:len(*h)-1] + return last +} diff --git a/codes/go/chapter_heap/heap_test.go b/codes/go/chapter_heap/heap_test.go new file mode 100644 index 000000000..db3844efa --- /dev/null +++ b/codes/go/chapter_heap/heap_test.go @@ -0,0 +1,90 @@ +// File: heap_test.go +// Created Time: 2023-01-12 +// Author: Reanon (793584285@qq.com) + +package chapter_heap + +import ( + "container/heap" + "fmt" + "testing" + + . "github.com/krahets/hello-algo/pkg" +) + +func testPush(h *intHeap, val int) { + // 调用 heap.Interface 的方法,来添加元素 + heap.Push(h, val) + fmt.Printf("\n元素 %d 入堆后 \n", val) + PrintHeap(*h) +} + +func testPop(h *intHeap) { + // 调用 heap.Interface 的方法,来移除元素 + val := heap.Pop(h) + fmt.Printf("\n堆顶元素 %d 出堆后 \n", val) + PrintHeap(*h) +} + +func TestHeap(t *testing.T) { + /* 初始化堆 */ + // 初始化大顶堆 + maxHeap := &intHeap{} + heap.Init(maxHeap) + /* 元素入堆 */ + testPush(maxHeap, 1) + testPush(maxHeap, 3) + testPush(maxHeap, 2) + testPush(maxHeap, 5) + testPush(maxHeap, 4) + + /* 获取堆顶元素 */ + top := maxHeap.Top() + fmt.Printf("堆顶元素为 %d\n", top) + + /* 堆顶元素出堆 */ + testPop(maxHeap) + testPop(maxHeap) + testPop(maxHeap) + testPop(maxHeap) + testPop(maxHeap) + + /* 获取堆大小 */ + size := len(*maxHeap) + fmt.Printf("堆元素数量为 %d\n", size) + + /* 判断堆是否为空 */ + isEmpty := len(*maxHeap) == 0 + fmt.Printf("堆是否为空 %t\n", isEmpty) +} + +func TestMyHeap(t *testing.T) { + /* 初始化堆 */ + // 初始化大顶堆 + maxHeap := newMaxHeap([]any{9, 8, 6, 6, 7, 5, 2, 1, 4, 3, 6, 2}) + fmt.Printf("输入数组并建堆后\n") + maxHeap.print() + + /* 获取堆顶元素 */ + peek := maxHeap.peek() + fmt.Printf("\n堆顶元素为 %d\n", peek) + + /* 元素入堆 */ + val := 7 + maxHeap.push(val) + fmt.Printf("\n元素 %d 入堆后\n", val) + maxHeap.print() + + /* 堆顶元素出堆 */ + peek = maxHeap.poll() + fmt.Printf("\n堆顶元素 %d 出堆后\n", peek) + maxHeap.print() + + /* 获取堆大小 */ + size := maxHeap.size() + fmt.Printf("\n堆元素数量为 %d\n", size) + + /* 判断堆是否为空 */ + isEmpty := maxHeap.isEmpty() + fmt.Printf("\n堆是否为空 %t\n", isEmpty) +} diff --git a/codes/go/chapter_heap/my_heap.go b/codes/go/chapter_heap/my_heap.go new file mode 100644 index 000000000..53e961210 --- /dev/null +++ b/codes/go/chapter_heap/my_heap.go @@ -0,0 +1,139 @@ +// File: my_heap.go +// Created Time: 2023-01-12 +// Author: Reanon (793584285@qq.com) + +package chapter_heap + +import ( + "fmt" + + . "github.com/krahets/hello-algo/pkg" +) + +type maxHeap struct { + // 使用切片而非数组,这样无需考虑扩容问题 + data []any +} + +/* 构造函数,建立空堆 */ +func newHeap() *maxHeap { + return &maxHeap{ + data: make([]any, 0), + } +} + +/* 构造函数,根据切片建堆 */ +func newMaxHeap(nums []any) *maxHeap { + // 所有元素入堆 + h := &maxHeap{data: nums} + for i := len(h.data) - 1; i >= 0; i-- { + // 堆化除叶结点以外的其他所有结点 + h.siftDown(i) + } + return h +} + +/* 获取左子结点索引 */ +func (h *maxHeap) left(i int) int { + return 2*i + 1 +} + +/* 获取右子结点索引 */ +func (h *maxHeap) right(i int) int { + return 2*i + 2 +} + +/* 获取父结点索引 */ +func (h *maxHeap) parent(i int) int { + // 向下整除 + return (i - 1) / 2 +} + +/* 交换元素 */ +func (h *maxHeap) swap(i, j int) { + h.data[i], h.data[j] = h.data[j], h.data[i] +} + +/* 获取堆大小 */ +func (h *maxHeap) size() int { + return len(h.data) +} + +/* 判断堆是否为空 */ +func (h *maxHeap) isEmpty() bool { + return len(h.data) == 0 +} + +/* 访问堆顶元素 */ +func (h *maxHeap) peek() any { + return h.data[0] +} + +/* 元素入堆 */ +func (h *maxHeap) push(val any) { + // 添加结点 + h.data = append(h.data, val) + // 从底至顶堆化 + h.siftUp(len(h.data) - 1) +} + +/* 元素出堆 */ +func (h *maxHeap) poll() any { + // 判空处理 + if h.isEmpty() { + fmt.Println("error") + } + // 交换根结点与最右叶结点(即交换首元素与尾元素) + h.swap(0, h.size()-1) + // 删除结点 + val := h.data[len(h.data)-1] + h.data = h.data[:len(h.data)-1] + // 从顶至底堆化 + h.siftDown(0) + + // 返回堆顶元素 + return val +} + +/* 从结点 i 开始,从顶至底堆化 */ +func (h *maxHeap) siftDown(i int) { + for true { + // 判断结点 i, l, r 中值最大的结点,记为 max + l, r, max := h.left(i), h.right(i), i + if l < h.size() && h.data[l].(int) > h.data[max].(int) { + max = l + } + if r < h.size() && h.data[r].(int) > h.data[max].(int) { + max = r + } + // 若结点 i 最大或索引 l, r 越界,则无需继续堆化,跳出 + if max == i { + break + } + // 交换两结点 + h.swap(i, max) + // 循环向下堆化 + i = max + } +} + +/* 从结点 i 开始,从底至顶堆化 */ +func (h *maxHeap) siftUp(i int) { + for true { + // 获取结点 i 的父结点 + p := h.parent(i) + // 当“越过根结点”或“结点无需修复”时,结束堆化 + if p < 0 || h.data[i].(int) <= h.data[p].(int) { + break + } + // 交换两结点 + h.swap(i, p) + // 循环向上堆化 + i = p + } +} + +/* 打印堆(二叉树) */ +func (h *maxHeap) print() { + PrintHeap(h.data) +} diff --git a/codes/go/pkg/print_utils.go b/codes/go/pkg/print_utils.go index 575ab6b94..d65ebba1d 100644 --- a/codes/go/pkg/print_utils.go +++ b/codes/go/pkg/print_utils.go @@ -29,6 +29,22 @@ func PrintList(list *list.List) { fmt.Print(e.Value, "]\n") } +// PrintMap Print a hash map +func PrintMap[K comparable, V any](m map[K]V) { + for key, value := range m { + fmt.Println(key, "->", value) + } +} + +// PrintHeap Print a heap +func PrintHeap(h []any) { + fmt.Printf("堆的数组表示:") + fmt.Printf("%v", h) + fmt.Printf("\n堆的树状表示:\n") + root := ArrToTree(h) + PrintTree(root) +} + // PrintLinkedList Print a linked list func PrintLinkedList(node *ListNode) { if node == nil { @@ -97,10 +113,3 @@ func showTrunk(t *trunk) { showTrunk(t.prev) fmt.Print(t.str) } - -// PrintMap Print a hash map -func PrintMap[K comparable, V any](m map[K]V) { - for key, value := range m { - fmt.Println(key, "->", value) - } -} From 79d51e36913eac600ace5afc0b4b6fe9e52c49ef Mon Sep 17 00:00:00 2001 From: reanon <793584285@qq.com> Date: Thu, 12 Jan 2023 20:47:11 +0800 Subject: [PATCH 18/23] fix(heap): fix go code --- codes/go/chapter_heap/heap.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/codes/go/chapter_heap/heap.go b/codes/go/chapter_heap/heap.go index d28d2ba7e..a2c2eefef 100644 --- a/codes/go/chapter_heap/heap.go +++ b/codes/go/chapter_heap/heap.go @@ -4,9 +4,9 @@ package chapter_heap -// intHeap 是一个由整数组成的最大堆 +// intHeap 是一个由整数组成的堆 // 通过实现 heap.Interface 来构建堆 -type intHeap []any +type intHeap []int // Len sort.Interface 的方法 func (h *intHeap) Len() int { @@ -16,7 +16,7 @@ func (h *intHeap) Len() int { // Less sort.Interface 的方法 func (h *intHeap) Less(i, j int) bool { // 如果实现小顶堆,则需要调整为小于号 - return (*h)[i].(int) > (*h)[j].(int) + return (*h)[i] > (*h)[j] } // Swap sort.Interface 的方法 @@ -34,11 +34,11 @@ func (h *intHeap) Push(x any) { // Top 获取堆顶元素 func (h *intHeap) Top() int { - return (*h)[0].(int) + return (*h)[0] } // Pop heap.Interface 的方法,实现弹出堆顶元素 -func (h *intHeap) Pop() any { +func (h *intHeap) Pop() int { // 待出堆元素存放在最后 last := (*h)[len(*h)-1] *h = (*h)[:len(*h)-1] From 8117a1d47d0e130003126959fe5c17b395a36427 Mon Sep 17 00:00:00 2001 From: reanon <793584285@qq.com> Date: Thu, 12 Jan 2023 20:58:39 +0800 Subject: [PATCH 19/23] fix(heap): fix go code --- codes/go/chapter_heap/heap.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/codes/go/chapter_heap/heap.go b/codes/go/chapter_heap/heap.go index a2c2eefef..4a5b0c436 100644 --- a/codes/go/chapter_heap/heap.go +++ b/codes/go/chapter_heap/heap.go @@ -6,7 +6,7 @@ package chapter_heap // intHeap 是一个由整数组成的堆 // 通过实现 heap.Interface 来构建堆 -type intHeap []int +type intHeap []any // Len sort.Interface 的方法 func (h *intHeap) Len() int { @@ -16,7 +16,7 @@ func (h *intHeap) Len() int { // Less sort.Interface 的方法 func (h *intHeap) Less(i, j int) bool { // 如果实现小顶堆,则需要调整为小于号 - return (*h)[i] > (*h)[j] + return (*h)[i].(int) > (*h)[j].(int) } // Swap sort.Interface 的方法 @@ -33,12 +33,12 @@ func (h *intHeap) Push(x any) { } // Top 获取堆顶元素 -func (h *intHeap) Top() int { +func (h *intHeap) Top() any { return (*h)[0] } // Pop heap.Interface 的方法,实现弹出堆顶元素 -func (h *intHeap) Pop() int { +func (h *intHeap) Pop() any { // 待出堆元素存放在最后 last := (*h)[len(*h)-1] *h = (*h)[:len(*h)-1] From 264a2ab6bca636b299d045d61be5d75f626c7517 Mon Sep 17 00:00:00 2001 From: reanon <793584285@qq.com> Date: Fri, 13 Jan 2023 10:25:25 +0800 Subject: [PATCH 20/23] docs(heap): add go codes --- codes/go/chapter_heap/heap.go | 35 ++++---- codes/go/chapter_heap/my_heap.go | 32 +++---- docs/chapter_heap/heap.md | 139 ++++++++++++++++++++++++++++++- 3 files changed, 171 insertions(+), 35 deletions(-) diff --git a/codes/go/chapter_heap/heap.go b/codes/go/chapter_heap/heap.go index 4a5b0c436..5f846ced4 100644 --- a/codes/go/chapter_heap/heap.go +++ b/codes/go/chapter_heap/heap.go @@ -4,10 +4,25 @@ package chapter_heap -// intHeap 是一个由整数组成的堆 -// 通过实现 heap.Interface 来构建堆 +// Go 语言中可以通过实现 heap.Interface 来构建整数大顶堆 +// 实现 heap.Interface 需要同时实现 sort.Interface type intHeap []any +// Push heap.Interface 的方法,实现推入元素到堆 +func (h *intHeap) Push(x any) { + // Push 和 Pop 使用 pointer receiver 作为参数 + // 因为它们不仅会对切片的内容进行调整,还会修改切片的长度。 + *h = append(*h, x.(int)) +} + +// Pop heap.Interface 的方法,实现弹出堆顶元素 +func (h *intHeap) Pop() any { + // 待出堆元素存放在最后 + last := (*h)[len(*h)-1] + *h = (*h)[:len(*h)-1] + return last +} + // Len sort.Interface 的方法 func (h *intHeap) Len() int { return len(*h) @@ -24,23 +39,7 @@ func (h *intHeap) Swap(i, j int) { (*h)[i], (*h)[j] = (*h)[j], (*h)[i] } -// Push heap.Interface 的方法 - -func (h *intHeap) Push(x any) { - // Push 和 Pop 使用 pointer receiver 作为参数 - // 因为它们不仅会对切片的内容进行调整,还会修改切片的长度。 - *h = append(*h, x.(int)) -} - // Top 获取堆顶元素 func (h *intHeap) Top() any { return (*h)[0] } - -// Pop heap.Interface 的方法,实现弹出堆顶元素 -func (h *intHeap) Pop() any { - // 待出堆元素存放在最后 - last := (*h)[len(*h)-1] - *h = (*h)[:len(*h)-1] - return last -} diff --git a/codes/go/chapter_heap/my_heap.go b/codes/go/chapter_heap/my_heap.go index 53e961210..0b3d0a3b1 100644 --- a/codes/go/chapter_heap/my_heap.go +++ b/codes/go/chapter_heap/my_heap.go @@ -77,6 +77,22 @@ func (h *maxHeap) push(val any) { h.siftUp(len(h.data) - 1) } +/* 从结点 i 开始,从底至顶堆化 */ +func (h *maxHeap) siftUp(i int) { + for true { + // 获取结点 i 的父结点 + p := h.parent(i) + // 当“越过根结点”或“结点无需修复”时,结束堆化 + if p < 0 || h.data[i].(int) <= h.data[p].(int) { + break + } + // 交换两结点 + h.swap(i, p) + // 循环向上堆化 + i = p + } +} + /* 元素出堆 */ func (h *maxHeap) poll() any { // 判空处理 @@ -117,22 +133,6 @@ func (h *maxHeap) siftDown(i int) { } } -/* 从结点 i 开始,从底至顶堆化 */ -func (h *maxHeap) siftUp(i int) { - for true { - // 获取结点 i 的父结点 - p := h.parent(i) - // 当“越过根结点”或“结点无需修复”时,结束堆化 - if p < 0 || h.data[i].(int) <= h.data[p].(int) { - break - } - // 交换两结点 - h.swap(i, p) - // 循环向上堆化 - i = p - } -} - /* 打印堆(二叉树) */ func (h *maxHeap) print() { PrintHeap(h.data) diff --git a/docs/chapter_heap/heap.md b/docs/chapter_heap/heap.md index 73f425ff7..91f7670bc 100644 --- a/docs/chapter_heap/heap.md +++ b/docs/chapter_heap/heap.md @@ -97,7 +97,45 @@ comments: true === "Go" ```go title="heap.go" + // Go 语言中可以通过实现 heap.Interface 来构建整数大顶堆 + // 实现 heap.Interface 需要同时实现 sort.Interface + type intHeap []any + // Push heap.Interface 的方法,实现推入元素到堆 + func (h *intHeap) Push(x any) { + // Push 和 Pop 使用 pointer receiver 作为参数 + // 因为它们不仅会对切片的内容进行调整,还会修改切片的长度。 + *h = append(*h, x.(int)) + } + + // Pop heap.Interface 的方法,实现弹出堆顶元素 + func (h *intHeap) Pop() any { + // 待出堆元素存放在最后 + last := (*h)[len(*h)-1] + *h = (*h)[:len(*h)-1] + return last + } + + // Len sort.Interface 的方法 + func (h *intHeap) Len() int { + return len(*h) + } + + // Less sort.Interface 的方法 + func (h *intHeap) Less(i, j int) bool { + // 如果实现小顶堆,则需要调整为小于号 + return (*h)[i].(int) > (*h)[j].(int) + } + + // Swap sort.Interface 的方法 + func (h *intHeap) Swap(i, j int) { + (*h)[i], (*h)[j] = (*h)[j], (*h)[i] + } + + // Top 获取堆顶元素 + func (h *intHeap) Top() any { + return (*h)[0] + } ``` === "JavaScript" @@ -188,7 +226,33 @@ comments: true === "Go" ```go title="my_heap.go" + type maxHeap struct { + // 使用切片而非数组,这样无需考虑扩容问题 + data []any + } + /* 构造函数,建立空堆 */ + func newHeap() *maxHeap { + return &maxHeap{ + data: make([]any, 0), + } + } + + /* 获取左子结点索引 */ + func (h *maxHeap) left(i int) int { + return 2*i + 1 + } + + /* 获取右子结点索引 */ + func (h *maxHeap) right(i int) int { + return 2*i + 2 + } + + /* 获取父结点索引 */ + func (h *maxHeap) parent(i int) int { + // 向下整除 + return (i - 1) / 2 + } ``` === "JavaScript" @@ -249,6 +313,10 @@ comments: true === "Go" ```go title="my_heap.go" + /* 访问堆顶元素 */ + func (h *maxHeap) peek() any { + return h.data[0] + } ``` @@ -350,7 +418,29 @@ comments: true === "Go" ```go title="my_heap.go" + /* 元素入堆 */ + func (h *maxHeap) push(val any) { + // 添加结点 + h.data = append(h.data, val) + // 从底至顶堆化 + h.siftUp(len(h.data) - 1) + } + /* 从结点 i 开始,从底至顶堆化 */ + func (h *maxHeap) siftUp(i int) { + for true { + // 获取结点 i 的父结点 + p := h.parent(i) + // 当“越过根结点”或“结点无需修复”时,结束堆化 + if p < 0 || h.data[i].(int) <= h.data[p].(int) { + break + } + // 交换两结点 + h.swap(i, p) + // 循环向上堆化 + i = p + } + } ``` === "JavaScript" @@ -477,7 +567,45 @@ comments: true === "Go" ```go title="my_heap.go" + /* 元素出堆 */ + func (h *maxHeap) poll() any { + // 判空处理 + if h.isEmpty() { + fmt.Println("error") + } + // 交换根结点与最右叶结点(即交换首元素与尾元素) + h.swap(0, h.size()-1) + // 删除结点 + val := h.data[len(h.data)-1] + h.data = h.data[:len(h.data)-1] + // 从顶至底堆化 + h.siftDown(0) + // 返回堆顶元素 + return val + } + + /* 从结点 i 开始,从顶至底堆化 */ + func (h *maxHeap) siftDown(i int) { + for true { + // 判断结点 i, l, r 中值最大的结点,记为 max + l, r, max := h.left(i), h.right(i), i + if l < h.size() && h.data[l].(int) > h.data[max].(int) { + max = l + } + if r < h.size() && h.data[r].(int) > h.data[max].(int) { + max = r + } + // 若结点 i 最大或索引 l, r 越界,则无需继续堆化,跳出 + if max == i { + break + } + // 交换两结点 + h.swap(i, max) + // 循环向下堆化 + i = max + } + } ``` === "JavaScript" @@ -545,7 +673,16 @@ comments: true === "Go" ```go title="my_heap.go" - + /* 构造函数,根据切片建堆 */ + func newMaxHeap(nums []any) *maxHeap { + // 所有元素入堆 + h := &maxHeap{data: nums} + for i := len(h.data) - 1; i >= 0; i-- { + // 堆化除叶结点以外的其他所有结点 + h.siftDown(i) + } + return h + } ``` === "JavaScript" From 3dcdd1c72dafdae44ab5e7bfdbc06918a3adff93 Mon Sep 17 00:00:00 2001 From: reanon <793584285@qq.com> Date: Fri, 13 Jan 2023 17:37:24 +0800 Subject: [PATCH 21/23] fix(heap): add go codes --- codes/go/chapter_heap/heap.go | 40 +++++++++++++++++++++++++++++++++++ docs/chapter_heap/heap.md | 36 ++++++++++++++++++++++++++++++- 2 files changed, 75 insertions(+), 1 deletion(-) diff --git a/codes/go/chapter_heap/heap.go b/codes/go/chapter_heap/heap.go index 5f846ced4..886ec20e8 100644 --- a/codes/go/chapter_heap/heap.go +++ b/codes/go/chapter_heap/heap.go @@ -4,6 +4,11 @@ package chapter_heap +import ( + "container/heap" + "fmt" +) + // Go 语言中可以通过实现 heap.Interface 来构建整数大顶堆 // 实现 heap.Interface 需要同时实现 sort.Interface type intHeap []any @@ -43,3 +48,38 @@ func (h *intHeap) Swap(i, j int) { func (h *intHeap) Top() any { return (*h)[0] } + +/* Driver Code */ +func main() { + /* 初始化堆 */ + // 初始化大顶堆 + maxHeap := &intHeap{} + heap.Init(maxHeap) + /* 元素入堆 */ + // 调用 heap.Interface 的方法,来添加元素 + heap.Push(maxHeap, 1) + heap.Push(maxHeap, 3) + heap.Push(maxHeap, 2) + heap.Push(maxHeap, 4) + heap.Push(maxHeap, 5) + + /* 获取堆顶元素 */ + top := maxHeap.Top() + fmt.Printf("堆顶元素为 %d\n", top) + + /* 堆顶元素出堆 */ + // 调用 heap.Interface 的方法,来移除元素 + heap.Pop(maxHeap) + heap.Pop(maxHeap) + heap.Pop(maxHeap) + heap.Pop(maxHeap) + heap.Pop(maxHeap) + + /* 获取堆大小 */ + size := len(*maxHeap) + fmt.Printf("堆元素数量为 %d\n", size) + + /* 判断堆是否为空 */ + isEmpty := len(*maxHeap) == 0 + fmt.Printf("堆是否为空 %t\n", isEmpty) +} diff --git a/docs/chapter_heap/heap.md b/docs/chapter_heap/heap.md index 91f7670bc..924993257 100644 --- a/docs/chapter_heap/heap.md +++ b/docs/chapter_heap/heap.md @@ -136,6 +136,41 @@ comments: true func (h *intHeap) Top() any { return (*h)[0] } + + /* Driver Code */ + func TestHeap(t *testing.T) { + /* 初始化堆 */ + // 初始化大顶堆 + maxHeap := &intHeap{} + heap.Init(maxHeap) + /* 元素入堆 */ + // 调用 heap.Interface 的方法,来添加元素 + heap.Push(maxHeap, 1) + heap.Push(maxHeap, 3) + heap.Push(maxHeap, 2) + heap.Push(maxHeap, 4) + heap.Push(maxHeap, 5) + + /* 获取堆顶元素 */ + top := maxHeap.Top() + fmt.Printf("堆顶元素为 %d\n", top) + + /* 堆顶元素出堆 */ + // 调用 heap.Interface 的方法,来移除元素 + heap.Pop(maxHeap) + heap.Pop(maxHeap) + heap.Pop(maxHeap) + heap.Pop(maxHeap) + heap.Pop(maxHeap) + + /* 获取堆大小 */ + size := len(*maxHeap) + fmt.Printf("堆元素数量为 %d\n", size) + + /* 判断堆是否为空 */ + isEmpty := len(*maxHeap) == 0 + fmt.Printf("堆是否为空 %t\n", isEmpty) + } ``` === "JavaScript" @@ -317,7 +352,6 @@ comments: true func (h *maxHeap) peek() any { return h.data[0] } - ``` === "JavaScript" From ec28b4ce7a3d4580422c58275329b3cd192b4bb1 Mon Sep 17 00:00:00 2001 From: reanon <793584285@qq.com> Date: Fri, 13 Jan 2023 17:40:20 +0800 Subject: [PATCH 22/23] fix(heap): add go codes --- codes/go/chapter_heap/heap.go | 40 ----------------------------------- docs/chapter_heap/heap.md | 2 +- 2 files changed, 1 insertion(+), 41 deletions(-) diff --git a/codes/go/chapter_heap/heap.go b/codes/go/chapter_heap/heap.go index 886ec20e8..5f846ced4 100644 --- a/codes/go/chapter_heap/heap.go +++ b/codes/go/chapter_heap/heap.go @@ -4,11 +4,6 @@ package chapter_heap -import ( - "container/heap" - "fmt" -) - // Go 语言中可以通过实现 heap.Interface 来构建整数大顶堆 // 实现 heap.Interface 需要同时实现 sort.Interface type intHeap []any @@ -48,38 +43,3 @@ func (h *intHeap) Swap(i, j int) { func (h *intHeap) Top() any { return (*h)[0] } - -/* Driver Code */ -func main() { - /* 初始化堆 */ - // 初始化大顶堆 - maxHeap := &intHeap{} - heap.Init(maxHeap) - /* 元素入堆 */ - // 调用 heap.Interface 的方法,来添加元素 - heap.Push(maxHeap, 1) - heap.Push(maxHeap, 3) - heap.Push(maxHeap, 2) - heap.Push(maxHeap, 4) - heap.Push(maxHeap, 5) - - /* 获取堆顶元素 */ - top := maxHeap.Top() - fmt.Printf("堆顶元素为 %d\n", top) - - /* 堆顶元素出堆 */ - // 调用 heap.Interface 的方法,来移除元素 - heap.Pop(maxHeap) - heap.Pop(maxHeap) - heap.Pop(maxHeap) - heap.Pop(maxHeap) - heap.Pop(maxHeap) - - /* 获取堆大小 */ - size := len(*maxHeap) - fmt.Printf("堆元素数量为 %d\n", size) - - /* 判断堆是否为空 */ - isEmpty := len(*maxHeap) == 0 - fmt.Printf("堆是否为空 %t\n", isEmpty) -} diff --git a/docs/chapter_heap/heap.md b/docs/chapter_heap/heap.md index 924993257..15d22e787 100644 --- a/docs/chapter_heap/heap.md +++ b/docs/chapter_heap/heap.md @@ -262,7 +262,7 @@ comments: true ```go title="my_heap.go" type maxHeap struct { - // 使用切片而非数组,这样无需考虑扩容问题 + // 使用切片而非数组,这样无需考虑扩容问题 data []any } From 1ffe4392ec3632d3d2bf42a64556db8f811b8db5 Mon Sep 17 00:00:00 2001 From: Yudong Jin Date: Sat, 14 Jan 2023 01:21:47 +0800 Subject: [PATCH 23/23] Update C, C++ installation. --- docs/chapter_introduction/what_is_dsa.md | 2 +- docs/chapter_preface/installation.md | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/chapter_introduction/what_is_dsa.md b/docs/chapter_introduction/what_is_dsa.md index 67b3c37e1..10ea25133 100644 --- a/docs/chapter_introduction/what_is_dsa.md +++ b/docs/chapter_introduction/what_is_dsa.md @@ -6,7 +6,7 @@ comments: true ## 算法定义 -「算法 Algorithm」是在有限时间内解决问题的一组指令或操作步骤。算法具有以下特性: +「算法 Algorithm」是在有限时间内解决特定问题的一组指令或操作步骤。算法具有以下特性: - 问题是明确的,需要拥有明确的输入和输出定义。 - 解具有确定性,即给定相同输入时,输出一定相同。 diff --git a/docs/chapter_preface/installation.md b/docs/chapter_preface/installation.md index 828365edf..e23608a68 100644 --- a/docs/chapter_preface/installation.md +++ b/docs/chapter_preface/installation.md @@ -15,10 +15,9 @@ comments: true 1. 下载并安装 [OpenJDK](https://jdk.java.net/18/)(版本需满足 > JDK 9)。 2. 在 VSCode 的插件市场中搜索 `java` ,安装 Java Extension Pack 。 +## C/C++ 环境 -## C++ 环境 - -1. Windows 系统需要安装 [MinGW](https://www.mingw-w64.org/downloads/) ,MacOS 自带 Clang 无需安装。 +1. Windows 系统需要安装 [MinGW](https://sourceforge.net/projects/mingw-w64/files/) ([完整配置教程](https://zhuanlan.zhihu.com/p/77074009)),MacOS 自带 Clang 无需安装。 2. 在 VSCode 的插件市场中搜索 `c++` ,安装 C/C++ Extension Pack 。 ## Python 环境