Woodstock Blog

a tech blog for general algorithmic interview questions

[LeetCode 70] Climbing Stairs

Question

link

You are climbing a stair case. It takes n steps to reach to the top.

Each time you can either climb 1 or 2 steps. In how many distinct ways can you climb to the top?

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 DP question.

Solution

My code is simple and concise, better than any other.

So I posted it below.

My code

Original code

public int climbStairs(int n) {
    if (n == 1) return 1;
    if (n == 2) return 2;
    int a = 1;
    int b = 2;
    int sum = 0;
    for (int i=3 ; i<=n ; i++){
        sum = a + b;
        a = b;
        b = sum;
    }
    return sum;
}

Optimized code

public int climbStairs(int n) {
    if (n == 1) return 1;
    int a = 1;
    int sum = 1;
    for (int i=2 ; i<=n ; i++){
        sum += a;
        a = sum - a;
    }
    return sum;
}

[LeetCode 67] Add Binary

Question

link

Given two binary strings, return their sum (also a binary string).

For example,
a = "11"
b = "1"
Return "100".

Stats

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

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

Analysis

This is an easy question. Pure coding.

There are some boundary issues during my first attempt. And special note to the trim() operation after “String.valueOf(ans)”, it’s easy to omit.

My code

public String addBinary(String a, String b) {
    int m = a.length(), n = b.length();
    char[] ans = new char[Math.max(m, n) + 1];
    int p = m - 1, q = n - 1, r = ans.length - 1;
    int carry = 0;
    while (r >= 0) {
        if (p >= 0)
            ans[r] += a.charAt(p--) - '0';
        if (q >= 0)
            ans[r] += b.charAt(q--) - '0';
        int temp = ans[r] + carry;
        ans[r] = (char) (temp % 2 + '0');
        carry = temp / 2;
        r--;
    }
    if (ans[0] == '0')
        ans[0] = ' ';
    return String.valueOf(ans).trim();
}

[LeetCode 63] Unique Paths II

Question

link

Follow up for "Unique Paths":

Now consider if some obstacles are added to the grids. How many unique paths would there be?

An obstacle and empty space is marked as 1 and 0 respectively in the grid.

For example,

There is one obstacle in the middle of a 3x3 grid as illustrated below.

[
  [0,0,0],
  [0,1,0],
  [0,0,0]
]

The total number of unique paths is 2.

Note: m and n will be at most 100.

Stats

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

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

Solution

This is similar question as previous one, but DP solution.

My code

public class Solution {
    public int uniquePathsWithObstacles(int[][] obstacleGrid) {
        int[][] ob = obstacleGrid;
        if (ob == null || ob.length == 0) {
            return 0;
        }
        int m = ob.length;
        int n = ob[0].length;
        int[][] dp = new int[m][n];
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (i == 0 && j == 0) {
                    dp[i][j] = ob[i][j] == 1 ? 0 : 1;
                } else if (i == 0) {
                    dp[i][j] = dp[i][j - 1] * (ob[i][j] == 1 ? 0 : 1);
                } else if (j == 0) {
                    dp[i][j] = dp[i - 1][j] * (ob[i][j] == 1 ? 0 : 1);
                } else {
                    if (ob[i][j] == 1) {
                        dp[i][j] = 0;
                    } else {
                        dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
                    }
                }
            }
        }
        return dp[m - 1][n - 1];
    }
}

[LeetCode 62] Unique Paths

Question

link

A robot is located at the top-left corner of a m x n grid (marked 'Start' in the diagram below).

The robot can only move either down or right at any point in time. The robot is trying to reach the bottom-right corner of the grid (marked 'Finish' in the diagram below).

How many possible unique paths are there?


Above is a 3 x 7 grid. How many possible unique paths are there?

Note: m and n will be at most 100.

Stats

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

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

Solution

This is an easy question.

Basically to walk from (0,0) to (m,n), robot have to walk down (m-1) steps and rightward (n-1) steps. Then this problem simply becomes Number of k-combinations (also known as choose m from n problem). The code is just concise and short.

My code

public class Solution {
    public int uniquePaths(int m, int n) {
        m--;
        n--;
        if (m < 0 || n < 0) {
            return 0;
        } else if (m == 0 || n == 0) {
            return 1;
        }
        long sum = 1;
        // the answer would be "choose m from (m + n)"
        if (m > n) {
            int temp = m;
            m = n;
            n = temp;
        }
        int num = m + n;
        for (int i = 0; i < m; i++) {
            sum *= (num - i);
        }
        for (int i = 0; i < m; i++) {
            sum /= (i + 1);
        }
        return (int) sum;
    }
}

[LeetCode 64] Minimum Path Sum

Question

link

Given a m x n grid filled with non-negative numbers, find a path from top left to bottom right which minimizes the sum of all numbers along its path.

Note: You can only move either down or right at any point in time.

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 easy DP problem.

But this is not a boring question because I found a few interesting solutions.

Solution

Code 1 is 2-D DP.

2nd code is from this blog. Instead of 2-D array, it simply uses a “rotational array” (滚动数组), or 1-D array.

3rd code is using no extra space. It works in place.

My code

2D array

public class Solution {
    public int minPathSum(int[][] grid) {
        if (grid == null || grid.length == 0) {
            return 0;
        }
        int m = grid.length;
        int n = grid[0].length;
        int[][] dp = new int[m][n];
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (i == 0 && j == 0) {
                    dp[i][j] = grid[i][j];
                } else if (i == 0) {
                    dp[i][j] = grid[i][j] + dp[i][j - 1];
                } else if (j == 0) {
                    dp[i][j] = grid[i][j] + dp[i - 1][j];
                } else {
                    dp[i][j] = grid[i][j] + Math.min(dp[i - 1][j], dp[i][j - 1]);
                }
            }
        }
        return dp[m - 1][n - 1];
    }
}

1-D array

public int minPathSum(int[][] grid) {
    if (grid.length == 0) return 0;
    if (grid[0].length == 0) return 0;
    int[] dp = new int[grid[0].length];
    for (int i = 0; i < grid.length; i ++) 
        for (int j = 0; j < grid[0].length; j ++) 
            if (i == 0 && j == 0) dp[j] = grid[0][0];
            else if (i == 0) dp[j] = dp[j-1] + grid[i][j];
            else if (j == 0) dp[j] += grid[i][j];
            else dp[j] = Math.min(dp[j-1], dp[j]) + grid[i][j];
    return dp[dp.length-1];
}

in-place

public int minPathSum(int[][] grid) {
    if (grid.length == 0) return 0;
    if (grid[0].length == 0) return 0;
    for (int i = 0; i < grid.length; i ++) 
        for (int j = 0; j < grid[0].length; j ++) 
            if (i == 0 && j == 0) continue;
            else if (i == 0) grid[i][j] = grid[i][j] + grid[i][j-1];
            else if (j == 0) grid[i][j] = grid[i][j] + grid[i-1][j];
            else grid[i][j] = Math.min(grid[i-1][j], grid[i][j-1]) + grid[i][j];
    return grid[grid.length-1][grid[0].length-1];
}

[LeetCode 53] Maximum Subarray

Question

link

Find the contiguous subarray within an array (containing at least one number) which has the largest sum.

For example, given the array [−2,1,−3,4,−1,2,1,−5,4],
the contiguous subarray [4,−1,2,1] has the largest sum = 6.

More practice:

If you have figured out the O(n) solution, try coding another solution using the divide and conquer approach, which is more subtle.

Stats

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

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

Analysis

This problem can be solved using DP or Divide and Conquer.

DP solution is very easy. I will only post the code.

Divide and Conquer approach is difficult. Not only the idea of solution is hard to come up with, the coding part is even more challenging, especially when the input array contains all negative numbers!

Solution

First 2 code posted below are DP solutions.

2nd code is from this article (Algorithm 3). The idea is to divide the list by half, and find the max of this 3 values:

  1. max subarray to the left of mid-point (exclusive)
  2. max subarray to the right of mid-point (exclusive)
  3. max subarray from left of mid-point to the right of mid-point (inclusive)

For 1 and 2 are easy, for 3 is not. It needs to read left until the end, and right until the end (which means basically read n times). The total time complexity is O(nlgn).

3rd code is from this blog. It’s exactly same method, just coding a bit different.

Note there can be negative number! We can not initiate sum to 0. Instead I should do Integer.MIN_VALUE. Meanwhile, the sum should be initiated to 0, and cannot be Integer.MIN_VALUE.

My code

public class Solution {
    public int maxSubArray(int[] A) {
        if (A == null || A.length == 0) {
            return 0;
        }
        int pre = 0;
        int max = Integer.MIN_VALUE;
        for (int i = 0; i < A.length; i++) {
            max = Math.max(max, pre + A[i]);
            pre = Math.max(0, pre + A[i]);
        }
        return max;
    }
}

