Woodstock Blog

a tech blog for general algorithmic interview questions

[LeetCode 88] Merge Sorted Array

Question

link

Given two sorted integer arrays A and B, merge B into A as one sorted array.

Note:
You may assume that A has enough space (size that is greater or equal to m + n) to hold additional elements from B. The number of elements initialized in A and B are m and n respectively.

Stats

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

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

Analysis

This is an easy question.

Solution

Just insert from the end to the head.

Code

public void merge(int A[], int m, int B[], int n) {
    int p1 = m - 1, p2 = n - 1;
    for (int i = m + n - 1; i >= 0; i --){
        if (p2 == -1) return;
        if (p1 == -1) {
            for (int j = 0; j <= p2; j++) A[j] = B[j];
            return;
        }
        if (A[p1] > B[p2]) A[i] = A[p1--];
        else A[i] = B[p2--];
    }
}

[LeetCode 84] Largest Rectangle in Histogram

Question

link

Given n non-negative integers representing the histogram's bar height where the width of each bar is 1, find the area of largest rectangle in the histogram.


Above is a histogram where width of each bar is 1, given height = [2,1,5,6,2,3].


The largest rectangle is shown in the shaded area, which has area = 10 unit.

For example,
Given height = [2,1,5,6,2,3],
return 10.

Stats

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

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

Analysis

This is an extremely difficult question.

The idea of the solution (using stack) seems understandable, but can be very tricky when coding.

The basic idea is, always keep increasing elements in the stack. When I see a decrease in number, pop stack. And I calculate max area only when poping elements. In the end, a ‘0’ is inserted to the end, so that all stack item will be popped (and at the same time, max area been calculated).

Sorry if I did not explain well enough. Here is a better one:

We traverse all bars from left to right, maintain a stack of bars. Every bar is pushed to stack once. A bar is popped from stack when a bar of smaller height is seen. When a bar is popped, we calculate the area with the popped bar as smallest bar. How do we get left and right indexes of the popped bar – the current index tells us the ‘right index’ and index of previous item in stack is the ‘left index’. Following is the complete algorithm.

1) Create an empty stack.

2) Start from first bar, and do following for every bar ‘hist[i]‘ where ‘i’ varies from 0 to n-1.
……a) If stack is empty or hist[i] is higher than the bar at top of stack, then push ‘i’ to stack.
……b) If this bar is smaller than the top of stack, then keep removing the top of stack while top of the stack is greater. Let the removed bar be hist[tp]. Calculate area of rectangle with hist[tp] as smallest bar. For hist[tp], the ‘left index’ is previous (previous to tp) item in stack and ‘right index’ is ‘i’ (current index).

3) If the stack is not empty, then one by one remove all bars from stack and do step 2.b for every removed bar.

Time complexity of the stack solution is O(n). (Another algo analysis article here)

Solution

I wrote the code using idea from blog. It works, but there are 2 things that I got wrong.

First, if elements are equal. I shall also push it. I can not simply skip it, I don’t know why!

if (stack.isEmpty() || cur >= height[stack.peek()])

Second, about how to calculate the width of the rectangle. I used this before:

int width = stack.isEmpty() ? p : p - h;

It’s wrong, I must get the value of next element from stack, and then calculate width. Why? there might be element been popped before, which locate between these 2 elements in stack.

int width = stack.isEmpty() ? p : p - stack.peek() - 1;

Updated on July 4th, 2014: the above 2 points are very valid, especially the second. Keep in mind:

  1. The values in stack means the last position that a height can be found. For example, height is (2, 10, 5) then the stack would have (2, removed, 5). When calculating the area for height 5, we should note the removed space is in consideration as well.

  2. So, when equal height is found, pop it. Because you won’t need it any more. The stack only stores last position this height is found.

  3. When stack is empty, the width of rectange should be calculated from 0.

Code

My code written on July 4th, 2014

public int largestRectangleArea(int[] height) {
    if (height == null || height.length == 0) {
        return 0;
    }
    Stack<Integer> stack = new Stack<Integer>();
    stack.add(0);
    int len = height.length;
    int area = 0;
    for (int i = 1; i <= len; i++) {
        int h = i == len ? 0 : height[i];
        // pop a element and calculate its max area
        // pop until the top element is smaller than h, then push h
        while (!stack.isEmpty() && h <= height[stack.peek()]) {
            int pos = stack.pop();
            int width = stack.isEmpty() ? i : i - stack.peek() - 1;
            area = Math.max(area, height[pos] * width);
        }
        stack.push(i);
    }
    return area;
}

