搜索二叉树(二叉搜索树)的实现(递归与非递归)

news2025/1/17 3:00:49

一、搜索二叉树的概念

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

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

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

它的左右子树也分别为搜索二叉树。 

二、搜索二叉树的操作

1. 搜索二叉树的查找

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

b、最多查找高度次,走到到空,还没找到,这个值不存在。

template <class K>
bool BSTree<K>::Find(const K& key)
{
	node* cur = _root;
	while (cur)
	{
		if (key < cur->_key)//小就往左走
		{
			cur = cur->_left;
		}
		else if (key > cur->_key)//大就往右走
		{
			cur = cur->_right;
		}
		else//找到了
		{
			return true;
		}
	}
	return false;
}

2. 搜索二叉树的插入

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

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

template <class K>
bool BSTree<K>::Insert(const K& key)
{
	//树为空,则直接新增节点,赋值给root指针
	if (_root == nullptr)
	{
		_root = new node(key);
		return true;
	}
	node* parent = nullptr;
	node* cur = _root;
	while (cur)//找到key该去的位置
	{
		parent = cur;
		if (cur->_key < key)//大就往右走
		{
			cur = cur->_right;
		}
		else if (cur->_key > key)//小就往左走
		{
			cur = cur->_left;
		}
		else//有相等的值了无法再插入了
		{
			return false;
		}
	}
	if (parent->_key < key)
	{
		parent->_right = new node(key);
	}
	else
	{
		parent->_left = new node(key);
	}

	return true;
}

3.搜索二叉树的删除

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

a. 要删除的结点无孩子结点

b. 要删除的结点只有左孩子结点

c. 要删除的结点只有右孩子结点

d. 要删除的结点有左、右孩子结点

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

情况a:删除该结点且使被删除节点的双亲结点指向被删除节点的左孩子结点--直接删除

情况b:删除该结点且使被删除节点的双亲结点指向被删除结点的右孩子结点--直接删除

情况c:在它的右子树中寻找中序下的第一个结点(关键码最小),或者在它的左子树中寻找中序下的第一个结点(关键码最大)用它的值填补到被删除节点中,再来处理该结点的删除问题--替换法删除。

template <class K>
bool BSTree<K>::Erase(const K& key)
{
	node* parent = nullptr;
	node* cur = _root;
	while (cur)
	{
		
		if (cur->_key > key)
		{
			parent = cur;
			cur = cur->_left;
		}
		else if (cur->_key < key)
		{
			parent = cur;
			cur = cur->_right;
		}
		else//找到了就跳出循环
		{
			break;
		}
	}
	if (cur == nullptr)//cur走到空就意味着没找到
	{
		return false;
	}
	if (cur->_left == nullptr)//左为空 
	{
		if (cur == _root)
		{
			_root = cur->_right;
		}
		else if (cur == parent->_left)
		{
			parent->_left = cur->_right;
		}
		else if (cur == parent->_right)
		{
			parent->_right = cur->_right;
		}
		delete cur;
		return true;
	}
	else if (cur->_right == nullptr)//右为空
	{
		if (cur == _root)
		{
			_root = cur->_left;
		}
		else if (cur == parent->_left)
		{
			parent->_left = cur->_left;
		}
		else if (cur == parent->_right)
		{
			parent->_right = cur->_left;
		}
		delete cur;
		return true;
	}
	else//左右都不为空,去找它左树最大的节点替换它的值,再删除左树最大的节点
        //下面的图有做说明
	{
		node* parent = nullptr;
		node* leftMax = cur;
		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;
		}
		delete leftMax;
		return true;
	}
	return false;
}

三、搜索二叉树的完整代码实现

#pragma once

#include <iostream>
using namespace std;


template <class K>
struct BSTreeNode
{
	BSTreeNode* _left;
	BSTreeNode* _right;
	K _key;

	BSTreeNode(const K& key)
		:_left(nullptr)
		,_right(nullptr)
		,_key(key)
	{}
};

template <class K>
class BSTree
{
private:
	BSTreeNode<K>* _root;
	typedef BSTreeNode<K> node;
public:
	BSTree()
		:_root(nullptr)
	{

	}
	~BSTree()
	{
		Destroy(_root);
	}
	//增删查
	bool Insert(const K& key);
	bool Find(const K& key);
	bool Erase(const K& key);
	//中序遍历
	void InOrder();
	void _InOrder(node* root);
	//增删查的递归实现
	bool InsertR(const K& key);
	bool _InsertR(const K& key, node*& root);
	//为了对节点进行修改,这里的插入和删除的节点必须用引用传,这里是一个细节 
	bool EraseR(const K& key);
	bool _EraseR(const K& key, node*& root);
	bool FindR(const K& key);
	bool _FindR(const K& key, node* root);

