zoukankan      html  css  js  c++  java
  • 算法导论笔记之二叉搜索树

    12.1、二叉搜索树的定义
    1、一颗二叉搜索树是以二叉树来组织的,它具有如下性质:
    设x是二叉搜索树中的一个节点,如果y是x左子树中的一个节点,那么y.key <= x.key;如果y是x右子树中的一个节点,那么y.key >= x.key。我们用left、right、p分别表示二叉搜索树的左子树、右子树和父节点。
    2、根据二叉搜索树的性质,对二叉搜索树进行中序遍历,即可得到一个递增的序列;也就是说,为了检查一个树是否是二叉搜索树,可以使用中序遍历。
    中序遍历的伪代码为(递归):
    InorderTreeWalk(x)
    if(x != NIL)
    InorderTreeWalk(x.left);
    print x.key;
    InorderTreeWalk(x.right);
    练习:
    12.1-2:
    二叉搜索树:左子树关键字<根结点关键字<右子树关键字
    最小堆:根结点关键字 <= 左子树关键字 && 根结点关键字 <= 右子树关键字
    不能,因为在最小堆中,左子树和右子树的大小关系不能确定,而如果通过比较算法来进行排序的话,其下界为n*lgn 。
    12.1-3:
    用栈实现:见算法导论-10.4-有根树的表示中的10.4-3
    不用栈实现:见算法导论-10.4-5
    12.1-4:
    和中序遍历类似。只不过遍历根节点的顺序不同,其伪代码如下:
    PreorderTreeWalk(x)
    if(x != NIL)
    print x.key;
    InorderTreeWalk(x.left);
    InorderTreeWalk(x.right);
    PostorderTreeWalk(x)
    if(x != NIL)
    InorderTreeWalk(x.left);
    InorderTreeWalk(x.right);
    print x.key;
    12.1-5:
    反证法。假设存在一种基于比较的算法,从n个元素的任意序列中构造一颗二叉搜索树,其最坏情况下的时间复杂度Ω低于n*lgn。则可以构造一种基于比较的排序模型,首先,构造一颗二叉搜索树(时间复杂度为Ω),然后以中序遍历输出该二叉搜索树的元素(时间复杂度为n),即可完成排序。最终,该算法的时间复杂度为max(Ω,n),小于n*lgn,这与题设中所有基于比较的排序模型的算法的时间复杂度的下界为n*lgn矛盾,因此假设不成立,原命题得证。
    12.2、查询二叉搜索树
    1、查找。
    二叉搜索树查找操作的伪代码为(递归):
    TreeSearch(x,k)
    if(x == NIL || x .key == k)
    return x;
    if(k < x.key)
    return TreeSearch(x.left,k);
    else
    return TreeSearch(x.right,k);
    二叉搜索树查找操作的伪代码为(循环):
    TreeSearch(x)
    while(x != NIL || x .key != k)
    if(k < x.key)
    x = x.left;
    else
    x = x.right;
    return x;
    2、最大和最小关键字元素
    根据二叉搜索树的性质:通过从树根开始沿着left孩子的指针直到遇到NIL,我们总能在一颗二叉搜索树中找到一个元素,该元素就是以x为根的二叉搜索树的最小关键字元素;同理,从树根开始沿着right孩子的指针直到遇到NIL,即可获得最大关键字元素。
    获得最小关键字元素的伪代码为(循环):
    TreeMinimum(x)
    while(x.left != NIL)
    x = x.left;
    return x;
    获得最小关键字元素的伪代码为(递归):
    TreeMinimum(x)
    if(x.left == NIL)
    return x;
    return TreeMinimum(x.left);
    获取最大关键字元素和获取最小关键字元素的伪代码是对称的,为:
    循环:
    TreeMaximum(x)
    while(x.right != NIL)
    x = x.right;
    return x;
    递归:
    TreeMaximum(x)
    if(x.right == NIL)
    return x;
    return TreeMaximum(x.right);
    3、前驱和后继
    给定一个二叉搜索树中的一个节点,有时需要按中序遍历查找它的前驱和后继。如果所有关键字互不相同,则一个节点x的后继是大于x.key的最小关键字节点,x的前驱是小于x.key的最大关键字节点。如果x就是该二叉搜索树的最小或最大关键字,则x的前驱或后继为NIL。

    获取二叉搜索树后继的伪代码为:
    TreeSuccessor(x)
    if(x.right != NIL)
    return TreeMinimum(x.right);
    y = x.p;
    while( y != NIL && x == y.right)
    x = y;
    y = y.p;
    return y;
    把TreeSuccessor分为两种情况:如果x的右子树非空,那么x的后继恰是x右子树中最左的节点,通过TreeMinimum(x.right)可以找到;另一方面,如果x的右子树为空并且有一个后继y,那么y就是x的有左孩子的最底层祖先。

    获取二叉搜索树前驱与获取二叉搜索树后继的行为是对称的,其伪代码为:
    TreePredecessor(x)
    if(x.left != NIL)
    return TreeMaximum(x.left);
    y = x.p;
    while(y != NIL && x == y.left)
    x= y;
    y = y.p;
    return y;

    练习:
    12.2-1:
    根据二叉搜索树查找的特点,遇到比363小的数,就往右找,查找序列中的数会越来越大;反之,遇到比363大的数,就往左找,查找序列中的数就越来越小。因此,比363大的数是按降序排列的,比363小的数是按升序排列的。所以c和e不是查找过的序列。
    12.2-2、12.2-3:见前面的伪代码。
    12.2-4 反例太多了...
    12.2-5:
    如果二叉搜索树有右孩子,则它的后继为TreeMinimum(x.right),为最左边的元素,没有左孩子;同理可知,它的前驱没有右孩子。

    12.3 插入和删除
    1、插入
    要将一个新值v插入到一颗二叉搜索树T中,需要调用TreeInsert。该过程以节点z作为输入,其中z.key = v,z.left = NIL,z.right = NIL。插入过程的伪代码为:
    TreeInsert(T,Z)
    y = NIL;
    x = T.root;
    while (x != NIL)
    y = x;
    if(z.key < x.key)
    x = x.left;
    else
    x = x.right;
    z.p = y;
    if(y == NIL) //树是空的
    T.root = z;
    else if(z.key < y.key)
    y.left = z;
    else
    y.right = z;

    2、删除
    从一颗二叉搜索树T中删除一个节点z分为三种情况:
    a:如果z没有孩子节点,那么只需要简单的将它删除,并修改它的父节点,用NIL作为孩子替换z即可。
    b:如果z只有一个孩子节点,那么将这个孩子提升到树z的位置中,并修改z的父节点,用z的孩子来替换z
    c:如果z有两个孩子,那么找到z的后继y,并让y占据树中z的位置。z原来的右子树部分成为y的新的右子树,z原来的左子树成为y的新的左子树。
    P167的图12-4总结了删除二叉搜索树节点时的四种情况。
    从一颗二叉搜索树T中删除一个节点z的伪代码为:

    首先定义一个在二叉搜索树内迁移节点的函数Transplant.它是用一颗子树v替换一颗子树u,并成为u的双亲的孩子节点。
    Transplant(T,u,v)
    //u是根节点
    if(u.p == NIL)
    T.root = v;
    //u是其双亲的左孩子
    else if(u == u.p.left)
    u.p.left = v;
    //u是其双亲的右孩子
    else
    u.p.right = v;
    if(v != NIL)
    v.p = u.p;

    最后是TreeDelete的伪代码:
    TreeDelete(T,z)
    if(z.left == NIL)
    Transplant(T,z,z.right);//z.right为NIL也能正常删除
    else if(z.right == NIL)
    Transplant(T,z,z.left);
    else
    //找z的后继,因为z有右子树,所以直接调用TreeMinimum即可
    y = TreeMinimum(z.right);
    if(y.p != z)
    Transplant(T,y,y.right);
    y.right = z.right;
    y.right.p = y;
    Transplant(T,z,y);
    y.left = z.left;
    y.left.p = y;

  • 相关阅读:
    Unable to load the specified metadata resource
    Web开发人员速查卡
    vs 中大括号之间垂直虚线显示
    第4届华为编程大赛决赛试题解答(棋盘覆盖)
    assert()函数用法总结
    Win7安装VC++6.0已知的兼容性问题的解决方法
    VC6打开一个文件或工程的时候,会导致VC6崩溃而关闭
    浮点数取整.
    1.4 VC6.0在win7下安装的兼容性问题以及解决办法
    华为编程大赛_将字符数组内的数字排序
  • 原文地址:https://www.cnblogs.com/begodlike/p/6083182.html
Copyright © 2011-2022 走看看