My code written on July 18th, 2014

public int largestRectangleArea(int[] height) {
    if (height == null || height.length == 0) {
        return 0;
    }
    int maxArea = Integer.MIN_VALUE;
    Stack<Integer> stack = new Stack<Integer>();
    int p = 0;
    while (p < height.length) {
        if (stack.isEmpty() || height[stack.peek()] <= height[p]) {
            stack.push(p);
            p++;
        } else {
            int h = stack.pop();
            int w = stack.isEmpty() ? p : p - stack.peek() - 1;
            int area = height[h] * w;
            maxArea = Math.max(maxArea, area);
        }
    }
    while (!stack.isEmpty()) {
        int h = stack.pop();
        int w = stack.isEmpty() ? p : p - stack.peek() -1;
        int area = height[h] * w;
        maxArea = Math.max(maxArea, area);
    }
    return maxArea;
}

[LeetCode 89] Gray Code

Question

link

The gray code is a binary numeral system where two successive values differ in only one bit.

Given a non-negative integer n representing the total number of bits in the code, print the sequence of gray code. A gray code sequence must begin with 0.

For example, given n = 2, return [0,1,3,2]. Its gray code sequence is:

00 - 0
01 - 1
11 - 3
10 - 2

Note:
For a given n, a gray code sequence is not uniquely defined.

For example, [0,2,3,1] is also a valid gray code sequence according to the above definition.

For now, the judge is able to judge based on one instance of gray code sequence. Sorry about that.

Stats

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

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

Analysis

This is a qure mathematics question.

Gray Code is a very classic binary system, and we shall keep in mind clearly of its mathematical solution.

Solution

My solution is using recursion. First get the answer of input value = (n-1), then from that list, generate answer for input = n. A post talked about this.

The math approach to solve this problem is much more simpler. The (i)th element of Gray Code is calculated by the following method (I learnt from this blog):

binaryToGray = (i >> 1) ^ i;

For example,

00 -> 00

01 -> 01

10 -> 11

11 -> 10

Code

First, my solution

public ArrayList<Integer> grayCode(int n) {
    ArrayList<Integer> ans = new ArrayList<Integer>();
    if (n == 0) {
        ans.add(0);
        return ans;
    }
    ArrayList<Integer> half = grayCode(n-1);
    ans.addAll(half);
    for (int i = half.size() - 1; i >= 0; i -- ) 
        ans.add(half.get(i) + (int)Math.pow(2, n-1));
    return ans;
}

Second, math solution

public ArrayList<Integer> grayCode(int n) {
    ArrayList<Integer> ans = new ArrayList<Integer>();
    for (int i = 0; i < 1 << n; i ++) 
        ans.add((i >> 1) ^ i);
    return ans;
}

[LeetCode 91] Decode Ways

Question

link

A message containing letters from A-Z is being encoded to numbers using the following mapping:

'A' -> 1
'B' -> 2
...
'Z' -> 26

Given an encoded message containing digits, determine the total number of ways to decode it.

For example,
Given encoded message "12", it could be decoded as "AB" (1 2) or "L" (12).

The number of ways decoding "12" is 2.

Stats

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

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

Analysis

This question is easy to think, but hard to write.

Why? Because there are a lot of cases that the decode ways = 0. For example, input = “02” or “50”. We must handle those cases well. Also, boundary cases can cause some trouble.

Solution

DP solution, the transformation function is:

Count[i] = Count[i-1] if only S[i-1] is valid

Count[i] = Count[i-1] + Count[i-2] if S[i-1] and S[i-2] both valid

Code

First, my code.

public int numDecodings(String s) {
    int len = s.length();
    if (len == 0) return 0;
    // now len >= 1
    int[] dp = new int[len];
    if (s.charAt(0) == '0') dp[0] = 0;
    else dp[0] = 1;
    if (len == 1) return dp[0];
    // now len >= 2
    for (int i = 1; i < len; i ++) {
        if (s.charAt(i) != '0') dp[i] += dp[i-1];
        int doubleDigit = Integer.parseInt(s.substring(i-1, i+1));
        if (s.charAt(i-1) != '0' && 1 <= doubleDigit && doubleDigit <= 26)
            if (i != 1) dp[i] += dp[i-2];
            else dp[i] += 1;
    }
    return dp[len - 1];
}

Second, code from this blog.

The only difference is an additional ‘1’ at position 0 of the dp array. This helps simply the code a lot!

