LeetCode – 31 : Next Permutation

Implement next permutation, which rearranges numbers into the lexicographically next greater permutation of numbers.

If such arrangement is not possible, it must rearrange it as the lowest possible order (ie, sorted in ascending order).

The replacement must be in-place and use only constant extra memory.

Here are some examples. Inputs are in the left-hand column and its corresponding outputs are in the right-hand column.

1,2,3 → 1,3,2
3,2,1 → 1,2,3
1,1,5 → 1,5,1

[code lang="java"]
class Solution {
    public void nextPermutation(int[] nums) {
        
    }
}
[code]

Idea – 1

Idea is to increase the sequence as little as possible. We index from right to left (lsb to msb). Smallest increase is achieved by swapping two elements a_i and a_j such that i > j and a_i < a_j and a_j is the smallest such number in [i-1..0]. After swapping we decrease [i-1..0] as much as possible. For example, in [1, 2, 4, 3], from right to left since [4, 3] is non-decreasing, we cannot find a_i and a_j that meet the requirements. However, 2 is the best candidate for a_i since anything left of 2 will make the increase bigger. The smallest number bigger than 2 and right of 2 is 3, so we swap 2 and 3 and get [1, 3, 4, 2]. Now we decrease right of 3 as much as possible by just reversing it, since right of 3 is guaranteed to be nondecreasing (right to left). Finally we have the next higher permutation [1, 3, 2, 4]. Time complexity is O(n) and space complexity is O(1).
[code lang="java"]
class Solution {
    public void nextPermutation(int[] nums) {
        int n = nums.length;
        int i = n-2;
        while(i >= 0)
        {
            if(nums[i] < nums[i+1])
            {
                break;
            }
            
            --i;
        }
        
        if(i < 0) // highest permutation, cannot increase
        {
            reverse(nums, 0, n-1);
            return;
        }
        
        int j = n-1;
        while(j > i)
        {
            if(nums[j] > nums[i])
            {
                break;
            }
            
            --j;
        }
        
        swap(nums, i, j);
        reverse(nums, i+1, n-1);
    }
    
    private void reverse(int[] A, int begin, int end)
    {
        while(begin < end)
        {
            swap(A, begin++, end--);
        }
    }
    
    private void swap(int[] A, int i, int j)
    {
        int tmp = A[i];
        A[i] = A[j];
        A[j] = tmp;
    }
}
[code]

Runtime: 1 ms, faster than 99.58% of Java online submissions for Next Permutation.Memory Usage: 37.2 MB, less than 94.57% of Java online submissions for Next Permutation.

LeetCode – 22 : Generate Parentheses

Given n pairs of parentheses, write a function to generate all combinations of well-formed parentheses.

For example, given n = 3, a solution set is:

[
  "((()))",
  "(()())",
  "(())()",
  "()(())",
  "()()()"
]
[code lang="java"]
class Solution {
    public List<String> generateParenthesis(int n) {

    }
}
[code]

Idea – 1

If there is an open parenthesis left to take, we take it. If there is a close parenthesis left to take and taking it would not make close count bigger than open count, we take it. Time and space complexity both are proportional to Catalan number (exponential).
[code lang="java"]
class Solution {
    public List<String> generateParenthesis(int n) {
        List<String> ans = new ArrayList<>();
        collect(n, 0, 0, "", ans);
        
        return ans;
    }
    
    private void collect(int n, int openused, int closeused, String current, List<String> result)
    {
        if(openused==n && closeused==n)
        {
            result.add(current);
            return;
        }
        
        if(openused < n)
        {
            collect(n, openused+1, closeused, current+"(", result);
        }
        
        if(closeused < openused)
        {
            collect(n, openused, closeused+1, current+")", result);
        }
    }
}
[code]

Runtime: 1 ms, faster than 96.17% of Java online submissions for Generate Parentheses.Memory Usage: 35.2 MB, less than 100.00% of Java online submissions for Generate Parentheses.

LeetCode – 811 : Subdomain Visit Count

A website domain like “discuss.leetcode.com” consists of various subdomains. At the top level, we have “com”, at the next level, we have “leetcode.com”, and at the lowest level, “discuss.leetcode.com”. When we visit a domain like “discuss.leetcode.com”, we will also visit the parent domains “leetcode.com” and “com” implicitly.

