COMP 310
Spring 2019

Generic Extended Visitors Deletion Algorithm
for Self-Balancing Trees

Home  Info  Canvas   Java Resources  Eclipse Resources  Piazza

Here's how the self-balancing tree deletion algorithm changes when implemented with a generic lambda-based extended visitor.    Things to note:

 

/**
 * DeleteNAlgo  -  deletes the supplied parameter from a TreeN
 *  preserving balance with a maximum number of elements per node.
 * 
 * Utilizes lambda-based extended visitors
 */
public class DeleteNAlgo2<E> extends ATreeNAlgo<E, Integer, E, TreeN<E>> {
  
  /**
   * Splits child upwards if its state > order and splices it into
   * its parent.  The supplied param is an ISpliceCmd used to do the
   * splicing.
   */
  private SplitUpAndApply<E> splitUpAndSplice;
  
  
  private Comparator<E> comp = new Comparator<E>() {
    public int compare(E o1, E o2) {
      return ((Comparable<E>)o1).compareTo(o2);
    }
  };
  
  /**
   * Constructor for the class
   * @param order -- the max possible number of elements in a node
   */
  public DeleteNAlgo2(int order) {
    System.out.println("DeleteNAlgo2 instantiated.  order = "+order);
    
    splitUpAndSplice = new SplitUpAndApply<E>(order);
    
    setCmd(0, new IExtVisitorCmd<E,Integer, E, TreeN<E>>(){      
      public <T extends IExtVisitorHost<Integer, ? super TreeN<E>>> E apply(Integer index, T host, final E... keys) {
        return keys[0];  // no-op for empty case
      }
    });
    
    setCmd(1, new IExtVisitorCmd<E,Integer, E, TreeN<E>>(){      
      public <T extends IExtVisitorHost<Integer, ? super TreeN<E>>> E apply(Integer index, T host, final E... keys) {
        collapse2Node((TreeN<E>)host);
        return getDefaultCmd().apply(index, (TreeN<E>)host, keys);  // rest is same as default
      }
    });
    
    setDefaultCmd(new IExtVisitorCmd<E,Integer, E, TreeN<E>>(){      
      public <T extends IExtVisitorHost<Integer, ? super TreeN<E>>> E apply(Integer index, final T host, final E... keys) {
        return ((TreeN<E>)host).execute(new ATreeNAlgo<E, Integer, E, ILambda<TreeN<E>,TreeN<E>>>() {
          private ATreeNAlgo<E, Integer, E, ILambda<TreeN<E>,TreeN<E>>> this_helper = this;
          
          //Initializer block
          {            
            setCmd(0, new IExtVisitorCmd<E,Integer, ILambda<TreeN<E>,TreeN<E>>, TreeN<E>>(){      
              public &lt;T extends IExtVisitorHost<Integer, ? super TreeN<E>>> E apply(Integer index, T h, ILambda<TreeN<E>,TreeN<E>>... cmds) {
                return keys[0];
              }
            });
            
            setCmd(1, new IExtVisitorCmd<E,Integer, ILambda<TreeN<E>,TreeN<E>>, TreeN<E>>(){      
              public <T extends IExtVisitorHost<Integer, ? super TreeN<E>>> E apply(Integer index, T h, final ILambda<TreeN<E>,TreeN<E>>... cmds) {
                if (((TreeN<E>)h).getDat(0).equals(keys[0])) {
                  //System.out.println("Found data element!");
                  E d = ((TreeN<E>)h).getDat(0); // get key
                  ((TreeN<E>)h).splitDownAt(0); //transition to empty
                  return d;
                }
                else {
                  //System.err.println("Element "+ key[0] +" not in tree!");
                  cmds[0].apply(h);
                  return ((TreeN<E>)h).getDat(0);
                }
              }
            });
            
            setDefaultCmd(new IExtVisitorCmd<E,Integer, ILambda<TreeN<E>,TreeN<E>>, TreeN<E>>(){      
              public <T extends IExtVisitorHost<Integer, ? super TreeN<E>>> E apply(Integer s_help, final T h, final ILambda<TreeN<E>,TreeN<E>>... cmds) {
                final int x = findX((TreeN<E>)h, s_help, keys[0]);
                TreeN<E> newChild = collapse2Node(((TreeN<E>)h).splitDownAt(x).getChild(x)); // push data down
                E result = newChild.execute(this_helper, new ILambda<TreeN<E>, TreeN<E>>() {
                  /**
                   * @param child a TreeN subtree of h.
                   */
                  public TreeN<E> apply(TreeN<E>... child) {
                    return ((TreeN<E>)h).spliceAt(x, child[0]);
                  }
                });
                h.execute(splitUpAndSplice, cmds);
                return result;
              }
            });
          }
        }, new ILambda<TreeN<E>, TreeN<E>>() {
          public TreeN<E> apply(TreeN<E>... nu) {
            return host;
          }
        });
      }
    });    
  }
  
  
  //------ Utility methods ------------------------------------------------------
  
  /**
   * Utility method to collapses a 2-node tree by splicing it with
   * its two children.
   * @param t -- must be a 2-node tree!
   */
  private final TreeN<E> collapse2Node(TreeN<E> t) {
    t.spliceAt(1,t.getChild(1));
    t.spliceAt(0,t.getChild(0));
    return t;
  }
  
  /**
   * Utility method that finds the index of the data element that
   * either matches the supplied key or is the first element bigger
   * than the key.
   * @param t - an TreeN
   * @param state - the state of the tree
   * @param k - the key to locate
   * @return - the index of the data element such that if k exists in the
   * tree, it is guaranteed to be in the 2-node tree
   * defined by the data at the index and its left and right children.
   */
  private final int findX(TreeN<E> t, int state, E k) {
    for(int i = 0;i< state; i++) {
      if(0 <= comp.compare(t.getDat(i),k)) return i;
    }
    return state-1;
  }
}

Notice the constant casting of the algorithm's host.  This is due to the fact that the decoupling between the command and the main visitor precludes being able to exactly type the command.


© 2019 by Stephen Wong