mirror of
https://github.com/krahets/hello-algo.git
synced 2026-04-05 03:30:30 +08:00
feat: Revised the book (#978)
* Sync recent changes to the revised Word. * Revised the preface chapter * Revised the introduction chapter * Revised the computation complexity chapter * Revised the chapter data structure * Revised the chapter array and linked list * Revised the chapter stack and queue * Revised the chapter hashing * Revised the chapter tree * Revised the chapter heap * Revised the chapter graph * Revised the chapter searching * Reivised the sorting chapter * Revised the divide and conquer chapter * Revised the chapter backtacking * Revised the DP chapter * Revised the greedy chapter * Revised the appendix chapter * Revised the preface chapter doubly * Revised the figures
This commit is contained in:
@@ -32,11 +32,11 @@ pub fn insert(nums: []i32, num: i32, index: usize) void {
|
||||
while (i > index) : (i -= 1) {
|
||||
nums[i] = nums[i - 1];
|
||||
}
|
||||
// 将 num 赋给 index 处元素
|
||||
// 将 num 赋给 index 处的元素
|
||||
nums[index] = num;
|
||||
}
|
||||
|
||||
// 删除索引 index 处元素
|
||||
// 删除索引 index 处的元素
|
||||
pub fn remove(nums: []i32, index: usize) void {
|
||||
// 把索引 index 之后的所有元素向前移动一位
|
||||
var i = index;
|
||||
|
||||
@@ -53,7 +53,7 @@ pub fn main() !void {
|
||||
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;
|
||||
|
||||
@@ -29,7 +29,7 @@ pub fn main() !void {
|
||||
std.debug.print("\n清空列表后 nums = ", .{});
|
||||
inc.PrintUtil.printList(i32, nums);
|
||||
|
||||
// 尾部添加元素
|
||||
// 在尾部添加元素
|
||||
try nums.append(1);
|
||||
try nums.append(3);
|
||||
try nums.append(2);
|
||||
@@ -38,7 +38,7 @@ pub fn main() !void {
|
||||
std.debug.print("\n添加元素后 nums = ", .{});
|
||||
inc.PrintUtil.printList(i32, nums);
|
||||
|
||||
// 中间插入元素
|
||||
// 在中间插入元素
|
||||
try nums.insert(3, 6);
|
||||
std.debug.print("\n在索引 3 处插入数字 6 ,得到 nums = ", .{});
|
||||
inc.PrintUtil.printList(i32, nums);
|
||||
|
||||
@@ -5,14 +5,14 @@
|
||||
const std = @import("std");
|
||||
const inc = @import("include");
|
||||
|
||||
// 列表类简易实现
|
||||
// 列表类
|
||||
pub fn MyList(comptime T: type) type {
|
||||
return struct {
|
||||
const Self = @This();
|
||||
|
||||
arr: []T = undefined, // 数组(存储列表元素)
|
||||
arrCapacity: usize = 10, // 列表容量
|
||||
numSize: usize = 0, // 列表长度(即当前元素数量)
|
||||
numSize: usize = 0, // 列表长度(当前元素数量)
|
||||
extendRatio: usize = 2, // 每次列表扩容的倍数
|
||||
mem_arena: ?std.heap.ArenaAllocator = null,
|
||||
mem_allocator: std.mem.Allocator = undefined, // 内存分配器
|
||||
@@ -33,7 +33,7 @@ pub fn MyList(comptime T: type) type {
|
||||
self.mem_arena.?.deinit();
|
||||
}
|
||||
|
||||
// 获取列表长度(即当前元素数量)
|
||||
// 获取列表长度(当前元素数量)
|
||||
pub fn size(self: *Self) usize {
|
||||
return self.numSize;
|
||||
}
|
||||
@@ -57,7 +57,7 @@ pub fn MyList(comptime T: type) type {
|
||||
self.arr[index] = num;
|
||||
}
|
||||
|
||||
// 尾部添加元素
|
||||
// 在尾部添加元素
|
||||
pub fn add(self: *Self, num: T) !void {
|
||||
// 元素数量超出容量时,触发扩容机制
|
||||
if (self.size() == self.capacity()) try self.extendCapacity();
|
||||
@@ -66,7 +66,7 @@ pub fn MyList(comptime T: type) type {
|
||||
self.numSize += 1;
|
||||
}
|
||||
|
||||
// 中间插入元素
|
||||
// 在中间插入元素
|
||||
pub fn insert(self: *Self, index: usize, num: T) !void {
|
||||
if (index < 0 or index >= self.size()) @panic("索引越界");
|
||||
// 元素数量超出容量时,触发扩容机制
|
||||
@@ -130,7 +130,7 @@ pub fn main() !void {
|
||||
// 延迟释放内存
|
||||
defer nums.deinit();
|
||||
|
||||
// 尾部添加元素
|
||||
// 在尾部添加元素
|
||||
try nums.add(1);
|
||||
try nums.add(3);
|
||||
try nums.add(2);
|
||||
@@ -140,7 +140,7 @@ pub fn main() !void {
|
||||
inc.PrintUtil.printArray(i32, try nums.toArray());
|
||||
std.debug.print(" ,容量 = {} ,长度 = {}", .{nums.capacity(), nums.size()});
|
||||
|
||||
// 中间插入元素
|
||||
// 在中间插入元素
|
||||
try nums.insert(3, 6);
|
||||
std.debug.print("\n在索引 3 处插入数字 6 ,得到 nums = ", .{});
|
||||
inc.PrintUtil.printArray(i32, try nums.toArray());
|
||||
|
||||
@@ -31,7 +31,7 @@ fn whileLoop(n: i32) i32 {
|
||||
fn whileLoopII(n: i32) i32 {
|
||||
var res: i32 = 0;
|
||||
var i: i32 = 1; // 初始化条件变量
|
||||
// 循环求和 1, 4, ...
|
||||
// 循环求和 1, 4, 10, ...
|
||||
while (i <= n) {
|
||||
res += @intCast(i);
|
||||
// 更新条件变量
|
||||
|
||||
@@ -24,7 +24,7 @@ fn backtrack(choices: []i32, state: i32, n: i32, res: std.ArrayList(i32)) void {
|
||||
|
||||
// 爬楼梯:回溯
|
||||
fn climbingStairsBacktrack(n: usize) !i32 {
|
||||
var choices = [_]i32{ 1, 2 }; // 可选择向上爬 1 或 2 阶
|
||||
var choices = [_]i32{ 1, 2 }; // 可选择向上爬 1 阶或 2 阶
|
||||
var state: i32 = 0; // 从第 0 阶开始爬
|
||||
var res = std.ArrayList(i32).init(std.heap.page_allocator);
|
||||
defer res.deinit();
|
||||
|
||||
@@ -14,7 +14,7 @@ fn coinChangeDP(comptime coins: []i32, comptime amt: usize) i32 {
|
||||
for (1..amt + 1) |a| {
|
||||
dp[0][a] = max;
|
||||
}
|
||||
// 状态转移:其余行列
|
||||
// 状态转移:其余行和列
|
||||
for (1..n + 1) |i| {
|
||||
for (1..amt + 1) |a| {
|
||||
if (coins[i - 1] > @as(i32, @intCast(a))) {
|
||||
|
||||
@@ -73,7 +73,7 @@ fn editDistanceDP(comptime s: []const u8, comptime t: []const u8) i32 {
|
||||
for (1..m + 1) |j| {
|
||||
dp[0][j] = @intCast(j);
|
||||
}
|
||||
// 状态转移:其余行列
|
||||
// 状态转移:其余行和列
|
||||
for (1..n + 1) |i| {
|
||||
for (1..m + 1) |j| {
|
||||
if (s[i - 1] == t[j - 1]) {
|
||||
|
||||
@@ -6,11 +6,11 @@ const std = @import("std");
|
||||
|
||||
// 0-1 背包:暴力搜索
|
||||
fn knapsackDFS(wgt: []i32, val: []i32, i: usize, c: usize) i32 {
|
||||
// 若已选完所有物品或背包无容量,则返回价值 0
|
||||
// 若已选完所有物品或背包无剩余容量,则返回价值 0
|
||||
if (i == 0 or c == 0) {
|
||||
return 0;
|
||||
}
|
||||
// 若超过背包容量,则只能不放入背包
|
||||
// 若超过背包容量,则只能选择不放入背包
|
||||
if (wgt[i - 1] > c) {
|
||||
return knapsackDFS(wgt, val, i - 1, c);
|
||||
}
|
||||
@@ -23,7 +23,7 @@ fn knapsackDFS(wgt: []i32, val: []i32, i: usize, c: usize) i32 {
|
||||
|
||||
// 0-1 背包:记忆化搜索
|
||||
fn knapsackDFSMem(wgt: []i32, val: []i32, mem: anytype, i: usize, c: usize) i32 {
|
||||
// 若已选完所有物品或背包无容量,则返回价值 0
|
||||
// 若已选完所有物品或背包无剩余容量,则返回价值 0
|
||||
if (i == 0 or c == 0) {
|
||||
return 0;
|
||||
}
|
||||
@@ -31,7 +31,7 @@ fn knapsackDFSMem(wgt: []i32, val: []i32, mem: anytype, i: usize, c: usize) i32
|
||||
if (mem[i][c] != -1) {
|
||||
return mem[i][c];
|
||||
}
|
||||
// 若超过背包容量,则只能不放入背包
|
||||
// 若超过背包容量,则只能选择不放入背包
|
||||
if (wgt[i - 1] > c) {
|
||||
return knapsackDFSMem(wgt, val, mem, i - 1, c);
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ fn minPathSumDP(comptime grid: anytype) i32 {
|
||||
for (1..n) |i| {
|
||||
dp[i][0] = dp[i - 1][0] + grid[i][0];
|
||||
}
|
||||
// 状态转移:其余行列
|
||||
// 状态转移:其余行和列
|
||||
for (1..n) |i| {
|
||||
for (1..m) |j| {
|
||||
dp[i][j] = @min(dp[i][j - 1], dp[i - 1][j]) + grid[i][j];
|
||||
|
||||
@@ -18,7 +18,7 @@ const Pair = struct {
|
||||
}
|
||||
};
|
||||
|
||||
// 基于数组简易实现的哈希表
|
||||
// 基于数组实现的哈希表
|
||||
pub fn ArrayHashMap(comptime T: type) type {
|
||||
return struct {
|
||||
bucket: ?std.ArrayList(?T) = null,
|
||||
|
||||
@@ -95,7 +95,7 @@ pub fn MaxHeap(comptime T: type) type {
|
||||
pub fn pop(self: *Self) !T {
|
||||
// 判断处理
|
||||
if (self.isEmpty()) unreachable;
|
||||
// 交换根节点与最右叶节点(即交换首元素与尾元素)
|
||||
// 交换根节点与最右叶节点(交换首元素与尾元素)
|
||||
try self.swap(0, self.size() - 1);
|
||||
// 删除节点
|
||||
var val = self.max_heap.?.pop();
|
||||
|
||||
@@ -25,9 +25,9 @@ fn binarySearch(comptime T: type, nums: std.ArrayList(T), target: T) T {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 二分查找(左闭右开)
|
||||
// 二分查找(左闭右开区间)
|
||||
fn binarySearchLCRO(comptime T: type, nums: std.ArrayList(T), target: T) T {
|
||||
// 初始化左闭右开 [0, n) ,即 i, j 分别指向数组首元素、尾元素+1
|
||||
// 初始化左闭右开区间 [0, n) ,即 i, j 分别指向数组首元素、尾元素+1
|
||||
var i: usize = 0;
|
||||
var j: usize = nums.items.len;
|
||||
// 循环,当搜索区间为空时跳出(当 i = j 时为空)
|
||||
@@ -56,7 +56,7 @@ pub fn main() !void {
|
||||
var index = binarySearch(i32, nums, target);
|
||||
std.debug.print("目标元素 6 的索引 = {}\n", .{index});
|
||||
|
||||
// 二分查找(左闭右开)
|
||||
// 二分查找(左闭右开区间)
|
||||
index = binarySearchLCRO(i32, nums, target);
|
||||
std.debug.print("目标元素 6 的索引 = {}\n", .{index});
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ const inc = @import("include");
|
||||
pub fn twoSumBruteForce(nums: []i32, target: i32) ?[2]i32 {
|
||||
var size: usize = nums.len;
|
||||
var i: usize = 0;
|
||||
// 两层循环,时间复杂度 O(n^2)
|
||||
// 两层循环,时间复杂度为 O(n^2)
|
||||
while (i < size - 1) : (i += 1) {
|
||||
var j = i + 1;
|
||||
while (j < size) : (j += 1) {
|
||||
@@ -24,11 +24,11 @@ pub fn twoSumBruteForce(nums: []i32, target: i32) ?[2]i32 {
|
||||
// 方法二:辅助哈希表
|
||||
pub fn twoSumHashTable(nums: []i32, target: i32) !?[2]i32 {
|
||||
var size: usize = nums.len;
|
||||
// 辅助哈希表,空间复杂度 O(n)
|
||||
// 辅助哈希表,空间复杂度为 O(n)
|
||||
var dic = std.AutoHashMap(i32, i32).init(std.heap.page_allocator);
|
||||
defer dic.deinit();
|
||||
var i: usize = 0;
|
||||
// 单层循环,时间复杂度 O(n)
|
||||
// 单层循环,时间复杂度为 O(n)
|
||||
while (i < size) : (i += 1) {
|
||||
if (dic.contains(target - nums[i])) {
|
||||
return [_]i32{dic.get(target - nums[i]).?, @intCast(i)};
|
||||
|
||||
@@ -17,7 +17,7 @@ const QuickSort = struct {
|
||||
|
||||
// 哨兵划分
|
||||
pub fn partition(nums: []i32, left: usize, right: usize) usize {
|
||||
// 以 nums[left] 作为基准数
|
||||
// 以 nums[left] 为基准数
|
||||
var i = left;
|
||||
var j = right;
|
||||
while (i < j) {
|
||||
@@ -70,7 +70,7 @@ const QuickSortMedian = struct {
|
||||
var med = medianThree(nums, left, (left + right) / 2, right);
|
||||
// 将中位数交换至数组最左端
|
||||
swap(nums, left, med);
|
||||
// 以 nums[left] 作为基准数
|
||||
// 以 nums[left] 为基准数
|
||||
var i = left;
|
||||
var j = right;
|
||||
while (i < j) {
|
||||
@@ -107,7 +107,7 @@ const QuickSortTailCall = struct {
|
||||
|
||||
// 哨兵划分
|
||||
pub fn partition(nums: []i32, left: usize, right: usize) usize {
|
||||
// 以 nums[left] 作为基准数
|
||||
// 以 nums[left] 为基准数
|
||||
var i = left;
|
||||
var j = right;
|
||||
while (i < j) {
|
||||
@@ -127,7 +127,7 @@ const QuickSortTailCall = struct {
|
||||
while (left < right) {
|
||||
// 哨兵划分操作
|
||||
var pivot = partition(nums, left, right);
|
||||
// 对两个子数组中较短的那个执行快排
|
||||
// 对两个子数组中较短的那个执行快速排序
|
||||
if (pivot - left < right - pivot) {
|
||||
quickSort(nums, left, pivot - 1); // 递归排序左子数组
|
||||
left = pivot + 1; // 剩余未排序区间为 [pivot + 1, right]
|
||||
|
||||
@@ -13,7 +13,7 @@ fn digit(num: i32, exp: i32) i32 {
|
||||
|
||||
// 计数排序(根据 nums 第 k 位排序)
|
||||
fn countingSortDigit(nums: []i32, exp: i32) !void {
|
||||
// 十进制的位范围为 0~9 ,因此需要长度为 10 的桶
|
||||
// 十进制的位范围为 0~9 ,因此需要长度为 10 的桶数组
|
||||
var mem_arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
||||
// defer mem_arena.deinit();
|
||||
const mem_allocator = mem_arena.allocator();
|
||||
|
||||
@@ -65,7 +65,7 @@ pub fn LinkedListDeque(comptime T: type) type {
|
||||
pub fn push(self: *Self, num: T, is_front: bool) !void {
|
||||
var node = try self.mem_allocator.create(ListNode(T));
|
||||
node.init(num);
|
||||
// 若链表为空,则令 front, rear 都指向 node
|
||||
// 若链表为空,则令 front 和 rear 都指向 node
|
||||
if (self.isEmpty()) {
|
||||
self.front = node;
|
||||
self.rear = node;
|
||||
|
||||
@@ -14,7 +14,7 @@ pub fn main() !void {
|
||||
var n3 = inc.TreeNode(i32){ .val = 3 };
|
||||
var n4 = inc.TreeNode(i32){ .val = 4 };
|
||||
var n5 = inc.TreeNode(i32){ .val = 5 };
|
||||
// 构建引用指向(即指针)
|
||||
// 构建节点之间的引用(指针)
|
||||
n1.left = &n2;
|
||||
n1.right = &n3;
|
||||
n2.left = &n4;
|
||||
|
||||
Reference in New Issue
Block a user