	void Destroy(node* root);
};


template <class K>
bool BSTree<K>::Insert(const K& key)
{
	//树为空,则直接新增节点,赋值给root指针
	if (_root == nullptr)
	{
		_root = new node(key);
		return true;
	}
	node* parent = nullptr;
	node* cur = _root;
	while (cur)//找到key该去的位置
	{
		parent = cur;
		if (cur->_key < key)//大就往右走
		{
			cur = cur->_right;
		}
		else if (cur->_key > key)//小就往左走
		{
			cur = cur->_left;
		}
		else//有相等的值了无法再插入了
		{
			return false;
		}
	}
	if (parent->_key < key)
	{
		parent->_right = new node(key);
	}
	else
	{
		parent->_left = new node(key);
	}

	return true;
}

template <class K>
bool BSTree<K>::Find(const K& key)
{
	node* cur = _root;
	while (cur)
	{
		if (key < cur->_key)//小就往左走
		{
			cur = cur->_left;
		}
		else if (key > cur->_key)//大就往右走
		{
			cur = cur->_right;
		}
		else//找到了
		{
			return true;
		}
	}
	return false;
}

template <class K>
bool BSTree<K>::Erase(const K& key)
{
	node* parent = nullptr;
	node* cur = _root;
	while (cur)
	{
		
		if (cur->_key > key)
		{
			parent = cur;
			cur = cur->_left;
		}
		else if (cur->_key < key)
		{
			parent = cur;
			cur = cur->_right;
		}
		else//找到了就跳出循环
		{
			break;
		}
	}
	if (cur == nullptr)//cur走到空就意味着没找到
	{
		return false;
	}
	if (cur->_left == nullptr)//左为空 
	{
		if (cur == _root)
		{
			_root = cur->_right;
		}
		else if (cur == parent->_left)
		{
			parent->_left = cur->_right;
		}
		else if (cur == parent->_right)
		{
			parent->_right = cur->_right;
		}
		delete cur;
		return true;
	}
	else if (cur->_right == nullptr)//右为空
	{
		if (cur == _root)
		{
			_root = cur->_left;
		}
		else if (cur == parent->_left)
		{
			parent->_left = cur->_left;
		}
		else if (cur == parent->_right)
		{
			parent->_right = cur->_left;
		}
		delete cur;
		return true;
	}
	else//左右都不为空,去找它左树最大的节点替换它的值,再删除左树最大的节点
	{
		node* parent = nullptr;
		node* leftMax = cur;
		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;
		}
		delete leftMax;
		return true;
	}
	return false;
}

template <class K>
void BSTree<K>::_InOrder(node* root)
{
	if (root == nullptr)
		return;
	_InOrder(root->_left);
	cout << root->_key << " ";
	_InOrder(root->_right);
}

template <class K>
void BSTree<K>::InOrder()
{
	_InOrder(_root);
	cout << endl;
}


template <class K>
bool BSTree<K>::EraseR(const K& key)
{
	return _EraseR(key, _root);
}


template <class K>
bool BSTree<K>::_EraseR(const K& key, node*& root)
{
	if (root == nullptr)
		return false;
	if (root->_key < key)
	{
		_EraseR(key, root->_right);
	}
	else if (root->_key > key)
	{
		_EraseR(key, root->_left);
	}
	else//找到要删除的节点了
	{
		//准备开始删除
		node* del = root;
		if (root->_left == nullptr)
		{
			root = root->_right;
		}
		else if (root->_right == nullptr)
		{
			root = root->_left;
		}
		else
		{
			node* leftMax = root->_left;
			while (leftMax->_right)
			{
				leftMax = leftMax->_right;
			}
			swap(root->_key, leftMax->_key);
			return _EraseR(key, root->_left);//交换完后去要删除节点的左子树删除最大的节点
		}
		delete del;
	}
	return true;
}

template <class K>
bool BSTree<K>::FindR(const K& key)
{
	return _FindR(key, _root);
}

template <class K>
bool BSTree<K>::_FindR(const K& key, node* root)
{
	if (root == nullptr)
	{
		return false;
	}
	if (root->_key < key)
	{
		return _FindR(key, root->_right);
	}
	else if (root->_key > key)
	{
		return _FindR(key, root->_left);
	}
	else
	{
		return true;
	}
}

