【数据结构】二叉搜索树

@TOC

      在之前的博客中,我们有谈过二叉树的相关知识,对二叉树进行了一个基础的认识,而在今天的博客中,我们来将二叉树中一种特殊的树————二叉搜索树。

下面是之前代码的链接【精选】二叉树基础概念和堆的实现_疏 石 兰 兮的博客-CSDN博客

        在之前的学习中,我们知道二叉树也是一种存储数据的容器,但是如果我们在使用二叉树查找数据的时候,不管是使用前序,中序,后序还是层序这些遍历方式去查找数据,都会显得十分麻烦,所以前辈们设计了一种查找数据十分方便的二叉树————二叉搜索树。

二叉树搜索树的概念

     二叉搜索树又称为二叉排序树。它可以是一个空树,如果不为空,就必须具有以下性质的二叉树:

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

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

       它的左右子树同样满足以上条件。

如图:

        根据上面的特点,我们会发现,这棵树的最左边叶子结点一定是最小的树,它的根结点就是次小值,第三小的值就是根右节点的最左边叶子结点,依次类推,最大的结点自然就是最右边的节点。

而且如果使用中序遍历,我们就会发现打出来的序列就是[ 1, 3, 4, 6, 7, 8, 10,13, 14]正好是从小到大。

1. 二叉搜索树的查找

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

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

2. 二叉搜索树的插入

插入的具体过程如下:

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

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

3.二叉树的删除

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

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

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

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

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

其中,a可以和b,c结合

二叉搜索树的实现

首先 我们要设置我们的二叉树结点的结构体


template <class T>
struct BSTNode
{
	BSTNode(const T& data = T())//是一个带有默认参数的构造函数,这里的T()调用了类型T的默认函数,用于创建一个默认值的数据对象。
		:_Lchild(nullptr)
		:_Rchild(nullptr)
		:_data(data)
	{}
    //这个函数既可以当作默认构造函数,又可以当作拷贝构造函数

	BSTNode<T>* _Lchild;
	BSTNode<T>* _Rchild;
	T _Data;
};

二叉搜索树结点的插入迭代版

//插入函数迭代版
	bool Insert(const T& data)

	{
		//如果这个树为空
		if (_root == nullptr)
		{
			Node* newnode = new Node(data);
			_root = newnode;
			return true;
		}
		Node* cur = _root;
		while (cur)
		{
			if (cur->_Data == data)
			{
				//树中已经含有该值
				cout << "该值已存在" << endl;
				return false;
			}
			else if (cur->_Data < data)
			{
				if (cur->_Rchild == nullptr)
				{
					//说明已经找到正确的位置了
					Node* newnode = new Node(data);
					cur->_Rchild = newnode;
					return true;
				}
				cur = cur->_Rchild;
			}
			else if (cur->_Data > data)
			{
				if (cur->_Lchild == nullptr)
				{
					//说明已经找到正确的位置了
					Node* newnode = new Node(data);
					cur->_Lchild = newnode;
					return true;
				}
				cur = cur->_Lchild;
			}
		}
		return false;
	}

二叉搜索树的结点删除迭代版

bool _EraseR(Node*& root, const T& data, Node* parent)
	{
		if (root == nullptr)
		{
			cout << "树为空" << endl;
			return false;
		}
		else if (root->_Data == data)
		{
			//找到之后就可以进行删除了
				//删除该结点之后必须保持该树依然是一个二叉搜索树,而这个时候就得分情况
				//这时候得分情况
				//1.假如该节点左孩子为空
			if (!root->_Lchild)
			{
				Node* node = root;
				//如果root是根结点
				if (root == _root)
				{
					root = root->_Rchild;
				}
				else
				{
					if (root == parent->_Lchild)
					{
						parent->_Lchild = root->_Rchild;
					}
					else
					{
						parent->_Rchild = root->_Rchild;
					}

				}
				delete node;
				node = nullptr;
			}
			//2. 假如cur的右子树为空
			else if (!root->_Rchild)
			{
				Node* node = root;
				if (root == _root)
				{
					root = root->_Lchild;
				}
				else
				{
					if (parent->_Lchild == root)
					{
						parent->_Lchild = root->_Lchild;
					}
					else
					{
						parent->_Rchild = root->_Lchild;
					}
				}
				delete node;
				node = nullptr;
			}
			//3.假如root的左右子树都不为空
			else if (root->_Lchild && root->_Rchild)
			{
				//要找到cur右结点的最小结点
				Node* rootRMin = root->_Rchild;
				while (rootRMin->_Lchild)
					//我们要找的是root右子树的最小值,就要让rootRMin一直朝左走,
					//当rootRMin的左子树为空时,那么rootRMin就到了最小
				{
					rootRMin = rootRMin->_Lchild;
				}
				swap(rootRMin->_Data, root->_Data);
				delete rootRMin;
				rootRMin = nullptr;
			}
			return true;
		}
		else if (root->_Data > data)
		{
			return _EraseR(root->_Lchild, data, root);
		}
		else if (root->_Data < data)
		{
			return _EraseR(root->_Rchild, data, root);
		}
		return false;
	}

二叉搜索树的结点查找迭代版

