首页 > 其他分享 >二叉搜索树

二叉搜索树

时间:2024-11-09 14:47:25浏览次数:3  
标签:right cur 右子 二叉 搜索 key 节点 left

一.二叉搜索树介绍

        二叉搜索树又叫做二叉排序树,它拥有一些特殊的性质。

        1.若它的左子树不为空,那么左子树上面的节点全部小于根节点。

        2.若它的右子树不为空,那么右子树上面的节点全部大于根节点。

        3.它的左右子树也全部都是二叉搜素树,也就是同样遵循1,2点特征

二.二叉搜索树的使用

        由于二叉搜索树的特点,使得二叉搜索树在进行中序遍历的时候,恰好就是按照顺序排好的格式。

        

        在走中序遍历的时候,先左节点再根节点再右节点,例如本树:

        左0,根1,右2

        上面为左,根3,右4

        上面为左,根5,右子树

        左6,根7,右子树

        左0,根8,右子树

        左0,根9,右0

        所以整体的顺序就是0,1,2,3,4,5,6,7,8,9

        1.搜索二叉树的查找

                从根节点开始,通过与根节点比较比根节点大,则走到右子树,比根节点小则走到左子树,如此循环,极大降低了查找的时间复杂度。

        2.搜索二叉树的插入

                同搜索二叉树的查找一样,一直循环查找,找到可以插入的目标位置为nullptr后,通过在开始循环时记录父节点,在找到后,利用父节点和要插入的key大小比较选择插入到左节点还是右节点。

                在没有节点的时候,直接在根节点的位置插入即可。

        3.搜索二叉树的删除

                搜索二叉树的删除分为四种情况。

                1.要删除节点的左子树为空,右子树也为空

                2.要删除节点的左子树为空,右子树不为空

                3.要删除的节点左子树不为空,右子树为空

                4.要删除的节点左子树不为空,右子树为空

                首先分析第一种情况:

                  如果左右子树全为空,那么直接将该节点删除即可,并将父节点指向该节点的指针置空。

                第二种情况:

                  如果左子树为空,那么只需要将父节点指向该节点的指针,指向该节点的右子树即可,随后删除该节点。

                  不难看出,其实第一种情况可以合并到第二三种情况里面,只不过不是手动置空,而是让指针去指向一个空

                第三种情况:

                  与第二种情况一致,只是指向问题不一样。

                第四种情况:

                  这种情况比较复杂一点,如果左右子树都不为空的时候,我们最好不要去动这个节点,而是除了这个节点以外再度寻找一个节点取代它的位置,只需要更换一下key的数值即可,那么什么样的节点可以代替目标节点的位置而不会破坏搜索二叉树呢?

                  答案是,该节点左子树的最大值,也就是左子树一直往右找,或者右子树的最小值,也就是右子树一直往左找,这样找到的节点放到这个位置,完全可以取代源节点。

                  更换完毕后,我们只需要删除被更换的节点即可,我们拿右子树的最小值来说,在这种情况下,该节点的左节点一定为空,就相当于回到了第二三种情况里面,至于一些比较特殊的情况,在搜索二叉树的实现代码中会给与说明。

