目录
1. 二叉树定义
2. 二叉搜索树定义
2.1 定义:
2.2 二叉搜索树的优缺点:
3. 二叉搜索树代码模拟实现:
3.1 二叉搜索树的插入
3.2 二叉搜索树的查找
3.3 二叉搜索树的删除
3.4 二叉搜索树的模拟实现
4. 二叉搜索树的应用场景
1. 二叉树定义
【C 数据结构】 树 二叉树 堆 (堆的模拟实现篇)-CSDN博客
二叉树(Binary Tree)是树形结构的一种,指每个节点最多有两个子节点的树。这两个子节点通常分别称为左子树和右子树,且左右次序不能随意颠倒。
一棵二叉树通常满足以下特征:
- 每个节点最多有两个子节点,即节点的度不超过 2。
- 左子树和右子树是有序的,交换后属于不同的二叉树。
- 即使某个节点只有一个子节点,也需要明确它是左子节点还是右子节点。
- 二叉树可以为空,即不含任何节点。
从递归角度看,二叉树可以这样定义:一棵二叉树要么是空树,要么由根节点、一棵左子树和一棵右子树组成,且左子树和右子树本身也都是二叉树。
与普通树相比,二叉树的关键区别在于子节点数量受限且左右有序,这一特性使得二叉树在存储、遍历和查找等场景中更容易实现和分析。
2. 二叉搜索树定义
2.1 定义:
在某一个节点上的数值,一定比该节点的左子树大,比右子树小,不存在等于的情况。由中序遍历下,达到一个有序的排列。例如:该二叉树的底层是以<int>类型为主的二叉树,那么在某一个节点上,节点数值为8,那么左子树的所有数值一定小于8,右子树的所有数值一定大于8。

二叉搜索树展示图 2.1
2.2 二叉搜索树的优缺点:
优点:
缺点:

单边极端情况 2.2
3. 二叉搜索树代码模拟实现:
3.1 二叉搜索树的插入
基于概念进行设计函数功能即可,注意点:
代码判断:

二叉树插入展示图3.1

int arr[] = {5,3,7,1,4,2,0,6,8,9};
// 插入
bool Insert(const K& key, const V& value)
{
// 判断是否为空树
if (_root == nullptr)
{
_root = new Node(key, value);
return true;
}
else // 不为空树
{
// cur 记录上一步位置,dest 确定下一步位置
Node* dest = _root, *cur = nullptr;
while (dest)
{
// 比较找好 ket 的合适位置
if (key < dest->_key)
{
cur = dest;
dest = dest->_left;
}
else if (dest->_key < key)
{
cur = dest;
dest = dest->_right;
}
else // 存在相同 ket 不构建节点
{
return false;
}
}
dest = new Node(key, value);// 构造新节点
if (key < cur->_key)
{
cur->_left = dest;
}
else if (cur->_key < key)
{
cur->_right = dest;
}
return true;
}
}
3.2 二叉搜索树的查找
代码判断标准:

查找二叉树展示图3.2
// 查找 key
Node* Find(const K& key)
{
Node* root = _root;
while (root)
{
if (root->_key == key)
{
return root;
}
else if (root->_key < key)
{
root = root->_right;
}
else if (root->_key > key)
{
root = root->_left;
}
}
return nullptr;
}
3.3 二叉搜索树的删除
删除与其他功能相对比较复杂,如果只是单纯用查找函数的话,会到导致一个跟这个子节点的父亲节点找不到,所以目前 Find() 函数功能并不适合来用与查找想要删除节点,或者我们可以来实现一个Node* _Find(const &K ,Node*& cur )函数功能可以返回还是地址,并且 cur 的也可以找到对应的父亲节点。
如何删除:
二叉树的要被删除的节点可能分为4中情况,单独删掉该节点都有可能影响整个二叉搜索树的合理性,所以就会去找与他比较相似的数值代替位置,例如 要删除8,那么就会去找左右节点中最大最小的数代替,两种各选其一,但不是什么情况都是两者都可以,例如只有左子树,那么就只能去找左子树最大的数值,右子树同理,找最小的数值。

