Introduce to algorithm--------pseudo code to C/C++ code(chapter 12)

二叉搜索树


以下摘自算法导论

"顾名思义,一颗二叉搜索树是以二叉树来组织的。"

"二叉搜索树中的关键字总是以满足二叉搜索树性质来存储的:
    设x是二叉树中的一个结点。如果y是左子树x中的一个结点,那么
 y.key ≤ x.key。如果y是x右子树中的一个结点,那么y.key ≥ 
 x.key."

“二叉搜索树上的基本操作所花费的时间与这棵树的高度成正比。对有
n个节点的 完全二叉树 来说,这些操作的最坏运行时间为Θ(lg n)。"    

上面几句话非常简练的描述了二叉搜索树的基本性质和基本特性。但在实际中,并不能保证二叉搜索树始终是平衡的。比如,依次插入一列从小到大排列的数列,生成的二叉树很有可能是极端不平衡的—–它总是把下一个数据插入在前一个数据的右端,以致整棵树的右子树部分持续增长,破坏了二叉树的平衡。在这种情况下,有些二叉树上的操作时间会表现出最坏情况 Θ(n) ,与完全平衡状态下的 Θ(lg n) 相去甚远。

以下为伪代码的C++代码部分:

typedef Data int;

struct Node
{
    Data    key;
    Node*   left;
    Node*   right;
    Node*   parent;
};

typedef Tree     Node*;
typedef SubTree  Node*;

查询

//inorder traverse
void inorder_tree_walk (Node* x)
{
    if (x != nullptr)
    {
        inorder_tree_walk(x.left);
        //format string should change if Data is not an int
        printf ("%i\t", x.key);
        inorder_tree_walk (x.right);
    }
}

Node* tree_search (Node* x, Data key)
{
    if (x == nullptr || key == x.key)
        return x;
    if (key < x.key)
        return tree_search (x.left, key);
    else
        return tree_search (x.right, key); 
}

//iterative version of tree_search ()
Node* iterative_tree_search (Node* x, Data key)
{
    while (x != nullptr && key != x.key)
    {
        if (key < x.key)    x = x.left;
        else                x = x.right;
    }
    return x;
}

Node* tree_minimum (Node* x)
{
    while (x.left != nullptr)
        x = x.left;
    return x;
}

Node* tree_maximum (Node* x)
{
    while (x.right != nullptr)
        x = x.right;
    return x;
}

以上的函数比较简单,在此不作多余的赘述。

下面这个函数tree_successor ()接受结点指针为输入参数,输出该结点的后继结点指针。这里分两种情况:

  • 若输入结点的右结点不为空,则该节点的后继结点为该结点的右结点的最小值结点。
  • 若输入结点的右结点为空,则沿着二叉树向上,直至:1.遇到某个结点使得它为其父结点的左结点。2.遇到根节点。
//search for node x's successor
Node* tree_successor (Node* x)
{
    if (x.right != nullptr)
        return tree_minimum (x.right);

    Node* y = x.parent;
    while (y != nullptr && x == y.right)
    {
        x = y;
        y = y.parent'
    }
    return y;
}

函数tree_predecessor ()与tree_successor ()相似,在此不再给出。

插入

函数tree_insert (Tree root, Node* node)在 以root为根结点的二
叉树中结点node,其中结点node各字段已被正确初始化。

  • 过程:根据比较情况沿树向下,最后成为叶结点的子结点。
void tree_insert (Tree root, Node* node)
{
    Node*   y = static_cast<Noede*> (nullptr);
    Node*   x = root;

    while (x != nullptr)
    {
        y = x;
        if (node.key < x.key)   x = x.left;
        else x = x.right;
    }
    //assign new node's parent field
    node.parent = y;

    //meet the root node, change root node
    //assume node is correctly initialized
    if (y == nullptr)           root = node;
    else if (node.key < y.key)  y.left = node;
    else                        y.right = node;
}

删除

删除函数比较复杂,这里需要引入一个辅助函数:

  • prototype: transplant (Tree root, SubTree u, SubTree v);
  • result: 用一棵以v为根的子树来替换一棵以u为根的子树。结点u的双亲成为结点v的双亲,并且最后v成为u的双亲的相应的孩子。
  • attention: 函数只处理了子树u的更新,对子树v的更新由该函数的调用者(caller)负责。
void transplant (Tree root, SubTree u, SubTree v)
{
    //if sub-tree u is null, replace root with new tree v
    if (u.parent == nullptr)        root = v;
    else if (u == u.parent.left)    u.parent.left = v;
    else                            u.parent.right = v;

    if (v != nullptr)               v.parent = u.parent;
}

以下是删除函数的实现, 在以root为根的二叉树中删除结点z,其中调用了子例程transplant(…);
删除函数比较复杂,因为删除结点后可能会造成二叉树的不平衡。所以在该函数中要处理多种情况,这里假设要删除的结点z替换即为调用transplant(…)函数:

  • z结点无左子树—-直接将z结点替换为z的右子树。
  • z结点无右子树—-直接将z结点替换为z的左子树。
  • z结点既有左子树又有右子树:

    我们这里选择用z的后继结点来替换z。所以根据后继结点是否是z的右结点,又可以分为两种情况:1.后继结点是z的右结点—-直接将z结点替换为后继结点。(因为后继结点必定没有左结点)。2.后继结点不是z的右结点—-在不破坏所在子树的平衡条件情况下,将后继结点调整为z的右结点,即将情况2转化为情况1.

以下是delete结点时的四种情况:
picture

void tree_delete (Tree root, Node* z)
{
    if (z.left == nullptr)          transplant (root, z, z.right);
    else if (z.right == nullptr)    transplant (root, z, z.left);
    else
    {
        //because y is the minimum node of z.right, so no left child
        Node*   y = tree_minimum (z.right);

        //if y is not a neighbor of z, transplant it, and make it a neighbor
        if (y.parent != z)
        {
            transplant (root, y, y.right);
            y.right = z.right;
            y.right.parent = y;
        }
        transplant (root, z, y);;
        y.left = z.left;
        y.left.parent = y;  
    }
}

以上函数的正确性需要由二叉搜索树的性质来保证。比如,tree_maximum
()函数的正确性需要是由二叉搜索树的二叉性质来保证,即左子树的数据必须小于等于父节点的数据(如果有的话)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值