Woodstock Blog

a tech blog for general algorithmic interview questions

[LeetCode 23] Merge K Sorted Lists

Question

link

Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity.

Stats

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

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

Analysis

There are two ways to solve this problem.

  • k - number of lists
  • n - length of each list

First approach is merge sort. Using divide and conquer approach, divide the entire input into halves, and then merge 2 list each time. Instead of merging 1 by 1 which the time complexity is O(nk x (k-1)), the 2 lists to be merged is always similar length, thus time complexity is reduced to O(nk x logk).

You may wonder how I calculate time complexity. See, each round of sort, nk nodes are read and sorted. This happened O(logk) times, where k is the number of lists. Thus totoal time take is O(nk x logk).

Second approach is heap sort. The idea of this is to always keep a sorted list of the head of each list. When we retrieve items from the heap, it only take O(logk) time to find the next smallest element.

Not sure what is a heap? Read [Fundamental] Heap (data structure) before you proceed.

Both method are well explained in this csdn blog. Time complexity analysis is given by nootcoder blog.

Solution

Divide and conquer code is lengthy but medium difficulty.

Second solution, however, is not as easy. Especially when we have to write Comparator on our own. A priority queue (heap) is implemented and head of each list is inserted into the heap. Then poll elements out from the heap until heap is empty.

My code

Merge sort code, written by me

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode mergeKLists(List<ListNode> lists) {
        if (lists == null || lists.size() == 0) {
            return null;
        }
        return mergeHelper(lists, 0, lists.size() - 1);
    }

    private ListNode mergeHelper(List<ListNode> lists, int start, int end) {
        if (start == end) {
            return lists.get(start);
        } 
        int mid = start + (end - start) / 2;
        ListNode firstHalf = mergeHelper(lists, start, mid);
        ListNode secondHalf = mergeHelper(lists, mid + 1, end);
        return mergeTwo(firstHalf, secondHalf);
    }

    private ListNode mergeTwo(ListNode head1, ListNode head2) {
        ListNode dummy = new ListNode(0);
        ListNode p = dummy;
        while (head1 != null && head2 != null) {
            if (head1.val < head2.val) {
                p.next = head1;
                head1 = head1.next;
                p = p.next;
            } else {
                p.next = head2;
                head2 = head2.next;
                p = p.next;
            }
        }
        if (head1 == null) {
            p.next = head2;
        } else {
            p.next = head1;
        }
        return dummy.next;
    }
}

Heap sort code, written by me. source

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode mergeKLists(List<ListNode> lists) {
        if (lists == null || lists.size() == 0) {
            return null;
        }
        int size = lists.size();
        // create a heap with Java priority queue, set the initial size
        Queue<ListNode> heap = new PriorityQueue(size, new NodeComparator());
        // add all ListNode into the heap
        for (ListNode node: lists) {
            if (node != null) {
                heap.add(node);
            }
        }
        // start to link nodes from smallest to largest
        ListNode dummy = new ListNode(0);
        ListNode p = dummy;
        while (!heap.isEmpty()) {
            p.next = heap.remove();
            p = p.next;
            if (p.next != null) {
                heap.add(p.next);
            }
        }
        return dummy.next;
    }

    class NodeComparator implements Comparator<ListNode> {
        public int compare(ListNode n1, ListNode n2) {
            return n1.val - n2.val;
        }
    }
}

[LeetCode 30] Substring With Concatenation of All Words

Question

link

You are given a string, S, and a list of words, L, that are all of the same length. Find all starting indices of substring(s) in S that is a concatenation of each word in L exactly once and without any intervening characters.

For example, given:
S: "barfoothefoobarman"
L: ["foo", "bar"]

You should return the indices: [0,9].
(order does not matter).

Stats

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

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

Analysis

There are 2 ways to solve this question.

The naive approach takes around 1200ms to pass, and the KPM-like approach takes around half of that time. Both methods are explained well in this blog.

I will cover only the naive approach.

Naive approach

The naive approach uses a HashMap for 2 reasons. Reason 1 is because there can be duplications in L, and reason 2 is the searching is faster. For information on HashMap, refer to [Fundamental] Recap on Java HashMap.

Time complexity of this solution is O((n - k * m) x m), and space is the size of list L, O(m). If m is not very big, the time can be regarded as O(n).

My code

public class Solution {
    public List<Integer> findSubstring(String S, String[] L) {
        List<Integer> ans = new ArrayList<Integer>();
        if (L == null || L.length == 0 || S == null || S.length() == 0) {
            return ans;
        }
        int num = L.length;
        int len = L[0].length();
        if (num * len > S.length()) {
            return ans;
        }
        // build a hashset, for simplifying the hashmap generation later on
        HashMap<String, Integer> set = new HashMap<String, Integer>();
        for (String str: L) {
            if (set.containsKey(str)) {
                set.put(str, set.get(str) + 1);
            } else {
                set.put(str, 1);
            }
        }
        // starting from i, check Concatenation of All Words
        for (int i = 0; i <= S.length() - (num * len); i++) {
            // first build a HashMap from the set that we acquired before
            HashMap<String, Integer> map = new HashMap<String, Integer>(set);
            for (int j = 0; j < num; j++) {
                String str = S.substring(i + j * len, i + (j + 1) * len);
                if (!map.containsKey(str)) {
                    break;
                } else if (map.get(str) > 1) {
                    map.put(str, map.get(str) - 1);
                } else if (map.get(str) == 1) {
                    map.remove(str);
                }
            }
            if (map.isEmpty()) {
                ans.add(i);
            }
        }
        return ans;
    }
}

[Fundamental] Recap on Java HashMap

The HashMap

A hash table is made up of two parts: an array (the actual table where the data to be searched is stored) and a mapping function, known as a hash function.

The hash function is a mapping from the input space to the integer space that defines the indices of the array. In other words, the hash function provides a way for assigning numbers to the input data such that the data can then be stored at the array index corresponding to the assigned number.

For example, if I want to store <“Durant”>, I pass “Durant” into the hash function, and get (let’s say) number 3. So in the Hash Table, it will store table(3 -> “Durant”).

In this way, the searching of HashMap can almost achieve O(1) time in best case (like array access).

However,

For average case, It really is (as the wikipedia page says) O(1+n/k) where K is the hash table size. If K is large enough, then the result is effectively O(1). But suppose K is 10 and N is 100. In that case each bucket will have on average 10 entries, so the search time is definitely not O(1); it is a linear search through up to 10 entries.

In practise, we will just assume search in HashMap always O(1).

Below is a great conclusion.

Hash tables are O(1) average and amortized case complexity, however is suffers from O(n) worst case time complexity. [And I think this is where your confusion is]

Hash tables suffer from O(n) worst time complexity due to two reasons:

  1. If too many elements were hashed into the same key: looking inside this key may take O(n) time.
  2. Once a hash table has passed its load balance - it has to rehash [create a new bigger table, and re-insert each element to the table].

However, it is said to be O(1) average and amortized case because:

  1. It is very rare that many items will be hashed to the same key [if you chose a good hash function and you don't have too big load balance.
  2. The rehash operation, which is O(n), can at most happen after n/2 ops, which are all assumed O(1): Thus when you sum the average time per op, you get : (n*O(1) + O(n)) / n) = O(1)

[Fundamental] Java Bit Operation

Bit operators

operator what is means
~ invert every bit
<< shift left (same as *2)
>> signed shift right
>>> unsigned shift right
^ XOR
| OR


Note the unsigned right shift operator “>>>” shifts a zero into the leftmost position, while the leftmost position after “>>” depends on sign extension.

[LeetCode 28] Implement strStr

Question

link

Implement strStr().

Returns a pointer to the first occurrence of needle in haystack, or null if needle is not part of haystack.

