二叉搜索树的实现 K模型 和 KV模型

🌻个人主页:路飞雪吖~

      ✨专栏:C/C++


目录

一、二叉搜索树(K模型)的模拟实现

🌟二叉搜索树的概念

🌟二叉搜索树的操作

🌠二叉搜索树的查找

🌠二叉搜索树的插入

🌠二叉搜索树的删除

🌠二叉搜索树的中序遍历 

二、二叉搜索树(KV模型)的模拟实现

三、二叉搜索树的应用场景

四、二叉搜索树的性能分析

五、模拟实现的代码


一、二叉搜索树(K模型)的模拟实现

🌟二叉搜索树的概念

二叉搜索树(BST,Binary Search Tree),也称二叉排序树或二叉查找树。

一颗二叉搜索树,可以为空;

如果不为空,满足以下性质:

• 非空左子树的所有键值小于其根节点的键值

• 非空右子树的所有键值大于其根节点的键值

• 左、右子树都是二叉搜索树

🌟二叉搜索树的操作

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

模拟实现的基本框架:

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

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

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

template<class K>
class BSTree
{
	typedef BSTNode<K> Node;
public:

private:
	Node* _root = nullptr;
};

BSTNode  搜索二叉树的节点不仅要存该节点的值,还要存左右节点的指针,因此把节点封装成一个节点类,以便对节点插入、删除、查找等进行操作。

• K _key;  节点的值
  BSTNode<K>* _left;  节点的左  
  BSTNode<K>* _right;  节点的右

• BSTNode(const K& key)  节点的初始化

BSTree  二叉搜索树的主体实现(查找、删除、插入)

• typedef BSTNode<K> Node;  对节点进行取别名

🌠二叉搜索树的查找

我们知道在搜索树二叉树中,左子树的节点小于根节点的值,根节点的值小于右子树的值,因此我们根据这个特性对搜索二叉树进行查找:

	//查找
	bool 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 true;//找到返回真
			}
		}
		return false;//没找到返回假
	}

• 从根开始比较,查找,比根大往右边查找,比根小往左边查找;

•  最多查找高度次,走到空,还没找到,说明这个值不存在。

🌠二叉搜索树的插入

//插入
bool Insert(const K& key)
{
	//1、树为空,直接新增节点,赋值给root指针
if (_root == nullptr)
	{
		_root = new Node(key);
		return true;
	}
}

• 树为空,直接新增节点,赋值给root指针;

		//树不为空,按二叉搜索树性质查找插入位置,插入新节点
		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
			{
				return false;//插入已有的值返回false
			}
		}

 • 树不为空,按二叉搜索树性质查找插入的位置 ;

 • Node* parent = nullptr; 记录插入节点的前一个指针,方便插入时的操作;

 • 插入已有的值返回false,二叉搜索树中不能存相同的值。

		//找到位置,插入新节点
		cur = new Node(key);
		if (parent->_key < key)
		{
			parent->_right = cur;
		}
		else
		{
			parent->_left = cur;
		}

  • 找到位置,创建节点,并判断节点是插入在前一个节点的左边还是右边。

	//插入
	bool Insert(const K& key)
	{
		//1、树为空,直接新增节点,赋值给root指针
	if (_root == nullptr)
		{
			_root = new Node(key);
			return true;
		}
		//树不为空,按二叉搜索树性质查找插入位置,插入新节点
		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
			{
				return false;//插入已有的值返回false
			}
		}
		//找到位置,插入新节点
		cur = new Node(key);
		if (parent->_key < key)
		{
			parent->_right = cur;
		}
		else
		{
			parent->_left = cur;
		}
		return true;//插入完成
	}

 

🌠二叉搜索树的删除

• 按照二叉搜索树的性质查找到要删除的的节点:

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
		{

			//找到节点,进行删除
			
        }
	}
	//没有找到这个节点
	return false;
}

此时该如何进行删除呢?

