<二叉搜索树>——《C++高阶》

目录

1.内容说明: 

2. 二叉搜索树

2.1 二叉搜索树概念

2.2 二叉搜索树操作

1. 二叉搜索树的查找

2. 二叉搜索树的插入

3. 二叉搜索树的删除

2.3 二叉搜索树的实现

2.4 二叉搜索树的应用

2.5 二叉搜索树的性能分析

3.二叉搜索树的模拟实现:

3.1二叉搜索树(K模型)

3.1.1定义二叉搜索树的结构:​

3.1.2功能函数:

(1)非递归版Insert:​

(2)非递归版Find​

(3)非递归版Erase​

(4)递归版InsertR:

​(5)递归版FindR​

(6)递归版EraseR:​

3.1.3: 遍历(递归版):

3.1.4完整源码:

(1)BSTreeKey.h:

(2)test.cpp:

3.2二叉搜索树(KV模型) 

3.2.1 二叉树KV模型结构定义:​

3.2.2功能函数

(1)非递归版Insert、Find、Erase​

(2)递归版InsertR、FindR、EraseR:​

 3.2.3遍历(递归版):

 3.2.4完整源码:

(1)BSTreeKeyValue.h:

(2)test.cpp:

后记:●由于作者水平有限,文章难免存在谬误之处,敬请读者斧正,俚语成篇,恳望指教!

                                                                       ——By 作者:新晓·故知


1.内容说明: 

二叉树在前面C数据结构阶段已经学习了解,这里开始新的章节学习:
1. map和set特性需要先铺垫二叉搜索树,而二叉搜索树也是一种树形结构
2. 二叉搜索树的特性了解,有助于更好的理解map和set的特性
3. 二叉树中部分面试题稍微有点难度,在前面学习不容易接受,且时间长容易忘
4. 有些OJ题使用C语言方式实现比较麻烦
因此这里学习二叉树搜索树,对二叉树部分进行总结。

2. 二叉搜索树

2.1 二叉搜索树概念

二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:
若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
它的左右子树也分别为二叉搜索树

 int a [] = {5,3,4,1,7,8,2,6,0,9};

2.2 二叉搜索树操作

1. 二叉搜索树的查找

2. 二叉搜索树的插入

插入的具体过程如下:
a. 树为空,则直接插入

 

 b. 树不空,按二叉搜索树性质查找插入位置,插入新节点

3. 二叉搜索树的删除

首先查找元素是否在二叉搜索树中,如果不存在,则返回, 否则要删除的结点可能分下面四种情况:
a. 要删除的结点无孩子结点
b. 要删除的结点只有左孩子结点
c. 要删除的结点只有右孩子结点
d. 要删除的结点有左、右孩子结点
看起来有待删除节点有4中情况,实际情况a可以与情况b或者c合并起来,因此真正的删除过程如下:
情况b:删除该结点且使被删除节点的双亲结点指向被删除节点的左孩子结点
情况c:删除该结点且使被删除节点的双亲结点指向被删除结点的右孩子结点
情况d:在它的右子树中寻找中序下的第一个结点(关键码最小),用它的值填补到被删除节点中, 再来处理该结点的删除问题

 2.3 二叉搜索树的实现

