二叉搜索树,数据结构中的”hello world” 我们使用二叉搜索树(BST)作为数据结构中的“hello world”。Jon Bentley在他的《编程珠玑》一书中,曾给了这样一个有趣的题目:如何统计一段文字中每
二叉搜索树,数据结构中的”hello world”
我们使用二叉搜索树(BST)作为数据结构中的“hello world”。Jon Bentley在他的《编程珠玑》一书中,曾给了这样一个有趣的题目:如何统计一段文字中每个单词出现的次数?下面的C++程序展示了一个解法。
C++标准库中提供的map是一种用平衡二叉树实现的字典数据结构。例子中用单词作为key,用单词出现的次数作为值。
一棵二叉搜索树
是一棵满足下面条件的二叉树:
- 所有左侧分支的值都小于本节点的值,
- 本节点的值小于所有右侧分支的值。
节点数据结构
插入
我们可以使用下述算法向一个二叉搜索树中插入一个键k(在实际应用中,有时会同时插入一对键和值):
- 如果树为空,创建一个叶子节点,令该节点的key = k;
- 如果k小于根节点的key,将它插入到左子树中;
- 如果k大于根节点的key,将它插入到右子树中。
遍历
遍历是指依次访问二叉树中的每个元素。有三种遍历方法,分别是前序遍历、中序遍历和后序遍历。它们是按照访问根节点和子节点的先后顺序命名的。
- 前序遍历:先访问key,然后访问左子树,最后访问右子树;
- 中序遍历:先访问左子树,然后访问key,最后访问右子树;
- 后序遍历:先访问左子树,然后访问右子树,最后访问key。
所有的“访问”操作都是递归的。先访问根后访问子分支称为先序,在访问左右分支的中间访问根称为中序,先访问子分支后访问根称为后序。对于图中的二叉树,下面分别列出了三种遍历的结果:
- 前序遍历:4, 3, 1, 2, 8, 7, 16, 10, 9, 14;
- 中序遍历:1, 2, 3, 4, 7, 8, 9, 10, 14, 16;
- 后序遍历:2, 1, 3, 7, 9, 14, 10, 16, 8, 4。
对二叉搜索树进行中序遍历,元素就会按照从小到大的顺序输出。
中序遍历的算法可以描述为:
- 如果树为空,则返回;
- 否则先中序遍历左子树,然后访问key,最后再中序遍历右子树。
搜索
Look up
二叉搜索树的定义使得它非常适合进行元素的搜索。可以按照下面描述的方法在树中搜索一个key:
- 如果树为空,搜索失败;
- 如果根节点的key等于待搜索的值,搜索成功,返回根节点作为结果;
- 如果待搜索的值小于根节点的key,继续在左子树中递归搜索;
- 否则,待搜索的值大于根节点的key,继续在右子树中递归搜索。
最小元素和最大元素
为了获取最小元素,我们可以不断向左侧前进,直到左侧分支为空。类似地,我们可以通过不断向右侧前进获取最大元素。
前驱(Successor)和后继(predecessor)
给定元素x,它的后继元素y是满足y > x的最小值。有两种情况:如果x所在的节点有一个非空的右子树,则右子树中的最小值就是答案。如图所示,8的后继元素为9,它是元素8的右子树中的最小值。另外一种情况是,如果x没有非空的右子树,我们需要向上回溯,找到最近的一个祖先,使得该祖先的左侧孩子,也为x的祖先。元素2所在的节点没有右侧分支,我们向上回溯一步找到元素1,但是1没有左侧分支,因此需要继续向上查找,这次我们到达了元素3所在的节点。而3的左侧孩子,同样也是2的祖先。至此,我们找到了2的后继元素3。
代码如下:
插入排序的进化
改进一
任何时刻,我们手中的牌都是已序的,因此我们可以用二分查找来搜索插入位置。
使用二叉搜索树的最终改进