Monday, August 11, 2014

Leetcode: Remove Duplicates from Sorted List II

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.


Understand the problem:
The problem asks for removing duplicated numbers from a linked list. Note the the difference from the last post is all duplicated numbers are deleted.  For example,
1  ->  1  -> 1  -> 2  , return 2

For an empty linked list, return null
For a linked list with only 1 element, return the original linked list. 

Solution:
We still use two pointers, p and q. q pointer is one step ahead of p at beginning. Move q pointer if q is duplicated. If q pointer moves more than one steps (has duplicates), move p.next = q.next. Else, move p and q one step ahead. The crux is to determine if q moves multiple steps. If yes, it means q pointer has duplicates, we should jump the q pointer. Else, q pointer should be saved. For example,
0 -> 1  -> 2 ->3, where p and q points to 0 and 1, respectively. Since 1 has no duplicates afterwards, we move both p and q one step ahead.
0  -> 1  -> 1 -> 2  -> 3, where again p and q points to 0 and 1, respectively. Since now i has duplicates afterwards, we should jump the 1s and points p to the value of 2.

Code (Java):
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        if (head == null || head.next == null) return head;
        
        ListNode helper = new ListNode(0);
        helper.next = head;
        head = helper;
        
        ListNode p = head;
        ListNode q = head.next;
        
        while (q != null) {
            while (q.next != null && q.val == q.next.val) {
                q = q.next;
            }
            if (p.next != q) {
                p.next = q.next;
                if (q != null) q = q.next;
            } else {
                p = p.next;
                q = q.next;
            }
        }
        return head.next;
    }
}

Discussion:
Note that we used a helper node ahead of the head node. This is to avoid the complexities in deleting the head node, which may be the case for this question.  Using helper node is a common usage when updating the head node.

Updates on 10/7/14:
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        if (head == null || head.next == null) {
            return head;
        }
        
        ListNode dummyNode = new ListNode(Integer.MIN_VALUE);
        dummyNode.next = head;
        
        ListNode prev = dummyNode;
        ListNode curr = head;
        
        while (curr != null && curr.next != null) {
            ListNode next = curr.next;
            if (curr.val == next.val) {
                while (curr.next != null && curr.val == next.val) {
                    curr = curr.next;
                    next = next.next;
                }
            
                prev.next = next;
                curr = next;
            } else {
                prev = prev.next;
                curr = curr.next;
            }
        }
        
        return dummyNode.next;
    }
}


Leetcode: Remove Duplicates from Sorted List

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.


Understand the problem:
The question asks for removing duplicates in a linked list.
For example, 1  -> 1  -> 2, return 1  -> 2

For the linked list is null or empty, return the empty linked list
For the linked list having only one element, return the original linked list

Note that the question does not require in-place transform. The out-of-place solution is clearly simple: we just create a new linked list without duplicated elements. But can we do it in-place?

Solution:
Use two pointers, p and q, q is ahead of p at beginning. Compare the value of p and q, if different, move both p and q, else move the q pointer until they are different again. 

Code (Java):
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        if (head == null) return null;
        if (head.next == null) return head;
        
        ListNode p = head;
        ListNode q = head.next;
        
        while (q != null) {
            if (p.val != q.val) {
                p = p.next;
                q = q.next;
            } else {
                p.next = q.next;
                q = q.next;
            }
        }
        return head;
    }
}

Summary:
The problem is very straight-forward, but be very careful to handle the null-pointer well when moving the pointer.

Leetcode: Merge k Sorted Lists

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


Understand the problem:
The question asks for merging k sorted linked list and return it as one sorted list. It is an extension of merging the two sorted linked list but more complicated. 

The question also asked for analyzing and describing the complexity. 

Naive Solution:
One naive solution is to merge two linked list at first, then merge it with next linked list until we are done with all the input linked list. 