Stats

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

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

Analysis

There are 2 ways to solve this problem.

  1. Most common way is using 2 nested loop

  2. Also can be solved by KPM algorithm

Solution

Standard solution is posted below. Read this post for more.

As for KMP algo, I have to admit I am not able to do it. Plz refer to this blog and this blog for more!

My code

public class Solution {
    public int strStr(String haystack, String needle) {
        if (haystack == null || needle == null) {
            return -1;
        } else if (haystack.length() < needle.length()) {
            return -1;
        } else if (haystack.length() == 0 && needle.length() == 0) {
            return 0;
        }
        for (int i = 0; i <= haystack.length() - needle.length(); i++) {
            int j;
            // try to match part of haystack (starting from i) to needle 
            for (j = 0; j < needle.length(); j++) {
                if (haystack.charAt(i + j) != needle.charAt(j)) {
                    break;
                }
            }
            // if part of haystack (starting from i) matches needle 
            if (j == needle.length()) {
                return i;
            }
            // if not match, proceed to next loop
        }
        return -1;
    }
}

[LeetCode 29] Divide Two Integers

Question

link

Divide two integers without using multiplication, division and mod operator.

Stats

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

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

Analysis

This question might be more difficult than you thought. So do not overlook it because of its seemingly simple description. First of all, please refer to [Fundamental] Java Bit Operation for information on bit operators.

And remember… overflow can happen, especially when you dealing with Integer.MAX_VALUE and Integer.MIN_VALUE.

Solution

This solution is a while loop that keeps subtracting (divisor * (2 ^ n)) from dividend. You can find a good solution from this blog.

code

public class Solution {
    public int divide(int dividend, int divisor) {
        int sign = 1;
        long x = dividend;
        long y = divisor;
        if (x < 0) {
            x = x * -1;
            sign *= -1;
        }
        if (y < 0) {
            y = y * -1;
            sign *= -1;
        }
        return divide(sign, x, y);
    }

    private int divide(int sign, long x, long y) {
        // both x and y are positive numbers
        if (x < y) {
            return 0;
        }
        long quotient = 0;
        long partialQuotient;
        long partialSubtract;
        while (x >= y) {
            // the idea is to subtract a certain times of x from y
            // and save the remainder value back to x
            partialQuotient = 1;
            partialSubtract = y;
            while ((partialSubtract << 1) <= x) {
                partialQuotient <<= 1; // doubles quotient
                partialSubtract <<= 1; // doubles subtraction
            }
            x -= partialSubtract;
            quotient += partialQuotient;
        }
        long finalQuo = sign * quotient;
        if (finalQuo < Integer.MIN_VALUE || finalQuo > Integer.MAX_VALUE) {
            return Integer.MAX_VALUE;
        } else {
            return (int) finalQuo;
        }
    }
}

[LeetCode 20] Valid Parentheses

Question

link

Given a string containing just the characters '(', ')', '{', '}', '[' and ']', determine if the input string is valid.

The brackets must close in the correct order, "()" and "()[]{}" are all valid but "(]" and "([)]" are not.

Stats

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

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

Analysis

Either stack or string would work.

Solution

The code is easy.

My code

public class Solution {
    public boolean isValid(String s) {
        if (s == null || s.length() == 0) {
            return false;
        }
        Stack<Character> stack = new Stack<Character>();
        // process s char by char
        for (char c: s.toCharArray()) {
            if (c == '(' || c == '[' || c == '{') {
                stack.push(c);
            } else {
                if (stack.isEmpty()) {
                    // eg. input = "())))"
                    return false;
                }
                char top = stack.pop();
                if (Math.abs(top - c) > 2) {
                    // parentheses does not match
                    return false;
                }
            }
        }
        // after this, stack should be empty (if parentheses valid)
        return stack.isEmpty();
    }
}

[LeetCode 24] Swap Nodes in Pairs

Question

link

