C++实现二叉搜索树(模型)

目录

1.二叉搜索树的概念

2.二叉搜索树的实现

2.1总体代码预览

2.2各个函数实现原理

链表结构体

二叉搜索树的成员变量

二叉搜索树的插入

二叉搜索树的查找

二叉搜索树的遍历

二叉搜索树的删除


1.二叉搜索树的概念

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

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

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

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

2.二叉搜索树的实现

由上面的定义,我们知道二叉搜索树的左孩子是小于父亲的,而右孩子是大于父亲的。由这个规律我们就可以实现一下这个二叉搜索树。

2.1总体代码预览

template<class k>
	struct BSTreeNode
	{
		BSTreeNode<k>* _left;
		BSTreeNode<k>* _right;
		k _key;

		BSTreeNode(const k& key)
			:_left(nullptr)
			,_right(nullptr)
			,_key(key)
		{}

	};


	template<class K>
	class BST
	{
		typedef BSTreeNode<K> Node;
	public:
		bool Insert(const K& key)
		{
			if (_root == nullptr)
			{
				_root = new Node(key);
				return true;
			}
			Node* parent = nullptr;
			Node* cur = _root;
			
			while (cur)
			{
				if (key > cur->_key)
				{
					parent = cur;
					cur = cur->_right;
				}
				else if (key < cur->_key)
				{
					parent = cur;
					cur = cur->_left;
				}
				else
				{
					return false;
				}
			}
			cur = new Node(key);
			if (key < parent->_key)
			{
				parent->_left = cur;
			}
			else
			{
				parent->_right = cur;
			}
			return true;
		}
		bool Find(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 true;
				}
			}
			return false;
		}
		bool Erase(const K& key)
		{
			Node* parent = nullptr;
			Node* cur = _root;

			while (cur)
			{
				if (key > cur->_key)
				{
					parent = cur;
					cur = cur->_right;
				}
				else if (key < cur->_key)
				{
					parent = cur;
					cur = cur->_left;
				}
				else
				{
					//删除
					if (cur->_left == nullptr)
					{
						if (cur == _root)
						{
							_root = cur->_right;
						}
						else
						{
							if (parent->_left == cur)
							{
								parent->_left = cur->_right;
							}
							else if (parent->_right == cur)
							{
								parent->_right = cur->_right;
							}
						}
						delete cur;
					}
					else if (cur->_right == nullptr)
					{
						if (cur == _root)
						{
							_root = cur->_left;
						}
						else
						{
							if (parent->_left == cur)
							{
								parent->_left = cur->_left;
							}
							else if (parent->_right == cur)
							{
								parent->_right = cur->_left;
							}
						}
						delete cur;
					}
					//cur左右都不为空
					else
					{
						Node* rightminparent = cur;
						Node* rightmin = cur->_right;
						while (rightmin->_left)
						{
							rightminparent = rightmin;
							rightmin = rightmin->_left;
						}
						swap(rightmin->_key, cur->_key);
						if(rightminparent->_left== rightmin)
							rightminparent->_left = rightmin->_right;
						else
							rightminparent->_right = rightmin->_right;
						delete rightmin;
					}
					return true;
				}
			}
			return false;
		}
		
		void InOrder()
		{
			_InOrder(_root);
		}
	private:
		void _InOrder(Node* root)
		{
			if (root == nullptr)
			{
				return;
			}

			_InOrder(root->_left);
			cout << root->_key << endl;
			_InOrder(root->_right);

		}
		Node* _root=nullptr;
	};

2.2各个函数实现原理

链表结构体

template<class k>
	struct BSTreeNode
	{
		BSTreeNode<k>* _left;
		BSTreeNode<k>* _right;
		k _key;

		BSTreeNode(const k& key)
			:_left(nullptr)
			,_right(nullptr)
			,_key(key)
		{}

	};

既然是我们的二叉树,那么链表的部分肯定是不能少的吧,这个链表里面,我们来定义左右孩子的指针,还有用来进行标识的key值,接下来就可以写一下它的构造函数,这个节点一开始肯定是没有左右孩子的,所以我们给它们赋上空指针,而key就是我们传的key。

二叉搜索树的成员变量

Node* _root=nullptr;

二叉搜索树的成员变量就很简单了,我们直接定义一个结构体指针的变量的就可以了。Node类型是我们重命名链表名后的名字。

二叉搜索树的插入

