【问题标题】:How do I switch my remove method in a BST from being recursive to being iterative?如何将 BST 中的删除方法从递归切换为迭代?
【发布时间】:2020-03-10 19:10:21
【问题描述】:

我想知道如何将删除方法从递归切换为迭代。我的递归方法工作得很好,但我所有让它迭代的尝试都没有。我哪里出错了,我该如何解决?

所以这是我的递归方法:

public boolean remove(E someElement) {
    return remove(root, someElement);
}
private boolean remove(Node<E> node, E dataItem) {
    if (node == null) {
        return false;
    }
    int val = dataItem.compareTo(node.data);
    if (val < 0)
        return remove(node.left, dataItem);
    else if (val > 0)
        return remove(node.right, dataItem);
    else
        return false;
}

【问题讨论】:

  • BST 的本质是您不会真正迭代地对其进行操作。它会破坏使用 BST 的意义,关键是它可以执行比使用迭代更快、更容易的操作。你打算迭代什么?此外,您可能已经意识到这一点,但您的算法实际上并没有从树中删除节点,即算法实际上没有找到相关节点的情况。有关 BST 上删除(或“删除”)操作的说明,请参见此处:geeksforgeeks.org/binary-search-tree-set-2-delete
  • 请注意,您的方法没有做任何事情
  • 你不能这样做:如果你删除一个节点,你可能需要更改root,但你是按值传递它。在 C/C++ 中做这种事情要容易得多,因为可以获得指向变量的指针。

标签: java algorithm recursion iteration binary-search-tree


【解决方案1】:

我给你算法,你可以自己写代码。
您可以使用 Stack 来遍历树。
所以这里是你如何迭代:

  1. push要堆叠的树
  2. 循环直到堆栈不为空
  3. pop一个节点
  4. 空检查。如果nullcontinue
  5. push 左右子树到Stack

现在在迭代过程中,您只需检查弹出的节点是否就是您要查找的节点。
是的?检查它是否有孩子。

  1. 有孩子吗?像往常一样实现孩子的抢夺逻辑进行递归删除
  2. 没有子节点(又名叶节点)?只需将其分配给null
    打破

没有?继续迭代

虽然我觉得 Trees 本质上是递归的,但就提高对该数据结构的一般工作原理的概念理解而言,使用递归只是一个更好的选择。

【讨论】:

    【解决方案2】:

    如 cmets 中所述,remove 现在什么都不做,可以安全地替换为 return false;

    假设在else 的情况下,您想做一些明智的事情,如

    private boolean remove(Node<E> node, E dataItem) {
        if (node == null) {
            return false;
        }
        int val = dataItem.compareTo(node.data);
        if (val < 0)
            return remove(node.left, dataItem);
        else if (val > 0)
            return remove(node.right, dataItem);
        else
            return do_something(node);
    }
    

    标准策略是将其转换为尾递归。将多个递归调用合并为一个,并使其成为函数中的最后一条语句:

    private boolean remove(Node<E> node, E dataItem) {
        if (node == null) {
            return false;
        }
        int val = dataItem.compareTo(node.data);
        if (val == 0) {
            return do_something(node);
        }
    
        if (val < 0)
            node = node.left;
        else
            node = node.right;
    
        return remove(node);
    }
    

    到目前为止,只是重写了一个实现尾递归的形式。

    现在,任何尾递归函数

    foo(args) {
        if (interesting_condition(args)) {
            return do_something_important(args);
        }
    
        args = recompute_arguments(args);
    
        return foo(args);
    }
    

    可以机械地转化为迭代:

    foo(args) {
        while (!interesting_condition(args)) {
            args = recompute_arguments(args);
        }
        return do_something_important(args);
    }
    

    希望我回答了你的问题。

    【讨论】:

      【解决方案3】:

      BST 操作在 C/C++ 中比在 Java 中更容易迭代执行,因为可以获得指向变量的指针。

      在Java中,您需要区别对待在root处找到元素的情况;在所有其他情况下,您正在考虑的节点位于其父节点的左侧或右侧;因此您可以将 C 的指针(或引用)替换为带有父节点的指针和一个布尔值,指示当前节点在父节点的哪一侧:

      public boolean remove(E someElement) {
          if (root == null) {
              return false;
          }
          int val = someElement.compareTo(root.data);
          if (val < 0) {
              return remove(root, false, someElement);
          } else if (val > 0) {
              return remove(root, true, someElement);
          } else {
              root = removeNode(root);
              return true;
          }
      }
      
      private boolean remove(Node<E> parent, boolean right, E dataItem) {
          Node<E> node = right ? parent.right : parent.left;
          if (node == null) {
              return false;
          }
          int val = dataItem.compareTo(node.data);
          if (val < 0) {
              return remove(node, false, dataItem);
          } else if (val > 0) {
              return remove(node, true, dataItem);
          } else {
              node = removeNode(node);
              if (right) {
                  parent.right = node;
              } else {
                  parent.left = node;
              }
              return true;
          }
      }
      

      我暂时省略了方法removeNode,现在我们可以迭代第二个方法:

      private boolean remove(Node<E> parent, boolean right, E dataItem) {
          while (true) {
              Node<E> node = right ? parent.right : parent.left;
              if (node == null) {
                  return false;
              }
              int val = dataItem.compareTo(node.data);
              if (val < 0) {
                  right = false;
              } else if (val > 0) {
                  right = true;
              } else {
                  node = removeNode(node);
                  if (right) {
                      parent.right = node;
                  } else {
                      parent.left = node;
                  }
                  return true;
              }
              parent = node;
          }
      }
      

      现在removeNode 方法必须删除顶部节点,并在删除后返回新的顶部节点。如果leftrightnull,则可以直接返回另一个节点,否则,我们必须找到一个节点来替换顶部节点,它可以是left子树的最右边的节点,或者right 子树的 leftmode 节点。

      private Node<E> removeNode(Node<E> parent) {
          if (parent.left == null) {
              return parent.right;
          } else if (parent.right == null) {
              return parent.left;
          }
          boolean right = random.nextBoolean();
          Node<E> node = right ? parent.right : parent.left;
          Node<E> last = removeLast(node, !right);
          if (last == null) {
              if (right) {
                  node.left = parent.left;
              } else {
                  node.right = parent.right;
              }
              return node;
          } else {
              last.left = parent.left;
              last.right = parent.right;
              return last;
          }
      }
      
      private Node<E> removeLast(Node<E> parent, boolean right) {
          Node<E> node = right ? parent.right : parent.left;
          if (node == null) {
              return null;
          }
          while (true) {
              Node<E> next = right ? node.right : node.left;
              if (next == null) {
                  break;
              }
              parent = node;
              node = next;
          }
          if (right) {
              parent.right = node.left;
              node.left = null;
          } else {
              parent.left = node.right;
              node.right = null;
          }
          return node;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-08-09
        • 1970-01-01
        • 1970-01-01
        • 2015-01-08
        • 1970-01-01
        • 2017-08-01
        相关资源
        最近更新 更多