Now, call a “count-paired domain” to be a count (representing the number of visits this domain received), followed by a space, followed by the address. An example of a count-paired domain might be “9001 discuss.leetcode.com”.

We are given a list cpdomains of count-paired domains. We would like a list of count-paired domains, (in the same format as the input, and in any order), that explicitly counts the number of visits to each subdomain.

Example 1: 
Input: ["9001 discuss.leetcode.com"]
Output: ["9001 discuss.leetcode.com", "9001 leetcode.com", "9001 com"]
Explanation: We only have one website domain: "discuss.leetcode.com". As discussed above, the subdomain "leetcode.com" and "com" will also be visited. So they will all be visited 9001 times.
Example 2: 
Input: ["900 google.mail.com", "50 yahoo.com", "1 intel.mail.com", "5 wiki.org"]
Output: ["901 mail.com","50 yahoo.com","900 google.mail.com","5 wiki.org","5 org","1 intel.mail.com","951 com"]
Explanation: We will visit "google.mail.com" 900 times, "yahoo.com" 50 times, "intel.mail.com" once and "wiki.org" 5 times. For the subdomains, we will visit "mail.com" 900 + 1 = 901 times, "com" 900 + 50 + 1 = 951 times, and "org" 5 times.

Notes:

  • The length of cpdomains will not exceed 100
  • The length of each domain name will not exceed 100.
  • Each address will have either 1 or 2 “.” characters.
  • The input count in any count-paired domain will not exceed 10000.
  • The answer output can be returned in any order.

Idea – 1

We break each cpdomain into count and domain. Then in the domain part we process from right to left since all higher level domain has the same count. Since dot count and lengths are limited, this has time and space O(n).
[code lang="java"]
class Solution {
    public List<String> subdomainVisits(String[] cpdomains) {
        List<String> ans = new ArrayList<>();
        if(cpdomains==null || cpdomains.length==0)
        {
            return ans;
        }
        
        int n = cpdomains.length;
        
        HashMap<String, Integer> map = new HashMap<>();
        for(String cp : cpdomains)
        {
            String[] pair = cp.split(" ");
            int count =  Integer.parseInt(pair[0]);
            
            String[] parts = pair[1].split("\\.");
            
            String prev = "";
            for(int i = parts.length-1; i >= 0; --i)
            {
                String key = parts[i];
                if(!prev.isEmpty())
                {
                    key = key+"."+prev;
                }
                
                int oldCount = map.getOrDefault(key, 0);
                map.put(key, oldCount + count);
                
                prev = key;
            }
        }
        
        for(String k : map.keySet())
        {   
            int count =  map.get(k);
            ans.add( String.format("%d %s", count, k) );
        }
        
        return ans;
    }
}
[code]

Runtime: 36 ms, faster than 12.47% of Java online submissions for Subdomain Visit Count.Memory Usage: 39.6 MB, less than 48.68% of Java online submissions for Subdomain Visit Count.

LeetCode – 771 : Jewels and Stones

You’re given strings J representing the types of stones that are jewels, and S representing the stones you have.  Each character in S is a type of stone you have.  You want to know how many of the stones you have are also jewels.

The letters in J are guaranteed distinct, and all characters in J and S are letters. Letters are case sensitive, so"a" is considered a different type of stone from "A".

Example 1:

Input: J = "aA", S = "aAAbbbb" 
Output: 3

Example 2:

Input: J = "z", S = "ZZ" 
Output: 0

Note:

  • S and J will consist of letters and have length at most 50.
  • The characters in J are distinct.
[code lang="java"]
class Solution {
    public int numJewelsInStones(String J, String S) {
        
    }
}
[code]

Idea – 1

While processing letter c in S, we need to answer “Is c a jewel?” To answer efficiently we use hash set (array based) for all the jewels. Time complexity is O( max(|J|, |S|) ) and space complexity is O(1).
[code lang="java"]
class Solution {
    public int numJewelsInStones(String J, String S) {
        boolean[] set = new boolean[128];
        for(int i = 0; i < J.length(); ++i)
        {
            int j = J.charAt(i);
            set[j] = true;
        }
        
        int jewelCount = 0;
        for(int i = 0; i < S.length(); ++i)
        {
            int j = S.charAt(i);
            if(set[j])
            {
                ++jewelCount;
            }
        }
        
        return jewelCount;
    }
}
[code]

