数据结构-- 二叉查找树

来源:互联网 发布:淘宝付费推广技巧 编辑:程序博客网 时间:2024/05/20 11:47

数据结构 -- 二叉查找树

查找树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),除了一些个别情形外。。。


原创粉丝点击