Thursday, May 29, 2014

[LeetCode] Combinations

Problem Statement (link):
Given two integers n and k, return all possible combinations of k numbers out of 1 ... n.
For example,
If n = 4 and k = 2, a solution is:
[
  [2,4],
  [3,4],
  [2,3],
  [1,2],
  [1,3],
  [1,4],
]
Analysis:
Another classic DFS problem. Once we see "all possible" key words, we need to consider using DFS idea.

Two things to think about:
1) Avoid duplicates - we use a int st to avoid choosing the same combination again. In other words, we only choose the numbers larger than current numbers.
2) Choose k numbers - we decrease k each time we choose a number.

Code:
class Solution {
public:
    vector<vector<int> > combine(int n, int k) {
        vector<vector<int> > outv;
        if (k==0 || n<k) return outv;
        vector<int> res;
        recur(outv, res, n, k, 0);
        return outv;
    }

    void recur(vector<vector<int>> &outv, vector<int> &res, int n, int k, int st) {
        if (k==0) {
            outv.push_back(res);
            return;
        }

        for (int i=st; i<n; i++) {
            res.push_back(i+1);
            recur(outv, res, n, k-1, i+1);
            res.pop_back();
        }
        return;
    }
};

Tuesday, May 27, 2014

[LeetCode] Insertion Sort List

Problem Statement (link):
Sort a linked list using insertion sort.
Analysis:
We traverse the linked list, once we find a node has a smaller value than that of its previous node, we start another traversal from the very beginning of the linked list to find the right position for that node to insert in.

Note:
- Once we found an unordered node and re-insert it to the right position, we should not advance the prev pointer. For instance: we have 2->4->1->3, the prev pointer points to node 4, and the tmp pointer points to node 1, i.e., we need to re-insert node 1 to its right position. After re-insertion, the list becomes: 1->2->4->3, the prev pointer still points to node 4, if we advance prev to node 3, we would miss re-insertion of node 3. The bool flag is for this purpose.

Extra link:
A very good review of sorting algorithms is summarized here by Yu.

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

class Solution {
public:
    ListNode *insertionSortList(ListNode *head) {
        if (head==NULL) return NULL;
        bool inserted=false;
        ListNode *prev=new ListNode(INT_MIN);
        prev->next=head;
        ListNode *begin=prev;

        while(prev->next->next!=NULL) {
            ListNode *cur=prev->next;
            ListNode* tmp=cur->next;
            if (cur->val>tmp->val) {
                locate(begin, cur, tmp);
                inserted=true;
            }
            else if (prev->next->next!=NULL && inserted==false) {
                prev=prev->next;
                inserted=false;
            }
            else
                prev=prev->next;
        }
        return begin->next;
    }

    // locate and insert target node
    void locate(ListNode* begin, ListNode* prev, ListNode* target) { 
        while(begin->next->val<target->val)
            begin=begin->next;

        // insert
        ListNode* tmp=target->next;
        target->next=begin->next;
        prev->next=tmp;
        begin->next=target;
    }
};


Monday, May 26, 2014

[LeetCode] Count and Say

Problem Statement (link):
The count-and-say sequence is the sequence of integers beginning as follows:
1, 11, 21, 1211, 111221, ...
1 is read off as "one 1" or 11.
11 is read off as "two 1s" or 21.
21 is read off as "one 2, then one 1" or 1211.
Given an integer n, generate the nth sequence.
Note: The sequence of integers will be represented as a string.
Analysis:
No particular algorithms applied to this problem. Simply go through n rounds, wherein we calculate the time of each repeated characters and append to output string.

Code:
class Solution {
public:
    string countAndSay(int n) {
        string sout;
        string s="1";
        while(n-->1) {
            sout=helper(s);
            s=sout;
        }
        return s;
    }

    string helper(string s) {
        int count=0;
        char last=s[0];
        string sout="";
        for (int i=0; i<=s.length(); i++) {
            if (s[i]==last) count++;
            else {
                sout+=to_string(count)+s[i-1];
                last=s[i];
                count=1;
            }
        }
        return sout;
    }
};

Monday, May 19, 2014

Tower of Hanoi

Problem Statement (Wikipedia link):

Move N disks from one peg to another.

Analysis:
The Wikipedia page gives a pretty good explanation of the algorithm. Here we only consider recursive solution.

Suppose we have the following initial setup. Our goal is to move all disks from peg A to peg C.


Let's name the disk from 1, ..., N, where N corresponds to each disk's size.

Our goal is to move all disks from A to C, where A is the source and C is the destination. Thinking about this goal, the only situation that we could be successful is that all other 1, ..., N-1 disks are on peg B - spare, then we could move disk N from A to C. Hence, For these N-1 smaller disks, B is their source peg and C is destination peg. Recursively, the same logic applies to these N-1 disks.

