BST树(二叉查找树)
二叉查找树(Binary Search Tree),又被称为二叉搜索树
二叉查找树演示连接
1. 特点
- 任意节点的左子树不空, 则左子树上所有节点的key均小于它的根节点的key
- 任意节点的右子树不空, 则右子树上所有节点的key均大于它的根节点的key
- 任意节点的左,右子树也分别为二叉查找树
- 没有key相等的节点
2. 操作
2.1 查找
用递归的方式查找,判断是否有对应数
// 查找
bool Search(char val){
return Search(m_root,val);
}
bool Search(Node* root,char val){
if(nullptr == root) return false; // 根节点为空,表示没找到
if(root->val == val) return true; // 根节点的值等于要找的值
if(root->val < val){
return Search(root->right,val); // 根节点的值小于要找的值,往右边找
}else{
return Search(root->left,val); // 根节点的值大于要找的值,往左边找
}
}
2.2 添加
用递归的方式对数做判断,插入左子树或右子树
// 插入
void Insert(char val){
if(Search(val)) return; // 如果找到了要插入的值,则不用插入
m_root = Insert(m_root,val); // 如果没找到,插入到根节点中
}
Node* Insert(Node* root,char val){
if(nullptr == root) return new Node(val); // 根节点为空,创建一个类空间,值为val
if(root->val > val){
root->left = Insert(root->left,val); // 如果根节点大于要插的数,插到左子树
}else{
root->right = Insert(root->right,val); // 如果根节点小于要插的数,插到右子树
}
return root; // 返回当前节点
}
对一个BST二叉搜索数进行依次添加
#include <iostream>
using namespace std;
class Node{
public:
char val;
Node* left;
Node* right;
Node(int val):val(val),left(nullptr),right(nullptr){}
Node(int val,Node* left,Node* right):val(val),left(left),right(right){}
// void SetRight(Node* right){this->right = right;}
// void SetLeft(Node* left){this->left = left;}
};
// 查找、添加、删除
class BSTree{
Node* m_root = nullptr; // 成员变量,初始化
public:
// 查找
bool Search(char val){
return Search(m_root,val);
}
bool Search(Node* root,char val){
if(nullptr == root) return false; // 根节点为空,表示没找到
if(root->val == val) return true; // 根节点的值等于要找的值
if(root->val < val){
return Search(root->right,val); // 根节点的值小于要找的值,往右边找
}else{
return Search(root->left,val); // 根节点的值大于要找的值,往左边找
}
}
// 插入
void Insert(char val){
if(Search(val)) return; // 如果找到了要插入的值,则不用插入
m_root = Insert(m_root,val); // 如果没找到,插入到根节点中
}
Node* Insert(Node* root,char val){
if(nullptr == root) return new Node(val); // 根节点为空,创建一个类空间,值为val
if(root->val > val){
root->left = Insert(root->left,val); // 如果根节点大于要插的数,插到左子树
}else{
root->right = Insert(root->right,val); // 如果根节点小于要插的数,插到右子树
}
return root; // 返回当前节点
}
// 外部调用
void Print(){
preorder(m_root);
}
// 做前序遍历进行打印
void preorder(const Node* root){
if(nullptr == root) return;
if(root->left) cout << root->val << "--" << root->left->val << endl;
if(root->right) cout << root->val << "--" << root->right->val << endl;
preorder(root->left);
preorder(root->right);
}
};
int main(){
BSTree bst;
bst.Insert('G');
bst.Insert('T');
bst.Insert('E');
bst.Insert('F');
bst.Insert('B');
bst.Insert('X');
bst.Print();
}
结果为:
G--E
G--T
E--B
E--F
T--X
上面结果可以得到对应数形图:
2.3 删除
替代删除,后继代替删除节点,然后删除后继;或者前驱代替删除节点,然后删除前驱
例如:
删除节点“15”,即先找到该节点的右子树的最小值,即为“20”,把“15”替换成“20”,并删除原来的“20”,删除后经过递归返回“15”的右节点“30”
对上述程序继续进行删除节点的操作
#include <iostream>
using namespace std;
class Node{
public:
char val;
Node* left;
Node* right;
Node(int val):val(val),left(nullptr),right(nullptr){}
Node(int val,Node* left,Node* right):val(val),left(left),right(right){}
};
// 查找、添加、删除
class BSTree{
Node* m_root = nullptr; // 成员变量,初始化
public:
// 查找
bool Search(char val){
return Search(m_root,val);
}
bool Search(Node* root,char val){
if(nullptr == root) return false; // 根节点为空,表示没找到
if(root->val == val) return true; // 根节点的值等于要找的值
if(root->val < val){
return Search(root->right,val); // 根节点的值小于要找的值,往右边找
}else{
return Search(root->left,val); // 根节点的值大于要找的值,往左边找
}
}
// 插入
void Insert(char val){
if(Search(val)) return; // 如果找到了要插入的值,则不用插入
m_root = Insert(m_root,val); // 如果没找到,插入到根节点中
}
Node* Insert(Node* root,char val){
if(nullptr == root) return new Node(val); // 根节点为空,创建一个类空间,值为val
if(root->val > val){
root->left = Insert(root->left,val); // 如果根节点大于要插的数,去左子树插
}else{
root->right = Insert(root->right,val); // 如果根节点小于要插的数,去右子树插
}
return root; // 返回当前节点
}
// 删除
void Remove(char val){
m_root = Remove(m_root,val);
}
Node* Remove(Node* root,char val){
if(nullptr == root) return nullptr; // 如果没找到要删除的值,则返回空
if(root->val < val){
root->right = Remove(root->right,val); // 如果节点数小于要删的数,去右子树删
}else if(root->val > val){
root->left = Remove(root->left,val); // 如果节点数大于要删的数,去左子树删
}else{ // 如果找到了要删除的节点,分四种情况
if(nullptr == root->right && nullptr == root->left){ // 左右节点都为空,即是叶子节点
delete root; // 释放该节点
return nullptr; // 返回空
}
if(nullptr == root->right){ // 右子树为空,即只有左子树
Node* left = root->left; // 保存左节点
delete root; // 释放该节点
return left; // 返回左节点
}
if(nullptr == root->left){ // 左字数为空,即只有右字数
Node* right = root->right; // 保存右节点
delete root; // 释放该节点
return right; // 返回右节点
}
// 当左右子树都存在
int minval = Minimun(root->right); // 找到右子树的最小值
root->val = minval; // 把当前节点的值替换成该最小值
root->right = Remove(root->right,minval); // 再去删除那个最小值的节点,层层递归后最终返回右节点
}
return root;
}
// 找根对应当前树的最小值
int Minimun(Node* root){
if(nullptr == root) throw runtime_error("root is null"); // 没有根,抛异常
while(nullptr != root->left) root = root->left; // 一直往左找,一直到左子树为空,返回当前节点值
return root->val;
}
// 外部调用
void Print(){
preorder(m_root);
}
// 做前序遍历
void preorder(const Node* root){
if(nullptr == root) return;
if(root->left) cout << root->val << "--" << root->left->val << endl;
if(root->right) cout << root->val << "--" << root->right->val << endl;
preorder(root->left);
preorder(root->right);
}
};
int main(){
BSTree bst;
bst.Insert('G');
bst.Insert('T');
bst.Insert('E');
bst.Insert('F');
bst.Insert('B');
bst.Insert('X');
bst.Remove('E');
bst.Print();
}
结果为:
G--F
G--T
F--B
T--X
上面结果可以得到对应数形图:
我们将两张图对比,可以清楚的发现删除过程为:在E的右子树下找到最小的值即F,把E替换为F,并将之前的F删掉
2.4 优化
让类型变换更灵活,从终端输入
#include <iostream>
using namespace std;
template <typename T>
class _Node{
public:
T val;
_Node* left;
_Node* right;
_Node(const T& val):val(val),left(nullptr),right(nullptr){}
_Node(const T& val,_Node* left,_Node* right):val(val),left(left),right(right){}
};
template <typename T>
// 查找、添加、删除
class BSTree{
typedef _Node<T> Node; // 统一替换
Node* m_root = nullptr; // 成员变量,初始化
public:
// 查找
bool Search(const T& val){
return Search(m_root,val);
}
bool Search(Node* root,const T& val){
if(nullptr == root) return false; // 根节点为空,表示没找到
if(root->val == val) return true; // 根节点的值等于要找的值
if(root->val < val){
return Search(root->right,val); // 根节点的值小于要找的值,往右边找
}else{
return Search(root->left,val); // 根节点的值大于要找的值,往左边找
}
}
// 插入
void Insert(const T& val){
if(Search(val)) return; // 如果找到了要插入的值,则不用插入
m_root = Insert(m_root,val); // 如果没找到,插入到根节点中
}
Node* Insert(Node* root,const T& val){
if(nullptr == root) return new Node(val); // 根节点为空,创建一个类空间,值为val
if(root->val > val){
root->left = Insert(root->left,val); // 如果根节点大于要插的数,去左子树插
}else{
root->right = Insert(root->right,val); // 如果根节点小于要插的数,去右子树插
}
return root; // 返回当前节点
}
// 删除
void Remove(const T& val){
m_root = Remove(m_root,val);
}
Node* Remove(Node* root,const T& val){
if(nullptr == root) return nullptr; // 如果没找到要删除的值,则返回空
if(root->val < val){
root->right = Remove(root->right,val); // 如果节点数小于要删的数,去右子树删
}else if(root->val > val){
root->left = Remove(root->left,val); // 如果节点数大于要删的数,去左子树删
}else{ // 如果找到了要删除的节点,分四种情况
if(nullptr == root->right && nullptr == root->left){ // 左右节点都为空,即是叶子节点
delete root; // 释放该节点
return nullptr; // 返回空
}
if(nullptr == root->right){ // 右子树为空,即只有左子树
Node* left = root->left; // 保存左节点
delete root; // 释放该节点
return left; // 返回左节点
}
if(nullptr == root->left){ // 左字数为空,即只有右字数
Node* right = root->right; // 保存右节点
delete root; // 释放该节点
return right; // 返回右节点
}
// 当左右子树都存在
T minval = Minimun(root->right); // 找到右子树的最小值
root->val = minval; // 把当前节点的值替换成该最小值
root->right = Remove(root->right,minval); // 再去删除那个最小值的节点,返回右节点
}
return root;
}
// 找整个树的最小值
T Minimun(Node* root){
if(nullptr == root) throw runtime_error("root is null"); // 没有根,抛异常
while(nullptr != root->left) root = root->left; // 一直往左找,一直到左子树为空,返回当前节点值
return root->val;
}
// 外部调用
void Print(){
preorder(m_root);
}
// 做前序遍历
void preorder(const Node* root){
if(nullptr == root) return;
if(root->left) cout << root->val << "--" << root->left->val << endl;
if(root->right) cout << root->val << "--" << root->right->val << endl;
preorder(root->left);
preorder(root->right);
}
};
int main(){
BSTree<int> intbst;
int n;
while(cin >> n){
intbst.Insert(n);
}
intbst.Print();
}
结果为:
12 8 25 5 18 30 20
12--8
12--25
8--5
25--18
25--30
18--20
上面结果可以得到对应数形图: