一篇文带你搞懂搜索二叉树的原理和实现

二叉搜索树

概念

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

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

在这里插入图片描述

1.二叉搜索树的查找
在这里插入图片描述

2.二叉树的插入

a. 树为空,则直接插入
在这里插入图片描述
b. 树不空,按二叉搜索树性质查找插入位置,插入新节点
在这里插入图片描述

3.二叉搜索树的删除

首先查找元素是否在二叉搜索树中,如果不存在,则返回, 否则要删除的结点可能分下面四种情况:

  • a. 要删除的结点无孩子结点
  • b. 要删除的结点只有左孩子结点
  • c. 要删除的结点只有右孩子结点
  • d. 要删除的结点有左、右孩子结点

看起来有待删除节点有4中情况,实际情况a可以与情况b或者c合并起来,因此真正的删除过程如下:

  • 情况b:删除该结点且使被删除节点的双亲结点指向被删除节点的左孩子结点
  • 情况c:删除该结点且使被删除节点的双亲结点指向被删除结点的右孩子结点
  • 情况d:在它的右子树中寻找中序下的第一个结点(关键码最小),用它的值填补到被删除节点中,再来处理该结点的删除问题
    在这里插入图片描述

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

插入和删除操作都必须先查找,查找效率代表了二叉搜索树中各个操作的性能。
对有n个结点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树平均查找长度是结点在二叉搜索树的深度的函数,即结点越深,则比较次数越多。
但对于同一个关键码集合,如果各关键码插入的次序不同,可能得到不同结构的二叉搜索树:
在这里插入图片描述

5.以下就是二叉树的常见的两种用法

1.用来进行基础的查找,因为其特殊的性质,所以
查找的时间复杂度为 O(logN),只有在极端情况下,二叉搜索树会变成单链表,此时时间复杂度为 O(N),为了解决这个问题,又引入了新的AVL树,有兴趣的可以去了解。

