C++数据结构之BST(二叉搜索树)的实现

news2024/9/28 7:23:23

目录

  • 01.BST的介绍
  • 02.BST 要实现的对外方法
  • 03.摘要
  • 04.查找节点
      • 4.1四个引用,都有妙用
      • 4.2递归版
      • 4.3非递归版
  • 05.插入节点
      • 5.1利用search的返回值
      • 5.2更新高度的注意事项
      • 5.3插入算法的完整代码
  • 06.删除节点
      • 6.1框架
      • 6.2单分支,直接替代
      • 6.3双分支,化繁为简
      • 6.4代码
  • 07.code BST
  • 树的后续

预告:本文是后续实现各种各样平衡二叉搜索树的铺垫。

在这里插入图片描述

01.BST的介绍

  1. BST 寻关键码访问 (call by key)
    相比于——
    Vector call by rank
    List call by position
    Hash table call by value
  2. 有序性
  3. 单调性

扩:从不可重复到可重复的扩充(下图)

在这里插入图片描述
在这里插入图片描述

02.BST 要实现的对外方法

方法 功能 参数 返回值
search 查找 T const & val BinNode * & 待插入位置/目标节点指针的引用
insert 插入 BinNode * 新节点指针
remove 移除 bool 树上是否存在值为val的节点

03.摘要

  1. 虚函数,方便派生类进行重写。
  2. 全局静态模板函数,适用于AVL,Splay,RedBlack等各种BST
  3. 这里的remove一看就是对外的,因为参数终于不是指针了,而是值。需要我们先找位置。
  4. return searchIn(BinTree::root, hot = NULL, val); 注意 =NULL

04.查找节点

4.1四个引用,都有妙用

看到searchIn的声明,居然全都是引用类型。

static BinNode<T> * & searchIn(BinNode<T> * & rt, BinNode<T> * & hot_node, T const & val)

列举这四个引用各自的功能——

返回值引用:插入节点时,这个引用相当于插入位置,后续我们将新节点的指针赋给到这个返回值,父节点的左右孩子之一就会连上新节点。

BinNode<T> * & rt:如果这个不是引用,返回值返回的就是一个仅在函数内部的局部变量(即形参),后续改写这个引用值时,会发生错误。

BinNode<T> * & hot_node:在递归中随深度不断更新这个记忆热点,也是为了方便插入算法,等到最后退出时hot存的是插入位置的父节点。

T const & val:传递引用变量可以提速,为了不误改,前面加上const做约束。

4.2递归版

		virtual BinNode<T> * & search(T const & val)
		{
			return searchIn(BinTree<T>::root, hot = NULL, val); //注意,这个=NULL,如果不写,插入根节点并向上更新高度时就会报错
		}
		static BinNode<T> * & searchIn(BinNode<T> * & rt, BinNode<T> * & hot_node, T const & val)
		{
			if (!rt || rt->data == val) return rt; // 返回的是引用

			hot_node = rt; //在递归中随深度不断更新

			if (val < rt->data) return searchIn(rt->left, hot_node, val);
			else return searchIn(rt->right, hot_node, val);
		}

在这里插入图片描述

4.3非递归版

尾递归转迭代,略。

05.插入节点

5.1利用search的返回值

有了查找节点算法中“记忆热点”hot的设计,经过search()的运行,就可以得到插入位置的父节点。或许应该记得BinTree里写过的几个函数:insertAsLeft()insertAsRight(),我们只需要将valhot->data做比较即可。在这里,我们换一种写法——不浪费search的返回值。你知道,查找一旦失败,返回值就是NULL的引用,利用它,就无需在insert()中判断究竟应该插入到hot的左边还是右边。

先找到插入位置,X的类型必须是引用,后续我们将新节点的指针赋给到X,hot的左右孩子之一就会连上新节点。

BinNode<T> * & X = search(val); 

下面这一句话将 “父->子” “子->父” 相互关系都连接好了。

X = new BinNode(val, hot); 

在这里插入图片描述

5.2更新高度的注意事项

更新高度由于之前做的优化,检测到某处更新后与更新前高度一致则不会再上行更新,所以高度更新要给父节点更新,即updateHighAbove(hot),如果给了X更新,那就不会继续下去。

