1、二叉搜索树的概念
2、二叉搜索树的基本操作实现
3、完整代码
1、二叉搜索树的概念
二叉搜索树的性质:根节点大于左子树的节点,小于右子树的节点,并且每个节点的值唯一。
如图即为一棵最普通的二叉搜索树:
2、二叉搜索树的基本操作实现
首先我们要先定义二叉搜索树中每一个节点的结构,这里我们让节点包含它的值和两个分别指向左右孩子的指针:
template<class T>
struct TreeNode
{
TreeNode* _left;
TreeNode* _right;
T _key;
TreeNode(const T& x) //这里使用初始化参数列表初始化三个变量
:_key(x)
, _left(nullptr)
, _right(nullptr)
{
}
};
接下来要定义树的基本结构:包括构造函数、插入、查找、删除、析构这些基本函数,根节点以及插入删除的实现放在私有类中,在公有类中声明一个接口,可以保障代码的安全性:
template<class T>
class BinarySearchTree
{
public:
typedef TreeNode<T> Node;
BinarySearchTree()
:_root(nullptr)
{
}
//插入(递归版本)
bool InsertR(const T& key)
{
return _InsertR(_root, key);
}
//查找
Node* find(const T& key);
//删除(递归版本)
bool eraseR(const T& x)
{
return _eraseR(_root, x);
}
//析构函数
void Destory(Node* root)
{
if (root == nullptr)
return;
Destory(root->_left);
Destory(root->_right);
delete root;
}
~BinarySearchTree()
{
Destory(_root);
_root = nullptr;
}
private:
//定义Node变量,在插入删除时方便判断是否为空,节点的左右孩子为指针方便赋值
Node* _root;//根节点
bool _InsertR(Node*& root, const T& key);
bool _eraseR(Node *&root,const T& key);
};
插入、查找与删除操作的具体实现:
1’插入
首先是插入操作,新节点插入一颗二叉搜索树需要满足根节点永远大于左子树且小于右子树的要求,那么实现就很简单,只需要从根节点开始比较其值的大小,若小于根节点则进入左子树继续进行比较,若大于根节点则继续进入右子树进行比较,直到所比较的节点为空或者有相等的节点为止,就是这样的一个递归思想:
template<class T>
bool BinarySearchTree<T>::_InsertR(Node*& root, const T& key)
{
if (root == nullptr) //两种情况,一种是没有根节点,另一种为比较到空节点
{
root = new Node(key);
}
if (root->_key > key)
{
_InsertR(root->_left, key);
}
else if (root->_key < key)
{
_InsertR(root->_right, key);
}
else // 相等时则会返回false插入失败
return false;
return true;
}
2’查找
查找操作就更加简单了,只需要依据二叉搜索树的性质依次比较值的大小最后返回即可,二叉搜索树正是应为其查找的效率而得名,如果是完全二叉树则时间复杂度为O(logN),若是单支二叉树时间复杂度就是O(N),因为时间复杂度的定义是最坏的时间,所以二叉搜索树的查询效率也就是O(N):
template<class T>
BinarySearchTree<T>::Node* BinarySearchTree<T>::find(const T& key)
{
{
if (_root == nullptr)
return nullptr;
Node* cur = _root;
while (cur)
{
if (cur->_key > key)
{
cur = cur->_left;
}
else if (cur->_key < key)
{
cur = cur->_right;
}
else
return cur;
}
return nullptr;
}
}
3’删除
删除操作就比较复杂一点,因为在删除节点后也需要满足二叉搜索树的性质,所以这里分四种情况:
1‘’没有孩子节点的情况,这个时候可以直接删除,不会影响二叉搜索树的结构,比如下图的1、2、5、8节点。
2‘’只有左孩子节点
如上图的节点2,如果要删除这个节点,就需要先将4与2的左孩子节点1相连,再将其删除。
3‘’只有右孩子节点
这种情况同理,要先让7的父节点连接子节点,再删除7。
4''有左右孩子节点
比如上面这个节点7的情况,那么这种情况就需要寻找节点来替代这个节点,替代的规则就是被删除节点左子树最大的节点或者右子树最小的节点,这样能在不破坏二叉搜索树结构的同时也能将被删除节点转移到叶子节点上直接进行删除,比如上图我们就可以用节点8替代上来,然后再把8原位置的节点删除:
接下来是代码实现:
template<class T>
bool BinarySearchTree<T>::_eraseR(Node*& root, const T& key)
{
if (root == nullptr)
return false;
if (root->_key < key)
_eraseR(root->_right, key);
else if (root->_key > key)
_eraseR(root->_left, key);
else
{
if (root->_left == nullptr) //这里代表两种情况,有一个右孩子或者左右孩子都为空
{
Node* tmp = root;
root = root->_right; //如果是左右孩子都没有,那么root的right也是空,不会影响
delete tmp;
}
else if (root->_right == nullptr) //只有右孩子为空一种情况
{
Node* tmp = root;
root = root->_left;
delete tmp;
}
else //左右孩子都有的情况
{
Node* leftmax = root->_left;
Node* leftmaxparent = root;
//找出左子树最大的节点
while (leftmax->_right)
{
leftmaxparent = leftmax;
leftmax = leftmax->_right;
}
//赋给根节点
root->_key = leftmax->_key;
//删除节点
//此情况为要删除节点的左孩子没有右孩子,该左孩子即为替换节点
if (leftmaxparent->_left == leftmax)
{
leftmaxparent->_left = leftmax->_left;
}
else
{
leftmaxparent->_right = leftmax->_left;
}
delete leftmax;
return true;
}
}
reutrn false;
}
3、完整代码
#include <iostream>
using namespace std;
//树的节点实现
template<class T>
struct TreeNode
{
TreeNode* _left;
TreeNode* _right;
T _key;
TreeNode(const T& x)
:_key(x)
, _left(nullptr)
, _right(nullptr)
{
}
};
//搜索树的基本结构
template<class T>
class BinarySearchTree
{
public:
typedef TreeNode<T> Node;
BinarySearchTree()
:_root(nullptr)
{
}
//插入(递归版本)
bool InsertR(const T& key)
{
return _InsertR(_root, key);
}
//查找
Node* find(const T& key);
//删除(递归版本)
bool eraseR(const T& x)
{
return _eraseR(_root, x);
}
//析构函数
void Destory(Node* root)
{
if (root == nullptr)
return;
Destory(root->_left);
Destory(root->_right);
delete root;
}
~BinarySearchTree()
{
Destory(_root);
_root = nullptr;
}
private:
//定义Node变量,在插入删除时方便判断是否为空,节点的左右孩子为指针方便赋值
Node* _root;//根节点
bool _InsertR(Node*& root, const T& key);
bool _eraseR(Node *&root,const T& key);
};
template<class T>
bool BinarySearchTree<T>::_InsertR(Node*& root, const T& key)
{
if (root == nullptr)
{
root = new Node(key);
}
if (root->_key > key)
{
_InsertR(root->_left, key);
}
else if (root->_key < key)
{
_InsertR(root->_right, key);
}
else
return false;
return true;
}
template<class T>
BinarySearchTree<T>::Node* BinarySearchTree<T>::find(const T& key)
{
{
if (_root == nullptr)
return nullptr;
Node* cur = _root;
while (cur)
{
if (cur->_key > key)
{
cur = cur->_left;
}
else if (cur->_key < key)
{
cur = cur->_right;
}
else
return cur;
}
return nullptr;
}
}
template<class T>
bool BinarySearchTree<T>::_eraseR(Node*& root, const T& key)
{
if (root == nullptr)
return false;
if (root->_key < key)
_eraseR(root->_right, key);
else if (root->_key > key)
_eraseR(root->_left, key);
else
{
if (root->_left == nullptr)
{
Node* tmp = root;
root = root->_right;
delete tmp;
}
else if (root->_right == nullptr)
{
Node* tmp = root;
root = root->_left;
delete tmp;
}
else
{
Node* leftmax = root->_left;
Node* leftmaxparent = root;
//找出左子树最大的节点
while (leftmax->_right)
{
leftmaxparent = leftmax;
leftmax = leftmax->_right;
}
//赋给根节点
root->_key = leftmax->_key;
//删除节点
//此情况为要删除节点的左孩子没有右孩子,该左孩子即为替换节点
if (leftmaxparent->_left == leftmax)
{
leftmaxparent->_left = leftmax->_left;
}
else
{
leftmaxparent->_right = leftmax->_left;
}
delete leftmax;
return true;
}
}
reutrn false;
}