c++之二叉搜索树的实现

首先从这棵树开始,以后的模板参数都叫K(key关键类型)了

1.二叉搜索树的节点设置

cpp 复制代码
template<class K>
struct BSTreeNode
{
	K _key;
	BSTreeNode<K>* _left;
	BSTreeNode<K>* _right;
	BSTreeNode(const K& x)
		:_key(x)
		,_left(nullptr)
		,_right(nullptr)
	{ }
};

2.二叉树的大致框架

cpp 复制代码
template<class K>
class BSTree
{
	typedef BSTreeNode<K> Node;
	//using Node = BSTreeNode;
	//二者的写法在目前的效果一样

private:
	Node* _root = nullptr;
};

3.插入

二叉搜索树有去重和不去重的,下面两种都进行说明

cpp 复制代码
//bool是用于确定insert是否成功的
//不能返回结点只是因为不能对节点中的值做修改
bool insert(const K& x)
{
	if (_root == nullptr)
	{
		_root = new Node(x);
		return true;
	}
	Node* parent = nullptr;
	Node* cur = _root;
	while (cur)
	{
		if (cur->_key < x)
		{
			parent = cur;
			cur = cur->_right;
		}
		else if (cur->_key > x)
		{
			parent = cur;
			cur = cur->_left;
		}
		else return false;
	}
	//这样就可以实现相同值的赋值
	/*while (cur)
	{
		if (cur->_key < x)
		{
			parent = cur;
			cur = cur->_right;
		}
		else
		{
			parent = cur;
			cur = cur->_left;
		}
	}*/
	cur = new Node(x);
	//当cur指向新结点时,parent就找不到cur了
	//但之前的cur的位置就是parent的位置
	//因此parent只要学着cur再走一次就知道cur去哪了
	if (parent->_key < x)
	parent->_right = cur;
	else
	parent->_left = cur;
	return true;
}

4.中序遍历

二叉平衡树是小的在左边,大的在右边,因此中序遍历是就能优先走小的值再走大的值。

且由于递归需要参数root,但在类外面是无法有树的根节点的,因此要套一层壳。

cpp 复制代码
	void inorder()
	{
		_inorder(_root);
		cout << endl;
	}
private:
	void _inorder(Node* root)
	{
		if (root == nullptr)return;
		_inorder(root->_left);
		cout << root->_key << " ";
		_inorder(root->_right);
	}

5.查找(也是只能知道在不在,无法知道节点的位置)

cpp 复制代码
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;
}

6.删除

删除可以分为两种情况

1.被删去的结点只有一个孩子或没有孩子

2.被删去的结点有两个孩子

cpp 复制代码
	bool pop(const K& key)
	{
		Node* cur = _root;
		Node* parent = nullptr;
		//首先要找到要删出的结点
		while (cur)
		{
			if (cur->_key < key)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (cur->_key > key)
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				//找到了以后就分情况讨论
				if (cur->_left == nullptr)
				{
					//cur是root时,后面删cur就变成删root了,因为此时
					//parent与root并不同步
					if (cur == _root)
					{
						_root = cur->_right;
						delete cur;
						return true;
					}
					else
					{
						//并不清楚cur是parent的哪个孩子,因此要分类讨论
						if (parent->_left == cur)
						{
							parent->_left = cur->_right;
						}
						else
						{
							parent->_right = cur->_right;
						}
					}
					delete cur;
				}
				else if (cur->_right == nullptr)
				{
					if (cur == _root)
					{
						_root = cur->_left;
						delete cur;
						return true;
					}
					else
					{
						if (parent->_left == cur)
						{
							parent->_left = cur->_left;
						}
						else
						{
							parent->_right = cur->_left;
						}
					}
					delete cur;
				}
				//此处就是对两个孩子的处理
				else
				{
					Node* replace = cur->_right;
					Node* replacepar = cur;
					while (replace->_left)
					{
						replacepar = replace;
						replace = replace->_left;
					}
					cur->_key = replace->_key;
                    //此处也要找,因为当replace没有拐弯时,他是父亲的右孩子
					if (replacepar->_left == replace)
					{
						replacepar->_left = replace->_right;
					}
					else
					{
						replacepar->_right = replace->_right;
					}
					delete replace;
				}
			}
		}
		return true;
	}

replace是replacepar的左右结点的两种情况

平衡二叉树的用途:

1.查key

(1)小区停车场查车牌

(2)用英语库查文章是否有单词错误

2.用key/value查value

(1)记录一辆车在停车场停的时间

(2)一篇文章一个单词的出现次数

补充:时间复杂度也叫线性复杂度

7.写key/value的二叉树代码(只需要修改部分即可)