5.3插入算法的完整代码

		virtual BinNode<T> * insert(T const & val)
		{
			
			BinNode<T> * & X = search(val); //为了找到插入位置
			if (!X)
			{
				X = new BinNode(val, hot); //这一句话将两个关系连接

				// 不要忘记
				BinTree<T>::size++;
				updateHighAbove(hot);
			}
			return X;
		}

insert()的返回值是X,但返回类型是BinNode<T> *,并不是引用,这在语法中是允许的。所返回的东西仅仅在数值上与X相同,但与X完全脱离了关系。

06.删除节点

6.1框架

		virtual bool remove(T const & val)
		{
			BinNode<T> * & X = search(val);
			if (!X) //树里没有val
			{
				return false;
			}
			else
			{
				removeAt(X, hot);
				BinTree<T>::size--;
				updateHighAbove(hot);
				return true;
			}
		}

6.2单分支,直接替代

在这里插入图片描述

6.3双分支,化繁为简

还是想,哪一个节点替代被删节点的位置。那一定是直接后继。求中序遍历下的直接后继。

// struct BinNode中,功能:求中序遍历下的直接后继
	BinNode<T> * succ()
	{
		BinNode<T> * succ_node;
		if (right)
		{
			succ_node = right;
			while (succ_node->left)
				succ_node = succ_node->left;
		}
		else
		{
			succ_node = this;
			while (succ_node != succ_node->parent->left)
				succ_node = succ_node->parent;
			succ_node = succ_node->parent;
		}
		return succ_node;
	}

在这里插入图片描述

6.4代码

		static BinNode<T> * removeAt(BinNode<T> * X, BinNode<T> * & hot_node)
		{
			// hot_node指向要被删除的父亲
			BinNode<T> * del_node; // 实际要被删除的节点
			BinNode<T> * succ_node; // 实际要被删除的节点的接替者

			if (!X->left)
			{
				del_node = X;
				succ_node = X->right;
			}
			else if (!X->right)
			{
				del_node = X;
				succ_node = X->left;
			}
			else // 双分支情况
			{ 
				// 找到中序的直接后继
				del_node = X->succ();
				succ_node = del_node->right;

				swap(del_node->data, X->data);
				BinNode<T>::fromParentTo(del_node) = succ_node;
			}

			hot_node = del_node->parent;
			if (succ_node) succ_node->parent = hot_node;
			delete del_node;
			return succ_node;
		}

07.code BST

# pragma once

# include "BinTree.h"

template <typename T>
void swap(T & a, T & b)
{
	T t;
	t = a;
	a = b;
	b = t;
}

template <typename T>
class BST : public BinTree<T> {
	public:

		//***********************************************************查找*********************************************************

		virtual BinNode<T> * & search(T const & val)
		{
			return searchIn(BinTree<T>::root, hot = NULL, val);
		}

		static BinNode<T> * & searchIn(BinNode<T> * & rt, BinNode<T> * & hot_node, T const & val)
		{
			if (!rt || rt->data == val) return rt; // 返回的是引用

			hot_node = rt; //在递归中随深度不断更新

			if (val < rt->data) return searchIn(rt->left, hot_node, val);
			else return searchIn(rt->right, hot_node, val);
		}

		//***********************************************************插入*********************************************************

		virtual BinNode<T> * insert(T const & val)
		{
			
			BinNode<T> * & X = search(val); //为了找到插入位置
			if (!X)
			{
				X = new BinNode<T>(val, hot); //这一句话将两个关系连接

				// 不要忘记
				BinTree<T>::size++;
				BinTree<T>::updateHighAbove(hot);
			}
			return X;
		}

		//***********************************************************删除*********************************************************

		virtual bool remove(T const & val)
		{
			BinNode<T> * & X = search(val);
			if (!X) //树里没有val
			{
				return false;
			}
			else
			{
				removeAt(X, hot);
				BinTree<T>::size--;
				BinTree<T>::updateHighAbove(hot);
				return true;
			}
		}

