Deleting leftmost leaf in a binary tree

前提是你 提交于 2019-12-11 10:54:06

问题


I wish to delete the leftmost leaf in a BT( not BST!). I tried to delete it using Reverse level order traversal but once I find the leftmost node and delete it and try returning, I cannot handle the extra recursive calls. Basically with the current code, my entire tree gets deleted recursively, but I just want it to delete leftmost leaf.

I tried breaking the recursion using exit() and my entire program stopped.

On first call only 5 should be deleted. On next call 25 should be deleted . On next call 66 should be deleted.( basically swimming up)

This is what my code looks like:

def del_leaf(node):
    if(root==None):
        return

    if root:
        # First recur on left child
        del_leaf(root.left)

        # the recur on right child
        del_leaf(root.right)

        # now print the data of node
        if ( (root.isvisited == False)):
            root.isvisited = True
            root.left = None
            root.right = None
            return


回答1:


The function needs a return value, something that tells the caller:

  1. this node does not exist
  2. this node is childless so you can delete it
  3. OK, we're done.

So, something like

def del_leaf(node):
    if(root==None):
        return 1

    left = del_leaf(root.left)
    if (left == 2):
        root.left = None
        return 3
    if (left == 3):
        return 3

    right = del_leaf(root.right)
    if (right == 2):
        root.right = None
        return 3
    if (right == 3):
        return 3

    return 2

I haven't tested it; that's left as an exercise for you. Also left:

  • better enum names than 1, 2, and 3
  • support for an arbitrary number of child nodes
  • support for an ordering function



回答2:


I have modified the program that does not use recursion to:

  1. Not rely on garbage collection to print out when a node has been removed.
  2. Build the entire tree as presented by the OP.

Note: When the program descends down the left subtree and finds that the leftmost leaf has a right subtree, it removes this subtree. This program does not descend down the right subtree to delete the leftmost leaf of that subtree. The second algorthm does. Take your pick.

class Node:
    def __init__(self, value):
        self.value = value

# build tree
root = Node(90)
# left subtree:
node_50 = Node(50)
node_20 = Node(20)
node_75 = Node(75)
node_5 = Node(5)
node_5.left = None
node_5.right = None
node_25 = Node(25)
node_25.left = None
node_25.right = None
node_66 = Node(66)
node_66.left = None
node_66.right = None
node_80 = Node(80)
node_80.left = None
node_80.right = None
root.left = node_50
node_50.left = node_20
node_50.right = node_75
node_20.left = node_5
node_20.right = node_25
node_75.left = node_66
node_75.right = node_80
# right subtree:
node_150 = Node(150)
node_95 = Node(95)
node_175 = Node(175)
node_92 = Node(92)
node_92.left = None
node_92.right = None
node_111 = Node(111)
node_111.left = None
node_111.right = None
node_166 = Node(166)
node_166.left = None
node_166.right = None
node_200 = Node(200)
node_200.left = None
node_200.right = None
root.right = node_150
node_150.left = node_95
node_150.right = node_175
node_95.left = node_92
node_95.right = node_111
node_175.left = node_166
node_175.right = node_200

def del_leftmost_leaf(root):
    if root.left is None and root.right is None:
        print(f'Removing {root.value}')
        return None # root has changed to None
    parent = root
    if root.left is None:
        node = root.right
    else:
        node = root.left
    while node.left:
        parent = node
        node = node.left
    if node.right is None:
        print(f'Removing {node.value}')
        if parent.left is None:
            parent.right = None
        else:
            parent.left = None
    else:
        print(f'Removing {node.right.value}')
        node.right = None
    return root # root hasn't changed


while root:
    root = del_leftmost_leaf(root)

Prints:

Removing 5
Removing 25
Removing 20
Removing 75
Removing 50
Removing 92
Removing 111
Removing 95
Removing 175
Removing 150
Removing 90

See demo

Second Algortihm

def del_leftmost_leaf(root):
    if root.left is None and root.right is None:
        print(f'Removing {root.value}')
        return None # root has changed to None
    parent = root
    if root.left is None:
        node = root.right
    else:
        node = root.left
    while node.left:
        parent = node
        node = node.left
    if node.right is None:
        print(f'Removing {node.value}')
        if parent.left is None:
            parent.right = None
        else:
            parent.left = None
    else:
        node.right = del_leftmost_leaf(node.right)
    return root # root hasn't changed


while root:
    root = del_leftmost_leaf(root)

Prints:

Removing 5
Removing 25
Removing 20
Removing 66
Removing 80
Removing 75
Removing 50
Removing 92
Removing 111
Removing 95
Removing 166
Removing 200
Removing 175
Removing 150
Removing 90

See demo




回答3:


If you only actually to remove the leftmost leaf node, a simple recursion can handle this problem pretty easily, but you'll need to use a return value from the function to communicate information back up the call stack. I suggest we return True if the current node is the leaf we're searching for, which should be deleted by its parent, and False otherwise:

def del_leaf(node):
    if node is None:
        raise ValueError("Can't remove a leaf node from an empty tree")
    if node.left:      # first recursive case
        if del_leaf(node.left): # if the recursion returns True, our immediate child is a leaf
            node.left = None    # so delete it
        return False
    elif node.right:   # second recursive case
        if del_leaf(node.right):
            node.right = None
        return False
    else:              # base case, we're the leaf!
        return True

Note that a function like this cannot ever delete the root value of a one-element tree, because it don't own the reference to that root node (that's probably in a variable in the calling scope). What it will do is return True to the caller in that case, so the calling code can delete the root reference itself, if necessary.

However, if you're really looking to remove the furthest leaf node from the root, with a preference for the leftmost if there are several leaves at the same depth, then you need a somewhat more sophisticated algorithm.

One way to do it that might be pretty efficient is to make sure each node keeps its height as an attribute. Then we can easily tell which branch of the tree we need to follow to find the leaf we want to remove.

# assume the nodes have a height attribute, leaf nodes have height 0

def update_height(node): # a helper function
    node.height = 1 + max(node.left.height if node.left is not None else -1,
                          node.right.height if node.right is not None else -1)

# use this to initially set up heights (if you don't build them into the tree structure)
def setup_heights(node):
    if node is None:
        return
    setup_heights(node.left)
    setup_heights(node.right)
    update_height(node)

def del_leaf(node):
    if node is None:
        raise ValueError("Can't remove from an empty tree")
    if node.height == 0:
        return True # the root needs to be removed by the caller

    if self.height == 1: # we're the parent of a leaf, so delete one of our child nodes
        if self.left is not None:
            self.left = None
        else:
            self.right = None
    elif self.left is not None and (self.right is None or
                                    self.left.height >= self.right.height):
        del_leaf(self.left)
    else:
        del_leaf(self.right)

    update_height(node)

    return False


来源:https://stackoverflow.com/questions/58572946/deleting-leftmost-leaf-in-a-binary-tree

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