Hello 算法 1.0.0b4 C#版preOrder(root.right); // 回退 path.RemoveAt(path.Count - 1); } 剪枝是一个非常形象的名词。在搜索过程中,我们“剪掉”了不满足约束条件的搜索分支,避免许多无意义 的尝试,从而实现搜索效率的提高。 Figure 13‑3. 根据约束条件剪枝 13.1.3. 框架代码 接下来,我们尝试将回溯的“尝试、回退、剪枝”的主体框架提炼出来,提升代码的通用性。 ‧ 遍历选择列表 choices 时,跳过所有已被选择过的节点,即剪枝。 如下图所示,假设我们第一轮选择 1 ,第二轮选择 3 ,第三轮选择 2 ,则需要在第二轮剪掉元素 1 的分支, 在第三轮剪掉元素 1, 3 的分支。 13. 回溯 hello‑algo.com 253 Figure 13‑6. 全排列剪枝示例 观察上图发现,该剪枝操作将搜索空间大小从 ?(??) 降低至 ?(?!) 。 雅,因为生成重复排列的搜索分支是没有必要的,应当被提前识别并剪枝,这样可以进一步提升算法效率。 13. 回溯 hello‑algo.com 255 相等元素剪枝 观察发现,在第一轮中,选择 1 或选择 ̂1 是等价的,在这两个选择之下生成的所有排列都是重复的。因此应 该把 ̂1 剪枝掉。 同理,在第一轮选择 2 后,第二轮选择中的 1 和 ̂1 也会产生重复分支,因此也应将第二轮的 ̂10 码力 | 341 页 | 27.39 MB | 1 年前3
Hello 算法 1.1.0 C#版res = Fib(n - 1) + Fib(n - 2); // 返回结果 f(n) return res; } 观察以上代码,我们在函数内递归调用了两个函数,这意味着从一个调用产生了两个调用分支。如图 2‑6 所 示,这样不断递归调用下去,最终将产生一棵层数为 ? 的递归树(recursion tree)。 图 2‑6 斐波那契数列的递归树 从本质上看,递归体现了“将问题分解为更小子 com 212 循环完成后,? 指向最左边的 target ,? 指向首个小于 target 的元素,因此索引 ? 就是插入点。 图 10‑6 二分查找重复元素的插入点的步骤 观察以下代码,判断分支 nums[m] > target 和 nums[m] == target 的操作相同,因此两者可以合并。 即便如此,我们仍然可以将判断条件保持展开,因为其逻辑更加清晰、可读性更好。 第 10 章 时,跳过所有已被选择的节点,即剪枝。 如图 13‑6 所示,假设我们第一轮选择 1 ,第二轮选择 3 ,第三轮选择 2 ,则需要在第二轮剪掉元素 1 的分 支,在第三轮剪掉元素 1 和元素 3 的分支。 图 13‑6 全排列剪枝示例 观察图 13‑6 发现,该剪枝操作将搜索空间大小从 ?(??) 减小至 ?(?!) 。 第 13 章 回溯 hello‑algo.com 284 2. 代码实现0 码力 | 378 页 | 18.47 MB | 1 年前3
Hello 算法 1.2.0 简体中文 C# 版res = Fib(n - 1) + Fib(n - 2); // 返回结果 f(n) return res; } 观察以上代码,我们在函数内递归调用了两个函数,这意味着从一个调用产生了两个调用分支。如图 2‑6 所 示,这样不断递归调用下去,最终将产生一棵层数为 ? 的递归树(recursion tree)。 图 2‑6 斐波那契数列的递归树 从本质上看,递归体现了“将问题分解为更小子 com 212 循环完成后,? 指向最左边的 target ,? 指向首个小于 target 的元素,因此索引 ? 就是插入点。 图 10‑6 二分查找重复元素的插入点的步骤 观察以下代码,判断分支 nums[m] > target 和 nums[m] == target 的操作相同,因此两者可以合并。 即便如此,我们仍然可以将判断条件保持展开,因为其逻辑更加清晰、可读性更好。 第 10 章 时,跳过所有已被选择的节点,即剪枝。 如图 13‑6 所示,假设我们第一轮选择 1 ,第二轮选择 3 ,第三轮选择 2 ,则需要在第二轮剪掉元素 1 的分 支,在第三轮剪掉元素 1 和元素 3 的分支。 图 13‑6 全排列剪枝示例 观察图 13‑6 发现,该剪枝操作将搜索空间大小从 ?(??) 减小至 ?(?!) 。 第 13 章 回溯 www.hello‑algo.com 284 20 码力 | 379 页 | 18.48 MB | 10 月前3
Hello 算法 1.0.0b5 C#版res = fib(n - 1) + fib(n - 2); // 返回结果 f(n) return res; } 观察以上代码,我们在函数内递归调用了两个函数,这意味着从一个调用产生了两个调用分支。如图 2‑6 所 示,这样不断递归调用下去,最终将产生一个层数为 ? 的「递归树 recursion tree」。 图 2‑6 斐波那契数列的递归树 本质上看,递归体现“将问题分解为更小子问 ,? 指向首个小于 target 的元素,因此索引 ? 就是插入点。 第 10 章 搜索 hello‑algo.com 211 图 10‑6 二分查找重复元素的插入点的步骤 观察以下代码,判断分支 nums[m] > target 和 nums[m] == target 的操作相同,因此两者可以合并。 即便如此,我们仍然可以将判断条件保持展开,因为其逻辑更加清晰、可读性更好。 // === right); // 回退 path.RemoveAt(path.Count - 1); } 剪枝是一个非常形象的名词。如图 13‑3 所示,在搜索过程中,我们“剪掉”了不满足约束条件的搜索分支, 避免许多无意义的尝试,从而提高了搜索效率。 图 13‑3 根据约束条件剪枝 13.1.3 框架代码 接下来,我们尝试将回溯的“尝试、回退、剪枝”的主体框架提炼出来,提升代码的通用性。 在以下框架代码中,state0 码力 | 376 页 | 30.69 MB | 1 年前3
Hello 算法 1.0.0 C#版res = Fib(n - 1) + Fib(n - 2); // 返回结果 f(n) return res; } 观察以上代码,我们在函数内递归调用了两个函数,这意味着从一个调用产生了两个调用分支。如图 2‑6 所 示,这样不断递归调用下去,最终将产生一棵层数为 ? 的「递归树 recursion tree」。 图 2‑6 斐波那契数列的递归树 从本质上看,递归体现了“将问题分解为更小 com 213 循环完成后,? 指向最左边的 target ,? 指向首个小于 target 的元素,因此索引 ? 就是插入点。 图 10‑6 二分查找重复元素的插入点的步骤 观察以下代码,判断分支 nums[m] > target 和 nums[m] == target 的操作相同,因此两者可以合并。 即便如此,我们仍然可以将判断条件保持展开,因为其逻辑更加清晰、可读性更好。 第 10 章 时,跳过所有已被选择的节点,即剪枝。 如图 13‑6 所示,假设我们第一轮选择 1 ,第二轮选择 3 ,第三轮选择 2 ,则需要在第二轮剪掉元素 1 的分 支,在第三轮剪掉元素 1 和元素 3 的分支。 图 13‑6 全排列剪枝示例 观察图 13‑6 发现,该剪枝操作将搜索空间大小从 ?(??) 减小至 ?(?!) 。 第 13 章 回溯 hello‑algo.com 285 2. 代码实现0 码力 | 376 页 | 17.59 MB | 1 年前3
Hello 算法 1.2.0 繁体中文 C# 版res = Fib(n - 1) + Fib(n - 2); // 返回結果 f(n) return res; } 觀察以上程式碼,我們在函式內遞迴呼叫了兩個函式,這意味著從一個呼叫產生了兩個呼叫分支。如圖 2‑6 所示,這樣不斷遞迴呼叫下去,最終將產生一棵層數為 ? 的遞迴樹(recursion tree)。 圖 2‑6 費波那契數列的遞迴樹 從本質上看,遞迴體現了“將問題分解為更小子問題”的思維範式,這種分治策略至關重要。 com 212 迴圈完成後,? 指向最左邊的 target ,? 指向首個小於 target 的元素,因此索引 ? 就是插入點。 圖 10‑6 二分搜尋重複元素的插入點的步驟 觀察以下程式碼,判斷分支 nums[m] > target 和 nums[m] == target 的操作相同,因此兩者可以合併。 即便如此,我們仍然可以將判斷條件保持展開,因為其邏輯更加清晰、可讀性更好。 第 10 章 時,跳過所有已被選擇的節點,即剪枝。 如圖 13‑6 所示,假設我們第一輪選擇 1 ,第二輪選擇 3 ,第三輪選擇 2 ,則需要在第二輪剪掉元素 1 的分 支,在第三輪剪掉元素 1 和元素 3 的分支。 圖 13‑6 全排列剪枝示例 觀察圖 13‑6 發現,該剪枝操作將搜尋空間大小從 ?(??) 減小至 ?(?!) 。 第 13 章 回溯 www.hello‑algo.com 284 20 码力 | 379 页 | 18.79 MB | 10 月前3
共 6 条
- 1