public int numDecodings(String s) {  
    int n = s.length();  
    if (n==0) return 0;  
    int[] dp = new int[n+1];  
    dp[0] = 1;  
    if (isValid(s.substring(0,1))) dp[1] = 1;  
    else dp[1] = 0;  
    for(int i=2; i<=n;i++){  
        if (isValid(s.substring(i-1,i)))  
            dp[i] = dp[i-1];  
        if (isValid(s.substring(i-2,i)))  
            dp[i] += dp[i-2];  
    }  
    return dp[n];  
}  

public boolean isValid(String s){  
    if (s.charAt(0)=='0') return false;  
    int code = Integer.parseInt(s);  
    return code>=1 && code<=26;  
}

[LeetCode 90] Subsets II

Question

link

Given a collection of integers that might contain duplicates, S, return all possible subsets.

Note:

  • Elements in a subset must be in non-descending order.
  • The solution set must not contain duplicate subsets.

For example,
If S = [1,2,2], a solution is:

[
  [2],
  [1],
  [1,2,2],
  [2,2],
  [1,2],
  []
]

Stats

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

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

Analysis

This is a question based on “Subsets” question.

Solution is similar, only need to add features to avoid duplication.

Solution

Based on the solution of previous question, I only add 1 variable called nonDupSize, which is the size that I need to merge new element with. For example, input = {1, 2, 3, 3}

Initialize the subset: {}

Added element “1”: {}, {“1”} (1 more elements)

Added element “2”: {}, {“1”}, {“2”}, {“1”, “2”} (2 more elements)

Added element “3”: {}, {“1”}, {“2”}, {“1”, “2”}, {“3”}, {“1”,“3”}, {“2”,“3”}, {“1”, “2”,“3”} (4 more elements)

Added element “3”: {}, {“1”}, {“2”}, {“1”, “2”}, {“3”}, {“1”,“3”}, {“2”,“3”}, {“1”,“2”,“3”}, {“3”,“3”}, {“1”,“3”,“3”}, {“2”,“3”,“3”}, {“1”,“2”,“3”,“3”} (4 more elements)

So instead of getting every list and merge with new element, I only get the lists of pre-calculated size (from bottom), and merge. Someone is having similar solution using a start-pointer.

Code

my code

public ArrayList<ArrayList<Integer>> subsetsWithDup(int[] num) {
    ArrayList<ArrayList<Integer>> ans = new ArrayList<ArrayList<Integer>>();
    ans.add(new ArrayList<Integer>());
    Arrays.sort(num);
    // In this solution I introduce a new variable: nonDupSize
    int curSize = 1, nonDupSize = 1;
    for (int i = 0; i < num.length; i ++) {
        curSize = ans.size();
        if (i > 0 && num[i] != num[i - 1]) nonDupSize = curSize;
        for (int j = curSize - nonDupSize; j < curSize; j ++) {
            ArrayList<Integer> cur = new ArrayList<Integer>(ans.get(j));
            cur.add(num[i]);
            ans.add(cur);
        }
    }
    return ans;
}

Updated on June 12th - solution using the ‘Permutation Model’.

public List<List<Integer>> subsetsWithDup(int[] num) {
    List<List<Integer>> result = new ArrayList<List<Integer>>();
    if(num == null || num.length == 0) {
        return result;
    }
    Arrays.sort(num);
    helper(result, new LinkedList<Integer>(), num, 0);
    return result;
}

private void helper(List<List<Integer>> ans, List<Integer> path, int[] num, int position) {
    ans.add(new LinkedList<Integer>(path));
    for (int i = position; i < num.length; i ++) {
        if (i > position && num[i - 1] == num[i]) {
            // if duplicate, only append num[position]
            continue;
        }
        path.add(num[i]);
        helper(ans, path, num, i + 1);
        path.remove(path.size() - 1);
    }
}

[LeetCode 78] Subsets

Question

link

Given a set of distinct integers, S, return all possible subsets.

Note:

  • Elements in a subset must be in non-descending order.
  • The solution set must not contain duplicate subsets.

For example,
If S = [1,2,3], a solution is:

[
  [3],
  [1],
  [2],
  [1,2,3],
  [1,3],
  [2,3],
  [1,2],
  []
]

Stats

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

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

Solution

This is a classic DFS problem. This problem is easily solved by recursive calls. But recursion is always not the best solution.

A popular solution is (since elements got no duplications) adding elements 1 by 1. That is to say, get previous answer, add each list by a new element, and then add each new list back to answer. Continue until all elements are added in sequence. For example:

