@Override
 public V put(K key, V value) {
   Path<TreeNode<K, V>> path = new Path<TreeNode<K, V>>();
   TreeNode<K, V> node = this.findNode(key, path);
   if (node == null) {
     TreeNode<K, V> newLeaf = new TreeNode<K, V>(key, value);
     this.joinTrees(newLeaf, path.getLastParent(), path.getLastSide());
     super.size++;
     return null;
   } else {
     V result = node.getValue();
     node.setValue(value);
     return result;
   }
 }
  /**
   * Deletes a node with the specified key, if one is found, and stores the path traveled path in
   * path
   *
   * @param key Key to remove
   * @param path Traveled path
   * @return The deleted node's value, if no node was found returns null
   */
  protected V delete(K key, Path<TreeNode<K, V>> path) {
    TreeNode<K, V> node = this.findNode(key, path);
    if (node == null) {
      return null;
    }

    V result = node.getValue();
    if (node.getLeftNode() == null) {
      this.joinTrees(node.getRightNode(), path.getLastParent(), path.getLastSide());
    } else if (node.getRightNode() == null) {
      this.joinTrees(node.getLeftNode(), path.getLastParent(), path.getLastSide());
    } else {
      path.addRightStep(node);
      TreeNode<K, V> minNode = this.getLeafBySide(node.getRightNode(), Side.LEFT, path);
      node.setKey(minNode.getKey());
      node.setValue(minNode.getValue());
      this.joinTrees(minNode.getRightNode(), path.getLastParent(), path.getLastSide());
    }
    super.size--;
    return result;
  }