Runtime: 1 ms, faster than 98.37% of Java online submissions for Jewels and Stones.Memory Usage: 36.8 MB, less than 87.95% of Java online submissions for Jewels and Stones.

LeetCode – 289 : Game of Life

According to the Wikipedia’s article: “The Game of Life, also known simply as Life, is a cellular automaton devised by the British mathematician John Horton Conway in 1970.”

Given a board with m by n cells, each cell has an initial state live (1) or dead (0). Each cell interacts with its eight neighbors (horizontal, vertical, diagonal) using the following four rules (taken from the above Wikipedia article):

  1. Any live cell with fewer than two live neighbors dies, as if caused by under-population.
  2. Any live cell with two or three live neighbors lives on to the next generation.
  3. Any live cell with more than three live neighbors dies, as if by over-population..
  4. Any dead cell with exactly three live neighbors becomes a live cell, as if by reproduction.

Write a function to compute the next state (after one update) of the board given its current state. The next state is created by applying the above rules simultaneously to every cell in the current state, where births and deaths occur simultaneously.

Example:

Input:  
[  
[0,1,0],  
[0,0,1],  
[1,1,1],  
[0,0,0]
]
Output:
[  
[0,0,0],  
[1,0,1],  
[0,1,1],  
[0,1,0]
]

Follow up:

  1. Could you solve it in-place? Remember that the board needs to be updated at the same time: You cannot update some cells first and then use their updated values to update other cells.
  2. In this question, we represent the board using a 2D array. In principle, the board is infinite, which would cause problems when the active area encroaches the border of the array. How would you address these problems?
[code lang="java"]
class Solution {
    public void gameOfLife(int[][] board) {
        
    }
}
[code]

Idea – 1

We can use another m\times n board to update. Time and space complexity O(m\cdot n).
[code lang="java"]
class Solution {
    public void gameOfLife(int[][] board) {
        if(board==null || board.length==0)
        {
            return;
        }

        int m = board.length;
        int n = board[0].length;
        
        int[][] tmp = new int[m][n];
        for(int row = 0; row < m; ++row)
        {
            for(int col = 0; col < n; ++col)
            {
                int aliveNeighbors = countLiveNeighbors(board, row, col);
                if(board[row][col]==0)
                {
                    tmp[row][col] = (aliveNeighbors==3) ? 1 : 0;
                }
                else
                {
                    tmp[row][col] = (aliveNeighbors==2 || aliveNeighbors==3) ? 1 : 0;
                }
            }
        }
        
        for(int row = 0; row < m; ++row)
        {
            for(int col = 0; col < n; ++col)
            {
                board[row][col] = tmp[row][col];
            }
        }
    }
    
    private int countLiveNeighbors(int[][] board, int row, int col)
    {
        int m = board.length;
        int n = board[0].length;
        
        int live = 0;
        int[] offsets = {-1, 0, 1};
        for(int dr : offsets)
        {
            for(int dc : offsets)
            {
                if(dr==0 && dc==0)
                {
                    continue;
                }
                
                int neighborRow = row+dr;
                int neighborCol = col+dc;
                
                if(neighborRow >= 0 
                   && neighborRow < m 
                   && neighborCol >= 0 
                   && neighborCol < n 
                   && board[neighborRow][neighborCol]==1)
                {
                    ++live;
                }
            }
        }
        
        return live;
    }
}
[code]

Runtime: 0 ms, faster than 100.00% of Java online submissions for Game of Life. Memory Usage: 37.1 MB, less than 84.24% of Java online submissions for Game of Life.

Idea – 2

