一、 概念
1. 性质
二叉搜索树是数据结构中树形结构一种。它或者是一棵空树,或者具有以下特性:
a.若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
b.若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
c.它的左、右子树也分别为二叉搜索树
下图1 为一个搜索二叉树:
![](https://img-blog.csdnimg.cn/20200714135859444.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDEzMjYyNw==,size_16,color_FFFFFF,t_70)
对这个二叉树进行中序遍历的结果为:0 1 2 3 4 5 6 7 8 9
2. 结构
二叉搜索树是能够高效地进行插入、删除、查询的数据结构,其时间复杂度为O(logn).
3. 操作
搜索二叉树需要的实现的操作有,查找、删除、插入、构造函数、拷贝构造函数、析构函数、赋值运算符重载。
二、 操作解析
1. 查找
根据搜索二叉树的特性,左子树节点的值<根节点的值<右子树节点的值即可实现二叉树的查找。其查找过程为:
若根节点不为空:
如果根节点val == 目标值 , 返回真;
如果根节点val < 目标值, 在其右子树找;
如果根节点val > 目标值,在其左子树找;
否则返回假
2. 插入
说明一下,搜索二叉树的插入位置都是度为0或1的节点。即使插入的数字距离度为0或1的距离远,经过调整之后任然插入的是度为0或1的节点。插入可有以下情况及其过程:
1> 树为空,则直接插入
2> 树不为空,首先进行二叉树的搜索操作,找到插入位置,以及插入位置的父亲节点。记录父亲节点的目的是为了方便判断插入左子树还是右子树。
3. 删除
首先查找要删除的数据是否在二叉树中,如果不存在,返回flase;否则删除的节点可能存在以下四种情况:
1> 要删除的节点为叶子节点。
找到节点进行删除即可。
2> 要删除的节点度为1,即只有孩子,或者只有有孩子。
以下图2 删除 8 节点为例:只是其中一例,有左孩子的情况在代码中进行实现。
![](https://img-blog.csdnimg.cn/20200714144223811.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDEzMjYyNw==,size_16,color_FFFFFF,t_70)
3> 要删除的节点度为2,即左右孩子都存在。
以删除以下图3 中7为例:
![](https://img-blog.csdnimg.cn/20200714145418676.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDEzMjYyNw==,size_16,color_FFFFFF,t_70)
其删除的过程为:
a. 找到次删除节点的子树中左子树的最右节点或者右子树的最左节点
b. 将删除的节点值替换成 a 中找到的节点值
c. 删除 a 中找到的节点。(该步即为删除度为1或0的节点)
具体的是实现在以下代码中进行展示。
4. 构造函数、 析构函数、赋值运算符重载的操作和普通类的显示函数的操作类似,在下面代码中进行展示。
三、 C++代码实现
#include <iostream>
using namespace std;
// 创建一个节点
template <class T>
struct BSTNode {
T _val;
BSTNode<T>* _left;
BSTNode<T>* _right;
// 构造
BSTNode (const T& val = T())
:_val(val)
,_left(nullptr)
,_right(nullptr)
{}
};
template <class T>
class BSTree {
public:
typedef BSTNode<T> Node;
// 查找函数
Node* find(const T& val) {
Node* cur = _root;
while (cur) {
if (cur->_val == val) {
return cur;
}
if (cur->_val < val) {
cur = cur->_right;
}
if (cur->_val > val) {
cur = cur->_left;
}
}
return nullptr; // 未找到,返回空
}
// 插入。 只能插入到度为 0 或 1 的节点中,只有插入是否成功,所有用 bool 数据类型.
bool insert(const T& val) {
// 判断是否为空树, 是空树,则用 val 创建(node)一个树
if (_root == nullptr) {
_root = new Node(val);
}
Node* cur = _root;
Node* parent = nullptr;
// 先查找
while (cur) {
parent = cur;
if (cur->_val == val) {
return false;
}
else if (cur->_val < val) {
cur = cur->_right;
}
else {
cur = cur->_left;
}
}
// while 走完了,没有 val 节点,就创建一个吧
cur = new Node(val);
// 判断 cur 应该放在 parent 的那一边
if (parent->_val < val) {
parent->_right = cur;
}
else {
parent->_left = cur;
}
return true;
}
bool erase(const T& val) {
// 删除
// 四种情况 叶子节点 左孩子为空 右孩子为空 度为 2 的节点
//1. 删除叶子节点
// 先查找
Node* cur = _root;
Node* parent = nullptr;
while (cur) {
if (cur->_val == val) {
break;
}
if (cur->_val < val) {
parent = cur;
cur = cur->_right;
}
if (cur->_val > val) {
parent = cur;
cur = cur->_left;
}
}
if (cur == nullptr) { // 没找到要删除的节点
return false;
}
// 如果 cur 是叶子节点
if (cur->_left == nullptr && cur->_left == nullptr) {
if (cur == _root) {
_root = nullptr;
}
else {
if (parent->_right == cur) {
parent->_right = nullptr;
}
else {
parent->_left = nullptr;
}
}
delete cur; // 释放 cur, 以防内存泄漏
}
// 左孩子为空
else if (cur->_left == nullptr) {
if (cur == _root) {
_root = cur->_right;
}
else {
if (parent->_right == cur) {
parent->_right = cur->_right;
}
else if (parent->_left == cur) {
parent->_left = cur->_right;
}
}
delete cur;
}
// 右孩子为空
else if (cur->_right == nullptr) {
if (cur == _root) {
_root = cur->_left;
}
else {
if (parent->_left == cur) {
parent->_left = cur->_left;
}
else {
parent->_right = cur->_left;
}
}
delete cur;
}
// 左右孩子都存在
else {
Node* leftMostChid = cur->_right;
Node* parent = cur;
while (leftMostChid->_left) {
parent = leftMostChid;
leftMostChid = leftMostChid->_left;
}
cur->_val = leftMostChid->_val;
// 判断最左值在父亲的左边还是右边, 有nullptr那就接nullptr
if (parent->_left = cur) {
parent->_left = leftMostChid->_right;
}
else {
parent->_right = leftMostChid->_right;
}
delete leftMostChid;
}
return true;
}
// 中序遍历显示结果
void inorder() {
_inorder(_root);
cout << endl;
}
void _inorder(Node* root) {
if (root) {
_inorder(root->_left);
cout << root->_val << ' ';
_inorder(root->_right);
}
}
// 默认构造
BSTree() :_root(nullptr) {};
// 拷贝构造
Node* copyTree(Node* root) {
if (root) {
Node* cur = new Node(root->_val);
cur->_left = copyTree(root->_left);
cur->_right = copyTree(root->_right);
return cur;
}
return nullptr;
}
BSTree(const BSTree<T>& bst):_root(nullptr){
_root = copyTree(bst._root);
}
//析构函数
void destory(Node* root) {
if (root) {
destory(root->_left);
destory(root->_right);
delete root;
}
}
~BSTree() {
destory(_root);
}
// 赋值运算符
BSTree<T>& operator=(BSTree<T> bst)
{
swap(_root, bst._root);
return *this;
}
private:
Node* _root = nullptr;
};
void test() {
BSTree <int> bst;
bst.insert(0);
bst.insert(5);
bst.insert(6);
bst.insert(3);
bst.insert(7);
bst.insert(1);
bst.inorder();
// 拷贝
BSTree<int> p(bst);
p.inorder();
// 删除
bst.erase(3);
bst.inorder();
bst.erase(6);
bst.inorder();
}
int main() {
test();
system("pause");
return 0;
}
其运行结果如下:
0 1 3 5 6 7
0 1 3 5 6 7
0 1 5 6 7
0 1 5
请按任意键继续. . .
结果证明所述功能已经实现。
代码只是实现本文的所述功能,当然还有更优化的代码;如果该代码有错误,也请同志们多多指教,多多交流