繁体   English   中英

如何删除二进制搜索树的所有节点

[英]How to delete all nodes of a Binary Search Tree

我正在尝试编写代码以删除BST的所有节点(每个节点只有三个属性,left,right和data,没有父指针)。 以下代码是我想出的,它仅删除树的右半部分,而保留左半部分不变。 我该如何修改它以使左半部分也被删除(以便最终我只剩下没有左或右子树的根节点)?

def delete(root):
    global last
    if root:
     delete(root.left)
     delete(root.right)
     if not (root.left or root.right):
        last = root
     elif root.left == last:
        root.left = None
     else:
        root.right = None

其次,有人可以使用堆栈或其他相关数据结构来建议一种迭代方法吗?

如果要删除两个子树,则无需递归。 只需将root.leftroot.right设置为None然后让垃圾收集器来处理它们。 确实,不必首先设置delete功能,只需设置root = None完成!

编辑:如果您需要对数据值运行清除代码,如果GC做得不够好,您可能需要遍历树以获取所有数据。 确实没有必要删除树中的链接,但是我也将采取适当的措施:

def delete(node):
    if node:
        node.data.cleanup() # run data value cleanup code

        delete(node.left)   # recurse
        delete(node.right)

        node.data = None    # clear pointers (not really necessary)
        node.left = None
        none.right = None

您还询问了遍历树的迭代方法,这种方法稍微复杂一些。 这是一种使用deque (作为堆栈)遍历祖先的方法:

from collections import deque

def delete_iterative(node):
    stack = deque()
    last = None

    # start up by pushing nodes to the stack until reaching leftmost node
    while node:
        stack.append(node)
        node = node.left

    # the main loop
    while stack:
        node = stack.pop()

        # should we expand the right subtree?
        if node.right && node.right != last: # yes
            stack.append(node)
            node = node.right

            while node: # expand to find leftmost node in right subtree
                stack.append(node)
                node = node.left

        else: # no, we just came from there (or it doesn't exist)
            # delete node's contents
            node.data.cleanup()

            node.data = None # clear pointers (not really necessary)
            node.left = None
            node.right = None

            # let our parent know that it was us it just visited
            last = node

Blckknght关于垃圾回收是正确的,但是如果您想进行比示例所建议的更为复杂的清理,或者理解为什么您的代码不起作用,我将提供一个附加的答案:

您的问题似乎是elif node.left == last检查。

我不确定您的last变量是用来做什么的,或者它背后的逻辑是什么。
但是问题在于node.left几乎永远不会等于last (如果两个子节点都已经设置为None ,则仅将节点分配给last变量,对于任何有趣的节点(具有子节点的节点)来说,它们都不是。 )。

如果看一下代码,您会发现如果node.left不等于last只有正确的孩子被设置为None ,因此只有子树的右边被删除。

我不知道python,但这应该可以工作:

def delete(node):
    if node:

        # recurse: visit all nodes in the two subtrees
        delete(node.left)           
        delete(node.right)

        # after both subtrees have been visited, set pointers of this node to None
        node.left = None
        node.right = None

(我把你的重命名的自由root参数node ,因为给该函数的节点不一定是树的根节点。)

使用堆栈的迭代后遍历可能看起来像这样:

def is_first_visit(cur, prev):
    return prev is None or prev.left is cur or prev.right is cur

def visit_tree(root):
    if root:
        todo = [root]
        previous = None
        while len(todo):
            node = todo[-1]
            if is_first_visit(node, previous):
                # add one of our children to the stack
                if node.left: 
                    todo.append(node.left)
                elif node.right:
                    todo.append(node.right)
                # now set previous to ourself and continue
            elif previous is node.left:
                # we've done the left subtree, do right subtree if any
                if node.right:
                    todo.append(node.right)
            else: 
                # previous is either node.right (we've visited both sub-trees)
                # or ourself (we don't have a right subtree)
                do_something(node)
                todo.pop()
            previous = node

do_something做您想调用的“实际上删除此节点”。

您可以通过在每个节点上设置一个属性来声明它是否已经调用过do_something来做一些简单的事情,但是显然,如果您的节点上有__slots__或其他内容,并且您不希望这样做,该方法就不能很好地工作修改节点类型以允许该标志。

我不确定在递归调用之后您对这些条件做什么,但是我认为这应该足够了:

def delete(root):
  if root:
    delete(root.left)
    delete(root.right)

    root = None

正如注释中指出的那样,Python不会通过引用传递参数。 在这种情况下,您可以像这样在Python中完成这项工作:

def delete(root):
  if root:
    delete(root.left)
    delete(root.right)

    root.left = None
    root.right = None

Usage:
delete(root)
root = None

至于迭代方法,您可以尝试一下。 这是伪代码,我不懂python。 基本上,我们进行BF搜索。

delete(root):
  make an empty queue Q
  Q.push(root)
  while not Q.empty:
    c = Q.popFront()
    Q.push(c.left, c.right)
    c = None

同样,如果您将根用作功能,则默认情况下不会修改根,但是它将删除所有其他节点。 您可以在函数调用之后将根设置为“无”,或者删除参数并使用全局根变量。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM