package com.freetymekiyan.algorithms.level.medium;

import com.freetymekiyan.algorithms.utils.Utils.NestedInteger;

import java.util.ArrayList;
import java.util.List;

/**
 * Given a nested list of integers, return the sum of all integers in the list weighted by their depth.
 * <p>
 * Each element is either an integer, or a list -- whose elements may also be integers or other lists.
 * <p>
 * Different from the previous question where weight is increasing from root to leaf, now the weight is defined from
 * bottom up. i.e., the leaf level integers have weight 1, and the root level integers have the largest weight.
 * <p>
 * Example 1:
 * Given the list [[1,1],2,[1,1]], return 8. (four 1's at depth 1, one 2 at depth 2)
 * <p>
 * Example 2:
 * Given the list [1,[4,[6]]], return 17. (one 1 at depth 3, one 4 at depth 2, and one 6 at depth 1; 1*3 + 4*2 + 6*1 =
 * 17)
 * <p>
 * Company Tags: LinkedIn
 * Tags: Depth-first Search
 * Similar Problems: (E) Nested List Weight Sum
 */
public class NestedListWeightSum2 {

    /**
     * DFS.
     * Instead of level, pass the integer sum of this level to the next level.
     * Recurrence Relation:
     * sum = this level's integers' sum + all previous levels integers' sum + all following levels integers' sum.
     * Base case:
     * If list is empty, return 0.
     */
    public int depthSumInverse(List<NestedInteger> nestedList) {
        return dfs(nestedList, 0);
    }

    /**
     * DFS.
     * Create next level's list.
     * For each NestedInteger ni in nestedList:
     * | If ni.isInteger():
     * |   Add it to prev.
     * | Else:
     * |   Add ni.getList() to next level.
     * Return previous sum + recursive call sum.
     */
    private int dfs(List<NestedInteger> nestedList, int prev) {
        if (nestedList.isEmpty()) {
            return 0;
        }
        List<NestedInteger> nextLevel = new ArrayList<>();
        for (NestedInteger ni : nestedList) {
            if (ni.isInteger()) {
                prev += ni.getInteger();
            } else {
                nextLevel.addAll(ni.getList());
            }
        }
        return prev + dfs(nextLevel, prev);
    }

    /**
     * DFS. Iterative.
     * Add integer in list multiple times.
     * Sum of current level is: sum of all integers in this level + sum of all integers in previous levels.
     * Iterate current list:
     * | For each NestedInteger in nestedList:
     * |   If ni.isInteger(), add it to previous sum
     * |   Else if ni is a list, add its list to next level
     * | Add prev to sum.
     * | nestedList = nextLevel
     * Return sum.
     */
    public int depthSumInverseB(List<NestedInteger> nestedList) {
        int cur = 0, sum = 0;
        while (!nestedList.isEmpty()) {
            List<NestedInteger> nextLevel = new ArrayList<>();
            for (NestedInteger ni : nestedList) {
                if (ni.isInteger()) {
                    cur += ni.getInteger();
                } else {
                    nextLevel.addAll(ni.getList());
                }
            }
            sum += cur;
            nestedList = nextLevel;
        }
        return sum;
    }
}


update nested list weight sum 2