【数据结构】二叉搜索树——二叉搜索树的概念和介绍、二叉搜索树的简单实现、二叉搜索树的增删查改

文章目录

  • 二叉搜索树
    • 1. 二叉搜索树的概念和介绍
    • 2. 二叉搜索树的简单实现
      • 2.1二叉搜索树的插入
      • 2.2二叉搜索树的查找
      • 2.3二叉搜索树的遍历
      • 2.4二叉搜索树的删除
      • 2.5完整代码和测试

二叉搜索树

1. 二叉搜索树的概念和介绍

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

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

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

  (3)它的左右子树也分别为二叉搜索树

在这里插入图片描述

  二叉搜索树(Binary Search Tree)的每个节点包含三个属性:键(key)、左孩子(lchild)和右孩子(rchild)。 键用于存储节点的值,左孩子和右孩子分别指向左子树和右子树的根节点。

  二叉搜索树的结构类似于一个倒置的树,最底层的节点位于树的顶部,每个节点都有一个键值,并且每个节点的左子树上的所有节点的键值都小于该节点的键值,而右子树上的所有节点的键值都大于该节点的键值。这种结构使得二叉搜索树在处理有序数据时非常高效。

  基于以上性质,二叉搜索树在查找、插入和删除操作上具有较好的效率,时间复杂度为O(logn)。

  基于二叉搜索树的特殊结构,二叉搜索树的中序遍历(Inorder Traversal)按照左子树、根节点、右子树的顺序遍历二叉树,它会按照从大到小的顺序输出二叉树中的所有元素。

  以下这颗二叉搜索树的中序遍历结果:1 3 4 6 7 8 10 13 14
在这里插入图片描述
             

2. 二叉搜索树的简单实现

  和之前的二叉树实现类似,二叉搜索树的实现只需要在二叉树的实现基础上,将左右元素根据和根节点的大小,重新考虑排列顺序即可。

  定义二叉搜索树的节点:

//搜索二叉树创建节点
template<class K>
struct BSTreeNode
{//左右节点和节点值keyBSTreeNode<K>* _left;BSTreeNode<K>* _right;K _key;//二叉树节点构造函数BSTreeNode(const K& key):_left(nullptr), _right(nullptr), _key(key){}
};

  定义二叉搜索树类:

//创建二叉搜索树
template<class K>
class BSTree
{//便于书写BNode节点typedef BSTreeNode<K> BNode;public://二叉搜索树构造函数BSTree():_root(nullptr){}private:BNode* _root;
};

             

2.1二叉搜索树的插入

  二叉搜索树的插入过程可以分为以下步骤:

  (1)如果二叉搜索树为空,创建一个新的节点,并将待插入关键字放入新节点的数据域。将新节点作为根节点,左右子树均为空。

  (2)如果二叉搜索树非空,将待查找关键字与根节点关键字进行比较。如果小于根节点关键字,则将待查找关键字插入左子树中;如果大于根节点关键字,则将待查找关键字插入右子树中。

  重复上述步骤,直到找到合适的位置插入待插入的关键字。

  在插入过程中,需要保持二叉搜索树的性质:任意节点的左子树的所有节点的值都小于该节点的值,右子树的所有节点的值都大于该节点的值。如果插入后破坏了这种性质,需要进行调整。

在这里插入图片描述

  非递归的实现:

//二叉树插入节点
bool BSInsert(const K& key)
{//如果该树为空树,直接创建节点if (_root == nullptr){_root = new BNode(key);return true;}//找到二叉树所在的节点BNode* parent = nullptr;BNode* 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 BNode(key);if (parent->_key > key){parent->_left = cur;}else {parent->_right = cur;}return true;
}


  递归的实现:

bool _BSInsertR(BNode*& root, const K& key)//这里传&,直接可以得到地址
{if (root == nullptr){root = new BNode(key);return true;}if (root->_key < key){_BSInsertR(root->_right, key);}else if (root->_key > key){_BSInsertR(root->_left, key);}else{return false;}
}

             

2.2二叉搜索树的查找

  二叉搜索树的查找实现过程可以分为以下步骤:

  (1)将待查找关键字与根节点关键字进行比较。如果相等,则找到了目标关键字,查找结束。

  (2)如果待查找关键字小于根节点关键字,则将待查找关键字放入左子树中继续查找。

  (3)如果待查找关键字大于根节点关键字,则将待查找关键字放入右子树中继续查找。

  重复上述步骤,直到找到目标关键字,或者搜索到了空节点(表示未找到目标关键字)。

  在查找过程中,由于二叉搜索树是基于二分的思想,所以每次比较都可以排除一半的搜索空间,因此时间复杂度为O(logn)。

