C++二叉搜索树

概念

二叉搜索树也叫二叉排序树(也可以是空树),它是具备以下特征的二叉树:

1.若它的左子树不为空,则它的所有左子树的值都小于根结点。

2.若它的右子树不为空,则它的所有右子树的值都大于根结点。

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

二叉树的应用

1.K模型(set):

  即只有K作为关键码的模型,结构中只存储Key。

  我们经常用这个模型来判断有没有的场景,比如一个单词在一个词典里面有没有。

2.K-V模型(map):

  每一个关键码Key都有一个对应的Val。

  这种对应关系我们可以用来找单词的出现频率,英文对应的中文的场景。

代码实现(K-V模型非递归)

#pragma once

#pragma once
#include<iostream>
#include <queue>

using namespace std;

namespace hzj {
	template<class K, class V>
	struct BSTreeNode
	{
		BSTreeNode<K, V>* _left;
		BSTreeNode<K, V>* _right;
		K _key;
		V _val;
		BSTreeNode(const K& key, const V& val)
			:
			_left(nullptr),
			_right(nullptr),
			_key(key),
			_val(val)
		{}
	};

	template<class K, class V>
	class BSTree
	{
		typedef BSTreeNode<K, V> Node;
	public:
		BSTree()
			:_root(nullptr)
		{}
		bool Insert(const K& key, const V& val)
		{
			if (_root == nullptr)
			{
				_root = new Node(key, val);
				return true;
			}
			Node* prev = _root;
			Node* cur = _root;
			while (cur)
			{
				if (key < cur->_key)
				{
					prev = cur;
					cur = cur->_left;
				}
				else if (key > cur->_key)
				{
					prev = cur;
					cur = cur->_right;
				}
				else
				{
					cout << "该元素已经存在" << endl;
					return false;
				}
			}
			cur = new Node(key, val);
			if (key < prev->_key)
			{
				prev->_left = cur;
			}
			else
			{
				prev->_right = cur;
			}
			return true;
		}
		Node* Find(const K& key)
		{
			if (_root == nullptr)
			{
				return nullptr;
			}
			Node* cur = _root;
			while (cur)
			{
				if (key < cur->_key)
				{
					cur = cur->_left;
				}
				else if (key > cur->_key)
				{
					cur = cur->_right;
				}
				else
				{
					return cur;
				}
			}
			return nullptr;
		}
		bool Erase(const K& key)
		{
			Node* cur = _root;
			Node* prev = _root;
			while (cur)
			{
				if (key < cur->_key)
				{
					prev = cur;
					cur = cur->_left;
				}
				else if (key > cur->_key)
				{
					prev = cur;
					cur = cur->_right;
				}
				else
				{
					if (cur->_left == nullptr)//左为空
					{
						if (cur == _root)
						{
							_root = _root->_right;
						}
						if (cur->_right == nullptr)//左右均为空
						{
							if (prev->_left == cur)
							{
								prev->_left = nullptr;
							}
							else
							{
								prev->_right = nullptr;
							}
						}
						else //右不为空
						{
							if (cur == prev->_right)
							{
								prev->_right = cur->_right;
							}
							else
							{
								prev->_left = cur->_right;
							}
						}
					}
					else if (cur->_right == nullptr)//右为空,左不为空
					{
						if (cur == _root)
						{
							_root = _root->_left;
						}
						if (cur == prev->_right)
						{
							prev->_right = cur->_left;
						}
						else
						{
							prev->_left = cur->_left;
						}
					}
					else //左右均不为空,此时可以选择找cur左树的最大值或者右树的最小值
					{
						//此时不需要处理特殊情况(cur==_root),因为这里与prev无关,不会造成野指针问题。
						Node* leftMax = cur->_left;//此处用找左树的最大值做法
						Node* prevMax = cur;
						while (leftMax->_right)
						{
							prevMax = leftMax;
							leftMax = leftMax->_right;
						}
						swap(cur->_key, leftMax->_key);
						swap(cur->_val, leftMax->_val);
						if (prevMax->_left == leftMax)  // 一种特殊情况,就是左树没有右结点
						{
							prevMax->_left = leftMax->_left;
						}
						else
						{
							prevMax->_right = leftMax->_left;
						}
						cur = leftMax;
					}
					//cout << endl;
					delete cur;
					return true;
				}
			}
			return false;
		}
		void InOrder()
		{
			_InOrder(_root);
		}
		void PreOrder()
		{
			_PreOrder(_root);
		}
		void LevelOrder() // 层序遍历
		{
			_LevelOrder(_root);
		}
	private:
		Node* _root;
		void _InOrder(Node* root)
		{
			if (root == nullptr) return;
			_InOrder(root->_left);
			cout << root->_key << ":" << root->_val << " ";
			_InOrder(root->_right);
		}
		void _PreOrder(Node* root)
		{
			if (root == nullptr) return;
			cout << root->_key << ":" << root->_val << " ";
			_PreOrder(root->_left);
			_PreOrder(root->_right);
		}
		void _LevelOrder(Node* root)
		{
			queue<Node*> q;
			q.push(root);
			while (q.size())
			{
				Node* tmp;
				int n = q.size();
				for (int i = 0; i < n; ++i)
				{
					tmp = q.front();
					q.pop();
					cout << tmp->_key << " : " << tmp->_val;
					if (tmp->_left) q.push(tmp->_left);
					if (tmp->_right) q.push(tmp->_right);
				}
				cout << endl;
			}
		}
	};
	void test1()
	{
		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->_val << 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->_val++;
			}
		}
		countTree.InOrder();
	}
	void test2()
	{
		BSTree<string, string> dict;
		dict.Insert("insert", "插入");
		dict.Insert("erase", "删除");
		dict.Insert("left", "左边");
		dict.Insert("string", "字符串");
		dict.InOrder();
		cout << endl;
		dict.Erase("insert");
		dict.Erase("erase");
		dict.Erase("left");
		dict.Erase("string");
		dict.InOrder();
	}
}

 二叉搜索树代码实现唯一比较困难的地方就是删除函数(Erase),因为如何将目标结点删除后仍然保持二叉搜索树的结构有点复杂,需要耐心分析多种情况。其中要注意的是,当删除的目标结点左右都不为空时最为复杂,我们可以通过找到目标结点左树最大值,或者右树最小值,找到后与目标结点的值进行交换,然后再进行尾部删除。

二叉搜索树的性能分析

  因为插入和删除都需要查找,所以查找效率代表了二叉搜索树中各个操作的性能。

  总结:

  最好的情况查找次数为高度次,时间复杂度就是O(logn),最坏会退化为单分支树,那么时间复杂度也就是O(n)。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值