目录
二叉搜索树概念
二叉搜索树的应用
二叉搜索树的实现(K模型)
构造函数
默认拷贝构造函数
赋值运算符重载函数
普通写法:
进阶写法:
析构函数
验证是否遵循搜索二叉树规则
插入函数(Insert)的实现
常规实现:
递归实现:
删除函数(Erase)的实现
常规实现:
递归实现:
查找函数(Find)的实现
常规实现:
递归实现:
二叉搜索树的性能分析
代码汇总
所谓二叉树(binary tree),其意义是:“任意节点最多只允许两个子节点”。这两个子节点称为左子节点和右子节点。
所谓二叉搜索树(binary search tree),可提供对数时间(logarithmic time)的元素插入和访问。
K模型:K模型即只有key作为关键码,结构中只需要存储Key即可,关键码即为需要搜索到
K模型节点结构:
template
struct BSTreeNode
{
BSTreeNode* _left;
BSTreeNode* _right;
K _key;
BSTreeNode(const K& key)
:_left(nullptr)
, _right(nullptr)
, _key(key)
{}
};
KV模型:每一个关键码key,都有与之对应的值Value,即
KV模型节点结构:
template
struct BSTreeNode
{
BSTreeNode* _left;
BSTreeNode* _right;
K _key;
V _value;
BSTreeNode(const K& key, const V& value)
:_left(nullptr)
, _right(nullptr)
, _key(key)
, _value(value)
{}
};
(KV模型同样思维)
template
struct BSTreeNode
{
BSTreeNode* _left;
BSTreeNode* _right;
K _key;
BSTreeNode(const K& key)
:_left(nullptr)
, _right(nullptr)
, _key(key)
{}
};
template
struct BSTree
{
typedef BSTreeNode Node; //便于后期使用
//…………(实现函数)
Node* _root = nullptr; //头节点
};
使用编译器自动生成的即可。但是,由于需要写拷贝构造函数,所以要强制编译器生成默认的构造。
// C++的用法:强制编译器生成默认的构造
BSTree() = default;
利用前序递归。赋值并按相同顺序链接。
BSTree(const BSTree& t)
{
_root = _Copy(t._root);
}
Node* _Copy(Node* root)
{
if (root == nullptr)
{
return nullptr;
}
Node* copyRoot = new Node(root->_key);
copyRoot->_left = _Copy(root->_left);
copyRoot->_right = _Copy(root->_right);
return copyRoot;
}
利用前序递归。赋值并按相同顺序链接。
const BSTree& operator=(const BSTree& t)
{
if (this != &t) //防止自己给自己赋值
{
_root = _Copy(t._root);
}
return *this;
}
Node* _Copy(Node* root)
{
if (root == nullptr)
{
return nullptr;
}
//利用前序递归
Node* copyRoot = new Node(root->_key);
copyRoot->_left = _Copy(root->_left);
copyRoot->_right = _Copy(root->_right);
return copyRoot;
}
利用临时变量t进行接收,通过t可以间接调用BSTree的拷贝构造函数完成拷贝构造出t。然后只需要将,拷贝构造出来的对象的二叉搜索树头节点与this对象的二叉搜索树头节点进行交换,而对象t会在该赋值运算符重载函数调用结束时自动析构。
// 利用t间接调用拷贝构造函数,后赋值给this->_root
BSTree& operator=(BSTree t)
{
swap(_root, t._root);
return *this;
}
对于析构函数,需要将每一个节点都进行释放。所以需要利用后序释放。从下面的节点开始进行释放空间。
//释放树中结点
void Destory(Node* root)
{
if (root == nullptr)
return;
// 释放左子树中的结点
Destory(root->_left);
// 释放右子树中的结点
Destory(root->_right);
delete root;
root = nullptr;
}
~BSTree()
{
//递归实现删除
Destory(_root);
_root = nullptr;
}
已知,搜索二叉树一定遵循左子树所有节点一定小于根节点,右子树所有节点一定大于根节点,所以可以利用中序的方式,打印二叉树,打印出来的一定为升序。
// this指针没法实现递归
void InOrder()
{
// 实现递归
_InOrder(_root);
cout << endl;
}
// 中序遍历
void _InOrder(Node* root)
{
if (root == nullptr)
return;
_InOrder(root->_left);
cout << root->_key << " ";
_InOrder(root->_right);
}
二叉搜索树的插入
若不是空树,插入结点的具体操作如下:
(因为二查搜索树一个数只能存储一次,所以循环到空即一定是可以插入的)
// 根据二叉搜索树的性质查找:找到值为key的节点在二叉搜索树中的位置
bool Insert(const K& key)
{
// 如果树为空,直接插入
if (_root == nullptr)
{
_root = new Node(key);
return true;
}
// 按照二叉搜索树的性质查找key在树中的插入位置cur
Node* cur = _root;
// 记录cur的双亲,因为新元素最终插入在cur双亲左右孩子的位置
Node* parent = nullptr;
while (cur)
{
if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
else if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
else // 元素已经在树中存在
return false;
}
// 插入元素
cur = new Node(key);
// 因为新元素最终插入在cur双亲左右孩子的位置,所以进行连接
if (parent->_key < key)
parent->_right = cur;
else
parent->_left = cur;
return true;
}
与常规实现,本质上没有区别,只不过是将循环寻找变为递归的形式。在右就递归右,在左就递归左。
// this指针没法实现递归
bool InsertR(const K& key)
{
// 实现递归
return _InsertR(_root, key);
}
bool _InsertR(Node*& root, const K& key)
{
//找到了插入的位置
if (root == nullptr)
{
root = new Node(key);
return true;
}
// 查找插入位置
if (root->_key > key)
_InsertR(root->_left, key);
else if (root->_key < key)
_InsertR(root->_right, key);
else // 该值已有
return false;
}
1、删除该结点且使被删除节点的双亲结点指向被删除节点的左孩子结点--直接删除。
正常情况:
特例情况 :
2、删除该结点且使被删除节点的双亲结点指向被删除结点的右孩子结点--直接删除。
正常情况:
特例情况 :
3、在它的右子树中寻找中序下的第一个结点(关键码最小),用它的值填补到被删除节点中,再来处理该结点的删除问题--替换法删除。
bool Erase(const K& key)
{
// 如果树为空,删除失败
if (nullptr == _root)
return false;
// 按照二叉搜索树的性质查找key在树中的位置cur
Node* cur = _root;
// 记录cur的双亲,因为删除cur后cur双亲连接cur的左右孩子
Node* parent = nullptr;
while (cur)
{
// 查找key在树中的位置
if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
else if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
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;
cur = nullptr;
}
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;
}
}
else
{
//找右子树的最小值节点,进行替换
Node* minParent = cur;
Node* min = cur->_right;
while (min->_left)
{
minParent = min;
min = min->_left;
}
swap(min->_key, cur->_key);
if (min == minParent->_left)
minParent->_left = min->_right;
else
minParent->_right = min->_left;
delete min;
}
return true;
}
}
return false;
}
// this指针没法实现递归
bool EraseR(const K& key)
{
// 实现递归
return _EraseR(_root, key);
}
bool _EraseR(Node*& root, const K& key)
{
if (root == nullptr)
return false;
// 查找key在树中的位置
if (root->_key > key)
return _EraseR(root->_left, key);
else if (root->_key < key)
return _EraseR(root->_right, key);
else
{
Node* del = root;
if (root->_left == nullptr)
root = root->_right;
else if (root->_right == nullptr)
root = root->_left;
else
{
//找右子树的最小值节点,进行替换
Node* min = root->_right;
while (min->_left)
min = min->_left;
swap(root->_key, min->_key);
return _EraseR(root->_right, key); //此时删除key,是root->_left == nullptr的情况
}
// 删除
delete del;
del = nullptr;
return true;
}
}
对于第1、2种情况:与常规实现的没区别,只是由于,常规实现使用的是局部变量cur,所以需要使用局部变量parent来确保链接情况以及是否为根节点,但是由于递归实现,直接就可以直接更改其节点的地址,所以底层差不多,而表面就一条语句。
对于第3种情况:思维与常规是类似的,但是我们需要删除就需要利用前面的第1、2种情况操作。
二叉搜索树的查找
// 根据二叉搜索树的性质查找:找到值为key的节点在二叉搜索树中的位置
bool Find(const K& key)
{
Node* cur = _root;
while (cur)
{
// 查找key在树中的位置
if (cur->_key > key)
cur = cur->_left;
else if (cur->_key < key)
cur = cur->_right;
else // 找到了
return true;
}
return false;
}
// this指针没法实现递归
bool FindR(const K& key)
{
// 实现递归
return _FindR(_root, key);
}
bool _FindR(const Node* root, const K& key)
{
if (root == nullptr)
return false;
// 查找key在树中的位置
if (root->_key > key) // >即只有可能在左树
return _FindR(root->_left, key);
else if (root->_key < key) // <即只有可能在左树
return _FindR(root->_right, key);
else // 找到了
return true;
return false;
}
#include
using namespace std;
namespace cr
{
template
struct BSTreeNode
{
BSTreeNode* _left;
BSTreeNode* _right;
K _key;
BSTreeNode(const K& key)
:_left(nullptr)
, _right(nullptr)
, _key(key)
{}
};
template
struct BSTree
{
typedef BSTreeNode Node;
// C++的用法:强制编译器生成默认的构造
BSTree() = default;
BSTree(const BSTree& t)
{
_root = _Copy(t._root);
}
Node* _Copy(Node* root)
{
if (root == nullptr)
{
return nullptr;
}
Node* copyRoot = new Node(root->_key);
copyRoot->_left = _Copy(root->_left);
copyRoot->_right = _Copy(root->_right);
return copyRoot;
}
//const BSTree& operator=(const BSTree& t)
//{
// if (this != &t) //防止自己给自己赋值
// {
// _root = _Copy(t._root);
// }
// return *this;
//}
// 利用t间接调用拷贝构造函数,后赋值给this->_root
BSTree& operator=(BSTree t)
{
swap(_root, t._root);
return *this;
}
//释放树中结点
void Destory(Node* root)
{
if (root == nullptr)
return;
// 释放左子树中的结点
Destory(root->_left);
// 释放右子树中的结点
Destory(root->_right);
delete root;
root = nullptr;
}
~BSTree()
{
//递归实现删除
Destory(_root);
_root = nullptr;
}
// 根据二叉搜索树的性质查找:找到值为key的节点在二叉搜索树中的位置
bool Insert(const K& key)
{
// 如果树为空,直接插入
if (_root == nullptr)
{
_root = new Node(key);
return true;
}
// 按照二叉搜索树的性质查找key在树中的插入位置cur
Node* cur = _root;
// 记录cur的双亲,因为新元素最终插入在cur双亲左右孩子的位置
Node* parent = nullptr;
while (cur)
{
if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
else if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
else // 元素已经在树中存在
return false;
}
// 插入元素
cur = new Node(key);
// 因为新元素最终插入在cur双亲左右孩子的位置,所以进行连接
if (parent->_key < key)
parent->_right = cur;
else
parent->_left = cur;
return true;
}
// 根据二叉搜索树的性质查找:找到值为key的节点在二叉搜索树中的位置
bool Find(const K& key)
{
Node* cur = _root;
while (cur)
{
// 查找key在树中的位置
if (cur->_key > key)
cur = cur->_left;
else if (cur->_key < key)
cur = cur->_right;
else // 找到了
return true;
}
return false;
}
bool Erase(const K& key)
{
// 如果树为空,删除失败
if (nullptr == _root)
return false;
// 按照二叉搜索树的性质查找key在树中的位置cur
Node* cur = _root;
// 记录cur的双亲,因为删除cur后cur双亲连接cur的左右孩子
Node* parent = nullptr;
while (cur)
{
// 查找key在树中的位置
if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
else if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
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;
cur = nullptr;
}
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;
}
}
else
{
//找右子树的最小值节点,进行替换
Node* minParent = cur;
Node* min = cur->_right;
while (min->_left)
{
minParent = min;
min = min->_left;
}
swap(min->_key, cur->_key);
if (min == minParent->_left)
minParent->_left = min->_right;
else
minParent->_right = min->_left;
delete min;
}
return true;
}
}
return false;
}
void InOrder()
{
_InOrder(_root);
cout << endl;
}
//递归版本
// this指针没法实现递归
bool FindR(const K& key)
{
// 实现递归
return _FindR(_root, key);
}
// this指针没法实现递归
bool EraseR(const K& key)
{
// 实现递归
return _EraseR(_root, key);
}
// this指针没法实现递归
bool InsertR(const K& key)
{
// 实现递归
return _InsertR(_root, key);
}
private:
//中序输出
void _InOrder(Node* root)
{
if (root == nullptr)
return;
_InOrder(root->_left);
cout << root->_key << " ";
_InOrder(root->_right);
}
bool _FindR(const Node* root, const K& key)
{
if (root == nullptr)
return false;
// 查找key在树中的位置
if (root->_key > key) // >即只有可能在左树
return _FindR(root->_left, key);
else if (root->_key < key) // <即只有可能在左树
return _FindR(root->_right, key);
else // 找到了
return true;
return false;
}
bool _EraseR(Node*& root, const K& key)
{
if (root == nullptr)
return false;
// 查找key在树中的位置
if (root->_key > key)
return _EraseR(root->_left, key);
else if (root->_key < key)
return _EraseR(root->_right, key);
else
{
Node* del = root;
if (root->_left == nullptr)
root = root->_right;
else if (root->_right == nullptr)
root = root->_left;
else
{
//找右子树的最小值节点,进行替换
Node* min = root->_right;
while (min->_left)
min = min->_left;
swap(root->_key, min->_key);
// 此时删除key,是root->_left == nullptr的情况
return _EraseR(root->_right, key);
}
// 删除
delete del;
del = nullptr;
return true;
}
}
bool _InsertR(Node*& root, const K& key)
{
//找到了插入的位置
if (root == nullptr)
{
root = new Node(key);
return true;
}
// 查找插入位置
if (root->_key > key)
_InsertR(root->_left, key);
else if (root->_key < key)
_InsertR(root->_right, key);
else // 该值已有
return false;
}
Node* _root = nullptr;
};
}