Wednesday, June 11, 2014

[LeetCode] Merge Sorted Array

Problem Statement (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 andn respectively.
Analysis:
The obvious way is to insert B's entries into A after each comparison, and shift all the rest of A to right. Repeating this until we reach the end of B. I bet you'll get TLE.

The way to improve is considering about avoid these shifts. As the A[m+1 : m+n] is empty, we could start from the large values and works backwards. See the following implementation. The time complexity is O(m+n) and no extra space is needed.

Code:
class Solution {
public:
    void merge(int A[], int m, int B[], int n) {
        if (n==0) return;
        if (m==0) {
            for (int i=0; i<n; i++)
                A[i]=B[i];
            return;
        }

        int curA=m; int curB=n;
        while(curB>0 && curA>0) {
            if (A[curA-1]>=B[curB-1]) {
                A[curA+curB-1]=A[curA-1];
                curA--;
            }
            else  {
                A[curA+curB-1]=B[curB-1];
                curB--;
            }
        }

        if (curB>0)
            for (int i=0; i<curB; i++)
                A[i]=B[i];
        if (curA>0)
            return;
    }
};



Tuesday, June 10, 2014

[LeetCode] Unique Paths I && II

Unique Paths I

Problem Statement (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.
Analysis:
First, notice that this is a DP problem, the number of paths to reach (i, j) equals to the number of paths to reach (i-1, j) + the number of paths to reach (i, j-1), except the first row and column, where the number of paths are all 1.

It's obvious that we could use a 2D DP to solve it. However, if we consider reusing a 1D DP vector, we could solve it with O(n) time complexity. The time complexity is O(m*n).

Code:
Sol 1: 2D DP
class Solution {
public:
    int uniquePaths(int m, int n) {
        vector<vector<int>> res(m, vector<int>(n, 0));
        for (int i=0; i<m; i++)
            res[i][0]=1;
        for (int j=0; j<n; j++)
            res[0][j]=1;

        for (int i=1; i<m; i++)

            for (int j=1; j<n; j++)
                res[i][j]=res[i-1][j]+res[i][j-1];
        return res[m-1][n-1];
    }
};

Sol 2: 1D DP
class Solution {
public:
    int uniquePaths(int m, int n) {
        vector<int> res(n, 1);
        for (int i=1; i<m; i++)
            for (int j=1; j<n; j++)
                res[j]=res[j-1]+res[j];
        return res[n-1];
    }
};


Unique Paths II

Problem Statement (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.
Analysis:
The basic idea is same as the previous problem. Just a few special cases that we need to take care of when encounter obstacles.

- For the first row and first column, once we encounter an obstacle, that space and all the following spaces should be set to 0 as we couldn't reach these places;
- For the rest spaces, once we encounter an obstacle, that space should be set to 0; otherwise, we sum up the value in its left and top and put the sum to that space. Note that the summation takes case of the cases where either or both its left and top are 0.

The space complexity is O(n) and time complexity is O(m*n).

Code:
class Solution {
public:
    int uniquePathsWithObstacles(vector<vector<int> > &obstacleGrid) {
        int m=obstacleGrid.size();
        int n=obstacleGrid[0].size();
        vector<int> res(n, 0);

        // initialize using first row
        for (int j=0; j<n; j++) {
            if (obstacleGrid[0][j]==1)
                break;
            res[j]=1;
        }

        for (int i=1; i<m; i++) {
            for (int j=0; j<n; j++) {
                // assign the fist element
                if (j==0 && (obstacleGrid[i][0]==1 || res[0]==0)) {
                    res[0]=0;
                    continue;
                }
                else if (j==0 && obstacleGrid[i][0]==0) {
                    res[0]=1;
                    continue;
                }

                // the rest
                if (obstacleGrid[i][j]==0)
                    res[j]=res[j-1]+res[j];
                if (obstacleGrid[i][j]==1)
                    res[j]=0;
            }
        }
        return res[n-1];
    }
};



[LeetCode] Path Sum I && II

Path Sum I
Problem Statement (link):
Given a binary tree and a sum, determine if the tree has a root-to-leaf path such that adding up all the values along the path equals the given sum.
For example:
Given the below binary tree and sum = 22,
              5
             / \
            4   8
           /   / \
          11  13  4
         /  \      \
        7    2      1
return true, as there exist a root-to-leaf path 5->4->11->2 which sum is 22.
Analysis:
This problem is a variation of tree traversal. We DFS the binary tree to find if any root-to-leaf path has the same value sum. We continue searching until we find a path.

Code:
/**
 * Definition for binary tree
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    bool hasPathSum(TreeNode *root, int sum) {
        if (root==NULL) return false;
        return trav(root, 0, sum);
    }

    bool trav(TreeNode *node, int pathSum, int sum) {
        bool result = false;
        pathSum += node->val;
        if (node->left==NULL && node->right==NULL)
            if (pathSum==sum)
                return true;
        if (node->left!=NULL)
            result = trav(node->left, pathSum, sum);
        if (!result && node->right!=NULL)
            result = trav(node->right, pathSum, sum);
        return result;
    }
};


Path Sum II
Problem Statement (link):
Given a binary tree and a sum, find all root-to-leaf paths where each path's sum equals the given sum.
For example:
Given the below binary tree and sum = 22,
              5
             / \
            4   8
           /   / \
          11  13  4
         /  \    / \
        7    2  5   1
return
[
   [5,4,11,2],
   [5,8,4,5]
]
Analysis:
The idea is basically the same as the previous problem. But instead of updating the value sum, we store the nodes as we traverse down the tree. When we reach the leaf, we check if the sum value of all nodes in the path equals the expected sum, if so, we push the path to out vector.

It is important to pop_back the node after we push_back it into path vector. This ensures that the path vector only stores the nodes in the current path, and thus we could reuse the path vector as we traverse. This is a common trick in tree traversal.

Code:
/**
 * Definition for binary tree
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    vector<vector<int> > pathSum(TreeNode *root, int sum) {
        vector<vector<int> > out;
        if (root==NULL) return out;
        vector<int> path;
        path.push_back(root->val);
        recur(root, sum, out, path);
        return out;
    }
    void recur(TreeNode *node, int sum, vector<vector<int>> &out, vector<int> &path) {
        if (node->left==NULL && node->right==NULL) {
            int temp=0;
            for (int i=0; i<path.size(); i++) temp+=path[i];
            if (temp==sum) out.push_back(path);
            return;
        }

        if (node->left!=NULL) {
            path.push_back(node->left->val);
            recur(node->left, sum, out, path);
            path.pop_back();
        }
        if (node->right!=NULL) {
            path.push_back(node->right->val);
            recur(node->right, sum, out, path);
            path.pop_back();
        }
    }
};


[LeetCode] Flatten Binary Tree to Linked List

Problem Statement (link):
Given a binary tree, flatten it to a linked list in-place.
For example,
Given
         1
        / \
       2   5
      / \   \
     3   4   6
The flattened tree should look like:
   1
    \
     2
      \
       3
        \
         4
          \
           5
            \
             6
Analysis:
Observe the flattened tree closely, we find that it's basically a pre-order traversal transformation. However, what's more than that is we need to link the tree properly, as in-place transformation is required.

The idea is: before we traverse the left sub-tree of a node, we use a pointer to save the right child, because when we finished left sub-tree traversal, the root node's right child would be the current left child. Additionally, we would need another pointer prev to store the previous visited node, as later when we need to link the node to it's previous node.

Code:
/**
 * Definition for binary tree
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    // Recursive preorder traversal
    void flatten(TreeNode *root) {
        TreeNode *prev = NULL;
        recur(root, prev);
    }
    void recur(TreeNode *node, TreeNode *&prev) {
        if (node==NULL) return;
        TreeNode *saveRightNode = node->right;

        if (prev!=NULL) {
            prev->right=node;
            prev->left=NULL;
        }
        prev=node;
        recur(node->left, prev);
        recur(saveRightNode, prev);
    }
};



[LeetCode] Populating Next Right Pointers in Each Node II

Problem Statement (link):
Follow up for problem "Populating Next Right Pointers in Each Node".
What if the given tree could be any binary tree? Would your previous solution still work?
Note:
You may only use constant extra space.
For example,
Given the following binary tree,
         1
       /  \
      2    3
     / \    \
    4   5    7
After calling your function, the tree should look like:
         1 -> NULL
       /  \
      2 -> 3 -> NULL
     / \    \
    4-> 5 -> 7 -> NULL
Analysis:
The idea is similar to the previous problem I. The difference is that we couldn't simply connect nodes in the same layer easily. We need an extra pointer prev to record the previous visited node on the same layer. See the following iterative solution for details.

Code:
/**
 * Definition for binary tree with next pointer.
 * struct TreeLinkNode {
 *  int val;
 *  TreeLinkNode *left, *right, *next;
 *  TreeLinkNode(int x) : val(x), left(NULL), right(NULL), next(NULL) {}
 * };
 */
class Solution {
public:
    void connect(TreeLinkNode * root) {
        while(root!=NULL) {
            TreeLinkNode *curr=root;
            TreeLinkNode *prev=NULL;

            while (curr!=NULL) { // traverse each layer
                if (curr->left!=NULL) {
                    if (prev!=NULL)
                        prev->next=curr->left;
                    prev=curr->left;
                }
                if (curr->right!=NULL) {
                    if (prev!=NULL) 
                        prev->next=curr->right;
                    prev=curr->right;
                }
                curr=curr->next;
            }

            // find starting node in next layer
            while(root!=NULL) {
                if (root->left!=NULL) {
                    root=root->left;
                    break;
                }
                if (root->right!=NULL) {
                    root=root->right;
                    break;
                }
                root=root->next;
            }
        }
    }
};


[LeetCode] Best Time to Buy and Sell Stock I && II && III

Best Time to Buy and Sell Stock I
Problem Statement (link):
Say you have an array for which the ith element is the price of a given stock on day i.
If you were only permitted to complete at most one transaction (ie, buy one and sell one share of the stock), design an algorithm to find the maximum profit.
Analysis:
The max profit at day i is the difference between prices[i] and the min price before day i. Thus, the max profit until day i is the max among all days before and including day i. We traverse the prices vector and store the min value we found so far, and calculate the max profit of day i in use of the min, update the overall  max profit if new max is larger.

This algorithm has O(n) in time complexity and with constant space complexity.

Code:
class Solution {
public:
    int maxProfit(vector<int> &prices) {
        if (prices.empty() || prices.size()==1) return 0;
        int profit = 0;
        int minPrice = prices[0];
        int len = prices.size();
        for (int i=0; i<len; i++) {
            if (prices[i]<minPrice)
                minPrice = prices[i];
            else
                profit = max(profit, prices[i]-minPrice);
        }
        return profit;
    }
};


Best Time to Buy and Sell Stock II
Problem Statement (link):
Say you have an array for which the ith element is the price of a given stock on day i.
Design an algorithm to find the maximum profit. You may complete as many transactions as you like (ie, buy one and sell one share of the stock multiple times). However, you may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).
Analysis:
The problem is not well-stated, judging from the answer, it implies that you are allowed to sell and buy stock at the same day.

With that, the solution is simple. We make a profit by buying at day i and sell at day j as long as i<j and prices[i]<prices[j]. Thus, we simply traverse the vector and accumulate all the differences between day pairs.

This algorithm has O(n) in time complexity and with constant space complexity.

Code:
class Solution {
public:
    int maxProfit(vector<int> &prices) {
        if (prices.empty() || prices.size()==1) return 0;
        int profit = 0;
        for (int i=0; i<prices.size()-1; i++) {
            if (prices[i+1]>prices[i])
                profit += prices[i+1]-prices[i];
        }
        return profit;
    }
};


Best Time to Buy and Sell Stock III
Problem Statement (link):
Say you have an array for which the ith element is the price of a given stock on day i.
Design an algorithm to find the maximum profit. You may complete at most two transactions.
Note:
You may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).
Analysis:
As we may complete two transaction, the first thought is to separate the vector into two parts and find max profit for each parts. Sol 1 is the implementation of this algorithm. However, it yields TLE in a large test case.

If we think about the previous algorithm, we notice that we scanned many entries multiple times, which piles up to the time complexities. Sol 2 is an O(n) algorithm. In this algorithm, we first scan from left to right to get a vector of max profit that ends at each day i in O(n) time; then we scan from right to left to get another vector of max profit that starts at each day i in O(n) time; finally, we calculate the max profit overall by finding out the day that separate two transactions.

Code:
Sol 1: O(n^2) - TLE
class Solution {
public:
    int maxProfit(vector<int> &prices) {
        if(prices.empty() || prices.size()==1) return 0;
        int profit1 = 0;    // max profit from 0:i
        int profit2 = 0;    // max profit from i:n
        int min1 = 0;
        int min2 = 0;
        int n = prices.size();
        int profit = 0;
        for (int i=0; i<n; i++) {
            // From 0-i
            min1 = prices[0];
            for (int p=0; p<=i; p++){
                if (prices[p]<min1) min1=prices[p];
                else profit1 = max(profit1, prices[p]-min1);
            }
            min2 = prices[i];
            for (int p=i; p<n; p++){
                if (prices[p]<min2) min2=prices[p];
                else profit2 = max(profit2, prices[p]-min2);
            }
            
            profit = max(profit, profit1+profit2);
        }
        return profit;
    }
};

Sol 2: O(n)
class Solution {
public:
    int maxProfit(vector<int> &prices) {
        if (prices.empty() || prices.size()==1) return 0;
        int minp=prices[0]; int profit=0; int maxProfit=0;
        vector<int> left; vector<int> right;

        // traverse from left to right
        for (int i=0; i<prices.size(); i++) {
            if (prices[i]<minp) minp=prices[i];
            else profit=max(profit, prices[i]-minp);
            left.push_back(profit);
        }

        // traverse from right to left
        int maxp=prices[prices.size()-1]; profit=0;
        for (int i=prices.size()-1; i>=0; i--) {
            if (prices[i]>maxp) maxp=prices[i];
            else profit=max(profit,maxp-prices[i]);
            right.push_back(profit);
        }

        // combine the two vectors to find the split index s.t. profit is maximized
        for (int i=0; i<prices.size(); i++) {
            maxProfit=max(maxProfit, left[i]+right[prices.size()-i-1]);
        }
        return maxProfit;
    }
};


Monday, June 9, 2014

[LeetCode] Divide Two Integers

Problem Statement (link):
Divide two integers without using multiplication, division and mod operator.
Analysis:
As multiplication and division operations are forbade, we could using deduction - deduct divisor from dividend until the remainder is less than the divisor, and count how many times we performed the deduction. However, the algorithm yields TLE.

The idea is that instead of deducting the divisor from dividend, we deduct i*divisor from the dividend. We repeatedly increase i to reduce the number of deductions, until the remainder is less that i*divisor.

Takeaway:
abs(INT_MIN) or -INT_MIN doesn't produce positive result. We could choose one of the following approaches:
long long pos1 = abs((double) INT_MIN);

unsigned int pos2 = -INT_MIN; // or abs(INT_MIN)

Code:
class Solution {
public:
    int divide(int dividend, int divisor) {
        if (dividend==0 || divisor==0) return 0;
        int sign=(dividend^divisor)>>31;
        long long rem=abs((double)dividend);
        long long div=abs((double)divisor);

        long long res=0;
        while(rem>=div && rem>0) {
            long long div2=div;
            for (int i=0; rem>=div2; i++, div2<<=1) {
                rem-=div2;
                res+=1<<i;
            }
        }
        return sign==0? res:-res;
    }
};


Sunday, June 8, 2014

[LeetCode] Unique Binary Search Trees I && II

Unique Binary Search Trees I

Problem Statement (link):
Given n, how many structurally unique BST's (binary search trees) that store values 1...n?
For example,
Given n = 3, there are a total of 5 unique BST's.
   1         3     3      2      1
    \       /     /      / \      \
     3     2     1      1   3      2
    /     /       \                 \
   2     1         2                 3
Analysis:
At first sight, this problem seems complicated. We may get some salt if we divide the problem into smaller sub-problems.

If n=0, there is 1 BST, which is the NULL tree.
If n=1, there is 1 BST, which is the single root node.
If n=2, there is 2 BST, which are BST with 1 being the root and 2 being the root.
...

We observe that for a value k, the root node may be any k=[1:n]. Thus, the number of BSTs in total is the number of sub-trees with node k valued from 1 to n.

Suppose the root node is k, then its left sub-tree consists of all nodes less that k, it's right sub-tree consists of all nodes larger than k. Then number of BSTs with root node k is then the product of number of BSTs of left sub-tree and number of BSTs of right sub-tree. Further, The number of BSTs of the left sub-tree is the number of sub-trees with the left root node valued from 1 to k-1, the number of BSTs of the right sub-tree is the number of sub-sub-trees with the right root node valued from k+1 to n. Thus we could model and solve this problem with recursion.

Furthermore, if we think twice about the method, it's sort of similar to the idea of DP - having previous calculated results stored, calculate and update new results.

Code:
class Solution {
public:
    int numTrees(int n) {
        // count[i] - number of unique BST constructed from i nodes
        vector<int> count(n+1);
        count[0] = 1;
        recur(count, n);
        return count[n];
    }

    void recur(vector<int> &count, int n) {
        for (int i=1; i<=n; i++) {
            // k - all possible num of nodes of left subtree
            for (int k=0; k<i; k++) {
                count[i] += count[k]*count[i-k-1];
            }
        }
    }
};


Unique Binary Search Trees II

Problem Statement (link):
Given n, generate all structurally unique BST's (binary search trees) that store values 1...n.
For example,
Given n = 3, your program should return all 5 unique BST's shown below.
   1         3     3      2      1
    \       /     /      / \      \
     3     2     1      1   3      2
    /     /       \                 \
   2     1         2                 3
Analysis:
This problem is a bit difficult as it's not easy to come up with a clear solution.

First, the problem asks for "all" BSTs, similar to the previous problem, we consider using DFS on every possible node values, i.e., from 1 through n.

The difficulty lies in finding a way to store each BST. The root node's value i of a valid BST must be within 1 and n, the root of its left subtree must be within 1 and i-1, the root of its right subtree must be within i+1 and n. This idea is same as that of the previous problem. Now, suppose we have all the possible nodes stored in two vectors - leftSub and rightSub, in order to construct a tree, we must construct a new root node with value i that points to a member in leftSub and points to a member in rightSub, respectively. Then, we push the node into our result vector.

Codes:
/**
 * Definition for binary tree
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    vector<TreeNode *> generateTrees(int n) {
        vector<TreeNode*> res;
        recur(1, n, res);
        return res;
    }
    void recur(int l, int r, vector<TreeNode*> &res) {
        if (l>r) 
            res.push_back(NULL);
        else {
            for (int i=l; i<=r; i++) {
                // recursively build-up left and right subtree for root node i
                vector<TreeNode*> leftSub, rightSub;
                recur(l, i-1, leftSub);
                recur(i+1, r, rightSub);
                // Choose left and right subtree combinations for root node i
                for (int j=0; j<leftSub.size(); j++) {
                    for (int k=0; k<rightSub.size(); k++) { 
                        TreeNode* node=new TreeNode(i);
                        node->left = leftSub[j];
                        node->right= rightSub[k];
                        res.push_back(node);
                    }
                }
            }
        }
    }
};


Friday, June 6, 2014

[LeetCode] Remove Duplicates from Sorted List I && II

Remove Duplicates from Sorted List I

Problem Statement (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.
Analysis:
Use two pointers, one pointer points to the current node, the other pointer advances if there's duplicates.

The time complexity is O(n), where n is length of the linked list. The space complexity is constant.

Code:
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *deleteDuplicates(ListNode *head) {
        if (head==NULL || head->next==NULL) return head;

        ListNode *p1=head;
        while (p1->next!=NULL) {
            ListNode *p2=p1->next;
            while (p2 && p1->val==p2->val)
                p2=p2->next;

            if (p1->next==p2) // no dup
                p1=p1->next;
            else
                p1->next=p2;
        }
        return head;
    }
};


Remove Duplicates from Sorted List II

Problem Statement (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.
Analysis:
Once the previous problem is understood, the only difference with this problem is that we need to save previous node.

Code:
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *deleteDuplicates(ListNode *head) {
        if (head==NULL || head->next==NULL) return head;

        ListNode *prev=new ListNode(INT_MIN);
        prev->next=head;
        head=prev;
        while(prev->next!=NULL) {
            ListNode *curr=prev->next;
            // advance pointer if duplicate
            while(curr->next && curr->val==curr->next->val)
                curr=curr->next;

            if (curr!=prev->next) // re-link prev if duplicate
                prev->next=curr->next;
            else    // advance prev if no duplicate
                prev=prev->next;
        }
        return head->next;
    }
};



[LeetCode] Merge k Sorted List

Problem Statement (link):
Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity.
Analysis:
There are three types of solutions:

Suppose the list has k linked list, the longest linked list is with length n.

1) Naive approach:
We compare each of the first k nodes' value, find the node with smallest value, pull that node out and add it to the new list. We repeat this process until all nodes are removed from the original list.

The time complexity is O(k*n*k) = O(n*k^2)

2) Similar idea to Merge sort
We merge every two linked lists in sequence, and repeat this merging until we are left only one linked list.
In run 1, we did k/2 pair merges and left with (k+1)/2 linked lists;
In run 2, we did k/4 pair merges and left with (k+1)/4 linked lists;
...

The time complexity is O(n*k*log k), the log k comes from the fact that we did log k merges in total.

The implementation of this algorithm is below in Sol 1.

3) Use container which auto-sort the incoming data. Three possible options we have are: multiset, heap, priority_queue. I choose to implement using priority_queue for no good reason.

The idea is simple: push each node in into a size k priority queue, each time we pop out the top node - the one with smallest value among all others, and connect it to output list. As we will go through each node once, and each push operation cost log k, the overall time complexity is O(n*k*log k).

Code:
Sol 1: Merge sort
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */

class Solution {
public:
    ListNode *mergeKLists(vector<ListNode *> &lists) {
        int k=lists.size();
        if (k==0) return NULL;
        return recur(lists, 0, k-1);
    }
    ListNode *recur(vector<ListNode *> &lists, int left, int right) {
        if (left<right) {
            int mid=(left+right)/2;
            return mergeTwoLists(recur(lists, left, mid), recur(lists, mid+1, right));
        }
        else
            return lists[left];
    }
    ListNode *mergeTwoLists(ListNode* head1, ListNode* head2) {
        if (!head1) return head2;
        if (!head2) return head1;

        ListNode *head=new ListNode(INT_MIN);
        ListNode *curr=head;

        while(head1 && head2) {
            if (head1->val<head2->val) {
                curr->next=head1;
                head1=head1->next;
            }
            else {
                curr->next=head2;
                head2=head2->next;
            }
            curr=curr->next;
        }
        if (head1) curr->next=head1;
        else if (head2) curr->next=head2;
        return head->next;
    }
};


Sol 2: Priority queue
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */

class Solution {
private:
    struct cmp {
        bool operator() (ListNode *n1, ListNode *n2) {
            return n1->val>n2->val;
        }
    };

public:
    ListNode *mergeKLists(vector<ListNode *> &lists) {
        if (lists.size()==0) return NULL;
        priority_queue<ListNode *, vector<ListNode *>, cmp> heap;

        // add all lists' heads in heap
        for (int i=0; i<lists.size(); i++)
            if (lists[i]) // skill NULL lists
                heap.push(lists[i]);

        // dummy head
        ListNode* head=new ListNode(INT_MAX);
        ListNode* curr=head;

        // pop and push
        while(!heap.empty()) {
            curr->next=heap.top();
            heap.pop();
            curr=curr->next;
            if (curr->next)
                heap.push(curr->next);
        }
        return head->next;
    }
};

Takeaways:
All the three data structures/abstract data type (ADT) are capable of storing items in sorted order. They allow user-defined comparison functions comp as well.


multiset
heap 
 priority_queue
Type
Container object (ADT)
A way of organizing items in the range
Container adaptor - the standard underlying container is vector
 Implementation 
Self-balanced BST 

 Similar to heap, call make_heap, push_heap, pop_heap to maintain heap properties
Complexity (insert, emplace, erase, find, push, pop etc.)
logarithmic in general, but amortized linear or constant in special cases indicated here
Up to linear in three times the range distance
One push_back call to underlying container, one push_heap call on the range *
Key ops
empty
begin, end
insert, emplace
erase
find
clear
make_heap
push_heap
pop_heap
sort_heap
emplace
empty
pop
push
size
top
Relatives
unordered_multiset - implemented using hash tables 


Other


Default comparison is less, i.e., greatest value at the top

* push_back: in vector case, time complexity is constant in general. But if reallocation happens, the reallocation can be up to linear on the range of the entire data size.
   push_heap: up to logarithmic time cost.


Sunday, June 1, 2014

[LeetCode] Next Permutation

Problem Statement (link):
Implement next permutation, which rearranges numbers into the lexicographically next greater permutation of numbers.
If such arrangement is not possible, it must rearrange it as the lowest possible order (ie, sorted in ascending order).
The replacement must be in-place, do not allocate extra memory.
Here are some examples. Inputs are in the left-hand column and its corresponding outputs are in the right-hand column.
1,2,3 → 1,3,2
3,2,1 → 1,2,3
1,1,5 → 1,5,1
Analysis:
There's a classic algorithm on Wiki of finding the next string permutation in lexicographical order. There are four steps:
1) Find the largest index k where num[k]<num[k+1]
2) Find the largest index l where l>k and num[l]>num[k]
3) Swap num[k] and num[l]
4) Reverse num[k+1 : len], where len is the length of the given string