		static BinNode<T> * removeAt(BinNode<T> * X, BinNode<T> * & hot_node)
		{
			// hot_node指向要被删除的父亲
			BinNode<T> * del_node; // 实际要被删除的节点
			BinNode<T> * succ_node; // 实际要被删除的节点的接替者

			if (!X->left)
			{
				del_node = X;
				succ_node = X->right;
			}
			else if (!X->right)
			{
				del_node = X;
				succ_node = X->left;
			}
			else // 双分支情况
			{ 
				// 找到中序的直接后继
				del_node = X->succ();
				succ_node = del_node->right;

				swap(del_node->data, X->data);
				BinNode<T>::fromParentTo(del_node) = succ_node;
			}

			hot_node = del_node->parent;
			if (succ_node) succ_node->parent = hot_node;
			delete del_node;
			return succ_node;
		}

		//******************************************************统一重平衡算法****************************************************

		// 在AVL中有极大用处

		BinNode<T> * connect34(BinNode<T> *, BinNode<T> *, BinNode<T> *, BinNode<T> *, BinNode<T> *, BinNode<T> *, BinNode<T> *);
		BinNode<T> * rotateAt(BinNode<T> * x);

	protected:
		BinNode<T> * hot; // 命中节点的父亲
};



谢谢观看~

树的后续

C++数据结构之平衡二叉搜索树(一)——AVL的实现(zig-zag/左右双旋/3+4重构)

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

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

相关文章

实现天气预报走势图

实现效果&#xff1a; 这里我用的天气接口是网上开源的&#xff0c;可以自己找一下。 稍微简单封装了一下axiso以及接口 封装的axios&#xff1a; // import { useUserStore } from /stores/user import axios from axios import router from /router import { ElMessage } f…

P14 电路定理——巧妙-灵性-智慧

1、替代定理 图示表示&#xff1a; 叠加定理和齐性定理只能用于线性电路&#xff0c;但是替代定理无论线不线性都可以用。 常见的&#xff1a;线性电路将某复杂支路等效成电压源或电流源之后&#xff0c;就可以使用叠加原理了。 引入两个相互抵消的电压源&#xff0c;拿其中一…

【数字IC基础】低功耗设计

低功耗技术 功耗构成静态功耗(漏电功耗)动态功耗翻转功耗(Switch Power)短路功耗(Internal Power) 不同类型的标准单元的功耗 低功耗设计方法降低芯片工作电压多阈值工艺方法电源门控&#xff08;Power Gating&#xff09;多电压域(Multi-Voltage Domain)体偏置门控时钟一个简单…

AWS Amplify 部署node版本18报错修复

Amplify env&#xff1a;Amazon Linux:2 Build Error : Specified Node 18 but GLIBC_2.27 or GLIBC_2.28 not found on build 一、原因 报错原因是因为默认情况下&#xff0c;AWS Amplify 使用 Amazon Linux:2 作为其构建镜像&#xff0c;并自带 GLIBC 2.26。不过&#xff0c;…

【M波段2D双树(希尔伯特)小波多分量图像去噪】基于定向M波段双树(希尔伯特)小波对多分量/彩色图像进行降噪研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

使用 OpenCV 和 Python 卡通化图像-附源码

介绍 在本文中,我们将构建一个有趣的应用程序,它将卡通化提供给它的图像。为了构建这个卡通化器应用程序,我们将使用 python 和 OpenCV。这是机器学习令人兴奋的应用之一。在构建此应用程序时,我们还将了解如何使用 easygui、Tkinter 等库。在这里,您必须选择图像,然后应…

二叉树的遍历(先序遍历,中序遍历,后序遍历)递归与非递归算法

目录 一、先序遍历题目链接1.递归2.非递归 二、中序遍历题目链接1.递归2.非递归 三、后序遍历题目链接1.递归2.非递归 一、先序遍历 先序遍历&#xff1a;先遍历一颗树的根节点&#xff0c;后遍历左子树&#xff0c;最后遍历右子树 先序遍历序列&#xff1a; 1 -> 2 -> 4…

20.4 HTML 表单

1. form表单 <form>标签: 用于创建一个表单, 通过表单, 用户可以向网站提交数据. 表单可以包含文本输入字段, 复选框, 单选按钮, 下拉列表, 提交按钮等等. 当用户提交表单时, 表单数据会发送到服务器进行处理.action属性: 应指向一个能够处理表单数据的服务器端脚本或UR…

vue使用拖拽功能实现仓库存放货物的需求

