二叉搜索树

1. 概念

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

  1. 若它的左子树不为空,则左子树上所有节点的值都小于或大于根节点的值。
  2. 若它的右子树不为空,则右子树上所有节点的值都大于或小于根节点的值。
  3. 它的左右子树也分别为二叉搜索树。

比如

在这里插入图片描述
如果按照中序遍历这颗二叉树,得到的序列为升序序列。

2. 二叉搜索树的实现

这里按照左子树的值小于根节点的值,右子树的值大于根节点的值的方式实现。

2.1 插入

先定义这样的一个模板,成员分别为存储的数据、左孩子指针和右孩子指针

template<class T>
struct BSTreeNode 
{
	BSTreeNode(const T& val)
		:_val(val),
		_left(nullptr),
		_right(nullptr)
	{}
	T _val;
	BSTreeNode<T>* _left;
	BSTreeNode<T>* _right;
};

插入大致过程如下,这里默认不能插入相同数据。
在这里插入图片描述

bool insert(const T& val)
{
	if (_root == nullptr) //空树
	{
		_root = new Node(val);
		return true;
	}
	Node* cur = _root;  
	Node* parent = _root; //保留父亲结点
	while (cur)
	{
		if (val > cur->_val)        //val值大,往右
		{
			parent = cur;
			cur = cur->_right;
		}
		else if (val < cur->_val)  //val值小,往左
		{
			parent = cur;
			cur = cur->_left;
		}
		else					   //相等  返回
			return false;
	}

	cur = new Node(val);

	if (val > parent->_val)        //判断插入在父节点的哪一边   
		parent->_right = cur;
	else
		parent->_left = cur;

	return true;
}

2.2 删除

首先找到待删除的结点,方式和也是比较存储的数据的大小。

找到后,需要分以下两种情况进行删除
在这里插入图片描述
在这里插入图片描述
总结:
对于情况1:先找待删除结点,将这个结点的孩子交给这个结点的父亲管理。
对于情况2:先找待删除结点,再找左树的最右结点右数的最左结点。交换数据,再删除,此时会转化为情况1。

代码如下

bool erase(const T& val)
{
	Node* parent = nullptr;
	Node* cur = _root;
	while (cur)
	{
		if (val > cur->_val)
		{
			parent = cur;
			cur = cur->_right;
		}
		else if (val < cur->_val)
		{
			parent = cur;
			cur = cur->_left;
		}
		else
		{
			//0个或1个孩子
			if (cur->_left == nullptr)
			{
				if (parent == nullptr) //删除结点为根结点
					_root = cur->_right;
				else
				{
					if (parent->_left == cur)
						parent->_left = cur->_right;
					else
						parent->_right = cur->_right;
				}
				delete cur;
				return true;
			}
			else if (cur->_right == nullptr)
			{
				if (parent == nullptr) //删除结点为根结点
					_root = cur->_left;
				else
				{
					if (parent->_left == cur)
						parent->_left = cur->_left;
					else
						parent->_right = cur->_left;
				}
				delete cur;
				return true;
			}

			else //两个孩子
			{
				Node* rightMinPar = cur;			//最左结点的父亲
				Node* rightMin = cur->_right;    //最左结点

				//找右子树的最左结点
				while (rightMin->_left)
				{
					rightMinPar = rightMin;
					rightMin = rightMin->_left;
				}
				
				// 赋值
				cur->_val = rightMin->_val;

				if (rightMinPar->_right==rightMin)
					rightMinPar->_right = rightMin->_right;
				else
					rightMinPar->_left = rightMin->_right;

				delete rightMin;
				return true;
			}
		}
	}
	return false;
}

2.3 查找

由于二叉搜索树的性质,不允许修改存储的数据,修改会破坏搜索树的性质

//查找
Node* find(const T& val)
{
	Node* cur = _root;
	while (cur)
	{
		if (val > cur->_val)
			cur = cur->_right;
		else if (val < cur->_val)
			cur = cur->_left;
		else 
			return cur;
	}
	return nullptr;
}

2.4 中序遍历

再类外无法访问私有成员,因此,再类内会套用一层函数。