A cell has two possible current states (alive and dead) and two possible next states. We can represent these four cases with four different numbers other than 0 and 1, and update the input table in place. Later we will do another pass to convert those numbers into 0 and 1.
Current State Next State Code
0 0 -2
0 1 2
1 0 -3
1 1 3
Time complexity is O(m\cdot n) and space is O(1).
[code lang="java"]
class Solution {
    public void gameOfLife(int[][] board) {
        if(board==null || board.length==0)
        {
            return;
        }

        int m = board.length;
        int n = board[0].length;
        
        // (0, 0) : -2
        // (0, 1) :  2
        // (1, 0) : -3
        // (1, 1) :  3
        
        for(int row = 0; row < m; ++row)
        {
            for(int col = 0; col < n; ++col)
            {
                int aliveNeighbors = countLiveNeighbors(board, row, col);
                if(Math.abs(board[row][col])%2==0) // current state == dead
                {
                    board[row][col] = (aliveNeighbors==3) ? 2 : -2;
                }
                else
                {
                    board[row][col] = (aliveNeighbors==2 || aliveNeighbors==3) ? 3 : -3;
                }
            }
        }
        
        for(int row = 0; row < m; ++row)
        {
            for(int col = 0; col < n; ++col)
            {
                board[row][col] = board[row][col]<0 ? 0 : 1; 
            }
        }
    }
    
    private int countLiveNeighbors(int[][] board, int row, int col)
    {
        int m = board.length;
        int n = board[0].length;
        
        int live = 0;
        int[] offsets = {-1, 0, 1};
        for(int dr : offsets)
        {
            for(int dc : offsets)
            {
                if(dr==0 && dc==0)
                {
                    continue;
                }
                
                int neighborRow = row+dr;
                int neighborCol = col+dc;
                
                if(neighborRow >= 0 
                   && neighborRow < m 
                   && neighborCol >= 0 
                   && neighborCol < n 
                   && Math.abs(board[neighborRow][neighborCol])%2==1)
                {
                    ++live;
                }
            }
        }
        
        return live;
    }
}
[code]

Runtime: 0 ms, faster than 100.00% of Java online submissions for Game of Life.Memory Usage: 37.1 MB, less than 91.99% of Java online submissions for Game of Life.

To represent an infinite board, we could store coordinates of the cells that are alive in a HashSet. If a cell dies in the next update that gets removed from the hash set, if a cell becomes alive then it gets added to the hash set. Integer values are bounded by [Integer.MIN_VALUE, Integer.MAX_VALUE], which causes problem for an infinite board. We may think the hash set implicitly represents a m x n board where m is the difference between the smallest y coordinate and the largest y coordinate. and n is the difference between the smallest x coordinate and the largest x coordinate.

LeetCode – 322: Coin Change

You are given coins of different denominations and a total amount of money amount. Write a function to compute the fewest number of coins that you need to make up that amount. If that amount of money cannot be made up by any combination of the coins, return -1.

Example 1:

Input: coins = [1, 2, 5], amount = 11 
Output: 3
Explanation: 11 = 5 + 5 + 1

Example 2:

Input: coins = [2], amount = 3 
Output: -1

Note:
You may assume that you have an infinite number of each kind of coin.

[code lang="java"]
class Solution {
    public int coinChange(int[] coins, int amount) {
        
    }
}
[code]

Idea – 1

Starting with the first coin we try taking it once, twice etc., for each such choice we try taking second coin once, twice, etc. If the smallest coin goes into amount m number of times and there are n coins, time complexity is O(n^m) and space complexity due to recursion stack is O(n).
[code lang="java"]
class Solution {
    public int coinChange(int[] coins, int amount) {
        int n = dfs(coins, 0, amount);
        
        return n == Integer.MAX_VALUE ? -1 : n;
    }
    
    private int dfs(int[] coins, int i, int amount)
    {
        if(i >= coins.length || amount < 0)
        {
            return Integer.MAX_VALUE;
        }
        
        if(amount == 0) // found a change
        {
            return 0;
        }
        
        int takeit = dfs(coins, i, amount-coins[i]);
        if(takeit < Integer.MAX_VALUE)
        {
            // take into account the current coin
            ++takeit;
        }
        
        int leaveit = dfs(coins, i+1, amount);
        
        return Math.min(takeit, leaveit);
    }
}
[code]

Time limit exceeded for the following input:

[122,112,383,404,25,368]
6977

Idea – 2

