C++ 二叉搜索树(BST)原理与实现
一、概念
二叉搜索树也称二叉排序树。它要么是空树,要么满足下面这套'大小规则'(这就是 BST 的灵魂):
- 若它的左子树不为空,则左子树上所有结点的值均小于它的根结点的值。
- 若它的右子树不为空,则右子树上所有结点的值均大于它的根结点的值。
- 它的左右子树也分别为二叉搜索树(也要继续满足上面 1、2)。
换句话说:对任意结点,都要满足左边都比我小,右边都比我大,而且子树也要同样遵守。
二叉搜索树支持中序遍历(左 - 根 - 右)。如果一棵二叉树是 BST,那么对它做中序遍历得到的结果一定是一个有序序列。
二、二叉搜索树的性能分析
BST 的插入、查找、删除,真正花多少时间,主要取决于一个东西:树高(高度越小,走的路越短)。
- 最好情况:BST 是一棵完全二叉树 此时树高约为:log2(N) 所以增删查改都大约是:O(log2 N)
- 最坏情况:BST 退化成单支树(一边倒,像链表) 此时树高约为:N 所以增删查改会变成:O(N)
综合而言,二叉搜索树的增删查改时间复杂度为:O(N)(按最坏情况算)。
为什么后面还要学二叉平衡树? 因为有些场景不能接受'最坏 O(N)',就需要 AVL 树、红黑树这类二叉平衡树来把高度控制在 O(logN),从而稳定地快。
另外,二分查找也可以做到 O(log2 N),但它有两个问题:
- 必须支持随机访问(典型是数组)。
- 数据必须有序。 而有序数组要插入/删除一个元素,通常需要大量移动元素,效率不高。
所以:想要'查找快',又想要'插入/删除也相对方便',平衡 BST 就很有价值。
| 情况 | 树形态 | 树高 | 查找/插入/删除 |
|---|---|---|---|
| 最好 | 完全二叉树 | log2(N) | O(log2 N) |
| 最坏 | 单支树(退化) | N | O(N) |
三、二叉搜索树的插入
BST 的插入,本质就是:按大小规则一路往下走,直到走到空位置,把新结点挂上去。
插入过程:
- 先看根结点是否为空。
- 若为空,直接用待插入值构造根结点。
- 若不为空:
- 把当前结点
cur指向根,从根开始找插入位置; - 比较插入值和
cur的值:- 小:往左子树走
- 大:往右子树走
- 当
cur走到空(nullptr)时,说明位置找到了:把新结点插入,并挂到它父结点的左/右上。
- 把当前结点
- 二叉搜索树不允许冗余(相等)元素。
当需要冗余元素时,这里用不着二叉搜索树,用后面的二叉平衡树。
当然,如果需要实现插入相同值的元素,也是可以实现的,只需要提前规定:
- 比如遇到相同值时都插入到右子树,或者都插入到左子树。
- 归结为:遇到相同元素时,一定要优先去某一个方向。
| 步骤 | 当前状态 | 判断 | 下一步 |
|---|---|---|---|
| 1 | cur 指向根 | root 是否为空 | 为空:root=new;不空:进入循环 |
| 2 | 循环 | key 与 cur->_key 比较 | 小:cur=cur->_left;大:cur=cur->_right |