template<class T>
struct BSTNode
{
	BSTNode(const T& data = T())
		: _pLeft(nullptr), _pRight(nullptr), _data(data)
	{}
	BSTNode<T>* _pLeft;
	BSTNode<T>* _pRight;
	T _data;
};
template<class T>
class BSTree
{
	typedef BSTNode<T> Node;
	typedef Node* PNode;
public:
	BSTree() : _pRoot(nullptr)
	{}
	// 同学们自己实现,与二叉树的销毁类似
	~BSTree();
	// 根据二叉搜索树的性质查找:找到值为data的节点在二叉搜索树中的位置
	PNode Find(const T& data);
	bool Insert(const T& data)
	{
		// 如果树为空,直接插入
		if (nullptr == _pRoot)
		{
			_pRoot = new Node(data);
			return true;
		}
		// 按照二叉搜索树的性质查找data在树中的插入位置
		PNode pCur = _pRoot;
		// 记录pCur的双亲,因为新元素最终插入在pCur双亲左右孩子的位置
		PNode pParent = nullptr;
		while (pCur)
		{
			pParent = pCur;
			if (data < pCur->_data)
				pCur = pCur->_pLeft;
			else if (data > pCur->_data)
				pCur = pCur->_pRight; // 元素已经在树中存在
			else
				return false;
		}
		// 插入元素
		pCur = new Node(data);
		if (data < pParent->_data)
			pParent->_pLeft = pCur;
		else
			pParent->_pRight = pCur; return true;
	}
	bool Erase(const T& data)
	{
		// 如果树为空,删除失败
		if (nullptr == _pRoot)
			return false;
		// 查找在data在树中的位置
		PNode pCur = _pRoot;
		PNode pParent = nullptr;
		while (pCur)
		{
			if (data == pCur->_data)
				break;
			else if (data < pCur->_data)
			{
				pParent = pCur;
				pCur = pCur->_pLeft;
			}
			else
			{
				pParent = pCur;
				pCur = pCur->_pRight;
			}
		}
		// data不在二叉搜索树中,无法删除
		if (nullptr == pCur)
			return false;
		// 分以下情况进行删除,同学们自己画图分析完成
		if (nullptr == pCur->_pRight)
		{
			// 当前节点只有左孩子或者左孩子为空---可直接删除
		}
		else if (nullptr == pCur->_pRight)
		{
			// 当前节点只有右孩子---可直接删除
		}
		else
		{
			// 当前节点左右孩子都存在,直接删除不好删除,可以在其子树中找一个替代结点,比如:
			// 找其左子树中的最大节点,即左子树中最右侧的节点,或者在其右子树中最小的节点,即右子树中最小的节点
				// 替代节点找到后,将替代节点中的值交给待删除节点,转换成删除替代节点
		}
		return true;
	}
	// 同学们自己实现
	void InOrder();
private:
	PNode _pRoot;
}

2.4 二叉搜索树的应用

1. K模型:K模型即只有key作为关键码,结构中只需要存储Key即可,关键码即为需要搜索到的值
比如:给一个单词word,判断该单词是否拼写正确,具体方式如下:
以单词集合中的每个单词作为key,构建一棵二叉搜索树
在二叉搜索树中检索该单词是否存在,存在则拼写正确,不存在则拼写错误。
2. KV模型:每一个关键码key,都有与之对应的值Value,即<Key, Value>的键值对。该种方式在现实生活中非常常见:比如英汉词典就是英文与中文的对应关系,通过英文可以快速找到与其对应的中文,英文单词与其对应的中文<word, chinese>就构成一种键值对;再比如统计单词次数,统计成功后,给定单词就可快速找到其出现的次数,单词与其出现次数就是<word, count>就构成一种键值对
比如:实现一个简单的英汉词典dict,可以通过英文找到与其对应的中文,具体实现方式如下: <单词,中文含义>为键值对构造二叉搜索树,注意:二叉搜索树需要比较,键值对比较时只比较 Key 查询英文单词时,只需给出英文单词,就可快速找到与其对应的key

template<class K, class V>
struct BSTNode
{
	BSTNode(const K& key = K(), const V& value = V())
		: _pLeft(nullptr), _pRight(nullptr), _key(key), _Value(value)
	{}
	BSTNode<T>* _pLeft;
	BSTNode<T>* _pRight;
	K _key;
	V _value
};
template<class K, class V>
class BSTree
{
	typedef BSTNode<K, V> Node;
	typedef Node* PNode;
public:
	BSTree() : _pRoot(nullptr)
	{}
	// 同学们自己实现,与二叉树的销毁类似
	~BSTree();
	// 根据二叉搜索树的性质查找:找到值为data的节点在二叉搜索树中的位置
	PNode Find(const K& key);
	bool Insert(const K& key, const V& value)
	{
		// ...

		PNode pCur = _pRoot;
		PNode pParent = nullptr;
		while (pCur)
		{
			pParent = pCur;
			if (key < pCur->_key)
				pCur = pCur->_pLeft;
			else if (key > pCur->_key)
				pCur = pCur->_pRight; // 元素已经在树中存在
			else
				return false;
		}
		// ...
		return true;
	}
	bool Erase(const K& key)
	{
		// ...
		return true;
	}
private:
	PNode _pRoot;
};

插入数据,然后去重,进行排序

递归的问题:虽然代码简短,但如果深度太大,可能会溢出。

2.5 二叉搜索树的性能分析

