C++(进阶) 第3章 二叉搜索树

C++(进阶) 第3章 二叉搜索树



前言

之前在数据结构篇简单的介绍了二叉搜索树,本篇博客会详细的介绍


一、什么是二叉搜索树?

⼆叉搜索树的概念

⼆叉搜索树⼜称⼆叉排序树,它或者是⼀棵空树,或者是具有以下性质的⼆叉树:
• 若它的左⼦树不为空,则左⼦树上所有结点的值都⼩于等于根结点的值
• 若它的右⼦树不为空,则右⼦树上所有结点的值都⼤于等于根结点的值
• 它的左右⼦树也分别为⼆叉搜索树

在这里插入图片描述

下面就是一颗比较经典的二叉搜索树
在这里插入图片描述

搜索二叉树也叫排序二叉树,因为它走中序遍历的时候刚好就是有序的


二、搜索二叉树的实现

0.搜索二叉树的时间复杂度

搜索二叉树的做好情况下时间复杂度就是log N,但是会出现极端情况
在这里插入图片描述
所以时间复杂度就是N
但是后面会有AVL树,红黑树,可以优化差不多 log N


1.基本框架

#include<bits/stdc++.h>
using namespace std;
template<class K>

struct BSTreeNode
{
	K _key;
	BSTreeNode<K>* _left;
	BSTreeNode<K>* _right;
	BSTreeNode(const K& key)
		:_key(key)
		,_left(nullptr)
		,_right(nullptr)
	{

	}
};
template<class K>
class BSTree
{
	typedef BSTreeNode<K> Node;
public:
private:
	Node* _root = nullptr;
};

2.Find

普通的二叉树其实没有什么意义,如果只是普通的二叉树它存东西的效率其实还不如链表,所以在原来的二叉树基础上添加了一些规则,左孩子比根小,右孩子比根大

假如就用下面的例子,现在我要在这颗树里面搜索看看有没有16这个数字

在这里插入图片描述
如果我们查找的值比根大那么就可以直接往右走,比根小只需要往左走就可以了那么最多只需要找这棵树的高度次就可以了

如果找到null了那么就说明找不到

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;
}

3.Insert

注意:这里有一个规定,树里面的数据是不允许重复的,也,假如树里面已经有了10,那么这里就不允许再次插入,所以这里insert返回值用bool

bool Insert(const K& key)
{
	if (_root == nullptr)
	{
		_root = new Node(key);
		return true;
	}
	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
		{
			return false;
		}
	}

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

	}
	return true;


}

4.InOder

这里我用的是子函数的形式因为如果不这样的话,外面调用就要传入参数(_root)但是这里我_root是保护调用不了,

	void InOder()
	{
		_InOder(_root);
	}
private:
	void _InOder(Node* root)
	{
		if (root == nullptr)
		{
			return;
		}

		_InOder(root->_left);
		cout << root->_key << ' ';
		_InOder(root->_right);

	}

写完中序遍历就可以看下程序有没有写错了
在这里插入图片描述
这里可以看到输出的是有序的,就说明这里是没有写错的

到这里的完整代码就长这样

#pragma once

#include<bits/stdc++.h>
using namespace std;
template<class K>

struct BSTreeNode
{
	K _key;
	BSTreeNode<K>* _left;
	BSTreeNode<K>* _right;
	BSTreeNode(const K& key)
		:_key(key)
		,_left(nullptr)
		,_right(nullptr)
	{

	}
};

template<class K>
class BSTree
{
	typedef BSTreeNode<K> Node;
public:
	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;
	}
	bool Insert(const K& key)
	{
		if (_root == nullptr)
		{
			_root = new Node(key);
			return true;
		}
		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
			{
				return false;
			}
		}

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

		}
		return true;


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

		InOder(root->_left);
		cout << root->_key << ' ';
		InOder(root->_right);



	}
	void InOder()
	{
		_InOder(_root);
	}
private:
	void _InOder(Node* root)
	{
		if (root == nullptr)
		{
			return;
		}

		_InOder(root->_left);
		cout << root->_key << ' ';
		_InOder(root->_right);

	}
private:
	Node* _root = nullptr;
};
#define _CRT_SECURE_NO_WARNINGS 
#include"bstree.h"

int main()
{
    int arr[] = { 8,3,1,10,6,4,7,14,13 };
    BSTree<int> t;
    for (auto e : arr)
    {
        t.Insert(e);
    }
    t.InOder();

    return 0;
}


5.Erase

这里删除的话就比较复杂了,需要先找到要删的那个节点,然后删除,但是删除的那个节点可能会还有孩子并且删除了以后搜索二叉树不能乱,大致可以分成以下三种情况

  1. 没有孩子
  2. 只要一个孩子
  3. 有俩个孩子

在这里插入图片描述
假如这里我要删除3,那么我需要找到一个人替代3的位置,这个时候左子树最右边的节点,或者右子树最左边的节点都可以,所以上面的1,2种情况可以归类成一种情况,因为最左边或者最左边的一定是只有一个孩子或者没有孩子的情况

假如上面我要删掉3,那么我去找右子树的最左边的值也就是4,那么3替换成4,然后下面的4就可以直接删掉了

