【C++】二叉搜索树

发布于:2024-06-05 ⋅ 阅读:(145) ⋅ 点赞:(0)

在本篇博客中,作者将会讲解普通的二叉搜索树,其目的是为后面的AVLTree红黑树进行铺垫。

一. 二叉搜索树

那么什么是二叉搜索树呢,二叉树应该大家都能理解,所以顾名思义,二叉搜索树就是一个用来搜索的二叉树,二叉树搜索树也可以叫做一棵二叉排序树,因为它的中序遍历是有序的

1.性质

二叉搜索树的性质:

1.如果当前结点的左子树不为空,则左子树上的所有结点小于当前结点

2.如果当前结点的右子树不为空,则右子树上的所有结点大于当前结点

3.它的左右子树分别也是二叉搜索树

即一棵二叉树的中序遍历是有序的就是二叉搜索树,也叫二叉排序树

如下就是一棵二叉搜索树。 

二.二叉搜索树的实现 

那么已经知道了二叉搜索树是什么,也知道了它的性质,那么现在我们来实现一下。

首先,二叉搜索树最主要的接口有下面这几个:

1.插入

2.删除

3.查找

4.修改(注意,不是所有二叉搜索树都能修改的,因为二叉搜索树要严格遵守它的性质,一旦它的性质被破坏,则这棵树就会无意义) 

1.树结点的定义

首先我们来看一下树结点的定义。 

template<class K>
struct Blog_TreeNode
{
	//成员变量
	K _key;
	Blog_TreeNode<K>* _left;
	Blog_TreeNode<K>* _right;

	//成员函数
	Blog_TreeNode(const K& key)//构造函数
		:_key(key)
		,_left(nullptr)
		,_right(nullptr)
	{}
};

树结点的定义不是很难,就二叉树常规的定义即可。 

2.类的定义 

我们可以把树结点封装到一个Blog_Tree的类中,在类中实现它的成员函数。 

template<class K>
class Blog_Tree
{
	typedef Blog_TreeNode<K> Node;//typedef一下,这样在类中可以使用Node来代替Blog_TreeNode
public:
    //构造函数
	Blog_Tree()
		:_root(nullptr)
	{}

private:
	Node* _root;//成员变量为一个根节点的指针
};

3.构造函数与析构函数

 对于一个类,有四个比较重要的成员函数:构造函数、析构函数、拷贝构造、赋值=重载

在这里,拷贝构造和赋值=重载先不做讲解,先把构造函数析构函数进行讲解。

①构造函数

对于二叉搜索树的构造函数来说非常简单,直接把_root给nullptr即可。

	//构造函数
	Blog_Tree()
		:_root(nullptr)
	{}
②析构函数 

对于析构函数来说,我们可以使用后序遍历来完成。 

	//析构函数
	~Blog_Tree()
	{
		destory(_root);
	}

	void destory(Node* root)
	{
		if (root == nullptr)
			return;

		destory(root->_left);
		destory(root->_right);
		delete root;
	}

 4.插入结点 

二叉搜索树插入结点的过程如下:

1.如果树为空,则直接将新结点给_root根结点。

2.如果树不为空,则将新结点当前结点进行比较,如果新结点大于当前结点,则当前结点向右走否则向左走,如果相等,则不插入。

如下图所示。 

 

接下来我们来编写代码来实现这个过程。 

	//插入结点
	bool insert(const K& data)
	{
		//如果树为空,则直接将新结点给到_root
		if (_root == nullptr)
			_root = new Node(data);

		//如果树不为空,这开始找空位,将新结点插入
		Node* cur = _root;
		Node* cur_parent = nullptr;//这里要提前保留cur的parent,因为后面插入时要用到
		while (cur != nullptr)
		{
			if (data > cur->_key)//如果要插入的数据大于cur,则cur往右走
			{
				cur_parent = cur;//保留cur的parent
				cur = cur->_right;
			}
			else if (data < cur->_key)//如果要插入的数据小于cur,则cur往左走
			{
				cur_parent = cur;//保留cur的parent
				cur = cur->_left;
			}
			else//如果相等,说明树中已有,则不插入,返回失败
			{
				return false;
			}
		}

		//找到空位后,将新结点进行插入
		Node* newnode = new Node(data);
		if (cur_parent->_key > newnode->_key)
			cur_parent->_left = newnode;
		else
			cur_parent->_right = newnode;

		return true;
	}

