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

一、二叉搜索树定义

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

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.hqwc.cn/news/573155.html

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

相关文章

java中的单例模式

一、描述 单例模式就是程序中一个类只能有一个对象实例 举个例子: //引出单例模式&#xff0c;一个类中只能由一个对象实例 public class Singleton1 {private static Singleton1 instance new Singleton1();//通过这个方法来获取实例public static Singleton1 getInstance…

手写SpringBoot(一)之简易版SpringBoot

手写SpringBoot&#xff08;一&#xff09;之简易版SpringBoot 添加依赖 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"…

数据结构——排序算法

1、排序的概念 排序是指的是将一组数据&#xff08;如数字、单词、记录等&#xff09;按照某种特定的顺序&#xff08;升序或降序&#xff09;进行排列的过程。排序算法是实现排序的程序或方法&#xff0c;它们在软件开发和数据处理中扮演着至关重要的角色。 排序算法可以根据…

强化基础-Java-泛型

什么是泛型&#xff1f; 泛型其实就参数化类型&#xff0c;也就是说这个类型类似一个变量是可变的。 为什么会有泛型&#xff1f; 在没有泛型之前&#xff0c;java中是通过Object来实现泛型的功能。但是这样做有下面两个缺陷&#xff1a; 1 获取值的时候必须进行强转 2 没有…

[BT]BUUCTF刷题第9天(3.27)

第9天&#xff08;共2题&#xff09; [护网杯 2018]easy_tornado 打开网站就是三个txt文件 /flag.txt flag in /fllllllllllllag/welcome.txt render/hints.txt md5(cookie_secretmd5(filename))当点进flag.txt时&#xff0c;url变为 http://b9e52e06-e591-46ad-953e-7e8c5f…

银行卡的分类

银行卡是银行账户的一种体现形式&#xff0c;它是由银行机构发行的具有消费信用、转账结算、存取现金等全部或部分功能作为结算支付工具的各类卡的统称。 &#xff08;1&#xff09;按是否具有授信额度分类 ①借记卡&#xff1a;借记卡是指发卡银行向申请人签发的&#xff0c;没…

第十一章:位运算符与位运算

文章目录 第十一章&#xff1a;位运算符与位运算1.按位与运算&#xff1a;&2.按位或运算&#xff1a;|3.按位异或运算&#xff1a;^4.取反运算符&#xff1a;~5.左移运算符&#xff1a;<<6.右移运算符&#xff1a;>>总结 第十一章&#xff1a;位运算符与位运算…

动态内存操作函数使用过程中会遇见的问题

越界访问 首先我们上一个代码&#xff0c;看看这个的代码的问题 这个代码的问题显而易见 &#xff0c;就是在循环里面&#xff0c;产生了越界访问的问题&#xff0c;这里你开辟了10个整形空间&#xff0c;但是从0-10一共是11个整形空间。导致访问不合法的空间&#xff0c;从而…

Cy3-PEG-NH2 Cy3-聚乙二醇-氨基 磷脂PEG花菁Cy3

产品名称 Cy3-PEG-NH2 目录号 410502 中文名称 Cy3-聚乙二醇-氨基 英文名称 Cy3-PEG-NH2 Cy3-PEG-Amine 分子量 2000 溶解度 溶于氯仿等有机溶剂 存储条件 -20冷冻避光 保存时间 一年 Ex/Em(nm) 550/570 其它分子量 1000 3400 5000 10000 结构 花氰染料Cyanine,常应用…

记一次由gzip引起的nginx转发事故

故事背景 书接前几篇文章&#xff0c;仍然是交付甲方遇到的一个特殊诉求&#xff0c;从而引发了本期的事故。甲方的诉求是前端的请求过来&#xff0c;需要加密&#xff0c;但是要经过waf&#xff0c;必须要求是请求明文&#xff0c;那就要在waf和nginx之间做一个解密前置应用处…

搜索插入位置-java

题目描述 : 给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位置。请必须使用时间复杂度为 O(log n) 的算法。 思路分析: 这段代码的解题思想是利用二分查找的方法在…

MySql实战--事务到底是隔离的还是不隔离的

第3篇文章和你讲事务隔离级别的时候提到过&#xff0c;如果是可重复读隔离级别&#xff0c;事务T启动的时候会创建一个视图read-view&#xff0c;之后事务T执行期间&#xff0c;即使有其他事务修改了数据&#xff0c;事务T看到的仍然跟在启动时看到的一样。也就是说&#xff0c…