Interview Questions in 2018

A list of coding interview questions that I was asked in 2018.

Question 1: Add Binary

Statement (leetcode: 791)

Given two binary strings, return their sum (also a binary string).
The input strings are both non-empty and contains only characters 1 or 0.

Example 1:

Input: a = "11", b = "1"
Output: "100"

Example 2:

Input: a = "1010", b = "1011"
Output: "10101"

Solution

String addBinary(String a, String b) {
    int i = a.length() - 1, j = b.length() - 1;
    int carry = 0;
    StringBuilder sb = new StringBuilder();
    while (i >= 0 || j >= 0) {
        int sum = carry;
        if (i >= 0) {
            sum += a.charAt(i) - '0';
            i--;
        }
        if (j >= 0) {
            sum += b.charAt(j) - '0';
            j--;
        }
        carry = sum / 2;
        sb.insert(0, sum % 2);
    }
    if (carry != 0) sb.insert(0, carry);
    return sb.toString();
}
Complexity Comments
Time O(n) /
Space O(n) StringBuilder

Extention

What if the given strings can be numbers of any base ?

String addBinary(String a, String b) {
    int i = a.length() - 1, j = b.length() - 1;
    int carry = 0;
    StringBuilder sb = new StringBuilder();
    while (i >= 0 || j >= 0) {
        int sum = carry;
        if (i >= 0) {
            sum += a.charAt(i) - '0';
            i--;
        }
        if (j >= 0) {
            sum += b.charAt(j) - '0';
            j--;
        }
        carry = sum / 2;
        sb.insert(0, sum % 2);
    }
    if (carry != 0) sb.insert(0, carry);
    return sb.toString();
}

Question 2: cd command

Statement

Write a function to simluate linux command cd

Example 1:

Input: cur = "/etc", path = "/bin/"
Output: "/bin"

Example 2:

Input: a = "/etc", b = "hadoop"
Output: "/etc/hadoop"

Example 3:

Input: a = "/etc/hadoop/conf", b = "../../hive"
Output: "/etc/hive"

Example 4:

Input: a = "/etc/hadoop/conf", b = ".././conf"
Output: "/etc/hadoop/conf"

Solution

String cd(String cur, String path) {
    if (path.startsWith("/")) return path;
    Stack<String> stack = new Stack<>();

    for (String dir : cur.split("/"))
        if (!dir.isEmpty()) stack.push(dir);

    for (String dir : path.split("/"))
        if (dir.equals("..")) {
            if (!stack.isEmpty()) stack.pop();
        } else if (!dir.equals(".")) {
            stack.push(dir);
        }

    String res = String.join("/", stack);
    return res.startsWith("/") ? res : "/" + res;
}
Complexity Comments
Time O(n) /
Space O(n) Stack

Question 3: Custom Sort String

Statement (leetcode: 791)

S and T are strings composed of lowercase letters. In S, no letter occurs more than once.
S was sorted in some custom order previously. We want to permute the characters of T so that they match the order that S was sorted. More specifically, if x occurs before y in S, then x should occur before y in the returned string.
Return any permutation of T (as a string) that satisfies this property.

Example :

Input: S = "cba", T = "abcd"
Output: "cbad"

Explanation: 
"a", "b", "c" appear in S, so the order of "a", "b", "c" should be "c", "b", and "a". 
Since "d" does not appear in S, it can be at any position in T. "dcba", "cdba", "cbda" are also valid outputs.

Note:

  • S has length at most 26, and no character is repeated in S.
  • T has length at most 200.
  • S and T consist of lowercase letters only.

Solution

public String customSortString(String S, String T) {
    int[] dict = new int[26];
    for (char c : T.toCharArray()) {
        dict[c - 'a'] += 1;
    }
    StringBuilder sb = new StringBuilder();
    
    for (char c : S.toCharArray()) {
        for (int i = 0; i < dict[c - 'a']; i++)
            sb.append(c);
        dict[c - 'a'] = 0;
    }
    
    for (char c = 'a'; c <= 'z'; c++)
        for (int i = 0; i < dict[c - 'a']; i++)
            sb.append(c);
    
    return sb.toString();
}
Complexity Comments
Time O(n) /
Space O(n) StringBuilder

Question 4: Position of the leftmost one

Statement

Given a binary matrix (containing only 0 and 1) of order n * n. All rows are sorted already. We need to find position of the left most 1.
Note: in case of tie, return the position of the smallest row number.

Example:

