概念
二叉搜索树(BST - Binary Search Tree)是一种特殊的二叉树,每个顶点最多可以有两个子节点。其遵顼以下规则:
其有两个特性:
- 查找数据非常快,每次查找数据,只需要将数据与当前节点比较,然后决定去左子树还是右子树找,如果最后找到了
nullptr
,那就是不存在。 - 二叉搜索树的中序遍历,得到值是有序的。正是因为二叉搜索树左子树的值都小于根,右子树的值都大于根。中序遍历是 左子树 - 根 - 右子树,所以最后得到的数据就是有序的
模拟实现
节点类
:
template<class K>
struct BSTreeNode
{
typedef BSTreeNode<K> Node;
Node* _left;
Node* _right;
K _key;
BSTreeNode(const K& key)
:_left(nullptr)
,_right(nullptr)
,_key(key)
{}
};
二叉搜索树类
:
template<class K>
class BSTree
{
typedef BSTreeNode<K> Node;
private:
Node* _root = nullptr;
};
插入
想要对二叉搜索树进行节点插入,有两种情况:
值得注意的是:如果某一个值不存在于二叉搜索树中,那么插入这个值一定是在nullptr
插入。
bool Insert(const K& key)
{
if (_root == nullptr)
{
_root = new Node(key);
return true;
}
Node* cur = _root;
Node* parent = nullptr;
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;
}
中序遍历
中序遍历,对于二叉树而言是一个比较简单的操作,我们看到以下代码:
void InOrder(Node* root)
{
if (root == nullptr)
return;
InOrder(root->_left);
cout << root->_key << " - ";
InOrder(root->_right);
}
以上代码,先遍历左子树InOrder(root->_left);
,然后输出当前节点的值cout << root->_key << " - ";
,再遍历右子树InOrder(root->_right);
。是一个很常规的中序遍历,但是存在一个问题:这个函数外部没法传参。
比如说我要遍历某一个二叉搜索树bst
:
bst.InOrder(bst._root);
这个调用是存在问题的,那就是_root
是私有成员,外部不能把_root
作为参数传入中序遍历的接口。此时我们就需要再在外面套一层接口,像这样:
void InOrder()
{
_InOrder(_root);
}
void _InOrder(Node* root)
{
if (root == nullptr)
return;
_InOrder(root->_left);
cout << root->_key << " - ";
_InOrder(root->_right);
}
我们给函数InOrder
创建了一个子函数_InOrder
,外部无需传参就可以调用InOrder
。我们将中序遍历的代码写在了子函数中,而在InOrder
内部,再去调用这个子函数 _InOrder(_root);
,就可以正常传参了。
查找
想要查找,基本逻辑就是:
bool 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 true;
}
return false;
}
删除
首先,既然要删除特定的节点,那么我们就要先查找到该节点。既然要修改该节点,那么也要查找到其父节点,这个思路与前面的插入接口非常相似。
只要被删除的节点,有一个子树为nullptr
,那么就可以将另外一个子树直接链接到其父节点下。
这里还要额外考虑根节点的情况
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;
}
else if (cur->_right == nullptr)
{
if (cur == _root)
{
_root = cur->_right;
}
else
{
if (cur == parent->_left)
parent->_left = cur->_left;
else
parent->_right = cur->_left;
}
delete cur;
}
else
{
//其他情况
}
当待删除节点的左右子树都不为空
有两种解决方案:
取出节点后,我们把右子树最小节点rightMin
的值交给待删除节点的cur
,但是rightMin
还有可能有右子树,那么就要把右子树移交给rightMin
的父节点。
Node* rightMinParent = cur;
Node* rightMin = cur->_right;
while (rightMin->_left)
{
rightMinParent = rightMin;
rightMin = rightMin->_left;
}
cur->_key = rightMin->_key;
if (rightMinParent->_left == rightMin)
rightMinParent->_left == rightMin->_right;
else
rightMinParent->_right == rightMin->_right;
delete rightMin;
删除总代码:
bool Erase(const K& key)
{
Node* cur = _root;
Node* parent = nullptr;
while (cur)
{
if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
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;
}
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;
}
else
{
Node* rightMinParent = cur;
Node* rightMin = cur->_right;
while (rightMin->_left)
{
rightMinParent = rightMin;
rightMin = rightMin->_left;
}
cur->_key = rightMin->_key;
if (rightMin == rightMinParent->_left)
rightMinParent->_left = rightMin->_right;
else
rightMinParent->_right = rightMin->_right;
delete rightMin;
}
return true;
}
}
return false;
}
析构函数
想要删除整棵树,那就需要递归式地删除每一个节点,为了保证树的节点不会错乱,我们最好通过后序遍历删除,代码如下:
~BSTree()
{
Destroy(_root);
}
void Destroy(Node* root)
{
if (root == nullptr)
return;
Destroy(root->_left);
Destroy(root->_right);
delete root;
}
拷贝构造
想要拷贝一棵树出来,我们也需要进行递归式的深拷贝,不过由于要先有父节点,再有子节点,所以要用前序遍历。代码如下:
BSTree(const BSTree<K>& t)
{
_root = Copy(t._root);
}
Node* Copy(Node* root)
{
if (root == nullptr)
return nullptr;
Node* newRoot = new Node(root->_key);
newRoot->_left = Copy(root->_left);
newRoot->_right = Copy(root->_right);
return newRoot;
}
赋值重载
代码如下:
BSTree<K>& operator=(BSTree<K> t)
{
swap(_root, t._root);
return *this;
}
递归查找
bool FindR(const K& key)
{
return _FindR(_root, key);
}
bool _FindR(Node* root, const K& key)
{
if (root == nullptr)
return false;
if (key < root->_key)
return _FindR(root->_left, key);
else if (key > root->_key)
return _FindR(root->_right, key);
else
return true;
}
递归插入
bool InsertR(const K& key)
{
return _InsertR(_root, key);
}
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 EraseR(const K& key)
{
return _EraseR(_root, key);
}
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
{
//删除节点
}
}
通过递归找到删除节点后,要考虑两种情况:
先看到第一种:
Node* del = root;
if (root->_left == nullptr)
{
root = root->_right;
}
else if (root->_right == nullptr)
{
root = root->_left;
}
else
{
}
delete del;
return true;
接下来我们再看到左右子树都不为nullptr
的情况:
else
{
Node* rightMin = root->_right;
while (rightMin->_left)
{
rightMin = rightMin->_left;
}
swap(root->_key, rightMin->_key);
return _EraseR(root->_right, key);
}
总代码展示
BinarySearchTree.h
:
#pragma once
#include <iostream>
using namespace std;
template<class K>
struct BSTreeNode
{
typedef BSTreeNode<K> Node;
Node* _root;
Node* _left;
Node* _right;
K _key;
BSTreeNode(const K& key)
:_root(nullptr)
,_left(nullptr)
,_right(nullptr)
,_key(key)
{}
};
template<class K>
class BSTree
{
typedef BSTreeNode<K> Node;
public:
BSTree() = default;
BSTree(const BSTree<K>& t)
{
_root = Copy(t._root);
}
Node* Copy(Node* root)
{
if (root == nullptr)
return nullptr;
Node* newRoot = new Node(root->_key);
newRoot->_left = Copy(root->_left);
newRoot->_right = Copy(root->_right);
return newRoot;
}
~BSTree()
{
Destroy(_root);
}
void Destroy(Node* root)
{
if (root == nullptr)
return;
Destroy(root->_left);
Destroy(root->_right);
delete root;
}
BSTree<K>& operator=(BSTree<K> t)
{
swap(_root, t._root);
return *this;
}
bool Insert(const K& key)
{
if (_root == nullptr)
{
_root = new Node(key);
return true;
}
Node* cur = _root;
Node* parent = nullptr;
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 Erase(const K& key)
{
Node* cur = _root;
Node* parent = nullptr;
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;
}
else if (cur->_right == nullptr)
{
if (cur == _root)
{
_root = cur->_right;
}
else
{
if (cur == parent->_left)
parent->_left = cur->_left;
else
parent->_right = cur->_left;
}
delete cur;
}
else
{
Node* rightMinParent = cur;
Node* rightMin = cur->_right;
while (rightMin->_left)
{
rightMinParent = rightMin;
rightMin = rightMin->_left;
}
cur->_key = rightMin->_key;
if (rightMinParent->_left == rightMin)
rightMinParent->_left == rightMin->_right;
else
rightMinParent->_right == rightMin->_right;
delete rightMin;
}
return true;
}
}
return false;
}
bool 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 true;
}
return false;
}
void InOrder()
{
_InOrder(_root);
cout << "end" << endl;
}
bool FindR(const K& key)
{
return _FindR(_root, key);
}
bool InsertR(const K& key)
{
return _InsertR(_root, key);
}
bool EraseR(const K& key)
{
return _EraseR(_root, key);
}
private:
void _InOrder(Node* root)
{
if (root == nullptr)
return;
_InOrder(root->_left);
cout << root->_key << " - ";
_InOrder(root->_right);
}
bool _FindR(Node* root, const K& key)
{
if (root == nullptr)
return false;
if (key < root->_key)
return _FindR(root->_left, key);
else if (key > root->_key)
return _FindR(root->_right, key);
else
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 _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
{
Node* del = root;
if (root->_left == nullptr)
{
root = root->_right;
}
else if (root->_right == nullptr)
{
root = root->_left;
}
else
{
Node* rightMin = root->_right;
while (rightMin->_left)
{
rightMin = rightMin->_left;
}
swap(root->_key, rightMin->_key);
return _EraseR(root->_right, key);
}
delete del;
return true;
}
}
Node* _root = nullptr;
};