本篇文章进行数据结构中二叉搜索树的学习!!!
概念:二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:

前言:该文章将用下图作为插入删除操作的辅助

int array[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13 };
前言:二叉搜索树是由模板定的,节点存储的指针和对象可泛化
template <typename K>
struct BSTNode
{
BSTNode(const K& key = K())
: left(nullptr)
, right(nullptr)
, _key(key)
{}
BSTNode<K>* left;
BSTNode<K>* right;
K _key;
};
template <typename K>
class BSTree
{
typedef BSTNode<K> Node;
private:
Node* root;
}
注意:这里的节点跟二叉树完全一致,都是有二个指针指向左右孩子和存储一个值
二叉搜索树查找指定值的思路:
bool find(const K& key)
{
Node* cur = root;
while (cur != nullptr)
{
if (cur->_key > key)
cur = cur->left;
else if (cur->_key < key)
cur = cur->right;
else
return true;
}
return false;
}

第二种思路:使用递归进行查找 – 分治法,思路跟循环差不多
3. 从根节点开始查找,大于根值走左子树,小于走右子树
4. 缺陷:树不平衡且节点很多时,会导致递归太深 – 栈溢出
bool Find(const K& key)
{
return _find(root, key);
}
bool _Find(Node* _root, const K& key)
{
// 分治法 -- ①小于key走左子树 ②大于key走右子树 ③相等返回true ④走到最后没找到返回false
if (_root == nullptr)
return false;
if (_root->_key > key) {
return _Find(_root->left, key);
}
else if (_root->_key < key) {
return _Find(_root->right, key);
}
else {
return true;
}
return false;
}
二叉搜索树插入的思路:
bool insert(const K& key)
{
// 思路:维护二个指针,一个指向根,一个为根父节点,比根的值小走左子树,反之右子树,相等返回false
if (root == nullptr)
{
root = new Node(key);
return true;
}
Node* cur = root;
Node* parent = nullptr;
while (cur != nullptr)
{
if (cur->_key > key) {
parent = cur;
cur = cur->left;
}
else if (cur->_key < key) {
parent = cur;
cur = cur->right;
}
else
return false;
}
// cur走到空时,判断根父节点的值比插入值大还是小,大插入右边,小插入左边
Node* newNode = new Node(key);
if (parent->_key > key)
parent->left = newNode;
else
parent->right = newNode;
return true;
}

第二种方法:使用递归进行插入,思路:
bool Insert(const K& key)
{
return _insert(root, key);
}
// 这里根节点加引用说明_root是原指针别名,构造新节点直接在原指针上构造,不需要加一个父指针链接
bool _Insert(Node*& _root, const K& key)
{
// 根为nullptr,直接构造一个节点
if (_root == nullptr) {
_root = new Node(key);
return true;
}
// 分治法 -- ①小于key走左子树 ②大于key走右子树 ③相等返回false(不能数据冗余) ④构造节点链接
if (_root->_key > key) {
return _Insert(_root->left, key);
}
else if (_root->_key < key) {
return _Insert(_root->right, key);
}
else {
return false;
}
}
首先查找元素是否在二叉搜索树中,如果不存在,则返回, 否则要删除的结点可能分下面四种情况:
看起来有待删除节点有4中情况,实际情况a可以与情况b或者c合并起来,因此真正的删除过程
如下:
bool erase(const K& key)
{
if (root == nullptr) {
return false;
}
Node* parent = nullptr;
Node* cur = root;
while (cur != nullptr)
{
// 找删除节点和它的父节点
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 (parent == nullptr) {
root = cur->left;
}
else
{
if (cur == parent->left) {
parent->left = cur->left;
}
else {
parent->right = cur->left;
}
}
}
// 情况三
else if (cur->left == nullptr)
{
if (parent == nullptr) {
root = cur->right;
}
else
{
if (cur == parent->left) {
parent->left = parent->right;
}
else {
parent->right = cur->right;
}
}
}
else
{
// 情况四
Node* RMin = cur->right;
Node* Rparent = cur;
while (RMin->left != nullptr)
{
Rparent = RMin;
RMin = RMin->left;
}
cur->_key = RMin->_key;
cur->_value = RMin->_value;
if (Rparent->left == RMin) {
Rparent->left = RMin->right;
}
else {
Rparent->right = RMin->right;
}
cur = RMin;
}
delete cur;
return true;
}
}
return false;
}
图解:情况二和情况三

图解:情况四