5.删除结点

删除结点是所有操作里面比较难的,但是不要怕,我们来分析一下。 


删除结点,有四种情况:

1.要删除的结点没有左子树和右子树

2.要删除的结点没有左子树,右子树

3.要删除的结点左子树,没有右子树

4.要删除的结点左子树和右子树

我们基于这四种情况分析一下。 

①要删除的结点没有左子树和右子树 

对于这种情况的处理,非常简单。

我们只需要将该结点删除,让它的parent指向空即可。如下图所示。 

注意!!!在进行删除操作时,要注意一种情况

1.被删除的结点是根结点 

②要删除的结点没有左子树,有右子树 

对于这种情况,也还好,直接将要删除结点parent指向被删除结点的右孩子即可,如下图所示。 


 


这里也一样,要判断被删除的cur是否时根结点。 

③要删除的结点有左子树,没有右子树

 这种情况与第二种一样,只不过相反了而已。这里就不多解释。

④ 要删除的结点有左子树和右子树

对于这种情况,我们的解决方法是,去找被删除结点(cur)右子树的最小结点(即被删除结点的右子树的最左结点)(cur_right_min),后将cur_right_min赋值给cur,这样我们就可以将删除cur转换完删除cur_right_min。如下图所示。


  


  


  

此时删除cur变为删除cur_right_min,那么这个时候删除cur_right_min,也就是第一种情况,或者第二种情况。 

注意!!!这里也可能会有特殊情况,例如,下面这种情况。 

所以最后要进行一个判断。


删除代码如下: 

	//删除结点
	bool erase(const K& data)
	{
		if (_root == nullptr)
			return false;

		//先找到要删除的结点
		Node* cur = _root;
		Node* cur_parent = nullptr;
		while (cur != nullptr)
		{
			//先找到要删除的结点
			if (data > cur->_key)
			{
				cur_parent = cur;
				cur = cur->_right;
			}
			else if (data < cur->_key)
			{
				cur_parent = cur;
				cur = cur->_left;
			}
			else//找到了cur要删除的结点
			{
				//判断是那种删除情况
				if (cur->_left == nullptr && cur->_right == nullptr)//第一种情况
				{
					if (cur_parent == nullptr)//如果cur_parent为空,说明删除的cur为_root
					{
						_root = nullptr;
					}
					else
					{
						if (cur_parent->_left == cur)//这里要判断cur时cur_parent左还是右
						{
							cur_parent->_left = nullptr;
						}
						else
						{
							cur_parent->_right = nullptr;
						}
					}
					delete cur;
				}
				else if (cur->_left == nullptr && cur->_right != nullptr)//第二种情况
				{
					if (cur_parent == nullptr)//如果cur_parent为空,说明删除的cur为_root
					{
						_root = cur->_right;
					}
					else
					{
						if (cur_parent->_left == cur)//这里要判断cur时cur_parent左还是右
						{
							cur_parent->_left = cur->_right;
						}
						else
						{
							cur_parent->_right = cur->_right;
						}
					}
					delete cur;
				}
				else if (cur->_left != nullptr && cur->_right == nullptr)//第三种情况
				{
					if (cur_parent == nullptr)
					{
						_root = cur->_left;
					}
					else
					{
						if (cur_parent->_left == cur)//这里要判断cur时cur_parent左还是右
						{
							cur_parent->_left = cur->_left;
						}
						else
						{
							cur_parent->_right = cur->_left;
						}
						delete cur;
					}
				}
				else//第四种情况
				{
					Node* cur_right_min = cur->_right;//cur右子树的最小左结点
					Node* cur_right_min_parent = cur;
					while (cur_right_min->_left != nullptr)//去找cur右子树的最小左结点
					{
						cur_right_min_parent = cur_right_min;
						cur_right_min = cur_right_min->_left;
					}
					cur->_key = cur_right_min->_key;//找到后将其赋值给cur,后转变为删除cur_right_min

					if (cur_right_min_parent->_key > cur_right_min->_key)
					{
						cur_right_min_parent->_left = cur_right_min->_right;
					}
					else
					{
						cur_right_min_parent->_right = cur_right_min->_right;
					}
					delete cur_right_min;
				}
				return true;
			}
		}
		return false;
	}