删除示例图3.3
| 如果删除的节点为子节点(没有左右子树) | 直接删除 |
| 如果删除的节点只有右子树 | 找右子树的最小数值 |
| 如果删除的节点只有左子树 | 找左子树的最大数值 |
| 如果删除的节点左右子树 | 找左右子树的最大最小都可以 |
bool Erase(const K& key)
{
Node* root = _root, *cur = nullptr;//cur 预防找到 root 的位置为子节点
// find
while (root)
{
if (root->_key == key)
{
break;
}
else if (root->_key < key)
{
cur = root;
root = root->_right;
}
else if (root->_key > key)
{
cur = root;
root = root->_left;
}
}
// 找不到
if (root == nullptr)
{
return false;
}
// root 为子节点
if (root->_left == nullptr && root->_right == nullptr)
{
// root 是 cur 的左子树
if (cur == nullptr)
{
// root == _root 整个二叉搜索树就只有一个节点,并且要找到删除的数值就是 _root
}
else if (cur->_left == root)
{
cur->_left = nullptr;
}
else if(cur ->_right == root)
{
cur->_right = nullptr;
}
delete root;
}
// root 只有左子树
else if (root->_right == nullptr)
{
Node* LeftMax = root->_left;
cur = root;
// cur LeftMax
while (LeftMax->_right)
{
cur = LeftMax;
LeftMax = LeftMax->_right;
}
if (LeftMax->_left != nullptr)
{
cur->_right = LeftMax->_left;
}
else
{
cur->_right = nullptr;
}
swap(LeftMax->_key, root->_key);
swap(LeftMax->_value, root->_value);
delete LeftMax;
}
else
{
// root 只有右子树
// root 左右子树的情况
Node* RightMin = root->_right;
cur = root;
// cur LeftMax
while (RightMin->_left)
{
cur = RightMin;
RightMin = RightMin->_left;
}
if (RightMin->_right != nullptr)
{
cur->_left = RightMin->_right;
}
else
{
cur->_left = nullptr;
}
swap(RightMin->_key, root->_key);
swap(RightMin->_value, root->_value);
delete RightMin;
}
return true;
}
3.4 二叉搜索树的模拟实现
26_10_24 BSTree/BSTree.h · 浪子·悠仁/C++ 知识库 – 码云 – 开源中国
#pragma once
#include <string>
#include <assert.h>
#include <iostream>
using namespace std;
// K -> key V->talue
template<class K ,class V>
struct BSTreeNode
{
K _key;// 二叉搜索树的比较关键
V _value;// 二叉搜索树的附加节点对象
BSTreeNode<K,V>* _left;// 左子树
BSTreeNode<K,V>* _right;// 右子树
// 节点初始化
BSTreeNode(const K& key,const V& value)
{
_key = key;
_value = value;
_left = _right = nullptr;
}
};
template<class K, class V>
class BSTree
{
typedef BSTreeNode<K, V> Node;
public:
// 插入
bool Insert(const K& key, const V& value)
{
if (_root == nullptr)
{
_root = new Node(key, value);
return true;
}
else
{
Node* dest = _root, *cur = nullptr;
while (dest)
{
if (key < dest->_key)
{
cur = dest;
dest = dest->_left;
}
else if (dest->_key < key)
{
cur = dest;
dest = dest->_right;
}
else
{
return false;
}
}
dest = new Node(key, value);
if (key < cur->_key)
{
cur->_left = dest;
}
else if (cur->_key < key)
{
cur->_right = dest;
}
return true;
}
}
// 查找 key
Node* Find(const K& key)
{
Node* root = _root;
while (root)
{
if (root->_key == key)
{
return root;
}
else if (root->_key < key)
{
root = root->_right;
}
else if (root->_key > key)
{
root = root->_left;
}
}
return nullptr;
}
bool Erase(const K& key)
{
Node* root = _root, *cur = nullptr;//cur 预防找到 root 的位置为子节点
// find
while (root)
{
if (root->_key == key)
{
break;
}
else if (root->_key < key)
{
cur = root;
root = root->_right;
}
else if (root->_key > key)
{
cur = root;
root = root->_left;
}
}
// 找不到
if (root == nullptr)
{
return false;
}
// root 为子节点
if (root->_left == nullptr && root->_right == nullptr)
{
// root 是 cur 的左子树
if (cur == nullptr)
{
// root == _root 整个二叉搜索树就只有一个节点,并且要找到删除的数值就是 _root
}
else if (cur->_left == root)
{
cur->_left = nullptr;
}
else if(cur ->_right == root)
{
cur->_right = nullptr;
}
delete root;
}
// root 只有左子树
else if (root->_right == nullptr)
{
Node* LeftMax = root->_left;
cur = root;
// cur LeftMax
while (LeftMax->_right)
{
cur = LeftMax;
LeftMax = LeftMax->_right;
}
if (LeftMax->_left != nullptr)
{
cur->_right = LeftMax->_left;
}
else
{
cur->_right = nullptr;
}
swap(LeftMax->_key, root->_key);
swap(LeftMax->_value, root->_value);
delete LeftMax;
}
else
{
// root 只有右子树
// root 左右子树的情况
Node* RightMin = root->_right;
cur = root;
// cur LeftMax
while (RightMin->_left)
{
cur = RightMin;
RightMin = RightMin->_left;
}
if (RightMin->_right != nullptr)
{
cur->_left = RightMin->_right;
}
else
{
cur->_left = nullptr;
}
swap(RightMin->_key, root->_key);
swap(RightMin->_value, root->_value);
delete RightMin;
}
return true;
}
void _InOrder(Node* root)
{
if (root == nullptr)
{
return;
}
_InOrder(root->_left);
cout << root->_key << ":" << root->_value << endl;
_InOrder(root->_right);
}
void InOrder()
{
_InOrder(_root);
cout << endl;
}
private:
Node* _root = nullptr;
};
4. 二叉搜索树的应用场景
核心特点:中序遍历有序、支持动态插入 / 删除 / 查找、方便范围查询;工程里大多用它的平衡变种(红黑树、AVL 树),普通 BST 一般用于教学、小规模数据。
void TestBSTree()
{
//单词查询
BSTree<string, string> dict;
dict.Insert("insert", "插入");
dict.Insert("erase", "删除");
dict.Insert("left", "左边");
dict.Insert("string", "字符串");
string str;
while (cin >> str)
{
auto ret = dict.Find(str);
if (ret)
{
cout << str << ":" << ret->_value << endl;
}
else
{
cout << "单词拼写错误" << endl;
}
}
// 计算存在水果种类和各个水果数量
string strs[] = { "苹果", "西瓜", "苹果", "樱桃", "苹果", "樱桃", "苹果", "樱桃", "苹果" };
// 统计水果出现的次
BSTree<string, int> countTree;
for (auto str : strs)
{
auto ret = countTree.Find(str);
if (ret == NULL)
{
countTree.Insert(str, 1);
}
else
{
ret->_value++;
}
}
countTree.InOrder();
}
感谢观看!
悠仁さん