#include<iostream>
using namespace std;
template<class K>
struct BTtreeNode
{
	BTtreeNode* _left;
	BTtreeNode* _right;
	K _key;
	BTtreeNode(const K& key)
		:_left(nullptr)
		, _right(nullptr)
		, _key(key)
	{}
};
template<class K>
class BTtree
{
	typedef BTtreeNode<K> Node;
private:
	Node* _root;
public:
	BTtree()
		:_root(nullptr)
	{}
	bool insert(const K& key)
	{
		if (_root == nullptr)
		{
			_root = new Node(key);
			return true;
		}
		Node* cur = _root;
		Node* prev = nullptr;
		while (cur)
		{
			if (cur->_key > key)
			{
				prev = cur;
				cur = cur->_left;
			}
			else if(cur->_key<key)
			{
				prev = cur;
				cur = cur->_right;
			}
			else
			{
				return false;
			}
		}
		if (prev->_key < key)
		{
			prev->_right = new Node(key);
		}
		else
		{
			prev->_left = new Node(key);
		}
		return true;
	}
	void _Inorder(Node* root)
	{
		if (root == nullptr)
			return;
		_Inorder(root->_left);
		cout << root->_key << " ";
		_Inorder(root->_right);
	}
	void Inorder()
	{
		_Inorder(_root);
		cout << endl;
	}
	bool 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
			{
				//找到了,开始删除
				if (cur->_left == nullptr)
				{
					if (cur == _root)//后文使用了parent指针,如果要删除的节点恰好是根节点,那么parent就会使用空指针报错
					{				 //所以我们直接给出限制,如果是根节点的话直接让根节点指向左或者右子树即可
						_root = cur->_right;
					}
					else
					{
						if (parent->_key > key)
						{
							parent->_left = cur->_right;
						}
						else if (parent->_key < key)
						{
							parent->_right = cur->_right;
						}
					}
					delete cur;
				}
				else if (cur->_right == nullptr)
				{
					if (cur == _root)//补充上述情况
					{
						_root = cur->_left;
					}
					else
					{
						if (parent->_key > key)
						{
							parent->_left = cur->_left;
						}
						else if (parent->_key < key)
						{
							parent->_right = cur->_left;
						}
					}
					delete cur;
				}
				else//走到这种情况的时候,不需要再进行限制,因为这种情况里面的删除是更换删除,所以不需要动节点
				{
					Node* rightMinParent = cur;//因为后面涉及到rightMinParent的使用所以必须得保证其不为null,这种特殊情况后面附上图文解释
					Node* rightMin = cur->_right;
					while (rightMin->_left)
					{
						rightMinParent = rightMin;
						rightMin = rightMin->_left;
					}
					cur->_key = rightMin->_key;
					if (rightMinParent->_left == rightMin)//更换指向的时候,也分两种情况,并不是全部是父节点指向子节点的右子树,后面附上图文
					{
						rightMinParent->_left = rightMin->_right;
					}
					else
					{
						rightMinParent->_right = rightMin->_right;
					}
					delete rightMin;
				}
				return true;
			}
		}
		return false;
	}
	Node find(const K& key)
	{
		Node* cur = _root;
		while (cur)
		{
			if (cur->_key > key)
			{
				cur = cur->_left;
			}
			else if (cur->_key < key)
			{
				cur = cur->_right;
			}
			else
				return *cur;
		}
	}
};

指针不能给空的情况:

         

在这种情况下,并不会进入循环,所以rightMinParent也就是为null,后面就会涉及到空指针的使用,会报错。

下一个问题也可以用这张图来解释,普通情况下,我们只需要让父指针的左指向子节点的右子树即可,但是在这种情况下,rightMinParent在cur的位置,进行交换的是7和8,这时候我们需要删除8,更换指向的时候便是rightMinParent的右指向rightMin的右。 

三.搜索二叉树的K模型和KV模型

        K模型便是整个上述的例子,节点里面只有一个数据key,而KV模型则是每个节点里面有一个key还有一个value,这样使用key去进行排序(不能动,否则破坏搜索二叉树的功能),而value存储需求状况下的数据。

四.搜索二叉树的性能分析

        如果树是完全二叉树,那么只需要搜索logN次便可以找到目标节点。

        如果树是单支树的话,也就是一条链,那么就需要搜索一个遍,平均各种情况下(目标节点在第一个和目标节点在最后一个),一共需要搜索的次数为N/2次。

标签:right,cur,右子,二叉,搜索,key,节点,left
From: https://blog.csdn.net/2301_81245562/article/details/143634813

