From 1a55dbdf2e77bc12cbdcb9441ac87f694cb06d3c Mon Sep 17 00:00:00 2001 From: krahets Date: Fri, 21 Jul 2023 15:18:01 +0800 Subject: [PATCH] Finetune doc and code. --- .../chapter_graph/graph_adjacency_list_test.c | 1 - codes/c/chapter_graph/graph_bfs.c | 1 - .../backtracking_algorithm.md | 12 ++++----- .../fractional_knapsack_problem.md | 26 +++++++++---------- docs/chapter_greedy/greedy_algorithm.md | 6 ++--- 5 files changed, 21 insertions(+), 25 deletions(-) diff --git a/codes/c/chapter_graph/graph_adjacency_list_test.c b/codes/c/chapter_graph/graph_adjacency_list_test.c index 6b0384f87..4670adef7 100644 --- a/codes/c/chapter_graph/graph_adjacency_list_test.c +++ b/codes/c/chapter_graph/graph_adjacency_list_test.c @@ -8,7 +8,6 @@ /* Driver Code */ int main() { - /* 初始化无向图 */ graphAdjList *graph = newGraphAdjList(5); // 初始化顶点 diff --git a/codes/c/chapter_graph/graph_bfs.c b/codes/c/chapter_graph/graph_bfs.c index 51a384f03..c943d0da8 100644 --- a/codes/c/chapter_graph/graph_bfs.c +++ b/codes/c/chapter_graph/graph_bfs.c @@ -118,7 +118,6 @@ Vertex **graphBFS(graphAdjList *t, Vertex *startVet) { resIndex++; queuePop(que); // 队首元素出队 } - // 释放内存 freeQueue(que); freeHash(visited); diff --git a/docs/chapter_backtracking/backtracking_algorithm.md b/docs/chapter_backtracking/backtracking_algorithm.md index b3a64c7a7..c9ad1595c 100644 --- a/docs/chapter_backtracking/backtracking_algorithm.md +++ b/docs/chapter_backtracking/backtracking_algorithm.md @@ -8,7 +8,7 @@ 给定一个二叉树,搜索并记录所有值为 $7$ 的节点,返回节点列表。 -**解题思路**:前序遍历这颗树,并判断当前节点的值是否为 $7$ ,若是则将该节点的值加入到结果列表 `res` 之中。 +对于此题,我们前序遍历这颗树,并判断当前节点的值是否为 $7$ ,若是则将该节点的值加入到结果列表 `res` 之中。 === "Java" @@ -90,7 +90,7 @@ 在二叉树中搜索所有值为 $7$ 的节点,**返回根节点到这些节点的路径**。 -**解题思路**:在例题一代码的基础上,我们需要借助一个列表 `path` 记录访问过的节点路径。当访问到值为 $7$ 的节点时,则复制 `path` 并添加进结果列表 `res` 。遍历完成后,`res` 中保存的就是所有的解。 +在例题一代码的基础上,我们需要借助一个列表 `path` 记录访问过的节点路径。当访问到值为 $7$ 的节点时,则复制 `path` 并添加进结果列表 `res` 。遍历完成后,`res` 中保存的就是所有的解。 === "Java" @@ -199,12 +199,12 @@ !!! question "例题三" - 在二叉树中搜索所有值为 $7$ 的节点,返回根节点到这些节点的路径,**路径中有且只有一个值为 $7$ 的节点,并且不能包含值为 $3$ 的节点**。 + 在二叉树中搜索所有值为 $7$ 的节点,返回根节点到这些节点的路径,**要求路径中有且只有一个值为 $7$ 的节点,并且不能包含值为 $3$ 的节点**。 -**解题思路**:在例题二的基础上添加剪枝操作。 +在例题二的基础上添加剪枝操作,包括: -- 当遇到值为 $7$ 的节点时,记录解并停止搜索。 -- 当遇到值为 $3$ 的节点时,则终止继续搜索。 +- 当遇到值为 $7$ 的节点时,记录解并返回,终止搜索。 +- 当遇到值为 $3$ 的节点时,则直接返回,停止继续搜索。 === "Java" diff --git a/docs/chapter_greedy/fractional_knapsack_problem.md b/docs/chapter_greedy/fractional_knapsack_problem.md index 70ce3dbb0..143da33af 100644 --- a/docs/chapter_greedy/fractional_knapsack_problem.md +++ b/docs/chapter_greedy/fractional_knapsack_problem.md @@ -29,17 +29,7 @@ ![分数背包的贪心策略](fractional_knapsack_problem.assets/fractional_knapsack_greedy_strategy.png) -**第三步:正确性证明** - -采用反证法。假设物品 $x$ 是单位价值最高的物品,使用某算法求得最大价值为 $res$ ,但该解中不包含物品 $x$ 。 - -现在从背包中拿出单位重量的任意物品,并替换为单位重量的物品 $x$ 。由于物品 $x$ 的单位价值最高,因此替换后的总价值一定大于 $res$ 。**这与 $res$ 是最优解矛盾,说明最优解中必须包含物品 $x$ 。** - -对于该解中的其他物品,我们也可以构建出上述矛盾。总而言之,**单位价值更大的物品总是更优选择**,这说明贪心策略是有效的。 - -**实现代码** - -我们构建了一个物品类 `Item` ,以便将物品按照单位价值进行排序。在循环贪心选择中,分为放入整个物品或放入部分物品两种情况。当背包已满时,则跳出循环并返回解。 +我们构建了一个物品类 `Item` ,以便将物品按照单位价值进行排序。循环进行贪心选择,当背包已满时跳出并返回解。 === "Java" @@ -129,8 +119,16 @@ [class]{}-[func]{fractionalKnapsack} ``` -如下图所示,如果将一个 2D 图表的横轴和纵轴分别看作物品重量和物品单位价值,则分数背包问题可被转化为“求在有限横轴区间下的最大围成面积”。这个类比可以帮助我们从几何角度清晰地看到贪心策略的有效性。 +最差情况下,需要遍历整个物品列表,**因此时间复杂度为 $O(n)$** ,其中 $n$ 为物品数量。由于初始化了一个 `Item` 对象列表,**因此空间复杂度为 $O(n)$** 。 -![分数背包问题的几何表示](fractional_knapsack_problem.assets/fractional_knapsack_area_chart.png) +**第三步:正确性证明** -最差情况下,需要遍历整个物品列表,**因此时间复杂度为 $O(n)$** ,其中 $n$ 为物品数量。由于初始化了一个 `Item` 对象列表,**因此空间复杂度为 $O(n)$** 。 +采用反证法。假设物品 $x$ 是单位价值最高的物品,使用某算法求得最大价值为 $res$ ,但该解中不包含物品 $x$ 。 + +现在从背包中拿出单位重量的任意物品,并替换为单位重量的物品 $x$ 。由于物品 $x$ 的单位价值最高,因此替换后的总价值一定大于 $res$ 。**这与 $res$ 是最优解矛盾,说明最优解中必须包含物品 $x$ 。** + +对于该解中的其他物品,我们也可以构建出上述矛盾。总而言之,**单位价值更大的物品总是更优选择**,这说明贪心策略是有效的。 + +如下图所示,如果将物品重量和物品单位价值分别看作一个 2D 图表的横轴和纵轴,则分数背包问题可被转化为“求在有限横轴区间下的最大围成面积”。这个类比可以帮助我们从几何角度清晰地看到贪心策略的有效性。 + +![分数背包问题的几何表示](fractional_knapsack_problem.assets/fractional_knapsack_area_chart.png) diff --git a/docs/chapter_greedy/greedy_algorithm.md b/docs/chapter_greedy/greedy_algorithm.md index 901b0f26a..d68558dab 100644 --- a/docs/chapter_greedy/greedy_algorithm.md +++ b/docs/chapter_greedy/greedy_algorithm.md @@ -11,7 +11,7 @@ !!! question - 给定 $n$ 种硬币,第 $i$ 个硬币的面值为 $coins[i - 1]$ ,目标金额为 $amt$ ,**每种硬币可以重复选取**,问能够凑出目标金额的最少硬币个数。如果无法凑出目标金额则返回 $-1$ 。 + 给定 $n$ 种硬币,第 $i$ 个硬币的面值为 $coins[i - 1]$ ,目标金额为 $amt$ ,每种硬币可以重复选取,问能够凑出目标金额的最少硬币个数。如果无法凑出目标金额则返回 $-1$ 。 贪心算法会迭代地做出一个又一个的贪心选择,每轮都将问题转化成一个规模更小的子问题,直到问题被解决。 @@ -133,8 +133,8 @@ 确定贪心策略是求解问题的核心步骤,但实施起来并没有那么容易。主要有两方面原因: -1. **不同问题的贪心策略的差异较大**。对于许多问题来说,贪心策略都比较浅显,我们通过一些大概的思考与尝试就能得出。而对于一些复杂问题,贪心策略可能非常隐蔽,这种情况就非常考验个人的解题经验与算法能力了。 -2. **某些贪心策略具有较强的迷惑性**。当我们满怀信心设计好贪心策略,写出解题代码并提交运行,很可能发现部分测试样例无法通过。这是因为设计的贪心策略只是“部分正确”的,上文介绍的零钱兑换就是个很好的例子。 +- **不同问题的贪心策略的差异较大**。对于许多问题来说,贪心策略都比较浅显,我们通过一些大概的思考与尝试就能得出。而对于一些复杂问题,贪心策略可能非常隐蔽,这种情况就非常考验个人的解题经验与算法能力了。 +- **某些贪心策略具有较强的迷惑性**。当我们满怀信心设计好贪心策略,写出解题代码并提交运行,很可能发现部分测试样例无法通过。这是因为设计的贪心策略只是“部分正确”的,上文介绍的零钱兑换就是个很好的例子。 为了保证正确性,我们应该对贪心策略进行严谨的数学证明,**通常需要用到反证法或数学归纳法**。