目录
二叉搜索树是一种特殊的二叉树,也称作二叉查找树或者二叉排序树,相对于二叉树,二叉搜索树在查找,排序等方面性能优良,是一种常用的树形结构,这里我就总结一些关于二叉搜索树的知识要点。
1.二叉搜索树的特点
首先,二叉搜索树是一种二叉树,每一个节点最多有两个子节点,最少为零个节点。
其次,二叉搜索树有如下特点:
1.若它的左子树不为空,则左子树上所有节点的值都小于根节点的值2.若它的右子树不为空,则右子树上所有节点的值都大于根节点的值3.它的左右子树也分别为二叉搜索树
也正是由于搜索二叉树这些特点,才会让二叉搜索树在排序,查找等方面的功能相对于普通二叉树更加优良。
2.二叉搜索树的应用
二叉搜索树应用主要有两种模型:
1.key模型:
K模型即只有key作为关键码,结构中只需要存储Key即可,关键码即为需要搜索到
的值
。
2.key -value模型:
每一个关键码
key
,都有与之对应的值
Value
,即
<Key, Value>
的键值对
。
在日常生活中,key-value模型的应用更加广泛,如学生管理系统,我们可以使用学生学号为key,其他信息为value构建二叉搜索树,当我们进行操作只要操作学号对应的学号就可以进行学生信息的管理。
在这个例子里,我们可以发现,key是必须独一无二的,就比如学生的学号是独一无二的,但是value可以是相同的,比如学生姓名等信息是可以相同的。
下面我就以KV模型为例子,介绍二叉搜索树的各部分功能实现的要点。
3.KV模型的实现
1.结点结构
在key- value的模型中,我们可以使用pair模板作为结点的类型,但是这里我主要介绍用结构体作为结点结构的方法:
template<class K,class V>
struct BSTNode
{
//构造函数
BSTNode(const K& key = K(), const V& value = V())
: _pLeft(nullptr), _pRight(nullptr), _key(key),_value(value)
{}
//左孩子结点
BSTNode<K,V>* _pLeft;
//右孩子结点
BSTNode<K,V>* _pRight;
//key值
K _key;
//value值
V _value;
};
2.插入结点
插入结点只要两步操作:1.寻找位置 2.插入结点
需要注意的是,我们必须把寻找的位置的父结点也找到,操作如下
bool Insert(const K& key, const V& value)
{
//若为空树
if (!_root)
{
_root = new Node(key, value);
return true;
}
Node* Cur = _root;
Node* Parent = nullptr;
//寻找位置和其父节点
while (Cur)
{
Parent = Cur;
if (Cur->_key > key)
{
Cur = Cur->_pLeft;
}
else if (Cur->_key < key)
{
Cur = Cur->_pRight;
}
else
{
return false;
}
}
Cur = new Node(key, value);
//判断插入是左边还是右边
if (Parent->_key > key)
{
Parent->_pLeft = Cur;
}
else
{
Parent->_pRight = Cur;
}
return true;
}
3.查找结点
查找结点是二叉搜索树中简单的一种查找,和插入操作大差不差
Node* Find(const K& key)
{
if (!_root)
{
return nullptr;
}
Node* ret = _root;
while (ret)
{
if (ret->_key > key)
{
ret = ret->_pLeft;
}
else if (ret->_key < key)
{
ret = ret->_pRight;
}
else
{
return ret;
}
}
return nullptr;
}
4.删除结点
删除操作也需要找到删除结点以及其对应的父结点,在进行删除操作
bool Erase(const K& key)
{
//先找到目标节点Cur和其父节点Parent
//若树为空
if (!_root)
{
return false;
}
Node* Cur = _root;
Node* Parent = nullptr;
while (Cur)
{
if (Cur->_key > key)
{
Parent = Cur;
Cur = Cur->_pLeft;
}
else if (Cur->_key < key)
{
Parent = Cur;
Cur = Cur->_pRight;
}
else
{
break;
}
if (nullptr == Cur)//若找不到
{
return false;
}
//进行删除操作
//1.若删除节点无左右子树
if (nullptr == Cur->_pLeft && nullptr == Cur->_pRight)
{
delete Cur;
}
//2.删除节点只有右树
else if (nullptr == Cur->_pLeft)
{
if (Cur == _root)
{
_root = Cur->_pRight;
}
if (Parent->_pLeft == Cur)
{
Parent->_pLeft = Cur->_pRight;
}
else
{
Parent->_pRight = Cur->_pRight;
}
}
//3.删除节点只有左树
else if (nullptr == Cur->_pRight)
{
if (Cur == _root)
{
_root = Cur->_pLeft;
}
if (Parent->_pLeft == Cur)
{
Parent->_pLeft = Cur->_pLeft;
}
else
{
Parent->_pRight = Cur->_pLeft;
}
}
//4.删除节点左右树都有
else
{
//使用替代法,先找到删除节点的左树的最右节点
Node* tmp = Cur->_pLeft;
while (tmp->_pRight)
{
tmp = tmp->_pRight;
}
//将找到的节点值覆盖Cur,删除找到节点,就相当于删除了Cur
Cur->_key = tmp->_key;
Cur->_value = tmp->_value;
delete tmp;
}
}
}
5.中序遍历
对于二叉搜索树来说,中序遍历的结构就是升序排列,中序遍历分为递归和非递归两种方法,以下是具体操作:
void _InOrder(Node* root)
{
if (!root)
{
return;
}
//递归中序就是先左边在中间最后右边
_InOrder(root->_pLeft);
cout << root->_key << " " << root->_value << endl;
_InOrder(root->_pRight);
}
//非递归中序遍历
//规则:左路遍历入栈直到nullptr进行出栈,出栈结点的右结点入栈
void InOrder()
{
stack<Node*> sta;
Node* cur = _root;
while (!sta.empty() || cur)
{
if (nullptr != cur)
{
sta.push(cur);
cur = cur->_pLeft;
}
else
{
cur = sta.top();
sta.pop();
cout << cur->_key << " " << cur->_value << endl;
cur = cur->_pRight;
}
}
}
4.完整代码
下面是二叉搜索树常规操作的代码
#pragma once
#include<string>
#include<iostream>
#include<stack>
using std::string;
using std::cout;
using std::cin;
using std::endl;
using std::stack;
template<class K,class V>
struct BSTNode
{
BSTNode(const K& key = K(), const V& value = V())
: _pLeft(nullptr), _pRight(nullptr), _key(key),_value(value)
{}
BSTNode<K,V>* _pLeft;
BSTNode<K,V>* _pRight;
K _key;
V _value;
};
template<class K, class V>
class BSTree
{
typedef BSTNode<K, V> Node;
public:
BSTree()
:_root(nullptr)
{}
//插入节点
bool Insert(const K& key, const V& value)
{
if (!_root)
{
_root = new Node(key, value);
return true;
}
Node* Cur = _root;
Node* Parent = nullptr;
while (Cur)
{
Parent = Cur;
if (Cur->_key > key)
{
Cur = Cur->_pLeft;
}
else if (Cur->_key < key)
{
Cur = Cur->_pRight;
}
else
{
return false;
}
}
Cur = new Node(key, value);
if (Parent->_key > key)
{
Parent->_pLeft = Cur;
}
else
{
Parent->_pRight = Cur;
}
return true;
}
Node* Find(const K& key)
{
if (!_root)
{
return nullptr;
}
Node* ret = _root;
while (ret)
{
if (ret->_key > key)
{
ret = ret->_pLeft;
}
else if (ret->_key < key)
{
ret = ret->_pRight;
}
else
{
return ret;
}
}
return nullptr;
}
bool Erase(const K& key)
{
//先找到目标节点Cur和其父节点Parent
//若树为空
if (!_root)
{
return false;
}
Node* Cur = _root;
Node* Parent = nullptr;
while (Cur)
{
if (Cur->_key > key)
{
Parent = Cur;
Cur = Cur->_pLeft;
}
else if (Cur->_key < key)
{
Parent = Cur;
Cur = Cur->_pRight;
}
else
{
break;
}
if (nullptr == Cur)//若找不到
{
return false;
}
//进行删除操作
//1.若删除节点无左右子树
if (nullptr == Cur->_pLeft && nullptr == Cur->_pRight)
{
delete Cur;
}
//2.删除节点只有右树
else if (nullptr == Cur->_pLeft)
{
if (Cur == _root)
{
_root = Cur->_pRight;
}
if (Parent->_pLeft == Cur)
{
Parent->_pLeft = Cur->_pRight;
}
else
{
Parent->_pRight = Cur->_pRight;
}
}
//3.删除节点只有左树
else if (nullptr == Cur->_pRight)
{
if (Cur == _root)
{
_root = Cur->_pLeft;
}
if (Parent->_pLeft == Cur)
{
Parent->_pLeft = Cur->_pLeft;
}
else
{
Parent->_pRight = Cur->_pLeft;
}
}
//4.删除节点左右树都有
else
{
//使用替代法,先找到删除节点的左树的最右节点
Node* tmp = Cur->_pLeft;
while (tmp->_pRight)
{
tmp = tmp->_pRight;
}
//将找到的节点值覆盖Cur,删除找到节点,就相当于删除了Cur
Cur->_key = tmp->_key;
Cur->_value = tmp->_value;
delete tmp;
}
}
}
//递归中序遍历
void _InOrder(Node* root)
{
if (!root)
{
return;
}
_InOrder(root->_pLeft);
cout << root->_key << " " << root->_value << endl;
_InOrder(root->_pRight);
}
//非递归中序遍历
void InOrder()
{
stack<Node*> sta;
Node* cur = _root;
while (!sta.empty() || cur)
{
if (nullptr != cur)
{
sta.push(cur);
cur = cur->_pLeft;
}
else
{
cur = sta.top();
sta.pop();
cout << cur->_key << " " << cur->_value << endl;
cur = cur->_pRight;
}
}
}
private:
Node* _root = nullptr;
};