学习《算法导论》 二叉查找树 总结

来源:互联网 发布:淘宝怎么心极多点 编辑:程序博客网 时间:2024/05/22 03:35

学习《算法导论》 二叉查找树 总结

二叉查找树

二叉查找树可以用链表结构来表示, 其中每个结点除了包括卫星数据外,还包括域left, right, parent; 它们分别指向结点的左儿子,右儿子和父结点.

二叉查找树的链表结构定义如下:

typedef int ElemType;// 二叉树的链表结构typdef struct TreeNode{    ElemType data;    struct TreeNode* left;  // 指向左儿子    struct TreeNode* right; // 指向右儿子    struct TreeNode* parent; // 指向父结点}TreeNode, *PointTree, *TreeNode;

结点元素数据要满足二叉查找树性质:
1. 设x为二叉查找树的一个结点,若y是x的左子树中的一个结点,则:y.data <= x.data ; 若y是x的右子树中的一个结点,则:x.data <= y.data
注意:这里说的是x的左子树中的一个结点,并不是说x的左结点,右结点类似.

二叉查找树的遍历

根据上面的二叉查找树性质,可以按顺序输出树中的所有结点. 这些算法有:
中序遍历算法:根结点的输出介于左子树和右子树之间
先序遍历算法:根结点的输出介于左子树和右子树之前
后序遍历算法.:根结点的输出介于左子树和右子树之后

中序遍历算法

VOS_VOID INORDED-TREE-WALK(PointTree RootNode){    if (RootNode != NULL)    {        INORDED-TREE-WALK(RootNode->left);        printf(RootNode->data);        INORDED-TREE-WALK(RootNode->right);    }    return;}

先序遍历算法

VOS_VOID PREORDER-TREE-WALK(PointTree TreeRoot){    if (TreeRoot != NULL)    {        printf(TreeRoot->data);        PREORDER-TREE-WALK(TreeRoot->left);        PREORDER-TREE-WALK(TreeRoot->right);    }}

后序遍历算法

VOS_VOID POSTORDER-TREE-WALK(PointTree TreeRoot){    if (TreeRoot != NULL)    {        POSTORDER-TREE-WALK(TreeRoot->left);        POSTORDER-TREE-WALK(TreeRoot->right);        printf(TreeRoot->data);    }}

二叉查找树的查询

二叉查找树能支持以下操作:SEARCH, MINIMUM, MAXIMUM, SUCCESSOR和PREDECESSOR等. 并说明对高度为h的树, 它们都可以在O(h)时间内完成.

// 给定一关键字,查找树中是否存在; 递归算法PointTree TREE-SEARCH(PointTree Tree, ElemType KeyElem){    if (Tree == NULL)    {        return NULL;    }    if (Tree->data == KeyElem)    {        return Tree;    }    else if (KeyElem < Tree->data)    {        // 递归        return TREE-SEARCH(Tree->left, KeyElem);    }    else    {        return TREE-SEARCH(Tree->right, KeyElem);    }}
// 非递归算法PointTree ITERATIVE-TREE-SEARCH(PointTree Tree, ELemType KeyElem){    PointTree CurrentTreeNode = Tree;    if (NULL == Tree)        return NULL;    while(CurrentTreeNode->data != KeyElem)    {        if (KeyElem < CurrentTreeNode->data)            CurrentTreeNode = CurrentTreeNode->left;        else            CurrentTreeNode = CurrentTreeNode->right;    }    return Tree;}

MINIMUM

PointTree TREE-MINIMUM(PointTree TreeRoot){    PointTree CurrentNode = TreeRoot;    while(CurrentNode->left != NULL)    {        CurrentNode = CurrentNode->left;    }    return CurrentNode;}

MAXIMUM