With that, we have the following recursive logic:
1) Move N-1 disks from A to B;
2) Move disk N from A to C;
3) Move N-1 disks from B to C;

For a size N tower of hanoi problem, we need to perform 2^N - 1 movements. Hence, the time complexity is exponential.

Code:
void solveHanoi(int count, char src, char spare, char dest) {
    if (count==1)
        cout<<"Move disk from "<<src<<" to "<<dest<<endl;
    else {
        solveHanoi(count-1, src, dest, spare);
        solveHanoi(1, src, spare, dest);
        solveHanoi(count-1, spare, src, dest);
    }
}



Sunday, May 18, 2014

Knight's Tours

Problem Statement (Wikipedia Link):

On a NxN chess board, find all legal Knight's Tours.

Fig., a valid Knight's Tour demo on N=5 chess board, courtesy of Wikipedia

Analysis:
Same as N-Queens problem, Knight's Tour is a classic problem that can be solved using backtracking. As other backtracking problems, the time complexity is exponential.

I have run a N=5 case, there are in total 304 possible solutions.

Note that the backtracking is not the optimal solution for the problem. See the wiki page here for other better algorithms, such as Divide and Conquer and Neural Networks (here).

Code:
// Main function
int numSols;
bool knightTour(int n, vector<vector<int>>& out) {
    int xmove[8]={-2,-2,-1,-1,1,1,2,2};
    int ymove[8]={1,-1,2,-2,2,-2,1,-1};

    // initialization
    for (int i=0; i<n; i++)
        out.push_back(vector<int> (n, -1));

    // start at upper left corner
    numSols=0;
    out[0][0]=0;
    return recur(0, 0, n, 1, xmove, ymove, out);
}

bool recur(int x, int y, int n, int visited, int xmove[], int ymove[], vector<vector<int>>& out) {
    if (visited>=n*n) {
        numSols++;
        printTour(n, out);
    }
    else { // try next move
        for (int i=0; i<8; i++) {
            if (isValid(x+xmove[i], y+ymove[i], n, out)) {
                out[x+xmove[i]][y+ymove[i]]=visited;
                if (recur(x+xmove[i], y+ymove[i], n, visited+1, xmove, ymove, out))
                    return true;
                else
                    out[x+xmove[i]][y+ymove[i]]=-1;
            }
        }
    }
    return true;
}

bool isValid(int x, int y, int n, vector<vector<int>> out) {
    if (x>=0 && x<n && y>=0 && y<n && out[x][y]==-1) return true;
    return false;
}

void printTour(int n, vector<vector<int>> out) {
    for (int i=0; i<n; i++) {
        for (int j=0; j<n; j++) {
            cout<<out[i][j]<<"\t";
        }
        cout<<endl;
    }
    cout<<"\n";
}

Saturday, May 17, 2014

[LeetCode] N-Queens I && II

N-Queens I
Problem Statement (link):
The n-queens puzzle is the problem of placing n queens on an n×n chessboard such that no two queens attack each other.

Given an integer n, return all distinct solutions to the n-queens puzzle.
Each solution contains a distinct board configuration of the n-queens' placement,
where 'Q' and '.' both indicate a queen and an empty space respectively.
For example,
There exist two distinct solutions to the 4-queens puzzle:
[
 [".Q..",  // Solution 1
  "...Q",
  "Q...",
  "..Q."],

 ["..Q.",  // Solution 2
  "Q...",
  "...Q",
  ".Q.."]
]
Analysis:
Eight queens puzzle is a classic problem in use of DFS backtracking algorithm, it can be generalized to a N-queens puzzle.

The high-level idea of the backtracking algorithm is to choose any location as a start, try all possible positions until no violation to the rule has found. If a violation is found, we immediately abandon current placement and move the next possible position, if all possibilities has been tried for current queen, we go back to the previous queen and move that queen to its next possible position.

As describe in the statement, the rule for this game is: only one queen is placed in each row and column. This rule is used to check whether a position is legal for placing a queen.

Thus, the pseudo code looks like this:
  1. Place the first queen in the left upper corner of the table.
  2. Save the attacked positions.
  3. Move to the next queen (which can only be placed to the next line).
  4. Search for a valid position. If there is one go to step 8.
  5. There is not a valid position for the queen. Delete it (the x coordinate is 0).
  6. Move to the previous queen.
  7. Go to step 4.
  8. Place it to the first valid position.
  9. Save the attacked positions.
  10. If the queen processed is the last stop otherwise go to step 3.
The time complexity is exponential, same as all other backtracking algorithms.

A space-saving trick is we use 1-D vector to store the queens' 2-D location, where A[i]=j indicates row i, col j has a queen.

Code:
class Solution {
public:
    vector<vector<string> > out;
    // main function
    vector<vector<string> > solveNQueens(int n) {
        if (n==0) return out;
        vector<int> A(n, -1);   // Stores state for current solution, A[i]=j indicates row i, col j has a queen
        placeQueen(A, 0, n);    // starts from row 0
        return out;
    }