被删除的节点有以下几种情况:

   • <1>被删除的节点无孩子节点

   • <2>被删除的节点只有左孩子节点

   • <3>被删除的节点只有右孩子节点

情况1、2、3可归为同一类情况,因为有无孩节点也是指向空;

//0-1个孩子的情况
if (cur->_left == nullptr)//被删除节点的左为空 
{
				//如果要删除的是没有左子树的 根节点,此时根节点是没有parent的 
				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)//被删除节点的右为空
{
				//如果要删除的是没有右子树的 根节点,此时根节点是没有parent的 
				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
{
				//2个孩子的情况
				//如何删除?
}

  • <4>被删除的节点只有左、右孩子节点

情况4为另一类情况,该解决办法有两种,

方法一:选择被删除节点的右子树的最小值作为替代节点;

方法二:选择被删除节点的左子树的最大值作为替代节点。

这里选择方法一:

else
{
				//2个孩子的情况
				//法一:找要删除节点的右子树的最小节点作为替代节点  
                //法二:找要删除节点的左子树的最大节点作为替代节点
				//找右子树的最小节点
				//Node* rightMinP = nullptr;//rightMinP为rightMin的父亲,方便删除rightMin节点
				Node* rightMinP = cur;//如果rightMinP为空,就不进入循环里面,就为空指针,所以直接指向cur
				Node* rightMin = cur->_right;
				while (rightMin->_left)
				{
					rightMinP = rightMin;
					rightMin = rightMin->_left;//一直往左找
				}
				cur->_key = rightMin->_key;//替代要删除的节点
				//rightMin不一定是rightMinP的左,也有可能是右(rightMinP = cur)
				if(rightMinP->_left == rightMin)
				    rightMinP->_left = rightMin->_right;
				else
					rightMinP->_right = rightMin->_right;

				delete rightMin;
				return true;
}

🌠二叉搜索树的中序遍历 

public:
	void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}
	
private:
	void _InOrder(Node* root)
	{
		if (root == nullptr)
		{
			return;
		}

		_InOrder(root->_left);
		cout << root->_key << " ";
		_InOrder(root->_right);
	}

二、二叉搜索树(KV模型)的模拟实现

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

KV的模型与K模型一样,只不过是多存了一个值 :

  • 在查找时,找到返回节点的值,找不到返回nullptr;

  • 在插入时,树为空,新建的节点要存value;

namespace Key
{
	template<class K>
	class BSTree
	{
		
	};
}

namespace KeyValue
{
	template<class K, class V>
	class BSTree
	{
		

	};
}

三、二叉搜索树的应用场景

K模型:门禁系统 / 判断英语单词是否拼写正确

KV模型:英汉词典 / 统计单词出现的次数

四、二叉搜索树的性能分析

• 插入和删除操作都必须先查找,查找效率代表了二叉搜索树中的各个操作性能。

• 对有n个结点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树平均查找长度是结点在二叉搜索树的深度的函数,即结点越深,则比较次数越多。


• 最优情况下,二叉搜索树为完全二叉树(或者接近完全二叉树),

  其平均比较次数为:O(log₂N)

• 最差情况下,二叉搜索树退化为单支树(或者类似单支),其平均比较次数为:O(N)

五、模拟实现的代码

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

namespace Key
{
	template<class K>
	struct BSTNode
	{
		K _key;
		BSTNode<K>* _left;
		BSTNode<K>* _right;


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

	template<class K>
	class BSTree
	{
		typedef BSTNode<K> Node;
	public:
		BSTree()
			:_root(nullptr)
		{}

		bool Insert(const K& key)
		{
			//树为空,直接新增节点,赋值给root指针
			if (_root == nullptr)
			{
				_root = new Node(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
				{
					return false;
				}
			}
			cur = new Node(key);
			if (parent->_key < key)
			{
				parent->_right = cur;
			}
			else
			{
				parent->_left = cur;
			}
			return true;
		}

		bool 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 true;
				}
			}
			return false;
		}

		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
				{
					//找到节点删除
					//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
					{
						//有2个孩子的删除
						//找出该节点 右子树的最小节点
						Node* rightMinP = cur;
						Node* rightMin = cur->_right;
						while (rightMin->_left)
						{
							rightMinP = rightMin;
							rightMin = rightMin->_left;
						}
						cur->_key = rightMin->_key;

						if (rightMinP->_left == rightMin)
							rightMinP->_left = rightMin->_right;
						else
							rightMinP->_right = 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->_key << " ";
			_InOrder(root->_right);
		}

	private:
		Node* _root = nullptr;
	};
}

namespace KeyValue
{
	template<class K, class V>//+
	struct BSTNode
	{
		K _key;
		V _value;//+
		BSTNode<K, V>* _left;
		BSTNode<K, V>* _right;

		BSTNode(const K& key, const V& value)//+
			:_key(key)
			,_value(value)//+
			, _left(nullptr)
			, _right(nullptr)
		{}
	};

	template<class K, class V>//+
	class BSTree
	{
		typedef BSTNode<K, V> Node;//+
	public:
		BSTree()
			:_root(nullptr)
		{}

		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;//+ 没找到返回空
		}

		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 (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, value);//+
			if (parent->_key < key)
			{
				parent->_right = cur;
			}
			else
			{
				parent->_left = cur;
			}

			return true;

		}

		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
				{
					//找到节点,进行删除
					// 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* rightMinP = cur;
						Node* rightMin = cur->_right;
						while (rightMin->_left)
						{
							rightMinP = rightMin;
							rightMin = rightMin->_left;
						}
						cur->_key = rightMin->_key;

						if (rightMinP->_left == rightMin)
							rightMinP->_left = rightMin->_right;
						else
							rightMinP->_right = 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->_key << ":" << root->_value << endl;;
			_InOrder(root->_right);
		}

	private:
		Node* _root = nullptr;
	};
}
#include"SearchBinaryTree.h"
#include<string>

//int main()
//{
//	int a[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13 };
//	//int a[] = { 8, 3, 3, 1, 3, 10, 6, 3, 4, 7, 5, 14, 13 };
//
//	BSTree<int> t;
//	for (auto e : a)
//	{
//		t.Insert(e);
//	}
//
//	//t.InOrder();
//
//	t.Find(100);
//
//	//t.Erase(4);
//	//t.InOrder();
//
//	//t.Erase(3);
//	//t.InOrder();
//
//	for (auto e : a)
//	{
//		t.Erase(e);
//		t.InOrder();
//	}
//
//	return 0;
//}


int main()
{
	KeyValue::BSTree<string, string> dic;
	dic.Insert("left", "左边");
	dic.Insert("right", "右边");
	dic.Insert("string", "字符串");
	dic.Insert("insert", "插入");

	string str;
	while (cin >> str)//string的流提取  ctrl+^Z +enter杀掉进程
	{
		//auto ret = dic.Find(str);
		KeyValue::BSTNode<string, string>* ret = dic.Find(str);
		if (ret)
		{
			cout << "->" << ret->_value << endl;
		}
		else
		{
			cout << "无此单词,请重新输入!!!" << endl;
		}
	}

	return 0;
}

int main()
{
	// 统计水果出现的次数
	string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜",
   "苹果", "香蕉", "苹果", "香蕉" };
	KeyValue::BSTree<string, int> countTree;
	for (const auto& str : arr)
	{
		// 先查找水果在不在搜索树中
		// 1、不在,说明水果第一次出现,则插入<水果, 1>
		// 2、在,则查找到的节点中水果对应的次数++
		//BSTreeNode<string, int>* ret = countTree.Find(str);
		auto ret = countTree.Find(str);
		if (ret == NULL)
		{
			countTree.Insert(str, 1);
		}
		else
		{
			ret->_value++;
		}
	}
	countTree.InOrder();

	return 0;
}

如若对你有帮助,记得点赞、收藏、关注哦!

若有误,望各位,在评论区留言或者私信我 指点迷津!!!谢谢^ ^ ~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值