template <class K>
bool BSTree<K>::InsertR(const K& key)
{
	return _InsertR(key, _root);
}
template <class K>
bool BSTree<K>::_InsertR(const K& key, node*& root)
{
	if (root == nullptr)
	{
		root = new node(key);
		return true;
	}
	if (root->_key < key)
	{
		return _InsertR(key, root->_right);
	}
	else if (root->_key > key)
	{
		return _InsertR(key, root->_left);
	}
	else
	{
		return false;
	}
}

template <class K>
void BSTree<K>::Destroy(node* root)
{
	if (root == nullptr)
		return;
	Destroy(root->_left);
	Destroy(root->_right);
	delete root;
}
//test.c

#include "BinarySearchTree.h"

int main()
{
	BSTree<int> bs;

	int arr[] = { 1,3,6,4,7,8,10,14,13 };
	for (auto e : arr)
	{
		bs.Insert(e);
	}
	bs.InOrder();

	bs.EraseR(1);
	bs.InOrder();

	bs.Insert(20);
	bs.InsertR(9);
	bs.InOrder();

	bool ret = bs.FindR(20);
	cout << ret << endl;


	return 0;
}

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

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

相关文章

Vue3 源码解读系列(五)——响应式

响应式 响应式的本质是当数据变化后会自动执行某个函数。 映射到组件的实现就是&#xff0c;当数据变化后&#xff0c;会自动触发组件的重新渲染。 响应式的两个核心流程&#xff1a; 依赖收集派发通知 Vue2 Vue2 中只有 data 中定义的数据才是响应式的&#xff0c;因为 d…

从底层认识哈希表【C++】

目录 一. unordered系列关联式容器 二. unordered_map的文档介绍 接口使用 三. 底层实现 &#xff08;1&#xff09;哈希概念 例&#xff1a; &#xff08;2&#xff09;哈希冲突 &#xff08;3&#xff09;冲突解决 1.闭散列​​​​​​​ 闭散列框架 插入 查找 删除 2.开散…

中国净初级生产力年度合成产品NPP(MYD17A3H.006)

中国净初级生产力年度合成产品NPP&#xff08;MYD17A3H.006&#xff09;由航天宏图实验室提供&#xff0c;根据NASA MODIS数据&#xff08;MYD17A3H.006&#xff09;通过航天宏图 Smoother计算得到的平滑后NPP产品&#xff0c;解决了影像云雾覆盖、像元异常值等问题。对处理后的…

黑群晖断电导致存储空间已损毁修复记录

黑群晖断电2次&#xff0c;担心的事情还是发生了&#xff0c;登录后提示存储空间已损毁...... 开干&#xff01;&#xff01; 修复方式&#xff1a; 1.使用SSH登录到群晖&#xff0c;查看相关信息 # 登录后先获取最高权限 rootDiskStation:~# sudo -i # 检测存储池状态 root…

2、LeetCode之两数相加

给你两个非空的链表&#xff0c;表示两个非负的整数。它们每位数字都是按照逆序的方式存储的&#xff0c;并且每个节点只能存储一位数字。请你将两个数相加&#xff0c;并以相同形式返回一个表示和的链表。你可以假设除了数字0之外&#xff0c;这两个数都不会以0开头。 输入&am…

循环队列(出队、入队、判空、长度、遍历、取头)(数据结构与算法)

循环队列 涉及到移动、赋值原队列参数的函数参数列表如front&#xff0c;rear&#xff0c;都最好别用&引用&#xff0c;否则会修改原队列中的地址和数值如&#xff1a;SqQueue &Q 使用SqQueue Q作参数列表时&#xff0c;函数引入的只是一份副本&#xff0c;不会修改原队…

ImportError: DLL load failed while importing _iterative: %1 不是有效的 Win32 应用程序。

问题&#xff1a;这个错误是由于导入的模块 _iterative 找不到有效的 Win32 应用程序导致的。可能是由于你的环境中缺少了某个依赖库或者是版本不匹配的问题。 解决方法&#xff1a; 可以尝试以下几种&#xff1a; 确保你的环境中已经安装了所有需要的依赖库&#xff0c;并且…

分享 | 软件测试的基本流程是什么?软件测试流程详细介绍

软件测试 软件测试和软件开发一样&#xff0c;是一个比较复杂的工作过程&#xff0c;如果无章法可循&#xff0c;随意进行测试势必会造成测试工作的混乱。为了使测试工作标准化、规范化&#xff0c;并且快速、高效、高质量地完成测试工作&#xff0c;需要制订完整且具体的测试…

JAVAEE 初阶 多线程基础(一)