Initialize the subset: {}

Added element “1”: {}, {“1”}

Added element “2”: {}, {“1”}, {“2”}, {“1”, “2”}

Added element “3”: {}, {“1”}, {“2”}, {“1”, “2”}, {“3”}, {“1”,“3”}, {“2”,“3”}, {“1”, “2”,“3”}

I found a very interesting bit operation solution, and I will also post it below.

Code

First, recursion. This solution is trivial and boring.

public ArrayList<ArrayList<Integer>> subsets(int[] S) {
    ArrayList<ArrayList<Integer>> ans = new ArrayList<ArrayList<Integer>>();
    if (S.length == 0) {
        ans.add(new ArrayList<Integer>());
        return ans;
    }
    Arrays.sort(S);
    int head = S[0];
    int[] theRest = new int[S.length - 1];
    for (int i = 0; i < theRest.length; i++) {
        theRest[i] = S[i + 1];
    }
    ArrayList<ArrayList<Integer>> subAns = subsets(theRest);
    for (ArrayList<Integer> oneSet: subAns) {
        ArrayList<Integer> longerOneSet = new ArrayList<Integer>();
        longerOneSet.add(head);
        for (Integer a: oneSet) {
            longerOneSet.add(a);
        }
        ans.add(longerOneSet);
        ans.add(oneSet);
    }
    return ans;
}

Second, add elements 1 by 1

public ArrayList<ArrayList<Integer>> subsets(int[] S) {
    ArrayList<ArrayList<Integer>> ans = new ArrayList<ArrayList<Integer>>();
    ans.add(new ArrayList<Integer>());
    Arrays.sort(S);
    for (int i = 0; i < S.length; i ++) {
        int curSize = ans.size();
        for (int j = 0; j < curSize; j ++) {
            ArrayList<Integer> cur = new ArrayList<Integer>(ans.get(j));
            cur.add(S[i]);
            ans.add(cur);
        }
    }
    return ans;
}

Third, bit operations

public ArrayList<ArrayList<Integer>> subsets(int[] S) {  
    ArrayList<ArrayList<Integer>> ans = new ArrayList<ArrayList<Integer>>();  
    Arrays.sort(S);  
    int N = S.length;  
    for (int i = 0; i < Math.pow(2, N); ++i) {  
        ArrayList<Integer> list = new ArrayList<Integer>();  
        for (int j = 0; j < N; ++j) {  
            if ((i & (1 << j)) > 0) {  
                list.add(S[j]);  
            }  
        }  
        ans.add(list);  
    }  
    return ans;  
}  

Updated on June 12th - solution using the ‘Permutation Model’.

public List<List<Integer>> subsets(int[] num) {
    List<List<Integer>> result = new ArrayList<List<Integer>>();
    if(num == null || num.length == 0) {
        return result;
    }
    Arrays.sort(num);
    helper(result, new LinkedList<Integer>(), num, 0);
    return result;
}

private void helper(List<List<Integer>> ans, List<Integer> path, int[] num, int position) {
    ans.add(new LinkedList<Integer>(path));
    for (int i = position; i < num.length; i ++) {
        path.add(num[i]);
        helper(ans, path, num, i + 1);
        path.remove(path.size() - 1);
    }
}

[LeetCode 81] Search in Rotated Sorted Array II

Question

link

Follow up for "Search in Rotated Sorted Array":
What if duplicates are allowed?

Would this affect the run-time complexity? How and why?

Write a function to determine if a given target is in the array.

Stats

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

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

Analysis

This one is based on the solution of previous question.

The previous question is already very difficult, both the logic and coding part. However, if you solve previous question by yourself, you will do this one easily.

I will pick the 2nd piece of code in previous question, and modify it to solve this problem.

Solution for previous question:

public int search(int[] A, int target) {
    int left = 0;
    int right = A.length - 1;
    while (left <= right) {
        int mid = (left + right) / 2;
        if (target == A[mid]) return mid;
        if (A[left] <= A[mid]) {
            if (A[left] <= target && target <= A[mid]) right = mid;
            else left = mid + 1;
        } else {
            if (A[mid] <= target && target <= A[right]) left = mid;
            else right = mid - 1;
        }
    }
    return -1;
}

Solution

My solution is to skip all duplicated numbers before the while-loop.

The most standard solution is left-pointer incremental. A good code is written from this blog.

Code

First, my solution