插入和删除操作都必须先查找,查找效率代表了二叉搜索树中各个操作的性能。
对有n个结点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树平均查找长度是结点在二叉搜索树的深度的函数,即结点越深,则比较次数越多。
但对于同一个关键码集合,如果各关键码插入的次序不同,可能得到不同结构的二叉搜索树:
问题:如果退化成单支树,二叉搜索树的性能就失去了。那能否进行改进,不论按照什么次序插入关键码,都可以是二叉搜索树的性能最佳?

3.二叉搜索树的模拟实现:

3.1二叉搜索树(K模型)

 3.1.1定义二叉搜索树的结构:

 

3.1.2功能函数:

(1)非递归版Insert:

 (2)非递归版Find

 (3)非递归版Erase

(4)递归版InsertR:

 (5)递归版FindR

(6)递归版EraseR: 3.1.3: 遍历(递归版):

中序遍历、前序遍历、后序遍历

 

3.1.4 完整源码:

(1)BSTreeKey.h:

#pragma once
//二叉搜索树  BinarySearchTree  K模型
//一般不允许冗余,会进行去重,所以可有使用搜索树去重
#include<iostream>
using namespace std;
template<class K>      //根据传过来的值类型,K即为类型。例int、double等
struct BSTreeNode
{
	BSTreeNode<K>* _left;
	BSTreeNode<K>* _right;

	K _key;     //类型为K的类型,一般不允许修改,防止破坏树形结构
	BSTreeNode(const K& key)
		:_left(nullptr)
		, _right(nullptr)
		, _key(key)
	{}
};

template<class K>
class BSTree
{
	typedef BSTreeNode<K> Node;
public:
	//C++11支持default
	BSTree() = default;  //拷贝构造函数也是构造函数,只要写构造函数就不会生成默认构造,这里强制生成构造函数

	Node* CopyTree(Node* root)
	{
		if (root == nullptr)
			return nullptr;
		Node* copyNode = new Node(root->_key);
		copyNode->_left = CopyTree(root->_left);
		copyNode->_right = CopyTree(root->_right);
		return copyNode;
	}
	BSTree(const BSTree<K>& t)  //实现深拷贝
	{
		_root = CopyTree(t._root);
	}
	//赋值重载 例:t2=t1;
	//现代写法
	BSTree<K>& operator=(BSTree<K> t)
	{
		swap(_root, t._root);
		return *this;
	}
	void DestroyTree(Node* root)
	{
		if (root == nullptr)
			return;
		DestroyTree(root->_left);
		DestroyTree(root->_right);
		delete root;
	}
	~BSTree()  //因为析构函数没有参数,要写成递归就得借用成员函数,嵌套一层
	{
		DestroyTree(_root);
		_root = nullptr;
	}
	//非递归insert
	bool Insert(const K& key)   //一般不允许冗余,会进行去重
	{
		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;
			}
			/*//允许冗余
			if (cur->_key < key)
			{
				parent = cur;
				cur = cur->_right;
			}
			else
			{
				parent = cur;
				cur = cur->_left;
			}*/
		}
		cur = new Node(key);
		if (parent->_key < key)
		{
			parent->_right = cur;
		}
		else
		{
			parent->_left = cur;
		}
		return true;
	}
	//非递归find
	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;
	}
	//非递归erase
	//删除情况很多,需要分情况讨论
	//1.当删除的是父结点时,采用替换法:
	//找父结点的左子树的(所有值的)最大值结点或右子树的(所有值的)最小值结点替换被删除的父结点
	//2.当删除的是叶结点,直接删除,再链接
	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
			{
				//一个孩子--左为空or右为空

				if (cur->_left == nullptr)
				{
					//if(parent==nullptr)
					if (cur == _root)
					{
						_root = cur->_right;
					}
					else
					{
						if (cur == parent->_left)
						{
							parent->_left = cur->_right;
						}
						else
						{
							parent->_right = cur->_right;
						}
					}
					delete cur;
				}
				else if (cur->_right == nullptr)
				{
					//if(parent==nullptr)
					if (cur == _root)
					{
						_root = cur->_left;
					}
					else
					{
						if (cur == parent->_left)
						{
							parent->_left = cur->_left;
						}
						else
						{
							parent->_right = cur->_left;
						}
					}
					delete cur;

				}
				else  //两个孩子都不为空 //删除有两个孩子的结点:替换法
				{
					//1.使用右子树的最小值结点替代
					Node* minParent = cur;
					Node* minRight = cur->_right;
					while (minRight->_left)
					{
						minParent = minRight;
						minRight = minRight->_left;
					}
					swap(minRight->_key, cur->_key); //或cur->_key = minRight->_key;
					if (minParent->_left == minRight)
					{
						minParent->_left = minRight->_right;
					}
					else
					{
						minParent->_right = minRight->_right;
					}
					delete minRight;

					2.使用左子树的最大值结点替代
					//Node* maxParent = cur;
					//Node* maxLeft = cur->_left;
					//while (maxLeft->_right)
					//{
					//	maxParent = maxLeft;
					//	maxLeft = maxLeft->_right;
					//}
					//swap(maxLeft->_key, cur->_key); //或cur->_key = maxLeft->_key;
					//if (maxParent->_right == maxLeft)
					//{
					//	maxParent->_right = maxLeft->_left;
					//}
					//else
					//{
					//	maxParent->_left = maxLeft->_left;
					//}
					//delete maxLeft;
				}
				return true;
			}

		}
		return false;
	}
	/
	//递归
	//无法直接使用私有成员进行递归,因此嵌套一层成员函数,实现递归
	//递归find
	bool FindR(const K& key)
	{
		return _FindR(_root, key);
	}
	//递归insert
	bool InsertR(const K& key)
	{
		return _InsertR(_root, key);
	}
	//递归erase
	bool EraseR(const K& key)
	{
		return _EraseR(_root, key);
	}
