Woodstock Blog

a tech blog for general algorithmic interview questions

[LeetCode 117] Populating Next Right Pointers in Each Node II

Question

link

Follow up for problem "Populating Next Right Pointers in Each Node".

What if the given tree could be any binary tree? Would your previous solution still work?

Note:

  • You may only use constant extra space.

For example,
Given the following binary tree,

         1
       /  \
      2    3
     / \    \
    4   5    7

After calling your function, the tree should look like:

         1 -> NULL
       /  \
      2 -> 3 -> NULL
     / \    \
    4-> 5 -> 7 -> NULL

Stats

Frequency 2
Difficulty 4
Adjusted Difficulty 4
Time to use --------

Ratings/Color = 1(white) 2(lime) 3(yellow) 4/5(red)

Analysis

This is a difficult question.

We can of course do a BFS, but that’s too simple, and it’s not related to previous question, thus Queue is not the answer we’re looking for.

There are 2 ways to solve this problem, recursively and iteratively. The 2 solutions are quite different, I will explain them.

Solution

First solution is non-recursion, which is from this blog. First we declare a pointer ‘cur’ that points to a node. We assume the ‘next-links’ are already built in current level, and our job is to generate the ‘next-links’ for the next level.

In order to do that, we need another 2 pointers in the next level (one head and one tail) to keep track of the position till where we have finished building the links. So ‘cur’ keep traversing thru current level, until it reaches the end. Then, we move ‘cur’ to the head of next level, and continue the process.

The coding part is not difficult, which is not the case for next solution.

Second solution is recursive, and it is written in this blog. Same as previous solution, we assume that current level have all ‘next-links’ ready, and we will build these links for next level.

The way of getting next link for child nodes is a bit different, but I guess that’s fine. The difficult part is during recursive call, I NEED TO CALL THE RECURSION METHOD FOR RIGHT CHILD FIRST, THEN LEFT CHILD. The reason is, the links from left child to right child is going to be used in the recursion call. If we do left first, we will have problem getting current.left.next (which in this case, will be null). I spent a terribly long time debugging this error, until I found a great explanation here.

in your code: connect(root->left); connect(root->right);

which means you first recurs left sub-tree, then right subtree. The problem is the right subtree’s next pointers are not processed. So in your example, 9->next should be 1, however, when you traverse the left sub tree, 9’s next pointer is still NULL. Your code will think 9 is the end of the third level, so the fourth level’s 0 will have its next to be NULL.

Code

First, my initial solution making use of queue. It is around 50ms slower than the other solutions.

public void connect(TreeLinkNode root) {
    if (root == null) return;
    LinkedList<TreeLinkNode> list = new LinkedList<TreeLinkNode>();
    list.add(root);
    while (! list.isEmpty()) {
        LinkedList<TreeLinkNode> newList = new LinkedList<TreeLinkNode>();
        while (! list.isEmpty()) {
            TreeLinkNode node = list.remove();
            if (! list.isEmpty()) node.next = list.get(0);
            if (node.left != null) newList.add(node.left);
            if (node.right != null) newList.add(node.right);
        }
        list = newList;
    }
}

Second, non-recursion solution

public void connect(TreeLinkNode root) {
    TreeLinkNode nextLevelHead = null;
    TreeLinkNode nextLevelTail = null;
    TreeLinkNode cur = root;
    while (cur != null) {
        if (cur.left != null) {
            if (nextLevelHead == null) {
                nextLevelHead = cur.left;
                nextLevelTail = cur.left;
            }
            else {
                nextLevelTail.next = cur.left;
                nextLevelTail = cur.left;
            }
        }
        if (cur.right != null) {
            if (nextLevelHead == null) {
                nextLevelHead = cur.right;
                nextLevelTail = cur.right;
            }
            else {
                nextLevelTail.next = cur.right;
                nextLevelTail = cur.right;
            }
        }
        cur = cur.next;
        if (cur == null) {
            cur = nextLevelHead;
            nextLevelHead = null;
            nextLevelTail = null;
        }
    }
}

Third, recursive solution

