二叉搜索树
【概念】
二叉搜索树(BST,BinarySearchTree)又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:
- 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
- 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
- 它的左右子树也分别为二叉搜索树
先定义二叉树的结构
struct BSTreeNode
{
K _key;
BSTreeNode<K>* _left;
BSTreeNode<K>* _right;
BSTreeNode(const K& key)
:_key(key)
,_left(nullptr)
,_right(nullptr)
{}
};
利用二叉树结构模拟实现二叉搜索树
template<class K>
class BSTree
{
typedef BSTreeNode<K> Node;
private:
Node* _root = nullptr;
};
1. 二叉搜索树的插入
插入时要先判断_root是不是空节点
其次插入一个数(key)时要判断头结点与key的大小,大则往右树走,小则左树。再依次遍历判断。
最后判断要在父节点的哪个位置
bool Insert(const K& key)
{
if (_root == nullptr)
{
_root = new Node(key);
return true;
}
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
else {
return true;
}
}
cur = new Node(key);
if (parent->_key < key)
{
parent->_right = cur;
}
else {
parent->_left = cur;
}
return true;
}
2. 搜索二叉树
本质上就是遍历
bool Find(const K& key)
{
Node* cur = _root;
while (cur)
{
if (cur->_key < key)
{
cur = cur->_right;
}
else if (cur->_key > key)
{
cur = cur->_left;
}
else {
return true;
}
}
return false;
}
3. 中序遍历
中序遍历就是左子树,根,右子树。
采用递归思想。
void _InOrder(Node* root)
{
if (root == nullptr)
{
return;
}
_InOrder(root->_left);
cout << root->_key << " ";
_InOrder(root->_right);
}
当我们在main函数使用中序遍历时,发现传递不了root根节点。这时,我们把_InOrder设置为私有函数。在公有函数中引用私有函数_InOrder
void InOrder()
{
_InOrder(_root);
cout << endl;
}
4. 删除二叉树
删除二叉树中的节点
如果删除叶子节点,直接删除
如果删除只有一个子节点的节点,则需要把他的父节点与他的子节点链接
如果删除有两个节点的节点,则需要找一个节点替代 左子树的最大 或 右子树最小
无子节点或者有一个子节点可以归为一类,我们只需把有一个子节点的节点或者没有节点的节点他们的孩子,交给节点的父节点来看管就行
if (cur->_left == nullptr)
{
if (parent == nullptr)
{
_root = cur->_right;
}
else {
if (parent->_left == cur)
{
parent->_left = cur->_right;
}
else {
parent->_right = cur->_right;
}
}
delete cur;
return true;
}
else if (cur->_right == nullptr)
{
if (parent == nullptr)
{
_root = cur->_left;
}
else {
if (parent->_left == cur)
{
parent->_left = cur->_left;
}
else {
parent->_right = cur->_left;
}
}
delete cur;
return true;
}
删除两个子节点的节点
首先要找到右子树最小的节点与要删除的节点进行交换。(右子树最小节点的内容覆盖掉要删除的节点的内容,然后把右子树最小节点删除掉)
这里我们定义一个cur就是要删除的节点,和rightMin右子树最小的节点,和rightMinP右子树最小的节点的父节点。
这里的大体思路就是找到rightMin并交换cur中的内容,rightMinP为rightMin的父节点,再让rightMinP指向rightMin的右孩子。
这里rightMinP有两种情况,如左图不是cur本身,还有右图是cur本身
Node* rightMinP = cur;
Node* rightMin = cur->_right;
while (rightMin->_left)
{
rightMinP = rightMin;
rightMin = rightMin->_left;
}
cur->_key = rightMin->_key;
if (rightMinP->_left == rightMin)
rightMinP->_left = rightMin->_right;
else
rightMinP->_right = rightMin->_right;
delete rightMin;
return true;
删除二叉树总代码实现
bool Erase(int key)
{
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
else
{
// 删除
//0-1个孩子的情况
if (cur->_left == nullptr)
{
if (parent == nullptr)
{
_root = cur->_right;
}
else {
if (parent->_left == cur)
{
parent->_left = cur->_right;
}
else {
parent->_right = cur->_right;
}
}
delete cur;
return true;
}
else if (cur->_right == nullptr)
{
if (parent == nullptr)
{
_root = cur->_left;
}
else {
if (parent->_left == cur)
{
parent->_left = cur->_left;
}
else {
parent->_right = cur->_left;
}
}
delete cur;
return true;
}
else {
// 2 个孩子
// 右子树的最大节点作为替代节点
Node* rightMinP = cur;
Node* rightMin = cur->_right;
while (rightMin->_left)
{
rightMinP = rightMin;
rightMin = rightMin->_left;
}
cur->_key = rightMin->_key;
if (rightMinP->_left == rightMin)
rightMinP->_left = rightMin->_right;
else
rightMinP->_right = rightMin->_right;
delete rightMin;
return true;
}
}
}
return false;
}