1.什么是二叉搜索树?
二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:
1)若它的左子树不为空,则左子树上所有节点的值都小于根节点的值;
2)若它的右子树不为空,则右子树上所有节点的值都大于根节点的值;
3)它的左右子树也分别为二叉搜索树;
示意图:
2.这种数据结构的优缺点
优点:插入删除效率高,查找效率高;
缺点:根据插入顺序的不同,二叉搜索树最坏情况下可能会退化为单支树,也就是所有的节点在同一侧,这时查找的时间复杂度就是O(N)
3.主要操作及实现思路
插入
查找
如果要查找的值等于根结点的值,返回;
如果要查找的值小于根结点的值,去当前节点的左子树进行查找;
否则去当前节点的右子树进行查找
删除
第一步,查找要删除的元素的位置;
第二步,判断是否找到节点,没有找到返回false;
第三步,判断删除节点的左右子树是否为空,有四种情况:
1)左右子树都为空:将要删除节点的父节点的左孩子或者右孩子置为空;
2)左子树为空,右子树不为空:将要删除节点的右孩子连接到父节点的左边或者右边;
3)左子树不为空,右子树为空:将要删除节点的左孩子连接到父节点的左边或者右边;
4)左右孩子都存在:
找到左子树的最右节点;
将左子树最右节点的值和要删除节点的值进行交换;
让删除节点的父节点的左孩子或者右孩子指向删除节点的左子树最右节点的左孩子;
4.模拟实现源码
#pragma once
#include <iostream>
#include <stack>
using std::cout;
using std::endl;
//************************* 二叉搜索树 ***********************
//二叉搜索树节点
template<class T>
struct TreeNode
{
typedef TreeNode<T> Node;
T _data; //节点存储的数据
Node* _left; //当前节点的左孩子
Node* _right; //当前节点的右孩子
TreeNode(const T& data)
:_data(data)
, _left(nullptr)
, _right(nullptr)
{}
};
//二叉搜索树
template<class T>
class BSTree
{
private:
typedef TreeNode<T> Node;
Node* _root; //根节点
public:
//成员函数
BSTree()
:_root(nullptr)
{}
~BSTree()
{
if (_root)
{
Destroy(_root);
}
}
BSTree(const BSTree<T>& tree)
{
_root = Copy(tree._root);
}
BSTree<T>& operator=(const BSTree<T>& tree)
{
if (this != &tree)
{
//释放旧空间
Destroy(_root);
//拷贝一颗二叉树
_root = Copy(tree._root);
}
return *this;
}
//成员访问操作
//查找,返回值为空表示找不到
Node* FindNode(const T& data)
{
if (!_root)
{
return _root;
}
Node* curNode = _root;
while (curNode)
{
//当前节点的值等于要查找的节点的值,返回当前节点
if (data == curNode->_data)
{
return curNode;
}
//当前节点的值大于要查找的节点的值,去左子树进行查找
else if (data < curNode->_data)
{
curNode = curNode->_left;
}
//当前节点的值小于要查找的节点的值,去右子树进行查找
else
{
curNode = curNode->_right;
}
}
//找不到
return nullptr;
}
//插入数据
bool Push(const T& data)
{
//根节点为空,创建根节点
if (!_root)
{
_root = new Node(data);
return true;
}
//1.查找节点是否存在
if (FindNode(data))
{
//二叉搜索树不允许有重复节点
return false;
}
//2.找到合适的插入位置
Node* curNode = _root;
Node* parent = nullptr; //父节点
while (curNode)
{
if (data < curNode->_data)
{
//要插入的数据的值小于根节点的值,去左子树进行查找
parent = curNode;
curNode = curNode->_left;
}
else
{
//要插入的数据的值大于根节点的值,去右子树进行查找
parent = curNode;
curNode = curNode->_right;
}
}
//3.插入数据
if (data < parent->_data)
{
parent->_left = new Node(data);
}
else
{
parent->_right = new Node(data);
}
return true;
}
//删除数据
bool Pop(const T& data)
{
if (!_root)
{
return false;
}
//1.查找节点是否存在
if (!FindNode(data))
{
return false;
}
//2.找到节点的位置
Node* parent = nullptr; //父节点
Node* curNode = _root;
while (curNode)
{
if (data < curNode->_data)
{
parent = curNode;
curNode = curNode->_left;
}
else if (data > curNode->_data)
{
parent = curNode;
curNode = curNode->_right;
}
else
{
//找到了节点,退出查找
break;
}
}
//3.删除节点
//找到了目标节点,分情况讨论是不是根节点
//情况1:左右孩子都为空
if (!curNode->_left && !curNode->_right)
{
//删除的是根节点
if (curNode == _root)
{
_root = nullptr;
}
else
{
//更新父节点指向
if (parent->_left == curNode)
{
parent->_left = nullptr;
}
else
{
parent->_right = nullptr;
}
}
//删除节点
delete curNode;
curNode = nullptr;
}
//情况2:左孩子不为空,右孩子为空
else if (!curNode->_right)
{
//删除的是根节点
if (curNode == _root)
{
_root = curNode->_left;
}
else
{
//更新父节点指向
if (parent->_left == curNode)
{
parent->_left = curNode->_left;
}
else
{
parent->_right = curNode->_left;
}
}
//删除节点
delete curNode;
curNode = nullptr;
}
//情况3:左孩子为空,右孩子不为空
else if (!curNode->_left)
{
//判断删除的是否是根节点
if (curNode == _root)
{
_root = curNode->_right;
}
else
{
//更新父节点指向
if (parent->_left == curNode)
{
parent->_left = curNode->_right;
}
else
{
parent->_right = curNode->_right;
}
}
//删除节点
delete curNode;
curNode = nullptr;
}
//情况4:左右孩子都存在
else
{
//这种情况不需要考虑删除的是否是根节点,只需要进行节点指向的修改和节点的删除
//1.找到左孩子的最右节点
Node* LRMost_parent = curNode; //左孩子最右节点的父节点
Node* LRMost = curNode->_left; //左孩子的最右节点
while (LRMost->_right)
{
LRMost_parent = LRMost;
LRMost = LRMost->_right;
}
//2.将左孩子的最右节点和要删除的节点的值进行交换
T tmp = curNode->_data;
curNode->_data = LRMost->_data;
LRMost->_data = tmp;
//3.更新左孩子的最右节点的父节点的指向
if (LRMost_parent->_left == LRMost)
{
LRMost_parent->_left = LRMost->_left;
}
else
{
LRMost_parent->_right = LRMost->_left;
}
//4.删除左孩子的最右节点
delete LRMost;
LRMost = nullptr;
}
return true;
}
//中序遍历
void Inorder()
{
std::stack<Node*> st;
Node* curNode = _root;
while (curNode || !st.empty())
{
while (curNode)
{
st.push(curNode);
curNode = curNode->_left;
}
Node* top = st.top();
st.pop();
cout << top->_data << " ";
if (top->_right)
{
curNode = top->_right;
}
}
cout << endl;
}
private:
//销毁二叉树
void Destroy(Node* root)
{
if (root)
{
Destroy(root->_left);
Destroy(root->_right);
delete root;
_root = nullptr;
}
}
//递归拷贝二叉树
Node* Copy(Node* root)
{
Node* newRoot = nullptr;
if (root)
{
newRoot = new Node(root->_data);
newRoot->_left = Copy(root->_left);
newRoot->_right = Copy(root->_right);
}
return newRoot;
}
};