二叉搜索树
文章目录
一:二叉搜索树的简介
1.1.【二叉搜索树的概念】
小辉:小黑,二叉搜索树相比二叉树
有什么特别的地方吗?
小黑:当然有了,比如二叉搜索树
中结点值的大小一目了然,
还有其他的特殊之处,还需
要你自己去发现呢
- 二叉搜索树
二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:
- 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
- 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
- 它的左右子树也分别为二叉搜索树
小辉:大家看看下面这颗二叉搜索树
,核对一下相关的性质
- 看看这颗二叉树,是否满足上面的条件呢?
- 通过观察可以看到搜索二叉树的特点很明显.
1.2.【搜索二叉树的查找】
【查找过程】
- 1.判断根节点是否为空
- 2.比较查找值与根结点值是否相等,相等返回 true
- 3.若不相等的,继续查找根节点的左右子树
如果根节点的key>查找的key,则到根节点的左子树继续查找
如果根节点的key<查找的key,则到根节点的有子树继续查找
1.3.【搜索二叉树的插入】
【插入过程】
- 1.若为空树,直接插入
- 2.树不为空,按二叉搜索树性质查找合适的插入位置,插入新节点
1.4.【搜索二叉树的删除】
【删除过程】
首先查找元素是否在二叉搜索树中,如果不存在,则返回, 否则要删除的结点可能分下面四种情况:
- a.要删除的结点无孩子结点
if(cur->left==nullptr&& cur->right==nullptr){
if(cur!=_root){
if(parent->left==cur)
parent->left=nullptr;
if(parnet->right==cur)
parent->right==nullptr;
}else{
_root=nullptr;
}
}
delete cur;
cur=nullptr;
- b.要删除的结点只有左孩子结点
若要删除的结点只有左子树,则说明其左孩子就是其左子树中值最大的点,要删除掉该结点必须让其父亲结点和其左孩子结点连接起来
if(cur->left){
if(cur!=_root){
if(parent->left==cur)
parent->left=cur->left;
if(parent->right==cur)
parent->right==cur;
}else{
_root=parent->left;
}
}
delete cur;
cur=nullptr;
- c.要删除的结点只有右孩子结点
若要删除的结点只有右子树,则其右孩子结点就是其右子树中值最小的结点,要删除该结点需要将该结点的父亲结点和该结点的右孩子结点连接起来
if(cur->right){
if(cur!=_root){
if(parent->left==cur)
parent->left=cur->right;
if(parent->right==cur)
parent->right=cur->right;
}else{
_root=cur->right;
}
}
delete cur;
cur=nullptr;
-
d.要删除的结点有左、右孩子结点
-
情况一:左右结点都是叶子结点
- 情况二:其左子树存在左右结点
- 情况三:其左孩子只有右子树
- 情况四:其左孩子只有左子树
if(cur->left && cur->right){
//需要先到该结点的左子树中找到左子树最大的结点或者
//到右子树中找到最小的结点
pNode pNext=cur->left;
parent=cur;
//删除结点的左结点左右孩子都不存在的情况
if(pNext->left==nullptr && pNext->right==nullptr){
cur->value=pNext->value;
cur->left=nullptr;
}
//删除结点的左结点不存在右子树的情况
if(pNext->left&&pNext->right==nullptr){
parent->left=pNext->left;
}else{
//删除结点的左结点存在右子树的情况
//当前结点的左节点的右子树的情况,该右子树的最右结点
while(pNext->right){
parent=pNext;
pNext=pNext->right;
}
cur->value=pNext->value;
if(pNext->left){
parent->right=pNext->left;
}
if(parent->right==pNext){
parent->right==nullptr;
}
}
//判断pNext是否存在左子树
delete pNext;
pNext=nullptr;
}
看起来有待删除节点有4中情况,实际情况a可以与情况b或者c合并起来,因此真正的删除过程如下:
- 情况b:删除该结点且使被删除节点的双亲结点指向被删除节点的左孩子结点
- 情况c:删除该结点且使被删除节点的双亲结点指向被删除结点的右孩子结点
- 情况d:在它的右子树中寻找中序下的第一个结点(关键码最小),用它的值填补到被删除节点中,再来处理该结点的删除问题
二:二叉搜索树的实现
#include <iostream>
using namespace std;
//树结点的定义
template <class T>
struct BSTNode{
BSTNode(const T&val=T())
:_data(val)
, _pLeft(nullptr)
, _pRight(nullptr)
{}
T _data;
BSTNode<T>* _pRight;
BSTNode<T>* _pLeft;
};
//搜索二叉树的实现
template <class T>
class BSTree{
public:
typedef BSTNode<T> Node;//结点的引用
typedef Node* pNode;//结构体指针
//构造函数
BSTree(const pNode root=nullptr)
:_root(root)
{}
//查找指定值
pNode find(const T& val){
if (_root == nullptr)
return nullptr;
pNode cur = _root;
while (cur){
if (cur->_data == val)
return cur;
else if (cur->_data > val){
cur = cur->_pLeft;
}
else{
cur = cur->_pRight;
}
}
//程序运行到这里,则说明没找到想要的值
return nullptr;
}
//插入一个值
bool insert(const T&val){
if (_root == nullptr){
_root = new Node(val);
return true;
}
pNode cur = _root;
pNode parent = nullptr;//记录根节点的位置
while (cur){
parent = cur;
if (cur->_data > val){
cur = cur->_pLeft;
//根节点的值大于所要查找的值,找根节点的左子树
}
else if (cur->_data < val){
//根节点的值小于所要查找的值,找根节点的右子树
cur = cur->_pRight;
}
else{
//二叉搜索树不存在重复的结点
return false;
}
}
pNode newnode = new Node(val);
//申请值为插入值的结点
if (parent->_data >val){
parent->_pLeft = newnode;
}else
parent->_pRight=newnode;
return true;
}
/*
插入的总结
首先确定根节点是否为空,为空直接将值插入到根节点
不为空的话,按照二叉搜索树的性查找合适的插入点
当找到合适的插入点的时候判断插入点的结点值和插入结点值的大小关系,
在将值插入到对应的结点
*/
//中序遍历
void Inorder(){
_Inorder(_root);
cout << endl;
}
void _Inorder(pNode root){
if (root){
_Inorder(root->_pLeft);
cout << root->_data << "-->";
_Inorder(root->_pRight);
}
}
//删除操作
bool erase(const T& val){
if (_root == nullptr)
return false;
pNode cur = _root;
pNode parent = nullptr;
//寻找要删除的结点的位置
while (cur){
if (cur->_data == val)
break;
else if (cur->_data > val){
parent = cur;
cur = cur->_pLeft;
}
else{
parent = cur;
cur = cur->_pRight;
}
}
//删除
//1.删除叶子结点
if (cur->_pLeft == nullptr&&cur->_pRight == nullptr){
//是否删除根节点
if (cur != _root){
//让父亲结点对应位置置为空
if (parent->_pLeft == cur)
parent->_pLeft = nullptr;
else
parent->_pRight = nullptr;
}
else{
//删除_root,树为空
_root = nullptr;
}
delete cur;
cur = nullptr;
}
//2.非叶子节点,但是只有一个结点(右结点)
//首先确当当前结点的父亲结点在左边还是右边,对应位置
//置为cur->_pRight
else if (cur->_pLeft == nullptr){
if (cur != _root){
if (parent->_pLeft == cur)
//当前结点没有左孩子,但有右孩子
parent->_pLeft = cur->_pRight;
else
//当前结点没有左孩子,但有右孩子
parent->_pRight = cur->_pRight;
}
else{
//若删除的是根节点的
//更新_root
_root = _root->_pRight;
}
delete cur;
cur = nullptr;
}
//删除的是非叶子结点,却当前结点的右孩子为空
else if (cur->_pRight == nullptr){
if (cur != _root){
if (parent->_pLeft == cur)
//当前结点没有右孩子,但有左孩子
parent->_pLeft = cur->_pLeft;
else
//当前结点没有右孩子,但有左孩子
parent->_pRight = cur->_pLeft;
}
else{
//若删除的是根节点的
//更新_root
_root = _root->_pLeft;
}
delete cur;
cur = nullptr;
}
//3.左右孩子都存在,需要做置换
else {
//需要先到该结点的左子树中找到左子树最大的结点或者
//到右子树中找到最小的结点
pNode pNext=cur->_pLeft;
parent=cur;
//删除结点的左结点左右孩子都不存在的情况
if(pNext->_pLeft==nullptr && pNext->_pRight==nullptr){
cur->_value=pNext->_value;
cur->_pLeft=nullptr;
}
//删除结点的左结点不存在右子树的情况
if(pNext->_pLeft&&pNext->_pRight==nullptr){
parent->_pLeft=pNext->_pLeft;
}else{
//删除结点的左结点存在右子树的情况
//当前结点的左节点的右子树的情况,该右子树的最右结点
while(pNext->_pRight){
parent=pNext;
pNext=pNext->_pRight;
}
cur->_value=pNext->_value;
if(pNext->_pLeft){
parent->_pRight=pNext->_pLeft;
}
if(parent->_pRight==pNext){
parent->_pRight==nullptr;
}
}
//判断pNext是否存在左子树
delete pNext;
pNext=nullptr;
}
return true;
}
/*
删除的总结
1.寻找所要删除的结点的位置
2.开始删除
2.1:删除叶子节点
判断索要删除的结点是否为根节点
若不是则将叶子节点置空最后删除
若是的话,直接将根节点置空最后删除
2.2:删除非叶子结点
1.只有左孩子和只有右孩子的情况
需要建立该节点的父亲结点与其子节点的关系
2.两个孩子都有的情况
将该节点的左子树中最大者或者右子树中最小者移动到当前位置
该过程发生结点值的交换最终完成带有两个孩子结点的删除
*/
~BSTree(){
Distory(_root);
}
void Distory(pNode root){
if (root){
Distory(root->_pLeft);
Distory(root->_pRight);
delete root;
root = nullptr;
}
}
private:
pNode _root;
};
void testBSTree(){
BSTree<int> bt;
bt.insert(5);
bt.insert(3);
bt.insert(4);
bt.insert(1);
bt.insert(0);
bt.insert(2);
bt.insert(7);
bt.insert(6);
bt.insert(8);
bt.insert(9);
bt.Inorder();
bt.erase(4);
bt.Inorder();
bt.erase(8);
bt.Inorder();
bt.erase(0);
bt.Inorder();
bt.erase(1);
bt.Inorder();
bt.erase(2);
bt.Inorder();
bt.erase(5);
bt.Inorder();
bt.erase(3);
bt.Inorder();
bt.erase(7);
bt.Inorder();
bt.erase(6);
bt.Inorder();
bt.erase(9);
}
int main(){
testBSTree();
return 0;
}
小黑,小辉:一起来学习C++吧,
二叉搜索树的学习是
否刷新了你对二叉树的
认识了?期待下期再见.