目录
1. 二叉搜索树
1.1 二叉搜索树概念
二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:
- 若它的左子树不为空,则左子树上所有结点的值都小于根结点的值。
- 若它的左子树不为空,则左子树上所有结点的值都小于根结点的值。
- 它的左右子树也分别是二叉搜索树。
例如,下面就是一棵二叉搜索树:
由于二叉搜索树中,每个结点左子树上所有结点的值都小于该结点的值,右子树上所有结点的值都大于该结点的值,因此对二叉搜索树进行中序遍历后,得到的是升序序列。
2. 二叉搜索树的实现
2.1结点类
要实现二叉搜索树,我们首先需要实现一个结点类:
- 结点类当中包含三个成员变量:结点值、左指针、右指针。
- 结点类当中只需实现一个构造函数即可,用于构造结点值和初始化指针。
template<class K>
struct BSTNode
{
K _key;
BSTNode<K>* _left;
BSTNode<K>* _right;
BSTNode(const K& key = K())
{
_key = key;
_left = nullptr;
_right = nullptr;
}
};
2.2 各函数接口总览+小技巧
//二叉搜索树
template<class K>
class BSTree
{
typedef BSTNode<K> Node;
public:
//构造函数
BSTree();
//拷贝构造函数
BSTree(const BSTree<K>& t);
//赋值运算符重载函数
BSTree<K>& operator=(BSTree<K> t);
//析构函数
~BSTree();
//插入函数
bool Insert(const K& key);
//删除函数
bool Erase(const K& key);
//查找函数
Node* Find(const K& key);
//中序遍历
void InOrder();
private:
Node* _root; //指向二叉搜索树的根结点
};
小技巧
为了在实现其他接口的过程中方便随时检查,最好实现一个二叉搜索树的中序遍历接口,当我们对二叉搜索树进行一次操作后,可以调用中序遍历接口对二叉搜索树进行遍历,若二叉搜索树进行操作后的遍历结果仍为升序,则可以初步判断所实现的接口是正确。
//中序遍历的子函数
void _InOrder(Node* root)
{
if (root == nullptr)
return;
_InOrder(root->_left); //遍历左子树
cout << root->_key << " "; //遍历根结点
_InOrder(root->_right); //遍历右子树
}
//中序遍历
void InOrder()
{
_InOrder(_root);
cout << endl;
}
这里为什么需要调用_InOrder(_root)呢,因为我们在类外创建二叉搜索树后,要传入树中_root进行中序遍历,而_root却是私有了,不能访问,所以这边我们在实现中序遍历的过程中就套了一层,因为类函数中私有成员是可以访问的,因为我们有2个InOrder,所以我们让_InOrder被private修饰,这样类外就看不到。
2.3 构造函数
构造函数非常简单,构造一个空树即可
//构造函数
BSTree()
:_root(nullptr)
{}
2.4 拷贝构造函数
拷贝构造函数也并不难,拷贝一棵和所给二叉搜索树相同的树即可。
//拷贝树
Node* _Copy(Node* root)
{
if (root == nullptr) //空树直接返回
return nullptr;
Node* copyNode = new Node(root->_key); //拷贝根结点
copyNode->_left = _Copy(root->_left); //拷贝左子树
copyNode