|
1 |
| -# 二叉查找树 |
| 1 | +# 二叉查找树(BST) |
2 | 2 |
|
3 |
| -之前我们讲到了二叉树的一种应用就是来实现堆,今天我们再看看用二叉查找树。 |
| 3 | +二叉树的一种应用就是来实现堆,今天我们再看看用二叉查找树。 |
| 4 | +前面有章节说到了查找操作,包括线性查找和二分查找,线性查找效率比较低,二分又要求必须是有序的序列, |
| 5 | +为了维持有序插入的代价比较高。能不能有一种插入和查找都比较快的数据结构呢? |
| 6 | + |
| 7 | +# BST 定义 |
| 8 | + |
| 9 | +二叉查找树是这样一种二叉树结构,它的每个节点包含一个 key 和它附带的数据,对于每个内部节点 V: |
| 10 | +- 所有 key 小于 V 的都被存储在 V 的左子树 |
| 11 | +- 所有 key 大于 V 的都存储在 V 的右子树 |
| 12 | + |
| 13 | + |
| 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 | + |
| 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 章 |
0 commit comments