【问题标题】:Binary Search Tree Remove二叉搜索树删除
【发布时间】:2012-04-28 21:58:45
【问题描述】:

我目前正在使用 C++ 编写二叉搜索树,并且已经到了必须编写删除/删除函数的阶段(使用递归方法,x = change(x))。我有两个选择:

  • 停在要删除节点的父节点处;

  • 到达要删除的节点,然后调用将
    返回父级

方法 1:成本更低,代码更多

方法 2:代码更少,成本更高

您认为哪种方法更好,为什么?

【问题讨论】:

  • 更多代码永远不是坏事。
  • @Aziz:是的。更多的代码意味着更多的复杂性,这意味着更多的错误,这意味着更难让它正确。第一次通过选择复杂性较低且简单的解决方案。如果计时(分析)表明这还不够好,则优化(从未有过)。

标签: c++ tree binary-search-tree


【解决方案1】:

我不同意这是您仅有的两个选择。

我认为更简单的解决方案是询问每个节点是否应该删除它。如果它决定是,则将其删除并返回应替换它的新节点。如果它决定不,那么它会返回自己。

// pseudo code.
deleteNode(Node* node, int value)
{
    if (node == NULL) return node;

    if (node->value == value)
    {
        // This is the node I want to delete.
        // So delete it and return the value of the node I want to replace it with.
        // Which may involve some shifting of things around.
        return doDelete(node);
    }
    else if (value < node->value)
    {
        // Not node. But try deleting the node on the left.
        // whatever happens a value will be returned that
        // is assigned to left and the tree will be correct.
        node->left = deleteNode(node->left, value);
    }
    else
    {
        // Not node. But try deleting the node on the right.
        // whatever happens a value will be returned that
        // is assigned to right and the tree will be correct.
        node->right = deleteNode(node->right, value);
    }
    // since this node is not being deleted return it.
    // so it can be assigned back into the correct place.
    return node;
}

【讨论】:

    【解决方案2】:

    最好的方法是遍历要删除的节点的父节点,然后删除该子节点。最终使用这种方法你总是访问子节点,因为你总是必须确认子节点是你要删除的节点。

    【讨论】:

      【解决方案3】:

      我发现为树数据结构编写函数的最有效形式通常是以下伪代码格式。

          function someActionOnTree() {
               return someActionOnTree(root)
          }
      
          function someActionOnTree (Node current) {
               if (current is null) {
                    return null
               }
               if (current is not the node I seek) {
                    //logic for picking the next node to move to
                    next node = ...
      
                    next node = someActionOnTree(next node)
               }
               else {
                    // do whatever you need to do with current
                    // i.e. give it a child, delete its memory, etc
                    current = ...
               }
               return current;
          }
      

      此递归函数在数据结构的顶点集上递归。对于算法的每次迭代,它要么寻找一个节点来递归函数,然后用算法在该节点上的迭代值覆盖数据结构对该节点的引用。否则,它会覆盖节点的值(并可能执行一组不同的逻辑)。最后,该函数返回对参数节点的引用,这对于覆盖步骤至关重要。

      这通常是我为 C++ 中的树数据结构找到的最有效的代码形式。这些概念也适用于其他结构 - 您可以使用这种形式的递归,其中返回值始终是对数据结构平面表示中固定点的引用(基本上,始终返回应该在您所在位置的任何内容) '正在看)。

      下面是这种风格在二叉搜索树删除函数中的应用,以修饰我的观点。

      function deleteNodeFromTreeWithValue( value ) {
           return deleteNodeFromTree(root, value)
      }
      
      function deleteNodeFromTree(Node current, value) {
           if (current is null) return null
           if (current does not represent value) {
                if (current is greater than my value) {
                     leftNode = deleteNodeFromTree(leftNode, value)
                } else {
                      rightNode = deleteNodeFromTree(rightNode, value)
                }
            }
            else {
                 free current's memory
                 current = null
            }
            return current
      }
      

      显然,还有许多其他方法可以编写此代码,但根据我的经验,事实证明这是最有效的方法。请注意,覆盖指针并没有真正影响性能,因为硬件已经缓存了节点。如果您正在考虑提高搜索树的性能,我建议您研究专门的树,例如自平衡树(AVL 树)、B 树、红黑树等。

      【讨论】:

      • 删除内存后currentschildren 会发生什么?
      猜你喜欢
      • 2017-07-28
      • 1970-01-01
      • 1970-01-01
      • 2013-11-23
      • 1970-01-01
      • 1970-01-01
      • 2016-01-08
      • 2014-04-19
      相关资源
      最近更新 更多