BSTree二叉树讲解

二叉搜索树的概念:

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

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

二叉树的运用:(改代码就是KV模型的二叉搜索树)

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

的值。

比如:给一个单词word,判断该单词是否拼写正确,具体方式如下:
以词库中所有单词集合中的每个单词作为key,构建一棵二叉搜索树

在二叉搜索树中检索该单词是否存在,存在则拼写正确,不存在则拼写错误。

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

式在现实生活中非常常见:

比如英汉词典就是英文与中文的对应关系,通过英文可以快速找到与其对应的中文,英
文单词与其对应的中文<word, chinese>就构成一种键值对;
再比如统计单词次数,统计成功后,给定单词就可快速找到其出现的次数,单词与其出
现次数就是<word, count>就构成一种键值对。

二叉树的查找:

a、从根开始比较,查找,比根大则往右边走查找,比根小则往左边走查找。

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

cpp 复制代码
	Node* _Find(Node* root,const K& key)
	{
		if (root == nullptr)//遇到空节点就返回
		{
			return nullptr;
		}
		if (root->_kv.first == key)
		{
			return root;
		}
		else if (root->_kv.first > key)
		{
			_Find(root->_left, key);
		}
		else
		{
			_Find(root->_right, key);
		}
	}

二叉搜索树的插入:

插入的具体过程如下:

a. 树为空,则直接新增节点,赋值给root指针

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

cpp 复制代码
	bool _Insert(const pair<K, V>& kv)//这里使用pair------key,value模型 如字典
	{
		if (_root == nullptr)//如果该二叉树的树为空 就给个节点然后返回
		{
			_root = new Node(kv);
			return true;
		}
        //不为空就进行寻找 找到太该呆的位置 
		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_kv.first > kv.first)
			{
				parent = cur;
				cur = cur->_left;
			}
			else if (cur->_kv.first < kv.first)
			{
				parent = cur;
				cur = cur->_right;
			}
			else
			{
				return false;
			}
		}
        //判断该节点在父节点的左边还是右边
		if (parent->_kv.first > kv.first)
		{
			parent->_left = new Node(kv);
		}
		else
		{
			parent->_right = new Node(kv);
		}
	}
	Node* _root = nullptr;
};

二叉搜索树的删除:

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

况:

a. 要删除的结点无孩子结点

b. 要删除的结点只有左孩子结点

c. 要删除的结点只有右孩子结点

d. 要删除的结点有左、右孩子结点

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

如下:
情况b:删除该结点且使被删除节点的双亲结点指向被删除节点的左孩子结点--直接删除
情况c:删除该结点且使被删除节点的双亲结点指向被删除结点的右孩子结点--直接删除
情况d:在它的右子树中寻找中序下的第一个结点(关键码最小),用它的值填补到被删除节点
中,再来处理该结点的删除问题--替换法删除

cpp 复制代码
	bool _Erase(Node* root, const K& key)
	{
		Node* parent = nullptr;
		Node* cur = root;
		while (cur)
		{
			if (cur->_kv.first == key)//如果key与要搜索的值相等 就进行删除
			{
				if (cur->_left == nullptr)//左子树为空 右子树不为空
				{//再考虑要删除的节点是他父节点的左子树还是右子树 将该节点接到其父节点上
					if (cur == parent->_left)
					{
						parent->_left = cur->_right;
					}
					else
					{
						parent->_right = cur->_right;
					}
					delete cur;//将该节点进行释放
				}
				else if (cur->_right == nullptr)//右子树不存在 左子树存在 同上
				{
					if (cur == parent->_left)
					{
						parent->_left = cur->_left;
					}
					else
					{
						parent->_right = cur->_left;
					}
					delete cur;
				}
				else//左右子树都存在 需要寻找一个可以代替该节点值的值 
				{//在左子树中找最小的值 (就是左子树最左边的那个节点)右节点就找右子树中最大的节点 (就是右子树最右边的那个节点)
					Node* parent1 = nullptr;
					Node* tmp = cur;
					while (tmp->_left)//寻找左节点
					{
						parent1 = tmp;
						tmp = tmp->_left;
					}
					swap(&cur->_kv, &tmp->_kv);//找到进行值的交换
					parent1->_left = tmp->_right;//如果最小的左节点的左子树为空右子树不为空 将右子树接到该节点的父亲上 但是如果右子树为空 也可以将空节点接到其父节点上 不产生影响
					delete tmp;//释放节点
				}
			}
			else
			{
				if (root->_kv.first > key)
				{
					parent = cur;
					cur = cur->_left;
				}
				else
				{
					parent = cur;
					cur = cur->_right;
				}
			}
		}
	}

二叉树的遍历:(中序遍历)(将二叉树中key的值按顺序打印)

cpp 复制代码
	void _InOrder(Node* root)
	{
		if (root == nullptr)
		{
			return;
		}
		_InOrder(root->_left);
		cout << root->_kv.first << " " << root->_kv.second << endl;
		_InOrder(root->_right);
	}

以下就是二叉搜索树的完整代码 以及对应的测试用例,可以自行去调用测试。

cpp 复制代码
#include<iostream>
#include<string>
using namespace std;