#include<iostream>
#include<string>
using namespace std;

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:
	//递归插入的子函数
	bool _Insert(Node* root, Node* parent, const K key)
	{
		if (_root == nullptr)
		{
			_root = new Node(key);
			return true;
		}
		if (root)  //当 root 不为空就让 parent 等于 root
		{
			parent = root;
		}
		if (root == nullptr)
		{
			if (parent->_key < key)
			{
				root = new Node(key);
				parent->_right = root;
				return true;
			}
			else if (parent->_key > key)
			{
				root = new Node(key);
				parent->_left = root;
				return true;
			}
			else
			{
				return false;
			}
		}
		if (root->_key < key)  //key 大就插右边
		{
			return _Insert(root->_right, parent, key);
		}
		else if (root->_key > key)
		{
			return _Insert(root->_left, parent, key);
		}
		else
		{
			return false;
		}
	}
	//递归插入
	bool Insert(const K& key)
	{

		Node* parent = new Node(0);    //parent 的目的是为了保存 root 的上一个节点,并再 new 新节点时指向新节点
		return _Insert(_root, parent, key);
	}
	
	//非递归插入
	//bool Insert(const K& key)
	//{
	//	if (_root == nullptr)
	//	{
	//		_root = new Node(key);
	//		return true;
	//	}
	//	Node* parent = _root;
	//	Node* cur = _root;
	//	while (cur)
	//	{
	//		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)   //key大就插右边
	//	{
	//		parent->_right = cur;
	//	}
	//	else
	//	{
	//		parent->_left = cur;
	//	}
	//	return true;
	//}

	//寻找给被删除节点所替换的节点
	Node*& _FindNode(Node* root,int flag)  //flag 给0 找左子树最大,给 1 找右子树最小
	{
		if (flag == 0)
		{
			if (root == nullptr || root->_right == nullptr)
				return root;
			return _FindNode(root->_right,0);
		}
		else
		{
			if (root == nullptr || root->_left == nullptr)
				return root;
			return _FindNode(root->_left, 1);
		}
	}
	//递归删除的子函数
	bool _erase(Node* root, Node* parent,const K key)
	{
		if (root == nullptr)
			return false;
		if (root->_key < key)
		{
			if (root->_right != nullptr)
				parent = root;
			_erase(root->_right, parent, key);
		}
		else if(root->_key > key)
		{
			if (root->_left != nullptr)
				parent = root;
			_erase(root->_left, parent, key);
		}
		else  //等于
		{
			if (root->_left == nullptr && root->_right == nullptr)   //当没有子叶子,就直接删除
			{
				if (parent->_left == root)  //当父亲的左子树是root
				{
					delete root;
					parent->_left = nullptr;
					return true;
				}
				else if (parent->_right == root)  //当父亲的右子树是root
				{
					delete root;
					parent->_right = nullptr;
					return true;
				}
				else   //此时说明删除的是根节点,确定没有子树所以直接删除
				{
					_root = nullptr;
					return true;
				}
			}
			else if (root->_left != nullptr && root->_right == nullptr)  //没有右子树
			{
				if (parent->_left == root)
				{
					parent->_left = root->_left;
					delete root;
					return true;
				}
				else if(parent->_right == root)
				{
					parent->_right = root->_left;
					delete root;
					return true;
				}
				else  //此时要删除的是根节点,它没有右子树
				{
					_root = root->_left;   //让_root 指向根的左子树
					root = nullptr;    //再把root置空
					return true;
				}
			}
			else if (root->_right != nullptr && root->_left == nullptr)  //没有左子树
			{
				if (parent->_left == root)
				{
					parent->_left = root->_right;
					delete root;
					return true;
				}
				else if(parent->_right == root)
				{
					parent->_right = root->_right;
					delete root;
					return true;
				}
				else  //此时要删除的是根节点,它没有左子树
				{  
					_root = root->_right;    
					root = nullptr;
					return true;
				}
			}
			else //左右叶子都有,就找它左子树的最大节点或者它右子树的最小节点并记录该节点值,然后先删除该节点
			{     //再把值赋给要删除点
				//这里我找右子树的最小节点进行交换
				Node* tmp = root;
				tmp = _FindNode(root->_right, 1);
				int ret = tmp->_key;
				_erase(root, parent, tmp->_key);
				root->_key = ret;
			}
		}
	}
	//递归删除
	bool erase(const K& key)
	{
		Node* parent = new Node(0);
		return _erase(_root, parent,key);
	}

	//非递归删除
	//bool erase(const K& key)
	//{
	//	Node* parent = nullptr;
	//	Node* cur = _root;

	//	while (cur)
	//	{
	//		if (cur->_key < key)
	//		{
	//			parent = cur;
	//			cur = cur->_right;
	//		}
	//		else if (cur->_key > key)
	//		{
	//			parent = cur;
	//			cur = cur->_left;
	//		}
	//		else
	//		{
	//			// 找到了,开始删除
	//			// 1、左为空
	//			// 2、右为空
	//			// 3、左右都不为空
	//			if (cur->_left == nullptr && cur->_right != nullptr)
	//			{
	//				if (cur == _root)
	//				{
	//					_root = cur->_right;
	//				}
	//				else
	//				{
	//					if (parent->_right == cur)
	//						parent->_right = cur->_right;
	//					else
	//						parent->_left = cur->_right;
	//				}
	//				delete cur;
	//			}
	//			else if (cur->_right == nullptr && cur->_left != nullptr)
	//			{
	//				if (cur == _root)
	//				{
	//					_root = cur->_left;
	//				}
	//				else
	//				{
	//					if (parent->_left == cur)
	//						parent->_left = cur->_left;
	//					else
	//						parent->_right = cur->_left;
	//				}

	//				delete cur;
	//			}
	//			else
	//			{
	//				// 修改
	//				Node* rightMinParent = cur;
	//				Node* rightMin = cur->_right;
	//				while (rightMin->_left)
	//				{
	//					rightMinParent = rightMin;
	//					rightMin = rightMin->_left;
	//				}
	//				// 替代删除
	//				cur->_key = rightMin->_key;
	//				// 转换成删除rightMin(rightMin是左为空,父亲指向他的右)
	//				if (rightMin == rightMinParent->_left)
	//					rightMinParent->_left = rightMin->_right;
	//				else
	//					rightMinParent->_right = rightMin->_right;

	//				delete rightMin;
	//			}
	//			return true;
	//		}
	//	}
	//	return false;
	//}
	//递归查找的子函数
	bool _find(Node* root, const K key)
	{
		if (root == nullptr)
			return false;
		if (root->_key < key)
		{
			return _find(root->_right, key);
		}
		else if (root->_key > key)
		{
			return _find(root->_left, key);
		}
		else
			return true;	
	}
	//递归查找
	bool find(const K& key)
	{
		Node* parent = new Node(0);
		return _find(_root, key);
	}

	//中序输出的子函数
	void _Inorder(Node* root)
	{
		if (root == nullptr)
			return;
		else
		{
			_Inorder(root->_left);
			cout << root->_key << " ";
			_Inorder(root->_right);
		}
	}
	//中序输出
	void Inorder()
	{
		_Inorder(_root);
		cout << endl;
	}
