【数据结构】二叉搜索树

一、二叉搜索树的概念

⼆叉搜索树 ⼜称**⼆叉排序树**,它或者是⼀棵空树,或者是具有以下性质的⼆叉树:

①左子树不为空,则左子树节点值小于根节点值。
②右子树不为空,则右子树节点值大于根节点值。
③每个子树均为二叉搜索树。
中序遍历的结果是有序的

一般二叉搜索树不允许插入已有的值,但也有方法可以实现。

后面以二叉树为底层的容器:map,set,multimap,multiset,其中前两个不支持插入相等的值,而后两者便支持。

二、二叉搜索树的实现

节点实现

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

};

1.插入

为了保持其"左小右大"的特性,核心思路就是从根节点开始,寻找到一个合适的空位置进行插入。

如果树为空:直接将新节点作为根节点插入即可。

如果树不为空:从根节点开始,比较待插入节点和当前节点的值,小就插左边,大就插右边,循环找到一个合适的空位进行插入。

注意记录父亲节点的位置

代码实现:

cpp 复制代码
bool Insert(const K& key)
{
	if (_root == nullptr)
	{
		_root = new Node(key);
		return true;
	}

	Node* cur = _root;
	Node* parent = nullptr;

	while (cur)
	{
		if (key < cur->_key)
		{
			parent = cur;
			cur = cur->_left;
		}
		else if (key > cur->_key)
		{
			parent = cur;
			cur = cur->_right;
		}
		else
		{
			return false;
		}

	}

	cur = new Node(key);
	if (parent->_key > key)
	{
		parent->_left = cur;
	}
	else
	{
		parent->_right = cur;
	}

	return true;
}

2.查找

1.从根开始比较,查找的值比根的值大则往右边走查找,反之则往左边走查找。

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

3.如果不支持插入相等的值,找到x即可返回

4.如果支持插入相等的值,一般要求查找中序的第一个x。

代码实现:

cpp 复制代码
	bool Find(const K& key)
	{

			Node* cur = _root;
			while (cur)
			{
				if (key < cur->_key)
				{
					cur = cur->_left;
				}
				else if (key > cur->_key)
				{
					cur = cur->_right;
				}
				else
				{
					return true;
				}

			}
			return false;

	}

3.删除(难点)

删除是其中实现难度较大的一点,我们要先查找,并且在删除后要保持二叉树的性质。

1)被删节点的左右孩子均为空

这种情况可直接删除,具体方法:让父亲结点指向空即可,若删除结点在左,那父亲左指向空,反之,父亲右指向空。

2)被删节点有一个孩子节点(左孩子/右孩子)

这种情况本质上就是让孩子节点顶替被删节点

3)被删节点存在两个孩子

这种情况采用替换法,就是用左子树的****最大结点/或者右子树的****最小结点去替换,交换后,在删除。

实现代码:

cpp 复制代码
bool Erase(const K& key)
{
	Node* cur = _root;
	Node* parent = nullptr;

	while (cur)
	{
		if (key < cur->_key)
		{
			parent = cur;
			cur = cur->_left;
		}
		else if (key > cur->_key)
		{
			parent = cur;
			cur = cur->_right;
		}
		else //找到了要删除的值了
		{
			if (cur->_left == nullptr)
			{
				if (cur == _root)
				{
					_root = cur->_right;
				}
				else
				{
					if (cur == parent->_left)
					{
						parent->_left = cur->_right;
					}
					else
					{
						parent->_right = cur->_right;
					}
				}
				delete cur;
			}
			else if (cur->_right == nullptr)
			{
				if (cur == _root)
				{
					_root = cur->_left;

				}
				else
				{
					if (cur == parent->_left)
					{
						parent->_left = cur->_left;
					}
					else
					{
						parent->_right = cur->_left;
					}

				}
				delete cur;

			}
			else
			{
				Node* replaceParent = cur;
				Node* replace = cur->_right;
				while (replace->_left)
				{
					replaceParent = replace;
					replace = replace->_left;
				}

				swap(cur->_key, replace->_key);


				if (replaceParent->_left == replace)
					replaceParent->_left = replace->_right;
				else
					replaceParent->_right = replace->_right;

				delete replace;

			}
		
			return true;
		}

	}
	return false;

}

4.中序遍历

InOrder() (公共接口): 这是给用户调用的函数。它内部直接调用私有的 _InOrder 函数,并从根节点 _root 开始遍历。

代码实现:

cpp 复制代码
	void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}

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

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

5.拷贝构造

代码实现:

cpp 复制代码
	BSTree(const BSTree<K>& t)
	{
		_root = Copy(t._root);
	}	

    Node* Copy(Node* root)
	{
		if (root == nullptr)
			return nullptr;
		Node* newroot = new Node(root->_key);
		newroot->_left = Copy(root->_left);
		newroot->_right = Copy(root->_right);
		return newroot;
	}

6.析构函数

cpp 复制代码
    ~BSTree()
	{
		Destoy(_root);
		_root = nullptr;
	}
    void Destoy(Node* root)
	{
		if (root == nullptr)
			return;
		Destoy(root->_left);
		Destoy(root->_right);
		delete root;
	}

三、二叉搜索树的性能

所以⼆叉搜索树增删查改时间复杂度为:O(N)

如果是一般情况下效率比较高,极端情况下效率就很低了。

以后学习的AVL树和红黑树可以解决这个问题。

相关推荐
AI视觉网奇1 天前
Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr
开发语言·c++·算法
ghie90901 天前
ECG波形检查与分析系统
算法
智者知已应修善业1 天前
【输入两个数字,判断两数相乘是否等于各自逆序数相乘】2023-10-24
c语言·c++·经验分享·笔记·算法·1024程序员节
Shingmc31 天前
【Linux】进程控制
linux·服务器·算法
hefaxiang1 天前
分支循环(下)(二)
c语言·开发语言·数据结构
阿正的梦工坊1 天前
DreamGym:通过经验合成实现代理学习的可扩展化
人工智能·算法·大模型·llm
小武~1 天前
Leetcode 每日一题C 语言版 -- 45 jump game ii
c语言·算法·leetcode
行云流水6261 天前
前端树形结构实现勾选,半勾选,取消勾选。
前端·算法
laocooon5238578861 天前
一个C项目实现框架
c语言·算法
c#上位机1 天前
halcon图像增强——图像取反
图像处理·算法·c#·halcon