Input matrix
0 1 1 1
0 0 1 1
1 1 1 1  // this row has maximum 1s
0 0 0 0

Output: [2, 0]

Solution

int[] findPosition(int[][] matrix) {
    int r = matrix.length;
    if (r == 0) return null;
    int c = matrix[0].length;
    if (c == 0) return null;

    int[] res = new int[] {};
    int j = c - 1;
    for (int i = 0; i < r; i++) {
        while (j >= 0 && matrix[i][j] == 1) {
            j--;
            res = new int[] {i, j + 1};
        }
    }
    return res;
}
Complexity Comments
Time O(r + c) ends on the boundary
Space O(1) /

Question 5: Validate Binary Search Tree

Statement (leetcode: 98)

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.
Example 1:

Input:
    2
   / \
  1   3
Output: true

Example 2:

    5
   / \
  1   4
     / \
    3   6
Output: false
Explanation: The input is: [5,1,4,null,null,3,6]. The root node's value
             is 5 but its right child's value is 4.

Solution

boolean validate(TreeNode node, long min, long max) {
    if (node == null) {
        return true;
    } else {
        if (node.val > min && node.val < max) {
            return validate(node.left, min, node.val) && validate(node.right, node.val, max);
        } else {
            return false;
        }
    }
}

boolean isValidBST(TreeNode root) {
    return validate(root, Long.MIN_VALUE, Long.MAX_VALUE);
}
Complexity Comments
Time O(n) visit all the nodes
Space O(log n) recursice call stack

Question 6: Search word in the dictionary

Statement (leetcode: 211)

Design a data structure that supports the following two operations:

class WordDictionary {

    /** Initialize data structure */
    public WordDictionary()

    /** Adds a word into the data structure. */
    public void addWord(String word)

    /** Returns if the word is in the data structure. A word could contain the dot character '.' to represent any one letter. */
    public boolean search(String word)
}

void addWord(word)
bool search(word)

search(word) can search a literal word or a regular expression string containing only letters a-z or .. A . means it can represent any one letter.

Example:
addWord("bad")
addWord("dad")
addWord("mad")
search("pad") -> false
search("bad") -> true
search(".ad") -> true
search("b..") -> true

Solution

class WordDictionary {
    class TrieNode {
        TrieNode[] next = new TrieNode[26];
        String word = null;
    }

    TrieNode root;

    public WordDictionary() {
        this.root = new TrieNode();
    }
    
    /** Adds a word into the data structure. */
    public void addWord(String word) {
        TrieNode node = root;
        for (int i = 0; i < word.length(); i++) {
            char c = word.charAt(i);
            if (node.next[c - 'a'] == null) node.next[c - 'a'] = new TrieNode();
            node = node.next[c - 'a'];
        }
        node.word = word;
    }
    
    /** Returns if the word is in the data structure. A word could contain the dot character '.' to represent any one letter. */
    public boolean search(String word) {
        return match(word, 0, root);
    }
    
    private boolean match(String word, int i, TrieNode node) {
        if (i == word.length()) return node.word != null;
        
        char c = word.charAt(i);
        if (c == '.') {
            for (TrieNode nextNode : node.next) {
                if (nextNode != null && match(word, i + 1, nextNode)) {
                    return true;
                }
            }
            return false;
        } else {
            TrieNode nextNode = node.next[c - 'a'];
            return nextNode != null && match(word, i + 1, nextNode);
        }
    }
}
add Complexity Comments
Time O(n) /
Space O(n) node creation
search Complexity Comments
Time O(n) /
Space O(n) recursive call stack

Question 7: Valid Palindrome

Statement (leetcode: 125)

Given a string, determine if it is a palindrome, considering only alphanumeric characters and ignoring cases.
Note: For the purpose of this problem, we define empty string as valid palindrome.

Example 1:

Input: "A man, a plan, a canal: Panama"
Output: true

Example 2:

Input: "race a car"
Output: false

Solution

boolean isValid(char c) {
    return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9');
}

boolean isPalindrome(String s) {
    int i = 0;
    int j = s.length() - 1;
    while(i <= j) {
        if (!isValid(s.charAt(i))) {
            i++;
            continue;
        }
        if (!isValid(s.charAt(j))) {
            j--;
            continue;
        }
        if (Character.toLowerCase(s.charAt(i)) == Character.toLowerCase(s.charAt(j))) {
            i++;
            j--;
        } else {
            return false;
        }
    }
    return true;
}
Complexity Comments
Time O(n) /
Space O(1) /