    // iterate thru row
    void placeQueen(vector<int> A, int cur, int n){ // cur - current row #
        if (cur==n) printOut(A, n);
        else {
            for (int i=0; i<n; i++){    // traverse thru each row, i-col
                A[cur]=i;
                if (isValid(A,cur)) {
                    placeQueen(A, cur+1, n);
                }
            }
        }
    }

    // To check if the current position is valid to place a queue
    bool isValid(vector<int> A, int row){
        for (int k=0; k<row; k++) { // row iteration
            if (A[k]==A[row] || (abs(A[k]-A[row])==(row-k))) {
                return false;
            }
        }
        return true;
    }

    // Update out to include current solution
    void printOut(vector<int> A, int n) {
        vector<string> v;   // a solution
        for (int i=0; i<n; i++){
            string s(n,'.');    // for each row
            s[A[i]] = 'Q';
            v.push_back(s);
        }

        out.push_back(v);
    }
};


N-Queens II
Problem statement (link):
Follow up for N-Queens problem.
Now, instead outputting board configurations, return the total number of distinct solutions.
Analysis:
The algorithm is exactly the same as in N-Queens I. Only difference is that instead of populating the location of each queens when we find a solution, we only need to count the number of solutions.

Code:
class Solution {
public:
    vector<vector<string> > out;
    int num;
    // main function
    int totalNQueens(int n) {
        if (n==0) return 0;
        vector<int> A(n, -1);   // Stores state for current solution, A[i]=j indicates row i, col j has a queen
        num = 0;
        placeQueen(A, 0, n);    // starts from row 0
        return num;
    }

    // iterate thru row
    void placeQueen(vector<int> A, int cur, int n){ // cur - current row #
        if (cur==n) num++;
        else {
            for (int i=0; i<n; i++){    // traverse thru each row, i-col
                A[cur]=i;
                if (isValid(A,cur)) {
                    placeQueen(A, cur+1, n);
                }
            }
        }
    }

    // To check if the current position is valid to place a queue
    bool isValid(vector<int> A, int row){
        for (int k=0; k<row; k++) { // row iteration
            if (A[k]==A[row] || (abs(A[k]-A[row])==(row-k))) {
                return false;
            }
        }
        return true;
    }
};

Friday, May 16, 2014

[LeetCode] Minimum Window Substring

Problem Statement (link):
Given a string S and a string T, find the minimum window in S which will contain all the characters in T in complexity O(n).
For example,
S = "ADOBECODEBANC"
T = "ABC"
Minimum window is "BANC".
Note:
If there is no such window in S that covers all characters in T, return the emtpy string "".
If there are multiple such windows, you are guaranteed that there will always be only one unique minimum window in S.
Analysis:
We use two index pointers to maintain a sub-string window that contains the chars in T. Two hash tables are used, one contains all the chars in T as key field and their occurrences as value field; the other one is dynamic hash table that maintains chars in the sub-string window and their occurrences, these chars are only those appear in T. Further, an int is used to keep track of the number of matching chars in the sub-string window. This int is important as it closely associates with the movement of the two index pointers.

The pseudo-code goes like this:
Initialize parameters, two indexes == 0
Scan through T to update needtoFind hash table;
Start from the beginning of S, for each end value:
    if current char is not in T, continue;
    otherwise, add current char to hasFound hash table;
    if the occurrences of current char in hasFound is no larger than that in needtoFind, increase count;

    if count equals T size:
        if current char does not exist in T or its occurrences in hasFound is larger than that in needtoFind, we advance begin pointer, meanwhile, if current char exists in T and hasFound has larger occurrences, decrease the value in hasFound
        if current window is smaller, update the minimum string and its size

To better understand the algorithm, e.g., S = "DOBECAODEBAANC", T = "AABC", the red chars represent the dynamic window.

S
Variables

DOBECAODEBAANC
begin = 0, end = 0

DOBECAODEBAANC
begin = 0, end = 10Advance end until find a match
DOBECAODEBAANC
begin = 5, end = 10Advance begin 
DOBECAODEBAANC
begin = 5, end = 11count stays the same, advance end, increase hasFound['A']
DOBECAODEBAANC
begin = 5, end = 13Advance end, increase hasFound['C']
DOBECAODEBAANC
begin = 6, end = 13Advance begin, decrease hasFound['C']
DOBECAODEBAANCbegin = 9, end = 13Advance begin, decrease hasFound['A']

Take-aways:
In C++11, we could manipulate the nonexistent keys in unordered_map directly without initialize that key value, and the initial value of that key is 0. For instance, the following code is legal:

unordered_map<char, int> map;
int val = map['a'];   // val==0
map['b']--;     // map['b']==-1
int sz = map.size();   // sz==2