Looking at dfs(), it has two params of interest i and amount, so there are only n*amount number of distinct calls. We shall cache. Time complexity is O(n\cdot \textrm{amount}) and space complexity is also O(n\cdot \textrm{amount}).
[code lang="java"]
class Solution {
    public int coinChange(int[] coins, int amount) {
        
        if(coins.length == 0)
        {
            return -1;
        }
        
        Integer[][] cache = new Integer[coins.length][amount+1];
        
        int n = dfs(coins, 0, amount, cache);
        
        return n == Integer.MAX_VALUE ? -1 : n;
    }
    
    private int dfs(int[] coins, int i, int amount, Integer[][] cache)
    {
        if(i >= coins.length || amount < 0)
        {
            return Integer.MAX_VALUE;
        }
        
        if(amount == 0) // found a change
        {
            return 0;
        }
        
        if(cache[i][amount] != null)
        {
            return cache[i][amount];
        }
        
        int takeit = dfs(coins, i, amount-coins[i], cache);
        if(takeit < Integer.MAX_VALUE)
        {
            // take into account the current coin
            ++takeit;
        }
        
        int leaveit = dfs(coins, i+1, amount, cache);
        
        cache[i][amount] = Math.min(takeit, leaveit);
        
        return cache[i][amount];
    }
}
[code]

Runtime: 41 ms, faster than 12.06% of Java online submissions for Coin Change.
Memory Usage: 38.3 MB, less than 10.49% of Java online submissions for Coin Change.

Idea – 3

Bottom up dynamic programming, time and space O(n\cdot \textrm{amount}).
[code lang="java"]
class Solution {
    public int coinChange(int[] coins, int amount) {
        
        if(coins.length == 0)
        {
            return -1;
        }
        
        int n = coins.length;
        
        int[][] dp = new int[n][amount+1];
        for(int a = 1; a <= amount; ++a)
        {
            dp[0][a] = (a%coins[0]==0) ? a/coins[0] : Integer.MAX_VALUE;
        }
        
        for(int i = 0; i < n; ++i)
        {
            dp[i][0] = 0;
        }
        
        for(int i = 1; i < n; ++i)
        {
            for(int a = 1; a <= amount; ++a)
            {
                // if taking current coin makes sense
                int takeit = (coins[i] <= a && dp[i][a-coins[i]] != Integer.MAX_VALUE) ? 1+dp[i][a-coins[i]] : Integer.MAX_VALUE;
                int leaveit = dp[i-1][a];
                
                dp[i][a] = Math.min(takeit, leaveit);
            }
        }
        
        return dp[n-1][amount] == Integer.MAX_VALUE ? -1 : dp[n-1][amount];
    }
}
[code]

Runtime: 13 ms, faster than 78.26% of Java online submissions for Coin Change.
Memory Usage: 37.5 MB, less than 90.66% of Java online submissions for Coin Change.

Idea – 4

To update dp[i][a], we need either dp[i-1][a] or dp[i][a-coins[i]], so keeping a single row is enough. Thus time remains O(n\cdot \textrm{amount}), but space becomes O(\textrm{amount}).
[code lang="java"]
class Solution {
    public int coinChange(int[] coins, int amount) {
        
        if(coins.length == 0)
        {
            return -1;
        }
        
        int n = coins.length;
        
        int[] dp = new int[amount+1];
        
        // 0-th coin
        for(int a = 1; a <= amount; ++a)
        {
            dp[a] = (a%coins[0]==0) ? a/coins[0] : Integer.MAX_VALUE;
        }
        
        for(int i = 1; i < n; ++i)
        {
            for(int a = 1; a <= amount; ++a)
            {
                // if taking current coin makes sense
                int takeit = (coins[i] <= a && dp[a-coins[i]] != Integer.MAX_VALUE) ? 1+dp[a-coins[i]] : Integer.MAX_VALUE;
                int leaveit = dp[a];
                
                dp[a] = Math.min(takeit, leaveit);
            }
        }
        
        return dp[amount] == Integer.MAX_VALUE ? -1 : dp[amount];
    }
}
[code]

Runtime: 11 ms, faster than 84.75% of Java online submissions for Coin Change.
Memory Usage: 37 MB, less than 93.91% of Java online submissions for Coin Change.

LeetCode – 11: Container With Most Water

Given n non-negative integers a1a2, …, a, where each represents a point at coordinate (iai). n vertical lines are drawn such that the two endpoints of line i is at (iai) and (i, 0). Find two lines, which together with x-axis forms a container, such that the container contains the most water.

