修改二叉搜索树

Posted

技术标签:

【中文标题】修改二叉搜索树【英文标题】:Modify a binary search tree 【发布时间】:2016-11-25 17:21:43 【问题描述】:

我正在尝试为二叉搜索树类编写一个方法来修改平衡的正常树,使树仅在一侧有节点。

从元素出现在不平衡树中的顺序来看,对我来说,顺序遍历(左、中、右)之间似乎存在某种关系。

【问题讨论】:

如果你在课堂上得到了这个作业,我希望你的老师会提前提供足够的材料来完成这个作业;所以你的问题应该被提出来的合适的一方就是你的导师。 这不是课堂作业@SamVarshavchik,我正在练习 感谢@melpomene 在问题中进行了编辑 @SamVarshavchik 不要责怪教练,除非你比我们有更多的信息。 @nicomp 但它很有趣! 【参考方案1】:
void unbalance(Node **pp) 
    Node *p;
    while ((p = *pp)) 
        if (!p->left) 
            pp = &p->right;
         else 
            Node *tmp = p->left->right;
            *pp = p->left;
            p->left->right = p;
            p->left = tmp;
        
    


...
Node *tree = ...;
unbalance(&tree);

此代码基于@templatetypedef 的回答。它使用指针对指针来避免孤立节点(这也摆脱了finalRoot,其唯一目的是避免孤立调用者传入的原始根)。

这个想法是沿着最右边的树枝走下树。如果没有任何左孩子,我们只进入if 语句的第一部分并退出而不做任何事情。

当有左子树时使用else 部分。在这种情况下,我们向右旋转树。

这具有从左子树上拉一个节点的效果,即我们的左子树现在小了一个节点。我们重复这个直到左子树完全消失,此时我们再次进入if 的第一部分。然后我们下到剩下的右子树并重复整个过程。

指针对指针在修改链表或树的结构时通常很有用。它们让我们直接处理原始指针,而不是复制它们。在这种情况下,赋值*pp = ... 修改了别处的指针(pp 指向的指针)。这要么是原始根(由调用者传入),要么是树中的right 指针之一(由pp = &p->right 设置)。之所以需要重新指向,是因为 else 分支中的树旋转会打乱节点周围的节点,因此曾经是子树的根的会被进一步向下移动,而之前的左节点被拉上来成为新的根。我们更新*pp 以保留更高的指针(调用者中的tree 变量或父节点的right 成员)指向子树的(新)根的不变量。

【讨论】:

非常感谢!这适用于我正在使用的实现。您能否详细说明一下您对 Node ** 的使用以及它有何帮助?是不是因为它是开始的参考,因此实际上反映了变化?你能告诉我你用什么软件/网站制作动画吗? @C2K 我从en.wikipedia.org/wiki/Tree_rotation 偷了动画(实际上我只是热链接了它)。它由“Tar-Elessar”创建,位于CC BY-SA 4.0。【参考方案2】:

迭代执行此操作的一种方法是使用树旋转。这个想法是这样的:

    如果根节点有左子节点,则向右旋转根节点。 否则,根节点没有左子节点。向右下降并重复此过程。

在伪代码中:

Node finalRoot = null;
while (currNode != null) 
    if (currNode.left != null) 
        currNode = rotateRight(currNode);
     else 
        if (finalRoot == null) finalRoot = currNode;
        currNode = currNode.right;
    

return finalRoot;

这种方法改编自 Day-Stout-Warren 算法,这是第一步。它的运行时间为 O(n)。

【讨论】:

if (finalRoot == null) 部分让我感到困扰:它不优雅。现在我知道原因了:它处理原始根指针(currNode 的初始值)与所有其他指针 currNode 步骤不同(所有 .right 子级)。我认为这意味着伪代码已损坏:它不会更新currNode.right,因此树无法访问所有被轮换的左侧节点。 所以finalRoot 实际上并不是最初的树根——它最终成为树中最右边的节点(跟踪示例树上的代码)。另外,您能详细说明一下丢失节点的情况吗?当我们下降到右子树时,我们知道没有左子树可以丢失(而且我们上面的所有东西都在一个巨大的脊柱中。) 这是我正在谈论的图表:i.imgur.com/P1qPc1m.png - 这有意义吗?关键是A.right 永远不会更新,因此一直指向B,即使B 不再是其子树的根。 @melpomene 我确实尝试过运行伪代码,但似乎我在我的 3 树中丢失了一个节点......当我有一个可行的解决方案时会对其进行更多研究并更新。 @templatetypedef 这是理解 Day-Stout-Warren 算法的好读物吗? penguin.ewu.edu/~trolfe/DSWpaper我正在尝试理解正确的旋转

以上是关于修改二叉搜索树的主要内容,如果未能解决你的问题,请参考以下文章

二叉树二叉搜索树中的众数(leetcode501)

手撕STL二叉搜索树

手撕STL二叉搜索树

LeetCode-树二叉搜索树与双向链表

数据结构学习笔记04树(二叉树二叉搜索树平衡二叉树)

算法漫游指北(第十三篇):二叉树的基本概念满二叉树完全二叉树二叉树性质二叉搜索树二叉树定义二叉树的广度优先遍历