private:
	//递归insert
	bool _InsertR(Node*& root, const K& key)
	{
		//Node*& root这个处理,使得当root为空指针时,但root同时也是父结点的left或right的别名,使得结点得以链接
		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;
	}
	//递归FindR
	bool _FindR(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;
		}
	}
	//递归erase
	bool _EraseR(Node*& root, const K& key)
	{
		if (root == nullptr)
			return false;
		if (root->_key < key)
		{
			return _EraseR(root->_right, key);
		}
		else if (root->_key > key)
		{
			return _EraseR(root->_left, key);
		}
		else
		{
			Node* del = root; //先保存一下
			if (root->_left == nullptr)
			{
				root = root->_right;
			}
			else if (root->_right == nullptr)
			{
				root = root->_left;
			}
			else
			{
				//1.使用右子树的最小值结点替代
				Node* minRight = root->_right;
				while (minRight->_left)
				{
					minRight = minRight->_left;
				}
				swap(root->_key, minRight->_key);
				return _EraseR(root->_right, key);

				2.使用左子树的最大值结点替代
				//Node* maxLeft = root->_left;
				//while (maxLeft->_right)
				//{
				//	maxLeft = maxLeft->_right;
				//}
				//swap(root->_key, maxLeft->_key);
				//return _EraseR(root->_left, key);
			}
			delete del;
			return true;
		}
	}

public:
	//递归版遍历
	void InOrder()          //1.中序遍历
	{
		_InOrder(_root);
		cout << endl;
	}
	void PrevOrder()        //2.前序遍历
	{
		_PrevOrder(_root);
		cout << endl;
	}
	void PostOrder()       //3.后序遍历
	{
		_PostOrder(_root);
		cout << endl;
	}
private:
	//在C语言中,root可以被直接获取,
	//在C++中,因为_root 是私有,被封装起来了所以可有以下两种方式获得
	//1.可以写一个成员函数GetRoot()获取
	//2.也可以使用遍历,不被继承就设为私有成员函数

	void _InOrder(Node* root)     //1.中序遍历
	{
		if (root == nullptr)
			return;
		_InOrder(root->_left);
		cout << root->_key << " ";
		_InOrder(root->_right);
	}
	void _PrevOrder(Node* root)   //2.前序遍历
	{
		if (root == nullptr)
			return;
		cout << root->_key << " ";
		_PrevOrder(root->_left);
		_PrevOrder(root->_right);
	}
	void _PostOrder(Node* root)   //3.后序遍历
	{
		if (root == nullptr)
			return;
		_PostOrder(root->_left);
		_PostOrder(root->_right);
		cout << root->_key << " ";
	}
private:
	Node* _root = nullptr;  //调用自定义类型Node*
};

(2)test.cpp:

#include"BSTreeKey.h"

