使用递归从二叉搜索树中删除节点

时间:2014-10-25 23:04:49

标签: python binary-search-tree

我正在使用以下代码:

import random
from time import time

class BinaryNode:

    def __init__(self, value = None):
        """Create binary node"""
        self.value = value
        self.left = None
        self.right = None

    def add(self, val):
        """Adds a new node to the tree containing this value"""
        if val <= self.value:
            if self.left:
                self.left.add(val)
            else:
                self.left = BinaryNode(val)
        else:
            if self.right:
                self.right.add(val)
            else:
                self.right = BinaryNode(val)

    def delete(self):
        """
         Remove value of self from BinaryTree. Works in conjunction with remove
         method in BinaryTree
        """

        if self.left == self.right == None:
            return None
        if self.left == None:
            return self.right
        if self.right == None:
            return self.left

        child = self.left
        grandchild = child.right
        if grandchild:
            while grandchild.right:
                child = grandchild
                grandchild = child.right
            self.value = grandchild.value
            child.right = grandchild.left
        else:
            self.left = child.left
            self.value = child.value

        return self

class BinaryTree:

    def __init__(self):
        """Create empty binary tree"""
        self.root = None

    def add(self, value):
        """Insert value into proper location in Binary Tree"""
        if self.root is None:
            self.root = BinaryNode(value)
        else:
            self.root.add(value)

    def contains(self, target):
        """Check whether BST contains target value"""

        node = self.root
        while node:
            if target == node.value:
                return True
            if target < node.value:
                node = node.left
            else:
                node = node.right

        return False

    def remove(self, value):
        """Remove value from tree"""

        if self.root:
            self.root = self.removeFromParent(self.root, value)

    def removeFromParent(self, parent, value):
        """remove value from tree rooted at parent"""
        if parent is None:
            return None

        if value == parent.value:
            return parent.delete()
        elif value < parent.value:
            parent.left = self.removeFromParent(parent.left, value)
        else:
            parent.right = self.removeFromParent(parent.right, value)

        return parent

可在此处找到:https://github.com/heineman/python-algorithms/blob/master/1.%20Log%20N%20Behavior/bst.py#L2

我的问题如下。鉴于我想从中删除节点14:

enter image description here

我希望它能找到左边nodetree中的最大值,在本例中是13.然后我希望值为14的节点现在包含值13,并且应该从节点树中删除值为13的节点

但是,我不知道上面复制的代码是如何做到的。首先,我们将值14传递给remove函数。我们将根节点(8)和值14传递给remove_from_parent函数。由于该值大于父值,我们调用remove_from_parent递归传入右子(10)和值14.由于该值再次大于父值,我们再次调用remove_from_parent传入右子(14)和值14.现在我们有一个相等的匹配,所以我们调用parent.delete,它返回左节点(13)。所以现在callstack有三个递归迭代,在最后一个序列中它返回带有13的节点,在前一个序列中它返回带有10的节点,而初始序列返回带有8的节点。因此,因为初始调用执行了以下操作:@root = remove_from_parent (@root,value),我认为@root现在是值为13的节点,毕竟我们正在做任务&#34; =&#34;所以递归中的最后一次调用会覆盖其他调用。但这里似乎有些不对劲。因为带有13的节点不是根节点。值为8的节点是根。我错过了什么?

2 个答案:

答案 0 :(得分:0)

在最后一步(当您到达节点14时),您将返回“delete”的输出,该输出将其中一个子节点替换为要删除的节点并返回其父节点。这意味着,节点10将被返回,并最终返回它自己的父节点(即节点8)。

UPDATE(在仔细查看有点令人困惑的命名之后):“删除”的作用是用一个子节点(在本例中为13)替换节点值(在您的情况下为14)。然后它返回修改后的节点(13)。现在请记住,此调用是从上一次迭代中进行的,返回的结果将只是父节点之一子节点的新值(将返回到上一次调用)。最终你将进入以root开头的第一个电话。

命名中的混淆(对我来说)来自“父”这个词,它实际上意味着节点本身。

更新2:removeFromParent执行以下操作之一:

  • 如果调用它的节点为None,则返回None。

  • 如果调用它的节点具有要删除的值,则返回“delete”的结果,如果该节点没有子节点则返回None,否则返回带有该节点的节点值移动(节点值用其中一个分支代替)。

  • 否则,它会更改节点的一个子节点并返回节点。

当你到达节点10时,这正是将要发生的事情:它返回节点10,带有修改后的左分支(它将存储由“delete”返回的结果,即13)。其余的只是冒泡。

答案 1 :(得分:0)

此代码实现的算法实际上需要更复杂一些。只有对removeFromParent的最后一次递归调用才能返回除传递它之前的parent值以外的任何内容。只有当parent是要返回的值时它才会返回,并且是一个叶子节点。

如果您使用迭代算法,可能更容易理解:

def remove(self, value):
    if self.root and self.root.value == value:  # special case for removing the root
        self.root = self.root.delete()
        return

    else:                        # general case, removing a child node of some parent
        parent = self.root
        while parent:
            if value < parent.value:
                child = parent.left
                if child and child.value == value:
                    parent.left = child.delete()
                    return
                parent = child
            else:
                child = parent.right
                if child and child.value == value:
                    parent.right = child.delete()
                    return
                parent = child

    # if we get here, value was never found, perhaps raise an exception?