6.查找结点

接下来我们来实现查找结点的函数,查找的过程与插入的过程类似。

1.要查找的值比cur大,则cur往右走。 

2.要查找的值比cur小,则cur往左走。

3.如果找到,则返回该结点的指针,如果没找到,则返回nullptr

	Node* find(const K& data)
	{
		Node* cur = _root;
		while (cur != nullptr)
		{
			if (data > cur->_key)
			{
				cur = cur->_right;
			}
			else if (data < cur->_key)
			{
				cur = cur->_left;
			}
			else
			{
				return cur;
			}
		}
		return nullptr;
	}

三. 二叉搜索树的应用

那么二叉搜索树有什么应用了,由名字可以得知,二叉搜索树主要就是用来搜索的。

二叉搜索树的搜索又分为两种模型:K模型,和KV模型

K模型 

 什么是K模型,K指的是key的意思,如上面我们实现的二叉搜索树就是一种K模型的搜索树,就是在树的结点中,只存储一个K类型的值,剩下的是两个孩子指针。

template<class K>
struct Blog_TreeNode
{
	//成员变量
	K _key;
	Blog_TreeNode<K>* _left;
	Blog_TreeNode<K>* _right;

	//构造函数
	Blog_TreeNode(const K& key)
		:_key(key)
		,_left(nullptr)
		,_right(nullptr)
	{}
};

K模型的主要用途是:查找这个值在不在

比如说:学校宿舍的门禁系统,在这个门禁系统里面,就有着一棵二叉搜索树,每个树结点K都是一个string类型,存的是该宿舍学生的名字,那么这棵二叉搜索树里面存着你们宿舍里每个学生的姓名,当有一个人想要进入该宿舍的时候,他需要进行刷卡识别,这个时候,程序就会去该宿舍的二叉搜索树中去进行查找,查查这个人的姓名在不在这个二叉搜索树中,如果在就能进入,不在不能进入。

KV模型 

什么是KV模型,K是keyV是value的意思,在树结点中,不仅仅有一个K类型,还有一个V类型,意思就是说,每一个key都对应的一个value的意思,即<key,value>键值对,但是注意的是,在KV模型中,我们依然是使用K来进行比较来插入。

比如说,用来存储单词,又或者说统计次数。下面我会来演示一下。


在演示之前,我们需要需要把我们的K模型的二叉搜索树改造成KV模型的二叉搜索树。

改造非常简单,在树结点结构体中增加一个V类型的值,后将所有成员函数涉及到的K,V都小改一下,例如,插入中,要多传一个参数。改造完后如下所示。 

template<class K, class V>
struct Blog_TreeNode
{
	//成员变量
	K _key;
    V _val;
	Blog_TreeNode<K>* _left;
	Blog_TreeNode<K>* _right;

	//构造函数
	Blog_TreeNode(const K& key, const V& val)
		:_key(key)
        ,_val(val)
		,_left(nullptr)
		,_right(nullptr)
	{}
};

template<class K, class V>
class Blog_Tree
{
	typedef Blog_TreeNode<K, V> Node;
public:
	//构造函数
	Blog_Tree()
		:_root(nullptr)
	{}

	//析构函数
	~Blog_Tree()
	{
		destory(_root);
	}

	//插入结点
	bool insert(const K& key, const V& val)
	{
		//如果树为空,则直接将新结点给到_root
		if (_root == nullptr)
		{
			_root = new Node(key, val);
		}

		//如果树不为空,这开始找空位,将新结点插入
		Node* cur = _root;
		Node* cur_parent = nullptr;//这里要提前保留cur的parent,因为后面插入时要用到
		while (cur != nullptr)
		{
			if (key > cur->_key)//如果要插入的数据大于cur,则cur往右走
			{
				cur_parent = cur;//保留cur的parent
				cur = cur->_right;
			}
			else if (key < cur->_key)//如果要插入的数据小于cur,则cur往左走
			{
				cur_parent = cur;//保留cur的parent
				cur = cur->_left;
			}
			else//如果相等,说明树中已有,则不插入,返回失败
			{
				return false;
			}
		}

		//找到空位后,将新结点进行插入
		Node* newnode = new Node(key, val);
		if (cur_parent->_key > newnode->_key)
		{
			cur_parent->_left = newnode;
		}
		else
		{
			cur_parent->_right = newnode;
		}

		return true;
	}

