二叉搜索树

概述

这篇文章将阐述我对于搜索二叉树的理解,以及底层的模拟实现,请大家理性阅读,这是我第一次写博客,错误可能有点多,请大家多多包涵,希望对你有帮助!

基础知识

搜索二叉树的介绍

AVLTree和红黑树都是建立在搜索二叉树的基础之上的,首先我们来理解搜索二叉树,二叉搜索树又称为二叉排序树,或者是一颗空树,具有以下的性质,如果左子树的不为空,则左子树上所有的节点的值都小于根节点的值,若它的右子树不为空,则右子树上所有的节点的值都大于根节点的值,它的左右子树也都是搜索二叉树

如果按照int a[]={5,3,4,1,7,8,2,6,0,9}的方式构建搜索二叉树,得到如下的搜索二叉树

不难看出如果按照中序遍历的方式来看的话,得到就是 0,1,2,3,4,5,6,7,8,9,是一个排序好的序列。

那么搜索二叉树的意义在哪?如图所示

如果树比较平衡的话,那么搜索次数最大就是树的高度次,也就是o(logN),效率很高,但是如果是极端的情况,就会退化,如图所示

这样的话,在右单支的情况效率就退化成o(N),所以为了解决可能出现的情况,于是就有了AVLTree和红黑树的诞生!

在分析AVLTree和红黑树之前,我们先来实现一个搜索二叉树的插入和删除操作,首先二叉树的插入是十分简单的,只要满足搜索二叉树的规则就可以实现。首先给搜索二叉树定一个基本的框架

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

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

首先是实现一个BSTreeNode,这个作为节点,然后就是BSTree,这个就是作为的搜索二叉树的代码,增删查的功能都是在BSTree中实现的。

插入节点

插入数据其实是一个非常简单的过程,就是那插入的数据取依次比较即可,如果遇到空就链接,如果遇到相同的就失败(注意在搜索二叉树当中式不允许出现相同的节点的),具体代码
 

bool Insert(const K& key)
{
    if(_root==nullptr)//说明此时是一个空树,直接插入即可
    {
        _root=new Node(key);
        return true;
    }
    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
        {
            return false;//相同就返回false
        }
    }
    cur=new Node(key)
    //然后就是判断插入到哪一边
    if(parent->_key<key)
    {
        parent->_right=cur;
    }
    else
    {
        parent->_left=cur;
    }
    return true
}

搜索节点

搜索数据也是十分的简单 ,就是按照搜索树的规则,代码如下

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

  删除节点

删除数据是相比较而言就要复杂很多的,主要就是有很多的情况是需要分析的,读者可以先自己尝试构建一个二叉树然后去删除几个节点,总结一下各种可能会遇到的情况

 情况1 首先是删除节点左右子树都为空,这是最简单的情况,只需要直接删除即可                           情况2 左子树为空,但是右子树不为空                                                                                               情况3 左子树不为空,但是右子树为空                                                                                               情况4 左右子树都不为空

单独分析一下情况4,当左右子树都不为空的时候,这个时候情况比较复杂,解决办法就是转移问题,其实我们只是需要将这个值删除,而这个值的所在的节点是左右子树都存在的情况,所以我们采用交换的方式将情况4转变为之前的情况。

假设我们删除12,为了改变整棵树的结构,最好是能够找到一个值来替代12的删除,但是要满足搜索二叉树的规则,其实就是以12为根的子树的左子树的最右节点和右子树的最左节点,这个

然后只需要删除红色的节点,就转换成情况简单的情况

具体的代码实现

		bool Erase(const K& key)
		{
			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、左右都不为空,替换删除
					if (cur->_left == nullptr)
					{
						//if (parent == nullptr)
						if (cur == _root)
						{
							_root = cur->_right;
						}
						else
						{
							if (parent->_left == cur)
							{
								parent->_left = cur->_right;
							}
							else
							{
								parent->_right = cur->_right;
							}
						}

						delete cur;
					}
					else if (cur->_right == nullptr)
					{
						//if (parent == nullptr)
						if (cur == _root)
						{
							_root = cur->_left;
						}
						else
						{
							if (parent->_left == cur)
							{
								parent->_left = cur->_left;
							}
							else
							{
								parent->_right = cur->_left;
							}
						}

						delete cur;
					}
					else
					{
						// 右子树的最小节点
						Node* parent = cur;
						Node* minRight = cur->_right;
						while (minRight->_left)
						{
							parent = minRight;
							minRight = minRight->_left;
						}

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

					return true;
				}
			}

			return false;
		}

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值