C++模拟实现二叉搜索树

目录

1.二叉搜索树的概念

2.二叉搜索树的性能分析 

3.二叉搜索树的结构和中序遍历

3.1二叉搜索树中节点的结构

3.2二叉搜索树的结构 

3.3中序遍历 

4.二叉搜索树的插入

5.二叉搜索树的查找 

6.二叉树搜索树的删除

7. 二叉搜索树的默认成员函数

8.参考代码 

9.二叉搜索树key和key/value使用场景

9.1key搜索场景

9.2key/value搜索场景


1.二叉搜索树的概念

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

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

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

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

        (4)二叉搜索树可以支持插入相等的值,也可以不支持插入相等的值,具体看使用场景定义。如下图,左边是不支持插入相等的值,右边支持插入相等的值。

2.二叉搜索树的性能分析 

        最优情况下,二叉搜索树为完全二叉树(或者接近完全二叉树),其高度为:O(log_{2}N)。最差情况下,二叉树退化为单支树(或者类似单支),其高度为:O(N)。所有综合而言二叉搜索树增删查的时间复杂度为:O(N)

        另外需要说明的是,二分查找也可以实现O(log_{}N)级别的查找效率,但是二分查找有两个大的缺陷:
        (1)需要存储在支持下标随机访问的结构中,并且有序。

        (2)插入和删除数据效率很低,因为存储在下标随机访问的结构(例如数组)中,插入和删除数据一般需要挪动数据。

        所以二叉搜索树就能体现出价值所在,即通过二叉树中序遍历就能有序的访问数据,并且插入和删除都是节点的操作,不需要挪动数据。二叉搜索树的查找效率为O(N),但是这个缺陷在后续的平衡二叉树AVL树已经红黑树中可以解决,将效率提高到O(log_{}N)

3.二叉搜索树的结构和中序遍历

3.1二叉搜索树中节点的结构

        _key为节点中的值,_left和_right分别是指向左节点和右节点的指针。

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

3.2二叉搜索树的结构 

        二叉树的结构中只用一个_root成员。 

template<class K>
class BSTree
{
	//typedef BSTNode<K> Node;
	using Node = BSTNode<K>;
private:
    Node* _root = nullptr;
};

3.3中序遍历 

       因为二叉搜索树左边比根节点小,右边比根节点大,中序遍历的顺序是:左子树,根节点,右子树,所以通过中序遍历遍历二叉搜索树,天然就是有序的。

        中序遍历需要传一个根节点,在类外面不能直接访问私有成员,所以这里的中序遍历实现在private里面,然后外面再用InOrder()函数进行封装,这里在类外面直接调用InOrder()函数就可以实习中序遍历了,不需要传入根节点。

template<class K>
class BSTree
{
	//typedef BSTNode<K> Node;
	using Node = BSTNode<K>;
public:
	void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}
private:
	void _InOrder(Node* root)
	{
		if (root == nullptr)
		{
			return;
		}
		_InOrder(root->_left);
		cout << root->_key << " ";
		_InOrder(root->_right);
	}

    Node* _root = nullptr;
};

4.二叉搜索树的插入

        插入的具体过程如下:
        (1)树为空,则直接新增节点,赋值给root。

        (2)树不为空,按二叉搜索树的性质,插入值比当前节点大往右走,插入值比当前节点小往左走,找到空位置插入新节点。

        (3)如果支持插入相等的值,插入值跟当前节点相等的值可以往右走,也可以往左走,找到空位置插入新节点。(要注意保持逻辑的一致性,插入相等的值不要一会往右走,一会往左走)

        这里以插入下列这棵树为例子:

        1.不允许相同的值插入 

//不允许相同的值插入
		bool Insert(const K& key)    //K是插入的值的类型
		{
			if (_root == nullptr)    //当为空树时,直接赋值给root
			{
				_root = new Node(key);
				return true;
			}

			Node* parent = nullptr;    //parent指向cur的父亲节点
			Node* cur = _root;    

			while (cur)
			{
				if (cur->_key < key)    //当插入值key大于cur节点的值,往右走
				{
					parent = cur;
					cur = cur->_right;
				}
				else if (cur->_key > key)    //当插入值key小于cur节点的值,往左走
				{
					parent = cur;
					cur = cur->_left;
				}
				else    //插入相等的值时,插入失败
				{
					return false;
				}
			}
            
            //此时找到空位置了,cur == nullptr, parent指向cur的父亲节点,进行插入
			cur = new Node(key);
			if (parent->_key < key)       //如果插入值大于parent节点的值,插入在parent的右边
			{
				parent->_right = cur;
			}
			else    //如果插入值
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值