C++ 二叉搜索树

1. 二叉搜索树的概念

二叉搜索树是一种查找效率很高的数据结构,它要求任何一个节点的左子树比当前节点小,右子树比当前节点大,满足这样的条件的树叫做二叉搜索树

二叉搜索树分为Key模型和Key-Value模型,Key模型主要用于查找,Key-Value模型在查找时还能取到Key对应的Value值

两种模型分别有什么用,会在后面讲,我们先用代码实现

二叉搜索树不建议修改Key值,因此我们实现增、删、查的功能即可

2. Key模型

2.1 节点的定义

template<class K>
struct BSTNode
{
	BSTNode(const K& key)
		:_left(nullptr)
		, _right(nullptr)
		, _key(key)
	{}
    
	BSTNode<K>* _left;
	BSTNode<K>* _right;
	K _key;
};

2.2 插入

bool insert(const K& key)
{
	if (_root == nullptr)
	{
		_root = new Node(key);
		return true;
	}

	Node* parent = nullptr;
	Node* cur = _root;
	while (cur)
	{
		if (key < cur->_key)
		{
			parent = cur;
			cur = cur->_left;
		}
		else if (key > cur->_key)
		{
			parent = cur;
			cur = cur->_right;
		}
		else return false;
	}

	Node* newnode = new Node(key);
	if (key < parent->_key) parent->_left = newnode;
	else parent->_right = newnode;
	return true;
}

2.3 查找

Node* 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 cur;
	}
	return nullptr;
}

2.4 删除

由于插入、查找较简单,不再过多叙述,这里重点讲讲删除

删除总共有三种情况(记要删除的节点为cur,它的父亲节点为parent):

  1. cur的左右子树都为空
  2. cur的左/右子树为空
  3. cur的左右子树都不为空

第二种情况包含第一种情况,因此一二可以合并为一种

cur的左子树为空:如果cur是parent的左子树,parent的左子树指向cur的右子树;如果cur是parent的右子树,parent的右子树指向cur的右子树

cur的右子树为空:如果cur是parent的左子树,parent的左子树指向cur的左子树;如果cur的parent的右子树,parent的右子树指向cur的左子树

在这里插入图片描述

cur的左右子树都不为空:使用替换法,找cur左子树的最大节点或者cur右子树的最小节点中的值去替换cur中的值,再删除替换的节点

为什么是左子树的最大节点或者右子树的最小节点?首先直接删除肯定是不好做的,我们需要保证替换的值要比左子树所有的值要大,比右子树所有的值要小,这样替换后才能继续保持二叉搜索树的性质,而左子树的最大节点或者cur右子树的最小节点满足这点

在这里插入图片描述

还有种特殊情况,如果要删除的节点为根,且根的左右子树有一个为空,此时根节点需要更换,单独处理即可

在这里插入图片描述

bool erase(const K& key)
{
	Node* parent = nullptr;
	Node* cur = _root;
	while (cur)
	{
		if (key < cur->_key)
		{
			parent = cur;
			cur = cur->_left;
		}
		else if (key > cur->_key)
		{
			parent = cur;
			cur = cur->_right;
		}
		else
		{
			if (cur == _root && (cur->_left == nullptr || cur->_right == nullptr))
			{
				if (cur->_left == nullptr)
				{
					_root = cur->_right;
					delete cur;
				}
				else
				{
					_root = cur->_left;
					delete cur;
				}
			}
			else
			{
				if (cur->_left == nullptr)
				{
					if (cur == parent->_left) parent->_left = cur->_right;
					else parent->_right = cur->_right;
					delete cur;
				}
				else if (cur->_right == nullptr)
				{
					if (cur == parent->_left) parent->_left = cur->_left;
					else parent->_right = cur->_left;
					delete cur;
				}
				else
				{
					Node* leftMax = cur->_left;
					Node* leftMaxP = cur;
					while (leftMax->_right)
					{
						leftMaxP = leftMax;
						leftMax = leftMax->_right;
					}
					swap(leftMax->_key, cur->_key);

					if (leftMax == leftMaxP->_right) leftMaxP->_right = leftMax->_left;
					else leftMaxP->_left = leftMax->_left;
					delete leftMax;
				}
			}
			return true;
		}
	}
	return false;
}

3. Key_Value模型

相较于Key模型,Key_Value模型每个节点多一个Value值,但是查找、删除依旧按照Key比较

除了插入,查找和删除的代码不变

3.1 节点的定义

template<class K, class V>
struct BSTNode
{
	BSTNode(const K& key, const V& value)
		:_left(nullptr)
		, _right(nullptr)
		, _key(key)
		, _value(value)
	{}

	BSTNode<K, V>* _left;
	BSTNode<K, V>* _right;
	K _key;
	V _value;
};

3.2 插入

bool insert(const K& key, const V& value)
{
	if (_root == nullptr)
	{
		_root = new Node(key, value);
		return true;
	}

	Node* parent = nullptr;
	Node* cur = _root;
	while (cur)
	{
		if (key < cur->_key)
		{
			parent = cur;
			cur = cur->_left;
		}
		else if (key > cur->_key)
		{
			parent = cur;
			cur = cur->_right;
		}
		else return false;
	}

	Node* newnode = new Node(key, value);
	if (key < parent->_key) parent->_left = newnode;
	else parent->_right = newnode;
	return true;
}

4. 二叉搜索树的应用

Key模型的二叉搜索树典型的应用是字典,我们将字典的所有单词插入到搜索树中,当需要查找某个单词时,就能较高的效率查找到

Key-Value模型的二叉搜索树典型的应用是翻译字典/Key的出现次数,将单词和单词的中文意思插入到搜索树中,查找到该单词的同时能知道该单词的意思

5. 性能分析

在大部分情况下,插入的数据顺序是乱的,此时查找效率接近于 O ( l o g 2 N ) O(log_2{N}) O(log2N)

但是,如果插入的数据顺序有序或接近于有序,查找的效率就变成了 O ( N ) O(N) O(N)

在这里插入图片描述

为了在插入数据顺序有序或接近于有序的情况下,效率依旧保持为 O ( l o g 2 N ) O(log_2{N}) O(log2N),二叉搜索树还可以优化为AVL树和红黑树,这是下篇文章的内容

5.常见的搜索算法

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值