IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 中级C++:二叉搜索树、key-Value模型 -> 正文阅读

[C++知识库]中级C++:二叉搜索树、key-Value模型

二叉搜索树的特征

  • 左子树永远比根节点小,右子树永远比根节点大
  • 也是key模型,看某个数据在不在集合里面。

在这里插入图片描述

搜索树的添加

  • key值比根值大,往右边插入,小,往左边插入
  • key值和根节点值比较,确定插入在根节点的左边还是右边,根节点一定是某个叶子结点。
  • 比如插入 4,一定是在3的右边
  • 下面的x 当作key
template<class K>
struct bstn
{
	bstn()
	{
		cout << "单个链表结点的声明初始化" << endl;
	}
	bstn(const K& x)
		:_left(nullptr)
		, _right(nullptr)
		, _value(x)
	{

	}
	bstn* _left;
	bstn* _right;
	K _value;
};

template<class K>
bool bst<K>::Insert(const K& x)
{
	return bst<K>::_Insert(x);
}

template<class K>
bool bst<K>::_Insert(const K& x)
{
	if (_root==nullptr)/ /空树的处理
	{
		_root = new Node(x);/ /调用自定义类型并用x初始化
		return true;
	}
	
	Node* parent = nullptr;/ /防止走到空找不到根节点
	Node* cur = _root;
	while (cur)
	{
		/ /比右边大,就去右子树的右边插入,
		if (x>cur->_value)
		{
			parent = cur;
			cur = cur->_right;
		}
		else if(x<cur->_value)  / /,比根值 小去左边插入
		{
			parent = cur;
			cur = cur->_left;
		}
		else
		{
			return false;
			/ /这个版本的搜索二叉树不支持值相等
		}

	}
	/ /一定比叶子节点值 大或小 进行连接插入
	cur = new Node(x);          /new会调用自定义类型的构造函数初始化
	if (parent->_value < cur->_value)  /判断插入在根的左边还是右边
	{
		parent->_right = cur;
	}
	else
	{
		parent->_left = cur;
	}
	return true;
}

搜索树的遍历打印

  • 仿佛是二叉树的中序遍历,排出来就是升序。
template<class K>
bool bst<K>::Ergodic()
{
	return _Ergodic(_root);
}
template<class K>
bool bst<K>::_Ergodic(Node* root)
{
/视为一个满二叉树进行遍历,找到我左为空返回,根,右子树为空返回,在返回上一层;以此类推

	if (root == nullptr)
	{
		return false;
	}
	_Ergodic(root->_left);
	cout << root->_value << " ";
	_Ergodic(root->_right);
}

搜索树的查找

  • key值比根值大,往右边找;比根值小,往左边找,比如说找:8
    在这里插入图片描述
Node* FindR(const K& key)
	{
		return _FindR(_root, key);
	}
Node* _Find(const K& x)
	{
		Node* cur = _root;
		while (cur)
		{
			if (x>cur->_value)
			{
				cur = cur->_right;
			}
			else if(x<cur->_value)
			{
				cur = cur->_left;
			}
			else
			{
				/ /不小于左边,不大于右边
				return cur;
			}
		}
		return nullptr;
	}



搜索二叉树的删除

  • 先找到那个值:key值比根值大,往右边找;比根值小,往左边找,
  • 找到以后两种情况:
    1. (key值)结点的一侧为空 ,父结点连接上不为空的一侧,删除(key值)结点;5,6,8.删除5
    2. (key值)结点的两侧都不为空:找(key值)左右两边的最值替换:找左边最大的或右边最小的替换。
    3. 往根的左边走一步,如果右边不为空,然后持续往右走;最大值的右子树必然是空,往右找到空,记录父节点,然后删除最大值节点。
    4. 比如删除 2
      在这里插入图片描述
bool Erase(const K& key)
	{
		return _Erase(_root, key);
	}
