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

文章目录

  • 二叉搜索树
    • 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.rhkb.cn/news/122942.html

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

相关文章

网络技术学习十三:DNS(域名服务器)

DNS 域名 产生背景 通过IP地址访问目标主机&#xff0c;不便于记忆 通过容易记忆的域名来标识主机位置 域名的树形层次化结构 根域 领级域 主机所处的国家/区域&#xff0c;注册人的性质 二级域 注册人自行创建的名称 主机名 区域内部的主机的名称 由注册人自行创建…

stm32(GD32,apm32),开优化后需要特别注意的地方

提到优化就不得不提及 volatile 使用场景 1&#xff1a;中断服务程序中修改的供其它程序检测的变量&#xff0c;需要加volatile&#xff1b; : 2&#xff1a;多任务环境下各任务间共享的标志&#xff0c;应该加volatile&#xff1b; 3&#xff1a;并行设备的硬件寄存器&#x…

【免费模板】2023数学建模国赛word+latex模板免费分享

无需转发 免费获取2023国赛模板&#xff0c;获取方式见文末 模板文件预览如下&#xff1a; 模板参考格式如下&#xff1a; &#xff08;题目&#xff09;XXXXXX 摘 要&#xff1a; 开头段&#xff1a;需要充分概括论文内容&#xff0c;一般两到三句话即可&#xff0c;长度控…

TortoiseGit设置作者信息和用户名、密码存储

前言 Git 客户端每次与服务器交互&#xff0c;都需要输入密码&#xff0c;但是我们可以配置保存密码&#xff0c;只需要输入一次&#xff0c;就不再需要输入密码。 操作说明 在任意文件夹下&#xff0c;空白处&#xff0c;鼠标右键点击 在弹出菜单中按照下图点击 依次点击下…

国际慈善日 | 追寻大爱无疆,拓世科技集团的公益之路

每年的9月5日&#xff0c;是联合国大会正式选定的国际慈善日。这一天的设立&#xff0c;旨在通过提高公众对慈善活动的意识&#xff0c;鼓励慈善公益活动通过各种形式在全球范围内得到增强和发展。这是一个向慈善公益事业致敬的日子&#xff0c;同时也是呼吁全球团结一致共同发…

【两周学会FPGA】从0到1学习紫光同创FPGA开发|盘古PGL22G开发板学习之DDR3 IP简单读写测试(六)

本原创教程由深圳市小眼睛科技有限公司创作&#xff0c;版权归本公司所有&#xff0c;如需转载&#xff0c;需授权并注明出处 适用于板卡型号&#xff1a; 紫光同创PGL22G开发平台&#xff08;盘古22K&#xff09; 一&#xff1a;盘古22K开发板&#xff08;紫光同创PGL22G开发…

LED显示屏安全亮度参数设置方法和防护

随着LED显示屏应用领域越来越广&#xff0c;但其高亮度造成的光污染&#xff0c;常受到的人们的诟病。为了更好的避免光污染&#xff0c;我整理了一些关于LED显示安全亮度参数设置方法和安全防护措施。你知道LED广告牌是如何工作的吗&#xff1f; 设置LED显示屏的安全亮度参数和…

pycharm创建py文件时自动添加基础信息--模板

在图片中加入下面基本信息&#xff0c;这些基本信息可以自己定义&#xff1a; #!/usr/bin/env python # -*- coding: utf-8 -*- # Time : ${DATE} ${TIME} # Author : supermps # File : ${NAME}.py # Software : ${PRODUCT_NAME} import logging import math import w…

远程管理通道安全SSH协议主机验证过程

可以使用SSH协议进行远程管理通道安全保护&#xff0c;其中涉及的主要安全功能包括主机验证、数据加密性和数据完整性保护。 这里要注意的是【主机验证】和【身份验证】的区别&#xff0c;主机验证是客户端确认所访问的服务端是目标访问对象&#xff0c;比如从从客户端A(192.16…

Bytebase 和 GitLab 签署 Technology Partner 技术合作伙伴协议

