1.二叉搜索树
二叉搜索树又称二叉排序树 有两种情况:
一棵空树
若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
它的左右子树也分别为二叉搜索树
template<class K>
struct BSTreeNode
{
BSTreeNode<K>* _left;
BSTreeNode<K>* _right;
K _key;
//new要调构造函数 需要写
BSTreeNode(const K& key)
:_left(nullptr)
,_right(nullptr)
,_key(key)
{}
};
2.二叉搜索树的查找
//查找
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;
}
}
//cur找到空了 说明找不到
return false;
}
3.二叉搜索树的插入
这里以插入10为例
步骤:
-
1.给到parent和cur两个指针
-
2.如果cur->_key比插入的小 那么就往左子树走 如果cur->_key比插入的大 就往右子树走
-
3.直到cur为NULL的时候就可以跳出循环
-
4.此时cur就为插入的位置 new一个结点 但是你不知道是在你parent的左边还是右边
所以要用双指针先记录parent的位置 再分两类就可以 如果parent->_key比插入的大
那就把cur放到parent的左子树 反之就把cur放到右子树
注意:如果树里已经有这个值 那么插入失败 一个二叉搜索树中一个值只出现一次
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);//需要与父节点链接到一起 如何处理? 前后指针 记录cur上一个的位置
if (parent->_key < key)//插入的大 就链接到右边
{
parent->_right = cur;
}
else//插入的小 就链接到parent的左边
{
parent->_left = cur;
}
return true;
}
4.二叉搜索树的删除
-
1.要删除的结点无孩子结点
-
2.要删除的结点只有左孩子结点(说明该结点右边为空)
-
3.要删除的结点只有右孩子结点(说明该结点左边为空)
注意:情况1 2 3可能删的是根 那么就不同的情况进行讨论
-
4.要删除的结点有左、右孩子结点
节点左右孩子都存在,直接删除不好删除,可以在其子树中找一个替代结点,找其左子树
中的最大节点,即左子树中最右侧的节点,或者在其右子树中最小的节点,即右子树中最
小的节点。 替代节点找到后,将替代节点中的值交给待删除节点,转换成删除替代节点。
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 (cur == _root)
{
_root = cur->_right;
}
else
{
//找到了 开始删除
//1.左为空
//2.右为空
//3.左右都不为空
if (cur->_left == nullptr)
{
//看cur原来是parent的左还是右
if (parent->_right == cur)
parent->_right = cur->_right;
else
parent->_left = cur->_right;
}
}
delete cur;
}
else if (cur->_right == nullptr)
{
//此时cur是根且右为空 那么应该把根给到左边
if (cur == _root)
{
_root = cur->_left;
}
else
{
//看cur原来是parent的左还是右
if (parent->_right == cur)
parent->_right = cur->_left;
else
parent->_left = cur->_left;
}
delete cur;
}
else
{
//这里的替换节点一定满足情况1或者情况2 可以删除
//这里有逻辑问题 删8的时候
Node* rightMinParent = cur;//nullptr有问题
Node* rightMin = cur->_right;
while (rightMin->_left)
{
//左不为空 就往左走
rightMinParent = rightMin;
rightMin = rightMin->_left;
}
//替换
cur->_key = rightMin->_key;
//转换成删除rightMin rightMin的左一定为空 所以rightMinParent指向右即可
//rightMin在左边 父亲的左边指向rightMin的右边
//rightMin在右边 父亲的右边指向rightMin的右边
if (rightMin == rightMinParent->_left)
rightMinParent->_left = rightMin->_right;
else
rightMinParent->_right = rightMin->_right;
delete rightMin;
}
return true;
}
}
return false;
}
5.插入与删除的测试
void TestBSTree()
{
BSTree<int> t;
int a[] = { 5,3,4,1,7,8,2,6,0,9 };
for (auto e : a)
{
t.Insert(e);
}
t.InOrder();
//删7怎么办 解决
t.Erase(7);
t.InOrder();
//1.叶子
t.Erase(2);
t.InOrder();
//2.左为空 或者右为空
t.Erase(8);
t.Erase(1);
t.InOrder();
//3.左右都不为空(根)
t.Erase(5);
t.InOrder();
//4.删空也有问题 当根左或者右为空的时候 此时会走把父亲给到左或者右
//此时根的父亲是空指针 会解引用报错 所以要多一类 必须重置父亲的位置
for (auto e : a)
{
t.Erase(e);
t.InOrder();
}
}
#include "BSTree.hpp"
int main()
{
TestBSTree();
return 0;
}
6.二叉搜索树的应用
搜索使用场景:
1.key的模型 判断在不在?
2.key/value的模型 通过key找对应的value?
安检 快递
中英字典互译 计数
搜索效率:如果插入的数据是有序的或者接近有序?那么搜索树效率就完全没办法保障
搜索树的效率最坏的情况下是O(N)
如何解决?
1.AVLTree
2.红黑树
搜索树的修改
搜索树中key是不允许修改的
如果是kv模型的搜索树 可以修改value 但是不能修改key
void TestBSTree()
{
//匹配
BSTree<string, string> dict;
dict.Insert("sort", "排序");
dict.Insert("string", "字符串");
dict.Insert("tree", "树");
dict.Insert("insert", "插入");
string str;
while (cin >> str)
{
BSTreeNode<string, string>* ret = dict.Find(str);
if (ret)
{
cout << ret->_value << endl;
}
else
{
cout << "无此单词" << endl;
}
}
}
void TestBSTree()
{
//计数
string strArr[] = { "西瓜","苹果" ,"西瓜" ,"苹果" ,"西瓜" ,"香蕉" ,"西瓜","樱桃","西瓜","西瓜" };
BSTree<string, int> countTree;
for (auto str : strArr)
{
BSTreeNode<string, int>* ret = countTree.Find(str);
if (ret == nullptr)//没有出现过就插入
{
countTree.Insert(str, 1);
}
else//再次出现就++
{
ret->_value++;
}
}
countTree.InOrder();
}
【C++】14.二叉搜索树 完