/**
  * Returns the true if the differences of heights of subtrees at every node is no greater than
  * one.
  *
  * @return boolean
  */
 public boolean isBalanced() {
   if (isLeaf()) {
     return true;
   } else if (left() == null && right.isLeaf()) {
     return true;
   } else if (right() == null && left.isLeaf()) {
     return true;
   } else if (Math.abs(left().height() - right().height()) <= 1) {
     return true;
   } else return false;
 }
 /**
  * Returns the maximum path length to a descendent.
  *
  * @return int height
  */
 public int height() {
   if (isLeaf()) {
     return 0;
   } else if (left == null) {
     return 1 + right().height();
   } else if (right == null) {
     return 1 + left.height();
   } else {
     return 1 + (Math.max(left.height(), right.height()));
   }
 }
 /**
  * Returns true if the difference in heights between the branches are <1 and if any and all holes
  * appear in the right branches.
  *
  * @return boolean
  */
 public boolean isComplete() {
   if (isLeaf()) {
     return true;
   } else if ((left() == null && right.isLeaf()) || (right() == null && left.isLeaf())) {
     return true;
   } else if (left().isComplete() && right().isFull()) {
     return true;
   } else if (left().isFull() && right.isComplete()) {
     return true;
   } else return false;
 }
  /**
   * Returns a string representation of the binary tree.
   *
   * @return String string represenation
   */
  public String toString() {
    if (isLeaf()) {
      return value + "()";
    } else {
      String str = " " + value;
      if (left() != null && right() != null) {
        str += " (" + left().toString() + ", " + right().toString() + ")";
      } else {
        if (left() != null) {
          str += "(" + left().toString() + ", ())";
        } else if (right() != null) {
          str += "((), " + right.toString() + ")";
        }
      }

      return str;
    }
  }