	//删除结点
	bool erase(const K& key)
	{
		if (_root == nullptr)
		{
			return false;
		}

		//先找到要删除的结点
		Node* cur = _root;
		Node* cur_parent = nullptr;
		while (cur != nullptr)
		{
			//先找到要删除的结点
			if (key > cur->_key)
			{
				cur_parent = cur;
				cur = cur->_right;
			}
			else if (key < cur->_key)
			{
				cur_parent = cur;
				cur = cur->_left;
			}
			else//找到了cur要删除的结点
			{
				//判断是那种删除情况
				if (cur->_left == nullptr && cur->_right == nullptr)
				{
					if (cur_parent == nullptr)//如果cur_parent为空,说明删除的cur为_root
					{
						_root = nullptr;
					}
					else
					{
						if (cur_parent->_left == cur)
						{
							cur_parent->_left = nullptr;
						}
						else
						{
							cur_parent->_right = nullptr;
						}
					}
					delete cur;
				}
				else if (cur->_left == nullptr && cur->_right != nullptr)
				{
					if (cur_parent == nullptr)//如果cur_parent为空,说明删除的cur为_root
					{
						_root = cur->_right;
					}
					else
					{
						if (cur_parent->_left == cur)
						{
							cur_parent->_left = cur->_right;
						}
						else
						{
							cur_parent->_right = cur->_right;
						}
					}
					delete cur;
				}
				else if (cur->_left != nullptr && cur->_right == nullptr)
				{
					if (cur_parent == nullptr)
					{
						_root = cur->_left;
					}
					else
					{
						if (cur_parent->_left == cur)
						{
							cur_parent->_left = cur->_left;
						}
						else
						{
							cur_parent->_right = cur->_left;
						}
						delete cur;
					}
				}
				else
				{
					Node* cur_right_min = cur->_right;//cur右子树的最小左结点
					Node* cur_right_min_parent = cur;
					while (cur_right_min->_left != nullptr)//去找cur右子树的最小左结点
					{
						cur_right_min_parent = cur_right_min;
						cur_right_min = cur_right_min->_left;
					}
					cur->_key = cur_right_min->_key;//找到后将其赋值给cur,后转变为删除cur_right_min

					if (cur_right_min_parent->_key > cur_right_min->_key)
					{
						cur_right_min_parent->_left = cur_right_min->_right;
					}
					else
					{
						cur_right_min_parent->_right = cur_right_min->_right;
					}
					delete cur_right_min;
				}
				return true;
			}
		}
		return false;
	}

	//查找
	Node* find(const K& key)
	{
		Node* cur = _root;
		while (cur != nullptr)
		{
			if (key > cur->_key)
			{
				cur = cur->_right;
			}
			else if (key < cur->_key)
			{
				cur = cur->_left;
			}
			else
			{
				return cur;
			}
		}
		return nullptr;
	}

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

private:
	void _InOrder(Node* root)
	{
		if (root == nullptr)
			return;

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

	void destory(Node* root)
	{
		if (root == nullptr)
			return;

		destory(root->_left);
		destory(root->_right);
		delete root;
	}
private:
	Node* _root;
};
存储单词

接下来我们来演示一下。

//字典树
void Blog_test1()
{
	Blog_Tree<string, string> root;
	root.insert("树", "tree");
	root.insert("排序", "sort");
	root.insert("水", "water");

	Blog_TreeNode<string, string>* tmp = root.find("排序");
	if (tmp == nullptr)
	{
		cout << "无该单词" << endl;
	}
	else
	{
		cout << tmp->_val << endl;
	}
}

在上面的代码中,我们可以在树中存一个又一个的<中文,英文>键值对,通过这个中文可以找到对应的英文。 

统计次数 
//统计次数
void Blog_test2()
{
	string str[] = { "衣服","鞋子","衣服","裤子","衣服","衣服","裤子","鞋子","帽子","帽子","袜子" };
	Blog_Tree<string, int> root;
	for (auto& e : str)
	{
		Blog_TreeNode<string, int>* tmp = root.find(e);//如果树中没有,则插入,并且val给1
		if (tmp == nullptr)
		{
			root.insert(e, 1);
		}
		else//如果树中有,则val++
		{
			tmp->_val++;
		}
	}
	root.InOrder();
}

当我们要插入时,先判断在二叉搜索树中在不在,如果不在,则进行插入,并把val次数给1,如果存储,则对val进行++。 

四.二叉树搜索树的性能

对于二叉搜索树的性能来说,我们可以看到,它的插入和查找最多进行高度次的查找,例如下面这棵树,如果要进行查找最多查找3次,如果要插入,最多也就4次,所以可以得出二叉搜索树的性能为O(logN),其中N为树中结点的个数。

但是我们的树不一定是我们所想的那么理想的,如果要插入的数据完全有序或者接近有序的时候,二叉树会退化成单支树。如下图所示。 

这个时候二叉搜索树的性能就会退化成O(n),这显然不是我们想要的,所以这个时候又衍生出来的AVLTree红黑树,这两棵树会在后面的博客中进行讲解。 

五.所以源代码 

所以源代码分为K模型的和KV模型的。 

K模型 

BinarySearchTree.h
#pragma once
#include<iostream>
using namespace std;

template<class K>
struct Blog_TreeNode
{
	//成员变量
	K _key;
	Blog_TreeNode<K>* _left;
	Blog_TreeNode<K>* _right;