public void connect(TreeLinkNode root) {
    if (root == null) return;
    // now find root.next's valid child
    TreeLinkNode n = root.next, nextLevelNext = null;
    while (n != null && nextLevelNext == null) {
        if (n.left != null) {
            nextLevelNext = n.left;
            break;
        }
        if (n.right != null) {
            nextLevelNext = n.right;
            break;
        }
        n = n.next;
    }
    // now nextLevelNext is fetched
    if (root.right != null) 
        root.right.next = nextLevelNext;
    if (root.left != null) 
        root.left.next = root.right == null ? nextLevelNext : root.right;
    // recursion call
    connect(root.right);
    connect(root.left);
}

[LeetCode 116] Populating Next Right Pointers in Each Node

Question

link

Given a binary tree

    struct TreeLinkNode {
      TreeLinkNode *left;
      TreeLinkNode *right;
      TreeLinkNode *next;
    }

Populate each next pointer to point to its next right node. If there is no next right node, the next pointer should be set to NULL.

Initially, all next pointers are set to NULL.

Note:

  • You may only use constant extra space.
  • You may assume that it is a perfect binary tree (ie, all leaves are at the same level, and every parent has two children).

For example,
Given the following perfect binary tree,

         1
       /  \
      2    3
     / \  / \
    4  5  6  7

After calling your function, the tree should look like:

         1 -> NULL
       /  \
      2 -> 3 -> NULL
     / \  / \
    4->5->6->7 -> NULL

Stats

Frequency 3
Difficulty 3
Adjusted Difficulty 2
Time to use --------

Ratings/Color = 1(white) 2(lime) 3(yellow) 4/5(red)

Analysis

Although this question is not hard, it needs a bit thinking.

The important point is, how to effectively make use of the ‘next link’ generated before, to help us solve this problem.

Solution

My solution is actually very good. It pass current node and next node into a method, and then generate links for current node’s children.

There is a even better solution, which directly make use of the ‘next link’ generated already. In fact, it’s same as my solution, except it uses one less variable. Great idea it is!

Code

First, my solution

public void connect(TreeLinkNode root) {
    link(root, null);
}

private void link(TreeLinkNode node, TreeLinkNode rr){
    if (node == null || node.left == null) return;
    node.left.next = node.right;
    if (rr == null) node.right.next = null;
    else node.right.next = rr.left;

    link(node.left, node.left.next);
    link(node.right, node.right.next);
}

Second, better solution

public void connect(TreeLinkNode root) {
    if (root == null || root.left == null || root.right == null) 
        return;
    root.left.next = root.right;
    root.right.next = root.next == null ? null : root.next.left;
    connect(root.left);
    connect(root.right);
}

[LeetCode 113] Path Sum II

Question

link

Given a binary tree and a sum, find all root-to-leaf paths where each path's sum equals the given sum.

For example:
Given the below binary tree and sum = 22,
              5
             / \
            4   8
           /   / \
          11  13  4
         /  \    / \
        7    2  5   1

return

[
   [5,4,11,2],
   [5,8,4,5]
]

Stats

Frequency 2
Difficulty 2
Adjusted Difficulty 3
Time to use --------

Ratings/Color = 1(white) 2(lime) 3(yellow) 4/5(red)

Analysis

This is a standard question

Solution

My code is simple DFS. However, the coding part is not easy. Note that value can be negative, and think about when to add result into list.

Just write it by hand and get an idea of it.

Code

public ArrayList<ArrayList<Integer>> pathSum(TreeNode root, int sum) {
    ArrayList<ArrayList<Integer>> ans = new ArrayList<ArrayList<Integer>>();
    helper(root, sum, new ArrayList<Integer>(), ans);
    return ans;
}

private void helper(TreeNode node, int total, ArrayList<Integer> list, 
                    ArrayList<ArrayList<Integer>> ans) {
    if (node == null) return;
    list.add(node.val);
    if (node.val == total && node.left == null && node.right == null)
        ans.add(new ArrayList(list));
    helper(node.left, total - node.val, list, ans);
    helper(node.right, total - node.val, list, ans);
    list.remove(list.size() - 1);
}

[LeetCode 112] Path Sum

Question

link

Given a binary tree and a sum, determine if the tree has a root-to-leaf path such that adding up all the values along the path equals the given sum.

For example:
Given the below binary tree and sum = 22,
              5
             / \
            4   8
           /   / \
          11  13  4
         /  \      \
        7    2      1

