diff --git a/codes/c/chapter_backtracking/subset_sum_i.c b/codes/c/chapter_backtracking/subset_sum_i.c index cfe34157a..8d2b23b77 100644 --- a/codes/c/chapter_backtracking/subset_sum_i.c +++ b/codes/c/chapter_backtracking/subset_sum_i.c @@ -39,7 +39,7 @@ int comp(const void *a, const void *b) { } /* 求解子集和 I */ -vector *subsetSumINaive(vector *nums, int target) { +vector *subsetSumI(vector *nums, int target) { vector *state = newVector(); // 状态(子集) qsort(nums->data[0], nums->size, sizeof(int), comp); // 对 nums 进行排序 int start = 0; // 子集和 @@ -63,7 +63,7 @@ int main() { } int target = 9; - vector *res = subsetSumINaive(vNums, target); + vector *res = subsetSumI(vNums, target); printf("输入数组 nums = "); printVector(vNums, printFunc); diff --git a/codes/c/chapter_backtracking/subset_sum_ii.c b/codes/c/chapter_backtracking/subset_sum_ii.c index e7184326d..df9b9b85d 100644 --- a/codes/c/chapter_backtracking/subset_sum_ii.c +++ b/codes/c/chapter_backtracking/subset_sum_ii.c @@ -6,7 +6,7 @@ #include "../utils/common.h" -/* 回溯算法:子集和 I */ +/* 回溯算法:子集和 II */ void backtrack(vector *state, int target, vector *choices, int start, vector *res) { // 子集和等于 target 时,记录解 if (target == 0) { @@ -26,7 +26,6 @@ void backtrack(vector *state, int target, vector *choices, int start, vector *re if (target - *(int *)(choices->data[i]) < 0) { continue; } - // 剪枝四:如果该元素与左边元素相等,说明该搜索分支重复,直接跳过 if (i > start && *(int *)(choices->data[i]) == *(int *)(choices->data[i - 1])) { continue; @@ -40,13 +39,13 @@ void backtrack(vector *state, int target, vector *choices, int start, vector *re } } -/* 用来做比较的函数 */ +/* 比较规则 */ int comp(const void *a, const void *b) { return *(int *)a - *(int *)b; } -/* 求解子集和 I */ -vector *subsetSumINaive(vector *nums, int target) { +/* 求解子集和 II */ +vector *subsetSumII(vector *nums, int target) { vector *state = newVector(); // 状态(子集) qsort(nums->data[0], nums->size, sizeof(int), comp); // 对 nums 进行排序 int start = 0; // 子集和 @@ -70,7 +69,7 @@ int main() { } int target = 9; - vector *res = subsetSumINaive(vNums, target); + vector *res = subsetSumII(vNums, target); printf("输入数组 nums = "); printVector(vNums, printFunc); diff --git a/codes/python/chapter_stack_and_queue/array_queue.py b/codes/python/chapter_stack_and_queue/array_queue.py index 28e9e6f4b..119393a0e 100644 --- a/codes/python/chapter_stack_and_queue/array_queue.py +++ b/codes/python/chapter_stack_and_queue/array_queue.py @@ -31,7 +31,7 @@ class ArrayQueue: if self.__size == self.capacity(): raise IndexError("队列已满") # 计算尾指针,指向队尾索引 + 1 - # 通过取余操作,实现 rear 越过数组尾部后回到头部F + # 通过取余操作,实现 rear 越过数组尾部后回到头部 rear: int = (self.__front + self.__size) % self.capacity() # 将 num 添加至队尾 self.__nums[rear] = num diff --git a/docs/chapter_divide_and_conquer/hanota_problem.md b/docs/chapter_divide_and_conquer/hanota_problem.md index e79370dae..ab43091f3 100644 --- a/docs/chapter_divide_and_conquer/hanota_problem.md +++ b/docs/chapter_divide_and_conquer/hanota_problem.md @@ -66,7 +66,7 @@ === "<4>" ![hanota_f3_step4](hanota_problem.assets/hanota_f3_step4.png) -本质上看,**我们将问题 $f(3)$ 划分为两个子问题 $f(2)$ 和子问题 $f(1)$** 。按顺序解决这三个子问题之后,原问题随之得到解决。这说明子问题是独立的,且解是可以合并的。 +本质上看,**我们将问题 $f(3)$ 划分为两个子问题 $f(2)$ 和子问题 $f(1)$** 。按顺序解决这三个子问题之后,原问题随之得到解决。这说明子问题是独立的,而且解是可以合并的。 至此,我们可总结出汉诺塔问题的分治策略:将原问题 $f(n)$ 划分为两个子问题 $f(n-1)$ 和一个子问题 $f(1)$ 。子问题的解决顺序为: diff --git a/docs/chapter_dynamic_programming/dp_problem_features.md b/docs/chapter_dynamic_programming/dp_problem_features.md index 9a9b1ac73..bbfc1d882 100644 --- a/docs/chapter_dynamic_programming/dp_problem_features.md +++ b/docs/chapter_dynamic_programming/dp_problem_features.md @@ -1,9 +1,9 @@ # 动态规划问题特性 -在上节中,我们学习了动态规划是如何通过子问题分解来求解问题的。实际上,子问题分解是一种通用的算法思路,在分治、动态规划、回溯中各有特点: +在上节中,我们学习了动态规划是如何通过子问题分解来求解问题的。实际上,子问题分解是一种通用的算法思路,在分治、动态规划、回溯中的侧重点不同: -- 「分治算法」递归地将原问题划分为多个互相独立的子问题,直至最小子问题,并在回溯中合并子问题的解,最终得到原问题的解。 -- 「动态规划」也对问题进行递归分解,但与分治算法的主要区别是,**动态规划中的子问题往往不是相互独立的**,原问题的解依赖于子问题的解,而子问题的解又依赖于更小的子问题的解。 +- 「分治算法」递归地将原问题划分为多个相互独立的子问题,直至最小子问题,并在回溯中合并子问题的解,最终得到原问题的解。 +- 「动态规划」也对问题进行递归分解,但与分治算法的主要区别是,动态规划中的子问题是相互依赖的,在分解过程中会出现许多重叠子问题。 - 「回溯算法」在尝试和回退中穷举所有可能的解,并通过剪枝避免不必要的搜索分支。原问题的解由一系列决策步骤构成,我们可以将每个决策步骤之前的子序列看作为一个子问题。 实际上,动态规划常用来求解最优化问题,它们不仅包含重叠子问题,还具有另外两大特性:最优子结构、无后效性。 diff --git a/docs/chapter_dynamic_programming/intro_to_dynamic_programming.md b/docs/chapter_dynamic_programming/intro_to_dynamic_programming.md index 041a0c7c5..7ff397ffc 100644 --- a/docs/chapter_dynamic_programming/intro_to_dynamic_programming.md +++ b/docs/chapter_dynamic_programming/intro_to_dynamic_programming.md @@ -128,7 +128,7 @@ $$ dp[i] = dp[i-1] + dp[i-2] $$ -这意味着在爬楼梯问题中,**各个子问题之间不是相互独立的,原问题的解可以从子问题的解构建得来**。 +这意味着在爬楼梯问题中,各个子问题之间存在递推关系,**原问题的解可以由子问题的解构建得来**。 ![方案数量递推关系](intro_to_dynamic_programming.assets/climbing_stairs_state_transfer.png) diff --git a/docs/chapter_preface/about_the_book.md b/docs/chapter_preface/about_the_book.md index 0c411176f..4bfdceeb6 100644 --- a/docs/chapter_preface/about_the_book.md +++ b/docs/chapter_preface/about_the_book.md @@ -22,7 +22,7 @@ 本书主要内容包括: -- **复杂度分析**:数据结构和算法的评价维度,算法效率的评估方法。时间复杂度、空间复杂度的推算方法、常见类型、示例等。 +- **复杂度分析**:数据结构和算法的评价维度与方法。时间复杂度、空间复杂度的推算方法、常见类型、示例等。 - **数据结构**:基本数据类型,数据结构的分类方法。数组、链表、栈、队列、散列表、树、堆、图等数据结构的定义、优缺点、常用操作、常见类型、典型应用、实现方法等。 - **算法**:搜索、排序、分治、回溯、动态规划、贪心等算法的定义、优缺点、效率、应用场景、解题步骤、示例题目等。