//二叉树查找迭代版
	bool Find(const T& data)
	{
		Node* cur = _root;
		while (cur)
		{
			if (cur->_Data > data)
			{
				cur = cur->_Lchild;
			}
			else if (cur->_Data < data)
			{
				cur = cur->_Rchild;
			}
			else if (cur->_Data == data)
			{
				return true;
			}
		}
		cout << "树中没有这个值" << endl;
		return false;
	}

 二叉搜索树的结点遍历迭代版(中序遍历版)

//这里选择中序遍历
	void traverse()
	{
		stack<Node*> st;
		if (_root == nullptr)
		{
			cout << "树为空" << endl;
			return;
		}
		st.push(_root);
		while (!st.empty())
		{
			Node* node = st.top();
			st.pop();
			if (node != nullptr)
			{
				if (node->_Rchild)
				{
					st.push(node->_Rchild);
				}
				st.push(node);
				st.push(nullptr);
				if (node->_Lchild)
				{
					st.push(node->_Lchild);
				}
			}
			else
			{
				node = st.top();
				st.pop();
				cout << node->_Data << " ";
			}
		}
		cout << endl;
	}

我写迭代遍历的主体思路:区分父亲结点和孩子结点,父亲结点会在和孩子结点之间添加一个nullptr,用于区分。

析构函数迭代版

//析构函数迭代版
	~BSTree()
	{
		//使用后序遍历
		//因为我们要删除左结点之后还要能找到右结点,所以得保留根节点
		stack<Node*> s;
		Node* cur = _root;
		if (cur == nullptr)
		{
			return;
		}
		s.push(cur);
		while (!s.empty())
		{
			Node* node = s.top();
			if (node != nullptr)
			{
				s.pop();
				s.push(node);
				s.push(nullptr);
				if(node->_Rchild)
				{
					s.push(node->_Rchild);
				}
				if (node->_Lchild)
				{
					s.push(node->_Lchild);
				}
			}
			else
			{
				s.pop();
				node = s.top();
				s.pop();
				delete node;
			}
		}
	}

        析构函数在写迭代版本的时候,在释放左结点之后还需要释放右结点,所以必须使用后序遍历来进行释放。

二叉搜索树的代码难点就是在迭代,递归要比迭代简单很多,所以我把主要的函数进行递归化

二叉搜索树的结点查找递归版

//二叉搜索树的查找递归版
	bool _FindR(Node* root, const T& data)
	{
		if (root == nullptr)
		{
			return false;
		}
		if (root->_Data == data)
		{
			return true;
		}
		else if (root->_Data > data)
		{
			return _FindR(root->_Lchild, data);
		}
		else if (root->_Data < data)
		{
			return _FindR(root->_Rchild, data);
		}
		return true;
	}

二叉搜索树的结点删除递归版

bool _EraseR(Node*& root, const T& data, Node* parent)
	{
		if (root == nullptr)
		{
			cout << "树为空" << endl;
			return false;
		}
		else if (root->_Data == data)
		{
			//找到之后就可以进行删除了
				//删除该结点之后必须保持该树依然是一个二叉搜索树,而这个时候就得分情况
				//这时候得分情况
				//1.假如该节点左孩子为空
			if (!root->_Lchild)
			{
				Node* node = root;
				//如果root是根结点
				if (root == _root)
				{
					root = root->_Rchild;
				}
				else
				{
					if (root == parent->_Lchild)
					{
						parent->_Lchild = root->_Rchild;
					}
					else
					{
						parent->_Rchild = root->_Rchild;
					}

				}
				delete node;
				node = nullptr;
			}
			//2. 假如cur的右子树为空
			else if (!root->_Rchild)
			{
				Node* node = root;
				if (root == _root)
				{
					root = root->_Lchild;
				}
				else
				{
					if (parent->_Lchild == root)
					{
						parent->_Lchild = root->_Lchild;
					}
					else
					{
						parent->_Rchild = root->_Lchild;
					}
				}
				delete node;
				node = nullptr;
			}
			//3.假如root的左右子树都不为空
			else if (root->_Lchild && root->_Rchild)
			{
				//要找到cur右结点的最小结点
				Node* rootRMin = root->_Rchild;
				while (rootRMin->_Lchild)
					//我们要找的是root右子树的最小值,就要让rootRMin一直朝左走,
					//当rootRMin的左子树为空时,那么rootRMin就到了最小
				{
					rootRMin = rootRMin->_Lchild;
				}
				swap(rootRMin->_Data, root->_Data);
				delete rootRMin;
				rootRMin = nullptr;
			}
			return true;
		}
		else if (root->_Data > data)
		{
			return _EraseR(root->_Lchild, data, root);
		}
		else if (root->_Data < data)
		{
			return _EraseR(root->_Rchild, data, root);
		}
		return false;
	}

二叉搜索树的结点插入递归版

//二叉搜索树的插入递归版
	bool _InsertR(Node*& root, const T& data)
	{
		if (root == nullptr)
		{
			Node* newnode = new Node(data);
			root = newnode;
			return true;
		}
		else if (root->_Data == data)
		{
			return false;
		}
		else if (root->_Data > data)
		{
			if (root->_Lchild == nullptr)
			{
				Node* newnode = new Node(data);
				root->_Lchild = newnode;
				return true;
			}
			return _InsertR(root->_Lchild, data);
		}
		else if (root->_Data < data)
		{
			if (root->_Rchild == nullptr)
			{
				Node* newnode = new Node(data);
				root->_Rchild = newnode;
				return true;
			}
			return _InsertR(root->_Rchild, data);
		}
		return false;
	}

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值