return true, as there exist a root-to-leaf path 5->4->11->2 which sum is 22.

Stats

Frequency 3
Difficulty 1
Adjusted Difficulty 1
Time to use --------

Ratings/Color = 1(white) 2(lime) 3(yellow) 4/5(red)

Analysis

This question can be solved with either DFS or BFS.

Solution

My code is DFS recursion.

I found another BFS non-recursive solution here, but I did not post this code, because this question is too simple.

One more thing, the last line of code has a || operation. Isn’t it duplicate execution? I mean if answer is found in root.left, no need to check root.right.

Actually it’s not duplication. The official doc explains it:

The && and || operators perform Conditional-AND and Conditional-OR operations on two boolean expressions. These operators exhibit “short-circuiting” behavior, which means that the second operand is evaluated only if needed.

Code

public boolean hasPathSum(TreeNode root, int sum) {
    if (root == null) return false;
    if (root.left == null && root.right == null) 
        return (sum == root.val);
    return hasPathSum(root.left, sum - root.val) 
        || hasPathSum(root.right, sum - root.val);
}

[LeetCode 119] Pascal's Triangle II

Question

link

Given an index k, return the kth row of the Pascal's triangle.

For example, given k = 3,
Return [1,3,3,1].

Note:
Could you optimize your algorithm to use only O(k) extra space?

Stats

Frequency 1
Difficulty 2
Adjusted Difficulty
Time to use --------

Ratings/Color = 1(white) 2(lime) 3(yellow) 4/5(red)

Analysis

This is a math question.

First code below is my code.

Second code is a very concise solution from this blog.

Code

First, my code

public ArrayList<Integer> getRow(int rowIndex) {
    ArrayList<Integer> ans = new ArrayList<Integer>();
    if (rowIndex == 0) {
        ans.add(1);
        return ans;
    }
    int[] nodes = new int[rowIndex + 1];
    nodes[0] = 1;
    for (int k = 1; k <= rowIndex; k++) {
        for (int i = k / 2; i >= 1; i--) {
            if (k % 2 == 0 && i == k / 2)
                nodes[i] = 2 * nodes[i - 1];
            else
                nodes[i] = nodes[i - 1] + nodes[i];
        }
        for (int j = k / 2 + 1; j <= k; j++) {
            nodes[j] = nodes[k - j];
        }
    }
    for (Integer a: nodes) ans.add(a);
    return ans;
}

Second, a better version

public ArrayList<Integer> getRow(int rowIndex) {
    ArrayList<Integer> ans = new ArrayList<Integer>();
    for (int j = 0; j <= rowIndex; j ++){
        for (int i = 1; i < ans.size(); i ++)
            ans.set(i-1, ans.get(i-1)+ans.get(i));
        ans.add(0,1);
    }
    return ans;
}

[LeetCode 118] Pascal's Triangle

Question

link

Given numRows, generate the first numRows of Pascal's triangle.

For example, given numRows = 5,
Return

[
     [1],
    [1,1],
   [1,2,1],
  [1,3,3,1],
 [1,4,6,4,1]
]

Stats

Frequency 1
Difficulty 2
Adjusted Difficulty
Time to use --------

Ratings/Color = 1(white) 2(lime) 3(yellow) 4/5(red)

Analysis

This is a math question.

Below is my code.

Code

public ArrayList<ArrayList<Integer>> generate(int numRows) {
    ArrayList<ArrayList<Integer>> ans = new ArrayList<ArrayList<Integer>>();
    if (numRows == 0)
        return ans;
    for (int i = 0; i < numRows; i++){
        ArrayList<Integer> list = new ArrayList<Integer>();
        for (int j = 0; j <= i; j++){
            if (j == 0 || j == i){
                list.add(1);
            } else {
                list.add(ans.get(i-1).get(j-1) + ans.get(i-1).get(j));
            }
        }
        ans.add(list);
    }
    return ans;
}

[LeetCode 115] Distinct Subsequences

Question

link

Given a string S and a string T, count the number of distinct subsequences of T in S.

A subsequence of a string is a new string which is formed from the original string by deleting some (can be none) of the characters without disturbing the relative positions of the remaining characters. (ie, "ACE" is a subsequence of "ABCDE" while "AEC" is not).

Here is an example:
S = "rabbbit", T = "rabbit"