void TestBSTree1()
{
	BSTree<int> t;
	//int a[] = {8,3,1,10,6,4,7,14,13 };
	//int a[] = {8,3,1,10,6,4,7,14,13,10,6 };   //冗余测试

	int a[] = { 5,3,4,1,7,8,2,6,0,9 };
	for (auto e : a)
	{
		t.Insert(e);
	}
	t.InOrder();
	t.Insert(16);
	t.Insert(9);
	t.InOrder();
}
void TestBSTree2()
{
	BSTree<int> t;
	int a[] = { 8,3,1,10,6,4,7,14,13 };
	//int a[] = {8,3,1,10,6,4,7,14,13,10,6 };   //冗余测试
	//int a[] = { 5,3,4,1,7,8,2,6,0,9 };
	for (auto e : a)
	{
		t.Insert(e);
	}
	t.InOrder();
	t.PrevOrder();
	t.PostOrder();
	//t.Erase(8);
	//t.Erase(10);
	//t.Erase(3);
	//t.Erase(7);
	//t.Erase(6);
	//t.Erase(14);
	//t.Erase(13);
	//t.InOrder();

	/*for (auto e : a)
	{
		t.Erase(e);
	}
	t.InOrder();*/
}
void TestBSTree3()
{
	BSTree<int> t;
	int a[] = { 8,3,1,10,6,4,7,14,13 };
	//int a[] = {8,3,1,10,6,4,7,14,13,10,6 };   //冗余测试
	//int a[] = { 5,3,4,1,7,8,2,6,0,9 };
	//非递归insert
	/*for (auto e : a)
	{
		t.Insert(e);
	}*/
	//递归insertR
	/*for (auto e : a)
	{
		t.InsertR(e);
	}
	t.InOrder();*/

	//BSTree<int> copy=t;  //浅拷贝
	//copy.InOrder();
	t.Find(10);
	t.FindR(10);

}
void TestBSTree4()
{
	BSTree<int> t;
	int a[] = { 8,3,1,10,6,4,7,14,13 };
	//int a[] = {8,3,1,10,6,4,7,14,13,10,6 };   //冗余测试
	//int a[] = { 5,3,4,1,7,8,2,6,0,9 };
	//非递归insert
	for (auto e : a)
	{
		t.Insert(e);
	}
	//递归insertR
	/*for (auto e : a)
	{
		t.InsertR(e);
	}*/
	t.InOrder();
	t.EraseR(13);
	t.EraseR(10);
	t.EraseR(8);
	t.EraseR(3);

	t.InOrder();

	for (auto e : a)
	{
		t.EraseR(e);
	}
	t.InOrder();

}
int main()
{
	//TestBSTree1();
	//TestBSTree2();
	//TestBSTree3();
	TestBSTree4();

	return 0;
}

可画功能函数递归展开图进行分析

3.2二叉搜索树(KV模型) 

3.2.1 二叉树KV模型结构定义:

 

3.2.2功能函数

(1)非递归版Insert、Find、Erase 

(2)递归版InsertR、FindR、EraseR: 3.2.3遍历(递归版):

中序遍历、前序遍历、后序遍历:

 

 3.2.4完整源码:

(1)BSTreeKeyValue.h:

#pragma once
//二叉搜索树  BinarySearchTree-KeyValue模型

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

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

	 K _key;      //一般不允许修改key,防止树型结构变化
	 V _value;
	BSTreeNode(const K& key,const V& value)
		:_left(nullptr)
		, _right(nullptr)
		, _key(key)
		,_value(value)
	{}
};

template<class K, class V>

class BSTree
{
	typedef BSTreeNode<K,V> Node;
public:
	//C++11支持default
	BSTree() = default;  //拷贝构造函数也是构造函数,只要写构造函数就不会生成默认构造,这里强制生成构造函数

