二叉搜索树
目录
一、二叉搜索树的概念
左子树不为空,左子树上所有节点的值小于等于根节点的值
右子树不为空,右子树上所有节点的值大于等于根节点的值
(它的左右子树也分别为二叉搜索树)

二叉搜索树中可以支持插入相等的值
也可以不支持插入相等的值
**map/set:**不支持相等
**multimap/multiset:**支持相等
二、二叉搜索树的性能分析
**最优情况:**接近完全二叉树,高度:log(2)N
**最坏情况:**退化成为单枝树,高度:N/2
**时间复杂度:**O(N)
**二叉树的变形:**AVL树、红黑树、B树系列

三、二叉搜索树的模拟实现
3.1.二叉搜索树的节点定义
cpp
//定义节点模板
template < class K>
struct BSTNode
{
K _key;
BSTNode<K>*_left;
BSTNode<K>*_right;
BSTNode(const K & key)
:_key(key)
, _left(nullptr)
, _right(nullptr)
{}
};
3.2.二叉搜索树的插入
cpp
bool Insert(const K& key)
{
if (_root == nullptr)
{
_root = new Node(key);
return true;
}
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
else
{
return false;
}
}
cur = new Node(key);
if (parent->_key < key)
{
parent->_right = cur;
}
else
{
parent->_left = cur;
}
return true;
}
3.3.二叉搜索树的查找
cpp
bool Find(const K& key)
{
Node* cur = _root;
while (cur)
{
if (cur->_key < key)
{
cur = cur->_right;
}
else if (cur->_key > key)
{
cur = cur->_left;
}
else
{
return true;
}
}
return false;
}
3.4.二叉搜索树的中序遍历
cpp
void _InOrder(Node* root)
{
if (root == nullptr)
{
return;
}
_InOrder(root->_left);
cout << root->_key << " ";
_InOrder(root->_right);
}
3.5.二叉搜索树的删除
首先查找元素是否在二叉搜索树中,如果不存在,则返回false
查找元素要分四种情况处理
**第一种:**要删除节点N的左右孩子均为空
把节点N的父亲对应孩子的指针指向空,直接删除N节点
**第二种:**要删除节点N的左孩子为空,右孩子节点不为空
把节点N的父亲对应孩子指针指向N的右孩子,直接删除N节点

**第三种:**要删除节点N的右孩子为空,左孩子节点不为空
把节点N的父亲对应孩子指针指向N的左孩子,直接删除N节点

**第四种:**要删除节点N的左右孩子节点均不为空
替代法
找N左子树的最大节点R(最右节点)
找N右子树的最小节点R(最左节点)
这两个节点任意一个放到N的位置