Note: You may not slant the container and n is at least 2.

The above vertical lines are represented by array [1,8,6,2,5,4,8,3,7]. In this case, the max area of water (blue section) the container can contain is 49.

Example:

Input: [1,8,6,2,5,4,8,3,7] 
Output: 49

class Solution {
    public int maxArea(int[] height) {
        
    }
}

Idea - 1

We try all of the possible O(n^2) pairs of bars for the container. Time complexity O(n^2) and space complexity O(1).

class Solution {
    public int maxArea(int[] height) {
        int max = 0;
        
        for(int i = 0; i < height.length-1; ++i)
        {
            for(int j = i+1; j < height.length; ++j)
            {
                int curr = Math.min(height[i], height[j])*(j-i);
                max = Math.max(curr, max);
            }
        }
        
        return max;
    }
}


Runtime: 206 ms, faster than 20.38% of Java online submissions for Container With Most Water.
Memory Usage: 41.3 MB, less than 5.05% of Java online submissions for Container With Most Water.

Idea - 2

We start at two ends because that maximize width. The minimum of the two bars would define the height of the container - this area is best possible for the shorter container. So we move past the shorter. Time O(n) and space O(1).

class Solution {
    public int maxArea(int[] height) {
        int i = 0, j = height.length-1;
        int max = 0;
        
        while(i < j)
        {
            int w = j-i;
            int h = Math.min(height[i], height[j]);
            
            max = Math.max(max, w*h);
            
            if(height[i] == h)
            {
                ++i;
            }
            else
            {
                --j;
            }
        }
        
        return max;
    }
}


Runtime: 2 ms, faster than 97.80% of Java online submissions for Container With Most Water.
Memory Usage: 40.6 MB, less than 22.88% of Java online submissions for Container With Most Water.

LeetCode – 297 : Serialize and Deserialize Binary Tree

Serialization is the process of converting a data structure or object into a sequence of bits so that it can be stored in a file or memory buffer, or transmitted across a network connection link to be reconstructed later in the same or another computer environment.

Design an algorithm to serialize and deserialize a binary tree. There is no restriction on how your serialization/deserialization algorithm should work. You just need to ensure that a binary tree can be serialized to a string and this string can be deserialized to the original tree structure.

Example: 

You may serialize the following tree:     
1
/ \
2 3
/ \
4 5
as "[1,2,3,null,null,4,5]"

Clarification: The above format is the same as how LeetCode serializes a binary tree. You do not necessarily need to follow this format, so please be creative and come up with different approaches yourself.

Note: Do not use class member/global/static variables to store states. Your serialize and deserialize algorithms should be stateless.


/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Codec {

    // Encodes a tree to a single string.
    public String serialize(TreeNode root) {
        
    }

    // Decodes your encoded data to tree.
    public TreeNode deserialize(String data) {
        
    }
}

// Your Codec object will be instantiated and called as such:
// Codec codec = new Codec();
// codec.deserialize(codec.serialize(root));

Idea - 1

In the level order traversal of a full binary tree, each non-null node in a level would have exactly 2 children (can be null). Thus if we keep track of the null children in the serialization, we would be able to recreate the tree during deserialization. For an n-node tree, time and space complexity is O(n).

public class Codec {

    // Encodes a tree to a single string.
    public String serialize(TreeNode root) {
        
        if(root==null)
        {
            return "null";
        }
        
        StringBuilder sb = new StringBuilder();
        Deque<TreeNode> q = new ArrayDeque<>();
        q.addLast(root);
        
        sb.append(String.format("%s,", root.val));
        
        while(!q.isEmpty())
        {
            TreeNode front = q.removeFirst();
            String leftString = front.left==null ? "null" : String.valueOf(front.left.val);
            sb.append(String.format("%s,", leftString));
            
            if(front.left != null)
            {
                q.addLast(front.left);
            }
            
            String rightString = front.right==null ? "null" : String.valueOf(front.right.val);
            sb.append(String.format("%s,", rightString));
            
            if(front.right != null)
            {
                q.addLast(front.right);
            }
        }
        
        String result = sb.toString();
        
        // skip trailing comma
        return result.substring(0, result.length()-1);
    }