Question 8: Shortest Distance To All Stations

Statement

Given a metro map of London, find the station which is closest to all the others stations.

Solution (Floyd–Warshall algorithm)

/** graph is a weighted undirected adjacency matrix */
int solve(double[][] graph) {
    int n = graph.length;
    double[][] dist = new double[n][n];

    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
                dist[i][j] = graph[i][j];
            }
        }

    /** Floyd–Warshall algorithm */
    for (int k = 0; k < n; k++) {
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                if (dist[i][j] > dist[i][k] + dist[k][j]) {
                    dist[i][j] = dist[i][k] + dist[k][j];
                }
            }
        }
    }

    double min = Integer.MAX_VALUE;
    int res = -1;
    for (int i = 0; i < n; i++) {
        double sum = 0
        for (double d : dist[i])
            sum += d;
        if (sum < min) {
            res = i;
            min = sum;
        }
    }
    return res;
}
Complexity Comments
Time O(n ^ 3) /
Space O(n ^ 2) /

Question 9: Equilibrium Point

Statement (leetcode: 724)

Given an array of integers nums, write a method that returns the “pivot” index of this array.
We define the pivot index as the index where the sum of the numbers to the left of the index is equal to the sum of the numbers to the right of the index.
If no such index exists, we should return -1. If there are multiple pivot indexes, you should return the left-most pivot index.

Example 1:

Input: 
nums = [1, 7, 3, 6, 5, 6]
Output: 3
Explanation: 
The sum of the numbers to the left of index 3 (nums[3] = 6) is equal to the sum of numbers to the right of index 3.
Also, 3 is the first index where this occurs.

Example 2:

Input: 
nums = [1, 2, 3]
Output: -1
Explanation: 
There is no index that satisfies the conditions in the problem statement.

Solution

pivotIndex(int[] nums) {
    int sum = 0, leftsum = 0;
    for (int x: nums) sum += x;
    for (int i = 0; i < nums.length; ++i) {
        if (leftsum == sum - leftsum - nums[i]) return i;
        leftsum += nums[i];
    }
    return -1;
}
Complexity Comments
Time O(n) /
Space O(1) /

Question 10: Complete Binary Tree

Statement

Given a complete binary tree in which each node marked with a number in level order (root = 1) and several connections are removed.
Find if the given number is still reachable from the root of the tree.

Example 1:

Input: tree = root, num = 5
            1 -> root
           / \
          /   \
         /     \
        /       \
       /         \
      2           3
     /           / \
    /           /   \
   4     5     6     7
  / \   / \   / \   / \
 8   9 10 11 12 13 14 15
Output: false

Example 2:

Input: tree = root, num = 6
            1 -> root
             \
              \
               \
                \
                 \
      2           3
     / \         / \
    /   \       /   \
   4     5     6     7
  / \   / \   / \   / \
 8   9 10 11 12 13 14 15
Output: true

Solution

boolean findInCompleteTree(TreeNode root, int n) {
    List<Boolean> path = new LinkedList<>();
    
    while (n > 1) {
        if (n % 2 == 0) {
            path.add(0, true);
        } else {
            path.add(0, false);
        }
        n /= 2;
    }

    for (boolean p : path) {
        if (p) root = root.left;
        else root = root.right;
        if (root == null) return false;
    }

    return true;
}
Complexity Comments
Time O(log n) /
Space O(log n) /

Extension (leetcode: 222)

Count the number of node in a complete binary tree.

Example 1:

Input: tree = root
            1 -> root
           / \
          /   \
         /     \
        /       \
       /         \
      2           3
     / \         / \
    /   \       /   \
   4     5     6     7
  / \   / \   /
 8   9 10 11 12
Output: 12
int countInCompleteTree(TreeNode root) {
    TreeNode node = root;
    int depthLeft = 0;
    while (node != null) {
        depthLeft++;
        node = node.left;
    }

    node = root;
    int depthRight = 0;
    while (node != null) {
        depthRight++;
        node = node.right;
    }
    
    return depthLeft == depthRight ?
        (1 << depthLeft) - 1 :
        1 + countInCompleteTree(root.left) + countInCompleteTree(root.right);
}
Complexity Comments
Time O(log n * log n) log n calls and each call takes log n to compute depth
Space O(log n) recursive call stack

Question 11: UTF-8 Encoding

Statement