cpp 复制代码
	// 在结点处再加一个存vlaue的参数即可
	template<class K,class V>
	struct BSTreeNode
	{
		K _key;
		V _value;
		BSTreeNode<K,V>* _left;
		BSTreeNode<K,V>* _right;
		BSTreeNode(const K& x,const V& v)
			:_key(x)
			,_value(v)
			, _left(nullptr)
			, _right(nullptr)
		{ }
	};
	template<class K,class V>
	class BSTree
	{
	public:
		typedef BSTreeNode<K,V> Node;
		BSTree()
		{ }
		//再插多一个参数即可
		bool insert(const K& x,const V& v)
		{
			if (_root == nullptr)
			{
				_root = new Node(x,v);
				return true;
			}
			Node* parent = nullptr;
			Node* cur = _root;
			//比较逻辑完全不用变
			while (cur)
			{
				if (cur->_key < x)
				{
					parent = cur;
					cur = cur->_right;
				}
				else if (cur->_key > x)
				{
					parent = cur;
					cur = cur->_left;
				}
				else return false;
			}
			cur = new Node(x, v);
			if (parent->_key < x)
				parent->_right = cur;
			else
				parent->_left = cur;
			return true;
		}
		void inorder()
		{
			_inorder(_root);
			cout << endl;
		}
//返回值修改为Node*,原因在于key不允许被修改,但value可以.
		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;
		}
		bool pop(const K& key)
		{
			Node* cur = _root;
			Node* parent = nullptr;
			while (cur)
			{
				if (cur->_key < key)
				{
					parent = cur;
					cur = cur->_right;
				}
				else if (cur->_key > key)
				{
					parent = cur;
					cur = cur->_left;
				}
				else
				{
					if (cur->_left == nullptr)
					{
						if (cur == _root)
						{
							_root = cur->_right;
							delete cur;
							return true;
						}
						else
						{
							if (parent->_left == cur)
							{
								parent->_left = cur->_right;
							}
							else
							{
								parent->_right = cur->_right;
							}
						}
						delete cur;
					}
					else if (cur->_right == nullptr)
					{
						if (cur == _root)
						{
							_root = cur->_left;
							delete cur;
							return true;
						}
						else
						{
							if (parent->_left == cur)
							{
								parent->_left = cur->_left;
							}
							else
							{
								parent->_right = cur->_left;
							}
						}
						delete cur;
					}
					else
					{
						Node* replace = cur->_right;
						Node* replacepar = cur;
						while (replace->_left)
						{
							replacepar = replace;
							replace = replace->_left;
						}
						cur->_key = replace->_key;
						if (replacepar->_left == replace)
						{
							replacepar->_left = replace->_right;
						}
						else
						{
							replacepar->_right = replace->_right;
						}
						delete replace;
					}
				}
			}
			return true;
		}
	private:
		void _inorder(Node* root)
		{
			if (root == nullptr)return;
			_inorder(root->_left);
			//多打印了value
			cout << root->_key << " " << root->_value;
			_inorder(root->_right);
		}
		Node* _root;
	};

用法一:查中文

cpp 复制代码
int main()
{
	gal::BSTree<string, string> tree;
	tree.insert("love","喜欢");
	tree.insert("genshin", "原神");
	tree.insert("rewrite", "罚抄");
	tree.insert("summer pocker", "夏日口袋");
	string str;
	while (cin >> str)
	{
		auto e = tree.find(str);
		cout << str << "->" << e->_value << " ";
	}
	return 0;
}

2.统计单词出现次数

核心在于明白查的是string,计算用的是int,用tree中的结点来说明是否存在这个结点。

cpp 复制代码
int main()
{
	string arr[] = { "love","love","love","gal","gal","love","re","re"};
	gal::BSTree<string, int> tree;
	for (auto& e : arr)
	{
		auto x = tree.find(e);
		if (x == nullptr)
		{
			tree.insert(e, 1);
		}
		else x->_value++;
	}
	//cout << tree.find("love")->_value << endl;
	return 0;
}

8.set容器就是key类型的,map容其就是key/value类型的

9.析构和拷贝构造,与=重载

析构:

cpp 复制代码
~BSTree()
{
	destory(_root);
	_root = nullptr;
}
private:
	//析构由于要递归,因此还是要套壳
	void destory(Node* _root)
	{
		if (_root == nullptr)return;
		destory(_root->_left);
		destory(_root->_right);
		delete _root;
	}

拷贝构造:

cpp 复制代码
BSTree(const BSTree<K>& x)
{
	_root = create(x._root);
}
private:
	//也可以在同一个函数完成,这里无所谓
	Node* create(Node* root)
	{
		//只能采用前序递归或层序遍历,不然树的结构会被破坏
		if (root == nullptr)return nullptr;
		Node* newnode = new Node(root->_key);
		newnode->_left = create(root->_left);
		newnode->_right = create(root->_right);
		return newnode;
	}

=重载

cpp 复制代码
BSTree& operator=(BSTree<K>& tree)
{
    BSTree tmp = tree;
    std::swap(_root,tmp._root);
    return *this;
}

发现一件事,当=重载的形参要调用拷贝构造时,编译器直接优化成去调拷贝构造了,因此只有像上面用引用然后设形参才能调到=重载中。

相关推荐
梵尔纳多2 小时前
视角的移动以及模型的平移,旋转,缩放
c++·图形渲染·opengl
gis分享者2 小时前
华为OD面试-Java、C++、Pyhton等多语言实现-目录
java·c++·华为od·面试·目录·od·机试
kyle~2 小时前
C++--- dlsym 调用封装好的算法动态库的核心工具 <dlfcn.h>
开发语言·c++·算法
Irissgwe2 小时前
线程概念与控制
linux·开发语言·c++·线程
m0_716765232 小时前
C++提高编程--STL初识、string容器详解
java·开发语言·c++·经验分享·学习·青少年编程·visual studio
楼田莉子2 小时前
高并发内存池项目:内存池性能分析及其优化
开发语言·c++·后端·学习
wapicn993 小时前
智能识别技术在生活服务领域的落地应用与前景展望
java·c++·人工智能·python·php
2201_758642643 小时前
自定义内存检测工具
开发语言·c++·算法
fpcc3 小时前
C++编程实践—操作系统调优和内核旁支
开发语言·c++