From 503ca797b8bc41ac74db019dc488a7ec36dd3e70 Mon Sep 17 00:00:00 2001 From: krahets Date: Sat, 2 Sep 2023 23:03:42 +0800 Subject: [PATCH] build --- .../iteration_and_recursion.md | 85 +++++++++++++-- .../time_complexity.md | 4 +- .../dp_problem_features.md | 5 +- .../knapsack_problem.md | 6 +- chapter_greedy/fractional_knapsack_problem.md | 65 ++++++++++- chapter_greedy/greedy_algorithm.md | 38 ++++++- chapter_greedy/max_capacity_problem.md | 44 +++++++- chapter_greedy/max_product_cutting_problem.md | 42 +++++++- chapter_searching/binary_search_insertion.md | 5 +- .../replace_linear_by_hashing.md | 2 +- chapter_sorting/bubble_sort.md | 8 +- chapter_tree/binary_search_tree.md | 101 ++++++++---------- 12 files changed, 314 insertions(+), 91 deletions(-) diff --git a/chapter_computational_complexity/iteration_and_recursion.md b/chapter_computational_complexity/iteration_and_recursion.md index 44928c81f..7c0f9bb2d 100644 --- a/chapter_computational_complexity/iteration_and_recursion.md +++ b/chapter_computational_complexity/iteration_and_recursion.md @@ -122,7 +122,15 @@ status: new === "Swift" ```swift title="iteration.swift" - [class]{}-[func]{forLoop} + /* for 循环 */ + func forLoop(n: Int) -> Int { + var res = 0 + // 循环求和 1, 2, ..., n-1, n + for i in 1 ... n { + res += i + } + return res + } ``` === "Zig" @@ -148,7 +156,7 @@ status: new === "Rust" ```rust title="iteration.rs" - /* for 循环 */ + /* for 循环 */ fn for_loop(n: i32) -> i32 { let mut res = 0; // 循环求和 1, 2, ..., n-1, n @@ -294,7 +302,17 @@ status: new === "Swift" ```swift title="iteration.swift" - [class]{}-[func]{whileLoop} + /* while 循环 */ + func whileLoop(n: Int) -> Int { + var res = 0 + var i = 1 // 初始化条件变量 + // 循环求和 1, 2, ..., n-1, n + while i <= n { + res += i + i += 1 // 更新条件变量 + } + return res + } ``` === "Zig" @@ -473,7 +491,19 @@ status: new === "Swift" ```swift title="iteration.swift" - [class]{}-[func]{whileLoopII} + /* while 循环(两次更新) */ + func whileLoopII(n: Int) -> Int { + var res = 0 + var i = 1 // 初始化条件变量 + // 循环求和 1, 4, ... + while i <= n { + res += i + // 更新条件变量 + i += 1 + i *= 2 + } + return res + } ``` === "Zig" @@ -649,7 +679,18 @@ status: new === "Swift" ```swift title="iteration.swift" - [class]{}-[func]{nestedForLoop} + /* 双层 for 循环 */ + func nestedForLoop(n: Int) -> String { + var res = "" + // 循环 i = 1, 2, ..., n-1, n + for i in 1 ... n { + // 循环 j = 1, 2, ..., n-1, n + for j in 1 ... n { + res.append("(\(i), \(j)), ") + } + } + return res + } ``` === "Zig" @@ -829,7 +870,17 @@ status: new === "Swift" ```swift title="recursion.swift" - [class]{}-[func]{recur} + /* 递归 */ + func recur(n: Int) -> Int { + // 终止条件 + if n == 1 { + return 1 + } + // 递:递归调用 + let res = recur(n: n - 1) + // 归:返回结果 + return n + res + } ``` === "Zig" @@ -1006,7 +1057,15 @@ status: new === "Swift" ```swift title="recursion.swift" - [class]{}-[func]{tailRecur} + /* 尾递归 */ + func tailRecur(n: Int, res: Int) -> Int { + // 终止条件 + if n == 0 { + return res + } + // 尾递归调用 + return tailRecur(n: n - 1, res: res + n) + } ``` === "Zig" @@ -1179,7 +1238,17 @@ status: new === "Swift" ```swift title="recursion.swift" - [class]{}-[func]{fib} + /* 斐波那契数列:递归 */ + func fib(n: Int) -> Int { + // 终止条件 f(1) = 0, f(2) = 1 + if n == 1 || n == 2 { + return n - 1 + } + // 递归调用 f(n) = f(n-1) + f(n-2) + let res = fib(n: n - 1) + fib(n: n - 2) + // 返回结果 f(n) + return res + } ``` === "Zig" diff --git a/chapter_computational_complexity/time_complexity.md b/chapter_computational_complexity/time_complexity.md index 28793b49f..242a8a4d3 100755 --- a/chapter_computational_complexity/time_complexity.md +++ b/chapter_computational_complexity/time_complexity.md @@ -1695,7 +1695,7 @@ $$ let count = 0; // 计数器 // 外循环:未排序区间为 [0, i] for (let i = nums.length - 1; i > 0; i--) { - // 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端 + // 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端 for (let j = 0; j < i; j++) { if (nums[j] > nums[j + 1]) { // 交换 nums[j] 与 nums[j + 1] @@ -1718,7 +1718,7 @@ $$ let count = 0; // 计数器 // 外循环:未排序区间为 [0, i] for (let i = nums.length - 1; i > 0; i--) { - // 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端 + // 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端 for (let j = 0; j < i; j++) { if (nums[j] > nums[j + 1]) { // 交换 nums[j] 与 nums[j + 1] diff --git a/chapter_dynamic_programming/dp_problem_features.md b/chapter_dynamic_programming/dp_problem_features.md index ca7fdf103..e40f50557 100644 --- a/chapter_dynamic_programming/dp_problem_features.md +++ b/chapter_dynamic_programming/dp_problem_features.md @@ -659,10 +659,7 @@ $$ return 1; } // 初始化 dp 表,用于存储子问题的解 - const dp = Array.from( - { length: n + 1 }, - () => new Array(3) - ); + const dp = Array.from({ length: n + 1 }, () => new Array(3)); // 初始状态:预设最小子问题的解 dp[1][1] = 1; dp[1][2] = 0; diff --git a/chapter_dynamic_programming/knapsack_problem.md b/chapter_dynamic_programming/knapsack_problem.md index 0d494882a..1fe338269 100644 --- a/chapter_dynamic_programming/knapsack_problem.md +++ b/chapter_dynamic_programming/knapsack_problem.md @@ -185,8 +185,7 @@ $$ } // 计算不放入和放入物品 i 的最大价值 const no = knapsackDFS(wgt, val, i - 1, c); - const yes = - knapsackDFS(wgt, val, i - 1, c - wgt[i - 1]) + val[i - 1]; + const yes = knapsackDFS(wgt, val, i - 1, c - wgt[i - 1]) + val[i - 1]; // 返回两种方案中价值更大的那一个 return Math.max(no, yes); } @@ -438,7 +437,8 @@ $$ } // 计算不放入和放入物品 i 的最大价值 const no = knapsackDFSMem(wgt, val, mem, i - 1, c); - const yes = knapsackDFSMem(wgt, val, mem, i - 1, c - wgt[i - 1]) + val[i - 1]; + const yes = + knapsackDFSMem(wgt, val, mem, i - 1, c - wgt[i - 1]) + val[i - 1]; // 记录并返回两种方案中价值更大的那一个 mem[i][c] = Math.max(no, yes); return mem[i][c]; diff --git a/chapter_greedy/fractional_knapsack_problem.md b/chapter_greedy/fractional_knapsack_problem.md index 9fde59864..01cdf8007 100644 --- a/chapter_greedy/fractional_knapsack_problem.md +++ b/chapter_greedy/fractional_knapsack_problem.md @@ -193,17 +193,74 @@ status: new === "JS" ```javascript title="fractional_knapsack.js" - [class]{Item}-[func]{} + /* 物品 */ + class Item { + constructor(w, v) { + this.w = w; // 物品重量 + this.v = v; // 物品价值 + } + } - [class]{}-[func]{fractionalKnapsack} + /* 分数背包:贪心 */ + function fractionalKnapsack(wgt, val, cap) { + // 创建物品列表,包含两个属性:重量、价值 + const items = wgt.map((w, i) => new Item(w, val[i])); + // 按照单位价值 item.v / item.w 从高到低进行排序 + items.sort((a, b) => b.v / b.w - a.v / a.w); + // 循环贪心选择 + let res = 0; + for (const item of items) { + if (item.w <= cap) { + // 若剩余容量充足,则将当前物品整个装进背包 + res += item.v; + cap -= item.w; + } else { + // 若剩余容量不足,则将当前物品的一部分装进背包 + res += (item.v / item.w) * cap; + // 已无剩余容量,因此跳出循环 + break; + } + } + return res; + } ``` === "TS" ```typescript title="fractional_knapsack.ts" - [class]{Item}-[func]{} + /* 物品 */ + class Item { + w: number; // 物品重量 + v: number; // 物品价值 - [class]{}-[func]{fractionalKnapsack} + constructor(w: number, v: number) { + this.w = w; + this.v = v; + } + } + + /* 分数背包:贪心 */ + function fractionalKnapsack(wgt: number[], val: number[], cap: number): number { + // 创建物品列表,包含两个属性:重量、价值 + const items: Item[] = wgt.map((w, i) => new Item(w, val[i])); + // 按照单位价值 item.v / item.w 从高到低进行排序 + items.sort((a, b) => b.v / b.w - a.v / a.w); + // 循环贪心选择 + let res = 0; + for (const item of items) { + if (item.w <= cap) { + // 若剩余容量充足,则将当前物品整个装进背包 + res += item.v; + cap -= item.w; + } else { + // 若剩余容量不足,则将当前物品的一部分装进背包 + res += (item.v / item.w) * cap; + // 已无剩余容量,因此跳出循环 + break; + } + } + return res; + } ``` === "C" diff --git a/chapter_greedy/greedy_algorithm.md b/chapter_greedy/greedy_algorithm.md index 7e5791ae7..8ceb90721 100644 --- a/chapter_greedy/greedy_algorithm.md +++ b/chapter_greedy/greedy_algorithm.md @@ -121,13 +121,47 @@ status: new === "JS" ```javascript title="coin_change_greedy.js" - [class]{}-[func]{coinChangeGreedy} + /* 零钱兑换:贪心 */ + function coinChangeGreedy(coins, amt) { + // 假设 coins 数组有序 + let i = coins.length - 1; + let count = 0; + // 循环进行贪心选择,直到无剩余金额 + while (amt > 0) { + // 找到小于且最接近剩余金额的硬币 + while (i > 0 && coins[i] > amt) { + i--; + } + // 选择 coins[i] + amt -= coins[i]; + count++; + } + // 若未找到可行方案,则返回 -1 + return amt === 0 ? count : -1; + } ``` === "TS" ```typescript title="coin_change_greedy.ts" - [class]{}-[func]{coinChangeGreedy} + /* 零钱兑换:贪心 */ + function coinChangeGreedy(coins: number[], amt: number): number { + // 假设 coins 数组有序 + let i = coins.length - 1; + let count = 0; + // 循环进行贪心选择,直到无剩余金额 + while (amt > 0) { + // 找到小于且最接近剩余金额的硬币 + while (i > 0 && coins[i] > amt) { + i--; + } + // 选择 coins[i] + amt -= coins[i]; + count++; + } + // 若未找到可行方案,则返回 -1 + return amt === 0 ? count : -1; + } ``` === "C" diff --git a/chapter_greedy/max_capacity_problem.md b/chapter_greedy/max_capacity_problem.md index fc0edd15c..bf366bfc6 100644 --- a/chapter_greedy/max_capacity_problem.md +++ b/chapter_greedy/max_capacity_problem.md @@ -193,13 +193,53 @@ $$ === "JS" ```javascript title="max_capacity.js" - [class]{}-[func]{maxCapacity} + /* 最大容量:贪心 */ + function maxCapacity(ht) { + // 初始化 i, j 分列数组两端 + let i = 0, + j = ht.length - 1; + // 初始最大容量为 0 + let res = 0; + // 循环贪心选择,直至两板相遇 + while (i < j) { + // 更新最大容量 + const cap = Math.min(ht[i], ht[j]) * (j - i); + res = Math.max(res, cap); + // 向内移动短板 + if (ht[i] < ht[j]) { + i += 1; + } else { + j -= 1; + } + } + return res; + } ``` === "TS" ```typescript title="max_capacity.ts" - [class]{}-[func]{maxCapacity} + /* 最大容量:贪心 */ + function maxCapacity(ht: number[]): number { + // 初始化 i, j 分列数组两端 + let i = 0, + j = ht.length - 1; + // 初始最大容量为 0 + let res = 0; + // 循环贪心选择,直至两板相遇 + while (i < j) { + // 更新最大容量 + const cap: number = Math.min(ht[i], ht[j]) * (j - i); + res = Math.max(res, cap); + // 向内移动短板 + if (ht[i] < ht[j]) { + i += 1; + } else { + j -= 1; + } + } + return res; + } ``` === "C" diff --git a/chapter_greedy/max_product_cutting_problem.md b/chapter_greedy/max_product_cutting_problem.md index e53fdd988..a8b33a182 100644 --- a/chapter_greedy/max_product_cutting_problem.md +++ b/chapter_greedy/max_product_cutting_problem.md @@ -172,13 +172,51 @@ $$ === "JS" ```javascript title="max_product_cutting.js" - [class]{}-[func]{maxProductCutting} + /* 最大切分乘积:贪心 */ + function maxProductCutting(n) { + // 当 n <= 3 时,必须切分出一个 1 + if (n <= 3) { + return 1 * (n - 1); + } + // 贪心地切分出 3 ,a 为 3 的个数,b 为余数 + let a = Math.floor(n / 3); + let b = n % 3; + if (b === 1) { + // 当余数为 1 时,将一对 1 * 3 转化为 2 * 2 + return Math.pow(3, a - 1) * 2 * 2; + } + if (b === 2) { + // 当余数为 2 时,不做处理 + return Math.pow(3, a) * 2; + } + // 当余数为 0 时,不做处理 + return Math.pow(3, a); + } ``` === "TS" ```typescript title="max_product_cutting.ts" - [class]{}-[func]{maxProductCutting} + /* 最大切分乘积:贪心 */ + function maxProductCutting(n: number): number { + // 当 n <= 3 时,必须切分出一个 1 + if (n <= 3) { + return 1 * (n - 1); + } + // 贪心地切分出 3 ,a 为 3 的个数,b 为余数 + let a: number = Math.floor(n / 3); + let b: number = n % 3; + if (b === 1) { + // 当余数为 1 时,将一对 1 * 3 转化为 2 * 2 + return Math.pow(3, a - 1) * 2 * 2; + } + if (b === 2) { + // 当余数为 2 时,不做处理 + return Math.pow(3, a) * 2; + } + // 当余数为 0 时,不做处理 + return Math.pow(3, a); + } ``` === "C" diff --git a/chapter_searching/binary_search_insertion.md b/chapter_searching/binary_search_insertion.md index 889db664f..e782637d6 100644 --- a/chapter_searching/binary_search_insertion.md +++ b/chapter_searching/binary_search_insertion.md @@ -141,7 +141,10 @@ status: new ```typescript title="binary_search_insertion.ts" /* 二分查找插入点(无重复元素) */ - function binarySearchInsertionSimple(nums: Array, target: number): number { + function binarySearchInsertionSimple( + nums: Array, + target: number + ): number { let i = 0, j = nums.length - 1; // 初始化双闭区间 [0, n-1] while (i <= j) { diff --git a/chapter_searching/replace_linear_by_hashing.md b/chapter_searching/replace_linear_by_hashing.md index 66f7ff9bc..15a46c901 100755 --- a/chapter_searching/replace_linear_by_hashing.md +++ b/chapter_searching/replace_linear_by_hashing.md @@ -330,7 +330,7 @@ comments: true // 单层循环,时间复杂度 O(n) for (let i = 0; i < nums.length; i++) { if (m[target - nums[i]] !== undefined) { - return [m[target-nums[i]], i]; + return [m[target - nums[i]], i]; } else { m[nums[i]] = i; } diff --git a/chapter_sorting/bubble_sort.md b/chapter_sorting/bubble_sort.md index 3b32e7a9b..4087b9283 100755 --- a/chapter_sorting/bubble_sort.md +++ b/chapter_sorting/bubble_sort.md @@ -123,7 +123,7 @@ comments: true function bubbleSort(nums) { // 外循环:未排序区间为 [0, i] for (let i = nums.length - 1; i > 0; i--) { - // 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端 + // 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端 for (let j = 0; j < i; j++) { if (nums[j] > nums[j + 1]) { // 交换 nums[j] 与 nums[j + 1] @@ -143,7 +143,7 @@ comments: true function bubbleSort(nums: number[]): void { // 外循环:未排序区间为 [0, i] for (let i = nums.length - 1; i > 0; i--) { - // 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端 + // 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端 for (let j = 0; j < i; j++) { if (nums[j] > nums[j + 1]) { // 交换 nums[j] 与 nums[j + 1] @@ -380,7 +380,7 @@ comments: true // 外循环:未排序区间为 [0, i] for (let i = nums.length - 1; i > 0; i--) { let flag = false; // 初始化标志位 - // 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端 + // 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端 for (let j = 0; j < i; j++) { if (nums[j] > nums[j + 1]) { // 交换 nums[j] 与 nums[j + 1] @@ -403,7 +403,7 @@ comments: true // 外循环:未排序区间为 [0, i] for (let i = nums.length - 1; i > 0; i--) { let flag = false; // 初始化标志位 - // 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端 + // 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端 for (let j = 0; j < i; j++) { if (nums[j] > nums[j + 1]) { // 交换 nums[j] 与 nums[j + 1] diff --git a/chapter_tree/binary_search_tree.md b/chapter_tree/binary_search_tree.md index 7d0652e80..22307123e 100755 --- a/chapter_tree/binary_search_tree.md +++ b/chapter_tree/binary_search_tree.md @@ -155,17 +155,16 @@ comments: true ```typescript title="binary_search_tree.ts" /* 查找节点 */ - function search(num: number): TreeNode | null { - let cur = root; + search(num: number): TreeNode | null { + let cur = this.root; // 循环查找,越过叶节点后跳出 while (cur !== null) { - if (cur.val < num) { - cur = cur.right; // 目标节点在 cur 的右子树中 - } else if (cur.val > num) { - cur = cur.left; // 目标节点在 cur 的左子树中 - } else { - break; // 找到目标节点,跳出循环 - } + // 目标节点在 cur 的右子树中 + if (cur.val < num) cur = cur.right; + // 目标节点在 cur 的左子树中 + else if (cur.val > num) cur = cur.left; + // 找到目标节点,跳出循环 + else break; } // 返回目标节点 return cur; @@ -489,7 +488,7 @@ comments: true else cur = cur.left; } // 插入节点 - let node = new TreeNode(num); + const node = new TreeNode(num); if (pre.val < num) pre.right = node; else pre.left = node; } @@ -499,33 +498,28 @@ comments: true ```typescript title="binary_search_tree.ts" /* 插入节点 */ - function insert(num: number): void { + insert(num: number): void { // 若树为空,则初始化根节点 - if (root === null) { - root = new TreeNode(num); + if (this.root === null) { + this.root = new TreeNode(num); return; } - let cur = root, + let cur: TreeNode | null = this.root, pre: TreeNode | null = null; // 循环查找,越过叶节点后跳出 while (cur !== null) { - if (cur.val === num) { - return; // 找到重复节点,直接返回 - } + // 找到重复节点,直接返回 + if (cur.val === num) return; pre = cur; - if (cur.val < num) { - cur = cur.right as TreeNode; // 插入位置在 cur 的右子树中 - } else { - cur = cur.left as TreeNode; // 插入位置在 cur 的左子树中 - } + // 插入位置在 cur 的右子树中 + if (cur.val < num) cur = cur.right; + // 插入位置在 cur 的左子树中 + else cur = cur.left; } // 插入节点 - let node = new TreeNode(num); - if (pre!.val < num) { - pre!.right = node; - } else { - pre!.left = node; - } + const node = new TreeNode(num); + if (pre!.val < num) pre!.right = node; + else pre!.left = node; } ``` @@ -1044,7 +1038,7 @@ comments: true // 子节点数量 = 0 or 1 if (cur.left === null || cur.right === null) { // 当子节点数量 = 0 / 1 时, child = null / 该子节点 - let child = cur.left !== null ? cur.left : cur.right; + const child = cur.left !== null ? cur.left : cur.right; // 删除节点 cur if (cur !== this.root) { if (pre.left === cur) pre.left = child; @@ -1073,57 +1067,48 @@ comments: true ```typescript title="binary_search_tree.ts" /* 删除节点 */ - function remove(num: number): void { + remove(num: number): void { // 若树为空,直接提前返回 - if (root === null) { - return; - } - let cur = root, + if (this.root === null) return; + let cur: TreeNode | null = this.root, pre: TreeNode | null = null; // 循环查找,越过叶节点后跳出 while (cur !== null) { // 找到待删除节点,跳出循环 - if (cur.val === num) { - break; - } + if (cur.val === num) break; pre = cur; - if (cur.val < num) { - cur = cur.right as TreeNode; // 待删除节点在 cur 的右子树中 - } else { - cur = cur.left as TreeNode; // 待删除节点在 cur 的左子树中 - } + // 待删除节点在 cur 的右子树中 + if (cur.val < num) cur = cur.right; + // 待删除节点在 cur 的左子树中 + else cur = cur.left; } // 若无待删除节点,则直接返回 - if (cur === null) { - return; - } + if (cur === null) return; // 子节点数量 = 0 or 1 if (cur.left === null || cur.right === null) { // 当子节点数量 = 0 / 1 时, child = null / 该子节点 - let child = cur.left !== null ? cur.left : cur.right; + const child: TreeNode | null = + cur.left !== null ? cur.left : cur.right; // 删除节点 cur - if (cur != root) { - if (pre!.left === cur) { - pre!.left = child; - } else { - pre!.right = child; - } + if (cur !== this.root) { + if (pre!.left === cur) pre!.left = child; + else pre!.right = child; } else { // 若删除节点为根节点,则重新指定根节点 - root = child; + this.root = child; } } // 子节点数量 = 2 else { // 获取中序遍历中 cur 的下一个节点 - let tmp = cur.right; - while (tmp.left !== null) { - tmp = tmp.left; + let tmp: TreeNode | null = cur.right; + while (tmp!.left !== null) { + tmp = tmp!.left; } // 递归删除节点 tmp - remove(tmp!.val); + this.remove(tmp!.val); // 用 tmp 覆盖 cur - cur.val = tmp.val; + cur.val = tmp!.val; } } ```