在这里插入图片描述

  非递归实现:

//查找二叉树的节点
bool BSFind(const K& key)
{BNode* cur = _root;while (cur){if (cur->_key > key){cur = cur->_left;}else if (cur->_key < key){cur = cur->_right;}else{return true;}}return false;
}

  递归实现:

bool _BSFindR(BNode* root, const K& key)
{if (root == nullptr){return false;}if (root->_key < key){return _BSFindR(root->_right, key);}else if (root->_key > key){return _BSFindR(root->_left, key);}else{return true;}
}

             

2.3二叉搜索树的遍历

  二叉搜索树常使用中序遍历,而不是前序遍历和后序遍历。

  因为中序遍历的特点是先访问左子树,然后访问根节点,最后访问右子树。 在访问左子树时,先访问左子树的左子树,再访问左子树的右子树;在访问右子树时,先访问右子树的左子树,再访问右子树的右子树。

  二叉搜索树中序遍历的结果是按照从小到大的顺序输出二叉搜索树中的所有元素。

  二叉搜索树的中序遍历过程可以分为以下步骤:

  (1)首先遍历左子树,访问左子树中的所有节点,并按照中序遍历的顺序递归地访问它们的右子树。

  (2)然后访问根节点。

  (3)最后遍历右子树,访问右子树中的所有节点,并按照中序遍历的顺序递归地访问它们的左子树。

  递归实现:

void _BSInOrder(BNode* root)
{if (root == nullptr){return;}_BSInOrder(root->_left);printf("%d ", root->_key);_BSInOrder(root->_right);
}

             

2.4二叉搜索树的删除

  二叉搜索树的删除操作可以按照以下步骤实现:

  (1)首先,找到要删除的节点。可以通过中序遍历或者二分查找等方式找到要删除的节点。

  (2)如果要删除的节点是叶子节点(没有子节点),直接将该节点从二叉搜索树中删除即可。

  (3)如果要删除的节点只有一个子节点,将该节点的子节点与其父节点相连,删除该节点即可。

  (4)如果要删除的节点有两个子节点,需要找到该节点的左子树中的最大节点(或者右子树中的最小节点),用该节点代替要删除的节点的位置,然后删除该最小(或最大)节点。

  在实现删除操作时,需要注意保持二叉搜索树的性质,即任意节点的左子树的所有节点的值都小于该节点的值,右子树的所有节点的值都大于该节点的值。同时,删除操作可能会导致二叉搜索树的平衡被破坏,需要进行相应的调整操作。

  (1)当没有叶子结点,或者只有一个子节点的情况:
在这里插入图片描述

  (2)当左右都有子节点的情况:
在这里插入图片描述

  非递归实现:

//删除二叉树中的节点
bool BSErase(const K& key)
{BNode* cur = _root;BNode* parent = nullptr;//先要找到所删除的节点while (cur){if (cur->_key > key){parent = cur;cur = cur->_left;}else if (cur->_key < key){parent = cur;cur = cur->_right;}else//找到了所要删除的节点{if (cur->_left == nullptr)//当左节点为空节点{if (cur == _root)//cur为根节点{_root = cur->_right;}else//cur不为空节点{if (parent->_right == cur){parent->_right = cur->_right;}else{parent->_left = cur->_right;}}}else if(cur->_right==nullptr)//当右节点为空节点{if (cur == _root)//cur为空节点{_root = cur->_left;}else//cur不为空节点{if (parent->_right == cur){parent->_right = cur->_left;}else{parent->_left = cur->_left;}}}else//当左右节点都不为空节点{//找代替节点BNode* parent = cur;BNode* leftMax = cur->_left;while (leftMax->_right){parent = leftMax;leftMax = leftMax->_right;}swap(cur->_key, leftMax->_key);if (parent->_left == leftMax){parent->_left = leftMax->_left;}else{parent->_right = leftMax->_left;}cur=leftMax;}delete cur;return true;}}return false;
}

  递归实现:

bool _BSEraseR(BNode* &root, const K& key)
{if (root == nullptr){return false;}if (root->_key < key){_BSEraseR(root->_right, key);}else if (root->_key > key){_BSEraseR(root->_left, key);}else{BNode* del = root;//1.左为空//2.右为空//3.左右都不为空if (root->_left == nullptr){root = root->_right;}else if (root->_right == nullptr){root = root->_left;}else{BNode* leftMax = root->_left;while (leftMax->_right){leftMax = leftMax->_right;}swap(root->_key, leftMax->_key);return _BSEraseR(root->_left, key);}delete del;return true;}
}

             

2.5完整代码和测试

  完整代码:

#pragma once//搜索二叉树创建节点
template<class K>
struct BSTreeNode
{//左右节点和节点值keyBSTreeNode<K>* _left;BSTreeNode<K>* _right;K _key;//二叉树节点构造函数BSTreeNode(const K& key):_left(nullptr), _right(nullptr), _key(key){}
};//创建搜索二叉树
template<class K>
class BSTree
{//便于书写BNode节点typedef BSTreeNode<K> BNode;public://搜索二叉树构造函数BSTree():_root(nullptr){}//二叉树插入节点bool BSInsert(const K& key){//如果该树为空树,直接创建节点if (_root == nullptr){_root = new BNode(key);return true;}//找到二叉树所在的节点BNode* parent = nullptr;BNode* 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 BNode(key);if (parent->_key > key){parent->_left = cur;}else {parent->_right = cur;}return true;}//查找二叉树的节点bool BSFind(const K& key){BNode* cur = _root;while (cur){if (cur->_key > key){cur = cur->_left;}else if (cur->_key < key){cur = cur->_right;}else{return true;}}return false;}//删除二叉树中的节点bool BSErase(const K& key){BNode* cur = _root;BNode* parent = nullptr;//先要找到所删除的节点while (cur){if (cur->_key > key){parent = cur;cur = cur->_left;}else if (cur->_key < key){parent = cur;cur = cur->_right;}else//找到了所要删除的节点{if (cur->_left == nullptr)//当左节点为空节点{if (cur == _root)//cur为根节点{_root = cur->_right;}else//cur不为空节点{if (parent->_right == cur){parent->_right = cur->_right;}else{parent->_left = cur->_right;}}}else if(cur->_right==nullptr)//当右节点为空节点{if (cur == _root)//cur为空节点{_root = cur->_left;}else//cur不为空节点{if (parent->_right == cur){parent->_right = cur->_left;}else{parent->_left = cur->_left;}}}else//当左右节点都不为空节点{//找代替节点BNode* parent = cur;BNode* leftMax = cur->_left;while (leftMax->_right){parent = leftMax;leftMax = leftMax->_right;}swap(cur->_key, leftMax->_key);if (parent->_left == leftMax){parent->_left = leftMax->_left;}else{parent->_right = leftMax->_left;}cur=leftMax;}delete cur;return true;}}return false;}//二叉树的中序遍历void BSInOrder(){_BSInOrder(_root);cout << endl;}//递归实现二叉树的查找bool BSFindR(const K& key){return _BSFindR(_root, key);}//递归实现二叉树的插入节点bool BSInsertR(const K& key){return _BSInsertR(_root, key);}//递归实现二叉树的删除节点bool BSEraseR(const K& key){return _BSEraseR(_root, key);}private://递归二叉树删除的封装实现bool _BSEraseR(BNode* &root, const K& key){if (root == nullptr){return false;}if (root->_key < key){_BSEraseR(root->_right, key);}else if (root->_key > key){_BSEraseR(root->_left, key);}else{BNode* del = root;//1.左为空//2.右为空//3.左右都不为空if (root->_left == nullptr){root = root->_right;}else if (root->_right == nullptr){root = root->_left;}else{BNode* leftMax = root->_left;while (leftMax->_right){leftMax = leftMax->_right;}swap(root->_key, leftMax->_key);return _BSEraseR(root->_left, key);}delete del;return true;}}//递归二叉树插入的封装实现bool _BSInsertR(BNode*& root, const K& key)//这里传&,直接可以得到地址{if (root == nullptr){root = new BNode(key);return true;}if (root->_key < key){_BSInsertR(root->_right, key);}else if (root->_key > key){_BSInsertR(root->_left, key);}else{return false;}}//递归二叉树查找的封装实现bool _BSFindR(BNode* root, const K& key){if (root == nullptr){return false;}if (root->_key < key){return _BSFindR(root->_right, key);}else if (root->_key > key){return _BSFindR(root->_left, key);}else{return true;}}//为了递归实现,进行一层封装void _BSInOrder(BNode* root){if (root == nullptr){return;}_BSInOrder(root->_left);printf("%d ", root->_key);_BSInOrder(root->_right);}private:BNode* _root;
};


  简单测试:

#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>
using namespace std;#include"BinarySearchTree.h"void BSTree_test1()
{BSTree<int> t;t.BSInsert(5);t.BSInsert(8);t.BSInsert(6);t.BSInsert(1);t.BSInsert(3);t.BSInsert(2);t.BSInsert(4);t.BSInOrder();cout << "是否存在二叉树的值为1:";if (t.BSFind(1))cout << "存在";else cout << "不存在";cout << endl;int arr[] = { 5,3,1,2,4,9,8,6,7,10 };BSTree<int> t1;for (auto e : arr){t1.BSInsert(e);}t1.BSInOrder();t1.BSErase(3);t1.BSInOrder();cout << "是否存在二叉树的值为1:";if (t1.BSFindR(1))cout << "存在";else cout << "不存在";cout << endl;t1.BSInsertR(11);t1.BSInOrder();t1.BSEraseR(11);t1.BSInOrder();}int main()
{BSTree_test1();return 0;
}

这里就是数据结构中二叉搜索树的基本介绍了😉
如有错误❌望指正,最后祝大家学习进步✊天天开心✨🎉

             

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

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

相关文章

【Spring 事务和事务传播机制】

目录 1 事务概述 1.1 为什么需要事务 1.2 事务的特性 1.3 Spring 中事务的实现 2 Spring 声明式事务 2.1 Transactional 2.2 Transactional 的作用范围 2.3 Transactional 的各种参数 2.3.1 ioslation 2.4 事务发生了异常&#xff0c;也不回滚的情况 异常被捕获时 3 事务的传…

通过 Blob 对二进制流文件下载实现文件保存下载

原理&#xff1a;前端将二进制文件做转换实现下载: 请求后端接口->接收后端返回的二进制流(通过二进制流&#xff08;Blob&#xff09;下载,把后端返回的二进制文件放在 Blob 里面)->再通过file-saver插件保存 页面上使用&#xff1a; <span click"downloadFil…

作为产品经理,有必要考PMP或者NPDP么?

产品经理的核心竞争力是什么? 三点&#xff1a;知识、能力和决策 懂得越多&#xff0c;能力越强&#xff0c;决策越正确&#xff0c;核心竞争力越强。一般来说&#xff0c;看的越多&#xff0c;做的越多&#xff0c;实践出经验才是王道&#xff0c;但是&#xff0c;总有看不…

智慧物流发展的重要推动力量:北斗卫星导航系统

随着经济的快速发展和电商的普及&#xff0c;物流行业的规模不断扩大&#xff0c;对物流运输的效率和安全性也提出了更高的要求。传统的物流运输方式存在着效率低下、信息不对称、安全隐患等问题&#xff0c;因此发展智慧物流已经成为物流行业的必然趋势。智慧物流可以通过先进…

立晶半导体Cubic Lattice Inc 专攻音频ADC,音频DAC,音频CODEC,音频CLASS D等CL7016

概述&#xff1a; CL7016是一款高保真USB Type-C兼容音频编解码芯片。可以录制和回放有24比特音乐和声音。内置回放通路信号动态压缩&#xff0c; 最大42db录音通路增益&#xff0c;PDM数字麦克风&#xff0c;和立体声无需电容耳机驱动放大器。 5V单电源供电。兼容USB 2.0全速工…

深度学习面试八股文(2023.9.06持续更新)

一、优化器 1、SGD是什么&#xff1f; 批梯度下降&#xff08;Batch gradient descent&#xff09;&#xff1a;遍历全部数据集算一次损失函数&#xff0c;计算量开销大&#xff0c;计算速度慢&#xff0c;不支持在线学习。随机梯度下降&#xff08;Stochastic gradient desc…

基于vue-cli创建后台管理系统前端页面——element-ui,axios,跨域配置,布局初步,导航栏

目录 引出安装npm install安装element-ui安装axios 进行配置main.js中引入添加jwt前端跨域配置 进行初始布局HomeView.vueApp.vue 新增页面和引入home页面导航栏总结 引出 1.vue-cli创建前端工程&#xff0c;安装element-ui&#xff0c;axios和配置&#xff1b; 2.前端跨域的配…

记录学习--字节码解析try catch

1.示例代码 Testpublic void someTest() {String s "111";try {s "222";int i 1/0;} catch (Exception e){e.printStackTrace();System.out.println(s);}System.out.println(s);}2.示例代码对应的字节码 0 ldc #2 <111>2 astore_13 ldc #3 <22…

“深入理解SpringMVC的注解驱动开发“

目录 引言1. SpringMVC的常用注解2. SpringMVC的参数传递3. SpringMVC的返回值4. SpringMVC页面跳转总结 引言 在现代的Web开发中&#xff0c;SpringMVC已经成为了一个非常流行和强大的框架。它提供了许多注解来简化开发过程&#xff0c;使得我们能够更加专注于业务逻辑的实现…

【python】TCP socket服务器 Demo

目录 一、单线程服务器 二、多线程服务器 三、多线程服务器&#xff08;发送和接收分离&#xff09; 一、单线程服务器 说明&#xff1a;只能连接一个客户端 import socket,binascii# 创建一个 TCP 套接字 server_socket socket.socket(socket.AF_INET, socket.SOCK_STRE…

nas汇编程序的调试排错方法

nas汇编程序的调试排错方法&#xff1a; 1、查找是哪一步错了 2、查看对应的*.lst文件&#xff0c;本例中是"asmhead.lst" 3、根据*.lst文件的[ERROR #002]提示查看源码&#xff0c;改错。 4、重新运行编译&#xff0c;OK 1、查找是哪一步错了&#xff1a; nask.ex…

基于任务队列的机器学习服务实现

将机器模型部署到生产环境的方法有很多。 常见的方法之一是将其实现为 Web 服务。 最流行的类型是 REST API。 它的作用是全天候&#xff08;24/7&#xff09;部署和运行&#xff0c;等待接收来自客户端的 JSON 请求&#xff0c;提取输入&#xff0c;并将其发送到 ML 模型以预测…

SpringBoot / Vue 对SSE的基本使用(简单上手)

一、SSE是什么&#xff1f; SSE技术是基于单工通信模式&#xff0c;只是单纯的客户端向服务端发送请求&#xff0c;服务端不会主动发送给客户端。服务端采取的策略是抓住这个请求不放&#xff0c;等数据更新的时候才返回给客户端&#xff0c;当客户端接收到消息后&#xff0c;…

http请求头部(header)详解

目录 常见的请求头部字段 GET方法的使用方法&#xff1a; POST方法的使用方法&#xff1a; Accept字段的使用方法 Content-Type字段的使用 总结 在互联网协议中&#xff0c;HTTP请求头部&#xff08;header&#xff09;是一个非常重要的组成部分。它们是客户端和服务器之…

【python自动化】七月PytestAutoApi开源框架学习笔记(一)

前言 本篇内容为学习七月大佬开源框架PytestAutoApi记录的相关知识点&#xff0c;供大家学习探讨 项目地址&#xff1a;https://gitee.com/yu_xiao_qi/pytest-auto-api2 阅读本文前&#xff0c;请先对该框架有一个整体学习&#xff0c;请认真阅读作者的README.md文件。 本文…

【嵌入式软件C编程】主函数free子函数malloc地址的两种方式以及注意事项

本文档主要记录嵌入式C语言在子函数中应用malloc函数的方式&#xff0c;在实际项目中内存管理特别重要 一般在主函数中&#xff08;main&#xff09;使用malloc函数&#xff0c;然后在通过free函数进行释放内存&#xff0c;但有时候如果必须在子函数长调用malloc函数该怎样进行…

基于VueCli创建自定义项目

1.安装脚手架 (已安装) npm i vue/cli -g2.创建项目 vue create hm-exp-mobile选项 Vue CLI v5.0.8 ? Please pick a preset:Default ([Vue 3] babel, eslint)Default ([Vue 2] babel, eslint) > Manually select features 选自定义手动选择功能 选择vue的版本 3.x …

电脑黑屏只有鼠标箭头?4个正确处理方法记得收藏!

“我刚刚在用电脑办公呢&#xff0c;突然之间电脑就黑屏了&#xff0c;只剩下一个鼠标箭头。这是什么原因引起的呢&#xff1f;怎么解决电脑黑屏的问题呀&#xff1f;求解答&#xff01;” 不知道朋友们有没有遇到过在使用电脑时&#xff0c;突然电脑就黑屏了并且只剩下鼠标箭头…

国内免费无限制的chatgpt导航和ai画画

非常实用的AI网址导航&#xff0c;其实际使用体验非常便捷。该导航系统不仅提供了全面的网站分类和搜索功能&#xff0c;还对每个网站进行了精准的评估和排序。推荐高质量的网站资源&#xff0c;并实时检测网站的安全性&#xff0c;保障用户的上网安全。 总的来说&#xff1a…

RHCA之路---EX280(8)

RHCA之路—EX280(8) 1. 题目 On master.lab.example.com using the template file in http://materials.example.com/exam280/gogs as a basis, install an application in the ditto project according to the following requirements: All of the registry entries must poi…