template<class K,class V>
struct BSTreeNode
{
	typedef BSTreeNode<K, V> Node;

	BSTreeNode(const pair<K, V>& x)
		:_kv(x)
		, _left(nullptr)
		, _right(nullptr)
	{

	}
	pair<K, V> _kv;
	Node* _left;
	Node* _right;
};

template<class K, class V>
class BSTree
{
	typedef BSTreeNode<K, V> Node;
public:
	bool Insert(const K& key, const V& value)
	{
		pair<K, V> kv(key, value);
		return _Insert(kv);
	}
	Node* Find(const K& key)
	{
		return _Find(_root,key);
	}
	bool Erase(const K& key)
	{
		return _Erase(_root, key);
	}
	void InOrder()
	{
		_InOrder(_root);
	}
private:
	bool _Erase(Node* root, const K& key)
	{
		Node* parent = nullptr;
		Node* cur = root;
		while (cur)
		{
			if (cur->_kv.first == key)
			{
				if (cur->_left == nullptr)
				{
					if (cur == parent->_left)
					{
						parent->_left = cur->_right;
					}
					else
					{
						parent->_right = cur->_right;
					}
					delete cur;
				}
				else if (cur->_right == nullptr)
				{
					if (cur == parent->_left)
					{
						parent->_left = cur->_left;
					}
					else
					{
						parent->_right = cur->_left;
					}
					delete cur;
				}
				else
				{
					Node* parent1 = nullptr;
					Node* tmp = cur;
					while (tmp->_left)
					{
						parent1 = tmp;
						tmp = tmp->_left;
					}
					swap(&cur->_kv, &tmp->_kv);
					parent1->_left = tmp->_right;
					delete tmp;
				}
			}
			else
			{
				if (root->_kv.first > key)
				{
					parent = cur;
					cur = cur->_left;
				}
				else
				{
					parent = cur;
					cur = cur->_right;
				}
			}
		}
	}
	void _InOrder(Node* root)
	{
		if (root == nullptr)
		{
			return;
		}
		_InOrder(root->_left);
		cout << root->_kv.first << " " << root->_kv.second << endl;
		_InOrder(root->_right);
	}
	Node* _Find(Node* root,const K& key)
	{
		if (root == nullptr)
		{
			return nullptr;
		}
		if (root->_kv.first == key)
		{
			return root;
		}
		else if (root->_kv.first > key)
		{
			_Find(root->_left, key);
		}
		else
		{
			_Find(root->_right, key);
		}
	}
	bool _Insert(const pair<K, V>& kv)
	{
		if (_root == nullptr)
		{
			_root = new Node(kv);
			return true;
		}
		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_kv.first > kv.first)
			{
				parent = cur;
				cur = cur->_left;
			}
			else if (cur->_kv.first < kv.first)
			{
				parent = cur;
				cur = cur->_right;
			}
			else
			{
				return false;
			}
		}
		if (parent->_kv.first > kv.first)
		{
			parent->_left = new Node(kv);
		}
		else
		{
			parent->_right = new Node(kv);
		}
	}
	Node* _root = nullptr;
};

void TestBSTree()
{
	BSTree<string, string> dict;
	dict.Insert("insert", "插入");
	dict.Insert("erase", "删除");
	dict.Insert("left", "左边");
	dict.Insert("string", "字符串");

	string str;
	while (cin >> str)
	{
		auto ret = dict.Find(str);
		if (ret)
		{
			cout << str << ":" << ret->_kv.second << endl;
		}
		else
		{
			cout << "单词拼写错误" << endl;
		}
	}

	string strs[] = { "苹果", "西瓜", "苹果", "樱桃", "苹果", "樱桃", "苹果", "樱桃", "苹果" };
	// 统计水果出现的次
	BSTree<string, int> countTree;
	for (auto str : strs)
	{
		auto ret = countTree.Find(str);
		if (ret == NULL)
		{
			countTree.Insert(str, 1);
		}
		else
		{
			ret->_kv.second++;
		}
	}
	countTree.InOrder();
}
相关推荐
奋斗的小花生12 分钟前
c++ 多态性
开发语言·c++
魔道不误砍柴功15 分钟前
Java 中如何巧妙应用 Function 让方法复用性更强
java·开发语言·python
pianmian116 分钟前
python数据结构基础(7)
数据结构·算法
闲晨18 分钟前
C++ 继承:代码传承的魔法棒,开启奇幻编程之旅
java·c语言·开发语言·c++·经验分享
老猿讲编程1 小时前
一个例子来说明Ada语言的实时性支持
开发语言·ada
UestcXiye2 小时前
《TCP/IP网络编程》学习笔记 | Chapter 3:地址族与数据序列
c++·计算机网络·ip·tcp
Chrikk2 小时前
Go-性能调优实战案例
开发语言·后端·golang
幼儿园老大*2 小时前
Go的环境搭建以及GoLand安装教程
开发语言·经验分享·后端·golang·go
canyuemanyue2 小时前
go语言连续监控事件并回调处理
开发语言·后端·golang
杜杜的man2 小时前
【go从零单排】go语言中的指针
开发语言·后端·golang