搜索二叉树(二叉树进阶)

目录

1.二叉搜索树

1.1二叉搜索树概念

1.2二叉搜索树操作

2.3二叉搜索树的实现

2.4二叉搜索树的应用

2.5二叉搜索树的性能分析

1.二叉搜索树

1.1二叉搜索树概念

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

1.若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
2.若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
3.它的左右子树也分别为二叉搜索树

1.2二叉搜索树操作

int a[] = {8, 3, 1, 10, 6, 4, 7, 14, 13};

1. 二叉搜索树的查找
a、从根开始比较,查找,比根大则往右边走查找,比根小则往左边走查找。
b、最多查找高度次,走到到空,还没找到,这个值不存在。(时间复杂度最坏是O(N),比如所有节点都在右子树或者左子树上面。)

2. 二叉搜索树的插入
插入的具体过程如下:
a. 树为空,则直接新增节点,赋值给root指针
b. 树不空,按二叉搜索树性质查找插入位置,插入新节点

3.二叉搜索树的删除

首先查找元素是否在二叉搜索树中,如果不存在,则返回, 否则要删除的结点可能分下面四种情况:
a. 要删除的结点无孩子结点
b. 要删除的结点只有左孩子结点
c. 要删除的结点只有右孩子结点
d. 要删除的结点有左、右孩子结点

看起来有待删除节点有4中情况,实际情况a可以与情况b或者c合并起来,因此真正的删除过程如下:

情况b:删除该结点且使被删除节点的双亲结点指向被删除节点的左孩子结点--直接删除
情况c:删除该结点且使被删除节点的双亲结点指向被删除结点的右孩子结点--直接删除
情况d:在它的右子树中寻找中序下的第一个结点(关键码最小),用它的值填补到被删除节点中,再来处理该结点的删除问题--替换法删除

2.3二叉搜索树的实现

//BinarySearchTree.h
#define _CRT_SECURE_NO_WARNINGS 1#include <iostream>
#include <utility>using namespace std;template<class K>
struct BSTreeNode
{BSTreeNode<K>* _left;BSTreeNode<K>* _right;K _key;BSTreeNode(const K& key):_left(nullptr), _right(nullptr), _key(key){}
};template <class K>
class BSTree
{typedef BSTreeNode<K> Node;
public:BSTree():_root(nullptr){}BSTree(const BSTree<K>& t){_root = Copy(t._root);}~BSTree(){Destroy(_root);}//现代写法:因为operator=是传值调用,过程中调用了拷贝构造形成了t//然后通过swap交换他们的内部资源,最后引用返回延长了内部资源的生命周期。//防止t出了作用域被销毁时把内部给释放了BSTree<K>& operator=(BSTree<K> t){swap(_root, t._root);return *this;}bool Insert(const K& key){if (_root == nullptr){_root = new Node(key);return true;}Node* cur = _root;Node* parent = nullptr;while (cur){if (cur->_key < key){parent = cur;cur = cur->_right;}else if (cur->_key > key){parent = cur;cur = cur->_left;}elsereturn false;}cur = new Node(key);if (parent->_key < key)parent->_right = cur;elseparent->_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 //找到了{if (cur->_left == nullptr)//左为空{if (cur == _root)_root = cur->_right;else{if (parent->_right == cur)parent->_right = cur->_right;elseparent->_left = cur->_right;}}else if (cur->_right == nullptr)//右为空{if (cur == _root)_root = cur->_left;else{if (parent->_right == cur)parent->_right = cur->_left;elseparent->_left = cur->_left;}}else//左右不为空{Node* parent = cur;//找替代节点 -- 左子树最大节点Node* leftMax = cur->_left;while (leftMax->_right){parent = leftMax;leftMax = leftMax->_right;}swap(cur->_key, leftMax->_key);if (parent->_left == leftMax)parent->_left = leftMax->_left;elseparent->_right = leftMax->_left;cur = leftMax;}delete cur;return true;}}return false;}//二叉搜索树中序遍历出来是有序的void InOrder(){_InOrder(_root);cout << 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);}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 Destroy(Node* root){if (root == nullptr)return;Destroy(root->_left);Destroy(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* leftMax = root->_left;while (leftMax->_right){leftMax = leftMax->_right;}swap(root->_key, leftMax->_key);return _EraseR(root->_left, key);}delete del;return true;}}bool _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;}}bool _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;
};
//main.cpp
#define _CRT_SECURE_NO_WARNINGS 1#include"BinarySearchTree.h"//迭代版
void TestBSTree1()
{int a[] = { 8,3,1,10,6,4,7,14,13 };BSTree<int> t;for (auto e : a){t.Insert(e);}BSTree<int> t1;t1 = t;t.InOrder();t.Erase(4);t.InOrder();t.Erase(6);t.InOrder();t.Erase(7);t.InOrder();t.Erase(3);t.InOrder();for (auto e : a){t.Erase(e);t.InOrder();}
}//递归版
void TestBSTree2()
{int a[] = { 8,3,1,10,6,4,7,14,13 };BSTree<int> t;for (auto e : a){t.InsertR(e);}BSTree<int> t1(t);t1 = t;t.InOrder();t1.InOrder();t.EraseR(4);t.InOrder();
}int main()
{TestBSTree1();return 0;
}