Return 3.

Stats

Frequency 2
Difficulty 4
Adjusted Difficulty 5
Time to use --------

Ratings/Color = 1(white) 2(lime) 3(yellow) 4/5(red)

Analysis

This is an extremely difficult DP question, probably the most difficult DP on leetcode.

Normally, string matching question is best solved with DP, so is this question. The problem is how to construct the Bellman equation (also known as dynamic programming equation).

Updated on June 24th, I listed down one example using S = “abab” and T = “ab”.

{} a b
{} 1 0 0
a 1 1 0
b 1 1 1
a 1 2 1
b 1 2 3

Solution

It took me a really long time to understand, until I read this blog.

Let W(i, j) stand for the number of subsequences of S(0, i) in T(0, j). If S.charAt(i) == T.charAt(j), W(i, j) = W(i-1, j-1) + W(i-1,j); Otherwise, W(i, j) = W(i-1,j).

Two code are posted below, realizing this solution with 2D and 1D array respectively (first code is better).

Code

First, DP code

public int numDistinct(String S, String T) {
    int m = S.length(), n = T.length();
    int[][] dp = new int[m + 1][n + 1];
    for (int i = 0; i <= m; i ++) {
        for (int j = 0; j <= n; j ++) {
            if (j == 0) dp[i][j] = 1;
            else if (i == 0) dp[i][j] = 0;
            else if (S.charAt(i-1) == T.charAt(j-1)) 
                dp[i][j] = dp[i-1][j-1] + dp[i-1][j];
            else
                dp[i][j] = dp[i-1][j];
        }
    }
    return dp[m][n];
}

Second, same solution but reduced 2-D array to 1-D.

Code readability is reduced, however.

public int numDistinct(String S, String T) {
    int m = S.length(), n = T.length();
    int[] dp = new int[n + 1];
    for (int i = 0; i <= m; i ++) {
        for (int j = n; j >= 0; j --) {
            if (j == 0) 
                dp[j] = 1;
            else if (i == 0) 
                dp[j] = 0;
            else if (S.charAt(i-1) == T.charAt(j-1)) 
                dp[j] = dp[j-1] + dp[j];
        }
    }
    return dp[n];
}

[LeetCode 105] Construct Binary Tree From Preorder and Inorder

Question

link

Given preorder and inorder traversal of a tree, construct the binary tree.

Note:
You may assume that duplicates do not exist in the tree.

Stats

Frequency 3
Difficulty 3
Adjusted Difficulty 3
Time to use --------

Ratings/Color = 1(white) 2(lime) 3(yellow) 4/5(red)

Analysis

This is an interesting question.

The key is, the tree header is always the first element in the pre-order traversal. Knowing this is enough to help us divide the lists and solve the question recursively.

About time complexity. According to the analysis, it is O(nlgn) if the tree is balance, and O(n2) if the tree is totally skewed. This article suggests using HashTable for searching, which achieves O(n) efficiency.

We left out some details on how we search the root value’s index in the inorder sequence. How about a simple linear search? If we assume that the constructed binary tree is always balanced, then we can guarantee the run time complexity to be O(N log N), where N is the number of nodes. However, this is not necessarily the case and the constructed binary tree can be skewed to the left/right, which has the worst complexity of O(N2). I quote the entire article below.

A more efficient way is to eliminate the search by using an efficient look-up mechanism such as hash table. By hashing an element’s value to its corresponding index in the inorder sequence, we can do look-ups in constant time. Now, we need only O(N) time to construct the tree, which theoretically is the most efficient way.

Solution

I post my code below. However, a better code is written in this post.

Code

First, my code, it’s 100ms slower than next code.

public TreeNode buildTree(int[] preorder, int[] inorder) {
    int len = preorder.length;
    if (len == 0) return null;
    TreeNode root = new TreeNode(preorder[0]);
    int leftLen = 0;
    while (inorder[leftLen] != preorder[0]) leftLen++;
    int[] leftPreOrder = new int[leftLen];
    int[] rightPreOrder = new int[len - 1 - leftLen];
    int[] leftInOrder = new int[leftLen];
    int[] rightInOrder = new int[len - 1 - leftLen];
    for (int i = 1; i <= leftLen; i ++) {
        leftPreOrder[i-1] = preorder[i];
        leftInOrder[i-1] = inorder[i-1];
    }
    for (int i = leftLen + 1; i < len; i ++) {
        rightPreOrder[i-leftLen-1] = preorder[i];
        rightInOrder[i-leftLen-1] = inorder[i];
    }
    root.left = buildTree(leftPreOrder, leftInOrder);
    root.right = buildTree(rightPreOrder, rightInOrder);
    return root;
}

