一、什么是二叉搜索树
二叉搜索树又称为二叉排序树,是具有以下性质的二叉树:
- 若它的左子树不为空,则左子树上所有结点的值都小于根结点的值。
- 若它的右子树不为空,则右子树上所有结点的值都大于根结点的值。
- 它的左右子树也分别是二叉搜索树。
比如这一棵树:
每棵子树的左孩子都比子树的根节点小,右孩子比子树的根节点大,也就意味着二叉搜索树中不能出现两个值相同的节点。实际上我们在构建二叉搜索树时,如果树中有节点和待插入节点的值相同,我们不会将这个节点进行插入。
1.1. 二叉搜索树查找的时间复杂度
二叉搜索树如果是完全二叉树,查找的效率很高,为O(logN):
因为我们每次查找时只需要判断是否比根节点大就能够确定目标值在左子树还是在右子树,相当于每一次比较都会减少一层树的深度,最多只需要查找树的深度层logN:
当二叉搜索树为单支数时,其深度为N,此时查找的时间复杂度为O(N):
综上,二叉搜索树最坏的时间复杂度为O(logN),最好为O(N),整体时间复杂度取其最坏的情况为O(N)。
二、二叉搜索树的实现
2.1. 接口概览
template<class K>
struct BSTreeNode
{
K _key;
BSTreeNode<K>* _left;
BSTreeNode<K>* _right;
BSTreeNode(const K& key = 0)
:_key(key)
, _left(nullptr)
, _right(nullptr)
{}
};
template<class K>
class BSTree
{
typedef BSTreeNode<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;
};
2.2. 构造函数和析构函数
构造和拷贝构造
BSTree()
:_root(nullptr)
{}
Node* _Copy(Node* root)
{
if (root == nullptr)
return nullptr;
Node* copyNode = new Node(root->_key);
copyNode->_left = _Copy(root->_left);
copyNode->_right = _Copy(root->_right);
return copyNode;
}
BSTree(const BSTree<K>& t)
{
_root = _Copy(t._root);
}
重载=运算符
BSTree<K>& operator=(BSTree<K> t)
{
std::swap(_root, t._root);
return *this;
}
析构函数
void _Destory(Node* root)
{
if (root == nullptr)
return;
_Destory(root->_left);
_Destory(root->_right);
delete root;
}
~BSTree()
{
_Destory(_root);
_root = nullptr;
}
2.3. 插入节点的函数
非递归实现
插入分为两种情况:
- 如果为空,则创建一个节点做根,
_root 指向这个节点 - 如果不为空,则需要按照二叉搜索树的性质寻找插入的位置,插入新节点:
使用parent ,cur 两个指针,parent 记录cur 的父节点 key大于当前节点,cur 往当前节点的右子树走,key小于当前节点则cur 往左子树走,如果等于返回false,因为二叉搜索树不会出现两个节点相同的情况。 当节点为空的时候,需要判断是parent 左节点还是右节点,此时用key 和parent 节点的值进行比较,如果key 小于parent 对应的值,则连接在左边,否则连接在右边。
bool Insert(const K& key)
{
if (_root == nullptr)
{
_root = new Node(key);
return true;
}
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (key < cur->_key)
{
parent = cur;
cur = cur->_left;
}
else if (key > cur->_key)
{
parent = cur;
cur = cur->_right;
}
else
{
return false;
}
}
cur = new Node(key);
if (key < parent->_key)
{
parent->_left = cur;
}
else
{
parent->_right = cur;
}
return true;
}
递归实现
相比于非递归的实现,递归实现不需要key的父节点,因为是通过递归的方式寻找左子树或右子树中的空节点来插入的,所以可以确定是左孩子还是右孩子。递归插入函数的子函数接收参数root时,必须采用引用接收,否则传入的root只是根节点指针的一份拷贝,当执行root = new Node(key); 这条语句时,只是这个拷贝指针指向了new出来的节点,root节点仍然是nullptr。
bool _InsertR(Node*& root, const K& key)
{
if (root == nullptr)
{
root = new Node(key);
return true;
}
if (key < root->_key)
{
return _InsertR(root->_left, key);
}
else if (key > root->_key)
{
return _InsertR(root->_right, key);
}
else
{
return false;
}
}
bool InsertR(const K& key)
{
return _InsertR(_root, key);
}
2.4. 查找的函数
根据二叉搜索树的特性,我们在二叉搜索树当中查找指定值的结点的方式如下:
- 如果二叉搜索树一开始为空树,或者查找到了叶子节点的左右孩子节点,则查找失败,返回nullptr。
- 如果key值小于当前结点的值,则应该在该结点的左子树当中进行查找。
- 如果key值大于当前结点的值,则应该在该结点的右子树当中进行查找。
- 如果key值等于当前结点的值,则查找成功,返回对应结点的地址。
非递归实现
Node* Find(const K& key)
{
Node* cur = _root;
while (cur)
{
if (key < cur->_key)
{
cur = cur->_left;
}
else if (key > cur->_key)
{
cur = cur->_right;
}
else
{
return cur;
}
}
return nullptr;
}
递归实现
Node* _FindR(Node* root, const K& key)
{
if (root == nullptr)
return nullptr;
if (key < root->_key)
{
return _FindR(root->_left, key);
}
else if (key > root->_key)
{
return _FindR(root->_right, key);
}
else
{
return root;
}
}
Node* FindR(const K& key)
{
return _FindR(_root, key);
}
2.5. 删除节点的函数
首先查找元素是否在树之中,如果不存在返回false。
存在的话要删除的节点可能有下面四种情况:
- 要删除的节点没有孩子节点 ,也就是叶子节点,此时可以直接删除。
- 要删除的节点只有左孩子节点,此时让父节点连接它的左节点,然后删除该节点。
- 要删除的孩子只有右孩子节点 ,此时让父节点连接它的右节点,然后删除节点
- 要删除的孩子有左右孩子节点 ,找到其左子树的最大值或者右子树的最小值,替换根节点的值,然后删除该节点。在删除时,有两种方法:(1)注意右子树的最小节点或左子树的最大节点是其父节点的左孩子还是右孩子,然后让其父节点指向该节点的左树或右树。(2)该节点符合上面三种情况,因此可以直接递归调用该函数删除该节点。
非递归实现
bool Erase(const K& key)
{
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (key < cur->_key)
{
parent = cur;
cur = cur->_left;
}
else if (key > cur->_key)
{
parent = cur;
cur = cur->_right;
}
else
{
if (cur->_left == nullptr)
{
if (cur == _root)
{
_root = cur->_right;
}
else
{
if (cur == parent->_left)
{
parent->_left = cur->_right;
}
else
{
parent->_right = cur->_right;
}
}
delete cur;
return true;
}
else if (cur->_right == nullptr)
{
if (cur == _root)
{
_root = cur->_left;
}
else
{
if (cur == parent->_left)
{
parent->_left = cur->_left;
}
else
{
parent->_right = cur->_left;
}
}
delete cur;
return true;
}
else
{
Node* minRight = cur->_right;
while (minRight->_left)
{
minRight = minRight->_left;
}
K minKey = minRight->_key;
Erase(minKey);
cur->_key = minKey;
}
}
}
return false;
}
递归实现
递归的思路和非递归相同。递归实现也要传引用,因为要删除根节点,_root的指向会发生改变,如果不传引用改变的是其拷贝的指向,_root的指向没有发生改变。
bool _EraseR(Node*& root, const K& key)
{
if (root == nullptr)
return false;
if (key < root->_key)
return _EraseR(root->_left, key);
else if (key > root->_key)
return _EraseR(root->_right, key);
else
{
if (root->_left == nullptr)
{
Node* del = root;
root = root->_right;
delete del;
}
else if (root->_right == nullptr)
{
Node* del = root;
root = root->_left;
delete del;
}
else
{
Node* minRight = root->_right;
while (minRight->_left)
{
minRight = minRight->_left;
}
K minKey = minRight->_key;
_EraseR(root->_right, minKey);
root->_key = minKey;
}
}
}
bool EraseR(const K& key)
{
return _EraseR(_root, key);
}
三、二叉搜索树的应用模型及实例
3.1. k模型
K模型,即只有key作为关键码,结构中只需存储key即可,关键码即为需要搜索到的值。比如排序去重;宿舍楼门禁,根据同学的学号是否在BSTree中查询该同学是否在宿舍中。
#include<iostream>
using namespace std;
template<class K>
struct BSTreeNode
{
K _key;
BSTreeNode<K>* _left;
BSTreeNode<K>* _right;
BSTreeNode(const K& key = 0)
:_key(key)
, _left(nullptr)
, _right(nullptr)
{}
};
template<class K>
class BSTree
{
typedef BSTreeNode<K> Node;
public:
BSTree()
:_root(nullptr)
{}
Node* _Copy(Node* root)
{
if (root == nullptr)
return nullptr;
Node* copyNode = new Node(root->_key);
copyNode->_left = _Copy(root->_left);
copyNode->_right = _Copy(root->_right);
return copyNode;
}
BSTree(const BSTree<K>& t)
{
_root = _Copy(t._root);
}
BSTree<K>& operator=(BSTree<K> t)
{
std::swap(_root, t._root);
return *this;
}
void _Destory(Node* root)
{
if (root == nullptr)
return;
_Destory(root->_left);
_Destory(root->_right);
delete root;
}
~BSTree()
{
_Destory(_root);
_root = nullptr;
}
bool Insert(const K& key)
{
if (_root == nullptr)
{
_root = new Node(key);
return true;
}
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (key < cur->_key)
{
parent = cur;
cur = cur->_left;
}
else if (key > cur->_key)
{
parent = cur;
cur = cur->_right;
}
else
{
return false;
}
}
cur = new Node(key);
if (key < parent->_key)
{
parent->_left = cur;
}
else
{
parent->_right = cur;
}
return true;
}
bool _InsertR(Node*& root, const K& key)
{
if (root == nullptr)
{
root = new Node(key);
return true;
}
if (key < root->_key)
{
return _InsertR(root->_left, key);
}
else if (key > root->_key)
{
return _InsertR(root->_right, key);
}
else
{
return false;
}
}
bool InsertR(const K& key)
{
return _InsertR(_root, key);
}
Node* Find(const K& key)
{
Node* cur = _root;
while (cur)
{
if (key < cur->_key)
{
cur = cur->_left;
}
else if (key > cur->_key)
{
cur = cur->_right;
}
else
{
return cur;
}
}
return nullptr;
}
Node* _FindR(Node* root, const K& key)
{
if (root == nullptr)
return nullptr;
if (key < root->_key)
{
return _FindR(root->_left, key);
}
else if (key > root->_key)
{
return _FindR(root->_right, key);
}
else
{
return root;
}
}
Node* FindR(const K& key)
{
return _FindR(_root, key);
}
bool Erase(const K& key)
{
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (key < cur->_key)
{
parent = cur;
cur = cur->_left;
}
else if (key > cur->_key)
{
parent = cur;
cur = cur->_right;
}
else
{
if (cur->_left == nullptr)
{
if (cur == _root)
{
_root = cur->_right;
}
else
{
if (cur == parent->_left)
{
parent->_left = cur->_right;
}
else
{
parent->_right = cur->_right;
}
}
delete cur;
return true;
}
else if (cur->_right == nullptr)
{
if (cur == _root)
{
_root = cur->_left;
}
else
{
if (cur == parent->_left)
{
parent->_left = cur->_left;
}
else
{
parent->_right = cur->_left;
}
}
delete cur;
return true;
}
else
{
Node* minRight = cur->_right;
while (minRight->_left)
{
minRight = minRight->_left;
}
K minKey = minRight->_key;
Erase(minKey);
cur->_key = minKey;
}
}
}
return false;
}
bool _EraseR(Node*& root, const K& key)
{
if (root == nullptr)
return false;
if (key < root->_key)
return _EraseR(root->_left, key);
else if (key > root->_key)
return _EraseR(root->_right, key);
else
{
if (root->_left == nullptr)
{
Node* del = root;
root = root->_right;
delete del;
}
else if (root->_right == nullptr)
{
Node* del = root;
root = root->_left;
delete del;
}
else
{
Node* minParent = root;
Node* minRight = root->_right;
while (minRight->_left)
{
minParent = minRight;
minRight = minRight->_left;
}
root->_key = minRight->_key;
if (minRight == minParent->_left)
{
minParent->_left = minRight->_right;
}
else
{
minParent->_right = minRight->_right;
}
delete minRight;
}
return true;
}
}
bool EraseR(const K& key)
{
return _EraseR(_root, key);
}
void _InOrder(Node* root)
{
if (root == nullptr)
return;
_InOrder(root->_left);
cout << root->_key << " ";
_InOrder(root->_right);
}
void InOrder()
{
_InOrder(_root);
cout << endl;
}
private:
Node* _root;
};
宿舍门禁
3.2. KV模型:
每一个关键码都有对应的value值,即二叉搜索树的参数为<key,value> 。 KV模型在K模型应用的基础上,增加了一些用法。 比如:中英对应字典,通过中文查找英文,统计次数。
KV模型和K模型相比只是多了一个参数:
template<class K, class V>
struct BSTreeNode
{
BSTreeNode(const K& key, const V& value)
:_key(key)
, _value(value)
, left(nullptr)
, right(nullptr)
{}
const K _key;
V _value;
BSTreeNode* left;
BSTreeNode* right;
};
template<class K, class V>
class BSTree
{
typedef BSTreeNode<K, V> Node;
public:
bool Insert(const K& key, const V& value)
{
if (_root == nullptr)
{
_root = new Node(key, value);
return true;
}
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (cur->_key > key)
{
parent = cur;
cur = cur->left;
}
else if (cur->_key < key)
{
parent = cur;
cur = cur->right;
}
else
{
return false;
}
}
Node* newnode = new Node(key, value);
if (parent->_key > key)
parent->left = newnode;
else
parent->right = newnode;
return true;
}
void _InOrder(Node* root)
{
if (!root)
return;
_InOrder(root->left);
cout << root->_key << ":" << root->_value << " ";
_InOrder(root->right);
}
void InOrder()
{
_InOrder(_root);
cout << endl;
}
Node* Find(const K& key)
{
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;
}
bool Erase(const K& key)
{
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (cur->_key > key)
{
parent = cur;
cur = cur->left;
}
else if (cur->_key < key)
{
parent = cur;
cur = cur->right;
}
else
{
if (cur->left == nullptr)
{
if (cur == _root)
{
_root = cur->right;
}
else
{
if (parent->left == cur)
{
parent->left = cur->right;
}
else if (parent->right == cur)
{
parent->right = cur->right;
}
}
delete cur;
}
else if (cur->right == nullptr)
{
if (cur == _root)
_root = cur->left;
else
{
if (parent->left == cur)
{
parent->left = cur->left;
}
else if (parent->right == cur)
{
parent->right = cur->left;
}
}
delete cur;
}
else
{
Node* smParent = cur;
Node* SubMax = cur->left;
while (SubMax->right)
{
smParent = SubMax;
SubMax = SubMax->right;
}
cur->_key = SubMax->_key;
if (smParent->right == SubMax)
smParent->right = SubMax->left;
else
smParent->left = SubMax->left;
delete SubMax;
}
return true;
}
}
return false;
}
private:
Node* _root = nullptr;
};
中英文对应字典
统计次数
|