import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import java.util.List;
import java.util.Objects;
public final class Tree {
private final List<List<Integer>> rows;
private Tree(Builder builder) {
rows = builder.rows;
}
public int computeTopDownMaxSum() {
if (rows.isEmpty()) {
return 0;
}
return computeMaxSumIndexed(0, 0, 0);
}
public int computeBottomUpMaxSum() {
if (rows.isEmpty()) {
return 0;
}
return computeBottomUpMaxSum(rows.size() - 1);
}
private int computeBottomUpMaxSum(int rowIndex) {
if (rowIndex == 0) {
return rows.get(0).get(0);
}
for (int columnIndex = 0; columnIndex < rows.get(rowIndex).size() - 1; columnIndex++) {
int first = rows.get(rowIndex).get(columnIndex);
int second = rows.get(rowIndex).get(columnIndex + 1);
int parentIndex = rowIndex - 1;
int parentValue = rows.get(parentIndex).get(columnIndex) + Math.max(first, second);
rows.get(parentIndex).set(columnIndex, parentValue);
}
return computeBottomUpMaxSum(rowIndex - 1);
}
private int computeMaxSumIndexed(int cumulativeSum, int row, int column) {
int currentSum = cumulativeSum + rows.get(row).get(column);
int nextRow = row + 1;
if (nextRow == rows.size()) {
return currentSum;
}
int leftSum = computeMaxSumIndexed(currentSum, nextRow, column);
int rightSum = computeMaxSumIndexed(currentSum, nextRow, column + 1);
return Math.max(leftSum, rightSum);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Tree tree = (Tree) o;
return Objects.equals(rows, tree.rows);
}
@Override
public int hashCode() {
return Objects.hash(rows);
}
public static final class Builder {
private final List<List<Integer>> rows;
private Builder() {
rows = Lists.newArrayList();
}
public static Builder aTreeBuilder() {
return new Builder();
}
public Builder withRow(List<Integer> row) {
Preconditions.checkArgument(row.size() == (rows.size() + 1));
rows.add(row);
return this;
}
public Tree build() {
return new Tree(this);
}
}
}