Code (Java):
/**
 * 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(ArrayList<ListNode> lists) {
        if (lists == null || lists.size() == 0) return null;
        if (lists.size() == 1) return lists.get(0);
        
        int numLists = lists.size();
        ListNode newHead = new ListNode(0);
        ListNode l1, l2;
        
        for (int i = 1; i < numLists; i++) {
            if (i == 1) {
                l1 = lists.get(0);
            } else {
                l1 = newHead;
            }
            l2 = lists.get(i);
            
            newHead = mergeTwoLists(l1, l2);
        }
        return newHead;
    }
    
    // Merge two sorted linked lists
    private ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        ListNode p1 = l1;
        ListNode p2 = l2;
        ListNode head = new ListNode(0);
        ListNode p = head;
        
        while (p1 != null && p2 != null) {
            if (p1.val <= p2.val) {
                p.next = p1;
                p1 = p1.next;
            } else {
                p.next = p2;
                p2 = p2.next;
            }
            p = p.next;
        }
        
        if (p1 != null) p.next = p1;
        else if (p2 != null) p.next = p2;
        
        return head.next;
    }
}


Analysis:
Now we discuss the complexity of the algorithm. Suppose we have k sorted lists to merge, each has size of n. When we merge the first two lists, the time complexity is O(2n), then merge with the third one, the time complexity is O(2n + n) = O(3n), the fourth one, O(3n + n) = O(4n)... the kth one O(kn). So the total time complexity for merging k sorted lists is O(2n) + O(3n) + ... + O(kn) = O(k^2 n). The space complexity is O(1).


A Better Solution:
A better solution is to use the idea of merge sort, i.e, we divide the k linked list into k/2, then k/4, until there is only one or two linked list. So the time complexity would be O(klogk * n). 

Code (Java):
/**
 * 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(ArrayList<ListNode> lists) {
        if (lists == null || lists.size() == 0) return null;
        if (lists.size() == 1) return lists.get(0);
        int numLists = lists.size();
        
        return merge(lists, 0, numLists - 1);
    }
    
    private ListNode merge(ArrayList<ListNode> lists, int lo, int hi) {
        if (lo == hi) return lists.get(lo);
        
        int mid = (lo + hi) / 2;
        ListNode l1 = merge(lists, lo, mid);
        ListNode l2 = merge(lists, mid + 1, hi);
        
        return mergeTwoLists(l1, l2);
    }
    
    private ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        ListNode p1 = l1;
        ListNode p2 = l2;
        
        ListNode head = new ListNode(0);
        ListNode p = head;
        
        while (p1 != null && p2 != null) {
            if (p1.val <= p2.val) {
                p.next = p1;
                p1 = p1.next;
            } else {
                p.next = p2;
                p2 = p2.next;
            }
            p = p.next;
        }
        if (p1 != null) p.next = p1;
        else if (p2 != null) p.next = p2;
        
        return head.next;
    }
} 

Another Solution:
Another solution is to maintain a size of k priority queue. Priority queue is actually a heap. So initially we add the first element of each linked list into the queue. And remove the minimum element from the queue, insert the next one, until the queue is empty.

Code (Java):
/**
 * 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(ArrayList<ListNode> lists) {
        if (lists == null || lists.size() == 0) return null;
        if (lists.size() == 1) return lists.get(0);
        
        PriorityQueue<ListNode> pq = new PriorityQueue<ListNode> (lists.size(), new ListNodeComparator());
        
        // Add first element of each list into the queue
        for (ListNode x : lists) {
            if (x != null) pq.add(x);
        }
            
        // retrive the minimum element from the queue and insert the next one
        ListNode head = new ListNode(0);
        ListNode p = head;
        while (pq.size() > 0) {
            ListNode temp = pq.poll();
            p.next = temp;
            
            if (temp.next != null) pq.add(temp.next);
            p = p.next;
        }
        return head.next;
    }
    
    private class ListNodeComparator implements Comparator<ListNode> {
        public int compare(ListNode l1, ListNode l2) {
            return l1.val - l2.val;
        }
    }
} 

Discussion:
Regarding the heap operations, inserting an element into the heap requires an average O(log k) time, k is the number of elements in the heap. Poll the minimum is O(1) operation since it is at the top of the heap. Since we inserted totally O(n * k) elements into the heap, the total time complexity is O(klogk * n), the same as solution 2. The space complexity is O(k) since we maintained a k-sized heap to store the minimum k elements. 

Summary:
In this post, we learned how to merge k sorted linked list into one. It is a very important operation it real applications. For instance, given k sorted entries from k servers, we wanna merge them into one sorted entries at the centralized server. The first solution is straight-forward, but for the k is very large, it is clearly inefficient. The solution two is based on the idea of merge sort. It divides-and-concur the k lists into two or one and then merge together. Note the recursion implementation. Solution three is neat but requires deep understanding of the priority queue. 

Leetcode: Merge Two Sorted Lists

Merge two sorted linked lists and return it as a new list. The new list should be made by splicing together the nodes of the first two lists.

Understanding the problem:
The question has for merging two sorted linked lists and return it as a new list. Note that the question requires the in-place transform, which means that we are not allowed to create a new merged linked list. 

Several examples to illustrate the question:
1  ->  3  ->  5  ->  7
2  ->  4  ->  6  ->  8

The merged list is 1  ->  2  -> 3  -> 4  -> 5  -> 6  -> 7  -> 8

1  ->  2  -> 3   -> 4
5  -> 6   -> 7   -> 8

The merged list is 1 -> 2  -> 3  -> 4  -> 5  -> 6   ->  7  ->  8


Naive Solution:
If you remember the question of merging two sorted array, this question is very closed to that one. The basic idea is to use two pointers pointing to the two linked list. The crux is to define a new fake head. Compare the first element from each list, merge the smaller one into the merged list. At last, when one list is empty, append the rest of the other one into the merged list.

Code (Java):
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        // handle null linked lists
        if (l1 == null) return l2;
        else if (l2 == null) return l1;
        
        ListNode p1 = l1;
        ListNode p2 = l2;
        ListNode newHead = new ListNode(0);
        ListNode p = newHead;
        
        while (p1 != null && p2 != null) {
            if (p1.val <= p2.val) {
                p.next = p1;
                p1 = p1.next;
            } else {
                p.next = p2;
                p2 = p2.next;
            }
            p = p.next;
        }
        
        if (p1 != null) {
            p.next = p1;
        } else if (p2 != null) {
            p.next = p2;
        }
        
        return newHead.next;
    }
}

Discussion:
The solution takes O(n + m) in time, since we traverse the linked lists only once. The space complexity is O(1). 

Summary:
Note the idea of using the fake head. It is much trickier than merging two sorted arrays. If the question requires the in-place merge, we must think about using the fake head. 

Friday, August 8, 2014

Leetcode: Linked List Cycle II

Given a linked list, return the node where the cycle begins. If there is no cycle, return null.
Follow up:
Can you solve it without using extra space?


Understand the problem:
The question is closed to determine a linked list has a cycle. The question asks for if a cycle exists, find its entrance. For e.g.,  for a cycled linked list:
1 -> 2  -> 3  ->4  ->2
The entrance of the cycle is 2.

Naive Solution:
Again, the native solution could use a hash table to store the elements we visited before. If we found a duplicated element, then that element is the entrance of the cycle. 

As we can see, the solution is straight-forward. The time complexity is O(n). The space complexity is O(n) as well since we need to store the nodes in the hash table. 

A Better Solution:
A better solution without using hash table is, we first find the collision point in the loop. Then move two pointers at the same speed, one starts from the head of the linked list, while the other starts from the collision node. The next collision is the entrance of the loop.

Code (Java):
/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode detectCycle(ListNode head) {
        // Find the collision point if the list has a cycle
        if (head == null) return null;
        
        ListNode slow = head;
        ListNode fast = head;
        
        while (fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
            
            if (slow == fast) break;
        }
        
        // check if the list has no cycle
        if (fast == null || fast.next == null) 
            return null;
        
        // find the entrance of the cycle
        slow = head;
        while (slow != fast) {
            slow = slow.next;
            fast = fast.next;
        }
        return slow;
    }
}

Discussion:
The mathematical proof is way of complicated. There are many online resources to prove that. Frankly, I don't think this gonna be a good interview question, because the solution is too tricky and requires too much mathematical proof. If you never seen that problem before, it is very hard to come out the solution from scratch at the code interview.  

Follow up: How to know the length of the cycle?
It is not hard to come up with the solution. From the collision point in the cycle, use two pointers, one moves one step at a time while the other moves two at a time. Until they meet again, the number of steps is the length of the cycle. 

It is relatively easy to understand the idea. From the collision point, one stand still while the other moves one step at a time, the next they meet, the moving pointer must move one cycle. 

Summary:
This question composed the three classic questions in the circular linked list:
1. Determine a linked list have a cycle?
2. If yes, what is the entrance node of the cycle?
3. If yes, what is the length of the cycle?
Be familiar with the solutions. 


Leetcode: Linked List Cycle

Given a linked list, determine if it has a cycle in it.
Follow up:
Can you solve it without using extra space?


Understand the problem:
This is a classic code interview problem. Before we jump into any solutions, let's take a look at the question. The question asks for if a linked list have a cycle in it. 

So we must understand what is a cycle in a linked list? A cycle in the linked list means the trail of the linked list points to another previous node. For instance, 1 -> 2 ->3 ->4 ->5 ->2. 
Note that cycle is prohibited in a linked list anyway because it will make the linked list never ends. Remember that for a linked list, we only know the current node and its next. 

Consequently, we can define several more examples to illustrate the problem.
1 -> 2  ->3  -> 4, has no cycle
 1 -> 3 ->4 -> 1, has a cycle


Naive Solution:
As a straight-forward solution, it is very easy to see that for a linked list without cycle, all nodes will never repeat; otherwise, we must see a repeated node. So we can use a Hash Table to store the visited nodes. If not see before, we put into the hash table, else the linked list must have a cycle. 

Before we decide to implement the algorithm, let's analyze the solution. For the time complexity, it is O(n) because the linked list is traversed only once. The space complexity is O(n) as well because it needs at most n key-value pairs to store the visited nodes. 


A better Solution:
Since this question asks for solving it without using extra space, we need to devise another solution without using hash table. 

Most of the linked list questions can be solved by using two pointers, either with different speed, or the same but different distance. We can use the similar idea to solve this question. 

Use two pointers. One moves one node at a time, while the other moves two at a time. If the linked list has a circle, they must meet at the circle. It is easy to understand the idea. One pointer moves one step while the other moves two equals to one stands and the other moves one step a time to chase him. In a circle, two pointers must meet somewhere. 

Code (Java):
/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public boolean hasCycle(ListNode head) {
        if (head == null) return false;
        
        ListNode slow = head;
        ListNode fast = head;
        
        while (fast !=null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
            
            if (slow == fast) return true;
        }
        return false;
    }
}

Discussion:
Now let's analyze the complexity of this solution. If the linked list have a cycle, we are sure that the collision point must be within the cycle. In worst case, when the slower pointer is at the entrance of the cycle and faster pointer is just one step ahead. The fast pointer must chase the length of cycle steps to catch up the slow pointer. In the best case, when the slow pointer is at the entrance of the cycle, the fast pointer is one step behind it, then they will meet at the next step. In this case, it takes 1 step to catch up the slow pointer. Therefore, on the average case, the faster pointer takes O(n) steps to catch up the slow pointer.

Summary:
Determine a linked list has a cycle is a very classic interview problem. The solution is tricky but not hard to understand. Most of the linked list problems can be solved by using two pointers. 

Last but not least, when you use two pointers, make sure the check the fast pointer will not "overflow", i.e, move to a wild address. Make sure you understand the Line 19.