二叉搜索树
二叉搜索树的概念
二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:
若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
它的左右子树也分别为二****叉搜索树
eg.
搜索二叉树的中序遍历是有序的
二叉搜索树可以用来进行查找,排序,去重
由于二叉搜索树本身节点的性质,默认情况下的二叉搜索树不允许进行节点的值的修改
二叉搜索树的插入
二叉搜索树由于本身的性质,可以用于排序或搜索
当二叉搜索树进行搜索时,可以不再进行暴力搜索,而是根据节点大小进行搜索,最多进行树的高度次即可成功搜索
由于二叉搜索树本身未必是完全二叉树,可能存在极端情况,因此他的增删查改的时间复杂度最坏情况为O(N)
但是,由于其本身的性质,一般的二叉搜索树是不允许节点冗余的,即不允许同一棵树存在相同大小的节点
cpp
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 // 如果存在相同大小的节点就返回false,插入失败
{
return false;
}
}
//通过与父节点比较大小,将节点插入适当位置
cur = new Node(key);
if (parent->_key < key)
{
parent->_right = cur;
}
else
{
parent->_left = cur;
}
return true;
}
二叉搜索树的删除
二叉搜索树的删除分为多种情况
1.没有子节点的节点
直接删除该节点,并修改父节点的对应指针
2.只有一个子节点的节点
删除该节点后让其子节点代替该节点的位置
前两种情况可以总结为一种代码
eg:一个没有子节点的节点的左子树一定为空,若让他指向右子树,右子树也为空,则效果和直接指向空一致,而第二种情况,则可以直接根据此逻辑进行编写
如果当前需要删除的节点左为空,则令该节点的父节点指向该节点的右节点
cpp
if (cur->_left == nullptr)
{
if (parent->_left == cur)
{
parent->_left = cur->_right;
}
else
{
parent->_right = cur->_right;
}
delete cur;
return true;
}
else if (cur->_right == nullptr)
{
if (parent->_left == cur)
{
parent->_left = cur->_left;
}
else
{
parent->_right = cur->_left;
}
delete cur;
return true;
}
3.有两个子节点的节点
若要删除拥有两个子节点的节点
需要找到一个节点,该节点要求满足比原节点的左子树大,比原节点的右子树小
可以选择的节点是 左子树的最大节点 或 右子树的最小节点
左子树的最大节点一定 大于等于 原节点的左子节点(左节点的最右节点)
又一定比当前节点的右子节点小(大的节点都在右子树)
右子树的最小节点同理
cpp
// 删除多个子节点的节点
else
{
// 可以找右子树的最小节点或左子树的最大节点(右子树最左侧的节点或左子树最右的节点)
Node* rightMinP = cur; // 右子树最小节点的父节点
Node* rightMin = cur->_right;
while (rightMin->_left)
{
rightMinP = rightMin;
rightMin = rightMin->_left;
}
// 用最左节点给该节点赋值
cur->_key = rightMin->_key;
// 随后删除最左节点(相当于两个节点进行了交换)
// 判断需要被删除的节点是P的左节点还是右节点(由于左可能为空,所以最左节点未必是左节点)
if (rightMinP->_left == rightMin)
{
rightMinP->_left = rightMin->_right;
}
else
{
rightMinP->_right = rightMin->_right;
}
}
源代码
cpp
#pragma once
#include <iostream>
using namespace std;
template<class K>
struct BSTNode // 二叉树节点
{
K _key;
BSTNode<K>* _left;
BSTNode<K>* _right;
BSTNode (const K& key) // 构造函数
:_key(key)
,_left(nullptr)
,_right(nullptr)
{ }
};
template<class K>
class BSTree
{
typedef BSTNode<K> Node;
public:
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 // 如果存在相同大小的节点就返回false,插入失败
{
return false;
}
}
//通过与父节点比较大小,将节点插入适当位置
cur = new Node(key);
if (parent->_key < key)
{
parent->_right = cur;
}
else
{
parent->_left = cur;
}
return true;
}
bool Search(const K& key) // 搜索节点
{
Node* cur = _root;
while (cur) // 当节点没有走完
{
if (cur->_key < key) // key比当前节点大,则向右走找更大的节点
{
cur = cur->_right;
}
else if (cur->_key > key) // 后续同理
{
cur = cur->_left;
}
else // 直到找到为止
{
return true;
}
}
return false; // 找不到就返回false
}
void InOrder() // 遍历子函数
{
_InOrder(_root);
}
bool Erase(const K& key) // 删除节点
{
Node* parent = nullptr;
// 先找到需要删除的节点
Node* cur = _root;
while (cur) // 当节点没有走完
{
if (cur->_key < 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
{
// 可以找右子树的最小节点或左子树的最大节点(右子树最左侧的节点或左子树最右的节点)
Node* rightMinP = cur; // 右子树最小节点的父节点
Node* rightMin = cur->_right;
while (rightMin->_left)
{
rightMinP = rightMin;
rightMin = rightMin->_left;
}
// 用最左节点给该节点赋值
cur->_key = rightMin->_key;
// 随后删除最左节点(相当于两个节点进行了交换)
// 判断需要被删除的节点是P的左节点还是右节点(由于左可能为空,所以最左节点未必是左节点)
if (rightMinP->_left == rightMin)
{
rightMinP->_left = rightMin->_right;
}
else
{
rightMinP->_right = rightMin->_right;
}
}
}
}
// 没找到
return false;
}
private:
Node* _root = nullptr;
void _InOrder(Node* root) // 遍历
{
if (root == nullptr)
{
return;
}
_InOrder(root->_left);
cout << root->_key << " ";
_InOrder(root->_right);
}
};