【C++】二叉搜索树(代码实现)

二叉搜索树

二叉搜索树又叫做 二叉排序树或者排序二叉树

二叉搜索树概念:
二叉搜索树又称为二叉排序树,它或者是一颗空树,或者是既有以下性质的二叉树:

  1. 若它的左子树不为空,则左子树的所有节点的值都小于根节点的值
  2. 若它的右子树不为空,则右子树的所有节点的值都大于根节点的值
  3. 它的左右子树也分别为二叉搜索树

二叉搜索树的特点:
每一个根节点的左孩子都比根节点的值小,每一个根节点的右孩子都比根节点的值大。通过二叉搜索树的中序遍历可以看出来,二叉搜索树的中序遍历就是节点值的升序排列。

:普通的二叉树进行查找没有意义,二叉树的增删查改是没有意义的。所以在数据结构阶段并没有学二叉树的增删改查

  1. 二叉搜索树的查找

若根节点不为空
如果根节点key == 查找key 返回true
如果根节点key > 查找key 在其左子树查找
如果根节点key < 查找key 在其右子树查找
否则 返回false

  1. 二叉搜索树的插入

a. 树为空,则直接插入
b. 树不为空,按二叉树的性质查找插入位置,插入新的节点

  1. 二叉搜索树树的删除

首先查找元素是否在二叉搜索树中,如果不存在,则返回,否则要删除的结点可能分下面四种情况:
a. 要删除的结点无孩子节点
b. 要删除的结点只有左孩子结点
c. 要删除的结点只有右孩子结点
d.要删除的结点有左、右孩子结点
还可以在合并一下: 实际情况a可以与情况b,c看成一种情况 真正删除的过程如下

  1. 删除该节点且使被删除的节点的双亲节点指向被删除节点的左孩子结点
  2. 删除该节点且使被删除的节点的双亲节点指向被删除节点的右孩子结点
  3. 在它的右子树中寻找中序下的第一个节点(关键码最小),用它的值填补到被删除节点中,再来处理该节点的删除问题。

只有一个孩子或者没有孩子在代码里面可以合成一种情况处理

在这里插入图片描述

二搜索树的实现

BinarySearchTree.h中实现

#include <iostream>
using namespace std;

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

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

template <class K>
class BSTree
{
	typedef BSTreeNode<K> Node;
public:
	BSTree()
		:_root(nullptr)
	{

	}
	~BSTree()
	{

	}
	//bool Insert(const K& key) // push用的都是线性结构, 一般都用insert
	//{
	//	if (_root == nullptr)
	//	{
	//		_root = new Node(key);  // new失败了,没有空间了, 就会抛一个异常。
	//		return true;
	//	}
	//	// 不用递归
	//	Node* cur = _root;
	//	// cur只是一个局部变量,把节点给了这个局部变量,cur指向了新的值,
	//	//但是你的目标是让上一个节点的左右指针指向它
	//	//类似于链表的尾插 定义一个prev跟着, cur走之前,要将cur的值给向prev链接起来
	//	while (cur)
	//	{
	//		if (cur->_key < key)
	//		{
	//			cur = cur->_right;
	//		}
	//		else if (cur->_key > key)
	//		{
	//			cur = cur->_left;
	//		}
	//		else
	//			return false;      //在搜索二叉树中,不允许出现相同的数据。现在 我们默认每一个元素的值都不相同
	//	}
	//	cur = new Node(key);
	//	return true; 
	//}