相关文章

  • C语言数据结构之二叉树(BINARY TREE)的多种数据类型存贮
    C语言数据结构之二叉树(BINARYTREE)的多种数据类型存贮用无类型指针(void*)来做为基本数据类型来存贮数据,将其他数据类型强制转化为无类型指针,从而达到目标!!!输出函数指针BTFunc比较函数指针BTCmpFunc返回值为整型值1、-1、0,表示大于、小于、相等代码如下:/*filename:btr......
  • 二叉树常用代码合集【C++版】(详细注释)
    二叉树常用代码合集【C++版】(详细注释)关键的地方有详细的注释。如果需要更详细的注释,可以丢给大模型再注释一遍。#include<iostream>#include<memory>#include<string>#definemv(x)std::move(x)#definecoutln(x)cout<<x<<endlusingnamespacestd;......
  • C++算法练习-day38——106.从中序和后序遍历序列构造二叉树
    题目来源:.-力扣(LeetCode)题目思路分析题目要求根据一棵二叉树的中序遍历(inorder)和后序遍历(postorder)结果重建这棵二叉树。中序遍历的特点是左子树->根节点->右子树,而后序遍历的特点是左子树->右子树->根节点。利用这两个遍历的特点,我们可以递归地重建整棵树。后序......
  • 网站显示在 Google 搜索结果中
    Google会自动查找可添加到Google索引中的网站;通常您无需执行任何操作,只需将网站发布到网络上即可。但是,网站有时会被遗漏。检查您的网站是否已收录到Google中,并了解如何让您的内容在Google搜索中更易于被发现。让网页出现在Google搜索结果中的基本核对清单首先,您需要问......
  • 关于 Google 搜索运作方式的深度指南
    Google搜索是一款全自动搜索引擎,会使用名为“网页抓取工具”的软件定期探索网络,找出可添加到Google索引中的网页。实际上,Google搜索结果中收录的大多数网页都不是手动提交的,而是我们的网页抓取工具在探索网络时找到并自动添加的。本文档从网站的角度介绍了Google搜索运作方......
  • 7-8 数据结构实验二 二叉树的遍历
    以二叉链表作存储结构,建立一棵二叉树。输出该二叉树的先序、中序、后序遍历序列,求出该二叉树的深度,并统计其叶子结点数。二叉链表的类型描述:typedefcharElemType;typedefstructBiNode{ElemTypedata;structBiNode*lchild,*rchild;}BiNode,*BiTree;......
  • 二叉搜索树、AVL(平衡二叉查找树)、红黑树
    一、二叉搜索树二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:若它的左子树不为空,则左子树上所有节点的值都小于根节点的值若它的右子树不为空,则右子树上所有节点的值都大于根节点的值它的左右子树也分别为二叉搜索树1.二叉搜索树的操作1.二......
  • JS数据结构之树和二叉树
    一、树树(Tree)是一种非常常见的数据结构,它模拟了自然界中树的结构,由节点(或称为顶点)组成,并且具有层次化的组织形式。树结构在计算机科学中广泛应用,例如在组织数据、管理信息层次以及算法设计中。1.基本概念节点(Node)根节点(Root):树的最顶端节点,没有父节点。内部节点(InternalNod......
  • Leecode热题100-104.二叉树中的最大路径和
    二叉树中的 路径 被定义为一条节点序列,序列中每对相邻节点之间都存在一条边。同一个节点在一条路径序列中 至多出现一次 。该路径 至少包含一个 节点,且不一定经过根节点。路径和 是路径中各节点值的总和。给你一个二叉树的根节点 root ,返回其 最大路径和 。示例......
  • 【书生实战营】L1G2000-玩转书生「多模态对话」与「AI搜索」产品
    MindSearch开源AI搜索引擎MindSearch:InternLM组织今年开源的AI搜索引擎(框架),基于多智能体技术将你提出的问题进行分析、拆解、网页搜索,最终给出有参考依据的高可信度回答。问题提问:目前生成式AI在学术和工业界有什么最新进展?2.2024年诺贝尔物理学奖为何会颁发......