2nd code

public int maxSubArray(int[] A) {
    return recmax(A, 0, A.length - 1);
}

private int recmax(int[] A, int l, int u) {
    if (l > u) /* zero elements */
        return 0;
    if (l == u) /* one element */
        return Math.max(Integer.MIN_VALUE, A[l]);
    int m = (l + u) / 2;
    /* find max crossing to left */
    int lmax = Integer.MIN_VALUE;
    int sum = 0;
    for (int i = m; i >= l; i--) {
        sum += A[i];
        lmax = Math.max(lmax, sum);
    }
    int rmax = Integer.MIN_VALUE;
    sum = 0;
    /* find max crossing to right */
    for (int i = m + 1; i <= u; i++) {
        sum += A[i];
        rmax = Math.max(rmax, sum);
    }
    return Math.max(Math.max(recmax(A, l, m), recmax(A, m + 1, u)), lmax
            + rmax);
}

3rd code

public int maxSubArray(int[] A) {
    return maxArray(A, 0, A.length - 1, Integer.MIN_VALUE);
}

private int maxArray(int A[], int left, int right, int maxV) {
    if (left > right) return Integer.MIN_VALUE;
    int mid = (left + right) / 2;
    int lmax = maxArray(A, left, mid - 1, maxV);
    int rmax = maxArray(A, mid + 1, right, maxV);
    maxV = Math.max(Math.max(maxV, lmax), rmax);
    int sum = 0, mlmax = 0;
    for (int i = mid - 1; i >= left; i--) {
        sum += A[i];
        if (sum > mlmax) mlmax = sum;
    }
    sum = 0;
    int mrmax = 0;
    for (int i = mid + 1; i <= right; i++) {
        sum += A[i];
        if (sum > mrmax) mrmax = sum;
    }
    maxV = Math.max(maxV, mlmax + mrmax + A[mid]);
    return maxV;
}

[LeetCode 61] Rotate List

Question

link

Given a list, rotate the list to the right by k places, where k is non-negative.

For example:
Given 1->2->3->4->5->NULL and k = 2,
return 4->5->1->2->3->NULL.

Stats

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

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

Solution

This is a very basic question, but not very easy.

The implementation is a lot of node operations. The rest of things is straight-forward.

Read this blog for more.

My code

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode rotateRight(ListNode head, int n) {
        if (head == null) {
            return null;
        }
        ListNode p = head;
        for (int i = 0; i < n; i++) {
            if (p.next != null) {
                p = p.next;
            } else {
                p = head;
            }
        }
        ListNode q = head;
        while (p.next != null) {
            p = p.next;
            q = q.next;
        }
        if (q.next == null) {
            return head;
        }
        ListNode newHead = q.next;
        q.next = null;
        p.next = head;
        return newHead;
    }
}

[LeetCode 60] Permutation Sequence

Question

link

The set [1,2,3,…,n] contains a total of n! unique permutations.

By listing and labeling all of the permutations in order,
We get the following sequence (ie, for n = 3):

  1. "123"
  2. "132"
  3. "213"
  4. "231"
  5. "312"
  6. "321"

Given n and k, return the kth permutation sequence.

Note: Given n will be between 1 and 9 inclusive.

Stats

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

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

Analysis

This is a math problem. Trying to solve it using DFS like in “Permutation” or “N queen” will get time limit exceed exception.

This blog have a very good explanation of the math solution.


[Thoughts]
两个解法。
第一,DFS
递归遍历所有可能,然后累加计算,直至到K为止。

第二,数学解法。

假设有n个元素,第K个permutation是
a1, a2, a3, .....   ..., an
那么a1是哪一个数字呢?

那么这里,我们把a1去掉,那么剩下的permutation为
a2, a3, .... .... an, 共计n-1个元素。 n-1个元素共有(n-1)!组排列,那么这里就可以知道

设变量K1 = K
a1 = K1 / (n-1)!

同理,a2的值可以推导为
a2 = K2 / (n-2)!
K2 = K1 % (n-1)!
 .......
a(n-1) = K(n-1) / 1!
K(n-1) = K(n-2) /2!

an = K(n-1)

Solution

I have written a math recursive solution and code is below. It’s very straight-forward.

There is also direct math solution. However, how to handle the removal of elements from the unmatched list is a tough problem. I saw a lot of people using swap to do it, but I don’t like this idea because of the bad readability of code.

Finally I found a readable code from this blog. It’s a very good solution.

My code