Given a linked list, swap every two adjacent nodes and return its head.

For example,
Given 1->2->3->4, you should return the list as 2->1->4->3.

Your algorithm should use only constant space. You may not modify the values in the list, only nodes itself can be changed.

Stats

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

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

Solution

The easier solution is of course recursion. We only need to swap 2 nodes and then call the method recursively. Find code below.

However, there exists iterative solution, just slightly complex. Read here for more. Two pieces of iterative code are posted below.

My code

Recursion

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode swapPairs(ListNode head) {
        if (head == null || head.next == null) {
            return head;
        }
        ListNode temp = head.next;
        head.next = swapPairs(temp.next);
        temp.next = head;
        return temp;
    }
}

Iterative solution using while loop. ref

public ListNode swapPairs(ListNode head) {
    if (head == null) return head;
    ListNode preHead = new ListNode(1);
    preHead.next = head;
    ListNode cur = head, pre = preHead;
    while (cur != null && cur.next != null) {
        pre.next = cur.next;
        ListNode newCur = cur.next.next;
        cur.next.next = cur;
        cur.next = newCur;
        pre = cur;
        cur = newCur;
    }
    return preHead.next;
}

Iterative solution using for loop. ref

public ListNode swapPairs(ListNode head) {
    ListNode start = new ListNode(0);
    start.next = head;
    for (ListNode cur = start; cur.next != null 
           && cur.next.next != null; cur = cur.next.next) {
        cur.next = swap(cur.next, cur.next.next);
    }
    return start.next;
}

private ListNode swap(ListNode next1, ListNode next2) {
    next1.next = next2.next;
    next2.next = next1;
    return next2;
}

[LeetCode 27] Remove Element

Question

link

Given an array and a value, remove all instances of that value in place and return the new length.

The order of elements can be changed. It doesn't matter what you leave beyond the new length.

Stats

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

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

Solution

Similar to the question [LeetCode 26] Remove Duplicates From Sorted Array. Use 2 pointers.

Let’s play a game

The code I gave is 21 lines. It’s too long. Can we solve this problem with less code?

Sure we can! I have a 10-line version:

public class Solution {
    public int removeElement(int[] A, int elem) {
        int left = 0, right = 0;
        while (right < A.length) {
            if (A[right] == elem) right++;
            else A[left++] = A[right++];
        }
        return left;
    }
}

Now for a moment I thought the above code is the most concise, until I read this blog. The code is:

public class Solution {
    public int removeElement(int[] A, int elem) {
        int p = 0;
        for (int i = 0; i < A.length; i++)
            if (A[i] != elem) A[p++] = A[i];
        return p;
    }
}

OK game over. Look at the standard answer below. Happy Leetcoding!

My code

public class Solution {
    public int removeElement(int[] A, int elem) {
        if (A == null || A.length == 0) {
            return 0;
        }
        int len = A.length;
        int left = 0;
        int right = 0;
        while (right < len) {
            // skip all instances of elem 
            while (right < len && A[right] == elem) {
                right++;
            }
            if (right == len) {
                break;
            }
            A[left++] = A[right++];
        }
        return left;
    }
}

[LeetCode 26] Remove Duplicates From Sorted Array

Question

link

Given a sorted array, remove the duplicates in place such that each element appear only once and return the new length.

Do not allocate extra space for another array, you must do this in place with constant memory.

For example,
Given input array A = [1,1,2],

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

Stats

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

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

Analysis

This question is easy.

Solution

Two pointer operations. A very similar question is [LeetCode 27] Remove Element.

My code

public class Solution {
    public int removeDuplicates(int[] A) {
        if (A == null || A.length == 0) {
            return 0;
        }
        int len = A.length;
        int left = 0;
        int right = 0;
        while (right < len) {
            A[left++] = A[right++];
            // advance right pionter to a new value 
            while (right < len && A[right - 1] == A[right]) {
                right++;
            }
        }
        return left;
    }
}