Skip to content

Commit ae1c871

Browse files
committed
Deployed 4847ccc with MkDocs version: 0.17.3
1 parent 0216a61 commit ae1c871

File tree

13 files changed

+170
-24
lines changed

13 files changed

+170
-24
lines changed

10_递归/recursion/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -387,7 +387,7 @@ <h1 id="_8">思考题</h1>
387387
<li>你能举出其他一些使用到递归的例子吗?</li>
388388
<li>实现一个 flatten 函数,把嵌套的列表扁平化,你需要用递归函数来实现。比如 [[1,2], [1,2,3] -&gt; [1,2,1,2,3]</li>
389389
<li>使用递归和循环各有什么优缺点,你能想到吗?怎么把一个尾递归用迭代替换?</li>
390-
<li>递归有时候虽然很优雅,但是时间复杂度却不理想,比如斐波那契数列,它的表达式是 F(n) = F(n-1) + F(n-2),你能计算它的时间复杂度吗?我们怎样去优化它</li>
390+
<li>递归有时候虽然很优雅直观,但是时间复杂度却不理想,比如斐波那契数列,它的表达式是 F(n) = F(n-1) + F(n-2),你能计算它的时间复杂度吗?请你画个树来表示它的计算过程,为什么这个时间复杂度很不理想?我们怎样去优化它</li>
391391
</ul>
392392

393393
</div>

13_高级排序算法/quick_sort/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,7 @@ <h1 id="_3">思考题</h1>
306306
<h1 id="_4">延伸阅读</h1>
307307
<ul>
308308
<li>《算法导论》第 7 章</li>
309+
<li><a href="https://zhuanlan.zhihu.com/p/36419582">《面试必备 | 排序算法的Python实现》</a></li>
309310
</ul>
310311

311312
</div>

17_二叉查找树/binary_search_tree/index.html

Lines changed: 87 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -175,15 +175,18 @@
175175

176176
<li><a class="toctree-l3" href="#_2">插入</a></li>
177177

178-
<li><a class="toctree-l3" href="#_3">删除</a></li>
178+
<li><a class="toctree-l3" href="#_3">删除节点</a></li>
179179

180180
</ul>
181181

182182

183-
<li class="toctree-l2"><a href="#_4">练习题:</a></li>
183+
<li class="toctree-l2"><a href="#_7">时间复杂度分析</a></li>
184184

185185

186-
<li class="toctree-l2"><a href="#_5">延伸阅读</a></li>
186+
<li class="toctree-l2"><a href="#_8">练习题:</a></li>
187+
188+
189+
<li class="toctree-l2"><a href="#_9">延伸阅读</a></li>
187190

188191

189192
</ul>
@@ -224,7 +227,7 @@
224227
<h1 id="bst">二叉查找树(BST)</h1>
225228
<p>二叉树的一种应用就是来实现堆,今天我们再看看用二叉查找树。
226229
前面有章节说到了查找操作,包括线性查找和二分查找,线性查找效率比较低,二分又要求必须是有序的序列,
227-
为了维持有序插入的代价比较高。能不能有一种插入和查找都比较快的数据结构呢?</p>
230+
为了维持有序插入的代价比较高。能不能有一种插入和查找都比较快的数据结构呢?二叉查找树就是这样一种结构,可以高效地插入和查询节点。</p>
228231
<h1 id="bst_1">BST 定义</h1>
229232
<p>二叉查找树是这样一种二叉树结构,它的每个节点包含一个 key 和它附带的数据,对于每个内部节点 V:
230233
- 所有 key 小于 V 的都被存储在 V 的左子树
@@ -320,9 +323,17 @@ <h2 id="key">获取最大和最小 key 的节点</h2>
320323
</code></pre>
321324

322325
<h2 id="_2">插入</h2>
323-
<p>插入节点的时候我们需要一直保持 BST 的性质,每次插入一个节点,我们都通过递归比较把它放到正确的位置。</p>
326+
<p>插入节点的时候我们需要一直保持 BST 的性质,每次插入一个节点,我们都通过递归比较把它放到正确的位置。
327+
你会发现新节点总是被作为叶子结点插入。(请你思考这是为什么)</p>
328+
<p><img alt="" src="../bst_insert.png" /></p>
324329
<pre><code class="py"> def _bst_insert(self, subtree, key, value):
325-
if subtree is None:
330+
&quot;&quot;&quot; 插入并且返回根节点
331+
332+
:param subtree:
333+
:param key:
334+
:param value:
335+
&quot;&quot;&quot;
336+
if subtree is None: # 插入的节点一定是根节点,包括 root 为空的情况
326337
subtree = BSTNode(key, value)
327338
elif key &lt; subtree.key:
328339
subtree.left = self._bst_insert(subtree.left, key, value)
@@ -341,14 +352,80 @@ <h2 id="_2">插入</h2>
341352
return True
342353
</code></pre>
343354

344-
<h2 id="_3">删除</h2>
345-
<h1 id="_4">练习题:</h1>
355+
<h2 id="_3">删除节点</h2>
356+
<p>删除操作相比上边的操作要麻烦很多,首先需要定位一个节点,删除节点后,我们需要始终保持 BST 的性质。
357+
删除一个节点涉及到三种情况:</p>
358+
<ul>
359+
<li>节点是叶节点</li>
360+
<li>节点有一个孩子</li>
361+
<li>节点有两个孩子</li>
362+
</ul>
363+
<p>我们分别来看看三种情况下如何删除一个节点:</p>
364+
<h4 id="_4">删除叶节点</h4>
365+
<p>这是最简单的一种情况,只需要把它的父亲指向它的指针设置为 None 就好。</p>
366+
<p><img alt="" src="../bst_remove_leaf.png" /></p>
367+
<h4 id="_5">删除只有一个孩子的节点</h4>
368+
<p>删除有一个孩子的节点时,我们拿掉需要删除的节点,之后把它的父亲指向它的孩子就行,以为根据 BST
369+
左子树都小于节点,右子树都大于节点的特性,删除它之后这个条件依旧满足。</p>
370+
<p><img alt="" src="../bst_remove_node_with_one_child.png" /></p>
371+
<h4 id="_6">删除有两个孩子的内部节点</h4>
372+
<p>假如我们想删除 12 这个节点改怎么做呢?你的第一反应可能是按照下图的方式:</p>
373+
<p><img alt="" src="../remove_interior_replace.png" /></p>
374+
<p>但是这种方式可能会影响树的高度,降低查找的效率。这里我们用另一种非常巧妙的方式。
375+
还记得上边提到的吗,如果你中序遍历 BST 并且输出每个节点的 key,你会发现就是一个有序的数组。
376+
[1 4 12 23 29 37 41 60 71 84 90 100]。 这里我们定义两个概念,逻辑前任(predecessor)和后继(successor),请看下图:</p>
377+
<p><img alt="" src="../predecessor_successor.png" /></p>
378+
<p>12 在中序遍历中的逻辑前任和后继分别是 4 和 23 节点。于是我们还有一种方法来删除 12 这个节点:</p>
379+
<ul>
380+
<li>找到节点待删除节点 N(12) 的后继节点 S(23)</li>
381+
<li>复制节点 S 到节点 N</li>
382+
<li>删除节点 S</li>
383+
</ul>
384+
<p>说白了就是找到后继并且替换,这里之所以能保证这种方法是正确的,你会发现替换后依旧是保持了 BST 的性质。
385+
有个问题是如何找到后继节点呢?待删除节点的右子树的最小的节点不就是后继嘛,上边我们已经实现了找到最小 key 的方法了。</p>
386+
<p><img alt="" src="../find_successor.png" /></p>
387+
<p>我们开始编写代码实现,和之前的操作类似,我们还是通过辅助函数的形式来实现,这个递归函数会比较复杂,请你仔细理解:</p>
388+
<pre><code class="py"> def _bst_remove(self, subtree, key):
389+
&quot;&quot;&quot;删除节点并返回根节点&quot;&quot;&quot;
390+
if subtree is None:
391+
return None
392+
elif key &lt; subtree.key:
393+
subtree.left = self._bst_remove(subtree.left, key)
394+
return subtree
395+
elif key &gt; subtree.key:
396+
subtree.right = self._bst_remove(subtree.right, key)
397+
return subtree
398+
else: # 找到了需要删除的节点
399+
if subtree.left is None and subtree.right is None: # left node
400+
return None
401+
elif subtree.left is None or subtree.right is None: # 只有一个孩子
402+
if subtree.left is not None:
403+
return subtree.left
404+
else:
405+
return subtree.right
406+
else: # 俩孩子
407+
successor_node = self._bst_min_node(subtree.right)
408+
subtree.key, subtree.value = successor_node.key, subtree.value
409+
subtree.right = self._bst_remove(subtree.right, successor_node.key)
410+
return subtree
411+
412+
def remove(self, key):
413+
assert key in self
414+
self.size -= 1
415+
</code></pre>
416+
417+
<p>完整代码你可以在本章的 bst.py 找到。</p>
418+
<h1 id="_7">时间复杂度分析</h1>
419+
<p>上边介绍的操作时间复杂度和二叉树的形状有关。平均来说时间复杂度是和树的高度成正比的,树的高度 h 是 log(n),
420+
但是最坏情况下以上操作的时间复杂度都是 O(n)。为了改善 BST 有很多变种,感兴趣请参考延伸阅读中的内容。</p>
421+
<p><img alt="" src="../bst_worstcase.png" /></p>
422+
<h1 id="_8">练习题:</h1>
346423
<ul>
347424
<li>请你实现查找 BST 最大值的函数</li>
348425
</ul>
349-
<h1 id="_5">延伸阅读</h1>
426+
<h1 id="_9">延伸阅读</h1>
350427
<ul>
351-
<li>《Data Structures and Algorithms in Python》14 章</li>
428+
<li>《Data Structures and Algorithms in Python》14 章,树的概念和算法还有很多,我们这里介绍最基本的</li>
352429
</ul>
353430

354431
</div>

17_二叉查找树/bst.py

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ def _bst_search(self, subtree, key):
3838
else:
3939
return subtree
4040

41+
def __contains__(self, key):
42+
"""实现 in 操作符"""
43+
return self._bst_search(self.root, key) is not None
44+
4145
def get(self, key, default=None):
4246
node = self._bst_search(self.root, key)
4347
if node is None:
@@ -58,7 +62,13 @@ def bst_min(self):
5862
return node.value if node else None
5963

6064
def _bst_insert(self, subtree, key, value):
61-
if subtree is None:
65+
""" 插入并且返回根节点
66+
67+
:param subtree:
68+
:param key:
69+
:param value:
70+
"""
71+
if subtree is None: # 插入的节点一定是根节点,包括 root 为空的情况
6272
subtree = BSTNode(key, value)
6373
elif key < subtree.key:
6474
subtree.left = self._bst_insert(subtree.left, key, value)
@@ -76,6 +86,35 @@ def add(self, key, value):
7686
self.size += 1
7787
return True
7888

89+
def _bst_remove(self, subtree, key):
90+
"""删除节点并返回根节点"""
91+
if subtree is None:
92+
return None
93+
elif key < subtree.key:
94+
subtree.left = self._bst_remove(subtree.left, key)
95+
return subtree
96+
elif key > subtree.key:
97+
subtree.right = self._bst_remove(subtree.right, key)
98+
return subtree
99+
else: # 找到了需要删除的节点
100+
if subtree.left is None and subtree.right is None: # left node
101+
return None
102+
elif subtree.left is None or subtree.right is None: # 只有一个孩子
103+
if subtree.left is not None:
104+
return subtree.left
105+
else:
106+
return subtree.right
107+
else: # 俩孩子
108+
successor_node = self._bst_min_node(subtree.right)
109+
subtree.key, subtree.value = successor_node.key, subtree.value
110+
subtree.right = self._bst_remove(subtree.right, successor_node.key)
111+
return subtree
112+
113+
def remove(self, key):
114+
assert key in self
115+
self.size -= 1
116+
return self._bst_remove(self.root, key)
117+
79118

80119
NODE_LIST = [
81120
{'key': 60, 'left': 12, 'right': 90, 'is_root': True},
@@ -105,3 +144,12 @@ def test_bst_tree():
105144

106145
bst.add(0, 0)
107146
assert bst.bst_min() == 0
147+
148+
bst.remove(12)
149+
assert bst.get(12) is None
150+
151+
bst.remove(1)
152+
assert bst.get(1) is None
153+
154+
bst.remove(29)
155+
assert bst.get(29) is None

17_二叉查找树/bst_insert.png

65.2 KB
Loading
68.5 KB
Loading
Loading

17_二叉查找树/bst_worstcase.png

41.3 KB
Loading

17_二叉查找树/find_successor.png

114 KB
Loading
42.1 KB
Loading
61.8 KB
Loading

index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -463,5 +463,5 @@ <h2 id="_19">本电子书制作和写作方式</h2>
463463

464464
<!--
465465
MkDocs version : 0.17.3
466-
Build Date UTC : 2018-05-04 16:32:43
466+
Build Date UTC : 2018-05-05 13:27:21
467467
-->

0 commit comments

Comments
 (0)