Skip to content

Commit 813eb00

Browse files
committed
二叉查找树
1 parent e502ebc commit 813eb00

File tree

5 files changed

+256
-2
lines changed

5 files changed

+256
-2
lines changed
Lines changed: 148 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,149 @@
1-
# 二叉查找树
1+
# 二叉查找树(BST)
22

3-
之前我们讲到了二叉树的一种应用就是来实现堆,今天我们再看看用二叉查找树。
3+
二叉树的一种应用就是来实现堆,今天我们再看看用二叉查找树。
4+
前面有章节说到了查找操作,包括线性查找和二分查找,线性查找效率比较低,二分又要求必须是有序的序列,
5+
为了维持有序插入的代价比较高。能不能有一种插入和查找都比较快的数据结构呢?
6+
7+
# BST 定义
8+
9+
二叉查找树是这样一种二叉树结构,它的每个节点包含一个 key 和它附带的数据,对于每个内部节点 V:
10+
- 所有 key 小于 V 的都被存储在 V 的左子树
11+
- 所有 key 大于 V 的都存储在 V 的右子树
12+
13+
![](./bst.png)
14+
15+
注意这个限制条件,可别和堆搞混了。说白了就是对于每个内部节点,左子树的 key 都比它小,右子树都比它大。
16+
如果中序遍历(二叉树遍历讲过了)这颗二叉树,你会发现输出的顺序正好是有序的。
17+
我们先来定义一下 BST 的节点结构:
18+
19+
```py
20+
class BSTNode(object):
21+
def __init__(self, key, value, left=None, right=None):
22+
self.key, self.value, self.left, self.right = key, value, left, right
23+
```
24+
25+
# 构造一个 BST
26+
我们还像之前构造二叉树一样,按照上图构造一个 BST 用来演示:
27+
28+
```py
29+
class BST(object):
30+
def __init__(self, root=None):
31+
self.root = root
32+
33+
@classmethod
34+
def build_from(cls, node_list):
35+
cls.size = 0
36+
key_to_node_dict = {}
37+
for node_dict in node_list:
38+
key = node_dict['key']
39+
key_to_node_dict[key] = BSTNode(key, value=key) # 这里值暂时用 和 key一样的
40+
41+
for node_dict in node_list:
42+
key = node_dict['key']
43+
node = key_to_node_dict[key]
44+
if node_dict['is_root']:
45+
root = node
46+
node.left = key_to_node_dict.get(node_dict['left'])
47+
node.right = key_to_node_dict.get(node_dict['right'])
48+
cls.size += 1
49+
return cls(root)
50+
51+
52+
NODE_LIST = [
53+
{'key': 60, 'left': 12, 'right': 90, 'is_root': True},
54+
{'key': 12, 'left': 4, 'right': 41, 'is_root': False},
55+
{'key': 4, 'left': 1, 'right': None, 'is_root': False},
56+
{'key': 1, 'left': None, 'right': None, 'is_root': False},
57+
{'key': 41, 'left': 29, 'right': None, 'is_root': False},
58+
{'key': 29, 'left': 23, 'right': 37, 'is_root': False},
59+
{'key': 23, 'left': None, 'right': None, 'is_root': False},
60+
{'key': 37, 'left': None, 'right': None, 'is_root': False},
61+
{'key': 90, 'left': 71, 'right': 100, 'is_root': False},
62+
{'key': 71, 'left': None, 'right': 84, 'is_root': False},
63+
{'key': 100, 'left': None, 'right': None, 'is_root': False},
64+
{'key': 84, 'left': None, 'right': None, 'is_root': False},
65+
]
66+
bst = BST.build_from(NODE_LIST)
67+
```
68+
69+
70+
# BST 操作
71+
72+
## 查找
73+
如何查找一个指定的节点呢,根据定义我们知道每个内部节点左子树的 key 都比它小,右子树的 key 都比它大,所以
74+
对于带查找的节点 search_key,从根节点开始,如果 search_key 大于当前 key,就去右子树查找,否则去左子树查找。 一直到当前节点是 None 了说明没找到对应 key。
75+
76+
![](./bst_search.png)
77+
78+
好,撸代码:
79+
80+
```py
81+
def _bst_search(self, subtree, key):
82+
if subtree is None: # 没找到
83+
return None
84+
elif key < subtree.key:
85+
return self._bst_search(subtree.left, key)
86+
elif key > subtree.key:
87+
return self._bst_search(subtree.right, key)
88+
else:
89+
return subtree
90+
91+
def get(self, key, default=None):
92+
node = self._bst_search(self.root, key)
93+
if node is None:
94+
return default
95+
else:
96+
return node.value
97+
```
98+
99+
100+
## 获取最大和最小 key 的节点
101+
102+
其实还按照其定义,最小值就一直向着左子树找,最大值一直向右子树找,递归查找就行。
103+
104+
```py
105+
def _bst_min_node(self, subtree):
106+
if subtree is None:
107+
return None
108+
elif subtree.left is None: # 找到左子树的头
109+
return subtree
110+
else:
111+
return self._bst_min_node(subtree.left)
112+
113+
def bst_min(self):
114+
node = self._bst_min_node(self.root)
115+
return node.value if node else None
116+
```
117+
118+
## 插入
119+
插入节点的时候我们需要一直保持 BST 的性质,每次插入一个节点,我们都通过递归比较把它放到正确的位置。
120+
121+
```py
122+
def _bst_insert(self, subtree, key, value):
123+
if subtree is None:
124+
subtree = BSTNode(key, value)
125+
elif key < subtree.key:
126+
subtree.left = self._bst_insert(subtree.left, key, value)
127+
elif key > subtree.key:
128+
subtree.right = self._bst_insert(subtree.right, key, value)
129+
return subtree
130+
131+
def add(self, key, value):
132+
node = self._bst_search(self.root, key)
133+
if node is not None: # 更新已经存在的 key
134+
node.value = value
135+
return False
136+
else:
137+
self.root = self._bst_insert(self.root, key, value)
138+
self.size += 1
139+
return True
140+
```
141+
142+
## 删除
143+
144+
# 练习题:
145+
- 请你实现查找 BST 最大值的函数
146+
147+
148+
# 延伸阅读
149+
- 《Data Structures and Algorithms in Python》14 章

