二叉搜索树

  • 二叉搜索树的概念
  • 二叉搜索树的操作
  • 二叉搜索树的实现
  • 二叉搜索树的应用

一.二叉搜索树的概念

二叉搜索树(BST),又称为二叉排序树,它具有以下性质

  • 若左子树不为空,任何一个根节点比它的左子树节点都大
  • 若右子树不为空,任何一个根节点比它的右子树节点都小
  • 左右子树都为二叉搜索树

二.二叉搜索树的操作

1.插入

树为空,则直接新增节点,赋值给root指针,树不空,按二叉搜索树性质查找插入位置,插入新节点

2.查找

从根开始比较,比根大的往右边查找,比根小的往左边查找。最多查找高度次,走到空说明值不存在

3.删除

先按照二叉搜索树的性质查找删除节点,不存在则返回,否则进行删除:
a. 要删除的节点无孩子节点(可以直接删除)
b. 要删除的节点只有一个孩子节点(要托管给父亲)
c. 要删除的节点有两个孩子,在删除时,必须保证树结构不变,所以不可以直接删除,只能间接删除,找到该节点左树中的最大节点或者右树中的最小节点,然后交换两个节点,此时就转换到了a或者b这两种情况

三.二叉搜索树的实现

#pragma once



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

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

template<class K>
class BSTree
{
	typedef BSTreeNode<K> node;
public:
	//因为有了拷贝构造函数,所以编译器不会自动生成默认构造,default就是强制默认生成

	BSTree() = default;

	//拷贝构造
	BSTree(const BSTree<K>& bt)
	{
		_root = copy(bt._root);
	}
	~BSTree()
	{
		destroy(_root);
	}

	BSTree<K>& operator=(BSTree<K> t)
	{
		swap(t._root, _root);
		return *this;
	}

	//循环实现插入
	bool Insert(const K& key)
	{
		//如果根为空,直接插入
		if (_root == nullptr)
		{
			_root = new node(key);
			return true;
		}

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

		cur = new node(key);
		if (parent->_key < key)
		{
			parent->_right = cur;
		}
		else
		{
			parent->_left = cur;
		}

		return true;

	}

	//递归实现插入
	bool InsertR(const K& key)
	{
		return _InsertR(_root, key);
	}




	//中序遍历
	void InOrder()
	{
		_InOrder(_root);
	}

	//循环实现查找
	bool find(const K& key)
	{
		node* cur = _root;
		while (cur != nullptr)
		{
			if (cur->_key > key)
			{
				cur = cur->_left;
			}
			else if (cur->_key < key)
			{
				cur = cur->_right;
			}
			else
			{
				return true;
			}
		}

		return false;
	}

	//循环实现删除
	bool erase(const K& key)
	{
		node* parent = nullptr;
		node* cur = _root;
		while (cur != nullptr)
		{
			if (cur->_key < key)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (cur->_key > key)
			{
				parent = cur;
				cur = cur->_left;
			}
			else        找到了目标节点
			{
				//找到目标节点,进行删除,需要分三种情况(没有孩子节点,只有一个孩子节点,两个孩子节点都有),但实际只需分两种情况
				if (cur->_left == nullptr)   
				{
					if (parent == nullptr)
					{
						_root == cur->_right;
					}
					else
					{
						if (parent->_left == cur)
						{
							parent->_left = cur->_right;
						}
						else
						{
							parent->_right = cur->_right;
						}
					}
					
				}
				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;
						}
					}
					
				}
				else      
				{
					//有两个孩子节点,需要找出右边最小/左边最大,这样就可以保证删除后结构不改变
					node* pminRight = cur;
					node* minRight = cur->_right;
					while (minRight->_left != nullptr)
					{
						pminRight = minRight;
						minRight = minRight->_left;
					}

					cur->_key = minRight->_key;
					if (pminRight == cur)
					{
						pminRight->_right = minRight->_right;
					}
					else
					{
						pminRight->_left = minRight->_right;
					}

					delete minRight;					
				}

				return true;
			}
		}
		return false;
	}

	//递归实现删除
	bool EraseR(const K& key)
	{
		if (_root == nullptr)
		{
			return false;
		}
		return _EraseR(_root, key);
	}
	//递归实现查找
	bool FindR(const K& key)
	{
		return _root != nullptr && _FindR(_root, key);
	}
	
protected:


	node* copy(const node* root)
	{
		if (root == nullptr)
		{
			return nullptr;
		}

		node* new_node = new node(root->_key);
		new_node->_left = copy(root->_left);
		new_node->_right = copy(root->_right);

		return new_node;
	}

	void destroy(node*& root)
	{
		if (root == nullptr)
		{
			return;
		}

		destroy(root->_left);
		destroy(root->_right);
		delete root;
		root = nullptr;
	}
	void _InOrder(node* root)
	{
		if (root == nullptr)
		{
			return;
		}

		_InOrder(root->_left);
		cout << root->_key << " ";
		_InOrder(root->_right);
	}
	bool _EraseR(node*& root, const K& key)
	{
		if (root == nullptr)
		{
			return false;
		}

		if (root->_key < key)
		{
			return _EraseR(root->_right, key);
		}
		if (root->_key > key)
		{
			return _EraseR(root->_left, key);
		}

		node* del = root;
		if (root->_left == nullptr)
		{
			root = root->_right;
		}
		else if (root->_right == nullptr)
		{
			root = root->_left;
		}
		else
		{
			//node* pmaxLeft = root;
			node* maxLeft = root->_left;
			while (maxLeft->_right != nullptr)
			{
				//pmaxLeft = maxLeft;
				maxLeft = maxLeft->_right;
			}

			swap(maxLeft->_key, root->_key);
			//del = maxLeft;
			/*if (pmaxLeft->_left == maxLeft)
			{
				pmaxLeft->_left = maxLeft->_left;
			}
			else
			{
				pmaxLeft->_right = maxLeft->_left;
			}*/

			return _EraseR(root->_left, key);

		}

		delete del;
		return true;


	}
	
	bool _FindR(const node*& root, const K& key)
	{
		if (root == nullptr)
		{
			return false;

		}

		if (root->_key < key)
		{
			return _FindR(root->_right, key);
		}
		else if (root->_key > key)
		{
			return _FindR(root->_left, key);
		}
		else
		{
			return true;
		}

	}
	bool _InsertR(node*& root, const K& key)
	{
		if (root == nullptr)
		{
			root = new node(key);
			return true;
		}

		if (root->_key < key)
		{
			return _InsertR(root->_right, key);
		}
		else if (root->_key > key)
		{
			return _InsertR(root->_left, key);
		}
		else
		{
			return false;
		}
	}
private:
	node* _root = nullptr;
};

:::tips

二叉搜索树有一个缺陷,它可能是一颗单分支树,这种情况时间复杂度不能视为O(lgn),只能按照O(n)。平衡搜索二叉树可以消除这一缺陷。

:::

四.二叉搜索树的应用

1.k模型

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

  • 只要涉及‘在不在’的问题,都是k模型,比如,人脸识别系统。
  • set容器就是k模型
2.kv模型

每一个关键码key,都有与之对应的值Value,即<Key, Value>的键值对。

  • 比如:统计单词出现次数,翻译
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值