mirror of
https://github.com/krahets/hello-algo.git
synced 2026-04-13 15:59:42 +08:00
build
This commit is contained in:
@@ -23,7 +23,7 @@ status: new
|
||||
|
||||
分治能够提升搜索效率,本质上是因为暴力搜索每轮只能排除一个选项,**而分治搜索每轮可以排除一半选项**。
|
||||
|
||||
### 基于分治实现二分
|
||||
### 1. 基于分治实现二分
|
||||
|
||||
在之前的章节中,二分查找是基于递推(迭代)实现的。现在我们基于分治(递归)来实现它。
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ status: new
|
||||
|
||||
<p align="center"> 图:构建二叉树的示例数据 </p>
|
||||
|
||||
### 判断是否为分治问题
|
||||
### 1. 判断是否为分治问题
|
||||
|
||||
原问题定义为从 `preorder` 和 `inorder` 构建二叉树。我们首先从分治的角度分析这道题:
|
||||
|
||||
@@ -21,7 +21,7 @@ status: new
|
||||
- **子问题是独立的**:左子树和右子树是相互独立的,它们之间没有交集。在构建左子树时,我们只需要关注中序遍历和前序遍历中与左子树对应的部分。右子树同理。
|
||||
- **子问题的解可以合并**:一旦得到了左子树和右子树(子问题的解),我们就可以将它们链接到根节点上,得到原问题的解。
|
||||
|
||||
### 如何划分子树
|
||||
### 2. 如何划分子树
|
||||
|
||||
根据以上分析,这道题是可以使用分治来求解的,但问题是:**如何通过前序遍历 `preorder` 和中序遍历 `inorder` 来划分左子树和右子树呢**?
|
||||
|
||||
@@ -40,7 +40,7 @@ status: new
|
||||
|
||||
<p align="center"> 图:在前序和中序遍历中划分子树 </p>
|
||||
|
||||
### 基于变量描述子树区间
|
||||
### 3. 基于变量描述子树区间
|
||||
|
||||
根据以上划分方法,**我们已经得到根节点、左子树、右子树在 `preorder` 和 `inorder` 中的索引区间**。而为了描述这些索引区间,我们需要借助几个指针变量:
|
||||
|
||||
@@ -67,7 +67,7 @@ status: new
|
||||
|
||||
<p align="center"> 图:根节点和左右子树的索引区间表示 </p>
|
||||
|
||||
### 代码实现
|
||||
### 4. 代码实现
|
||||
|
||||
为了提升查询 $m$ 的效率,我们借助一个哈希表 `hmap` 来存储数组 `inorder` 中元素到索引的映射。
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ status: new
|
||||
|
||||
那么,我们不禁发问:**为什么分治可以提升算法效率,其底层逻辑是什么**?换句话说,将大问题分解为多个子问题、解决子问题、将子问题的解合并为原问题的解,这几步的效率为什么比直接解决原问题的效率更高?这个问题可以从操作数量和并行计算两方面来讨论。
|
||||
|
||||
### 操作数量优化
|
||||
### 1. 操作数量优化
|
||||
|
||||
以「冒泡排序」为例,其处理一个长度为 $n$ 的数组需要 $O(n^2)$ 时间。假设我们把数组从中点分为两个子数组,则划分需要 $O(n)$ 时间,排序每个子数组需要 $O((\frac{n}{2})^2)$ 时间,合并两个子数组需要 $O(n)$ 时间,总体时间复杂度为:
|
||||
|
||||
@@ -67,7 +67,7 @@ $$
|
||||
|
||||
再思考,**如果我们多设置几个划分点**,将原数组平均划分为 $k$ 个子数组呢?这种情况与「桶排序」非常类似,它非常适合排序海量数据,理论上时间复杂度可以达到 $O(n + k)$ 。
|
||||
|
||||
### 并行计算优化
|
||||
### 2. 并行计算优化
|
||||
|
||||
我们知道,分治生成的子问题是相互独立的,**因此通常可以并行解决**。也就是说,分治不仅可以降低算法的时间复杂度,**还有利于操作系统的并行优化**。
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ status: new
|
||||
|
||||
**我们将规模为 $i$ 的汉诺塔问题记做 $f(i)$** 。例如 $f(3)$ 代表将 $3$ 个圆盘从 `A` 移动至 `C` 的汉诺塔问题。
|
||||
|
||||
### 考虑基本情况
|
||||
### 1. 考虑基本情况
|
||||
|
||||
对于问题 $f(1)$ ,即当只有一个圆盘时,则将它直接从 `A` 移动至 `C` 即可。
|
||||
|
||||
@@ -55,7 +55,7 @@ status: new
|
||||
|
||||
<p align="center"> 图:规模为 2 问题的解 </p>
|
||||
|
||||
### 子问题分解
|
||||
### 2. 子问题分解
|
||||
|
||||
对于问题 $f(3)$ ,即当有三个圆盘时,情况变得稍微复杂了一些。由于已知 $f(1)$ 和 $f(2)$ 的解,因此可从分治角度思考,**将 `A` 顶部的两个圆盘看做一个整体**,执行以下步骤:
|
||||
|
||||
@@ -93,7 +93,7 @@ status: new
|
||||
|
||||
<p align="center"> 图:汉诺塔问题的分治策略 </p>
|
||||
|
||||
### 代码实现
|
||||
### 3. 代码实现
|
||||
|
||||
在代码中,我们声明一个递归函数 `dfs(i, src, buf, tar)` ,它的作用是将柱 `src` 顶部的 $i$ 个圆盘借助缓冲柱 `buf` 移动至目标柱 `tar` 。
|
||||
|
||||
|
||||
Reference in New Issue
Block a user