【数据结构】6.搜索树

1.二叉搜索树

1.1 定义

二叉搜索树:

  •  一棵非空的二叉搜索树每个元素都有一个关键字,并且任意两个元素的关键字不同,所有关键字都是唯一的
  • 根节点的左子树小于根节点的关键字
  • 根节点的右子树大于根节点的关键字
  • 左右子树也是二叉搜索树

索引二叉搜索树:

  源于普通的二叉搜索树,在每个节点中添加一个 leftSize 字段,用来保存该节点左子树的元素个数。

1.2 二叉搜索树的操作和实现

1.2.1 搜索

根据搜索元素的 theKey 对应向左孩子或右孩子移动寻找即可,时间复杂度为O(height)

template<class K, class E>
pair<const K, E>* binarySearchTree<K, E>::find(const K& theKey) const
{// 返回关键字时K的元素指针
   // 从根节点 p 开始搜索,寻找关键字为 theKey 的元素
    binaryTreeNode<pair<const K, E> >* p = root;
    while (p != NULL)
        // 检查元素
        if (theKey < p->element.first)
            p = p->leftChild;
        else
            if (theKey > p->element.first)
                p = p->rightChild;
            else // 找到匹配的元素
                return &p->element;

    // 未找到匹配的元素
    return NULL;
}

1.2.2 插入

  使用 pp 指针记录 p 指针的双亲节点,p 指针不断查找应该插入的位置

  • p 指针指向空时,说明该插入到该位置。判断插入元素 theKey 和 pp 指针的大小关系插入该元素
  • p 指针找到相同的 theKey 时,更新该节点的 value

template<class K, class E>
void binarySearchTree<K, E>::insert(const pair<const K, E>& thePair)
{// 插入thePair节点,如果存在相同的Key则覆盖元素
    binaryTreeNode<pair<const K, E> >* p = root;
    binaryTreeNode<pair<const K, E> >* pp = NULL;

    // 如果循环结束未找到该元素, 则pp元素就是该元素的父节点
    while (p != NULL)
    {// 检查元素p
        pp = p;
        // 将p移动到它的孩子节点
        if (thePair.first < p->element.first)
            p = p->leftChild;
        else
            if (thePair.first > p->element.first)
                p = p->rightChild;
            else
            {// 如果有相同的key, 覆盖旧的值
                p->element.second = thePair.second;
                return;
            }
    }

    // 为thePair创建一个节点, 然后与pp连接
    binaryTreeNode<pair<const K, E> >* newNode = new binaryTreeNode<pair<const K, E> >(thePair);
    if (root != NULL) // 判断树是否是空
        if (thePair.first < pp->element.first)
            pp->leftChild = newNode;
        else
            pp->rightChild = newNode;
    else
        root = newNode; // 直接将节点作为根节点
    treeSize++;
}

1.2.3 删除

  删除分三种情况

  • 要删除的节点没有孩子节点:如左图所示,这种情况只需要找到该节点释放内存空间,并将双亲节点的右孩子指针置为空即可
  • 要删除的节点只有一个孩子节点:如右图所示,这种情况只需要将该节点内存空间释放,并且将 元素15 的左孩子连接到 元素12 的孩子节点即可

  • 要删除的节点有两个孩子节点,且要更换位置的节点没有孩子节点。我们需要在要删除的节点的左孩子中,向右寻找到最大的元素做替换,将 元素14 重新连接 元素12 和 元素18 构建一个新的树,并重新连接到 元素20 的左孩子位置,最后释放元素14的内存(这种情况实际上转换为构建一棵树,然后删除14,且14没有孩子节点)

  • 要删除的节点有两个孩子节点,且要更换位置的节点有一个孩子节点。我们需要在要删除的节点的左孩子中,向右寻找到最大的元素做替换,将 元素14 重新连接 元素12 和 元素18 构建一个新的树,并重新连接到 元素20 的左孩子的位置,然后把 元素14 的孩子连接到 元素14 的双亲节点处,释放 元素14 的内存空间(这种情况实际上转换为构建一棵树,然后删除14,且14有一个孩子节点)

template<class K, class E>
void binarySearchTree<K, E>::erase(const K& theKey)
{// 删除关键字为 theKey 的节点

    // 查找关键字为 theKey 的节点
    binaryTreeNode<pair<const K, E> >* p = root;    // p 是要删除的节点
    binaryTreeNode<pair<const K, E> >* pp = NULL;   // pp 是 p 的双亲
    while (p != NULL && p->element.first != theKey)
    {// p移动到它的孩子节点
        pp = p;
        if (theKey < p->element.first)
            p = p->leftChild;
        else
            p = p->rightChild;
    }
    if (p == NULL)
        return; // 未找到则直接结束

    // 找到一个 s 元素替换要删除的元素 p, 然后用元素 s 构建一棵树
    // 接下来转换为删除元素 s 的操作, s 只有左孩子或者没有孩子
    if (p->leftChild != NULL && p->rightChild != NULL)
    {
        binaryTreeNode<pair<const K, E>>* s = p->leftChild; // s: 在 p 的左孩子中找到最大的元素
        binaryTreeNode<pair<const K, E>>* ps = p;           // ps: s 的双亲
        while (s->rightChild != NULL)
        {
            ps = s;
            s = s->rightChild;
        }

        // 用元素 s 构建一棵树, 替代元素 p 的位置
        binaryTreeNode<pair<const K, E> >* q = new binaryTreeNode<pair<const K, E>> (s->element, p->leftChild, p->rightChild);

        // 如果要删除的没有双亲, 根节点就是 q, 否则把孩子节点更换为 q
        if (pp == NULL) { root = q; }
        else if (p == pp->leftChild) { pp->leftChild = q; }
        else { pp->rightChild = q; }

        // 如果 s 恰好是要删除元素 p 的下一个节点, 单独处理一下
        if (ps == p) { pp = q; }
        else { pp = ps; }

        // 释放元素 p 的空间, 把 p 的指针指向 s, 接下来的工作是删除 s
        delete p;
        p = s;
    }

    // 下面的情况: 元素 p 只有一个孩子或者没有孩子
    // 指针 c 指向了元素 p 的孩子或者 NULL
    binaryTreeNode<pair<const K, E> >* c;
    if (p->leftChild != NULL) { c = p->leftChild; }
    else if (p->leftChild != NULL) { c = p->rightChild; }
    else { c = NULL; }

    // 如果要删除的 p 是根节点
    if (p == root)
        root = c;
    else
    {// 否则将 pp 的孩子置为 p
        if (p == pp->leftChild)
            pp->leftChild = c;
        else pp->rightChild = c;
    }
    treeSize--;
    delete p;
}

 

posted @ 2023-10-13 20:10  imXuan  阅读(42)  评论(0编辑  收藏  举报