docs/17_二叉查找树/bst.png

35 KB
Loading

docs/17_二叉查找树/bst.py

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
# -*- coding: utf-8 -*-
2+
3+
4+
class BSTNode(object):
5+
def __init__(self, key, value, left=None, right=None):
6+
self.key, self.value, self.left, self.right = key, value, left, right
7+
8+
9+
class BST(object):
10+
def __init__(self, root=None):
11+
self.root = root
12+
13+
@classmethod
14+
def build_from(cls, node_list):
15+
cls.size = 0
16+
key_to_node_dict = {}
17+
for node_dict in node_list:
18+
key = node_dict['key']
19+
key_to_node_dict[key] = BSTNode(key, value=key) # 这里值暂时用 和 key一样的
20+
21+
for node_dict in node_list:
22+
key = node_dict['key']
23+
node = key_to_node_dict[key]
24+
if node_dict['is_root']:
25+
root = node
26+
node.left = key_to_node_dict.get(node_dict['left'])
27+
node.right = key_to_node_dict.get(node_dict['right'])
28+
cls.size += 1
29+
return cls(root)
30+
31+
def _bst_search(self, subtree, key):
32+
if subtree is None: # 没找到
33+
return None
34+
elif key < subtree.key:
35+
return self._bst_search(subtree.left, key)
36+
elif key > subtree.key:
37+
return self._bst_search(subtree.right, key)
38+
else:
39+
return subtree
40+
41+
def get(self, key, default=None):
42+
node = self._bst_search(self.root, key)
43+
if node is None:
44+
return default
45+
else:
46+
return node.value
47+
48+
def _bst_min_node(self, subtree):
49+
if subtree is None:
50+
return None
51+
elif subtree.left is None: # 找到左子树的头
52+
return subtree
53+
else:
54+
return self._bst_min_node(subtree.left)
55+
56+
def bst_min(self):
57+
node = self._bst_min_node(self.root)
58+
return node.value if node else None
59+
60+
def _bst_insert(self, subtree, key, value):
61+
if subtree is None:
62+
subtree = BSTNode(key, value)
63+
elif key < subtree.key:
64+
subtree.left = self._bst_insert(subtree.left, key, value)
65+
elif key > subtree.key:
66+
subtree.right = self._bst_insert(subtree.right, key, value)
67+
return subtree
68+
69+
def add(self, key, value):
70+
node = self._bst_search(self.root, key)
71+
if node is not None: # 更新已经存在的 key
72+
node.value = value
73+
return False
74+
else:
75+
self.root = self._bst_insert(self.root, key, value)
76+
self.size += 1
77+
return True
78+
79+
80+
NODE_LIST = [
81+
{'key': 60, 'left': 12, 'right': 90, 'is_root': True},
82+
{'key': 12, 'left': 4, 'right': 41, 'is_root': False},
83+
{'key': 4, 'left': 1, 'right': None, 'is_root': False},
84+
{'key': 1, 'left': None, 'right': None, 'is_root': False},
85+
{'key': 41, 'left': 29, 'right': None, 'is_root': False},
86+
{'key': 29, 'left': 23, 'right': 37, 'is_root': False},
87+
{'key': 23, 'left': None, 'right': None, 'is_root': False},
88+
{'key': 37, 'left': None, 'right': None, 'is_root': False},
89+
{'key': 90, 'left': 71, 'right': 100, 'is_root': False},
90+
{'key': 71, 'left': None, 'right': 84, 'is_root': False},
91+
{'key': 100, 'left': None, 'right': None, 'is_root': False},
92+
{'key': 84, 'left': None, 'right': None, 'is_root': False},
93+
]
94+
95+
96+
def test_bst_tree():
97+
bst = BST.build_from(NODE_LIST)
98+
for node_dict in NODE_LIST:
99+
key = node_dict['key']
100+
assert bst.get(key) == key
101+
assert bst.size == len(NODE_LIST)
102+
assert bst.get(-1) is None # 单例的 None 我们用 is 来比较
103+
104+
assert bst.bst_min() == 1
105+
106+
bst.add(0, 0)
107+
assert bst.bst_min() == 0
52.9 KB
Loading

mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,4 @@ pages:
2929
- 树与二叉树: '14_树与二叉树/tree.md'
3030
- 堆和堆排序: '15_堆与堆排序/heap_and_heapsort.md'
3131
- 优先级队列: '16_优先级队列/priority_queue.md'
32+
- 二叉查找树: '17_二叉查找树/binary_search_tree.md'

0 commit comments

Comments
 (0)