1

I am heavily struggling with making this method that removes all branches with only one child. For instance, this:

                    +---+
                    | 2 |
                ___ +---+ ___
              /               \
          +---+               +---+
          | 8 |               | 9 |
          +---+               +---+
         /                   /
     +---+               +---+
     | 7 |               | 6 |
     +---+               +---+
    /     \                   \
+---+     +---+               +---+
| 4 |     | 1 |               | 0 |
+---+     +---+               +---+
               \             /     \
               +---+     +---+     +---+
               | 3 |     | 4 |     | 5 |
               +---+     +---+     +---+

becomes...

               +---+
               | 2 |
           ___ +---+ ___
         /               \
     +---+               +---+
     | 7 |               | 0 |
     +---+               +---+
    /     \             /     \
+---+     +---+     +---+     +---+
| 4 |     | 3 |     | 4 |     | 5 |
+---+     +---+     +---+     +---+

I'm not really looking for any exact code solutions, but I'm more interested on how to approach this. My idea is to search the next nodes to see if they have single child nodes. If so, I will remove that node and "reattach" it to to one after that. However, the node after—and so on—may also have only one child. That's where I'm stuck. Is this the right way to think about this problem? If not, how else should I approach this?

Here's my code that is heavily flawed (doesn't work yet; to get idea accross):

 // post: eliminates all branches with one child
 public void tighten() {
     if (overallRoot == null || (overallRoot.left == null && overallRoot.right == null)) {
         return; // If empty or just overallRoot
     } else {
         tighten(overallRoot);
     }
 }

 // helper for tighten
 private void tighten(IntTreeNode root) {
     // checks right side first
     if (root.right.left != null && root.right.right == null) { // if next right is empty
         root.right = root.right.left;
         tighten(root.right);
     } else if (root.right.right != null && root.right.left == null) { // if next left is empty
         root.right = root.right.right;
         tighten(root.right);
     }

     // checks the left side
     if (root.left.left != null && root.left.right == null) { // if next right is empty
         root.left = root.left.left;
         tighten(root.left);
     } else if (root.left.right != null && root.left.left == null) { // if next left is empty
         root.left = root.right.right;
         tighten(root.left);
     }

     // If node with two children
     if (root.left != null && root.right != null) {
         tighten(root.left);
         tighten(root.right);
     }
 }
Jack L.
  • 405
  • 2
  • 13
  • 31
  • I think you should start with bottom-up approach like we do in recursive version of in-order traversal. Then you need to delete node with exact one child and update the child information of current node. – hcl Aug 06 '14 at 05:40

2 Answers2

1

The algorithm would go as follows:

   Perform an inorder traversal of your binary tree
        For every node which has 1 child
            node->parent->child = node->child

The parent->child would be either the left or right child depending on the current nodes location

Devarsh Desai
  • 5,984
  • 3
  • 19
  • 21
  • I'm still having a hard time understanding this, sorry. Could you explain a bit more? – Jack L. Aug 06 '14 at 05:50
  • Sure, start from the base and check the 2 child pointers of every node. If only 1 is null then make the node pointed by the child's not-null the new child of the current nodes parent. It will be the parents left child if the current node is a left child else it will be the parent's right child. Would you like clarification on any specific topic? – Devarsh Desai Aug 06 '14 at 05:53
  • Not for now. I'll try this out tomorrow. Thanks. – Jack L. Aug 06 '14 at 05:58
1

Recursion is certainly the correct way to solve this problem, which you have done. However, this problem gets much easier if you recurse first. Doing this allows you to assume the sub problems are solved when worrying about the main problem. That way you only have to worry about the direct children of root. Here's an implementation:

private static void tighten(IntTreeNode root){
    if (root == null)
        return;

    //Recurse on both children - if either are null, returns immediately
    tighten(root.left);
    tighten(root.right);

    //Try to tighten each child. If it shouldn't be tightened, the exception is thrown.
    //Could be done using if logic, this way is fairly concise 
    //and leaves both of the helper methods for use elsewhere
    try{
        IntTreeNode n = onlyChild(root.left);
        root.left = n;   
    }catch(IllegalArgumentException ioe){}
    try{
        IntTreeNode n = onlyChild(root.right);
        root.right = n;   
    }catch(IllegalArgumentException ioe){}
}

/** Returns the number of direct children of node, or 0 if node == null */
private static int countDirectChildren(IntTreeNode node){
    if (node == null)
        return 0;
    int i = 0;
    if (node.left != null) i++;
    if (node.right != null) i++;
    return i;
}

/** Returns the single child of node. 
 *  Throws an IllegalArgumentException if node has a number of 
 *  children other than 1, or if node is null */
private static IntTreeNode onlyChild(IntTreeNode node) throws IllegalArgumentException{
    if (countDirectChildren(node) != 1) 
        throw new IllegalArgumentException(node + " doesn't have exactly one child");
    if (node.right != null) return node.right;
    else return node.left;
}
Mshnik
  • 7,032
  • 1
  • 25
  • 38
  • What do you think about of https://stackoverflow.com/q/56319407/1410223 –  May 28 '19 at 01:27