cpp
bool Erase(const K& key)
{
Node * parent = nullptr;
Node * cur = _root;
while (cur)
{
if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
else
{
//有0或1个孩子
//删除情况1、2、3均可以直接删除
//改变父亲对应孩子指针指向即可
//左为空
if (cur->_left == nullptr)
{
//判断是否是删除根
//parent为空
//if(cur == _root)
if (parent == nullptr)
{
_root = cur->_right;
}
else
{
if (parent->_left == cur)
{
parent->_left = cur->_right;
}
else
{
parent->_right = cur->_right;
}
}
delete cur;
return true;
}
else if (cur->_right == nullptr)//右为空
{
if (parent == nullptr)
{
_root = cur->_left;
}
else
{
if (parent->_left == cur)
{
parent->_left = cur->_left;
}
else
{
parent->_right = cur->_left;
}
}
delete cur;
return true;
}
else
{
//有2个孩子的情况
//删除情况4
//假设这里我们取右子树的最小结点作为替代结点去删除
//这里尤其要注意右子树的根就是最小情况的情况的处理
//一定要把cur给replaceParent,否会报错
Node * replaceParent = cur;
Node * replace = cur->_right;
while (replace->_left)
{
replaceParent = replace;
replace = replace->_left;
}
cur->_key = replace->_key;
if (replaceParent->_left == replace)
{
replaceParent->_left = replace->_right;
}
else
{
replaceParent->_right = replace->_right;
}
delete replace;
return true;
}
}
}
return false;
}
3.6.二叉搜索树的总代码
cpp
#pragma once
//定义节点模板
template <class K>
struct BSTNode
{
K _key;
BSTNode<K>*_left;
BSTNode<K>*_right;
BSTNode(const K & key)
:_key(key)
, _left(nullptr)
, _right(nullptr)
{}
};
//定义搜索树模板
template <class K>
class BSTree
{
//typedef BSTNode<K> Node;
using Node = BSTNode<K>;
public:
bool Insert(const K& key)
{
if (_root == nullptr)
{
_root = new Node(key);
return true;
}
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
else
{
return false;
}
}
cur = new Node(key);
if (parent->_key < key)
{
parent->_right = cur;
}
else
{
parent->_left = cur;
}
return true;
}
bool Find(const K& key)
{
Node* cur = _root;
while (cur)
{
if (cur->_key < key)
{
cur = cur->_right;
}
else if (cur->_key > key)
{
cur = cur->_left;
}
else
{
return true;
}
}
return false;
}
bool Erase(const K& key)
{
Node * parent = nullptr;
Node * cur = _root;
while (cur)
{
if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
else
{
//有0或1个孩子
//删除情况1、2、3均可以直接删除
//改变父亲对应孩子指针指向即可
//左为空
if (cur->_left == nullptr)
{
//判断是否是删除根
//parent为空
//if(cur == _root)
if (parent == nullptr)
{
_root = cur->_right;
}
else
{
if (parent->_left == cur)
{
parent->_left = cur->_right;
}
else
{
parent->_right = cur->_right;
}
}
delete cur;
return true;
}
else if (cur->_right == nullptr)//右为空
{
if (parent == nullptr)
{
_root = cur->_left;
}
else
{
if (parent->_left == cur)
{
parent->_left = cur->_left;
}
else
{
parent->_right = cur->_left;
}
}
delete cur;
return true;
}
else
{
//有2个孩子的情况
//删除情况4
//假设这里我们取右子树的最小结点作为替代结点去删除
//这里尤其要注意右子树的根就是最小情况的情况的处理
//一定要把cur给replaceParent,否会报错
Node * replaceParent = cur;
Node * replace = cur->_right;
while (replace->_left)
{
replaceParent = replace;
replace = replace->_left;
}
cur->_key = replace->_key;
if (replaceParent->_left == replace)
{
replaceParent->_left = replace->_right;
}
else
{
replaceParent->_right = replace->_right;
}
delete replace;
return true;
}
}
}
return false;
}
void InOrder()
{
_InOrder(_root);
cout << endl;
}
private:
void _InOrder(Node* root)
{
if (root == nullptr)
{
return;
}
_InOrder(root->_left);
cout << root->_key << " ";
_InOrder(root->_right);
}
private:
Node* _root = nullptr;
};
四、二叉搜索树的使用场景
4.1.key搜索场景(set)
只有key作为关键码,结构中只需要存储key即可
(判断在不在)
**关键码:**需要搜索到的值
key的搜索场景实现的二叉搜索树支持增删查,但不支持修改
(修改key会破坏树的结构)
场景:
检查一篇英文文章单词拼写是否正确
将词库中的所有单词放入二叉搜索树,读取文章中的单词
查找是否在二叉搜索树中,不在用红色波浪线标红提示
4.2.key/value搜索场景(map)
每一个关键码key,都有与之对应的值value
value可以是任意类型的对象
树的结构中(节点)除了需要存储key还有存储对应的value
增/删/查还是以key为关键字走二叉搜索树的规则进行比较
可以快速查到key对应的value,这种二叉搜索树支持修改
但只支持修改value,因为修改key会破坏搜索树的结构
场景:
简单中英文互译字典,树的结构中节点存储key(英文)和value(中文)
搜索时输入英文,则同时查找到了英文对应的中文
代码实现:
cpp
#pragma once
#include <iostream>
using namespace std;
namespace key_value
{
template <class K,class V>
struct BSTNode
{
K _key;
V _value;
BSTNode<K,V>* _left;
BSTNode<K,V>* _right;
BSTNode(const K& key,const V& value)
:_key(key)
, _value(value)
, _left(nullptr)
, _right(nullptr)
{}
};
template <class K,class V>
class BSTree
{
using Node = BSTNode<K,V>;
public:
//强制生成构造
BSTree() = default;
BSTree(const BSTree<K,V>& t)
{
_root = Copy(t._root);
}
BSTree<K,V>& operator=(BSTree<K,V> t)
{
swap(_root, t._root);
return *this;
}
~BSTree()
{
Destroy(_root);
_root = nullptr;
}
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)
{
if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
else
{
return false;
}
}
cur = new Node(key,value);
if (parent->_key < key)
{
parent->_right = cur;
}
else
{
parent->_left = cur;
}
return true;
}
Node* Find(const K& key)
{
Node* cur = _root;
while (cur)
{
if (cur->_key < key)
{
cur = cur->_right;
}
else if (cur->_key > key)
{
cur = cur->_left;
}
else
{
return cur;
}
}
return nullptr;
}
bool Erase(const K& key)
{
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
else
{
if (cur->_left == nullptr)
{
if (parent == nullptr)
{
_root = cur->_right;
}
else
{
if (parent->_left == cur)
{
parent->_left = cur->_right;
}
else
{
parent->_right = cur->_right;
}
}
delete cur;
return true;
}
else if (cur->_right == nullptr)
{
if (parent == nullptr)
{
_root = cur->_left;
}
else
{
if (parent->_left == cur)
{
parent->_left = cur->_left;
}
else
{
parent->_right = cur->_left;
}
}
delete cur;
return true;
}
else
{
Node* replaceParent = cur;
Node* replace = cur->_right;
while (replace->_left)
{
replaceParent = replace;
replace = replace->_left;
}
cur->_key = replace->_key;
if (replaceParent->_left == replace)
{
replaceParent->_left = replace->_right;
}
else
{
replaceParent->_right = replace->_right;
}
delete replace;
return true;
}
}
}
return false;
}
void InOrder()
{
_InOrder(_root);
cout << endl;
}
private:
void _InOrder(Node* root)
{
if (root == nullptr)
{
return;
}
_InOrder(root->_left);
cout << root->_key << ":" << root->_value << endl;
_InOrder(root->_right);
}
void Destroy(Node* root)
{
if (root == nullptr)
{
return;
}
Destroy(root->_left);
Destroy(root->_right);
delete root;
}
Node* Copy(Node* root)
{
if (root == nullptr)
{
return nullptr;
}
Node * newRoot = new Node(root->_key, root->_value);
newRoot->_left = Copy(root->_left);
newRoot->_right = Copy(root->_right);
return newRoot;
}
private:
Node* _root = nullptr;
};
}
测试1:
cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include "BinarySearch.h"
int main()
{
string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉" };
key_value::BSTree<string, int> countTree;
for (const auto& str : arr)
{
//先查找水果在不在搜索树中
//不在,说明水果第一次出现,则插入<水果, 1>
//在,则查找到的结点中水果对应的次数++
//BSTreeNode<string, int>* ret = countTree.Find(str);
auto ret = countTree.Find(str);
if (ret == NULL)
{
countTree.Insert(str, 1);
}
else
{
ret->_value++;
}
}
countTree.InOrder();
return 0;
}
测试2:
cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include "BinarySearch.h"
int main()
{
key_value::BSTree<string, string> dict;
dict.Insert("left", "左边");
dict.Insert("right", "右边");
dict.Insert("insert", "插入");
dict.Insert("string", "字符串");
string str;
while (cin >> str)
{
auto ret = dict.Find(str);
if (ret)
{
cout << "->" << ret->_value << endl;
}
else
{
cout << "无此单词,请重新输入" << endl;
}
}
return 0;
}