【高阶数据结构】搜索二叉树
创始人
2024-05-02 01:03:09

🌈欢迎来到数据结构专栏~~搜索二叉树


  • (꒪ꇴ꒪(꒪ꇴ꒪ )🐣,我是Scort
  • 目前状态:大三非科班啃C++中
  • 🌍博客主页:张小姐的猫~江湖背景
  • 快上车🚘,握好方向盘跟我有一起打天下嘞!
  • 送给自己的一句鸡汤🤔:
  • 🔥真正的大师永远怀着一颗学徒的心
  • 作者水平很有限,如果发现错误,可在评论区指正,感谢🙏
  • 🎉🎉欢迎持续关注!
    请添加图片描述

请添加图片描述

二叉搜索树

  • 🌈欢迎来到数据结构专栏~~搜索二叉树
    • 一. 概念
    • 二. 基本操作
      • 🌈查找元素 Search
      • 🌈插入 Insert
      • 🌈删除 Delete
    • 三. 进阶操作 ~ 递归写法
      • 🥑递归查找 Search
      • 🥑递归插入 Insert
      • 🥑递归删除 Delete
    • 四. 性能分析
    • 五. 二叉搜索树的应用
      • 💦Key模型
      • 💦Key- Value模型
    • 附源码
      • `BinarySearchTree.h`
      • `test.c`
  • 📢写在最后

请添加图片描述

一. 概念

二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:

  • 若它的左子树不为空,则 左子树上所有节点的值都小于根节点的值

  • 若它的左子树不为空,则 右子树上所有节点的值都大于根节点的值

  • 它的左右子树也分别为二叉搜索树

在这里插入图片描述

二. 基本操作

🌈查找元素 Search

在这里插入图片描述

  • 从根开始比较,查找,比根大则往右边走查找,比根小则往左边走查找
  • 最多查找高度次,走到到空,还没找到,这个值不存在
	//查找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;}}}

🌈插入 Insert

思路如下:

  1. 树为空,则直接新增节点,直接插入root指针即可
  2. 因为不能插入重复的元素,并且每次插入都是要定位到空节点的位置;定义一个 cur 从root开始,插入的元素比当前位置元素小就往左走,比当前位置元素大就往右走,直到为空;
  3. 再定义一个parent记录 cur的前一个位置,最后判断cur是parent的左子树or右子树

动画演示:

请添加图片描述

    //相同的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;}

🌈删除 Delete

删除有三种情况:

  1. 无牵无挂型:如果是叶子结点,直接置空并链接到空指针
  2. 独生子女型:只有一个子节点,删除自己本身,并链接子节点和父节点
  3. 替罪羊型:寻找出右子树最小节点左子树最大节点,其节点可以作为交换节点和删除结点进行交换,交换后删除交换节点、交换节点要么没有孩子,要么只有一个孩子可以直接删除

在这里插入图片描述

其实在代码层面第一种情况可以归类到第二种情况(没有左孩子,就链接上右孩子)

在代码层面实际上是四种:

  1. 左为空
  2. 右为空
  3. 左右都为空(细节很多看下图)
  4. cur是跟节点位置(特殊情况移动root

在这里插入图片描述

	//删除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{//找到了,开始删除,有三种情况//1、左为空//2、右为空//3、左右都不为空//4、删除跟root 要移动root(特殊情况)if (cur->_left == nullptr){if (cur == _root){_root = cur->_right;}else{if (cur == parent->_left){parent->_left = cur->_right;}else{parent->_right = cur->_right;}}delete cur;cur = nullptr;}else if (cur->_right == nullptr){if (_root == cur){_root = cur->_left;}else{if (cur == parent->_left){parent->_left = cur->_left;}else{parent->_right = cur->_left;}}delete cur;cur = nullptr;}else{//左右都不为空  ——  替换法删除//找到右树最小节点进行替换Node* min = cur->_right;Node* minparent = nullptr;while (min->_left){minparent = min;min = min->_left;}swap(cur->_key, min->_key);//注意min的右子树还有连接节点的可能//和判断min在哪边的问题?if (minparent->left = min){minparent->_left = min->_right;}else{minparent->_right = min->_right;}delete min;}return true;}}return false;}

三. 进阶操作 ~ 递归写法

🥑递归查找 Search

唤起递归的记忆吧

	void _FindR(Node* root, const K& key){//根为空的情况下if (root == nullptr){return false;}if (root->_key < key){return _FindR(root->_right);}else if (root->_key > key){return _FindR(root->_left);}else{return true;}}

🥑递归插入 Insert

要注意,这里有“神之一手

在这里插入图片描述

在这里插入图片描述

	void _InsertR(Node*& root, const K& key){if (root == nullptr){root = new Node(key);return true;}if (root->_key < key){return _InsertR(root->_right, key);}else if (root->_key > key){return _InsertR(root->_left, key);}else{return false;}}

🥑递归删除 Delete

神之操作:root = root->_left

在这里插入图片描述

在这里插入图片描述

	bool _EraseR(Node*& root, const K& key){if (root == nullptr){return false;}if (root->_key < key){return _EraseR(root->_right, key);}else if (root->_key > key){return _EraseR(root->_left, 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(min->_key, root->_key);return _Erase(root->_right)}delete del;return true;}}

四. 性能分析

最优情况下,二叉搜索树为完全二叉树(或者接近完全二叉树),其平均比较次数为:log2Nlog_2 Nlog2​N
最差情况下,二叉搜索树退化为单支树(或者类似单支),其平均比较次数为:N2\frac{N}{2}2N​

在这里插入图片描述

五. 二叉搜索树的应用

💦Key模型

key的搜索模型,判断关键字在不在

  • 刷卡进宿舍楼
  • 检测一篇英文文档中单词拼写是否正确

以上都是把全部相关的资料都插入到一颗搜索树中,然后开始寻找,判断在不在

💦Key- Value模型

KV模型:每一个关键码key,都有与之对应的值Value,即的键值对

  • 比如英汉词典就是英文与中文的对应关系,通过英文可以快速找到与其对应的中文,英文单词与其对应的中文就构成一种键值对
  • 再比如统计单词次数,统计成功后,给定单词就可快速找到其出现的次数,单词与其出现次数就是就构成一种键值对
// 改造二叉搜索树为KV结构
template
struct BSTNode
{BSTNode(const K& key = K(), const V& value = V()): _pLeft(nullptr), _pRight(nullptr), _key(key), _Value(value){}BSTNode* _pLeft;BSTNode* _pRight;K _key;V _value
};
template
class BSTree
{typedef BSTNode Node;typedef Node* PNode;
public:BSTree() : _pRoot(nullptr) {}PNode Find(const K& key);bool Insert(const K& key, const V& value)bool Erase(const K& key)
private:PNode _pRoot;
}void TestBSTree3()
{// 输入单词,查找单词对应的中文翻译BSTree dict;dict.Insert("string", "字符串");dict.Insert("tree", "树");dict.Insert("left", "左边、剩余");dict.Insert("right", "右边");dict.Insert("sort", "排序");// 插入词库中所有单词string str;while (cin >> str){BSTreeNode* ret = dict.Find(str);if (ret == nullptr){cout << "单词拼写错误,词库中没有这个单词:" << str << endl;}else{cout << str << "中文翻译:" << ret->_value << endl;}}
}

附源码

BinarySearchTree.h

#pragma once#include
using namespace std;namespace Key
{templatestruct BSTreeNode{BSTreeNode* _left;BSTreeNode* _right;K _key;BSTreeNode(const K& key):_left(nullptr), _right(nullptr), _key(key){}};//class BinarySearchTreetemplateclass BSTree{typedef BSTreeNode Node;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;}}}//删除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{//找到了,开始删除,有三种情况//1、左为空//2、右为空//3、左右都不为空//4、删除跟root 要移动root(特殊情况)if (cur->_left == nullptr){if (cur == _root){_root = cur->_right;}else{if (cur == parent->_left){parent->_left = cur->_right;}else{parent->_right = cur->_right;}}delete cur;cur = nullptr;}else if (cur->_right == nullptr){if (_root == cur){_root = cur->_left;}else{if (cur == parent->_left){parent->_left = cur->_left;}else{parent->_right = cur->_left;}}delete cur;cur = nullptr;}else{//左右都不为空  ——  替换法删除//找到右树最小节点进行替换Node* min = cur->_right;Node* minparent = nullptr;while (min->_left){minparent = min;min = min->_left;}swap(cur->_key, min->_key);//注意min的右子树还有连接节点的可能//和判断min在哪边的问题?if (minparent->_left = min){minparent->_left = min->_right;}else{minparent->_right = min->_right;}delete min;}return true;}}return false;}//这样就能避免this指针问题,因为递归必须显示给参数void InOrder(){_InOrder(_root);//这样就可以使用_rootcout << endl;}/// ///// 递归写法bool FindR(const K& key){return _FindR(_root, key);}bool InsertR(const K& key){return _InsertR(_root, key);}bool EraseR(const K& key){return _EraseR(_root, key);}~BSTree(){_Destory(_root);}//BSTree()//{}//C++11的用法:强制编译器生成默认构造BSTree() = default;//拷贝构造BSTree(const BSTree& t){_root = _Copy(t._root);}//t2 = t1BSTree& operator = (BSTree t){swap(_root, t._root);return *this;}private: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;}void _Destory(Node*& root){if (root == nullptr){return;}_Destory(root->_left);_Destory(root->_right);delete root;root = nullptr;}bool _EraseR(Node*& root, const K& key){if (root == nullptr){return false;}if (root->_key < key){return _EraseR(root->_right, key);}else if (root->_key > key){return _EraseR(root->_left, 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(min->_key, root->_key);return _Erase(root->_right);//防止找不到key的情况}delete del;return true;}}void _InsertR(Node*& root, const K& key){if (root == nullptr){root = new Node(key);return true;}if (root->_key < key){return _InsertR(root->_right, key);}else if (root->_key > key){return _InsertR(root->_left, key);}else{return false;}}void _FindR(Node* root, const K& key){//根为空的情况下if (root == nullptr){return false;}if (root->_key < key){return _FindR(root->_right, key);}else if (root->_key > key){return _FindR(root->_left, key);}else{return true;}}void _InOrder(Node* root){if (root == nullptr){return;}_InOrder(root->_left);cout << root->_key << " ";_InOrder(root->_right);}private:Node* _root = nullptr;};void TestBSTree1(){BSTree t;int a[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13 };for (auto e : a){t.Insert(e);}//排序+去重t.InOrder();t.Erase(3);t.InOrder();t.Erase(6);t.InOrder();}void TestBSTree2(){BSTree t;int a[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13 };for (auto e : a){t.Insert(e);}BSTree copy = t;copy.InOrder();t.InOrder();}
}

test.c

#include "BinarySearchTree.h"int main()
{//TestBSTree1();/*TestBSTree2();*/TestBSTree3();return 0;
}

📢写在最后

多多更新

相关内容

热门资讯

北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
阿西吧是什么意思 阿西吧相当于... 即使你没有受到过任何外语培训,你也懂四国语言。汉语:你好英语:Shit韩语:阿西吧(아,씨발! )日...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...