C++ 二叉搜索树(BST)原理与完整实现
一、二叉搜索树的性能分析和概念
1.1 二叉搜索树的概念
二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有下列性质的二叉树:
- 若它的左子树不为空,则左子树上所有结点的值都小于等于根结点的值。
- 若它的右子树不为空,则右子树上所有结点的值都大于等于根结点的值。
- 它的左右子树都是二叉搜索树。
二叉搜索树中可以支持插入相等的值,也可以不支持插入相等的值,具体看使用场景定义。例如 map/set/multimap/multiset 系列容器底层就是二叉搜索树,其中 map/set 不支持插入相等值,multimap/multiset 支持插入相等值。
1.2 二叉搜索树的性能分析
由于它本身和它的子树都是二叉搜索树,观察上面的特点不难发现:二叉搜索树的中序遍历一定是升序序列(所以二叉搜索树又称二叉排序树)。
二叉搜索树顾名思义,其主要用于搜索(或者搜索效率高),下面我们来分析其最优情况和最差情况。
最优情况下,二叉搜索树为完全二叉树(或者接近完全二叉树),其高度为:$\log_2 N$。 最差情况下,二叉搜索树退化为单支树(或者类似单支),其高度为:$N$。
所以综合而言二叉搜索树增删查改时间复杂度为:$O(N)$。那么这样的效率显然是无法满足我们需求的,必须将二叉搜索树优化即二叉搜索树的变形:平衡二叉搜索树 AVL 树和红黑树,才能适用于我们在内存中存储和搜索数据。
那有的人说使用二分查找也可以实现 $O(\log_2 N)$ 级别的查找效率,为啥还有将二叉搜索树变形外?需要说明的是,二分查找也可以实现 $O(\log_2 N)$ 级别的查找效率,但是二分查找有两大缺陷:
- 需要存储在支持下标随机访问的结构中,并且有序。
- 插入和删除数据效率很低,因为存储在下标随机访问的结构中,插入和删除数据一般需要挪动数据。
这里也就体现出了平衡二叉搜索树的价值。
二、二叉搜索树增删查的实现
这里主要关注增删查,修改一个 key 就可能不是二叉搜索树的结构了,所以通常不支持直接修改 key。
2.1 二叉搜索树基本结构的实现
2.1.1 节点的定义
由于后面实现二叉搜索树要访问节点成员,所以这里使用 struct 定义(默认是公有)。
template<class K> struct BSTreeNode {
BSTreeNode* _left;
BSTreeNode* _right;
K _key;
BSTreeNode(const K& key) :_left(nullptr), _right(nullptr), _key(key) { }
};
2.1.2 默认成员函数
由于二叉搜索树涉及到节点资源的开辟、拷贝、删除,所以这里我们需要自己写拷贝构造、赋值重载、析构函数。默认构造一般不满足我们的需求,所以看情况是否要写。而这里由于我们需要写拷贝构造(一种特殊的默认构造),所以这里一定要手写默认规则(可以缺省值)。
2.1.2.1 二叉搜索树的基本结构
template<class K> class BSTree {
BSTreeNode<K> Node;
:
Node* _root = ;
};