bool Erase(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 (parent == nullptr)
				{
					_root = cur->_right;
				}
				else
				{
					if (parent->_left == cur)
					{
						parent->_left = cur->_right;
					}
					else
					{
						parent->_right = cur->_right;
					}
				}
				
				delete cur;
				return true;
			}
			else if(cur->_right == nullptr)
			{
				if (parent == nullptr)
				{
					_root = cur->_left;
				}
				else
				{
					if (parent->_left == cur)
					{
						parent->_left = cur->_left;
					}
					else
					{
						parent->_right = cur->_left;
					}
				}
				
				delete cur;
				return true;
			}
			else
			{
				Node* RightMin = cur->_right;
				Node* RightMinP = cur;
				while (RightMin ->_left)
				{
					RightMinP = RightMin;
					RightMin = RightMin->_left;
				}

				cur->_key = RightMin->_key;
				if (RightMinP->_left == RightMin )
				{
					RightMinP->_left = RightMin->_right;
				}
				else
				{
					RightMinP->_right = RightMin->_right;
				}
				
				delete RightMin;
				return true;
			}
		}
	}
	return false;
}

在这里插入图片描述


三 、完整代码

#pragma once
#include<bits/stdc++.h>
using namespace std;

template<class K>
struct BSTreeNode
{
	K _key;
	BSTreeNode<K>* _left;
	BSTreeNode<K>* _right;
	BSTreeNode(const K& key)
		:_key(key)
		,_left(nullptr)
		,_right(nullptr)
	{

	}

};



template<class K> 
class BSTree
{
	typedef BSTreeNode<K> Node;

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

	}
	void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}

	bool Erase(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 (parent == nullptr)
					{
						_root = cur->_right;
					}
					else
					{
						if (parent->_left == cur)
						{
							parent->_left = cur->_right;
						}
						else
						{
							parent->_right = cur->_right;
						}
					}
					
					delete cur;
					return true;
				}
				else if(cur->_right == nullptr)
				{
					if (parent == nullptr)
					{
						_root = cur->_left;
					}
					else
					{
						if (parent->_left == cur)
						{
							parent->_left = cur->_left;
						}
						else
						{
							parent->_right = cur->_left;
						}
					}
					
					delete cur;
					return true;
				}
				else
				{
					Node* RightMin = cur->_right;
					Node* RightMinP = cur;
					while (RightMin ->_left)
					{
						RightMinP = RightMin;
						RightMin = RightMin->_left;
					}

					cur->_key = RightMin->_key;
					if (RightMinP->_left == RightMin )
					{
						RightMinP->_left = RightMin->_right;
					}
					else
					{
						RightMinP->_right = RightMin->_right;
					}
					
					delete RightMin;
					return true;
				}
			}
		}
		return false;
	}
private:
	void _InOrder(Node* root)
	{
		if (root == nullptr)
		{
			return;
		}

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

private:
	
	Node* _root = nullptr;
};

#define _CRT_SECURE_NO_WARNINGS 
#include"BStree.h"
using namespace std;
int main()
{
    int arr[] = { 8,3,1,10,6,4,7,14,13 };
    BSTree<int> t;
    for (auto e : arr)
    {
        t.Insert(e);
    }
    t.Insert(16);
    t.Insert(4);
    t.InOrder();

    for (auto e : arr)
    {
        t.Erase(e);
        t.InOrder();
    }
    return 0;
}

总结

删除有点复杂还是要花时间去理解

参考资源链接:[算法竞赛入门经典授课教案第6数据结构基础.doc](https://wenku.youkuaiyun.com/doc/3xs4vmg2d4?utm_source=wenku_answer2doc_content)C++中实现一个基于二叉搜索树的数据结构,需要掌握二叉树的性质及其遍历方式。二叉搜索树(BST)是一种特殊的二叉树,它能够提供对数时间复杂度内的查找和插入操作。二叉搜索树的每个节点都包含一个键值对,其中左子树的所有节点的键值都小于该节点的键值,右子树的所有节点的键值都大于该节点的键值。 以下是C++代码示例,展示了如何实现一个基本的二叉搜索树及其查找和插入功能: ```cpp struct TreeNode { int val; TreeNode *left; TreeNode *right; TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} }; class BST { private: TreeNode* root; TreeNode* insert(TreeNode* node, int value) { if (node == nullptr) { return new TreeNode(value); } if (value < node->val) { node->left = insert(node->left, value); } else if (value > node->val) { node->right = insert(node->right, value); } return node; } TreeNode* search(TreeNode* node, int value) { if (node == nullptr || node->val == value) { return node; } if (value < node->val) { return search(node->left, value); } else { return search(node->right, value); } } public: BST() : root(nullptr) {} void insert(int value) { root = insert(root, value); } TreeNode* search(int value) { return search(root, value); } }; ``` 在这个示例中,我们首先定义了二叉搜索树的节点结构TreeNode,然后在BST类中实现了插入和查找的方法。插入方法通过递归遍历树来定位插入位置,并创建新节点。查找方法同样通过递归来查找给定的值。 为了更深入地理解这些概念以及如何在实际项目中应用它们,可以参考《算法竞赛入门经典授课教案第6数据结构基础.doc》。这份教案不仅涵盖了基本的数据结构和算法概念,还提供了丰富的示例和问题,适合那些希望在数据结构和算法领域进阶的学生和开发者。通过学习这份资料,你将能够更好地掌握二叉搜索树的实现细节,并将其应用到更复杂的项目中。 参考资源链接:[算法竞赛入门经典授课教案第6数据结构基础.doc](https://wenku.youkuaiyun.com/doc/3xs4vmg2d4?utm_source=wenku_answer2doc_content)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值