	//构造函数
	Blog_TreeNode(const K& key)
		:_key(key)
		,_left(nullptr)
		,_right(nullptr)
	{}

};

template<class K>
class Blog_Tree
{
	typedef Blog_TreeNode<K> Node;
public:
	//构造函数
	Blog_Tree()
		:_root(nullptr)
	{}

	//析构函数
	~Blog_Tree()
	{
		destory(_root);
	}

	//插入结点
	bool insert(const K& data)
	{
		//如果树为空,则直接将新结点给到_root
		if (_root == nullptr)
		{
			_root = new Node(data);
		}

		//如果树不为空,这开始找空位,将新结点插入
		Node* cur = _root;
		Node* cur_parent = nullptr;//这里要提前保留cur的parent,因为后面插入时要用到
		while (cur != nullptr)
		{
			if (data > cur->_key)//如果要插入的数据大于cur,则cur往右走
			{
				cur_parent = cur;//保留cur的parent
				cur = cur->_right;
			}
			else if (data < cur->_key)//如果要插入的数据小于cur,则cur往左走
			{
				cur_parent = cur;//保留cur的parent
				cur = cur->_left;
			}
			else//如果相等,说明树中已有,则不插入,返回失败
			{
				return false;
			}
		}

		//找到空位后,将新结点进行插入
		Node* newnode = new Node(data);
		if (cur_parent->_key > newnode->_key)
		{
			cur_parent->_left = newnode;
		}
		else
		{
			cur_parent->_right = newnode;
		}

		return true;
	}

	//删除结点
	bool erase(const K& data)
	{
		if (_root == nullptr)
		{
			return false;
		}

		//先找到要删除的结点
		Node* cur = _root;
		Node* cur_parent = nullptr;
		while (cur != nullptr)
		{
			//先找到要删除的结点
			if (data > cur->_key)
			{
				cur_parent = cur;
				cur = cur->_right;
			}
			else if (data < cur->_key)
			{
				cur_parent = cur;
				cur = cur->_left;
			}
			else//找到了cur要删除的结点
			{
				//判断是那种删除情况
				if (cur->_left == nullptr && cur->_right == nullptr)
				{
					if (cur_parent == nullptr)//如果cur_parent为空,说明删除的cur为_root
					{
						_root = nullptr;
					}
					else
					{
						if (cur_parent->_left == cur)
						{
							cur_parent->_left = nullptr;
						}
						else
						{
							cur_parent->_right = nullptr;
						}
					}
					delete cur;
				}
				else if (cur->_left == nullptr && cur->_right != nullptr)
				{
					if (cur_parent == nullptr)//如果cur_parent为空,说明删除的cur为_root
					{
						_root = cur->_right;
					}
					else
					{
						if (cur_parent->_left == cur)
						{
							cur_parent->_left = cur->_right;
						}
						else
						{
							cur_parent->_right = cur->_right;
						}
					}
					delete cur;
				}
				else if (cur->_left != nullptr && cur->_right == nullptr)
				{
					if (cur_parent == nullptr)
					{
						_root = cur->_left;
					}
					else
					{
						if (cur_parent->_left == cur)
						{
							cur_parent->_left = cur->_left;
						}
						else
						{
							cur_parent->_right = cur->_left;
						}
						delete cur;
					}
				}
				else
				{
					Node* cur_right_min = cur->_right;//cur右子树的最小左结点
					Node* cur_right_min_parent = cur;
					while (cur_right_min->_left != nullptr)//去找cur右子树的最小左结点
					{
						cur_right_min_parent = cur_right_min;
						cur_right_min = cur_right_min->_left;
					}
					cur->_key = cur_right_min->_key;//找到后将其赋值给cur,后转变为删除cur_right_min

					if (cur_right_min_parent->_key > cur_right_min->_key)
					{
						cur_right_min_parent->_left = cur_right_min->_right;
					}
					else
					{
						cur_right_min_parent->_right = cur_right_min->_right;
					}
					delete cur_right_min;
				}
				return true;
			}
		}
		return false;
	}