	Node* CopyTree(Node* root)
	{
		if (root == nullptr)
			return nullptr;
		Node* copyNode = new Node(root->_key,root->_value);
		copyNode->_left = CopyTree(root->_left);
		copyNode->_right = CopyTree(root->_right);
		return copyNode;
	}
	BSTree(const BSTree<K,V>& t)  //实现深拷贝
	{
		_root = CopyTree(t._root);
	}
	//赋值重载 例:t2=t1;
	//现代写法
	BSTree<K,V>& operator=(BSTree<K,V> t)
	{
		swap(_root, t._root);
		return *this;
	}
	void DestroyTree(Node* root)
	{
		if (root == nullptr)
			return;
		DestroyTree(root->_left);
		DestroyTree(root->_right);
		delete root;
	}
	~BSTree()  //因为析构函数没有参数,要写成递归就得借用成员函数,嵌套一层
	{
		DestroyTree(_root);
		_root = nullptr;
	}
	//非递归insert
	bool Insert(const K& key,const V& value)   //虽然传了key和value,但只用key比较
	{
		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;
			}
			/*//允许冗余
			if (cur->_key < key)
			{
				parent = cur;
				cur = cur->_right;
			}
			else
			{
				parent = cur;
				cur = cur->_left;
			}*/
		}
		cur = new Node(key,value);
		if (parent->_key < key)
		{
			parent->_right = cur;
		}
		else
		{
			parent->_left = cur;
		}
		return true;
	}
	//非递归find
	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;
	}
	//非递归erase
	//删除情况很多,需要分情况讨论
	//1.当删除的是父结点时,采用替换法:
	//找父结点的左子树的(所有值的)最大值结点或右子树的(所有值的)最小值结点替换被删除的父结点
	//2.当删除的是叶结点,直接删除,再链接
	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
			{
				//一个孩子--左为空or右为空

				if (cur->_left == nullptr)
				{
					//if(parent==nullptr)
					if (cur == _root)
					{
						_root = cur->_right;
					}
					else
					{
						if (cur == parent->_left)
						{
							parent->_left = cur->_right;
						}
						else
						{
							parent->_right = cur->_right;
						}
					}
					delete cur;
				}
				else if (cur->_right == nullptr)
				{
					//if(parent==nullptr)
					if (cur == _root)
					{
						_root = cur->_left;
					}
					else
					{
						if (cur == parent->_left)
						{
							parent->_left = cur->_left;
						}
						else
						{
							parent->_right = cur->_left;
						}
					}
					delete cur;

				}
				else  //两个孩子都不为空 //删除有两个孩子的结点:替换法
				{
					//1.使用右子树的最小值结点替代
					Node* minParent = cur;
					Node* minRight = cur->_right;
					while (minRight->_left)
					{
						minParent = minRight;
						minRight = minRight->_left;
					}
					swap(minRight->_key, cur->_key); //或cur->_key = minRight->_key;
					if (minParent->_left == minRight)
					{
						minParent->_left = minRight->_right;
					}
					else
					{
						minParent->_right = minRight->_right;
					}
					delete minRight;

					2.使用左子树的最大值结点替代
					//Node* maxParent = cur;
					//Node* maxLeft = cur->_left;
					//while (maxLeft->_right)
					//{
					//	maxParent = maxLeft;
					//	maxLeft = maxLeft->_right;
					//}
					//swap(maxLeft->_key, cur->_key); //或cur->_key = maxLeft->_key;
					//if (maxParent->_right == maxLeft)
					//{
					//	maxParent->_right = maxLeft->_left;
					//}
					//else
					//{
					//	maxParent->_left = maxLeft->_left;
					//}
					//delete maxLeft;
				}
				return true;
			}

		}
		return false;
	}
	/
	//递归版
	//无法直接使用私有成员进行递归,因此嵌套一层成员函数,实现递归
	//递归find
	Node* FindR(const K& key)
	{
		return _FindR(_root, key);
	}
	//递归insert
	bool InsertR(const K& key,const V& value)
	{
		return _InsertR(_root, key,value);
	}
	//递归erase
	bool EraseR(const K& key)
	{
		return _EraseR(_root, key);
	}
