let minPathSum = function(grid) {
    if(!grid || !grid.length) return 0;
    let maxRow = grid.length, maxCol = grid[0].length;
    
    for (let row = 0; row < maxRow;row++) {
        for (let col = 0; col < maxCol;col++) {
            console.log(row, col);
            if (row === 0) {
                grid[row][col] = grid[row][col] + (grid[row][col - 1] || 0);
            }
            else if (col === 0) {
                grid[row][col] = grid[row][col] + grid[row - 1][col];
            } else {
                grid[row][col] = Math.min(
                    grid[row][col] + grid[row - 1][col], // top
                    grid[row][col] + grid[row][col - 1] // left
                );
            }
        }
    }
    return grid[maxRow - 1][maxCol - 1];
};
Tag: array
let twoSumLessThanK = function(A, K) {
    let max = -1, start = 0, end = A.length - 1;
    A.sort((a, b) => b - a); //?
    while (start < end) {
        let sum = A[start] + A[end];
        if (sum < K) {
            max = Math.max(sum, max);
            end--;
        }
        else {
            start++;
        }
    }
    
    return max;
};
let reorganizeString = function(S) {
    let chars = {}, max = 0;
    for(let i = 0; i < S.length;i++) {
        if (chars[S[i]]) {
            chars[S[i]] = chars[S[i]] + 1;
            max = Math.max(chars[S[i]], max);
        }
        else {
            chars[S[i]] = 1;
        }
    }
    console.log(max, Math.floor((S.length + 1) / 2));
    if (max > (S.length + 1) / 2) {
        return "";
    }
    let result = [], index = 0;
    const keys = Object.keys(chars).sort((a, b) => chars[b] - chars[a]); //?
    while (keys.length) { //?
        const key = keys.shift(); //?
        result[index] = key;
        if (chars[key] - 1 > 0) {
            chars[key] = chars[key] - 1;
            keys.unshift(key); //?
        }
        index += 2; //?
        if (index >= S.length) {
            index = 1;
        }
    }
    return result.join('');
};
let  prisonAfterNDays = function(cells, N) {
    let cache = {},
        counter = 1,
        cycle = false,
        len = 8;
    let dup = [...cells];
    function getNextCycle(data) {
        let lastCycle = data[1];
        let cycleDays = counter - lastCycle;
        console.log(lastCycle, cycleDays);
        let nextCycle = N - ((N - lastCycle) % cycleDays); //?.
        // let nextCycle = counter; //?
        // while (nextCycle + counter < N) {
        //     nextCycle = nextCycle += cycleDays;
        // }
        return nextCycle; //?
    }
    while(counter <= N) {
        let key = dup.join(''),
            data = cache[key],
            row = [];
        if (data && !cycle) {
            cycle = true;
            counter = getNextCycle(data);
        }
        if (data) {
            dup = data[0];
            counter++; //?
        }
        else {
            for (let i = 0; i < len; i++) {
                row[i] = dup[i - 1] === dup[i + 1] ? 1 : 0;
            }
            cache[key] = [[...row], counter];
            dup = cache[key][0];
            counter++; //?
        }
    }
    return dup;
};
let mostVisitedPattern = function(username, timestamp, website) {
    let map = new Map();
    for (let i = 0; i < username.length; i++) {
        if (map.has(username[i])) {
            map.get(username[i]).push([website[i], timestamp[i]]);
        }
        else {
            map.set(username[i], [[website[i], timestamp[i]]]);
        }
    }
    // get all possibilites
    let visitedPattern = {};
    let possibilityMap = new Map();
    for(let [user, visits] of map.entries()) {
        visitedPattern[user] = {};
        const value = visits.sort((a, b) => a[1] - b[1]).map(([a]) => a);
        if ( value.length < 3) continue;
        for (let i = 0; i < value.length - 2; i++) {
            for (let j = i + 1; j < value.length - 1; j++) {
                for (let k = j + 1; k < value.length; k++) {
                    let key = `${value[i]},${value[j]},${value[k]}`;
                    if(possibilityMap.has(key)) {
                        if(!visitedPattern[user][key]) {
                            possibilityMap.set(key, possibilityMap.get(key) + 1);
                            visitedPattern[user][key] = 1;
                        }
                    }
                    else {
                        possibilityMap.set(key, 1);
                        visitedPattern[user][key] = 1;
                    }
                }
            }
        }
    }
    const result = [...possibilityMap];
    return result.sort((a, b) => {
        if(a[1] < b[1]) {
            return 1;
        }
        else if (a[1] > b[1]) {
            return -1;
        }
        return a[0].localeCompare(b[0]);
    }).map(arr => arr[0]).shift().split(','); //?
    // result.pop(); //?
        // .map(arr => arr[0]).pop().split('|'); //?
};
let suggestedProducts = function(products, searchWord) {
    products.sort();
    let result = [];
    for(let i = 0; i < searchWord.length; i++) {
        products = products.filter(p => p[i] === searchWord[i]);
        result.push(products.slice(0, Math.min(3, products.length)));
    }
    return result; //?
};
let suggestedProducts = function(products, searchWord) {
    const MAX_RESULT_COUNT = 3;
    let root = {
        children: {}
    };
    function addToNode(word, index, node) {
        if (index >= word.length) {
            node.end = true;
            return;
        }
        //
        const child = node.children[word[index]];
        if (child) {
            addToNode(word, index + 1, child);
        }
        else {
            let child = {val: word[index], parent: node, children: {}};
            node.children[word[index]] = child;
            addToNode(word, index + 1, child);
        }
    }
    function getWord(leaf) {
        let node = leaf;
        let str = '';
        while (node) {
            if (node.val) str = node.val + str; //?
            node = node.parent; //?
        }
        return str; //?
    }
    function searchProducts(searchString) {
        let words = [];
        let nodes = [...Object.entries(root.children)], len = searchString.length;
        while (nodes.length) {
            let currentNodeLength = nodes.length;
            while (currentNodeLength > 0) {
                currentNodeLength--;
                const node = nodes.pop();
                const [key, val] = node;
                if (len > 0 && key === searchString[searchString.length - len]) {
                    nodes.unshift(...Object.entries(val.children));
                    if (len === 1 && val.end) {
                        words.push(getWord(val));
                    }
                } else if (len <= 0) {
                    if (val.end) words.push(getWord(val));
                    nodes.unshift(...Object.entries(val.children));
                }
            }
            len--;
        }
        return words.sort().slice(0, Math.min(MAX_RESULT_COUNT, words.length));
    }
    for(let product of products) {
        addToNode(product, 0, root);
    }
    let result = [];
    for (let i = 0; i < searchWord.length; i++) {
        result.push(searchProducts(searchWord.substring(0, i + 1))); //?
    }
    return result; //?
};
Categories				
Search Matrix
let searchMatrix = function(matrix, target) {
    if(!matrix.length) return false;
    function search(startRow, endRow, startCol, endCol) {
        console.log(startRow, endRow, startCol, endCol);
        let midRow = Math.floor((startRow + endRow) / 2); //?
        let midCol = Math.floor((startCol + endCol) / 2); //?
        if (startRow < endRow && startCol < endCol) {
            const midValue = matrix[midRow][midCol]; //?
            if(midValue === target) {
                return true;
            }
            if (midValue < target) {
                return search(midRow + 1, endRow, startCol, endCol)
                        || search(startRow, endRow, midCol + 1, endCol);
            }
            else {
                return search(startRow, midRow, startCol, endCol)
                    || search(startRow, endRow, startCol, midCol);
            }
        }
        return false;
    }
    return search(0, matrix.length, 0, matrix[0].length);
};
let topKFrequent = function(words, k) {
    let map = new Map();
    words.forEach(word => {
        if(map.has(word)) {
            map.set(word, map.get(word) + 1);
        }
        else {
            map.set(word, 1);
        }
    });
    return [...map].sort((x, y) => {
        const f1 = x[1], f2 = y[1]
        if (f1 < f2) return 1;
        if (f1 > f2) return -1;
        return x[0].localeCompare(y[0]);
    }).map(x => x[0]).slice(0, k);
};