数据结构(十八)——二叉搜索树及实现

文章目录

    • 前言
    • 二叉搜索树
      • 为什么使用二叉搜索树?
    • 二叉搜索树实现
      • 搜索
      • 插入
      • 删除

前言

从二叉搜索树开始,到下一节的AVL树以及下下节的B树,都适合于字典描述。以前和字典相关的数据结构是线性结构跳表和散列,这一章到了树形结构。

二叉树的删除方法要特别注意,涉及到四个指针在树上游荡,需要理清每步的逻辑关系。

二叉搜索树

为什么使用二叉搜索树?

和其它每种数据结构一样,都有其存在的意义,优势及劣势。二叉搜索树也要与同为字典描述的跳表和散列进行对比。

查找(平均、最坏) 插入 删除
跳表 Θ(logn)、Θ(n) Θ(logn)、Θ(n) Θ(logn)、Θ(n)
散列 Θ(1)、Θ(n) Θ(1)、Θ(n) Θ(1)、Θ(n)
二叉搜索树 Θ(logn)、Θ(n) Θ(logn)、Θ(n) Θ(logn)、Θ(n)
平衡搜索树 Θ(logn)、Θ(logn) Θ(logn)、Θ(logn) Θ(logn)、Θ(logn)

可见在增删查领域二叉搜索树媲美跳表,但是不及散列在理想状况下的Θ(1)。
二叉搜索树的特殊形式,平衡二叉搜索树有着最稳定的增删查时间复杂度,它是我们下一节要学习的数据结构。在学习之前,我们要先学习二叉搜索树的基本操作。

其实在实际应用领域,是平衡二叉树在大显身手。平衡二叉树的优势还不止这些,

比如有个新的需求: 按关键字的升序输出字典元素。

时间复杂度
散列 O(nlogn)
跳表 Θ(n)
平衡搜索树 Θ(n)

这时散列的表现较差,平衡二叉搜索树仍能有良好的时间性能。

在学习平衡二叉搜索树之前,我们实现实现二叉搜索树。

二叉搜索树实现

template <class K, class E>
class BSTree : public linkedBinaryTree<K,E>
{
    pair<const K,E>* find(const K& theKey) const;
    void insert(const pair<const K, E>& thePair);
    void erase(const K& theKey);
    void ascend(){linkedBinaryTree<K,E> :: inOrderOutput();}
};

搜索

template<class K ,class E>
pair<const K, E>* BSTree :: find(const K& theKey) const
{
    binaryTreeNode<pair<const K, const E>> *p = root;

    while(p != NULL)
    {
        if(theKey < p->data.first)
        {
            p = p->leftChild;
        }else if(theKey > p->data.first)
        {
            p = p->rightChild;
        }else
        {
            return &p->data;
        }
    }
    return NULL;
}

插入

template<class K , class E>
void BSTree :: insert(const pair<const K , E>& thePair)
{
    binaryTreeNode<pair<const K, const E>> *p = root;
    binaryTreeNode<pair<const K, const E>> *pp = NULL;

    while(p != NULL)
    {
        pp = p;
        if(thePair.first < p->data.first)
        {
            p = p->leftChild;
        }else if(thePair.first > p->data.first)
        {
            p = p->rightChild;
        }else
        {
            p->data.second = thePair.second;
            return;
        }
    }

    binaryTreeNode<pair<const K, const E>> *newNode
    = new binaryTreeNode<pair<const K, const E>>(thePair);

    if(root != NULL)
    {
        if(thePair.first > pp->data.first)
            pp->rightChild = newNode;
        else
            pp->leftChild = newNode;
    }
    else
    {
        root = newNode;
    }
    treeSize++;
}

删除

删除是二叉搜索树最复杂的操作,强烈建议读者自己画两个二叉搜索树,标识算法中所要用到的节点,捋一遍代码,明确各指针的作用。有时间的话,我会更新二叉搜索树删除方法的图解。

/*
删除的方法体中可分为四个部分,选择使用左子树的最大节点替换
1. 搜索要删除的节点的key,并记录该节点和父节点。
2. 如果要删除的节点有两个孩子,进行搜索找到左子树最大节点,并记录该节点和父节点。
3. 调整四个指针的位置,使要删除的节点最多只剩一个孩子
4. 要删除的节点至多有一个孩子,删除节点,将孩子节点和父节点对接。
*/

template <class K, class E>
void bsTree<K,E> :: erase(const K& theKey)
{
    // 第一部分
    binaryTreeNode<pair<const K, const E>> *p = root;
    binaryTreeNode<pair<const K, const E>> *pp = NULL;

      while(p != NULL)
    {
        pp = p;
        if(theKey < p->data.first)
        {
            p = p->leftChild;
        }else if(theKey > p->data.first)
        {
            p = p->rightChild;
        }else
        {
            return;
        }
    }
    if(p != NULL)
        return;

    //第二部分
    if(p->leftChild != NULL && p->rightChild != NULL)
    {
        binaryTreeNode<pair<const K, const E>> *s = p->leftChild;
        binaryTreeNode<pair<const K, const E>> *pp = p;

        while(s->rightChild != NULL)
        {
            ps = s;
            s = s->rightCHild;
        }

    // 第三部分
    //将左子树找到的节点s移到p,因为const Key,所以我们
    // 直接新建一节点,其左右子树指向p的左右子树。
        binaryTreeNode<pair<const K, E>> *q =
        new binaryTreeNode<pair<const K, E>>(s->data, p-rightChild,p->rightChild);

        if(pp == NULL)
            root = q;
        else if (p == pp->rightChild)
            pp->rightChild = q;
        else
            pp->leftChild = q;

        if(ps == p)
            pp = q; // 此时不能等于ps,ps还在原来的树上,q是替换后的新树。
        else
            pp = ps;
        delete p; // 建立新节点会删除需要删除的节点,但是子树多了一需要删除的节点,即s
        p = s; // 把s当作至多只有一个孩子节点的p,继续往下进行。
    }

    // 第四部分,可直接适用于只有一个节点或无孩子的情况。
    binaryTreeNode<pair<const K, E>> *c;
    if(p->leftChild != NULL)
        c = p->leftChild;
    else
        c = p->rightChild; // 此时包含了p无孩子的情况

    //删除p
    if(p == root)
        root = c;
    else
    {
        if(p == pp->leftChild)
            pp->leftChild = c;
        else
            pp->rightChild = c;
    }
    treeSize--;
    delete p;
}

你可能感兴趣的:(数据结构)