效果&#xff1a; 代码 <template><div><div class"bigTitle">xxxxxxxxxxxxxx仓库拖拽系统</div><div class"container2 flex-j-space-between"><div class"product-list"><div class"leftTree h…

Python入门【​try和except结构、常见异常、with上下文管理 、traceback模块和生成异常日志、自定义异常类】(十八)

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是爱敲代码的小王&#xff0c;CSDN博客博主,Python小白 &#x1f4d5;系列专栏&#xff1a;python入门到实战、Python爬虫开发、Python办公自动化、Python数据分析、Python前后端开发 &#x1f4e7;如果文章知识点有错误…

【C++】STL——stack和queue的模拟实现、空间适配器、deque的介绍、增删查改函数的简单实现

文章目录 1.deque的简单介绍2.模拟实现stack3.模拟实现queue 1.deque的简单介绍 deque的介绍文档 deque(双端队列)&#xff1a;是一种双开口的"连续"空间的数据结构&#xff0c;双开口的含义是&#xff1a;可以在头尾两端进行插入和删除操作&#xff0c;且时间复杂度…

实现多线程的三种方式

1. 继承Thread 类实现多线程 想要实现多线程&#xff0c;第一种方法就是通过继承Thread类实现多线程&#xff0c;有以下几步 &#xff08;1&#xff09;我们要先自定义一个类然后继承Thread类&#xff1b; &#xff08;2&#xff09;在继承Trread的类中重写 run 方法&#x…

成功了!|| Poetry安装pytorch || 整理自github项目Poetry下的issue

在使用Poetry安装pytorch的时候&#xff0c;常常会遇到各种问题&#xff1a;首先是使用add添加时&#xff0c;会说只有torch没有什么pytorch&#xff0c;很显然&#xff0c;它是直接针对包的&#xff0c;第二点是&#xff0c;如果是一台没有nvidia显卡的机器&#xff0c;由于po…

CISA《网络安全事件和漏洞响应手册》提到的SSVC是什么?

2021年11月16日&#xff0c;美国网络安全和基础设施安全局(CISA)根据行政命令EO 14028的要求发布了《网络安全事件和漏洞响应手册》。手册规定的漏洞响应过程包括识别、评估、修复、报告通知4个步骤&#xff0c;其中评估部分的第一句话提到“使用特定相关者漏洞分类法(Stakehol…

C++ 动态内存分配

在C中动态内存的分配技术可以保证程序在允许过程中按照实际需要申请适量的内存&#xff0c;使用结束后还可以释放&#xff0c;这种在程序运行过程中申请和释放的存储单元也称为堆。 申请和释放过程一般称为建立和删除。 在C程序中&#xff0c;建立和删除堆对象使用两个运算符&…

html页面input设置日期和时分秒组件方法

html <input class"form-control" type"datetime-local" step"01">效果图

Java根据坐标经纬度计算两点距离(5种方法)、校验经纬度是否在圆/多边形区域内的算法推荐

目录 前言 一、根据坐标经纬度计算两点距离&#xff08;5种方法&#xff09; 1.方法一 2.方法二 3.方法三 4.方法四 5.方法五 5.1 POM引入第三方依赖 5.2 代码 6.测试结果对比 二、校验经纬度是否在制定区域内 1.判断一个坐标是否在圆形区域内 2.判断一个坐标是否…

安防监控国标GB28181平台EasyGBS视频快照无法显示是什么原因?如何解决?

安防视频监控国标视频云服务EasyGBS支持设备/平台通过国标GB28181协议注册接入&#xff0c;并能实现视频的实时监控直播、录像、检索与回看、语音对讲、云存储、告警、平台级联等功能。平台部署简单、可拓展性强&#xff0c;支持将接入的视频流进行全终端、全平台分发&#xff…

【Leetcode刷题】模拟

本篇文章为 LeetCode 模拟模块的刷题笔记&#xff0c;仅供参考。 目录 一. 字符串Leetcode43.字符串相乘Leetcode592.分数加减运算Leetcode68.文本左右对齐 二. 矩阵Leetcode54.螺旋矩阵Leetcode885.螺旋矩阵 IIILeetcode498.对角线遍历Leetcode874.模拟行走机器人 三. 数组Lee…