updated on my birthday this year

public String getPermutation(int n, int k) {
    int index = k - 1;
    List<Integer> nums = new ArrayList<Integer>();
    for (int i = 1; i <= n; i++) {
        nums.add(i);
    }
    String ans = "";
    for (int i = n - 1; i >= 1; i--) {
        int fact = factorial(i);
        int nextIndex = index / fact;
        index = index % fact;
        ans += nums.remove(nextIndex);
    }
    ans += nums.get(0);
    return ans;
}

private int factorial(int x) {
    if (x == 0) return 0;
    int ans = 1;
    for (int i = 2; i <= x; i++) {
        ans *= i;
    }
    return ans;
}

[LeetCode 65] Valid Number (Unsolvable)

Question

link

Validate if a given string is numeric.

Some examples:
"0" => true
" 0.1 " => true
"abc" => false
"1 a" => false
"2e10" => true

Note: It is intended for the problem statement to be ambiguous. You should gather all requirements up front before implementing one.

Stats

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

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

Solution

I am unable to solve this problem, but I found a quite good solution. I post the code below.

My code

public boolean isNumber(String s) {
    s = s.trim(); 
    if (s.length() > 0 && s.charAt(s.length() - 1) == 'e')
        return false; //avoid "3e" which is false
    String[] t = s.split("e");
    if (t.length == 0 || t.length > 2) return false;
    boolean res = valid(t[0], false);
    if (t.length > 1) res = res && valid(t[1], true);
    return res;
}

private boolean valid(String s, boolean hasDot) {
    if (s.length() > 0 && (s.charAt(0) == '+' || s.charAt(0) == '-')) 
        s = s.substring(1); //avoid "1+", "+", "+."
    char[] arr = s.toCharArray();
    if (arr.length == 0 || s.equals(".")) return false;
    for (int i = 0; i < arr.length; i++) {
        if (arr[i] == '.') {
            if (hasDot) return false;
            hasDot = true;
        } else if (!('0' <= arr[i] && arr[i] <= '9'))
            return false;
    }
    return true;
}

[LeetCode 68] Text Justification (Unsolvable)

Question

link

Given an array of words and a length L, format the text such that each line has exactly L characters and is fully (left and right) justified.

You should pack your words in a greedy approach; that is, pack as many words as you can in each line. Pad extra spaces ' ' when necessary so that each line has exactly L characters.

Extra spaces between words should be distributed as evenly as possible. If the number of spaces on a line do not divide evenly between words, the empty slots on the left will be assigned more spaces than the slots on the right.

For the last line of text, it should be left justified and no extra space is inserted between words.

For example,
words: ["This", "is", "an", "example", "of", "text", "justification."]
L: 16.

Return the formatted lines as:

[
   "This    is    an",
   "example  of text",
   "justification.  "
]

Note: Each word is guaranteed not to exceed L in length.

click to show corner cases.

Stats

Frequency 2
Difficulty 4
Adjusted Difficulty unsolvable
Time to use unsolvable

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

Solution

I was not able to come up with a solution.

I found solution in here, here and here. I will post the code from first link.

My code

public ArrayList < String > fullJustify(String[] words, int L) {
    int wordsCount = words.length;
    ArrayList < String > result = new ArrayList < String > ();
    int curLen = 0;
    int lastI = 0;
    for (int i = 0; i <= wordsCount; i++) {
        if (i == wordsCount || curLen + words[i].length() + i - lastI > L) {
            StringBuffer buf = new StringBuffer();
            int spaceCount = L - curLen;
            int spaceSlots = i - lastI - 1;
            if (spaceSlots == 0 || i == wordsCount) {
                for (int j = lastI; j < i; j++) {
                    buf.append(words[j]);
                    if (j != i - 1)
                        appendSpace(buf, 1);
                }
                appendSpace(buf, L - buf.length());
            } else {
                int spaceEach = spaceCount / spaceSlots;
                int spaceExtra = spaceCount % spaceSlots;
                for (int j = lastI; j < i; j++) {
                    buf.append(words[j]);
                    if (j != i - 1)
                        appendSpace(buf, spaceEach + (j - lastI < spaceExtra ? 1 : 0));
                }
            }
            result.add(buf.toString());
            lastI = i;
            curLen = 0;
        }
        if (i < wordsCount)
            curLen += words[i].length();
    }
    return result;
}

private void appendSpace(StringBuffer sb, int count) {
    for (int i = 0; i < count; i++)
        sb.append(' ');
}