bool Insert(const K& key)
		{
			if (_root == nullptr)
			{
				_root = new Node(key);
				return true;
			}
			Node* parent = nullptr;
			Node* cur = _root;
			
			while (cur)
			{
				if (key > cur->_key)
				{
					parent = cur;
					cur = cur->_right;
				}
				else if (key < cur->_key)
				{
					parent = cur;
					cur = cur->_left;
				}
				else
				{
					return false;
				}
			}
			cur = new Node(key);
			if (key < parent->_key)
			{
				parent->_left = cur;
			}
			else
			{
				parent->_right = cur;
			}
			return true;
		}

插入的逻辑其实非常简单,如果这棵树是一棵空树,那么我们直接创个根节点就可以了,不需要后续的操作,倘若不是空树,那我们就进行接下来的操作,我们先定义一个父亲节点parent和当前节点cur,然后按照我们的性质,key比cur中的大我们就往右,小的话我们就往左走,如果这个key在树中存在,我们就放回false,因为set是有去重功能的,就是说一棵树里不能存在两个一样的值。找到了适合的位置我们就出循环,然后开始创建节点,进行连接就可以了。

二叉搜索树的查找

bool Find(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 true;
				}
			}
			return false;
		}

查找的功能就更简单了,我们只需要按照性质,左边比根小右边比根大就可以了。

二叉搜索树的遍历

void _InOrder(Node* root)
		{
			if (root == nullptr)
			{
				return;
			}

			_InOrder(root->_left);
			cout << root->_key << endl;
			_InOrder(root->_right);

		}

我们的二叉搜索树按照中序遍历是刚好是有序的,这个是我们二叉搜索树的意义之一,所以我们这里也是实现中序遍历。

中序遍历的思想就简单的多了,使用递归对它的各个节点进行遍历就可以了。

但是我们调用中序遍历肯定不想别人传个参数,所以我们再进行封装就可以了。

二叉搜索树的删除

bool Erase(const K& key)
		{
			Node* parent = nullptr;
			Node* cur = _root;

			while (cur)
			{
				if (key > cur->_key)
				{
					parent = cur;
					cur = cur->_right;
				}
				else if (key < cur->_key)
				{
					parent = cur;
					cur = cur->_left;
				}
				else
				{
					//删除
					if (cur->_left == nullptr)
					{
						if (cur == _root)
						{
							_root = cur->_right;
						}
						else
						{
							if (parent->_left == cur)
							{
								parent->_left = cur->_right;
							}
							else if (parent->_right == cur)
							{
								parent->_right = cur->_right;
							}
						}
						delete cur;
					}
					else if (cur->_right == nullptr)
					{
						if (cur == _root)
						{
							_root = cur->_left;
						}
						else
						{
							if (parent->_left == cur)
							{
								parent->_left = cur->_left;
							}
							else if (parent->_right == cur)
							{
								parent->_right = cur->_left;
							}
						}
						delete cur;
					}
					//cur左右都不为空
					else
					{
						Node* rightminparent = cur;
						Node* rightmin = cur->_right;
						while (rightmin->_left)
						{
							rightminparent = rightmin;
							rightmin = rightmin->_left;
						}
						swap(rightmin->_key, cur->_key);
						if(rightminparent->_left== rightmin)
							rightminparent->_left = rightmin->_right;
						else
							rightminparent->_right = rightmin->_right;
						delete rightmin;
					}
					return true;
				}
			}
			return false;
		}

二叉搜索树的删除才是我们的真正难点

我们最开始的操作还是一样的,我们要找那个被删除的节点,找到后就开始我们的操作了。我们有三种大情况:

第一种是要删除的节点左孩子为空的时候:

如图所示,我们的cur只有一边是有值的,这个时候我们先判断这个cur是父亲的左孩子还是右孩子,然后再把链表指向修改就行了,但是不要忘记了,我们的cur有可能是根节点,这个时候我们的父亲节点是空,这个时候是没法去给parent修改指向的,所以我们要单独的写一个判断。

第二种自然就是右孩子为空了,思路跟左孩子是一模一样的。

第三种就是我们的cur的左右孩子都不为空,这里的做法右两种,第一种是找cur的左子树的最大值,替换cur,然后再把cur给删除,第二种是找右子树的最小值,然后进行同样的操作,这两种做法都可以使其保持二叉搜索树该有的性质。这里我们选择第二种。

可以看到外面用循环来找到右子树的最小值,找到后交换值,我们没有直接让rightminparent的左节点直接接向rightmin的右子树是因为可能有这种情况

假如我们删的是8,这个时候右孩子是没有左节点的,这个时候我们就不可能rightminparent的左节点接向rightmin的右子树,所以我们进行了这个分类讨论,这个细节也是很难想到的。

至此我们的二叉搜索树的实现原理就讲完了,感谢大家的收看!

  • 29
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值