目录
二叉搜索树的概念
二叉搜索树操作
二叉搜索树的查找
二叉搜索树的插入
二叉搜索树的删除
要删除的结点无孩子结点
要删除的结点只有右孩子结点
要删除的结点只有左孩子结点
要删除的结点有左、右孩子结点都存在
代码实现
二叉搜索树的概念
二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:
1.若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
2.若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
3.它的左右子树也分别为二叉搜索树,如下图所示:
二叉搜索树操作
二叉搜索树的查找
二叉搜索树的查找操作其实很简单,若根节点不空,只要比较要查找的值与当前根节点值的大小,根据比较结果去根节点的左子树或右子树中查找就可以了,然后重复上述过程。
二叉搜索树的插入
根据二叉搜索树的性质,找到要插入的位置,将新节点插入即可(如果树为空,直接插入后返回)
二叉搜索树的删除
首先查找元素是否在二叉搜索树中,如果不存在,则返回, 否则要删除的结点可能分下面四种情况:
要删除的结点无孩子结点
找到该节点,直接删除
要删除的结点只有右孩子结点
找到该节点,同时在寻找的过程中记录其双亲,让其双亲指向待删除的节点的右孩子节点即可。如下图:
二叉搜索树的查找
二叉搜索树的查找操作其实很简单,若根节点不空,只要比较要查找的值与当前根节点值的大小,根据比较结果去根节点的左子树或右子树中查找就可以了,然后重复上述过程。
二叉搜索树的插入
根据二叉搜索树的性质,找到要插入的位置,将新节点插入即可(如果树为空,直接插入后返回)
二叉搜索树的删除
首先查找元素是否在二叉搜索树中,如果不存在,则返回, 否则要删除的结点可能分下面四种情况:
要删除的结点无孩子结点
找到该节点,直接删除
要删除的结点只有右孩子结点
找到该节点,同时在寻找的过程中记录其双亲,让其双亲指向待删除的节点的右孩子节点即可。如下图:
二叉搜索树的删除
首先查找元素是否在二叉搜索树中,如果不存在,则返回, 否则要删除的结点可能分下面四种情况:
要删除的结点无孩子结点
找到该节点,直接删除
要删除的结点只有右孩子结点
找到该节点,同时在寻找的过程中记录其双亲,让其双亲指向待删除的节点的右孩子节点即可。如下图:
要删除的结点只有右孩子结点 找到该节点,同时在寻找的过程中记录其双亲,让其双亲指向待删除的节点的右孩子节点即可。如下图:
要删除的结点只有左孩子结点
找到该节点,同时在寻找的过程中记录其双亲,让其双亲指向待删除的节点的左孩子节点即可。
过程和只存在右孩子节点类似
要删除的结点有左、右孩子结点都存在
不能直接删除该节点,需要分几步完成:
1.找替代节点:在待删除节点的右子树中找最左侧节点,或者在待删除节点左子树中找最右侧节点。
2.将替代节点中的值域赋值给待删除节点。
3.将替代节点删除(注意该替代节点可能还存在左子树或者右子树,在删除时需要考虑其左右子树)
如下图,删除值为5的节点,去其右子树中找最左侧节点,过程如下:
代码实现
首先,节点的结构得有,所以定义如下模板类:
template
class BTNode{
public:
BTNode* _left;//左指针阈
BTNode* _right;//右指针阈
T _val;//值域
BTNode(T val=T())//使用默认的_val
:_left(nullptr)
,_right(nullptr)
,_val(val)
{
}
};
代码实现
首先,节点的结构得有,所以定义如下模板类:
template
class BTNode{
public:
BTNode* _left;//左指针阈
BTNode* _right;//右指针阈
T _val;//值域
BTNode(T val=T())//使用默认的_val
:_left(nullptr)
,_right(nullptr)
,_val(val)
{
}
};
使用模板类,可以存放任意类型的元素。
然后就可以定义二叉搜索树了,代码如下:
templateclass BSTree{ typedef BTNode Node;//重命名BTNode ,方便使用 public: BSTree()//构造函数,初始化root为空 :_root(nullptr) { } Node* Find(const T& val){//查找元素 Node* cur = _root; while (cur){ if (cur->_val _right; } else if (cur->_val>val){ cur = cur->_left; } else{ return cur; } } return nullptr; } Node* insert(T val){//插入元素 if (_root==nullptr){//当前树为空,插入一个元素后直接返回 _root = new Node(val); return _root; } //当前树不空,如下: Node* parent=nullptr; Node* child = _root; while (child){//循环结束后,要插入的节点就是parent的左或者右孩子 parent = child; if (val _val){ child = child->_left; } else{ child = child->_right; } } if (parent->_val>val){//判断要把新节点插入到parent的左还是右 parent->_left = new Node(val); } else{ parent->_right = new Node(val); } return _root; } bool Erase(T val){//删除元素 Node* parent = nullptr; Node* cur = _root; while (cur){//寻找待删除节点,注意parent = cur;必须放在if与else if中 if (val _val){ parent = cur; cur = cur->_left; } else if (val>cur->_val){ parent = cur; cur = cur->_right; } else{ break; } } if (cur==nullptr){//表示没找到,直接返回 return false; } //找到了待删除节点,如下: if (cur->_left == nullptr){//待删除节点只有右孩子,或者待删除节点没有孩子 if (parent == nullptr){//待删除节点是根节点 _root = cur->_right; delete cur; } else{//待删除节点不是根节点 if (parent->_left == cur){ parent->_left = cur->_right; } else{ parent->_right = cur->_right; } delete cur; } } else if (cur->_right==nullptr){//待删除节点只有左孩子 if (parent==nullptr){//待删除节点是根节点 _root = cur->_left; delete cur; } else{//待删除节点不是根节点 if (parent->_left == cur){ parent->_left = cur->_left; } else{ parent->_right = cur->_left; } delete cur; } } else{//待删除节点左右孩子都有 parent = cur; Node* rec = cur->_right; while (rec->_left){//找到其右子树中最左侧节点 parent = rec; rec = rec->_left; } cur->_val = rec->_val;//覆盖待删除节点的值 if (parent->_left==rec){//连接右子树中最左侧节点的右子树 parent->_left = rec->_right; } else{ parent->_right = rec->_right; } delete rec;//删除掉右子树中最左侧节点 } return true; } ~BSTree(){//析构 Destroy(_root); } void InOrder(){//暴露给外部,中序遍历 InOrder(_root); cout << endl; } private: void Destroy(Node*& root){//利用递归销毁二叉树 if (root==nullptr){ return; } Destroy(root->_left); Destroy(root->_right); delete root; root = nullptr; } void InOrder(Node*& root){//中序遍历 if (root==nullptr){ return; } InOrder(root->_left); cout << root->_val << ' '; InOrder(root->_right); } private: Node* _root;//底层维护的指针,指向根节点 };
代码中要注意的点就是当树为空时要插入元素,直接插入返回。当待删除节点是根节点时,需要改变底层维护的指针_root的指向。