private:
	//递归insert
	bool _InsertR(Node*& root, const K& key,const V& value)
	{
		//Node*& root这个处理,使得当root为空指针时,但root同时也是父结点的left或right的别名,使得结点得以链接
		if (root == nullptr)
		{
			root = new Node(key,value);
			return true;
		}
		if (root->_key < key)
			return _InsertR(root->_right, key,value);
		else if (root->_key > key)
			return _InsertR(root->_left, key,value);
		else
			return false;
	}
	Node* _FindR(Node* root, const K& key)  //不允许修改key,但可以修改value,因此可以使用Node*返回
	{
		if (root == nullptr)
			return nullptr;
		if (root->_key < key)
		{
			return _FindR(root->_right, key);
		}
		else if (root->_key > key)
		{
			return _FindR(root->_left, key);
		}
		else
		{
			return root;
		}
	}
	//递归erase
	bool _EraseR(Node*& root, const K& key)
	{
		if (root == nullptr)
			return false;
		if (root->_key < key)
		{
			return _EraseR(root->_right, key);
		}
		else if (root->_key > key)
		{
			return _EraseR(root->_left, key);
		}
		else
		{
			Node* del = root; //先保存一下
			if (root->_left == nullptr)
			{
				root = root->_right;
			}
			else if (root->_right == nullptr)
			{
				root = root->_left;
			}
			else
			{
				//1.使用右子树的最小值结点替代
				Node* minRight = root->_right;
				while (minRight->_left)
				{
					minRight = minRight->_left;
				}
				swap(root->_key, minRight->_key);
				return _EraseR(root->_right, key);

				2.使用左子树的最大值结点替代
				//Node* maxLeft = root->_left;
				//while (maxLeft->_right)
				//{
				//	maxLeft = maxLeft->_right;
				//}
				//swap(root->_key, maxLeft->_key);
				//return _EraseR(root->_left, key);
			}
			delete del;
			return true;
		}
	}

public:
	//递归版遍历
	void InOrder()          //1.中序遍历
	{
		_InOrder(_root);
		cout << endl;
	}
	void PrevOrder()        //2.前序遍历
	{
		_PrevOrder(_root);
		cout << endl;
	}
	void PostOrder()       //3.后序遍历
	{
		_PostOrder(_root);
		cout << endl;
	}
private:
	//在C语言中,root可以被直接获取,
	//在C++中,因为_root 是私有,被封装起来了所以可有以下两种方式获得
	//1.可以写一个成员函数GetRoot()获取
	//2.也可以使用遍历,不被继承就设为私有成员函数

	void _InOrder(Node* root)     //1.中序遍历
	{
		if (root == nullptr)
			return;
		_InOrder(root->_left);
		cout << root->_key << " 出现次数:"<<root->_value<<endl;
		_InOrder(root->_right);
	}
	void _PrevOrder(Node* root)   //2.前序遍历
	{
		if (root == nullptr)
			return;
		//cout << root->_key << " ";
		cout << root->_key << " 出现次数:" << root->_value << endl;

		_PrevOrder(root->_left);
		_PrevOrder(root->_right);
	}
	void _PostOrder(Node* root)   //3.后序遍历
	{
		if (root == nullptr)
			return;
		_PostOrder(root->_left);
		_PostOrder(root->_right);
		//cout << root->_key << " ";
		cout << root->_key << " 出现次数:" << root->_value << endl;

	}
private:
	Node* _root = nullptr;  //调用自定义类型Node*
};

(2)test.cpp:

#include"BSTreeKeyValue.h"

void TestBSTreeKV1()
{
	BSTree<string, string> ECdict;  //英文找中文
	//使用递归InsertR
	/*ECdict.InsertR("hello", "你好");
	ECdict.InsertR("world", "世界");*/
	//使用非递归Insert
	ECdict.Insert("hello", "你好");
	ECdict.Insert("world", "世界");

	string str;
	cout << "请输入要查询的英文单词:" << endl;
	while (cin >> str)  //原理同while(scanf()!=EOF)   //operator>>a
	{
		使用递归InsertR
		//BSTreeNode<string, string>*ret= ECdict.FindR(str);  //或使用auto
		auto ret= ECdict.FindR(str);
		

		//使用非递归Insert
		BSTreeNode<string, string>* ret = ECdict.Find(str);  //或使用auto
		//auto ret= ECdict.Find(str);


		if (ret != nullptr)
		{
			cout << "对应的中文:" << ret->_value << endl;
			cout << "请输入要查询的英文单词:" << endl;
		}
		else
		{
			cout << "无此单词,请重新输入!" << endl;
			cout << "请输入要查询的英文单词:" << endl;
		}
	}
}
void TestBSTreeKV2()
{
	1.使用非递归的insert、find
	 统计水果出现的次数
	//string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜",
 //  "苹果", "香蕉", "苹果", "香蕉" };
	//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();
	///*countTree.PrevOrder();
	//countTree.PostOrder();*/

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

3.2.5 K、V模型应用举例:

(1)

(2)

 

后记:
●由于作者水平有限,文章难免存在谬误之处,敬请读者斧正,俚语成篇,恳望指教!

                                                                       ——By 作者:新晓·故知

 

  • 10
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 12
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值