Bytebase 和 GitLab 签署技术合作伙伴协议&#xff0c;携手为开发者提供流畅的数据库协作开发和管理体验。 GitLab 是世界领先的开源 AI 驱动 DevSecOps 平台&#xff0c;旨在帮助开发者团队更好协作、更高效交付软件。Bytebase 是一款为 DevOps 团队准备的数据库 CI/CD 工具&a…

Cyber RT基础入门与实践_Hello Apollo

Hello Apollo 进入云实验环境模块的模块内包的 进入云实验环境 <1> 创建本节实验工程目录&#xff0c;创建完成后&#xff0c;工程目录如下所示&#xff1a; cyber_demo |-- cyber_01 |-- demo_main | |-- BUILD | |-- main.cc |–BUILD |–cyberfile.xml |–cyber_demo.…

NosQL之Redis配置与优化

目录 1、关系型数据库与非关系型数据库、 1.1、了解关系数据库与非关系型数据库 1.1.1、关系型数据库 1.1.2、非关系型数据库 1.2、非关系型数据库 1.2.1、概念 1.2.2、产生背景 1.2.3、非关系型数据库的主要特点 1.3、关系型数据库和非关系型数据库区别&#xff1a; …

CMake生成Visual Studio工程

CMake – 生成Visual Studio工程 C/C项目经常使用CMake构建工具。CMake 项目文件&#xff08;例如 CMakeLists.txt&#xff09;可以直接由 Visual Studio 使用。本文要说明的是如何将CMake项目转换到Visual Studio解决方案(.sln)或项目(.vcxproj) 开发环境 为了生成Visual S…

GptFuck—开源Gpt4分享

这个项目不错&#xff0c;分享给大家 项目地址传送门

mac安装adobe需要注意的tips(含win+mac all安装包)

M2芯片只能安装2022年以后的&#xff08;包含2022年的&#xff09; 1、必须操作的开启“任何来源” “任何来源“设置&#xff0c;这是为了系统安全性&#xff0c;苹果希望所有的软件都从商店或是能验证的官方下载&#xff0c;导致默认不允许从第三方下载应用程序。macOS sie…

GD32F470 MDK AC DSP库移植流程

前言 花费了将近大半天的时间&#xff0c;故简单记录一下&#xff0c;避免自己忘记&#xff0c;还未经更多的测试&#xff0c;所以仅供参考。 由于CMSIS的更新&#xff0c;在CMSIS v5.7.0版本之后&#xff0c;CMSIS-DSP 中已不再提供编译好的 lib文件。并且GitHub CMSIS仓库中…

深度优先搜索(dfs)--矩阵部分-leetcode以及常见题

介绍 深度优先搜索&#xff08;Depth-First Search&#xff0c;DFS&#xff09;是一种常用的图搜索算法&#xff0c;它用于查找图或树数据结构中的路径或解决问题。下面是深度优先搜索的常见步骤以及一个示例问题&#xff1a; 深度优先搜索的常见步骤&#xff1a; 选择起始节…

Jenkins实现基础CD操作

操作截图 在Jenkins里面设置通过标签进行构建 在Jenkins中进入项目&#xff0c;配置以下 将execute shell换到invoke top-level maven targets之前 在gitlab中配置标签 代码迭代新的版本 项目代码迭代 修改docker-compose.yml 提交新版本的代码 在Jenkins中追加新…

Linux CentOS7设置时区

在Linux系统中&#xff0c;默认使用的是UTC时间。 即使在安装系统的时候&#xff0c;选择的时区是亚洲上海&#xff0c;Linux默认的BIOS时间&#xff08;也称&#xff1a;硬件时间&#xff09;也是UTC时间。 在重启之后&#xff0c;系统时间会和硬件时间同步&#xff0c;如果…

rtthread下spi device架构MCP25625驱动

1.CAN驱动架构 由于采用了RTT的spi device架构&#xff0c;不能再随心所遇的编写CAN驱动 了&#xff0c;之前内核虽然采用了RTT内核&#xff0c;但是驱动并没有严格严格按RTT推荐的架构来做&#xff0c;这次不同了&#xff0c;上次是因为4个MCP25625挂在了4路独立的SPI总线上&…