二叉搜索树
二叉搜索树又叫做二叉排序树,具有以下特性:
- 二叉搜索树的左孩子比父节点小,右孩子比父节点大
- 二叉搜索树的左子树的全部节点都小于根节点,右子树的全部节点都大于根节点
- 所有节点的左右子树都为二叉搜索树
- 键值是唯一的,所以二叉搜索树不能有相同的键值
根据使用场景的不同,二叉搜索树还分为K模型和KV模型:
K模型:即只有key作为关键码,只需要存储Key即可(底层实际存放的是<value,value>键值对),关键码为需要搜索到的值,如STL中的set,multiset
V模型: 每一个关键码key,都有与之对应的值value,即<key,value>键值对.如STL中的map,multimap
二叉搜索树的作用:
1. 排序:
中序遍历:先遍历左子树,再遍历根节点,再遍历右子树.
二叉搜索树的特性:左子树小于根节点,右子树大于根节点
所以通过一次中序遍历,即可以获得排序的结果.
2. 查找:
对于二叉搜索树,查找是其主要的功能,STL中的map和set底层也是通过平衡二叉搜索树(红黑树)实现的.
二叉搜索树的查找十分简单,键值比根节点大则进入右子树,键值比根节点小则进入左子树,它的思路有点类似二分查找,平均时间复杂度为0(log2N)
但是上述情况仅限于二叉搜索树是一个完全二叉树,如果构建时树为有序数列,则二叉搜索树会退化为单支树,时间复杂度则会变成O(N)
二叉搜索树的实现思路:
节点数据结构:节点值,左右节点
template<class T>
struct BSTNode {
T _val;
BSTNode<T>* _left;
BSTNode<T>* _right;
BSTNode(const T& val = T())
:_val(val)
,_left(nullptr)
,_right(nullptr)
{}
};
查找:
查找是二叉搜索树的核心,插入以及删除操作都可以复用这里的代码
思路:直接从根节点出发,比根节点大则查找右子树,比根节点小则查找左子树,相同则返回.如果遍历完还没有找到,则说明不存在此树中,返回nullptr
Node* Find(const K& key)
{
//根据二叉搜索树的性质,从根节点出发,比根节点大则查找右子树,比根节点小则查找左子树
Node* cur = _root;
while (cur){
//比根节点大则查找右子树
if (key > cur->_key)
{
cur = cur->_right;
}
//比根节点小则查找左子树
else if (key < cur->_key)
{
cur = cur->_left;
}
//相同则返回
else
{
return cur;
}
}
//遍历完则说明查找不到,返回false
return nullptr;
}
插入:
先找到需要插入的位置(为了能够保证找到父节点,还需要用一个指针更新父节点)
找到合适的位置后,需要判断当前的键值与父节点的大小,进而判断出应该查到父节点的左子树还是右子树.
//插入:查找+插入
bool insert(const T& val) {
if (_root == nullptr) {
_root = new Node(val);
return true;
}
Node* cur = _root;
Node* parent = nullptr;
while (cur) {
parent = cur;
if (cur->_val == val) {
return false;
}
else if (cur->_val > val) {
cur = cur->_left;
}
else {
cur = cur->_right;
}
}
cur = new Node(val);
//找到合适的位置
if(parent->_val < val){
parent->_right = cur;
}
else {
parent->_left = cur;
}
return true;
}
删除:
删除操作相对起来会比较复杂,因为对于非叶子节点,删除节点就会导致二叉搜索树的结构破坏,所以不能直接删除,需要找到一个节点替换原有节点,再将原有节点删除.
删除分为三种情况:
- 删除叶子节点:直接删除
- 删除的节点只有一个子树:如果删除的节点只有一个子树,那么删除这个节点后,就让父节点指向他的这一子树(前两种情况可以合并处理)
- 删除的节点有左右子树:选择一个节点来替换它后,再删除,就可以不破坏原有结构.如果要保持原有结构不变化,那么选择的节点必须要和删除节点在中序遍历中是连续的,而满足的只有两个节点,一个是其左子树的最大值,一个是其右子树的最小值。
//删除
bool erase(const T& val) {
//查找
Node* cur = _root;
Node* parent = nullptr;
while (cur) {
parent = cur;
if (cur->_val == val)
break;
else if (cur->_val > val){
cur = cur->_left.;
}
else {
cur = cur->_right;
}
}
if (cur == nullptr) {
return false;
}
//删除:
//如果删除节点为叶子节点:
if (cur->_left == nullptr && cur->_right == nullptr) {
if (cur == _root)
_root = nullptr;
else {
if (parent->_left == cur)
parent->_left = nullptr;
else
parent->_right = nullptr;
}
delete cur;
}
//删除的节点只有一个子树:
//左节点为空,度为1
else if (cur->_left == nullptr) {
if (cur == _root) {
_root = cur->_right;
}
else {
if (parent->_left == cur) {
parent->_left = cur->_right;
}
else {
parent->_right = cur->_right;
}
}
delete cur;
}
//
//右节点为空,度为1
else if (cur->_right == nullptr) {
if (cur == _root) {
_root = cur->_left;
}
else {
if (parent->_left == cur) {
parent->_left = cur->_left;
}
else {
parent->_right = cur->_left;
}
}
delete cur;
}
//左右子树都不为空:
else {
Node* leftMostChild = cur->_right; //删除节点右子树的最左节点
Node* parent = cur; //删除节点
//找右子树的最左结点
while (leftMostChild->_left) {
parent = leftMostChild;
leftMostChild = leftMostChild->_left;
}
cur->_val = leftMostChild->_val;
//删除右子树最左结点
if (parent->_right == leftMostChild)
parent->_right = leftMostChild->_right;
else
parent->_left = leftMostChild->_right;
delete leftMostChild;
}
return true;
}
整体代码实现:
#include <iostream>
using namespace std;
template<class T>
struct BSTNode {
T _val;
BSTNode<T>* _right;
BSTNode<T>* _left;
BSTNode(T val)
:_val(val)
, _right(nullptr)
, _left(right)
{}
};
template<class T>
class BSTree {
public:
typedef BSTNode<T> Node;
//查找某个值在二叉树中是否存在
Node* find(const T& val) {
Node* cur = _root;
while (cur) {
if (cur->_val == val) {
return cur;
}
else if (cur->_val > val) {
cur = cur->_left;
}
else {
cur = cur->_right;
}
}
return nullptr;
}
//二叉搜索树插入节点
bool insert(const T& val) {
if (_root == nullptr) {
_root = new Node(val);
return true;
}
Node* cur = _root;
Node* parent = nullptr;
while (cur) {
parent = cur;
if (cur->_val == val) {
return false;
}
else if (cur->_val > val) {
cur = cur->_left;
}
else {
cur = cur->_right;
}
}
//找到插入的位置,执行插入操作
cur = new Node(val);
if (val < parent->_val) {
parent->_left = cur;
}
else {
parent->_right = cur;
}
return true;
}
//删除
bool erase(const T& val) {
//查找
Node* cur = _root;
Node* parent = nullptr;
while (cur) {
if (cur->_val == val)
break;
else if (cur->_val > val){
parent = cur;
cur = cur->_left.;
}
else {
parent = cur;
cur = cur->_right;
}
}
if (cur == nullptr) {
return false;
}
//删除:
if (cur->_left == nullptr && cur->_right == nullptr) {
if (cur == _root)
_root = nullptr;
else {
if (parent->_left == cur)
parent->_left = nullptr;
else
parent->_right = nullptr;
}
delete cur;
}
//左节点为空,度为1
else if (cur->_left == nullptr) {
if (cur == _root) {
_root = cur->_right;
}
else {
if (parent->_left == cur) {
parent->_left = cur->_right;
}
else {
parent->_right = cur->_right;
}
}
delete cur;
}
//右节点为空,度为1
else if (cur->_right == nullptr) {
if (cur == _root) {
_root = cur->_left;
}
else {
if (parent->_left == cur) {
parent->_left = cur->_left;
}
else {
parent->_right = cur->_left;
}
}
delete cur;
}
else {
Node* leftMostChild = cur->_right;
Node* parent = cur;
//找右子树的最左结点
while (leftMostChild->_left) {
parent = leftMostChild;
leftMostChild = leftMostChild->_left;
}
cur->_val = leftMostChild->_val;
//删除右子树最左结点
if (parent->_right == leftMostChild)
parent->_right = leftMostChild->_right;
else
parent->_left = leftMostChild->_right;
delete leftMostChild;
}
return true;
}
//按顺序输出:提供给外界的接口,因为外界无法访问类的私有成员_root
void inorder() {
_inorder(_root);
cout << endl;
}
//中序遍历
void _inorder(Node* root) {
if (root) {
//中序遍历
inorder(root->left);
cout << root->_val << " ";
_inorder(root->_right);
}
}
void destroy(Node* root) {
if (root) {
destroy(root->_left);
destroy(root->_right);
delete root;
}
}
~BSTree() {
destroy(_root);
}
void copyTree(Node* root) {
if (root) {
insert(root->_val);
copyTree(root->left);
copyTree(root->_right);
}
}
Node* copyTree2(Node* root) {
if (root) {
Node* cur = new Node(root->val);
cur->_left = copyTree2(root->_left);
cur->_right = copyTree2(root->_right);
return cur;
}
}
//构造函数
BSTree()
:_root(nullptr)
{}
//拷贝构造
BSTree(const BSTree<T>& bst)
:_root(nullptr)
{
_root = copyTree2(bst._root);
}
//赋值运算符重载:现代写法
//BSTree<T>& operator=(BSTree<T>& bst) {
//swap(_root, bst._root);
//return *this;
//}
BSTree<T>& operator=(const BSTree<T>& bst) {
if (this != &bst) {
destroy(_root);
_root = copyTree2(bst._root);
}
return *this;
}
private:
Node* _root;
};