问题
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