private:
	Node* _root = nullptr;
};
int main()
{
	BSTree<int> bs;
	int a[] = { 5,3,7,1,4,6,8,0,2,9 };
	for (auto e : a)
		bs.Insert(e);
	for (auto e : a)
	{
		cout << "删除 " << e << endl;
		bs.erase(e);
		bs.Inorder();
	}
}

2.用来作为字典记录某一个事物出现的次数 (没有该物种就添加,有就 +1 记载它出现的次数)

#include<iostream>
#include<string>
using namespace std;
template<class K,class T>
struct BSTreeNode
{
	BSTreeNode<K,T>* _left;
	BSTreeNode<K,T>* _right;
	K _key;
	T _val;
	BSTreeNode(const K& key = " ",const T& val = 0)
		:_key(key), _val(val),_left(nullptr), _right(nullptr) {}
};
template<class K, class T>
class BSTree
{
	typedef BSTreeNode<K,T> Node;
public:
	//递归插入的子函数
	bool _Insert(Node* root, Node* parent, const K key, const T val)
	{
		if (_root == nullptr)
		{
			_root = new Node(key,val);
			return true;
		}
		if (root)  //当 root 不为空就让 parent 等于 root
		{
			parent = root;
		}
		if (root == nullptr)
		{
			if (parent->_key < key)
			{
				root = new Node(key,val);
				parent->_right = root;
				return true;
			}
			else if (parent->_key > key)
			{
				root = new Node(key,val);
				parent->_left = root;
				return true;
			}
			else
			{
				return false;
			}
		}
		if (root->_key < key)  //key 大就插右边
		{
			return _Insert(root->_right, parent, key,val);
		}
		else if (root->_key > key)
		{
			 return _Insert(root->_left, parent, key,val);
		}
		else
		{
			return false;
		}
	}
	//递归插入
	bool Insert(const K& key, const T& val)
	{

		Node* parent = new Node();    //parent 的目的是为了保存 root 的上一个节点,并再 new 新节点时指向新节点
		return _Insert(_root, parent, key,val);
	}

	//非递归插入
	//bool Insert(const K& key)
	//{
	//	if (_root == nullptr)
	//	{
	//		_root = new Node(key);
	//		return true;
	//	}
	//	Node* parent = _root;
	//	Node* cur = _root;
	//	while (cur)
	//	{
	//		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)   //key大就插右边
	//	{
	//		parent->_right = cur;
	//	}
	//	else
	//	{
	//		parent->_left = cur;
	//	}
	//	return true;
	//}

	//寻找给被删除节点所替换的节点
	Node*& _FindNode(Node* root, int flag)  //flag 给0 找左子树最大,给 1 找右子树最小
	{
		if (flag == 0)
		{
			if (root == nullptr || root->_right == nullptr)
				return root;
			return _FindNode(root->_right, 0);
		}
		else
		{
			if (root == nullptr || root->_left == nullptr)
				return root;
			return _FindNode(root->_left, 1);
		}
	}
	//递归删除的子函数
	bool _erase(Node* root, Node* parent, const K key)
	{
		if (root == nullptr)
			return false;
		if (root->_key < key)
		{
			if (root->_right != nullptr)
				parent = root;
			_erase(root->_right, parent, key);
		}
		else if (root->_key > key)
		{
			if (root->_left != nullptr)
				parent = root;
			_erase(root->_left, parent, key);
		}
		else  //等于
		{
			if (root->_left == nullptr && root->_right == nullptr)   //当没有子树,就直接删除
			{
				if (parent->_left == root)  //当父亲的左子树是root
				{
					delete root;
					parent->_left = nullptr;
					return true;
				}
				else if (parent->_right == root)  //当父亲的右子树是root
				{
					delete root;
					parent->_right = nullptr;
					return true;
				}
				else  //到这里说明是根节点,因为确定没有子树,所以直接删除
				{
					_root = nullptr;
					return true;
				}
			}
			else if (root->_left != nullptr && root->_right == nullptr)  //没有右子树
			{
				if (parent->_left == root)   
				{
					parent->_left = root->_left;
					delete root;
					return true;
				}
				else if (parent->_right == root)
				{
					parent->_right = root->_left;
					delete root;
					return true;
				}
				else      //到这说明要删除节点就是根节点,没有右叶子,所以要判断
				{
					_root = root->_left;
					root = nullptr;
					return true;
				}
			}
			else if (root->_right != nullptr && root->_left == nullptr)  //没有左叶子
			{
				if (parent->_left == root)
				{
					parent->_left = root->_right;
					delete root;
					return true;
				}
				else if (parent->_right == root)
				{
					parent->_right = root->_right;
					delete root;
					return true;
				}
				else  // 到这说明要删除节点就是根节点,没有左叶子,所以要判断
				{
					_root = root->_right;
					root = nullptr;
					return true;
				}
			}
			else //左右叶子都有,就找它左子树的最大节点或者它右子树的最小节点并记录该节点值,然后先删除该节点
			{     //再把值赋给要删除点
				//这里我找右子树的最小节点进行交换
				Node* tmp = root;
				tmp = _FindNode(root->_right, 1);
				K ret = tmp->_key;
				T s1 = tmp->_val;
				_erase(root, parent, tmp->_key);
				root->_key = ret;
				root->_val = s1;
			}
		}
	}
	//递归删除
	bool erase(const K& key)
	{
		Node* parent = new Node();
		return _erase(_root, parent, key);
	}

	//非递归删除
	//bool erase(const K& key)
	//{
	//	Node* parent = nullptr;
	//	Node* cur = _root;

	//	while (cur)
	//	{
	//		if (cur->_key < key)
	//		{
	//			parent = cur;
	//			cur = cur->_right;
	//		}
	//		else if (cur->_key > key)
	//		{
	//			parent = cur;
	//			cur = cur->_left;
	//		}
	//		else
	//		{
	//			// 找到了,开始删除
	//			// 1、左为空
	//			// 2、右为空
	//			// 3、左右都不为空
	//			if (cur->_left == nullptr && cur->_right != nullptr) 
	//			{
	//				if (cur == _root)
	//				{
	//					_root = cur->_right;
	//				}
	//				else
	//				{
	//					if (parent->_right == cur)
	//						parent->_right = cur->_right;
	//					else
	//						parent->_left = cur->_right;
	//				}
	//				delete cur;
	//			}
	//			else if (cur->_right == nullptr && cur->_left != nullptr)
	//			{
	//				if (cur == _root)
	//				{
	//					_root = cur->_left;
	//				}
	//				else
	//				{
	//					if (parent->_left == cur)
	//						parent->_left = cur->_left;
	//					else
	//						parent->_right = cur->_left;
	//				}

	//				delete cur;
	//			}
	//			else
	//			{
	//				// 修改
	//				Node* rightMinParent = cur;
	//				Node* rightMin = cur->_right;
	//				while (rightMin->_left)
	//				{
	//					rightMinParent = rightMin;
	//					rightMin = rightMin->_left;
	//				}
	//				// 替代删除
	//				cur->_key = rightMin->_key;
	//				// 转换成删除rightMin(rightMin是左为空,父亲指向他的右)
	//				if (rightMin == rightMinParent->_left)
	//					rightMinParent->_left = rightMin->_right;
	//				else
	//					rightMinParent->_right = rightMin->_right;

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

	//递归查找的子函数
	Node* _find(Node* root, const K key)
	{
		if (root == nullptr)
			return nullptr;
		if (root->_key < key)
		{
			return _find(root->_right, key);
		}
		else if (root->_key > key)
		{
			return _find(root->_left, key);
		}
		else
			return root;
	}
	//递归查找
	Node* find(const K& key)
	{
		return _find(_root, key);
	}    
    //非递归查找
	//Node* find(const K& key)
	//{
	//	Node* cur = _root;
	//	while (cur)
	//	{
	//		if (cur->_key < key)
	//		{
	//			cur = cur->_right;
	//		}
	//		else if (cur->_key > key)
	//		{
	//			cur = cur->_left;
	//		}
	//		else
	//		{
	//			return cur;
	//		}
	//	}
	//
	//	return nullptr;
	//}

	//中序输出的子函数
	void _Inorder(Node* root)
	{
		if (root == nullptr)
			return;
		else
		{
			_Inorder(root->_left);
			cout << root->_key << "-" << root->_val << " ";
			_Inorder(root->_right);
		}
	}
	//中序输出
	void Inorder()
	{
		_Inorder(_root);
		cout << endl;
	}
private:
	Node* _root = nullptr;
};
int main()
{
	BSTree<string,int> bs;
	string strArr[] = { "西瓜", "苹果", "西瓜", "苹果", "蓝莓", "香蕉", "西瓜", "樱桃", "西瓜", "西瓜" ,"蓝莓","黄桃"};
	for (auto e : strArr)
	{
		BSTreeNode<string, int>* tmp = bs.find(e);
		if (tmp == nullptr)
		{
			bs.Insert(e, 1);
		}
		else
		{
			tmp->_val++;
		}
	}
	//bs.Inorder();
	//for (auto e : strArr)
	//{
	//	cout << "删除 " << e << endl;
	//	bs.erase(e);
	//	bs.Inorder();
	//}
}
  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值