	Node* find(const K& data)
	{
		Node* cur = _root;
		while (cur != nullptr)
		{
			if (data > cur->_key)
			{
				cur = cur->_right;
			}
			else if (data < cur->_key)
			{
				cur = cur->_left;
			}
			else
			{
				return cur;
			}
		}
		return nullptr;
	}

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

private:
	void _InOrder(Node* root)
	{
		if (root == nullptr)
			return;

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

	void destory(Node* root)
	{
		if (root == nullptr)
			return;

		destory(root->_left);
		destory(root->_right);
		delete root;
	}
private:
	Node* _root;
};
test.c 
#include"BinarySearchTree.h"

//测试插入和删除
void Blog_test1()
{
	Blog_Tree<int> root;
	int arr1[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16,14 };
	for (auto& e : arr1)
	{
		root.insert(e);
	}
	root.InOrder();

	for (auto& e : arr1)
	{
		root.erase(e);
		root.InOrder();
	}
}

//测试查找
void Blog_test2()
{
	Blog_Tree<int> root;
	int arr1[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16,14 };
	for (auto& e : arr1)
	{
		root.insert(e);
	}
	Blog_TreeNode<int>* tmp = root.find(20);
	if (tmp != nullptr)
	{
		cout << tmp->_key << endl;
	}
	else
	{
		cout << "不存在给值" << endl;
	}
}

int main()
{
	//Blog_test1();
	Blog_test2();
	return 0;
}

 KV模型

BinarySearchTree.h 
template<class K, class V>
struct Blog_TreeNode
{
	//成员变量
	K _key;
	V _val;
	Blog_TreeNode<K, V>* _left;
	Blog_TreeNode<K, V>* _right;

	//构造函数
	Blog_TreeNode(const K& key, const V& value)
		:_key(key)
		,_val(value)
		,_left(nullptr)
		,_right(nullptr)
	{}

};

template<class K, class V>
class Blog_Tree
{
	typedef Blog_TreeNode<K, V> Node;
public:
	//构造函数
	Blog_Tree()
		:_root(nullptr)
	{}

	//析构函数
	~Blog_Tree()
	{
		destory(_root);
	}

	//插入结点
	bool insert(const K& key, const V& val)
	{
		//如果树为空,则直接将新结点给到_root
		if (_root == nullptr)
		{
			_root = new Node(key, val);
		}

		//如果树不为空,这开始找空位,将新结点插入
		Node* cur = _root;
		Node* cur_parent = nullptr;//这里要提前保留cur的parent,因为后面插入时要用到
		while (cur != nullptr)
		{
			if (key > cur->_key)//如果要插入的数据大于cur,则cur往右走
			{
				cur_parent = cur;//保留cur的parent
				cur = cur->_right;
			}
			else if (key < cur->_key)//如果要插入的数据小于cur,则cur往左走
			{
				cur_parent = cur;//保留cur的parent
				cur = cur->_left;
			}
			else//如果相等,说明树中已有,则不插入,返回失败
			{
				return false;
			}
		}

		//找到空位后,将新结点进行插入
		Node* newnode = new Node(key, val);
		if (cur_parent->_key > newnode->_key)
		{
			cur_parent->_left = newnode;
		}
		else
		{
			cur_parent->_right = newnode;
		}

		return true;
	}