第二种方法:使用递归进行删除,思路:
bool Erase(const K& key)
{
return _Erase(root, key);
}
bool _Erase(Node*& _root, const K& key)
{
// 根节点为空或没找到,返回false
if (_root == nullptr) {
return false;
}
// 找删除节点
if (_root->_key > key) {
return _Erase(_root->left, key);
}
else if (_root->_key < key) {
return _Erase(_root->right, key);
}
else
{
// 保存删除节点,不保存会找不到原来的root,因为要删除root,就要让root的左/右孩子赋值给root
Node* del = _root;
// 找到后 ---- 分三种情况:①删除节点只有左孩子 ②删除节点只有有孩子 ③删除节点有左右孩子
if (_root->right == nullptr) {
_root = _root->left;
}
else if (_root->left == nullptr) {
_root = _root->right;
}
else
{
// 找删除节点左子树中最大的值
Node* left_Max = root->left;
while (left_Max->right) {
left_Max = left_Max->right;
}
std::swap(root->_key, left_Max->_key); // 替换它们的值
return _Erase(root->left, key); // 在当前root的左子树中找已经替换值的节点,并且删除它
}
delete del;
return true;
}
return false;
}
template <typename K>
struct BSTNode
{
BSTNode(const K& key = K())
: left(nullptr)
, right(nullptr)
, _key(key)
{}
BSTNode<K>* left;
BSTNode<K>* right;
K _key;
};
template <typename K>
class BSTree
{
typedef BSTNode<K> Node;
public:
BSTree()
: root(nullptr)
{}
BSTree(const BSTree<K>& s)
: root(nullptr)
{
root = structure(s.root);
}
BSTree<K>& operator=(const BSTree<K>& bst)
{
Destory(root);
root = structure(bst.root);
return *this;
}
~BSTree()
{
Destory(root);
}
bool insert(const K& key)
{
// 思路:维护二个指针,一个指向根,一个为根父节点,比根的值小走左子树,反之右子树,相等返回false
// 走到空时,判断根父节点的值比插入值大还是小,大插入右边,小插入左边
if (root == nullptr)
{
root = new Node(key);
return true;
}
Node* cur = root;
Node* parent = nullptr;
while (cur != nullptr)
{
if (cur->_key > key) {
parent = cur;
cur = cur->left;
}
else if (cur->_key < key) {
parent = cur;
cur = cur->right;
}
else
return false;
}
Node* newNode = new Node(key);
if (parent->_key > key)
parent->left = newNode;
else
parent->right = newNode;
return true;
}
bool erase(const K& key)
{
Node* cur = root;
Node* parent = nullptr;
while (cur != nullptr)
{
// 查找删除的节点的位置
if (cur->_key > key) {
parent = cur;
cur = cur->left;
}
else if (cur->_key < key) {
parent = cur;
cur = cur->right;
}
else
{
// 找到后,删除时分四种情况 -- ①删除节点是一个叶节点 -- ②只有左节点 -- ③只有右节点 -- ④有左右节点
// 删除节点只有左孩子时
if (cur->right == nullptr)
{
// 当要删除根节点时,会造成parent空指针访问,可以把"根指针"直接指向他的"左孩子节点"
if (parent == nullptr) {
root = cur->left;
}
else
{
if (cur == parent->left) {
parent->left = cur->left;
}
else {
parent->right = cur->left;
}
}
delete cur;
cur = nullptr;
}
// 删除节点只有右孩子时
else if (cur->left == nullptr)
{
// 当要删除根节点时,会造成parent空指针访问,可以把"根指针"直接指向他的"右孩子节点"
if (parent == nullptr) {
root = cur->right;
}
else
{
if (cur == parent->left) {
parent->left = cur->right;
}
else {
parent->right = cur->right;
}
}
delete cur;
cur = nullptr;
}
// 删除的节点有左右孩子 -- 替换法 -> 找删除节点中左子树最大的值或右子树最小的值,替换删除
else
{
Node* left_max = cur->left;
// 删除节点可能是根节点且右子树为空,那么下面的循环将进不去
Node* maxParent = cur;
// 找删除节点左子树中最大值的节点(一直往右走遍历)
while (left_max->right) {
maxParent = left_max;
left_max = left_max->right;
}
// 删除节点的值根left_max的值替换
cur->_key = left_max->_key;
// 删除left_max节点
if (maxParent->left == left_max) {
maxParent->left = left_max->left;
}
else {
maxParent->left = left_max->right;
}
delete left_max;
left_max = nullptr;
}
return true;
}
}
return false;
}
bool find(const K& key)
{
Node* cur = root;
while (cur != nullptr)
{
if (cur->_key > key)
cur = cur->left;
else if (cur->_key < key)
cur = cur->right;
else
return true;
}
return false;
}
// 中序遍历
void InOrder()
{
_InOrder(root);
cout << endl;
}
private:
// 使用前序遍历构造一颗新的SB树
Node* structure(Node* _root)
{
if (_root == nullptr)
return nullptr;
Node* newTree = new Node(_root->_key);
newTree->left = structure(_root->left);
newTree->right = structure(_root->right);
return newTree;
}
void Destory(Node* _root)
{
if (_root == nullptr)
return;
Destory(_root->left);
Destory(_root->right);
delete _root;
}
void _InOrder(Node* _root)
{
if (_root == nullptr)
return;
_InOrder(_root->left);
cout << _root->_key << ' ';
_InOrder(_root->right);
}
private:
Node* root;
};
前言:插入和删除操作都必须先查找,查找效率代表了二叉搜索树中各个操作的性能
对有n个结点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树平均查找长度是结点在二
叉搜索树的深度的函数,即结点越深,则比较次数越多
但对于同一个关键码集合,如果各关键码插入的次序不同,可能得到不同结构的二叉搜索树:

template <typename K, typename V>
struct BSTNode
{
BSTNode(const K& key = K(), const V& value = V())
: left(nullptr)
, right(nullptr)
, _key(key)
, _value(value)
{}
BSTNode<K, V>* left;
BSTNode<K, V>* right;
K _key;
V _value;
};
// KV版本 -- key value
template <typename K, typename V>
class BSTree
{
typedef BSTNode<K, V> Node;
public:
~BSTree() {
Destory(root);
}
void InOrder() {
_InOrder(root);
}
bool insert(const K& key, const V& value)
{
if (root == nullptr) {
root = new Node(key, value);
return true;
}
Node* parent = nullptr;
Node* cur = root;
while (cur != nullptr)
{
if (cur->_key > key) {
parent = cur;
cur = cur->left;
}
else if (cur->_key < key) {
parent = cur;
cur = cur->right;
}
else {
return false;
}
}
Node* newNode = new Node(key, value);
if (parent->_key > key) {
parent->left = newNode;
}
else {
parent->right = newNode;
}
return true;
}
bool erase(const K& key)
{
if (root == nullptr) {
return false;
}
Node* parent = nullptr;
Node* cur = root;
while (cur != nullptr)
{
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 (parent == nullptr) {
root = cur->left;
}
else
{
if (cur == parent->left) {
parent->left = cur->left;
}
else {
parent->right = cur->left;
}
}
}
else if (cur->left == nullptr)
{
if (parent == nullptr) {
root = cur->right;
}
else
{
if (cur == parent->left) {
parent->left = parent->right;
}
else {
parent->right = cur->right;
}
}
}
else
{
Node* RMin = cur->right;
Node* Rparent = cur;
while (RMin->left != nullptr)
{
Rparent = RMin;
RMin = RMin->left;
}
cur->_key = RMin->_key;
cur->_value = RMin->_value;
if (Rparent->left == RMin) {
Rparent->left = RMin->right;
}
else {
Rparent->right = RMin->right;
}
cur = RMin;
}
delete cur;
return true;
}
}
return false;
}
Node* find(const K& key)
{
if (root == nullptr) {
return nullptr;
}
Node* cur = root;
while (cur != nullptr)
{
if (cur->_key > key) {
cur = cur->left;
}
else if (cur->_key < key) {
cur = cur->right;
}
else {
return cur;
}
}
return nullptr;
}
private:
void Destory(Node* _root)
{
if (_root == nullptr)
return;
Destory(_root->left);
Destory(_root->right);
delete _root;
}
void _InOrder(Node* _root)
{
if (_root == nullptr)
return;
_InOrder(_root->left);
cout << _root->_key << ": " << _root->_value << endl;
_InOrder(_root->right);
}
private:
Node* root;
};
void Test()
{
string str[]{ "苹果", "雪梨", "榴莲", "苹果", "苹果", "雪梨", "香蕉", "苹果", "雪梨", "榴莲", "香蕉", "苹果" };
BSTree<string, int> countTree;
for (auto& f : str)
{
auto ret = countTree.find(f);
if (ret == nullptr)
{
countTree.insert(f, 1);
}
else
{
ret->_value++;
}
}
countTree.InOrder();
}