- 二叉搜索树
- 特点
- 操作
- 代码实现
1. 二叉搜索树
二叉搜索树又称二叉排序树/二叉查找树,它或者是一棵空树,或者是具有以下性质的二叉树:
- 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值。
- 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值。
- 它的左右子树也分别为二叉搜索树
2. 特点
二叉搜索树的中序遍历有序。
3 . 操作
3.1 查找数据:
判断要查找的数据是否等于当前节点的值(初始值:cur=_root)
i. 若等于,返回当前节点;
ii. 若小于,向左子树进行查找;(cur=cur->_left)
iii. 若大于,向右子树进行查找。(cur=cur->_right)
iv. 若未找到该数据,则返回nullptr
3.2 插入数据
在二叉树的叶子节点插入新数据(不能插入重复的数据,若插入重复数据,则直接返回错误)。
i. 判断树是否为空,若为空,创建新节点,让根节点指向创建的该节点,返回true;
ii. 若树不为空。
- 找合适的位置(并保留其父节点指针,用于插入);
- 若该元素已存在,则返回false;
- 否则,创建新节点;
- 插入到其父节点的左孩子或者右孩子(判断该节点的值和父节点值的大小,以确定其为父节点的左孩子/右孩子)。
3.3 删除数据
i. 找位置(并找其父节点)
ii. 找到节点,删除。
- 叶子节点(找父节点,直接删除,父节点对应位置(右/左)置空。)
1) 要删节点为根节点,删除根节点,将根节点置空;
2) 判断待删除节点为其父节点的左孩子还是右孩子 - 单边节点(找父节点,父节点对应位置连接其左子树/右子树,删除查找的节点)
1) 若要删节点为根节点,将根节点重新指向要删除节点的左子树/右子树,删除待删除节点;
2) 否则,判断待删除节点为其父节点的左孩子还是右孩子,将其父节点对应位置与其左子树/右子树相连接,删除待删除节点。
3)返回true - 既有左子树,也有右子树(找左子树的最右节点,与要删除节点进行数值交换,转换为删除叶子结点)
1) 找左子树最右节点,并记录其父节点
2) 交换(待删除节点和左子树最右节点的值)
3) 连接父节点,删除交换后的左子树最右节点。
3.4 中序遍历
核心步骤思路:
- 递归访问左子树;
- 访问节点内容;
- 递归访问右子树。
4. C++代码实现
#pragma once
#include<iostream>
using namespace std;
template<class K,class V>
struct BNode {
K _key;
V _value;
BNode* _left;
BNode* _right;
BNode(const K& key,const V& value) :_key(key),_value(value), _left(nullptr), _right(nullptr) {}
};
template<class K,class V>
class BTree {
public:
typedef BNode<K,V> Node;
Node* find(const K& key)
{
Node* cur = _root;
while (cur)
{
if (cur->_key == key)
return cur;
else if (cur->_key > key)
cur = cur->_left;
else
cur = cur->right;
}
return nullptr;
}
BTree()
:_root(nullptr)
{}
//拷贝二叉树的数据和结构
Node* copy(Node* root) //创建自上而下,连接自下而上
{
if (root == nullptr)
return nullptr;
Node* newNode = new Node(root->_key,root->_value);
newNode->_left = copy(root->_left);
newNode->_right = copy(root->_right);
return newNode;
}
BTree(const BTree<K,V>& btree)
:_root(copy(btree._root))
{}
//不插入重复的值,每次插入到叶子节点
bool insert(const K& key,const V& value)
{
if (_root == nullptr)
{
_root = new Node(key,value);
return true;
}
//搜索,找到一个合适的位置插入
Node* cur = _root;
Node* parent = nullptr;
while (cur)
{
parent = cur;
if (cur->_key == key)
return false;
else if (cur->_key > key)
cur = cur->_left;
else
cur = cur->_right;
}
//插入
cur = new Node(key,value);
if (parent->_key > key)
parent->_left = cur;
else
parent->_right = cur;
return true;
}
void inorder()
{
_inorder(_root);
}
void _inorder(Node* root)
{
if (root)
{
_inorder(root->_left);
cout <<" key:"<<root->_key << "——value:"<<root->_value<<'\t';
_inorder(root->_right);
}
cout << endl;
}
void destroy(Node* root)
{
if (root)
{
destroy(root->_left);
destroy(root->_right);
cout << "destroy: " << root->_key << "——" << root->_value << endl;
delete root;
}
}
~BTree()
{
if (_root)
{
destroy(_root);
_root = nullptr;
}
}
bool erase(const K& key)
{
//查找该节点
Node* cur = _root;
Node* parent = nullptr;
while (cur)
{
if (cur->_key == key)
break;
parent = cur;
if (cur->_key > key)
cur = cur->_left;
else
cur = cur->_right;
}
//判断需要删除的节点是否存在
if (cur == nullptr)
return false;
//删除
//1.要删除节点为叶子节点
if (cur->_left == nullptr && cur->_right == nullptr)
{
//判断要删除节点是否为根节点
if (cur == _root)
{
_root = nullptr;
}
//判断要删除的节点属于父节点的左孩子还是右孩子,并将对应的父节点子节点置为空
else
{
if (parent->_left == cur)
parent->_left = nullptr;
else
parent->_right = nullptr;
}
delete cur;
}
//2.非叶子节点,且当前节点有一个子树,另一个子树为空
//左子树为空,判断当前节点为父节点的左子树还是右子树(若为父节点的左子树,则将其右子树连接到父节点的左子树,若为父节点的右子树,则将其右子树连接到父节点的右子树)
else if (cur->_left == nullptr)
{
//判断当前节点是否为根节点
if (cur == _root)
_root = cur->_right; //根节点
else
{
if (parent->_left == cur)
parent->_left = cur->_right;
else
parent->_right = 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;
}
//3.要删除节点左右子树均不为空。当前位置的新的替代节点,一定为左子树的最大节点(左子树最右节点),或者为右子树的最小节点(右子树最左节点),。
//① 找左子树的最右节点(右子树的最左节点)
//② 交换待删除节点与左子树的最右节点(或者右子树的最左节点)的值
//③ 然后删除交换后的叶子结点
else
{
//①找左子树的最右节点
Node* leftMostRight = cur->_left;
parent = cur;
while (leftMostRight->_right)
{
parent = leftMostRight;
leftMostRight = leftMostRight->_right;
}
//②交换
swap(cur->_key, leftMostRight->_key);
swap(cur->_value, leftMostRight->_value);
//③连接,删除
if (parent->_left == leftMostRight)
parent->_left = leftMostRight->_left;
else
parent->_right = leftMostRight->_left;
delete leftMostRight;
}
}
private:
Node* _root;
};