	bool Insert(const K& key) // push用的都是线性结构, 一般都用insert
	{
		if (_root == nullptr)
		{
			_root = new Node(key);  // new失败了,没有空间了, 就会抛一个异常。
			return true;
		}
		// 不用递归
		Node* cur = _root;
		// cur只是一个局部变量,把节点给了这个局部变量,cur指向了新的值,
		//但是你的目标是让上一个节点的左右指针指向它
		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
				return false;      //在搜索二叉树中,不允许出现相同的数据。现在 我们默认每一个元素的值都不相同
		}
		cur = new Node(key);
		// 走到这里 parent不存在为空 的可能
		if (parent->_key < key)
		{
			parent->_right = cur;
		}
		if (parent->_key > key)
		{
			parent->_left = cur;
		}
		return true;
	}
	void InOrder() //中序。 中序得传进来一个根节点,而这个根节点(this指针指向的东西)是隐含传进来的
		//所以只能再写一个, 根是私有的,只能到类里面去拿
	{
		_InOrder(_root);
		cout << endl;
	}

	void _InOrder(Node* root)
	{
		if (root == nullptr)
			return;
		_InOrder(root->_left);
		cout << root->_key << " ";
		_InOrder(root->_right);
	}
	Node* Find(const K& key)
	{
		/*if (_root == nullptr)
			return _root;*/ 
		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 Erase(const K& key) //不能Find(),指针就是野指针, 还得找到它的父亲。有可能删除遇到很多问题
	{
		//不能直接进行删除。

		//叶节点也比较好删 只有一个孩子的也比较好删
		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_key < key)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (cur->_key > key)
			{
				parent = cur;
				cur = cur->_left;
			}
			else // 找到了 要删除
			{
				// 1. 作为空
				// 2. 右为空
				// 3. 左右都不为空
				Node* del = cur; //要删除的节点 要删除的节点,在情况1,2中都是cur
				if (cur->_left == nullptr) // 左为空
				{
					if (parent->_left == cur) //如果删除的cur是等于父亲的左,就让父亲的左指针指向cur的右
					{
						parent->_left = cur->_right;
					}
					else //如果删除的是cur等于父亲的右,就让父亲的有指针指向cur
					{
						parent->_right = cur->_right; 
					}
				}
				else if (cur->_right == nullptr) //右为空 也同理
				{
					if (parent->_left == cur)
					{
						parent->_left = cur->_left;
					}
					else
					{
						parent->_right = cur->_left;
					}
				}
				else //左右都不为空 找替代节点去取代它
				{
					Node* lessParent = cur; //不能给空 循环有可能没进去
					Node* lessRight = cur->_right;  //找右边的最左节点
					while (lessRight->_left)
					{
						lessParent = lessRight;
						lessRight = lessRight->_left;
					}
					//找到最左节点 替代
					cur->_key = lessRight->_key; 
					// 要删除的节点一定是左为空
					del = lessRight;
					//左为空 让父亲只想我的右
					if (lessParent->_left == lessRight) //如果父亲的左 等于我要删的节点
					{
						lessParent->_left = lessRight->_right; //lessRight是右边的最左节点,所以要指向lessRight的右
					}
					else
					{
						lessParent->_right = lessRight->_right;//右
					}
				}
				delete del;
				return true;
			}

		}
		//没找到
		return false;
	}

	//递归版本的实现
	Node* _FindR(Node* root, const K& key)
	{
		if (root == nullptr)
			return nullptr;  // 递归中nullptr 不代表整体为空,代表树节点为空

		if (root->_key == key)
		{
			return root;
		}
		else if (root->_key < key)
		{
			return _FindR(root->_right, key);
		}
		else
		{
			return _FindR(root->_left , key);
		}
	}
	Node* FindR(const K& key)
	{
		return _FindR(_root, key);
	}

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

		return true;
	}
	bool InsertR(const K& key)
	{
		return _InsertR(Node* root, const K& key);
	}

	bool EraseR(const K& key)
	{
		//..
		return false;
	}
private:

	Node* _root;
};

void TestBSTree()
{
	//int a[] = { 1, 4, 6, 2, 9, 10 };
	int a[] = { 5, 3, 4, 1, 7, 8, 3, 6, 0, 9 };
	BSTree<int> t;
	for (auto e: a)
	{
		t.Insert(e);
	}
	t.InOrder();
	t.Find(4);
	t.Erase(2);
	t.Erase(8);
	t.Erase(1);
	t.Erase(7);
	t.Erase(5);
	
	t.InOrder();

	for (auto e : a)
	{
		t.Erase(e);
	}
	cout << endl;

}
发布了55 篇原创文章 · 获赞 12 · 访问量 5237

猜你喜欢

转载自blog.csdn.net/weixin_43939593/article/details/103697391