To see how this algorithm works, scratching a simple example on your own will help.

For our purpose, in addition to step 4, if there's no possible larger string found, we wrap up to find the smallest string by reversing the entire string.

Code:
class Solution {
public:
    void nextPermutation(vector<int> &num) {
        int len=num.size();
        if (len<=1) return;

        // step 1
        int k=0;
        for (int i=0; i<len-1; i++)
            if (num[i]<num[i+1])
                k=i;

        // step 2
        int l=0;
        for (int i=0; i<len; i++)
            if (i>k && num[k]<num[i])
                l=i;

        // step 3 - swap
        swap(num, k, l);

        //step 4 - reverse
        k==l ? reverse(num.begin(), num.end()):reverse(num.begin()+k+1, num.end());
    }
    void swap(vector<int> &num, int a, int b) {
        int t=num[a];
        num[a]=num[b];
        num[b]=t;
    }
};



[LeetCode] Gray Code

Problem Statement (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.
Analysis:
The OJ could only judge one instance of gray code sequence, based on wiki page, the sequence of gray code when n=3 is:

000
001
011
010
110
111
101
100

If we observe carefully, we found out that except for the MSB, where the first four numbers are all 0's and last four numbers are all 1's, the bit sequence are mirrored as color coded above. Thus, our algorithm will build new gray codes when n=k, based on the gray codes when n=k-1. This is an iterative process.

The time complexity is exponential - O(2^n), as we need 1+2+4+...+(2^(n-1)) operations.

Code:
class Solution {
public:
    vector<int> grayCode(int n) {
        vector<int> res(1, 0);
        if (n==0) return res;

        for (int k=0; k<n; k++) {
            int sz=res.size(); //res's size is changing, need to assign a fixed value
            for (int i=sz-1; i>=0; i--) {
                res.push_back((1<<k)+res[i]);
            }
        }
        return res;
    }
};

[LeetCode] Minimum Path Sum

Problem Statement (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.
Analysis:
This problem is obviously a DPproblem. We could construct a 2D dp matrix with the same size as given grid, in which entry dp[i][j] represents the minimum path sum from (0, 0) to (i, j). After we initialize the first row and col of dp matrix, we could iteratively adding new min path sums to each entry. The last dp entry is the result we are looking for. This solution is shown in Sol 1 below. The time and space complexity of this algorithm is O(m*n), where m and n are dimension of the grid.

However, using the same idea from the problem Triangle, we could reduce the space complexity to O(n). The idea is to reuse the dp vector each time we reach a new row/col - depending on which one we choose. See Sol 2 for the implementation of this algorithm.

Code:
Sol 1:

class Solution {
public:
    int minPathSum(vector<vector<int> > &grid) {
        int m=grid.size();
        int n=grid[0].size();
        if (m==0) return 0;
        vector<vector<int>> dp(m, vector<int> (n));

        // init dp
        dp[0][0]=grid[0][0];
        for (int i=1; i<m; i++)
            dp[i][0]=grid[i][0]+dp[i-1][0];
        for (int j=1; j<n; j++)
            dp[0][j]=grid[0][j]+dp[0][j-1];

        // traverse
        for (int i=1; i<m; i++)
            for (int j=1; j<n; j++)
                dp[i][j]=min(dp[i-1][j], dp[i][j-1])+grid[i][j];
      
        return dp[m-1][n-1];
    }
};


Sol 2:

class Solution {
public:
    int minPathSum(vector<vector<int> > &grid) {
        int m=grid.size();
        int n=grid[0].size();
        if (m==0) return 0;
        vector<int> dp(n, INT_MAX);
        dp[0]=0;

        // traverse
        for (int i=0; i<m; i++) {
            dp[0]=grid[i][0]+dp[0];
            for (int j=1; j<n; j++)
                dp[j]=min(dp[j], dp[j-1])+grid[i][j];
        }
        return dp[n-1];
    }
};