2.4二叉搜索树的应用

1. K模型(key的搜索模型):K模型即只有key作为关键码,结构中只需要存储Key即可,关键码即为需要搜索到的值。
比如:给一个单词word,判断该单词是否拼写正确,具体方式如下:
以词库中所有单词集合中的每个单词作为key,构建一棵二叉搜索树
在二叉搜索树中检索该单词是否存在,存在则拼写正确,不存在则拼写错误。

//main.cpp
void TestBSTree1()
{bit::BSTree<string, string> dict;dict.InsertR("insert", "插入");dict.InsertR("sort", "排序");dict.InsertR("right", "右边");dict.InsertR("date", "日期");string str;while (cin >> str){bit::BSTreeNode<string, string>* ret = dict.FindR(str);if (ret){cout << ret->_value << endl;}else{cout << "无此单词" << endl;}}
}

2. KV模型(key/value的搜索模型):每一个关键码key,都有与之对应的值Value,即<Key, Value>的键值对。该种方式在现实生活中非常常见:
比如英汉词典就是英文与中文的对应关系,通过英文可以快速找到与其对应的中文,英文单词与其对应的中文<word, chinese>就构成一种键值对;
再比如统计单词次数,统计成功后,给定单词就可快速找到其出现的次数,单词与其出现次数就是<word, count>就构成一种键值对

//main.cpp
void TestBSTree2()
{string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜","苹果", "香蕉", "苹果", "香蕉", };bit::BSTree<string, int> countTree;for (auto& str : arr){auto ret = countTree.FindR(str);if (ret == nullptr){countTree.InsertR(str, 1);}else{ret->_value++;}}countTree.InOrder();
}

使用递归或者迭代写的二叉搜索树进行以下修改:

//BinarySearchTree.h
#define _CRT_SECURE_NO_WARNINGS 1#pragma once
#include <iostream>
#include <utility>using namespace std;namespace bit
{template<class K, class V>struct BSTreeNode{BSTreeNode<K, V>* _left;BSTreeNode<K, V>* _right;K _key;V _value;//添加一个成员,所以要多加一个模板参数BSTreeNode(const K& key, const V& value):_left(nullptr), _right(nullptr), _key(key), _value(value){}};template <class K, class V>class BSTree{typedef BSTreeNode<K, V> Node;public:BSTree():_root(nullptr){}//二叉搜索树中序遍历出来是有序的void InOrder(){_InOrder(_root);cout << endl;}Node* FindR(const K& key){return _FindR(_root, key);}bool InsertR(const K& key, const V& value){return _InsertR(_root, key, value);}bool EraseR(const K& key){return _EraseR(_root, key);}private: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* leftMax = root->_left;while (leftMax->_right){leftMax = leftMax->_right;}swap(root->_key, leftMax->_key);return _EraseR(root->_left, key);}delete del;return true;}}bool _InsertR(Node*& root, const K& key, const V& value){if (root == nullptr){root = new Node(key, value);return true;}if (root->_key < key){return _InsertR(root->_right, key, value);}else if (root->_key > key){return _InsertR(root->_left, key, value);}else{return false;}}Node* _FindR(Node* root, const K& key){if (root == nullptr)return nullptr;if (root->_key < key){return _FindR(root->_right, key);}else if (root->_key > key){return _FindR(root->_left, key);}else{return root;}}void _InOrder(Node* root){if (root == nullptr){return;}_InOrder(root->_left);cout << root->_key << ":" << root->_value << endl;_InOrder(root->_right);}private:Node* _root;};
}

2.5二叉搜索树的性能分析

插入和删除操作都必须先查找,查找效率代表了二叉搜索树中各个操作的性能。

对有n个结点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树平均查找长度是结点在二叉搜索树的深度的函数,即结点越深,则比较次数越多。但对于同一个关键码集合,如果各关键码插入的次序不同,可能得到不同结构的二叉搜索树:

最优情况下,二叉搜索树为完全二叉树(或者接近完全二叉树),其平均比较次数为:O(logN)

最差情况下,二叉搜索树退化为单支树(或者类似单支),其平均比较次数为:O(N)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/61412.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

定时器

定时器 定时器是什么 定时器也是软件开发中的一个重要组件. 类似于一个 “闹钟”. 达到一个设定的时间之后, 就执行某个指定好的代码. 定时器是一种实际开发中非常常用的组件. 比如网络通信中, 如果对方 500ms 内没有返回数据, 则断开连接尝试重连. 比如一个 Map, 希望里面的…

MAUI+Blazor:windows 打包踩坑

文章目录 前言MSIX安装文件如何发布选择Windows平台旁加载自定义签名版本号安装 总结 前言 最近打算研究一下MAUIBlazor&#xff0c;争取在今年年底之前彻底搞懂MAUIBlazor的安装模式&#xff0c; MSIX安装文件 Windows 4种安装程序格式MSI&#xff0c;EXE、AppX和MSIX优缺点…

uni-app之app上传pdf类型文件

通过阅读官方文档发现&#xff0c;uni.chooseFile在app端不支持非媒体文件上传&#xff1b; 可以使用这个插件&#xff0c;验证过可以上传pdf&#xff1b;具体使用可以去看文档 插件地址 就是还是会出现相机&#xff0c;这个可能需要自己解决下 实现功能&#xff1a;上传只能上…

详解Linux文本三剑客

目录 一、grep 1.什么是grep? 2.如何使用&#xff1f; 3.正则 二、sed 1.认识sed? 2.如何使用&#xff1f; 三、awk&#xff08;重点&#xff09; 1.awk变量 1.1内置变量 1.2自定义变量 2.awk数组 四、经典实战案例 案例一&#xff1a;筛选IPv4地址 案例二&am…

Oracle 开发篇+Java通过DRCP访问Oracle数据库

标签&#xff1a;DRCP、Database Resident Connection Pooling、数据库驻留连接池释义&#xff1a;DRCP&#xff08;全称Database Resident Connection Pooling&#xff09;数据库驻留连接池&#xff08;Oracle自己的数据库连接池技术&#xff09; ★ Oracle开启并配置DRCP sq…

Win11 VS2022 配置CGAL-5.6

由于项目要用到几何库CGAL&#xff0c;因此做了配置。采用的是官方文档中的“Installing from the Source Archive”方式。 1. 下载安装CGAL &#xff08;1&#xff09;CGAL-5.6.zip下载地址&#xff1a;Releases CGAL/cgal GitHub 下载下图所示的两个文件。 &#xff08…

竞赛项目 深度学习实现语义分割算法系统 - 机器视觉

文章目录 1 前言2 概念介绍2.1 什么是图像语义分割 3 条件随机场的深度学习模型3\. 1 多尺度特征融合 4 语义分割开发过程4.1 建立4.2 下载CamVid数据集4.3 加载CamVid图像4.4 加载CamVid像素标签图像 5 PyTorch 实现语义分割5.1 数据集准备5.2 训练基准模型5.3 损失函数5.4 归…

图像变形之IDW和RBF(附源码)

IDW原理 IDW(Inverse distance weighted interpolation)图像变形算法根据给定的控制点对和控制点对的位移矢量(方向和距离)&#xff0c;计算控制点对周围像素的反距离加权权重影响&#xff0c;从而实现图像每一个像素点的位移&#xff0c; 假设输入控制点pi&#xff0c;其对应…

Vue中data变量使用的注意事项

因为在Vue中&#xff0c;data中的属性往往都是用于双向绑定&#xff0c;所以Vue会对其有劫持&#xff0c;所以我们在对data属性进行操作时&#xff0c;尽量不要对其直接操作&#xff0c;比如下面代码&#xff1a; export default {data() {return {list: []}},methods: {init(…

Unity游戏源码分享-儿童益智数学大脑训练游戏

Unity游戏源码分享-儿童益智数学大脑训练游戏 5秒内选择答案 项目下载地址&#xff1a;https://download.csdn.net/download/Highning0007/88198773

JavaFx异常: Not on FX application thread; currentThread = Timer-0

我的定时器任务中有两个控件&#xff1a; FXML TextArea Display; FXML Label Label_Display; 执行下方代码会抛出&#xff1a;Exception in thread "Timer-0" java.lang.IllegalStateException: Not on FX application thread; currentThread Timer-0 Timer_tas…

xcode打包导出ipa

转载&#xff1a;xcode打包导出ipa 目录 转载&#xff1a;xcode打包导出ipa 第一步&#xff1a;注册苹果开发者账号 第二步&#xff1a;下载APP Uploader 第三步&#xff1a;使用xcode打包导出ipa文件&#xff0c;供其他人内测 众所周知&#xff0c;在开发苹果应用时需要使…