C++ 二叉树进阶:二叉搜索树

目录

二叉搜索树的概念

二叉搜索树的实现

基本结构

插入

1,当树是空树的时候

2,当树不为空的时候

3,纠正后的代码

查找

删除

1,左为空或右为空

 2,左右都不为空

3,删除的完整代码:

二叉搜索树的完整代码

BSTree.h

test.cpp

二叉搜索树的应用

Key 模型

Key-Value 模型

改造二叉搜索树为KV结构

BSTree.h

test.cpp

二叉搜索树的性能分析


二叉搜索树的概念

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

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

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

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

注意:二叉搜索树key值不能相同。

二叉搜索树中序遍历是有序的,因为二叉搜索树的定义决定了左子树节点值小于根节点值、右子树节点值大于等于根节点值(每一颗子树也满足),而中序遍历先左子树、再根节点、后右子树的方式使得遍历结果自然有序。 


二叉搜索树的实现

基本结构

二叉搜索树中的每个节点包含两个指针,分别指向左子树和右子树,以及一个存储关键值(key 值)的数据域。这种结构使得二叉搜索树能够以二叉树的形式组织数据,并通过比较节点的关键值来进行高效的查找、插入和删除操作。

二叉搜索树不能修改里面的key值,如果修改了就会破坏二叉搜索树的结构。

//节点的定义
template<class K>
struct BSTreeNode
{
	BSTreeNode<K>* _left;   //左节点
	BSTreeNode<K>* _right;  //右节点
	K _key;      //存储 key 值
	BSTreeNode(const K& key)  //构造函数完成初始化
		:_left(nullptr)
		,_right(nullptr)
		,_key(key)
	{}
};

template <class K>  //key 关键字,进行比较
class BSTree  //Binary Search Tree
{
	typedef BSTreeNode<K> Node;
private:
	Node* _root = nullptr;  //在类内进行成员初始化
};

插入

1,当树是空树的时候

直接定义一个节点把该节点给 _root。

2,当树不为空的时候

不是空树,去找这个需要插入的位置,插入一定是找一个空的位置,不可能替代某个位置。

如果插入时是相同的元素,则插入失败,因为二叉搜索树不允许出现相同的 key 值。

#pragma once
//节点的定义
template<class K>
struct BSTreeNode
{
	BSTreeNode<K>* _left;   //左节点
	BSTreeNode<K>* _right;  //右节点
	K _key;      //存储 key 值
	BSTreeNode(const K& key)  //构造函数完成初始化
		:_left(nullptr)
		,_right(nullptr)
		,_key(key)
	{}
};

template <class K>  //key 关键字,进行比较
class BSTree  //Binary Search Tree
{
	typedef BSTreeNode<K> Node;
public:
	bool Insert(const K& key)
	{
		//1,根为空的时候
		if (_root == nullptr)
		{
			_root = new Node(key);
		}
		//2,根不为空的时候
		Node* cur = _root;
		while (cur)
		{
			if (key > cur->_key)   //插入的key比当前节点大就往右边走
			{
				cur = cur->_right;   
			}
			else if (key < cur->_key) //插入的key比当前节点小就往左边走
			{
				cur = cur->_left;
			}
			else                     
			{
				return false; //插入的key和当前节点相等,就插入失败
			}
		}
		cur = 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;
	}
private:
	Node* _root = nullptr;  //在类内进行成员初始化
};
void Test() 
{
	BSTree<int> t;
	int a[] = { 5,3,4,1,7,8,2,6,0,9 };
	for (auto e : a)
	{
		t.Insert(e);
	}
	t.InOrder();  
}

通过测试我们会发现,这里只有 5 插入成功了,也就是根节点插入成功,那么这段代码存在一定的问题,如何解决呢???

问题:在循环中,只是不断地更新 cur 指针,让它指向树中的不同节点,但没有记录下新节点应该连接的父节点。
    当找到空位置并创建新节点 cur = new Node(key) 后,新节点与树中的其他节点没有任何连接,导致新节点成为一个孤立的节点,没有真正插入到树中。

改进:

  1. 添加一个 parent 指针来记录新节点的父节点。在循环中,当更新 cur 指针时,也同时更新 parent 指针。
  2. 在找到插入位置后,根据 keyparent->_key 的大小关系,将新节点连接到父节点的左子树或右子树。

3,纠正后的代码

#pragma once
//节点的定义
template<class K>
struct BSTreeNode
{
	BSTreeNode<K>* _left;   //左节点
	BSTreeNode<K>* _right;  //右节点
	K _key;      //存储 key 值
	BSTreeNode(const K& key)  //构造函数完成初始化
		:_left(nullptr)
		,_right(nullptr)
		,_key(key)
	{}
};

template <class K>  //key 关键字,进行比较
class BSTree  //Binary Search Tree
{
	typedef BSTreeNode<K> Node;
public:
	bool Insert(const K& key)
	{
		//当树是空树的时候
		if (_root == nullptr) 
		{
			_root = new Node(key);
			return true;
		}
		//树不为空的时候
		Node* parent = nullptr;  //用一个节点来记录cur的父亲
		Node* cur = _root;
		while (cur)
		{
			if (key > cur->_key) 
			{
				parent = cur;   
				cur = cur->_right;	
			}
			else if (key < cur->_key) 
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				return false;
			}
		}
		cur = new Node(key);
		if (key > parent->_key)  //判断到底是属于父亲的左树还是右树
			parent->_right = cur;
		else
			parent->_left = c
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值