Second, other ppl’s code

public TreeNode buildTree(int[] preorder, int[] inorder) {
    int preLength = preorder.length;
    int inLength = inorder.length;
    return buildTree(preorder, 0, preLength - 1, inorder, 0, inLength - 1);
}

public TreeNode buildTree(int[] pre, int preStart, int preEnd, int[] in,
        int inStart, int inEnd) {
    if (inStart > inEnd) return null;
    int rootVal = pre[preStart];
    int rootIndex = 0;
    for (int i = inStart; i <= inEnd; i++) {
        if (in[i] == rootVal) {
            rootIndex = i;
            break;
        }
    }
    int len = rootIndex - inStart;
    TreeNode root = new TreeNode(rootVal);
    root.left = buildTree(pre, preStart + 1, preStart + len, in, inStart,
            rootIndex - 1);
    root.right = buildTree(pre, preStart + len + 1, preEnd, in,
            rootIndex + 1, inEnd);
    return root;
}

[LeetCode 106] Construct Binary Tree From Inorder and Postorder

Question

link

Given inorder and postorder traversal of a tree, construct the binary tree.

Note:
You may assume that duplicates do not exist in the tree.

Stats

Frequency 3
Difficulty 3
Adjusted Difficulty 3
Time to use --------

Ratings/Color = 1(white) 2(lime) 3(yellow) 4/5(red)

Analysis

This is exactly the same question as “Construct Binary Tree from Preorder and Inorder”.

Solution

I post the code from this article.

Code

public TreeNode buildTree(int[] inorder, int[] postorder) {
    int inStart = 0, inEnd = inorder.length-1;
    int postStart =0, postEnd = postorder.length-1;
    return buildTree(inorder, inStart, inEnd, postorder, postStart, postEnd);
}

public TreeNode buildTree(int[] inorder, int inStart, int inEnd, 
                          int[] postorder, int postStart, int postEnd){
    if(inStart > inEnd || postStart > postEnd)
        return null;
    int rootValue = postorder[postEnd];
    TreeNode root = new TreeNode(rootValue);
    int k=0;
    for(int i=0; i< inorder.length; i++){
        if(inorder[i]==rootValue){
            k = i;
            break;
        }
    }
    root.left = buildTree(inorder, inStart, k-1, 
        postorder, postStart, postStart+k-(inStart+1));
    root.right = buildTree(inorder, k+1, inEnd, 
        postorder, postStart+k-inStart, postEnd-1);
    return root;
}

[LeetCode 98] Validate Binary Search Tree

Question

link

Given a binary tree, determine if it is a valid binary search tree (BST).

Assume a BST is defined as follows:

  • The left subtree of a node contains only nodes with keys less than the node's key.
  • The right subtree of a node contains only nodes with keys greater than the node's key.
  • Both the left and right subtrees must also be binary search trees.

Stats

Frequency 5
Difficulty 3
Adjusted Difficulty 3
Time to use --------

Ratings/Color = 1(white) 2(lime) 3(yellow) 4/5(red)

Analysis

This is a textbook-like example of recursion.

I solved it using DFS, but there is actually a much more concise solution.

Code

First, my code, it’s basically a in-order traversal.

int num = Integer.MIN_VALUE;

public boolean isValidBST(TreeNode root) {
    if (root == null) return true;
    if(! isValidBST(root.left)) return false;
    if (num >= root.val) return false;
    num = root.val;
    if(! isValidBST(root.right)) return false;
    return true;
}

Second, great solution I found from this post

public static boolean isValidBST(TreeNode root) {
    return validate(root, Integer.MIN_VALUE, Integer.MAX_VALUE);
}

public static boolean validate(TreeNode root, int min, int max) {
    if (root == null) return true;
    if (root.val <= min || root.val >= max)
        return false;
    return validate(root.left, min, root.val) 
        && validate(root.right, root.val, max);
}