Code:
class Solution {
public:
    string minWindow(string S, string T) {
        int begin=0, end=0;  // for scanning S
        int m=INT_MAX;  // min window size
        string ms="";      // min string
        int count=0;    // matching num of chars in current window

        unordered_map<char, int> hasFound;      // for string S
        unordered_map<char, int> needtoFind;    // for string T

        for (int i=0; i<T.size(); i++)
            needtoFind[T[i]]++;

        for (int end=0; end<S.size(); end++) {
            // skip chars not in T
            if (needtoFind.find(S[end])==needtoFind.end()) continue;
            // o.w. add char into hasFound table
            hasFound[S[end]]++;
            if (hasFound[S[end]] <= needtoFind[S[end]]) count++;

            if (count==T.size()) {
                // advance begin pointer
                while (needtoFind.find(S[begin])==needtoFind.end() || hasFound[S[begin]]>needtoFind[S[begin]]) {
                    if (needtoFind.find(S[begin])!=needtoFind.end() && hasFound[S[begin]]>needtoFind[S[begin]]) hasFound[S[begin]]--;
                    begin++;
                }

                // update min string
                int winLen=end-begin+1;
                if (winLen<m) {
                    m=winLen;
                    ms=S.substr(begin, winLen);
                }
            }
        }
        return ms;
    }
};



[LeetCode] Median of Two Sorted Arrays

Problem Statement (link):
There are two sorted arrays A and B of size m and n respectively. Find the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)).
Analysis:

- An O(m+n) solution
It is obvious we can merge the two array into one big array in O(m+n) time, and find the median in O(1). In addition, this requires O(m+n) space.

- An O(k) solution
As the two arrays are sorted and we know the index of median of the merged array - either (m+n)/2 in odd case or ((m+n)/2+(m+n)/2-1)/2 in even case, we could simply count from the start of two arrays until we reach the index.

- An O(log (m + n)) solution
The above two algorithms are in linear time, as we are required to solve the program in log time, it's only logical that we consider binary search. In addition, this problem can be generalized to finding the k-th smallest element in two sorted arrays, hereby k = (m+n)/2+1, note that k is not the index that starts from 0, instead it starts from 1.

In each recursion, we find the medians of two arrays. The two medians will split the two arrays into four parts as shown below:
Figure, The medians separates two arrays into four parts. Courtesy of Lei Zhang.

As shown in the picture, we need to consider the two conditions in each round and discard one part as we do in binary search. The logic goes like this:

- k >= m/2+n/2+1 && am/2 bn/2, discard Section 3;
- k >= m/2+n/2+1 && am/2 < bn/2, discard Section 1;
- k < m/2+n/2+1 && am/2 > bn/2, discard Section 2;
- k < m/2+n/2+1 && am/2 < bn/2, discard Section 4;

For instance, if k >= m/2+n/2+1, we know that the k-th item must be in larger half of the merged array; further, if  am/2 > bn/2, then we could discard section 3 as its elements are too small to be median. The other three conditions follow the same logic.

Code:
class Solution {
public:
    // generalized to finding kth smallest element problem
    double findMedianSortedArrays(int A[], int m, int B[], int n) {
        int total=m+n;
        if (total%2==1)
            return recur(A,m,B,n,total/2+1);
        else
            return (recur(A,m,B,n,total/2)+recur(A,m,B,n,total/2+1))/2.0;
    }

    // find k-th smallest element
    double recur(int A[], int m, int B[], int n, int k) {
        if (m<=0) return B[k-1];
        if (n<=0) return A[k-1];
        if (k<=1) return min(A[0], B[0]);

        int mid=m/2+n/2+1;
        if (B[n/2]>=A[m/2]) {
            if (mid>=k)
                return recur(A, m, B, n/2, k);
            else
                return recur(A+m/2+1, m-(m/2+1), B, n, k-(m/2+1)); 
        }
        else {
            if (mid>=k)
                return recur(A, m/2, B, n, k);
            else
                return recur(A, m, B+n/2+1, n-(n/2+1), k-(n/2+1));
        }
    }
};


Takeaways:
Sometimes we cannot simplify integer calculation argument, e.g., m/2-1 != m-(m/2+1)

Tuesday, May 13, 2014

[LeetCode] Populating Next Right Pointers in Each Node I

Populating Next Right Pointers in Each Node I

Problem Statement (link):
Given a binary tree
    struct TreeLinkNode {
      TreeLinkNode *left;
      TreeLinkNode *right;
      TreeLinkNode *next;
    }
Populate each next pointer to point to its next right node. If there is no next right node, the next pointer should be set to NULL.
Initially, all next pointers are set to NULL.
Note:

  • You may only use constant extra space.
  • You may assume that it is a perfect binary tree (ie, all leaves are at the same level, and every parent has two children).

For example,
Given the following perfect binary tree,
         1
       /  \
      2    3
     / \  / \
    4  5  6  7