多线程基础 一.线程的概念二.为什么要有线程三.进程和线程的区别和关系四.JAVA的线程和操作系统线程的关系五.第一个多线程程序1.继承Thread类 一.线程的概念 一个线程就是一个 “执行流”. 每个线程之间都可以按照顺讯执行自己的代码. 多个线程之间 “同时” 执行着多份代码 同…

Leetcode 剑指 Offer II 053. 二叉搜索树中的中序后继

题目难度: 中等 原题链接 今天继续更新 Leetcode 的剑指 Offer&#xff08;专项突击版&#xff09;系列, 大家在公众号 算法精选 里回复 剑指offer2 就能看到该系列当前连载的所有文章了, 记得关注哦~ 题目描述 给定一棵二叉搜索树和其中的一个节点 p &#xff0c;找到该节点在…

亚马逊云科技AI创新应用下的托管在AWS上的数据可视化工具—— Amazon QuickSight

目录 Amazon QuickSight简介 Amazon QuickSight的独特之处 Amazon QuickSight注册 Amazon QuickSight使用 Redshift和Amazon QuickSightt平台构建数据可视化应用程序 构建数据仓库 数据可视化 Amazon QuickSight简介 亚马逊QuickSight是一项可用于交付的云级商业智能 (BI…

AI智剪:批量剪辑实战,技巧与实例

随着人工智能技术的不断发展&#xff0c;越来越多的领域开始应用AI技术提升工作效率和质量。其中&#xff0c;AI智剪技术在视频剪辑领域的应用也越来越广泛。AI智剪是一种基于人工智能技术的视频剪辑方法&#xff0c;通过机器学习算法对视频进行自动分析和处理&#xff0c;实现…

VBA技术资料MF84:判断文件夹是否存在并创建

我给VBA的定义&#xff1a;VBA是个人小型自动化处理的有效工具。利用好了&#xff0c;可以大大提高自己的工作效率&#xff0c;而且可以提高数据的准确度。我的教程一共九套&#xff0c;分为初级、中级、高级三大部分。是对VBA的系统讲解&#xff0c;从简单的入门&#xff0c;到…

Ant Design for Figma设计系统组件库 支持变量 非社区版

Ant Design for Figma 是基于 Ant Design 设计系统的 Figma 组件库&#xff0c;提供丰富的 UI 组件和交互功能&#xff0c;帮助设计师快速构建高质量的 Figma 设计稿。 Ant Design for Figma 继承了 Ant Design 的设计理念和风格&#xff0c;提供丰富的 UI 组件和交互功能&…

在Go编程中调用外部命令的几种场景

1.摘要 在很多场合, 使用Go语言需要调用外部命令来完成一些特定的任务, 例如: 使用Go语言调用Linux命令来获取执行的结果,又或者调用第三方程序执行来完成额外的任务。在go的标准库中, 专门提供了os/exec包来对调用外部程序提供支持, 本文将对调用外部命令的几种使用方法进行总…

NET8 BlazorAuto渲染模式

.NET8发布后&#xff0c;Blazor支持四种渲染方式 静态渲染&#xff0c;这种页面只可显示&#xff0c;不提供交互&#xff0c;可用于网页内容展示使用Blazor Server托管的通过Server交互方式使用WebAssembly托管的在浏览器端交互方式使用Auto自动交互方式&#xff0c;最初使用 …

【项目设计】网络版五子棋游戏

文章目录 一、项目介绍1. 项目简介2. 开发环境3. 核心技术4. 开发阶段 二、环境搭建1. 安装 wget 工具2. 更换 yum 源3. 安装 lrzsz 传输工具4. 安装⾼版本 gcc/g 编译器5. 安装 gdb 调试器6. 安装分布式版本控制工具 git7. 安装 cmake8. 安装 boost 库9. 安装 Jsoncpp 库10. 安…

Python使用大连理工情感本体提取文本的情感倾向

import pandas as pd # 导入词典 df pd.read_excel(Sentiment_dictionary\大连理工情感词汇本体\情感词汇本体.xlsx) # 我们暂时只使用 [词语,词性种类,词义数,词义序号,情感分类,强度,极性] df df[[词语, 词性种类, 词义数, 词义序号, 情感分类, 强度, 极性]] df.head()# 按…

任你五花八门预训练方法,我自监督学习依然能打!

长时间没看论文&#xff0c;外面已经发展成这样了&#xff1f; 以下都是新paper&#xff0c;挑了几个感兴趣的&#xff0c;一起粗略看看吧~ Battle of the Backbones: A Large-Scale Comparison of Pretrained Models across Computer Vision Tasks GitHub | https://github.…