    // Decodes your encoded data to tree.
    public TreeNode deserialize(String data) {
        if(data.equals("null"))
        {
            return null;
        }
        
        String[] nodes = data.split(",");
        int i = 0;
        TreeNode root = new TreeNode(Integer.parseInt(nodes[i++]));
        List<TreeNode> previousLevel = new ArrayList<>();
        previousLevel.add(root);
        
        while(!previousLevel.isEmpty())
        {
            List<TreeNode> currentLevel = new ArrayList<>();
            for(TreeNode p : previousLevel)
            {
                String leftString = nodes[i++];
                if(!leftString.equals("null"))
                {
                    TreeNode leftChild = new TreeNode(Integer.parseInt(leftString));
                    p.left = leftChild;
                    
                    currentLevel.add(leftChild);
                }
                
                String rightString = nodes[i++];
                if(!rightString.equals("null"))
                {
                    TreeNode rightChild = new TreeNode(Integer.parseInt(rightString));
                    p.right = rightChild;
                    
                    currentLevel.add(rightChild);
                }
            }
            
            previousLevel = currentLevel;
        }
        
        return root;
    }
}


Runtime: 53 ms, faster than 17.30% of Java online submissions for Serialize and Deserialize Binary Tree.
Memory Usage: 46.8 MB, less than 5.05% of Java online submissions for Serialize and Deserialize Binary Tree.

Idea - 2

We could use preorder traversal as well, complexity is similar.

    public String serialize(TreeNode root) {
        
        if(root==null)
        {
            return "null";
        }
        
        StringBuilder sb = new StringBuilder();
        
        traversePreorder(root, sb);
        
        String result = sb.toString();
        
        return result.substring(0, result.length()-1);
    }
    
    private void traversePreorder(TreeNode x, StringBuilder sb)
    {
        if(x==null)
        {
            sb.append(String.format("null,"));
            return;
        }
        
        sb.append(String.format("%s,", x.val));
        traversePreorder(x.left, sb);
        traversePreorder(x.right, sb);
    }

    // Decodes your encoded data to tree.
    public TreeNode deserialize(String data) {
        if(data.equals("null"))
        {
            return null;
        }
        
        String[] nodeStrings = data.split(",");
        int[] I = {0};
        return buildFromPreorder(nodeStrings, I);
    }
    
    private TreeNode buildFromPreorder(String[] nodeStrings, int[] I)
    {
        int i = I[0];
        if(i >= nodeStrings.length || nodeStrings[i].equals("null"))
        {
            return null;
        }
        
        TreeNode x = new TreeNode( Integer.parseInt(nodeStrings[i]) );
        ++I[0];
        x.left = buildFromPreorder(nodeStrings, I);
        ++I[0];
        x.right = buildFromPreorder(nodeStrings, I);
        
        return x;
    }
}


Runtime: 44 ms, faster than 18.75% of Java online submissions for Serialize and Deserialize Binary Tree.
Memory Usage: 40.7 MB, less than 41.81% of Java online submissions for Serialize and Deserialize Binary Tree.

LeetCode – 238 : Product of Array Except Self

Given an array nums of n integers where n > 1,  return an array output such that output[i] is equal to the product of all the elements of nums except nums[i].

Example:

Input:  [1,2,3,4] 
Output: [24,12,8,6]

Note: Please solve it without division and in O(n).

Follow up:
Could you solve it with constant space complexity? (The output array does not count as extra space for the purpose of space complexity analysis.)


class Solution {
    public int[] productExceptSelf(int[] nums) {
        
    }
}

Idea - 1

We precompute cumulative products from left to right in l2r and cumulative products from right to left in r2l. Then result[i] = l2r[i-1]*r2l[i+1]. Here i-1 and i+1 can go out of range, if that case we use 1. Time complexity is O(n) and space complexity is O(n).