public boolean search(int[] A, int target) {
    int len = A.length, L = 0, R = len - 1;
    if (A[L] == A[R]) {
        int duplicate = A[R];
        if (duplicate == target) return true;
        while (L < len && A[L] == duplicate) L ++;
        while (R >= 0 && A[R] == duplicate) R --;
    }
    while (L <= R) {
        // Avoid overflow, same as M=(L+R)/2
        int M = L + ((R - L) / 2);
        if (A[M] == target) return true;
        // the bottom half is sorted
        if (A[L] <= A[M])
            if (A[L] <= target && target < A[M]) R = M - 1;
            else L = M + 1;
        // the upper half is sorted
        else 
            if (A[M] < target && target <= A[R]) L = M + 1;
            else R = M - 1;
    }
    return false;
}

Second, standard solution

public boolean search(int[] A, int target) {
    int len = A.length, L = 0, R = len - 1;
    while (L <= R) {
        int M = L + ((R - L) / 2);
        if (A[M] == target) return true;
        if (A[L] < A[M])
            if (A[L] <= target && target < A[M]) R = M - 1;
            else L = M + 1;
        else if (A[L] > A[M])
            if (A[M] < target && target <= A[R]) L = M + 1;
            else R = M - 1;
        else L ++;
    }
    return false;
}

[LeetCode 82] Remove Duplicates From Sorted List II

Question

link

Given a sorted linked list, delete all nodes that have duplicate numbers, leaving only distinct numbers from the original list.

For example,
Given 1->2->3->3->4->4->5, return 1->2->5.
Given 1->1->1->2->3, return 2->3.

Stats

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

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

Analysis

This is a very important question. Solution is a little tricky with boundary cases that need consider.

My code works, but there is an extremely good solution posted below.

Code

First, my code

public ListNode deleteDuplicates(ListNode head) {
    ListNode preHead = new ListNode(0);
    preHead.next = head;
    ListNode left = preHead, right = head;
    while (right != null) {
        if (right.next == null || right.val != right.next.val) {
            // next is differnet from previous
            left = right;
            right = right.next;
        }
        else {
            // next is same as previous
            while (right.next != null && right.val == right.next.val) 
                right = right.next;
            left.next = right.next;
            right = right.next;
        }
    }
    return preHead.next;
}

Second, a great solution from this blog.

public ListNode deleteDuplicates(ListNode head) {
    if(head == null) return head;
    ListNode helper = new ListNode(0);
    helper.next = head;
    ListNode pre = helper, cur = head;
    while(cur!=null)
    {
        while(cur.next!=null && pre.next.val==cur.next.val)
            cur = cur.next;
        if (pre.next == cur) 
            pre = pre.next;
        else 
            pre.next = cur.next;
        cur = cur.next;
    }
    return helper.next;
}

[LeetCode 83] Remove Duplicates From Sorted List

Question

link

Given a sorted linked list, delete all duplicates such that each element appear only once.

For example,
Given 1->1->2, return 1->2.
Given 1->1->2->3->3, return 1->2->3.

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 is trivial. The next following up question is not easy.

Code

my code

public ListNode deleteDuplicates(ListNode head) {
    if (head == null) return null;
    ListNode pre = head;
    ListNode post = head;
    while (post != null){
        post = post.next;
        if (post == null || pre.val != post.val){
            pre.next = post;
            pre = post;
        }
    }
    return head;
}

[LeetCode 80] Remove Duplicates From Sorted Array II

Question

link

Follow up for "Remove Duplicates":
What if duplicates are allowed at most twice?

For example,
Given sorted array A = [1,1,1,2,2,3],

Your function should return length = 5, and A is now [1,1,2,2,3].

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 an easy question

Code

my first code

public int removeDuplicates(int[] A) {
    if (A.length == 0) return 0;
    int insert = 1, pre = 0, cur = 1, count = 1;
    while (cur < A.length) {
        if (A[pre] == A[cur]) {
            if (count == 1) {
                count = 2;
                A[insert] = A[pre];
                insert++;
                pre++;
            }
        } else if (A[pre] != A[cur]) {
            count = 1;
            A[insert++] = A[cur];
            pre = cur;
        }
        cur ++;
    }
    return insert;
}

my second code

public int removeDuplicates(int[] A) {
    int len = A.length;
    if (len < 3) return len;
    int left = 0, right = 0;
    boolean dup = false;
    while (right < len) {
        if (right == 0 || A[right] != A[right - 1]) {
            A[left ++] = A[right ++];
            dup = false;
        } else if (! dup) {
            A[left ++] = A[right ++];
            dup = true;
        } else right ++;
    }
    return left;
}