数据结构——二叉搜索树详解

一、二叉搜索树定义

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

1.非空左子树上所有节点的值都小于根节点的值

2.非空右子树上所有节点的值都大于根节点的值

3.左右子树也都为二叉搜索树。

如下图所示:

二、二叉搜索树的操作

二叉搜索树结构:

template<class k>struct BSTreeNode {typedef BSTreeNode<k> Node;Node* _left;//左子树指针Node* _right;//右子树指针k _key;//节点数据};

2.1 二叉搜索树的查找

1.从根开始比较,查找,比根大则往右边走查找,比根小则往左边走查找。

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;}}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;}}

2.2 二叉搜索树的插入

插入的具体过程如下:

1. 树为空,则直接新增节点,赋值给root指针。

2. 树不空,按二叉搜索树性质查找插入位置,插入新节点。

插入key值为9的节点,如下图所示:

非递归插入:

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;//key值已存在}}cur = new Node(key);//以key值开辟节点if (parent->_key < key) {//新节点>父亲节点,在父亲的右边parent->_right = cur;}if (parent->_key > key) {//新节点<父亲节点,在父亲的左边parent->_left = cur;}return true;}

递归插入:

bool _InsertR(Node*& root, const k& key){//递归查找传参指针的引用,修改原指针,让原指针直接指向当前节点if (root == nullptr)//root节点为空,开辟新结点{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;}}

2.3 二叉搜索树的删除

首先查找元素是否在二叉搜索树中,如果不存在,则返回, 否则要删除的结点可能分下面四种情 况:

1. 要删除的结点无孩子结点 2. 要删除的结点只有左孩子结点 3. 要删除的结点只有右孩子结点 4.要删除的结点有左、右孩子结点

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

1.删除该结点且使被删除节点的双亲结点指向被删除节点的左孩子结点--直接删除。

2.删除该结点且使被删除节点的双亲结点指向被删除结点的右孩子结点--直接删除。 

3.查找删除结点的右子树的最左节点或者左子树的最右节点(距离删除节点最近,替换后不影响其他节点位置),用它的值填补到被删除节点中,再来处理该结点的删除问题--替换法删除。下面用右子树的最左节点进行替换。

非递归删除:

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 (cur == parent->_right){//判断删除节点是parent的left还是rightparent->_right = cur->_right;}else{parent->_left = cur->_right;}}delete cur;return true;}else if (cur->_right == nullptr){if (cur == _root){_root = cur->_left;}else{if (cur == parent->_right){//判断删除节点是parent的left还是rightparent->_right = cur->_left;}else{parent->_left = cur->_left;}}delete cur;return true;}else {//左右子树均有节点,将删除节点替换为其右子树的最左节点(左子树的最右节点)Node* rightMinParent = cur;//考虑右子树的根节点为最左节点,不进循环Node* rightMin = cur->_right;while (rightMin->_left) {rightMinParent = rightMin;rightMin = rightMin->_left;}cur->_key = rightMin->_key;if (rightMin == rightMinParent->_left) {rightMinParent->_left = rightMin->_right;//rightMin(右子树最左)为替换节点,替换后删除,rightMinParent的左或右指向rightMin的right}else {rightMinParent->_right = rightMin->_right;}delete rightMin;return true;}}}return false;}

非递归删除:

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->_right == nullptr){//删除节点右为空,将左节点赋值给要删除的节点root = root->_left;}else if (root->_left == nullptr){//删除节点左为空,将右节点赋值给要删除的节点root = root->_right;}else{Node* rightMin = root->_right;while (rightMin->_left){rightMin = rightMin->_left;}swap(root->_key, rightMin->_key);//交换删除节点和右子树节点的key值return _EraseR(root->_right, key);//交换后,删除节点为右子树最左节点,走left为空时情况}delete del;return true;}}

三、二叉搜索树的模拟实现

//二叉搜索树的模拟实现
namespace rab {template<class k>struct BSTreeNode {typedef BSTreeNode<k> Node;Node* _left;Node* _right;k _key;BSTreeNode(const k& key):_left(nullptr), _right(nullptr), _key(key){}};template<class k>class BSTree {typedef BSTreeNode<k> Node;public://强制生成构造函数BSTree() = default;//拷贝构造函数BSTree(const BSTree<k>& t){_root = Copy(t._root);//传入拷贝对象的根节点}//析构函数~BSTree() {Destroy(_root);}//插入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;//key值已存在}}cur = new Node(key);if (parent->_key < key) {parent->_right = cur;}if (parent->_key > key) {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{//删除节点左边为空if (cur->_left == nullptr){if (cur == _root){//删除根节点_root = cur->_right;}else{if (cur == parent->_right){//判断删除节点是parent的left还是rightparent->_right = cur->_right;}else{parent->_left = cur->_right;}}delete cur;return true;}else if (cur->_right == nullptr){if (cur == _root){_root = cur->_left;}else{if (cur == parent->_right){//判断删除节点是parent的left还是rightparent->_right = cur->_left;}else{parent->_left = cur->_left;}}delete cur;return true;}else {//左右子树均有节点,将删除节点替换为其右子树的最左节点(左子树的最右节点)Node* rightMinParent = cur;//考虑右子树的根节点为最左节点,不进循环Node* rightMin = cur->_right;while (rightMin->_left) {rightMinParent = rightMin;rightMin = rightMin->_left;}cur->_key = rightMin->_key;if (rightMin == rightMinParent->_left) {rightMinParent->_left = rightMin->_right;//rightMin(右子树最左)为替换节点,替换后删除,rightMinParent的左或右指向rightMin的right}else {rightMinParent->_right = rightMin->_right;}delete rightMin;return true;}}}return false;}/二叉搜索树的递归实现//递归查找bool FindR(const k& key){return _FindR(key);}//递归插入bool InsertR(const k& key){return _InsertR(_root, key);}//递归删除节点bool EraseR(const k& key){return _EraseR(_root, key);}//中序遍历void InOrder(){_InOrder(_root);cout << endl;}private://递归中序遍历void _InOrder(Node* root){if (root == nullptr)return;_InOrder(root->_left);cout << root->_key << " ";_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);newRoot->_left = Copy(root->_left);newRoot->_right = Copy(root->_right);return newRoot;}//递归查找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;}}//递归插入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 _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->_right == nullptr){root = root->_left;}else if (root->_left == nullptr){root = root->_right;}else{Node* rightMin = root->_right;while (rightMin->_left){rightMin = rightMin->_left;}swap(root->_key, rightMin->_key);return _EraseR(root->_right, key);//交换后,删除节点为右子树最左节点,走left为空时情况}delete del;return true;}}private:Node* _root = nullptr;};}

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

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

相关文章

浏览器扩展程序增加 vue_dev_tools 调试工具

1、引言 在做 Vue 项目的开发时&#xff0c;我们经常需要在页面上调试&#xff0c;接下来介绍如何在浏览器扩展程序增加 vue_dev_tools 调试工具。 Download the Vue Devtools extension for a better development experience 翻译&#xff1a;下载Vue Devtools扩展以获得更好…

1.6.1 变换

我们要想改变物体的位置&#xff0c;现有解决办法是&#xff0c;每一帧改变物体的顶点并且重配置缓冲区从而使物体移动&#xff0c;但是这样太繁琐&#xff0c;更好的解决方式是使用矩阵&#xff08;Matrix&#xff09;来更好的变换&#xff08;Transform&#xff09;一个物体。…

数据结构——快速排序的三种方法和非递归实现快速排序

数据结构——快速排序的三种方法和非递归实现快速排序&#xff08;升序&#xff09; 快速排序的单趟排序hoare法挖坑法前后指针法 快速排序的实现key基准值的选取快速排序代码快速排序的优化 快速排序&#xff08;非递归&#xff09; 快速排序的单趟排序 hoare法 思路:从给定…

后端前行Vue之路(二):模版语法之插值与指令

1.概述 Vue.js的模板语法是一种将Vue实例的数据绑定到HTML文档的方法。Vue的模板语法是一种基于HTML的扩展&#xff0c;允许开发者将Vue实例中的数据绑定到HTML元素&#xff0c;以及在HTML中使用一些简单的逻辑和指令。Vue.js 基于 HTML 的模板语法允许开发者声明式地将 DOM 绑…

Windows11系统缺少解决办法

一.缺少msvcp120.dll 下载Mircrosoft Visual C 2015等系统关键组件 Microsoft Visual C 2015-2022 Redistributable (x86) - 14.34.31931 Installation Error etc.. - Microsoft Q&A 二.缺少python27.dll 重新下载python2.7进行安装(选择Windows x86-64 MSI installer)…

三级等保建设技术方案-Word

1信息系统详细设计方案 1.1安全建设需求分析 1.1.1网络结构安全 1.1.2边界安全风险与需求分析 1.1.3运维风险需求分析 1.1.4关键服务器管理风险分析 1.1.5关键服务器用户操作管理风险分析 1.1.6数据库敏感数据运维风险分析 1.1.7“人机”运维操作行为风险综合分析 1.2…

物联网(IoT)常用的通信协议

物联网&#xff08;IoT&#xff09;的通信协议是物联网设备之间交换数据的规则和标准。这些协议对于确保设备能够有效、安全地通信至关重要。下面是物联网通信协议的概述&#xff1a; 1. MQTT&#xff08;消息队列遥测传输&#xff09; 概述&#xff1a;MQTT是一种轻量级的发…

IP如何异地共享文件?

【天联】 组网由于操作简单、跨平台应用、无网络要求、独创的安全加速方案等原因&#xff0c;被几十万用户广泛应用&#xff0c;解决了各行业客户的远程连接需求。采用穿透技术&#xff0c;简单易用&#xff0c;不需要在硬件设备中端口映射即可实现远程访问。 异地共享文件 在…

php程序员如何成为编程高手

php程序员如何成为编程高手? 成为一名编程高手&#xff0c;无论是对于PHP程序员还是其他编程语言的开发者来说&#xff0c;都需要持续学习、实践和不断挑战自己。下面是一些具体的建议&#xff0c;帮助PHP程序员提升技术水平&#xff0c;向编程高手迈进&#xff1a; 1. 深入…

腾讯云2核2G服务器CVM S5和轻量应用服务器优惠价格

腾讯云2核2G服务器多少钱一年&#xff1f;轻量服务器61元一年&#xff0c;CVM 2核2G S5服务器313.2元15个月&#xff0c;腾讯云2核2G服务器优惠活动 txyfwq.com/go/txy 链接打开如下图&#xff1a; 腾讯云2核2G服务器价格 轻量61元一年&#xff1a;轻量2核2G3M、3M带宽、200GB月…

AXI Memory Mapped to PCI Express 学习笔记(五)—— Test Bench

本文包含有关Vivado Design Suite环境中提供的测试平台&#xff08;Test Bench&#xff09;的信息。 一、Endpoint的Root Port模型测试平台 PCI Express Root Port Model是一个强大的测试平台环境&#xff0c;它提供了一个测试程序接口&#xff0c;可以与提供的PIO设计&#…

洛谷_P4995 跳跳!_python写法

P4995 跳跳&#xff01; - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) n int(input()) data list(map(int,input().split())) data.append(0) data.sort()sum 0 l 0 r len(data)-1 flag 1 while l<r:sum (data[l]-data[r])**2if flag:l 1flag 0else:r - 1flag 1…

LinkedList讲解指南

咦咦咦&#xff0c;各位小可爱&#xff0c;我是你们的好伙伴——bug菌&#xff0c;今天又来给大家普及Java SE相关知识点了&#xff0c;别躲起来啊&#xff0c;听我讲干货还不快点赞&#xff0c;赞多了我就有动力讲得更嗨啦&#xff01;所以呀&#xff0c;养成先点赞后阅读的好…

教育数字化调研团走进锐捷,共议职业教育数字化转型新思路

为贯彻落实国家教育数字化战略行动部署和2024年全国教育工作会议精神,加快推进职业教育数字化转型与发展,梳理职业教育数字化转型的现状、问题及发展趋势,并总结展示职业教育数字化转型的好经验、好做法,培育职业教育数字化创新成果,推动数字技术与职业教育深度融合、提高数字化…

SpringBoot + Vue 是否可以不分离前后端?

Spring Boot 和 Vue.js 是两个非常流行的技术栈&#xff0c;分别用于构建后端和前端应用程序。在传统的开发中&#xff0c;通常会将它们分离开发&#xff0c;即后端和前端分别独立开发、部署和维护。然而&#xff0c;你也可以选择不分离前后端&#xff0c;将它们整合在一起开发…

ensp的PPP实验报告

实验要求&#xff1a; 1、R1和R2使用PPP链路直连&#xff0c;R2和R3把2条PPP链路捆绑为PPP MP直连 2、按照图示配置IP地址 3、R2对R1的PPP进行单向chap验证 4、R2和R3的PPP进行双向chap验证 1、配置ip地址 R1&#xff1a; [R1] int Serial 3/0/0 [Rl-Seria13/0/0] ip add 192…

【Java 面试题】instanceof 关键字的作用

instanceof 关键字的作用&#xff1f; instanceof关键字是Java中的一个运算符&#xff0c;用于检查一个对象是否是某个类的实例&#xff0c;或者是否实现了某个接口。其作用可以概括如下&#xff1a; 判断对象类型&#xff1a; 使用instanceof可以判断一个对象是否是某个类的实…

机器学习——聚类算法-DBSCAN

机器学习——聚类算法-DBSCAN DBSCAN&#xff08;Density-Based Spatial Clustering of Applications with Noise&#xff09;是一种基于密度的聚类算法&#xff0c;可以发现任意形状的簇&#xff0c;并能有效处理噪声数据。本文将介绍DBSCAN算法的核心概念、算法流程、优缺点…

for in 和 for of的区别

区别一&#xff1a;for in 和 for of 都可以循环数组&#xff0c;for in 输出的是数组的index下标&#xff0c;而for of 输出的是数组的每一项的值。 const arr [1,2,3,4,5]// for ... in for (const key in arr){console.log(key) // 输出 0,1,2,3,4}// for ... of for (con…

C语言基于TCP的多线程服务器

核心思想: 1 在无限循环中 accpet()后 创建线程 2 预防多线程下的数据竞态: accept()返回的client_sockfd 是否可以直接填入pthread_create()作为创建线程的参数? 我们观察到 while(1)中并没有阻塞的函数,假设accept()的速度足够快 他会不断地更新client_sockfd的值,而传…