After calling your function, the tree should look like:
         1 -> NULL
       /  \
      2 -> 3 -> NULL
     / \  / \
    4->5->6->7 -> NULL
Analysis:
BFS using queue is an obvious solution to this problem. However, as we are asked to solve it without using extra space, we gotta find another way.

There are two types of connections:
1) Connect a node's left child to its right child;
2) Connect a node's right child to the node's sibling's left child, when the node doesn't have a sibling, we set its right child's next pointer to NULL;

A key is to the solution is that we need to connect the nodes while we are at their parent level, since we couldn't go back to the parent's sibling for type 2) connection.

The following code shows both recursive and iterative approaches.

Code:
Sol 1 - Recursive
/**
 * 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) {
        if (!root) return;
        if (root->left)
            root->left->next=root->right;
        if (root->right)
            root->right->next= root->next ? root->next->left:NULL;

        connect(root->left);
        connect(root->right);
    }
};

Sol 2 - Iterative
class Solution {
public:
    void connect(TreeLinkNode *root) {
        while(root!=NULL) {
            TreeLinkNode* firstNode=root;
            while(firstNode!=NULL) {
                if (firstNode->left)
                    firstNode->left->next=firstNode->right;
                if (firstNode->right)
                    firstNode->right->next= firstNode->next ? firstNode->next->left:NULL;

                firstNode=firstNode->next;
            }
            root=root->left;
        }
    }
};

Binary Tree Level-order traversal

Problem Statement:
Implement binary tree level-order traversal.

Analysis:
We usually use queue to realize BFS, the implementation is done in Sol 1. This is a standard BFS algorithm. It requires O(n) space and O(n) time, where n is the number of nodes in the tree.

However, recursion can be used to solve this problem as well, this algorithm is implemented in Sol 2. As we could see, we traverse the tree h time in order to print nodes in each level, where h is the tree height. This leads to the in-efficiency of this algorithm. However, it's good to know that recursion could be used in BFS problems. The overall time complexity is O(n^2), where n is the number of nodes in the tree.

Code:
Sol 1:
void levelOrderIter(TreeNode* root) {
    queue<TreeNode*> q;

    q.push(root);
    while (!q.empty()) {
        TreeNode* tmp=q.front();
        q.pop();
        cout<<tmp->val<<endl;

        if (tmp->left!=NULL) q.push(tmp->left);
        if (tmp->right!=NULL) q.push(tmp->right);
    }
    return;
}

Sol 2:
void levelOrder(TreeNode* root) {
    // height of tree
    int height=getHeight(root);
    // traverse each level
    for (int i=0; i<height; i++)
        printLevel(root, i);
    return;
}

void printLevel(TreeNode* node, int level) {
    if (node==NULL) return;

    if (level==0) {
        cout<<node->val<<endl;
        return;
    }

    printLevel(node->left, level-1);
    printLevel(node->right, level-1);
}

int getHeight(TreeNode* node) {
    if (node==NULL) return 0;
    int lHeight=getHeight(node->left);
    int rHeight=getHeight(node->right);
    return (lHeight>rHeight ? lHeight:rHeight)+1;
}

Monday, May 12, 2014

[LeetCode] Binary Tree Maximum Path Sum

Problem Statement (link):
Given a binary tree, find the maximum path sum.
The path may start and end at any node in the tree.
For example:
Given the below binary tree,
       1
      / \
     2   3
Return 6.
Analysis:
The idea is to use recursive DFS. For each node, we find the max sum for its left and right sub-tree, adding the node's value, we return a max summation to its parent.

In the following code, the maxSum variable is used to record the maximum summation that we got so far. The maxPath variable is the maximum summation of current node value adding its max left and right sub-tree path values.

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:
    int maxPathSum(TreeNode* root) {
        int maxSum = (root==NULL) ? 0:root->val;
        pathSum(root, maxSum);
        return maxSum;
    }

    // maxSum tracks the current max sum via a particular root node
    int pathSum(TreeNode* node, int& maxSum) {
        if (node==NULL) return 0;

        int l = pathSum(node->left, maxSum);
        int r = pathSum(node->right, maxSum);

        int maxPath = max(l>0?l:0, r>0?r:0) + node->val;
        maxSum = max(maxSum, (l>0?l:0) + (r>0?r:0) + node->val);
        return maxPath;
    }
};


[LeetCode] Sort Colors

Problem Statement (link):
Given an array with n objects colored red, white or blue, sort them so that objects of the same color are adjacent, with the colors in the order red, white and blue.
Here, we will use the integers 0, 1, and 2 to represent the color red, white, and blue respectively.
Note:
You are not suppose to use the library's sort function for this problem.
Follow up:
A rather straight forward solution is a two-pass algorithm using counting sort.
First, iterate the array counting number of 0's, 1's, and 2's, then overwrite array with total number of 0's, then 1's and followed by 2's.
Could you come up with an one-pass algorithm using only constant space?
Analysis:
As the problem Follow-up suggested, we could use counting sort to solve this problem, it's implemented in Sol 1 below.

However, to solve this problem in one pass, we need to think otherwise. Actually, this problem is called "Dutch flag problem". The thought is to have three parameters indicating the boundaries, and as we scan through the entire array, we keep swapping entries.

Code:
Sol 1 - counting sort:
class Solution {
public:
    void sortColors(int A[], int n) {
        int r = 0; int w = 0; int b = 0;

        // pass 1 - count
        for(int k = 0; k < n; k++) {
            if (A[k] == 0)  r++;
            else if (A[k] == 1) w++;
            else if (A[k] == 2) b++;
        }

        // pass 2 - assign values
        for (int k = 0; k < n; k++) {
            if (k < r)  A[k] = 0;
            else if (k < r + w && k >= r)   A[k] = 1;
            else if (k >= r + w) A[k] = 2;
        }
    }
};


Sol 2 - Dutch flag problem algorithm:
class Solution {
public:
    void sortColors(int A[], int n) {
        int low=0;
        int mid=0;
        int high=n-1;

        while(mid<=high) {
            if (A[mid]==0) swap(A,low++,mid++);
            else if (A[mid]==1) mid++;
            else if (A[mid]==2) swap(A,mid,high--);
        }
    }
    void swap(int A[], int a, int b) {
        int tmp=A[a];
        A[a]=A[b];
        A[b]=tmp;
    }
};

Sunday, May 11, 2014

[LeetCode] Jump Game I && II

Jump Game I
Problem Statement (link):
Given an array of non-negative integers, you are initially positioned at the first index of the array.
Each element in the array represents your maximum jump length at that position.
Determine if you are able to reach the last index.
For example:
A = [2,3,1,1,4], return true.
A = [3,2,1,0,4], return false.
Analysis:
Starting from the first entry, for each of the reachable next entries, we find the maximum reachable position. If the max reachable position is at end of array, return true; otherwise, return false.

Code:
class Solution {
public:
    bool canJump(int A[], int n) {
        int maxReach=0;

        for (int i=0; i<=maxReach && maxReach<=n-1; i++)
            maxReach=max(maxReach, A[i]+i);

        return maxReach>=n-1;
    }
};


Jump Game II
Problem Statement (link):
Given an array of non-negative integers, you are initially positioned at the first index of the array.
Each element in the array represents your maximum jump length at that position.
Your goal is to reach the last index in the minimum number of jumps.
For example:
Given array A = [2,3,1,1,4]
The minimum number of jumps to reach the last index is 2. (Jump 1 step from index 0 to 1, then 3 steps to the last index.)
Analysis:
We use the same methodology as that in the previous problem to find the farthest reachable position - maxReach - from index i. The idea is that we record the number of steps while we reach the farthest possible position from current index i. As long as we reach at or farther than the end of array, we found the solution.

E.g., A = [2, 3, 1, 1, 4, 1]
run 1: i = 0, found maxReach = 2 starting at index 0, number of steps = 1
run 2: i = 1, found maxReach = 4 starting at index 1, number of steps = 2
run 3: i = 4, found maxReach = 8 starting at index 4, number of steps = 3
--> found.

Again, the core of this algorithm is finding the farthest reachable position starting within the reachable range from previous index.

Code:
class Solution {
public:
    int jump(int A[], int n) {
        int maxReach=0, currFar=0, minStep=0;

        for (int i=0; i<n; ) {
            if (currFar>=n-1) break;

            while(i<=currFar) { // same as the for loop in previous problem
                maxReach=max(maxReach,A[i]+i);
                i++;
            }
            minStep++;
            currFar=maxReach;
        }
        return minStep;
    }
};

Saturday, May 10, 2014

[LeetCode] Permutations I & II

Permutations I
Problem Statement (link):
Given a collection of numbers, return all possible permutations.
For example,
[1,2,3] have the following permutations:
[1,2,3][1,3,2][2,1,3][2,3,1][3,1,2], and [3,2,1].
Analysis:
A picture worth a thousand words:
Fig. Algorithm demonstration (courtesy of Yu)

The algorithm is sort of like DFS. We start at the original vector, for each int at index i, we swap it with each int from index i to n, and we move to the next index i+1 recursively. If we reach the end, we push the result vector to our solution vector. Remember we need to swap back the two entries when getting back to the previous layer.

Code:
class Solution {
public:
    vector<vector<int> > permute(vector<int> &num) {
        vector<vector<int> > out;
        if (num.empty()) return out;

        perm(num, out, 0, num.size());
        return out;
    }
   
    void perm(vector<int> & num, vector<vector<int> >& out, int idx, int n) {
        if (idx==n)
            out.push_back(num);
        else {
            for (int i=idx; i<n; i++) {
                swap(num, idx, i);
                perm(num, out, idx+1, n);
                swap(num, idx, i);
            }
        }
    }

    void swap(vector<int> &num, int i, int j) {
        int tmp=num[i];
        num[i]=num[j];
        num[j]=tmp;
    }
};


Permutations II
Problem Statement (link):
Given a collection of numbers that might contain duplicates, return all possible unique permutations.
For example,
[1,1,2] have the following unique permutations:
[1,1,2][1,2,1], and [2,1,1].
Analysis:
If contains duplicates, the problem is trickier. My initial try was identifying if two swapping ints is same, if not, we swap. But it turns out this yields duplicated result. E.g., we have input 0111, if we swap ints at index 0 and 1, we have 1011; if we swap ints at index 0 and 2, we have 1011.

The condition should be more strict. If we want to swap two ints at index i and j, the idea is to check if any int between i and j are duplicates, if yes, we shouldn't swap as we must have already swapped a same int before. Back to our 0111 example, we could swap ints at index 0 and 1, then we go to swap ints at index 0 and 2, we found two ints at index 1 and 2 are same, then we do not execute the swapping. As a matter of fact, we've already swapped int 0 with int 1 before.

Code:
class Solution {
public:
    vector<vector<int> > permuteUnique(vector<int> &num) {
        vector<vector<int> > out;
        if (num.empty()) return out;
        sort(num.begin(), num.end()); // sorting for easier identifying duplicates
        perm(num, out, 0, num.size());
        return out;
    }

    void perm(vector<int> &num, vector<vector<int> >& out, int idx, int n) {
        if (idx==n)
            out.push_back(num);
        else {
            for (int i=idx; i<n; i++) {
                if (!containDup(num, idx, i)) { // only swap if no duplicates
                    swap(num, idx, i);
                    perm(num, out, idx+1, n);
                    swap(num, idx, i);
                }
            }
        }
    }

    bool containDup(vector<int> num, int st, int ed) {
        for (int i=st; i<ed; i++)
            if (num[i]==num[ed])
                return true;
        return false;
    }

    void swap(vector<int> &num, int i, int j) {
        int tmp=num[i];
        num[i]=num[j];
        num[j]=tmp;
    }
};

[LeetCode] Gas Station

Problem Statement (link):
There are N gas stations along a circular route, where the amount of gas at station i is gas[i].
You have a car with an unlimited gas tank and it costs cost[i] of gas to travel from station i to its next station (i+1). You begin the journey with an empty tank at one of the gas stations.
Return the starting gas station's index if you can travel around the circuit once, otherwise return -1.
Note:
The solution is guaranteed to be unique.
Analysis:
Brute force solution is trying out every possible start index, if the cumulative gas is negative at some point, it implies the start index we chose is not a valid solution, we will then move to next index to check. This algorithm could be implemented with two for loops and yield a O(N^2) time complexity.

To figure out a more efficient solution, we need to think about the characteristics of this problem. We notice that:
- If starts at index i, the car has to stop at station j if the left gas in the tank plus gas[j] is smaller than cost[j]. The left gas is calculated by summing up gas[i : j] minus cost[i : j];
- Further, if a car stops at station j, then any station between i and j could not be a starting station. This is crucial to lower time complexity. Consider station k, where i<k<j, as the car doesn't stop until station j, then the gas-cost sum from i to k must be positive; also, as the car stop at station j, then the gas-cost sum from i to j must be negative, this implies that the gas-cost sum from k to j must be negative, thus, k could not be a starting point.

With this understood, we start at any station i, and accumulate gas-cost. Once we find the accumulation is negative at station j, we change the starting station to the next station j+1, rather than i+1 in the brute force algorithm. The new algorithm has O(N) complexity. If the accumulative sum of all gas-cost is negative, we return -1 as there is no possible solution; otherwise, we return the start station index.

Code:
class Solution {
public:
    int canCompleteCircuit(vector<int> &gas, vector<int> &cost) {
        int st=0, left=0, sum=0; // left-gas left in the tank
        for (int i=0; i<gas.size(); i++) {
            left+=gas[i]-cost[i];
            sum+=gas[i]-cost[i];
            if (sum<0) {
                sum=0;
                st=i+1;
            }
        }
        return left<0 ? -1:st;
    }
};

Tuesday, May 6, 2014

[LeetCode] Search for a Range

Problem Statement (link):
Given a sorted array of integers, find the starting and ending position of a given target value.
Your algorithm's runtime complexity must be in the order of O(log n).
If the target is not found in the array, return [-1, -1].
For example,
Given [5, 7, 7, 8, 8, 10] and target value 8,
return [3, 4].
Analysis:
The search algorithm is same as that in the Search Insert Position problem.

- First we search if the target value exists in the array. If not, we return the [-1, -1] as required.
- Next we search for the lower and upper index bound of the given value in the array. A trick is to search for a value that is a bit smaller and a bit larger than the target value, as we are sure then the searching value doesn't exist in the array and will return us the exact index boundary of the given target value. Note that we need to -1 for the upper bound as we are not looking for the inserting position of the larger value.

The time complexity is O(log n). Precisely, we execute the binary search three times on the array.

Code:
class Solution {
public:
    vector<int> searchRange(int A[], int n, int target) {
        vector<int> index(2,-1);
        int found = recur(A, 0, n-1, n, (double)target);
        if (A[found]!=target) // if target doesn't exist in the array
            return index;

        index[0]=recur(A, 0, n-1, n, target-0.5);
        index[1]=recur(A, 0, n-1, n, target+0.5)-1; 
        return index;
    }

    int recur(int A[], int st, int ed, int n, double target) {
        if (st>ed) return st;

        int mid=(st+ed)/2;
        if (A[mid]<target)
            recur(A, mid+1, ed, n, target);
        else
            recur(A, st, mid-1, n, target);
    }
};

[LeetCode] Search Insert Position

Problem Statement (link):
Given a sorted array and a target value, return the index if the target is found. If not, return the index where it would be if it were inserted in order.
You may assume no duplicates in the array.
Here are few examples.
[1,3,5,6], 5 → 2
[1,3,5,6], 2 → 1
[1,3,5,6], 7 → 4
[1,3,5,6], 0 → 0
Analysis:
It's obvious we can use binary search to find the correct position. A little bit tweaking can make code much cleaner, see below.

The time complexity of this binary search is O(log n).

Code:
class Solution {
public:
    int searchInsert(int A[], int n, int target) {
        if (n==0) return 0;
        return recur(A, n, 0, n-1, target);
    }

    int recur(int A[], int n, int st, int ed, int target) {
        if (st>ed) return st; // when not found the same value in array

        int mid=(st+ed)/2;
        if (A[mid]==target) return mid;
        else if (A[mid]>target)
            recur(A, n, st, mid-1, target);
        else
            recur(A, n, mid+1, ed, target);
    }
};

Sunday, May 4, 2014

[LeetCode] Clone Graph

Problem Statement (link):
Clone an undirected graph. Each node in the graph contains a label and a list of its neighbors.
OJ's undirected graph serialization:
Nodes are labeled uniquely.
We use # as a separator for each node, and , as a separator for node label and each neighbor of the node.
As an example, consider the serialized graph {0,1,2#1,2#2,2}.
The graph has a total of three nodes, and therefore contains three parts as separated by #.
  1. First node is labeled as 0. Connect node 0 to both nodes 1 and 2.
  2. Second node is labeled as 1. Connect node 1 to node 2.
  3. Third node is labeled as 2. Connect node 2 to node 2 (itself), thus forming a self-cycle.
Visually, the graph looks like the following:
       1
      / \
     /   \
    0 --- 2
         / \
         \_/

Analysis:
It's a standard graph traversal problem. Herein we use DFS to solve this problem.

We keep an unordered_map<int, Node*> visited to maintain the visited nodes - the label and the pointer to it. Note that the pointer is the pointer to nodes in the new graph.

A big difference between graph and tree is that cycles may exist. So whenever we visit a node, we add the node into the visited map. For each of its neighbors, we then check the visited map if the neighbor's label exists. Here we assume the label is unique for each node.

- If the label exists, it means we have visited this neighbor somewhere else - even if it is not currently in the neighbor's vector, we need to add the neighbor's pointer to the vector - by looking up in the visited map (Again, remember the visited map stores the label and new node's pointer).

- If the label doesn't exist, it implies that we haven't traversed the node, we then need to do a DFS recursion on that node.

Additionally, if the label/value of each node is different, we only need to change the visited map to unordered_map<Node*, Node> visited, where the two pointers are the pointer to node in the old graph and that to the corresponding node in the new graph.

Code:
/**
 * Definition for undirected graph.
 * struct UndirectedGraphNode {
 *     int label;
 *     vector<UndirectedGraphNode *> neighbors;
 *     UndirectedGraphNode(int x) : label(x) {};
 * };
 */

class Solution {
    typedef UndirectedGraphNode Node;

public:
    UndirectedGraphNode *cloneGraph(UndirectedGraphNode *node) {
        if (node==NULL) return NULL;
        unordered_map<int, Node*> visited;
        return recur(node, visited);
    }

    Node *recur(Node *node, unordered_map<int, Node*>& visited) {
        Node *newNode=new Node(node->label);
        visited.emplace(node->label, newNode);

        for (int i=0; i<node->neighbors.size(); i++) {
            if (visited.find(node->neighbors[i]->label)==visited.end())
                newNode->neighbors.push_back(recur(node->neighbors[i], visited));
            else
                newNode->neighbors.push_back(visited[node->neighbors[i]->label]);
        }
        return newNode;
    }
};