[数据结构]二叉搜索树详解

目录

一、二叉搜索树的概念

二、二叉搜索树的性能分析

三、二叉搜索树的中序遍历用于排序+去重

四、二叉搜索树的查找

1、查找的非递归写法

2、查找的递归写法

五、二叉搜索树的插入

1、插入的非递归写法

2、插入的递归写法

六、二叉搜索树的删除

1、删除的非递归写法

2、删除的递归写法

七、二叉搜索树的使用场景

1、key搜索模型(节点存key)

key搜索模型整体代码

2、key/value搜索模型(节点既存key又存value)

key/value搜索模型整体代码


一、二叉搜索树的概念

        二叉搜索树又称二叉排序树。

        空树是二叉搜索树,如果一棵树不是空树,需要满足如下情况便可称其为二叉搜索树:

        1、左子树上每一个键值均小于根节点;

        2、右子树上每一个键值均大于根节点;

        3、左右子树均为二叉搜索树。


二、二叉搜索树的性能分析

它可以用来排序 – 由于二叉搜索树的左子树都小于根,右子树都大于根,所以如果对二叉搜索树进行中序遍历得到的数据天然就是有序的

对有n个结点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树平均查找长度是结点在二 叉搜索树的深度的函数,即结点越深,则比较次数越多。 但对于同一个关键码集合,如果各关键码插入的次序不同,可能得到不同结构的二叉搜索树:

  • 最优情况下,二叉搜索树为完全二叉树 (或者接近完全二叉树),其平均比较次数为 O(logN)
  • 最差情况下,二叉搜索树退化为单支树( 或者类似单支),其平均比较次数为 O(N)。
  • 所以,二叉搜索树进行查找的时间复杂度为 O(N)。

可能有的同学会想,既然二叉搜索树查找的时间复杂度为 O(N),那我们为什么不直接用二分查找呢?毕竟二分查找的时间复杂度可是 O(logN),这是因为二分查找存在许多限制:

  • 二分查找要求数据必须有序;
  • 二分查找使用顺序表进行数据存储,插入、删除数据效率低,而在实际开发中,我们是要经常插入删除数据的;

问题:如果退化成单支树,二叉搜索树的性能就失去了。那能否进行改进,不论按照什么次序插 入关键码,二叉搜索树的性能都能达到最优?那么我们后续章节学习的AVL树和红黑树就可以上场了。


三、二叉搜索树的中序遍历用于排序+去重

通过上面那张图不难发现,用二叉搜索树走个中序,就是升序+去重排序,这也是二叉搜索树又被称为二叉排序树的原因。

        使用InOrder调用_InOrder的原因是类外面传参传不了私有的_root,所以采用多套一层的方法。

//中序遍历
void _InOrder(Node* _root)
{
    if (_root == nullptr)
    {
        return;
    }
    _InOrder(_root->_left);
    std::cout << _root->_key << " ";
    _InOrder(_root->_right);
}
void InOrder()//因为外部取不到_root,所以这里套了一层调用函数
{
    _InOrder(_root);
    std::cout << std::endl;
}

四、二叉搜索树的查找

1、查找的非递归写法

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

2、查找的递归写法

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

五、二叉搜索树的插入

        二叉搜索树的插入需要考虑插入后,需要维持二叉搜索树的形态。

1、插入的非递归写法

bool Insert(const K& key)
{
    if (_root == nullptr)
    {
        _root = new Node(key);//BSTreeNode对象中存放key值,构造一个二叉搜索树节点 
    }
    else
    {
        Node* parent = nullptr;
        Node* cur = _root;
        //cur一直走,走到要插入的位置
        while (cur)
        {
            parent = cur;
            if (cur->_key < key)
            {
                cur = cur->_right;
            }
            else if (cur->_key > key)
            {
                cur = cur->_left;
            }
            else//说明数字重复,插入失败
                return false;
        }
        cur = new Node(key);
        //判断插入节点放在parent节点的左子树还是右子树
        if (parent->_key < key)
        {
            parent->_right = cur;
        }
        else
        {
            parent->_left = cur;
        }
    }
    return true;
}

        1、如果根是空,插入的节点就是新的根;

        2、如果根不为空,就先根据二叉搜索树的性质找到该节点要插入的位置,如果路上遇到相同的数,插入失败;

 &nb

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值