PointTree TREE-MAXIMUM(PointTree TreeRoot){    PointTree CurrentNode = TreeRoot;    while(CurrentNode->right != NULL)    {        CurrentNode = CurrentNode->right;    }    return CurrentNode;

后继

前驱和后继,这两个概念不好理解,在线性表,队列或栈中,前驱就是该结点前面一个结点,后继就是该结点后面一个结点;但是树不同,因为树有三种遍历方法,在前序遍历中结点的前驱和在后序遍历中结点的前驱是不一样的。后继也一样。所以在树结构中,谈到前驱和后继,是在某种遍历方法的前提下说的。

在看后继的算法之前,先看算法导论中的一道题:
题目:若二叉查找树T中某个结点x的右子树为空,且x有一个后继y,则:y就是x的最低祖先,且其左孩子也是x的祖先.
先解释下这个结论吧,结论比较绕口。我这里用集合来表示下吧:
{祖先 | 其左孩子也是x的} 这个集合中离x最近的祖先。

证明:给定结点x,其后继y存在,则:y>x. 由于y大于x,则y不可能在x的左子树中,又因为x的右子树为空。则y只能是结点x的祖先. 或x祖先的右子树中。
又因为y是其中大于x且最小的一个,则y不可能是其祖先的右子树,那么我们可以将范围缩小至y必定为x的祖先
又根据y>x,则x必定在y的左子树中,即y的左孩子也是x的祖先(x也是x的祖先)。

下面是算法

// 查找结点x的后继PointTree TREE-SUCCESSOR(PointTree TreeNode){    // TreeNode的右子树非空,则TreeNode的后继就是    // 右子树中的最小结点    if (TreeNode->right != NULL)    {        return TREE-MINIMUM(TreeNode->right);    }    else    {        // 若右子树为空,则根据上面的题目,即查找TreeNode的最低祖        // 先,且其左孩子也是x的祖先,下面算法实现:        // 从TreeNode开始向上查找,直到找到某个结点的左孩子也是        // TreeNode的祖先为止        PointTree CurrentNode = TreeNode->parent;        // 条件:CurrentNode->right == TreeNode要求:CurrentNode要大于TreeNode        while((CurrentNode != NULL) && (CurrentNode->right == TreeNode))        {            TreeNode = CurrentNode;            CurrentNode = CurrentNode->parent;        }        return CurrentNode;}

算法比较难理解,最好画个树的图来理解.

二叉查找树的插入

插入会引起二叉查找树的动态集合的变化,所以插入之后,要修改数据的结构。但要按照二叉查找树的原则来修改。话不多说,直接看算法:

// 将NewNode插入TreeRoot树中VOS_VOID TREE-INSERT(PointTree TreeRoot, TreeNode NewNode){    PointTree CurrentNode = TreeRoot;    PointTree TmpNode = NULL;    // 若CurrentNode 为空,也即找到NewNode插入的位置    while(CurrentNode != NULL)    {        TmpNode = CurrentNode;        // 左子树        if (NewNode->data < CurrentNode->data)        {            CurrentNode = CurrentNode->left;        }        else // 右子树        {            CurrentNode = CurrentNode->right;        }    }    // 由上面可知,TmpNode就是NewNode的父结点    NewNode->parent = TmpNode;    // 若TmpNode为空,则说明该树是空树    if (NULL == TmpNode)    {        TreeRoot = NewNode;    }    else if (NewNode->data < TmpNode->data)    {        // NewNode放在TmpNode的左子树        TmpNode->left = NewNode;    }    else    {        // NewNode放在TmpNode的右子树         TmpNode->right = NewNode;    }    return;}

二叉查找树的删除

删除操作有三种情况:
1. 若删除的结点z没有子女,即z为叶子结点,则修改其父结点,将它的子女改为NULL;
2. 若z只有一个子女,则通过在其子结点与父结点间建立一条链来删除z;
3. 若z有两个子女,则找到z的后继(后继也就是:z的右子树的最左边的结点,所以它是没有左子女的);

代码如下:

VOS_VOID TREE-DELETE(PointTree TreeRoot, TreeNode DeleteNode){    TreeNode CurrentNode = NULL;    TreeNode ChildNode = NULL;    // 第一种情况和第二种情况:z最多只有一个子结点    if (DeleteNode->left == NULL || DeleteNode->right != NULL)    {        CurrentNode = DeleteNode;    }    else    {        // 第三种情况:找到z的后继        CurrentNode = TREE-SUCCESSOR(DeleteNode);    }    // ChildNode被置为CurrentNode的非NULL子女结点    if (CurrentNode->left != NULL) // 即这里是第二种情况或第三种情况    {        ChildNode = CurrentNode->left;    else // 即这里是第一种情况或第二种情况    {        ChildNode = CurrentNode->right;    }    if(ChildNode != NULL) // 这就是第二种情况    {        ChildNode->parent = CurrentNode->parent;    }    if (CurrentNode->parent == NULL) // CurentNode为DeleteNode,但是无祖先,说明DeleteNode为根结点    {        TreeRoot = ChlidNode;    }    else if (CurrentNode == CurrentNode->parent->left)    {        CurentNode->parent->left = ChlidNode;    }    else    {        CurentNode->parent->right = ChildNode;    }    if (CurentNode != DeleteNode)    {        DeleteNode->data = CurentNode->data;    }    free(CurentNode);    return;}

这个删除算法没看懂,有三种情况比较好理解,但是这个算法没看明白。
下次找资料好好看看

下面再介绍一种二叉查找树的删除操作算法,这个比较好理解,实际上就是分别讨论了三种情况:

/*删除一个节点p为树根节点指针node_value要删除的节点的值*/ bool DeleteTreeNode(Tree * p,int node_value) {     Tree * t = FindNode(p,node_value);     if(t == NULL)     {         return false;     }     if((t->left == NULL)&&(t->right == NULL))     {/*没有子节点*/         Tree* tmp = t;         if(tmp->parent->left == tmp)         {             tmp->parent->left = NULL;         }         else         {             tmp->parent->right = NULL;         }         delete tmp;         tmp = NULL;     }     else if((t->left == NULL)||(t->right == NULL))     {/*一个子节点*/         Tree* tmp = t;         if(tmp->parent->left == tmp)         {             tmp->parent->left = (tmp->left ==NULL)?tmp->right :tmp->left;         }         else         {             tmp->parent->right = (tmp->left ==NULL)?tmp->right :tmp->left;         }         delete tmp;         tmp = NULL;     }     else     {/*两个子节点*/         Tree* s = Successor(t);         if(s == NULL)         {             return false;         }         t->node_value = s->node_value ;         DeleteTreeNode(s);     } } 
0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 孩子在幼儿园被老师罚站怎么办 孩子屁股打肿了又红又紫怎么办 生完孩子两个月一直浑身疼怎么办 宝宝被蚊子叮咬后红肿硬怎么办 小孩屁股青一块紫一块打的怎么办 小孩的手被鞭子抽红了怎么办 孩子每次写作业都要挨打挨骂怎么办 儿子四岁脾气特别大怎么办呢 月子里屁股被开水烫了怎么办 学生打闹家长只找老师责任怎么办 两个学生打闹受伤的孩子家长怎么办 小孩学习不好做家长的该怎么办 对学习不入门的小孩家长该怎么办 孩子老做作业发神上课不专心怎么办 儿子成绩考得差不专心未来怎么办 五年级学生写字慢又丑怎么办 宝宝上课坐不住不听老师话怎么办 三岁宝宝特调皮打他还还手怎么办 怀孕40天不知道喝酒了怎么办 怀孕四十天的时候喝酒抽烟了怎么办 宝宝怀孕三十天左右喝酒了怎么办 两个人都喝酒了意外怀孕怎么办 不知道自己怀孕了喝了很多酒怎么办 不知道自己怀孕了喝了一次酒怎么办 我儿子11岁了有多动症怎么办 面对老师的冷暴力家长该怎么办? 面对无德的老师家长该怎么办 如果你家长屏蔽老师老师该怎么办 小孩出完水痘后身上出现疱疹怎么办 脑子里兴奋的头疼怎么办 吃什么药 一个月宝宝异常兴奋不睡觉怎么办 四个月宝宝晚上兴奋不睡觉怎么办 20个月宝宝半夜惊醒哭闹怎么办 小孩吃了氨茶碱兴奋不睡觉怎么办 孩子在学校被同学撞鼻骨折怎么办 孩子在学校无意致使同学受伤怎么办 9个月的宝宝吃坏东西腹泻怎么办 8个月发烧到38度怎么办 1岁半宝宝鼻塞发烧38度6怎么办 八个月的宝宝发热38度怎么办 八个月宝宝发热在38度怎么办