void inorder() //公有
{
	Inorder(_root);
	cout << endl;
}

void Inorder(Node* root)  //私有
{
	if (root == nullptr)
		return;
	Inorder(root->_left);
	cout << root->_val << " ";
	Inorder(root->_right);
}

测试程序

int main()
{
	BSTree<int> bs;
	int a[] = { 8,3,10,1,6,14,4,7,13 };

	for (auto& val : a)
	{
		bs.insert(val);
	}
	bs.inorder();

	for (auto& val : a)
	{
		bs.erase(val);
		bs.inorder();
	}
	return 0;
}

运行结果

3. 搜索树的应用

  1. K模型:K模型即只有key作为关键码,结构中只需要存储key即可,关键码就是要搜索到的值。

  2. KV模型:每一个关键码key,都有与之对应的值value,即<key, value>的键值对。通过key查找value。比如单词查询。

  3. AVL树和红黑树。

搜索树在大多数情况性能都比较好,但是最差情况下,会退化为单支树。此时效率就不高。AVL树和红黑树可以解决这种情况。

4. 完整代码

BSTree.h

#pragma once
#include<iostream>
using namespace std;

template<class T>
struct BSTreeNode 
{
	BSTreeNode(const T& val)
		:_val(val),
		_left(nullptr),
		_right(nullptr)
	{}
	T _val;
	BSTreeNode<T>* _left;
	BSTreeNode<T>* _right;
};

template<class T>
class BSTree
{
	typedef BSTreeNode<T> Node;
public:

	//插入
	bool insert(const T& val)
	{
		if (_root == nullptr) //空树
		{
			_root = new Node(val);
			return true;
		}
		Node* cur = _root;  
		Node* parent = _root; //保留父亲结点
		while (cur)
		{
			if (val > cur->_val)        //val值大,往右
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (val < cur->_val) //val值小,往左
			{
				parent = cur;
				cur = cur->_left;
			}
			else								//相等  返回
				return false;
		}

		cur = new Node(val);

		if (val > parent->_val)       
			parent->_right = cur;
		else
			parent->_left = cur;

		return true;
	}

	//查找
	Node* find(const T& val)
	{
		Node* cur = _root;
		while (cur)
		{
			if (val > cur->_val)
				cur = cur->_right;
			else if (val < cur->_val)
				cur = cur->_left;
			else 
				return cur;
		}
		return nullptr;
	}

	//删除
	bool erase(const T& val)
	{
		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)
		{
			if (val > cur->_val)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (val < cur->_val)
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				//0个或1个孩子
				if (cur->_left == nullptr)
				{
					if (parent == nullptr) //删除结点为根结点
						_root = cur->_right;
					else
					{
						if (parent->_left == cur)
							parent->_left = cur->_right;
						else
							parent->_right = cur->_right;
					}
					delete cur;
					return true;
				}
				else if (cur->_right == nullptr)
				{
					if (parent == nullptr) //删除结点为根结点
						_root = cur->_left;
					else
					{
						if (parent->_left == cur)
							parent->_left = cur->_left;
						else
							parent->_right = cur->_left;
					}
					delete cur;
					return true;
				}

				else //两个孩子
				{
					Node* rightMinPar = cur;			//最左结点的父亲
					Node* rightMin = cur->_right;    //最左结点

					//找右子树的最左结点
					while (rightMin->_left)
					{
						rightMinPar = rightMin;
						rightMin = rightMin->_left;
					}
					
					// 赋值
					cur->_val = rightMin->_val;

					if (rightMinPar->_right==rightMin)
						rightMinPar->_right = rightMin->_right;
					else
						rightMinPar->_left = rightMin->_right;

					delete rightMin;
					return true;
				}
			}
		}
		return false;
	}

	void inorder()
	{
		Inorder(_root);
		cout << endl;
	}

private:

	void Inorder(Node* root)
	{
		if (root == nullptr)
			return;
		Inorder(root->_left);
		cout << root->_val << " ";
		Inorder(root->_right);
	}

private:
	Node* _root = nullptr;
};
  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值