template<class K>
bool bst<K>::_Erase(const K& x)  /  /x  ---》  key
{
	
	Node* parent = nullptr;
	Node* cur = _root;
	while (cur)
	{
		if (cur->_value < x)
		{
			parent = cur;
			cur = cur->_right;
		}
		else if(cur->_value>x)
		{
			parent = cur;
			cur = cur->_left;
		}
		else  / /上面走完之后 此处 值相等 找到
		{
			/ /左子树或右子树为空,双亲结点连接上孩子结点不为空的一侧,删除key值节点
			if (cur->_left == nullptr)//如果cur左边为空,右边不为空
			{
				if (_root == cur)//只有一个结点或单条边为空。
				{
					_root = cur->_right;
				}
				else
				{
					if (cur = parent->_left)//cur是双亲结点的左边
					{
						//则链接cur的右边
						parent->_left = cur->_right;
					}
					else  //是双亲结点的右边 且左边为空
					{
						parent->_right = cur->_right;
					}
				}
				delete cur;
			}
			else if (cur->_right == nullptr)
			{
				if (_root == cur)//只有一个结点或单条边为空。
				{
					_root = cur->_left;
				}
				else
				{
					if (cur = parent->_left)
					{
						parent->_left = cur->_left;
					}
					else
					{
						parent->_right = cur->_left;
					}
				}
				delete cur;
			}
			else  / /两边都不为空   左边的最大值节点和 key节点交换值 ,   删除最大值节点
			{

				Node* maxLeft = cur->_left;
				Node* pmax = cur;
				while (maxLeft->_right)   / /不为空就往右边走  一个值记录最值的父节点
				{
					pmax = maxLeft;
					maxLeft = maxLeft->_right;
				}
				cur->_value = maxLeft->_value;
				if (pmax->_right=maxLeft)  / /此时最大值节点的右边为空,左边还可能有子树比它小的
				{
					pmax->_right = maxLeft->_left;/ / 判断最大值节点是父结点的哪一边
				}
				else   / /让父节点接上最大值节点的左子树
				{
					pmax->_left = maxLeft->_left;
				}
				delete maxLeft;
			}
			return true;   / /删除完成返回
		}
	}
	return false;  / /走到空说明没有那个值
}


递归版本

  • 贵在每一次递归进来就是对根节点子树的引用。

递归插入

  • 插入10
    在这里插入图片描述

	bool InsertR(const K& key)
	{
		return _InsertR(_root, key);
	}
	
template<class K>
bool bst<K>::_InsertR(Node*& root, const K& key)
{
	if (root==nullptr)   / /是对8右指针的引用
	{
		root = new Node(key);  / / 8->right = .....接上即可
		return true;
	}
	else
	{
		if (root->_value < key)
			return _InsertR(root->_right, key);
		else if (root->_value > key)
			return _InsertR(root->_left, key);
		else
			return false;
	}
}

递归查找

  • 比如查找:3
    在这里插入图片描述
Node* FindR(const K& key)
	{
		return _FindR(_root, key);
	}

template<class K>
bst<K>::Node* bst<K>::_FindR(Node*& root,const K& key)  / /3
{
	if (root == nullptr)   / /2的右子树引用  3
	{
		return nullptr;
	}
	if (root->_value < key)                       / /2   
		return _FindR(root->_right, key);      / /不大
	else if (root->_value > key)  / / 5   2      / /不小
		return _FindR(root->_left, key);
	else
		return root;    / /相等返回  秒啊
}

递归删除

  • 比如删除:2
    在这里插入图片描述
    在这里插入图片描述
bool EraseR(const K& key)
	{
		return _EraseR(_root, key);
	}
template<class K>
bool bst<K>::_EraseR(Node*& root,const K& key)
{
	if (root == nullptr)
	{
		return false;
	}
	if (root->_value<key)
	{
		return _EraseR(root->_right, key);
	}
	else if (root->_value>key)  / /5 > 2  
	{
		return _EraseR(root->_left, key);  / /ture   返回....
	}
	else
	{
		Node* del = root;   / /root 是 对2的引用  也是2这个节点
		if (root->_left == nullptr)
		{
			root == root->_right;/ / 5的left 链接到key值不为空的一侧
		}
		else if (root->_right == nullptr)
		{
			root = root->_left;  / /最后删除del
		}
		else  / /1  3
		{
			Node* maxLeft = root->_left;  / /root 还是2 
			while (maxLeft->_right) /  / 左孩子节点的右子树为空则  说明左孩子的值就是最大值
			{
				maxLeft = maxLeft->_right;
			}
			root->_value = maxLeft->_value;/ /替换

			return _Erase(root->_left,maxLeft->key);  /  /再递归删除 最大值节点 
			/ / 2的左边引用     1 相等 ; 1的节点交给 del ;1的左边为空,  2左边的引用 链接到引用的右边;删除 del ,返回。
		}
		delete del;
		return true;
	}
}

接口总览

