数据结构-- 二叉查找树

数据结构 -- 二叉查找树

查找树ADT -- 二叉查找树

二叉树的一个重要的应用是它们在查找中的使用。使二叉树变为二叉查找树的性质是,对于树的每个节点X,它的左子树中所有关键字值小于X的关键值,而右子树中所有关键字值大于X的关键字值,这意味着,该树所有的元素可以用某种统一的方式排序。现在给出通常对二叉查找树进行的操作的简要描述。由于树的递归定义,通常是递归地编写这些擦欧总的例程。因为二叉查找树的平均深度为O(log N),所以我们一般不会担心栈空间用尽。

二叉查找树的声明

struct TreeNode;
typedef struct TreeNode *Position;
typedef struct TreeNode *SearchTree;

SearchTree MakeEmpty(SearchTree T);
Position Find(ElementType X, SearchTree T);
Position FindMin(SearchTree T);
Position FindMax(SearchTree T);
SearchTree Insert(ElementType X, SearchTree T);
SearchTree Delete(ElementType X, SearchTree T);

struct TreeNode
{
    ElementType Element;
    SearchTree Left;
    SearchTree Right;
}

MakeEmpty

这个操作主要用于初始化。

SearchTree MakeEmpty(SearchTree T)
{
    if(T != NULL)
    {
        MakeEmpty(T->Left);
        MakeEmpty(T->Right);
        free(T);
    }
    return NULL;
}

Find

这个操作一般返回指向树T中具有关键字X的节点的指针,如果这样的节点不存在则返回NULL。首先,要对是否为空树进行测试,如果T是NULL,那么我们就可以返回NULL。否则,如果存储在T中的关键字是X,那么我们就可以返回T。否则,我们对树T的左子树或右子树进行一次递归调用,这依赖于X与存储在T中关键字的关系。

Position Find(ElementType X, SearchTree T)
{
    if(T == NULL)
        return NULL;
    if(X < T->Element)
        return Find(X, T->Left);
    else if(X > T->Element)
        return Find(X, T->Right);
    else 
        return T;
}

FindMin

这个操作返回树中的最小元的位置。从根开始并且只要有左儿子就向左进行,终止点就是最小的元素

Position FindMin(SearchTree T)
{
    if(T == NULL)
        return NULL;
    else if(T->Left == NULL)
        return T;
    else 
        return FindMin(T->Left);
}

FindMax

这个例程返回树中的最大元的位置,除分支朝向右儿子外其余过程和FindMin一样
Position FindMax(SearchTree T)
{
    if(T != NULL)
        while(T->Right != NULL)
            T = T->Right;
     return T;
}


Insert

为了将X插入到树T中,如果找到X,那么什么也不用做(或做一些“更新”);否则,将X插入到遍历的路径上的最后一点上。
SearchTree Insert(ElementType X, SearchTree T)
{
    if(T == NULL)
    {
        /* Create and return a one-node tree*/
        T = malloc(sizeof(struct TreeNode));
        if(T == NULL)
        {
            printf("Out of space");
            exit(1);
        }
        else
        {
            T->Element = X;
            T->Left = T->Right = NULL;
        }
    }
    else if (X < T->Element)
        T->Left = Insert(X, T->Left);
    else if (X > T->Element)
        T->Right = Insert(X, T->Right);
    /* Else X is in the tree already; we'll do nothing */

    return T;
}

Delete

最困难的操作就是删除了。下面考虑几种可能的情况:
(1) 如果节点是一片树叶,那么它可以立即被删除
(2) 如果结点有一个儿子,则该节点可以在其父节点调整指针绕过该节点后被删除
(3) 如果结点有两个儿子,一般是用其右子树的最小的数据(很容易找到)代替该节点的数据并递归地删除那个节点(现在它是空的),因为右子树的最小节点不可能有左儿子,所以第二次Delete(删除)要容易。

SearchTree Delete(ElementType X, SearchTree T)
{
    Position TmpCell;
    if(T == NULL)
    {
        printf("Element not found");
        exit(1);
    }
    else if(X < T->Element)
        T->Left = Delete(X, T->Left);
    else if(X > T->Element)
        T->Right = Eelete(X, T->Right);
    else if(T->Left && T->Right)  /*Two children*/
    {
        /*Replace with smallest in right subtree*/
        TmpCell = FindMin(T->Right);
        T->Element = TmpCell->Element;
        T->Right = Delete(T->Element, T->Right);
    }
    else /* one or zero children */
    {
        TmpCell = T;
        if(T->Left == NULL)
            T = T->Right;
        else if(T->Right == NULL)
            T = T->Left;
        free(TmpCell);
    }
}
如果删除的次数不多,则通常使用的策略是懒惰删除:当一个元素要被删除时,它仍留在树中,而是只做了个删除的记号。这种做法特别是在有重复关键字时很流行,因此此时记录出现频率数的域可以减一。如果树中的实际节点数和“被删除”的节点数相同,那么树的深度预计只上升一个小的常数。因此,存在一个与懒惰删除相关的非常小的时间损耗,再有,如果被删除的关键字是重新插入的,那么分配一个新单元的开销就避免了。


平均情形分析

一棵树的所有节点的深度的和称为内部路径长。上述的操作的平均时间都是O(logN),除了一些个别情形外。。。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值