class Solution {
    public int[] productExceptSelf(int[] nums) {
        int n = nums.length;
        if(n<2)
        {
            return nums;
        }
        
        int[] l2r = new int[n];
        l2r[0] = nums[0];
        for(int i = 1; i < n; ++i)
        {
            l2r[i] = l2r[i-1]*nums[i];
        }
        
        int[] r2l = new int[n];
        r2l[n-1] = nums[n-1];
        for(int i = n-2; i >= 0; --i)
        {
            r2l[i] = nums[i]*r2l[i+1];
        }
        
        int[] result = new int[n];
        for(int i = 0; i < n; ++i)
        {
            int left = (i-1 >= 0) ? l2r[i-1] : 1;
            int right = (i+1 < n) ? r2l[i+1] : 1;
            
            result[i] = left*right;
        }
        
        return result;
    }
}


Runtime: 1 ms, faster than 100.00% of Java online submissions for Product of Array Except Self.
Memory Usage: 42.6 MB, less than 69.83% of Java online submissions for Product of Array Except Self.

Idea - 2

We could use the output array to precompute l2r. Then from right to left we shall be computing the final value for result[i], but to keep track of product in [i+1...n-1] we use another variable. Time O(n) and space O(1).

class Solution {
    public int[] productExceptSelf(int[] nums) {
        int n = nums.length;
        if(n<2)
        {
            return nums;
        }
        
        int[] result = new int[n];
        result[0] = nums[0];
        for(int i = 1; i < n; ++i)
        {
            result[i] = result[i-1]*nums[i];
        }
        
        int right = 1;
        for(int i = n-1; i >= 0; --i)
        {
            int left = (i-1) >= 0 ? result[i-1] : 1;
            result[i] = left*right;
            right *= nums[i];
        }
        
        return result;
    }
}


Runtime: 1 ms, faster than 100.00% of Java online submissions for Product of Array Except Self.
Memory Usage: 42.2 MB, less than 70.06% of Java online submissions for Product of Array Except Self.

LeetCode – 121 : Best Time to Buy and Sell Stock

Say you have an array for which the ith element is the price of a given stock on day i.

If you were only permitted to complete at most one transaction (i.e., buy one and sell one share of the stock), design an algorithm to find the maximum profit.

Note that you cannot sell a stock before you buy one.

Example 1:

Input: [7,1,5,3,6,4] 
Output: 5
Explanation: Buy on day 2 (price = 1) and sell on day 5 (price = 6), profit = 6-1 = 5. Not 7-1 = 6, as selling price needs to be larger than buying price.

Example 2:

Input: [7,6,4,3,1] 
Output: 0
Explanation: In this case, no transaction is done, i.e. max profit = 0.

class Solution {
    public int maxProfit(int[] prices) {
        
    }
}

Idea - 1

We try each of the possible O(n^2) pairs (one for buy and one for sell) and track the max profit. Time complexity O(n^2) and space O(1).

class Solution {
    public int maxProfit(int[] prices) {
        int n = prices.length;
        if(n < 2)
        {
            return 0;
        }
        
        int maxprofit = 0;
        for(int i = 0; i < n-1; ++i)
        {
            for(int j = i+1; j < n; ++j)
            {
                maxprofit = Math.max(maxprofit, prices[j]-prices[i]);
            }
        }
        
        return maxprofit;
    }
}


Runtime: 175 ms, faster than 7.70% of Java online submissions for Best Time to Buy and Sell Stock.
Memory Usage: 39.6 MB, less than 28.63% of Java online submissions for Best Time to Buy and Sell Stock.

Idea - 2

Say we have the min price in [0\ldots i-1] and it is \mu. Now consider i-th day to be the sell day, then the max profit we can make selling on i-th day is prices[i]-\mu. We need to find best of such max profits. Also, for the days that come after i, prices[i] can act like the best selling price (min) so we need to see if we can update \mu with prices[i]. The time complexity is O(n) and space O(1).

class Solution {
    public int maxProfit(int[] prices) {
        int n = prices.length;
        if(n < 2)
        {
            return 0;
        }
        
        int maxprofit = 0;
        int minsofar = prices[0];
        for(int i = 1; i < n; ++i)
        {
            maxprofit = Math.max(maxprofit, prices[i]-minsofar);
            minsofar = Math.min(minsofar, prices[i]);
        }
        
        return maxprofit;
    }
}


Runtime: 1 ms, faster than 81.15% of Java online submissions for Best Time to Buy and Sell Stock.
Memory Usage: 38.7 MB, less than 53.71% of Java online submissions for Best Time to Buy and Sell Stock.