removing a node from a binary search tree using recursion

泪湿孤枕 提交于 2019-12-11 20:55:57

问题


I am working with the following code:

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

which can be found here: https://github.com/heineman/python-algorithms/blob/master/1.%20Log%20N%20Behavior/bst.py#L2

My problem is as follows. Given I want to remove node 14 from this:

I expect it to find the largest value in the left nodetree, which in this case is 13. Then I expect that node with value 14 to now contain value 13, and that node with value 13 should be removed from the node tree.

However, I don't see how the code I copied above is doing that. First, we pass value 14 to the remove function. We pass the root node (8) and the value 14 to the remove_from_parent function. Since the value is greater than the parent's value, we call remove_from_parent recursively passing in the right child (10) and the value 14. Since the value is greater than the parent's value again, we call remove_from_parent again passing in the right child (14) and the value 14. Now we have an equal match, so we call parent.delete which returns the left node (13). So now the callstack had three recursive iterations, in the last sequence it returns node with 13, in previous sequence it returns node with 10 and the initial sequence it returned node with 8. So since the initial call did the following: @root = remove_from_parent(@root, value), I assume @root is now the node with value 13, after all, we are doing assignment "=" so the last call in the recursion overwrites the others. But something seems wrong here. Because node with 13 is not the root. Node with value 8 is the root. What am I missing?


回答1:


At the very last step (when you come to node 14) you get back the output of "delete" which substitutes the to-be-deleted node with one of its children and returns its parent. That means, the node 10 will be returned, and eventually its own parent as well (i.e. node 8).

UPDATE (after looking closer at a bit confusing naming): what "delete" does is substituting the node value (in your case, 14) with one of the children (in this case, 13). Then it returns back the modified node (13). Now remember that this call was made from the previous iteration, and the returned result will simply be the new value for one of the children of the parent node (which will be returned to the previous call). Eventually you'll get to the first call which started with the root.

The confusion in the naming (for me) comes from the word "parent" which actually means node itself.

UPDATE 2: removeFromParent does one of the following things:

  • if the node on which it was called, was None, it returns None.

  • if the node on which it was called, had the value to be removed, it returns the result of "delete" which will only return None if that node had no children, otherwise it will return the node with the values shifted around (node value substituted with one of the branches).

  • otherwise, it changes one of the node's children and returns the node.

When you get to node 10, this is exactly what will happen: it returns node 10, with the modified left branch (which will store the result returned by "delete", i.e. 13). the rest is simply bubbling up.




回答2:


The algorithm implemented by this code is a bit more complicated that it really needs to be. Only the last recursive call to removeFromParent can ever return anything other than the parent value it was passed in. And it will only do that if parent is the value to be returned, and it is a leaf node.

It might be simpler to understand if you used a iterative algorithm instead:

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?


来源:https://stackoverflow.com/questions/26568163/removing-a-node-from-a-binary-search-tree-using-recursion

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!