	//删除结点
	bool erase(const K& key)
	{
		if (_root == nullptr)
		{
			return false;
		}

		//先找到要删除的结点
		Node* cur = _root;
		Node* cur_parent = nullptr;
		while (cur != nullptr)
		{
			//先找到要删除的结点
			if (key > cur->_key)
			{
				cur_parent = cur;
				cur = cur->_right;
			}
			else if (key < cur->_key)
			{
				cur_parent = cur;
				cur = cur->_left;
			}
			else//找到了cur要删除的结点
			{
				//判断是那种删除情况
				if (cur->_left == nullptr && cur->_right == nullptr)
				{
					if (cur_parent == nullptr)//如果cur_parent为空,说明删除的cur为_root
					{
						_root = nullptr;
					}
					else
					{
						if (cur_parent->_left == cur)
						{
							cur_parent->_left = nullptr;
						}
						else
						{
							cur_parent->_right = nullptr;
						}
					}
					delete cur;
				}
				else if (cur->_left == nullptr && cur->_right != nullptr)
				{
					if (cur_parent == nullptr)//如果cur_parent为空,说明删除的cur为_root
					{
						_root = cur->_right;
					}
					else
					{
						if (cur_parent->_left == cur)
						{
							cur_parent->_left = cur->_right;
						}
						else
						{
							cur_parent->_right = cur->_right;
						}
					}
					delete cur;
				}
				else if (cur->_left != nullptr && cur->_right == nullptr)
				{
					if (cur_parent == nullptr)
					{
						_root = cur->_left;
					}
					else
					{
						if (cur_parent->_left == cur)
						{
							cur_parent->_left = cur->_left;
						}
						else
						{
							cur_parent->_right = cur->_left;
						}
						delete cur;
					}
				}
				else
				{
					Node* cur_right_min = cur->_right;//cur右子树的最小左结点
					Node* cur_right_min_parent = cur;
					while (cur_right_min->_left != nullptr)//去找cur右子树的最小左结点
					{
						cur_right_min_parent = cur_right_min;
						cur_right_min = cur_right_min->_left;
					}
					cur->_key = cur_right_min->_key;//找到后将其赋值给cur,后转变为删除cur_right_min

					if (cur_right_min_parent->_key > cur_right_min->_key)
					{
						cur_right_min_parent->_left = cur_right_min->_right;
					}
					else
					{
						cur_right_min_parent->_right = cur_right_min->_right;
					}
					delete cur_right_min;
				}
				return true;
			}
		}
		return false;
	}

	//查找
	Node* find(const K& key)
	{
		Node* cur = _root;
		while (cur != nullptr)
		{
			if (key > cur->_key)
			{
				cur = cur->_right;
			}
			else if (key < cur->_key)
			{
				cur = cur->_left;
			}
			else
			{
				return cur;
			}
		}
		return nullptr;
	}

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

private:
	void _InOrder(Node* root)
	{
		if (root == nullptr)
			return;

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

	void destory(Node* root)
	{
		if (root == nullptr)
			return;

		destory(root->_left);
		destory(root->_right);
		delete root;
	}
private:
	Node* _root;
};
 test.c
//字典树
void Blog_test1()
{
	Blog_Tree<string, string> root;
	root.insert("树", "tree");
	root.insert("排序", "sort");
	root.insert("水", "water");

	Blog_TreeNode<string, string>* tmp = root.find("排序");
	if (tmp == nullptr)
	{
		cout << "无该单词" << endl;
	}
	else
	{
		cout << tmp->_val << endl;
	}
}

//统计次数
void Blog_test2()
{
	string str[] = { "衣服","鞋子","衣服","裤子","衣服","衣服","裤子","鞋子","帽子","帽子","袜子" };
	Blog_Tree<string, int> root;
	for (auto& e : str)
	{
		Blog_TreeNode<string, int>* tmp = root.find(e);
		if (tmp == nullptr)
		{
			root.insert(e, 1);
		}
		else
		{
			tmp->_val++;
		}
	}
	root.InOrder();
}

int main()
{
	//Blog_test1();
	Blog_test2();
	return 0;
}

网站公告

今日签到

点亮在社区的每一天
去签到