• 二叉搜索树(结点添加、后继搜索、前驱搜索、结点删除)


    结点的添加:

    一开始没有看书没有查,自己写了一个,花了好久才调试成功:

     1 bool BST::Add_Node(TreeNode* temp) {
     2     if (!root) {
     3         root = temp;
     4         return true;
     5     }
     6     TreeNode* current = root;
     7     while (current) {
     8         if (temp->getData() >= current->getData()) {
     9             if (current->getRightChild())
    10                 current = current->getRightChild();
    11             else
    12                 break;
    13         }
    14         else if (temp->getData() < current->getData()) {
    15             if (current->getLeftChild())
    16                 current = current->getLeftChild();
    17             else
    18                 break;
    19         }
    20         else return false;
    21     }
    22     temp->setParent(current);
    23     if (temp->getData() < current->getData())
    24         current->setLeftChild(temp);
    25     else
    26         current->setRightChild(temp);
    27     return true;
    28 }

    结点数据:

     构造成功后数据结构应该如图所示:

     这里对于有重复键值的结点的加入是这样的:(见8-12行)允许重复值的加入,如碰到相同值的结点,则将当前指针指向这个结点的右孩子。

    其实这里对二叉搜索树的定义参考的是《算法导论》,其中是允许重复值的:

     如果想排除重复值,略微修改代码即可,很容易,不再叙述。

    以上算法通过先序遍历验证,成功:

     再看《算法导论》中对于结点添加算法的描述:

     后继搜索:

    看《算法导论》:

     假设y是x的后继(即中序排序:······x,y·········),如果x没有右子树,则y是x的最低的祖先,且y的左孩子也是x的祖先。

    (这里,一个结点的“祖先”也包括结点本身)

    所以换成人话就是:

    假设y是x的后继,如果x没有右子树,则y是x的最低的祖先,且y的左孩子是x的祖先或x本身。

     因此,想找到y,只需要从x开始向上走,直到找到一个是其父结点的左孩子的结点。

    二叉搜索树中,找任一结点的后继是什么思路呢?

    如果右孩子非空,那非常好理解,直接找右子树上的最小值就是它的后继。

    至于右孩子非空,那么其后继一定在他头顶上;倒过来想,当这个结点被添加进树的时候,由于比后继小,一定加在了其后继的左子树的某个位置上。

    这样就很好理解上面粗体字的结论了,如果把结点x后继的左子树独立出来看,x要么是这棵树(我们叫它T'好了)的根结点,要么是非根的某个结点。(x∈T')

    这么看来,上面代码就很好理解了:假设p是结点x的后继;先从结点本身这一层开始找起,p不断往上爬,t跟着它爬,每次一层,只要t还是p的右孩子,就说明p还不是后继,就继续往上爬。相当于我们从x开始找其所在的子树T'的根节点

    或者还有一种更简单的理解:从结点x开始向上(包括x,因为x可能是T'的根结点)寻找一对父子结点p-t,父子结点对p-t满足:p键值>t键值;则找到的第一对父子结点p-t中的父节点p即为x的后继结点。

     把上面的二叉搜索树用中序遍历跑一下,对比着看,帮助理解。

    重复键值嫌麻烦没有改,不影响理解。

    前驱搜索:

    本身逻辑其实一模一样,把后继搜索反过来就行了

    结点删除:

    直接看算法导论:

    分成了三类:

    1、如果要删除的结点没有孩子:直接将他的父结点对应的指针(左或右)指空,并删除他。

    2、如果要删除的结点有1个孩子:直接将他的父结点对应的指针(左或右)指向他唯一的那个孩子,并删除他。

    3、如果要删除的结点有2个孩子: 把他用他的后继结点代替。

    以下是算法代码(由于使用的数据结构和出于简便的考虑,没有真正删除结点数据,只是删除了其关联信息)

    测试删除50:

    成功。

    这里算法有个bug,如果删除根节点会出错,因为其父结点为null,访问null的左右孩子其实是不存在的,会抛出访问异常。

  • 相关阅读:
    白盒测试
    测试闰年
    黑盒测试
    等价类划分(2)
    等价类的划分
    《挑战程序设计竞赛》2.3 动态规划-优化递推 POJ1742 3046 3181
    《挑战程序设计竞赛》2.5 最短路 AOJ0189 2249 2200 POJ3255 2139 3259 3268(5)
    《挑战程序设计竞赛》2.6 数学问题-快速幂运算 POJ1995
    《挑战程序设计竞赛》2.6 数学问题-素数 AOJ0009 POJ3126 3421 3292 3641
    《挑战程序设计竞赛》2.6 数学问题-辗转相除法 AOJ0005 POJ2429 1930(1)
  • 原文地址:https://www.cnblogs.com/mrlonely2018/p/11954459.html
Copyright © 2020-2023  润新知