template<class K>
class bst
{
public:
	typedef bstn<K>  Node;
	bst()
	{
		cout << "搜索二叉树的节点" << endl;
	}
	bool Insert(const K& x);
	bool Ergodic();
	Node* Find(const K& x)
	{
		return _Find(x);
	}
	bool Erase(const K& x)
	{
		return _Erase(x);
	}
	bool InsertR(const K& key)
	{
		return _InsertR(_root, key);
	}
	Node* FindR(const K& key)
	{
		return _FindR(_root, key);
	}
	bool EraseR(const K& key)
	{
		return _EraseR(_root, key);
	}
private:
	bool _Insert(const K& x);/ /隐藏实现细节
	bool _Ergodic(Node* root);
	bool _Erase(const K& x);
	Node* _Find(const K& x)
	{
		Node* cur = _root;
		while (cur)
		{
			if (x>cur->_value)
			{
				cur = cur->_right;
			}
			else if(x<cur->_value)
			{
				cur = cur->_left;
			}
			else
			{
				/ /不小于左边,不大于右边
				return cur;
			}
		}
		return nullptr;
	}

	bool _InsertR(Node*& root,const K& key);
	Node* _FindR(Node*& root,const K& key);
	bool _EraseR(Node*& root,const K& key);

	Node* _root;
};

key-Value 模型

  • 场景:字典翻译,统计单词出现的次数; 刷身份证进站。
  • 是对Key模型的改造:
    • 添加一个V模板参数即可,添加时多添加一个参数
    • 找到Key就能删除Value

基建

namespace KEY_VALUE
{
	template<class K, class V>
	struct BSTreeNode
	{
		BSTreeNode<K, V>* _left;
		BSTreeNode<K, V>* _right;
		K _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:
	.....
	private:
	Node* _root = nullptr;
	}

key-Value的添加

bool _InsertR(Node*& root, const K& key, const V& value)
{
	if (root==nullptr)  
	{
		root = new Node(key,value);  
		return true;
	}
	else
	{
		if (root->_key < key)
			return _InsertR(root->_right, key);
		else if (root->_key > key)
			return _InsertR(root->_left, key);
		else
			return false;
	}
}

key-Value的遍历打印

bool _Ergodic(Node* root)
{
	if (root == nullptr)
	{
		return false;
	}
	_Ergodic(root->_left);
	cout << root->_key << " "<<root->_value<<endl;
	_Ergodic(root->_right);
}

key-Value的查找

Node* _FindR(const K& key)
	{
		Node* cur = _root;
		while (cur)
		{
			if (key>cur->_key)
			{
				cur = cur->_right;
			}
			else if(key<cur->_key)
			{
				cur = cur->_left;
			}
			else
			{
				/ /不小于左边,不大于右边
				return cur;
			}
		}
		return nullptr;
	}

key-Value的删除

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  
		{
			Node* maxLeft = root->_left;  
			while (maxLeft->_right) 
			{
				maxLeft = maxLeft->_right;
			}
			root->_key = maxLeft->_key;/ /替换
			root->_value =maxLeft->_value;

			return _Erase(root->_left,maxLeft->_key);  /  /再递归删除 最大值节点 
		}
		delete del;
		return true;
	}
}

简单字典

void TestDict()
{
	KEY_VALUE::BSTree<string, string> dict;
	dict.InsertR("crash", "崩溃");
	dict.InsertR("priority", "优先级");
	string str;
	while (cin >> str)
	{
		if (str == "Q")/ /quit 退出
		{
			break;
		}
		else
		{
			auto ret = dict.FindR(str);  / /返回的是个结构体指针Node*
			if (ret == nullptr)
			{
				cout << "This word is not available in the dictionary, please re-enter it" << endl;
			}
			else
			{
				cout << ret->_key << "->" << ret->_value << endl;
			}
		}
	}
}

统计单词(字符串)出现次数

void TestCWord()
{
	string str[] = { "i","dont","have","a","dream","when","i","was","a","child" };
	KEY_VALUE::BSTree<string, int> countree;
	for (auto& i : str)
	{
		auto ret = countree._FindR(i);
		if (ret==nullptr)
		{
			countree._InsertR(i, 1);/ /没有则进行插入,次数是第一次
		}
		else
		{
			ret->_value++;/ /有则 int value ++
		}
	}
	countree._Ergodic();
}

结束

  • 按时吃饭,不要熬夜,太累了就睡觉…
    在这里插入图片描述
  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-06-26 16:45:42  更:2022-06-26 16:45:53 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/11 21:01:49-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码