【问题标题】:List implemented using an inorder binary tree使用中序二叉树实现的列表
【发布时间】:2009-11-20 01:20:23
【问题描述】:

对于新的计算机科学作业,我们将使用中序二叉树来实现列表/数组。我只是想要一个建议而不是解决方案。

这个想法是有一个二叉树,它的节点可以通过索引访问,例如

t = ListTree()
t.insert(2,0) # 1st argument is the value, 2nd the index to insert at
t.get(0) # returns 2

存储值的 Node 类是不可修改的,但有一个属性 size 包含下面的子节点总数,以及指向子节点和存储的 leftrightvalue相应的值。

我目前跟踪索引的主要问题 - 因为我们不允许将节点的索引存储在节点本身中,所以我必须依靠遍历来跟踪它。因为我在遍历时总是从左节点开始,所以我还没有想到一种方法来递归地找出我们当前所在的索引。

欢迎提出任何建议。

谢谢!

【问题讨论】:

    标签: python algorithm binary-tree


    【解决方案1】:

    您真的不想将它存储在节点本身上,因为这样就必须在索引小于插入索引的所有节点的插入时更新索引。我认为真正的问题是如何进行有序遍历。尝试让您的递归函数返回其左侧的节点数。

    【讨论】:

    • 这正是我的想法——因为我们应该跟踪应该有帮助的子节点的数量。不过,保持所有子节点计数更新似乎有点棘手。
    • 从某种意义上说,您不需要保持“更新”的子节点数量。当您在任何给定节点时,您需要知道它的索引,您只需要知道左边有多少个节点。这有帮助吗?
    • 在插入时保持所有节点的 node.size 属性更新实际上是分配的一部分。部分原因是要求生成“列表”的长度应该是 O(1),但是当使每个节点都与子节点的数量保持同步时,似乎失去了有效长度计算的所有好处。
    • 我以为我们在谈论索引,但如果我们在谈论大小,那就更容易了。只需让任何节点的大小为其左右子节点的大小即可。这样,每当您执行插入或删除操作时,您只需更新节点的父节点。如果您跟踪根节点(我认为这是一个安全的假设),这将为您提供 O(1) 大小。
    【解决方案2】:

    我认为您不想存储索引,而只是存储每个子树的大小。例如,如果您想查找列表中的第 10 个元素,并且左右子树各有 7 个元素,您会知道根是第 8 个元素(因为它是有序二进制),第一个元素右子树的第 9 位。有了这些知识,您将递归到正确的子树,寻找第二个元素。

    HTH

    【讨论】:

    • 是的。这基本上就是我得到的解决方案,感谢其他人在这里的建议。非常感谢大家!
    【解决方案3】:

    嗯,二叉树中的节点不能有值和索引。它们可以有多条数据,但树只能键控/构建在一个上。

    也许您的任务希望您使用索引值作为树的键并将值附加到节点,以便快速检索给定索引的值。

    【讨论】:

      【解决方案4】:

      树必须平衡吗?算法需要高效吗?

      如果不是,那么最简单的做法是创建一棵所有左孩子都为空的树,即,一棵树会演变为链表。

      要插入,递归查找右子节点,然后在返回的途中更新节点的大小。类似(伪代码)

      function insert(node, value, index, depth)
          if depth < index
              create the rightchild if necessary
              insert( rightchild, value, index, depth + 1)
          if depth == size
              node.value = value
          node.size = rightchild.size + 1
      

      完成这项工作后,您可以对其进行修改以使其更加平衡。增加链表长度时,根据当前最少的向左或右子节点添加节点,并在退出递归时更新大小。

      为了更高效地进行泛化,您需要根据索引的二进制表示来处理索引。

      例如,空列表有一个节点,没有值为 null 且大小为 0 的子节点。

      假设您想在索引 1034 处插入“hello”。然后您希望得到一棵树,其根有两个子节点,大小分别为 1024 和 10。左子节点没有实际的子节点,但右节点有一个它自己的大小为 2 的右子节点。(隐含大小为 8 的左子节点。)该节点又具有一个大小为 0 的右子节点,其值为“hello”。 (这个列表有一个从 1 开始的索引,但是一个从 0 开始的索引是类似的。)

      因此,您需要递归地将索引分解为其二进制部分,并根据需要添加节点。搜索列表时,遍历具有空子节点的节点时需要小心

      【讨论】:

      • 它不需要平衡,尽管获取和插入必须是 O(n),其中 n 是树的高度。完全有可能拥有一棵非常不平衡的树,但教授说这不应该是我们在课程的这一点上担心的事情。不过感谢您的建议 - 我也会考虑这种方法
      • 这个答案听起来对家庭作业来说是一个糟糕的答案。家庭作业通常是关于学习一个概念并将“树”强制放入链接列表中,这可以保证我不会在我拥有的任何教授那里获得 A。
      • 是的——我们刚刚研究了链表镜像,使用二叉树会有些多余。我认为 uncleo 的观点是它可以很容易地制成更平衡的版本。
      【解决方案5】:

      一个非常简单的解决方案是执行 GetFirst() 来获取树的第一个节点(这只是找到树的最左边的节点)。如果您的索引 N 为 0,则返回第一个节点。否则,调用 GetNodeNext() N 次以获取适当的节点。这并不是非常高效,因为按索引访问节点需要 O(N Lg N) 时间。

      Node *Tree::GetFirstNode()
      {
          Node *pN,*child;
          pN=GetRootNode();
          while(NOTNIL(child=GetNodeLeft(pN)))
          {
              pN=child;
          }
          return(pN);
      }
      
      
      Node *Tree::GetNodeNext(Node *pNode)
      {
          Node *temp;
      
          temp=GetNodeRight(pNode);
          if(NOTNIL(temp))
          {
              pNode=temp;
              temp=GetNodeLeft(pNode);
              while(NOTNIL(temp))
              {
                  pNode=temp;
                  temp=GetNodeLeft(pNode);
              }
              return(pNode);
          }
          else
          {
              temp=GetNodeParent(pNode);
              while( (NOTNIL(temp)) && (GetNodeRight(temp)==pNode) )
              {
                  pNode=temp;
                  temp=GetNodeParent(pNode);
              }
              return(temp);
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-01-09
        • 2013-08-16
        • 2021-12-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多