A character in UTF8 can be from 1 to 4 bytes long, subjected to the following rules:

  • For 1-byte character, the first bit is a 0, followed by its unicode code.
  • For n-bytes character, the first n-bits are all one’s, the n+1 bit is 0, followed by n-1 bytes with most significant 2 bits being 10.

This is how the UTF-8 encoding would work:

Number of bytes Bits for code point First code point Last code point Byte 1 Byte 2 Byte 3 Byte 4
1 7 U+0000 U+007F 0xxxxxxx
2 11 U+0080 U+07FF 110xxxxx 10xxxxxx
3 16 U+0800 U+FFFF 1110xxxx 10xxxxxx 10xxxxxx
4 21 U+10000 U+10FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

Given a byte array which contains only UTF-8 encoded characters and an integer limit,
return the max number of bytes contains only valid UTF-8 encordings in the first limit bytes.

Example 1:

Input:
stream = | 0xxxxxxx | 110xxxxx | 10xxxxxx | 1110xxxx | 10xxxxxx | 10xxxxxx | 11110xxx | 10xxxxxx ||| 10xxxxxx | 10xxxxxx |
limit = 8

Output: 5

Example 2:

Input:
stream = | 0xxxxxxx | 110xxxxx | 10xxxxxx |
limit = 5

Output: 2

Solution

int countUTF8Byte(byte[] stream, int limit) {
    if (stream.length <= limit) {
        return stream.length;
    } else {
        while (limit > 0 && (stream[limit] & 0xFF) >> 6 == 2) {
            limit--;
        }
        return limit;
    }
}
Complexity Comments
Time O(1) No more than 6 bytes
Space O(1) /

Question 12: Design Rate limiter

Statement (inspired by leetcode: 362)

Design rate limiter API based on the count limit per minute and per hour.
The granularity of timestamp is in second if needed.

class RateLimiter {

    /** Initialize data structure */
    public RateLimiter(long minuteCount, long hourCount)
        
    /** Return true if the function calls exceeded either minuteCount or hourCount, otherwise return false */
    public boolean isLimited() 
}

RateLimiter rl = new RateLimit(100, 6000);
rl.isLimited() // return false;

Solution

public class RateLimiter {

    class HitCounter {

        private int   numBucket;
        private int[] time;
        private int[] hit;

        public HitCounter(int numBucket) {
            this.numBucket = numBucket;
            this.time = new int[numBucket];
            this.hit = new int[numBucket];
        }

        public void hit(int ts) {
            int bucket = ts % this.numBucket;
            if (time[bucket] == ts) {
                hit[bucket]++;
            } else {
                time[bucket] = ts;
                hit[bucket] = 1;
            }
        }

        public int count(int ts) {
            int cnt = 0;
            for (int i = 0; i < this.numBucket; i++) {
                if (ts - time[i] < this.numBucket) {
                    cnt += hit[i];
                }
            }
            return cnt;
        }
    }

    private long       minuteLimit;
    private long       hourLimit;
    private HitCounter minuteCounter;
    private HitCounter hourCounter;

    public RateLimiter(long minuteLimit, long hourLimit) {
        this.minuteLimit = minuteLimit;
        this.hourLimit = hourLimit;
        this.minuteCounter = new HitCounter(60);
        this.hourCounter = new HitCounter(3600);
    }

    public boolean isLimited() {
        int tsInSec = (int) (System.currentTimeMillis() / 1000);
        if (this.minuteCounter.count(tsInSec) < this.minuteLimit &&
            this.hourCounter.count(tsInSec) < this.hourLimit) {
            minuteCounter.hit(tsInSec);
            hourCounter.hit(tsInSec);
            return false;
        } else {
            return true;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        RateLimiter rl = new RateLimiter(10, 600);
        int count = 0;
        while (true) {
            Thread.sleep(1000);
            if (rl.isLimited()) {
                break;
            } else {
                count++;
                System.out.println("Limit not reached: " + count);
            }
        }
        System.out.println("Limit exceeded: " + count);
    }
}
hit Complexity Comments
Time O(1) /
Space O(n) number of the buckets
count Complexity Comments
Time O(n) number of the buckets
Space O(n) number of the buckets

Question 13: Design Task Scheduler (cron)

Statement

Implement the following 3 methods. Start with scheduling part and then execution part.

public class CronScheduler {
    void schedule(TimerTask task, long delay) {}
    void repeat(TimerTask t, long delay, long period) {}
    void daily(TimerTask t, long delay) {}
}

Solution

Reference: java.util.Timer and java.util.TimerTask

// TODO
0%