Friday, October 26, 2012

Deepest Node in a Binary Tree

[Question]
Find the deepest node in a binary tree.

[Analysis]
In BFS (Breadth First Search), the last node visited is the deepest node in a tree (not necessarily a Binary Tree; it applies to any trees). Time complexity is O(N), N is the number of nodes in the tree.

Using DFS (Depth First Search) works too. Explore every node and count the depth of each leaf node by pre-order DFS. Although the time complexity is also O(N), it needs more code.

Another advantage of BFS is, it can provide information like, the number of nodes at a given level (shown in code sample #2 below).

[Solution]

//  -- CODE #1 --

//class Node {
//public:
//    Node* left, *right;
//    int value;
//};

Node* deepestNode( Node* root )
{
    if (root==NULL) return NULL;

    queue<Node*> que;
    que.push( root );

    Node *p;
    while (!que.empty()) {
        p = que.front();
        que.pop();
        if (p->left) que.push(p->left);
        if (p->right) que.push(p->right);
    }

    return p;
}

// -- CODE #2 --
//-- get the number of nodes in a given level of BST
//
int NumberAtLevel( Node* root, int level )
{
    if (root==NULL || level <0) return 0;
    if (level ==0) return 1;

    queue<Node*> que;
    que.push( root );
    int num = 1;

    Node *p=NULL;
    while (!que.empty()) {
        p = que.front();
        que.pop();
        num--;

        if (p->left) que.push(p->left);
        if (p->right) que.push(p->right);

        if (num==0) {
            level --;
            num = que.size();
            if (level==0) return num;
        }
    }

    return 0; //level is over the depth of tree;
}

Sunday, May 27, 2012

Path of Minimum Sum in a Binary Tree

[Question]
Given a binary tree, find the minimum sum from root to the leaf and also print that path?

[Analysis]
Apply post order traversal on binary tree. For a given node, the minimum sum from this node is, the smaller of the minimum sums between left and right sub-trees, plus the value in the node. It is tricky to deal with the node with only one child. Time Complexity is O(N). Space Complexity O(N).

[Solution]
//
// class BSTNode {
//    BSTNode left;
//    BSTNode right;
//    int    value;
// }
//
//--- JAVA ---
public static int findMinimumSumOfPath( BSTNode root, List<BSTNode> path)
{
   if (root.left ==null && root.right==null) {
    path.add(root);
    return root.value;
   }
   
   List<BSTNode> leftPath = new ArrayList<BSTNode>();
   List<BSTNode> rightPath = new ArrayList<BSTNode>();
   int leftSum= Integer.MAX_VALUE;
   int rightSum=Integer.MAX_VALUE;
   int sum=0;
   
   if (root.left!=null) 
    leftSum = findMinimumSumOfPath ( root.left, leftPath );
   if (root.right!=null) 
        rightSum = findMinimumSumOfPath ( root.right, rightPath);
   if (leftSum < rightSum) {
        path.add(root);
        path.addAll(leftPath);
        sum = root.value+leftSum;
   } 
   else {
        path.add(root);
        path.addAll(rightPath);
        sum = root.value+rightSum;
   }
   return sum;
}

//--- C++ ---
int minSumPath (Node* root, vector<int>& path) {
    if (!root) return 0;

    vector<int> path1, path2;
    int left = minSumPath(root->left, path1);
    int right=minSumPath(root->right, path2);

    path.push_back(root->val);
    if (left< right) 
        path.insert(path.end(), path1.begin(), path1.end());
    else
       path.insert(path.end(), path2.begin(), path2.end());
    
    return min(left,right) + root->val;
}

Jump Game (2)

[Question]
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]
Be greedy. Assume the minimum steps to reach position i is S[i], then from position i+1 to A[i]+i, the steps to reach are at most S[i]+1 (part of [ i+1, A[i]+i ] may already be reached before using A[i]).

[Solution]
//-----------
//-- C++ --
//-----------
class Solution {
public:
    int jump(vector<int>& nums) {
        int n=nums.size();
        vector<int> cnt(n,0);

        for (int i=0, k=0; i<n && k<n; i++)
            for(int j=k; j<min(n, nums[i]+i+1); j++,k++)
                cnt[j] = (i==j)?0:cnt[i]+1;
               
        return cnt.back();
    }
};

Saturday, May 26, 2012

Jump Game (1)

[Question]
Assume an array of integers, A[i]>=0, represents the maximum steps allowed to move forward from i. Starting scan from A[0], is there a path to reach the end of A (i.e. A[n-1])?

For example, given {3, 0, 2, 1, 2, 0, 0}, there is a path A[0], A[3], A[4], A[6]; given {3, 0, 2, 0, 2, 0, 0}, the path has to be A[0], A[2], A[4], A[6]; given {3, 1, 1, 0, 2, 0, 0},  no path to the end.

[Analysis]
Be greedy. Push the reach from the A[0] to forward index  j ="A[0]+0", A[1] to index j = max(j, A[1]+1), until i> j (unreachable anymore), or forward index j covers A's last index.

[Solution]
class Solution {
public:
    bool canJump(vector<int>& nums) {
        int i=0, j=0;
        while (i<=j) {
            j = max(j, nums[i]+i);
            if (j>=nums.size()-1)
                return true;
            i++;
        }
        return false;
    }
};

Saturday, May 5, 2012

In-order Iterator of a Binary Search Tree

[Question]
Implement an in-order iterator of Binary Search Tree (BST)

[Analysis]
Use a stack to traverse BST. The top of the stack is the element whose value has not been visited by in-order:  the next is either the left most node from this node or itself when all left sub-tree has been visited.

[Solution]
/*
 * public class BSTNode {
 *  BSTNode left, right;
 * int     value;
 * }
 */

import java.util.*;

public class BSTIterator {
BSTNode root;
Stack<BSTNode>   st;
BSTNode last;

public BSTIterator( BSTNode root ){
this.root = root;
st = new Stack<BSTNode>();
if (root!=null)
st.push(root);
last = null;
}

public boolean hasNext() {
return (!st.isEmpty());
}

public BSTNode next() {
while (last==null && st.peek().left!= null ||
last!=null && st.peek().left!= null && st.peek().left.value > last.value) {
st.push(st.peek().left);
}

last = st.pop();
if (last.right!=null) st.push(last.right);

return last;
}
}

Trie Implementation

[Question]
Implement a Trie.

[Analysis]
Trie: http://en.wikipedia.org/wiki/Trie


[Solution]

import java.util.*;

public class Trie {
Trie[]  nodes; //nodes of characters: a, b, ... z.
int[]   count;
boolean eow;    //end of word
boolean isLeaf;

public Trie() {
nodes = new Trie[26];
count = new int[26];
eow = false;
isLeaf = true;
}

public void addWord(String word) {
if (word==null || word.length()==0) return;

String temp= word.toLowerCase();
char[] chars = temp.toCharArray();
Trie p = this;
for(char c: chars) {
p.count[c-'a'] ++;
p.isLeaf = false;
if (p.nodes[c-'a'] == null) {
Trie node = new Trie();
p.nodes[c-'a'] = node;
}
p = p.nodes[c-'a'];
}
p.eow = true;
}

public boolean isWord(String word) {
String temp= word.toLowerCase();
char[] chars = temp.toCharArray();

Trie p = this;
for (char c: chars) {
if (p.nodes[c-'a'] == null) return false;
p = p.nodes[c-'a'];
}
return p.eow;
}

public void removeWord (String word) {
String temp = word.toLowerCase();

if (isWord (temp)) {
char[] chars = temp.toCharArray();

Trie p = this;
boolean flag = false;
for (char c: chars) {
p.count[c-'a']--;
if (p.count[c-'a'] == 0) {
p.nodes[c-'a'] = null;
flag = true;
break;
}
p = p.nodes[c-'a'];
}
if (!flag && p.eow) {
p.eow = false;
}
if (p.numOfWords() == 0) p.isLeaf = true;
}
}

public List<String> getAllWords() {
List<Character> path = new ArrayList<Character> ();
List<String>    words= new ArrayList<String>();
getAllWords(path, words);
return words;
}

public int numOfWords() {
int total =0;
for (int i: count) {
total+=i;
}
return total;
}

private void getAllWords(List<Character> path, List<String> words) {
if (eow) {
String s = "";
for (Character c: path) s+=c;
words.add(s);
}

if (isLeaf) return;

for (int i=0; i<nodes.length; i++) {
if (nodes[i]!=null) {
char c = Character.toChars('a'+i)[0];
path.add(c);
nodes[i].getAllWords(path, words);
path.remove(path.size()-1);
}
}
}
}

Thursday, May 3, 2012

Stable Partition of an Array

[Question]
Given an array of positive and negative integers, re-arrange it so that positives on one end and negatives on the other, but retain the original order of appearance, e.g. 2,7,-5, 9,-1, 10,-3 => -5, -1, -3, 2, 7, 9, 10.  Do it in-place, in O(nlogn).

[Analysis]
Using the partition method in quick sort, the order of those elements may change. Using the bubble sort to exchange one by one, the order is kept but the time complexity will be O(n^2). Merge sort can be a help here to get the O(nLogn) and in place method: i.e. assume each half of the array has been in right order (negatives on the left, positives on the right), then merge two half into one.

[Solution]

public class ArrayPartition implements TestCaseRunner{
void swap (int[] a, int i, int j) {
int temp = a[i];
a[i] = a[j];
a[j] = temp;
}

void mirror (int[] a, int left, int right) {
if (left<0 || right >= a.length) return;
while (left<right) {
swap (a, left++, right--);
}
}

void merge(int[] a, int i, int length ) {
if (length < 2) return;

int med = i+(length>>1);
int j = med;
merge(a, i, length>>1);
merge(a, j, length - (length>>1));

while (i<j && a[i]<0) {
i++;
}

if (i<med) {
while (j< i+length && a[j]<0) {
j++;
}

if (j>med) {
mirror (a, i, j-1);
mirror (a, i, j-med + i-1);
mirror (a, j-med+i, j-1);
}
}
}

public int[] partition(int[] a) {
if (a==null || a.length < 2) return a;
merge(a, 0, a.length);
return a;
}

public void runTestCases() {
int[] a = {1, -1, 2, 3, -5, -2, 9, 3, -4};
partition(a);
for(int i: a) System.out.println(i);
}
}

Monday, April 30, 2012

Find the Longest Increasing Sub-sequence -- LeetCode 300

[Question]
Given an array of integers A of length n, find the longest sequence i1, i2, ... ik such that ij < ij+1 and A[ ij ] < A[ ij+1 ] for any j, 0<j<k.

For example, {1,3,5,2,7} --> {1,3,5,7} and {0,5,1,2,6,4} --> {0,1,2,4}.

[Analysis]
This is a classic Dynamic Programming (DP) case. A solution is given in book "Algorithm For Interviews". 
First, we define a function of Si, which refers the longest sequence that ends at A[i]. Then, Si is equal to the longest Sj +1, when j<i and A[j]<A[i].
Using a table, the algorithm can populate S[i] from 0 to the end of array A (S0 = 1). 

Note: 
1) To find the longest increasing consecutive sub-sequence would be simpler. DP is not needed.
2) There are a few other questions derived from this: e.g. find longest common sub-strings between two strings; or find the longest palindromic sub-sequence.

[Solution]

List<Integer> findLongestIncreasingSequence(int[] a) {
int[] s = new int[a.length];
int[] prev= new int[a.length];  //used to retrieve the list.

s[0] = 1;
for (int i=0; i<prev.length; i++) prev[i] =-1;

int maxLength =0;
int maxEndIndex =0;

for (int i=1; i<a.length; i++) {
for (int j=0; j<i; j++) {
if (a[i]>a[j] && s[i]<s[j]+1) {
s[i] = s[j]+1;
prev[i] = j;
}
}

if (s[i]>maxLength) {
maxLength = s[i];
maxEndIndex = i;
}
}

int i=maxEndIndex;
List<Integer> list = new ArrayList<Integer>();
while (prev[i] != -1) {
list.add(a[i]);
i = prev[i];

list.add(a[i]);
return list;
}

//
// -- O(NLogN) ---
//
class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        vector<int> res;
        for (auto& n: nums) {
            auto it = lower_bound(res.begin(), res.end(), n);
            if (it==res.end()) res.push_back(n);
            else *it = n;
        }
        return res.size();
    }

};

Saturday, April 28, 2012

Minimum Window Sub-string (a.k.a Sliding Window) -- LeetCode 76

[Question]

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".
[Analysis]
The problem can be resolved by using a window (tail, head) on string S.
1) Both indexes tail and head start from index 0 of S.
2) Move "head" forward step by step in S until find a window (tail, head) contain all the characters in T.
3) Then move "tail" forward to leave characters (in T) out of window, as many as possible until the sub-string in window cannot contain all characters in T.
4) Repeat 2, 3 until reach the end of S. Meanwhile, measure the length of window and record the minimum window as requested.

The time complexity is O(N).
[Solution]
//
//--- C++ ---
//
class Solution {
public:
    string minWindow(string s, string t) {
        unordered_map<char, int> target;
        for (auto c: t) 
            target[c]++;
            
        unordered_map<char, int> counter;
        auto contained=[&]() {
            for (auto& t: target) 
                if (t.second>counter[t.first]) return false;
            return true;
        };
        
        int i=0, j=0;
        int ri=0, rj=INT_MAX;
        while (j<s.size()) {
            while (!contained() && j<s.size()) 
                counter[s[j++]]++;
            
            while(contained() && i<j) {
                if (j-i<rj-ri)
                    ri=i, rj=j;
                counter[s[i++]]--;   
            }
        }
        return rj-ri<=s.size()?s.substr(ri, rj-ri):"";
    }
};
//
//--- Java ---
//
public class MinimumWindowString implements TestCaseRunner {

    public String minWindow(String S, String T) {
        if (S.length() < T.length() || T.length()==0) return "";
        String minStr = S+" ";
        S.toUpperCase();
        T.toUpperCase();

       // -- cnt arrays used for judging whether all characters 
       //  in target string are included in window sub-string.
        int[] wndCnts = new int[26];
        int[] tgtCnts = new int[26];
     
        char[] temp = T.toCharArray();
        for (char c: temp) {
            tgtCnts[c-'A'] ++;
        }
     
        int loadFlag = setFlag(T);
        int loadFlagCopy = loadFlag;
     
        int head, tail;
        head = tail =0;
     
        // -- skip letters that not meaningful --
        while (!charInTarget(S.charAt(head), tgtCnts)) {
        head++;
        };      
        tail = head;
     
        // -- start looping --
        while (head < S.length() ) {
            while (!charInTarget(S.charAt(tail), tgtCnts)) {
            tail++;
            };
         
            // -- grow to the match --
            while (head < S.length() && loadFlag!=0) {
                char c= S.charAt(head++);
                if (charInTarget(c, tgtCnts)) {
                    wndCnts[c-'A']++;
                    if (wndCnts[c-'A']==tgtCnts[c-'A']) {
                        loadFlag ^= 1<<(c-'A');
                    }
                }              
            }

            if (loadFlag == 0) {
                if (minStr.length()>head-tail) {
                    minStr = S.substring(tail, head);
                }
            }
            else break;
         
            // -- reduce the match --
            int unloadFlag = 0;
            while(tail<head && unloadFlag==0) {
                char c= S.charAt(tail++);
                if (charInTarget(c, tgtCnts)) {
                    wndCnts[c-'A']--;
                    if (wndCnts[c-'A']<tgtCnts[c-'A']) {
                        unloadFlag = 1<<(c-'A');
                    }
                    else if (minStr.length()>head-tail) {
                        minStr = S.substring(tail, head);
                    }
                }
            }
         
            loadFlag = loadFlagCopy & unloadFlag;
        }
     
        if (minStr.equals(S+" ")) return "";
        return minStr;
    }
 
    int setFlag (String s) {
        char[] chars = s.toCharArray();
        int a = 0;
        for (char c: chars) {
            a |= 1<<(c-'A');
        }
        return a;
    }
 
    boolean charInTarget (char c, int[] target) {
        return (target[c-'A']>0);
    }
 
    public void runTestCases() {
String[] S = {"ADOBECODEBANC", "ABBADBABDRGBACCCB", "HBBHHCADBEBCAECBCCBB"};
String[] T = {"ABC", "ABBC", "ABBC"};

for (int i=0; i<S.length; i++) {
String ret = minWindow(S[i], T[i]);
System.out.println(ret);
}
    }
}


Friday, April 27, 2012

Construct a BST from In-order and Pre-order Traversal Result

[Question]
Construct a BST from in-order and pre-order traversal result.

For example,
Given pre-order traversal [6, 2, 4, 3, 5, 8, 7] and in-order traversal [2, 3, 4, 5, 6, 7, 8], the BST should be
[Analysis]
Pre-order traversal gives out the root of BST as the first element. Based on root, left and right sub-trees can be found in in-order traversal.
As in the example above, because 6 is the first element in pre-order traversal, 6 must be the root element. Then [2, 3, 4, 5] is the left sub-tree, and [7, 8] is the right sub-tree, according to in-order traversal.
             pre-order:  [6, [2, 4, 3, 5], [8, 7]]     in-order: [[2, 3, 4, 5], 6, [7, 8]]
The problem is reduced to (1) re-construct left sub-tree based on pre-order [2, 4, 3, 5] and in-order [2, 3, 4, 5], and (2) re-construct right sub-tree based on pre-order [8, 7] and in-order [7,8].

[Solution]
/*
 * public class BSTNode {
 *       int value;
 *       BSTNode left, right;
 * }
 */

    public static BSTNode constructBST (List<Integer> preOrder, List<Integer> inOrder) {
        assert preOrder.size() == inOrder.size();
        if (preOrder.size() == 0) return null;

        BSTNode node = new BSTNode();
        node.value = preOrder.get(0);

        int index = inOrder.indexOf(preOrder.get(0));
        node.left = constructBST(preOrder.subList(1, index+1), inOrder.subList(0, index));
        node.right= constructBST(preOrder.subList(index+1, preOrder.size()), inOrder.subList(index+1, inOrder.size()));
        return node;
    }  



Thursday, April 26, 2012

Regular Expression Matching


[Question]

Implement regular expression matching with support for '.' and '*'.
'.' Matches any single character.
'*' Matches zero or more of the preceding element.

The matching should cover the entire input string (not partial).

The function prototype should be:
bool isMatch(const char *s, const char *p)

Some examples:
isMatch("aa","a") → false
isMatch("aa","aa") → true
isMatch("aaa","aa") → false
isMatch("aa", "a*") → true
isMatch("aa", ".*") → true
isMatch("ab", ".*") → true
isMatch("aab", "c*a*b") → true

[Analysis]
This problem can be solved in a similar way with Edit Distance. According to its rules, there are four kinds of patterns in this regular expression matching:

"." - Matches any single character.
"c*" - Matches zero or more of character 'c';
".*" - Matches zero or more of any characters;
"c" - Matches a single character 'c';

First, make a match matrix between the word and patterns: e.g. pattern is ".*bc*." and the word is "abbcc"

     .*   b   c*   .
a    0   1   1    0
b    0   0   1    0
b    0   0   1    0
c    0   1   0    0
c    0   1   0    0

"0" in (i,j) represents a possible match between the pattern p[j] and character w[i].
"1" in (i,j) represents no match at all between the pattern p[j] and character w[i].

If there is a match between the pattern and word, there is a path from [1,1] to [m, n] (m is the length of the word and n is the number of patterns), which composed of a series of "0" and the following actions:
1) [i,j] -> [i+1,j+1]  -- end the pattern match at [i,j] and move to next pattern match to [i+1, j+1];
2) [i,j] -> [i,j+1]  -- skip the current pattern match at [i,j] and use next pattern (j+1) on character (i);
3) [i,j]->  [i+1, j] -- continue use current pattern at letter (i) and try to apply to (i+1);

2) 3) are operations allowed only for pattern columns "c*" and ".*".
1) is the only operation allowed for pattern columns "c" and ".".

So back to the above example, we found a path (a match) from [1,1] to [5,4] (marked by red color).


     .*   b   c*   .
   0   1   1    0
   0   0   1    0
   0   0   1    0
c    0   1   0    0
c    0   1   0    0 



[Solution]
//-------------
//--- C++ ---
//-------------
class Solution {
public:
    bool isMatch(const char *s, const char *p) {
        string s1 (s);
        string p1 (p);
     
        if ( p1.empty()) return s1 == "";
     
        vector<vector<bool>> map (s1.size()+1, vector<bool>(p1.size()+1, false));
     
        map[0][0] = true;
        for (int i=1; i<p1.size()+1; i++) {
            if (p1[i-1] == '*') map[0][i] = map[0][i-2];
        }
     
        for (int i=1; i<s1.size()+1; i++) {
            for (int j=1; j<p1.size()+1; j++) {
                if (p1[j-1] == '.') {
                    map[i][j] = map[i-1][j-1];
                }
                else if (p1[j-1] == '*') {
                    map[i][j] = map[i][j-2]     //'*' is counted as zero
                            || map[i][j-1]      //'*' is counted as 1
                            || p1[j-2] == '.' && map[i-1][j]  //'.*'
                            || s1[i-1] == p1[j-2] && map[i-1][j];
                }
                else
                    map[i][j] = (s1[i-1] == p1[j-1])? map[i-1][j-1] : false;
            }
        }
        return map.back().back();
    }
};

//---------------
//--- JAVA ---
//---------------
import java.util.*;

public class Solution {
    public static boolean isMatch(String s, String p) {

        List<String> pat = new ArrayList<String> ();
        char[] ap = p.toCharArray();
     
        if (s.length() == 0) return (p.length()==0 || p.equals(".*"));
     
        if (ap.length<=1) {
            if (ap.length==0 && s.length() ==1) return true;
            if (ap.length==0 && ap[0]=='.' && s.length() == 1) return true;
            return false;
        }
     
        // -- parse and generate patterns sub-strings.
        int i = 0;
        while(i<ap.length-1) {
            char a = ap[i];
            char b = ap[i+1];
            if (a == '.') {
                if (b=='*') {
                    pat.add(new String("") + a+b);
                    i++;
                } else {
                    pat.add(new String("") +a);
                }
            }
            else if (a != '*') {
                if (b=='*') {
                    pat.add(new String("") + a +b);
                    i++;
                } else {
                    pat.add(new String("") +a);
                }
            } else { // a=='*'
                return false;
            }
            i++;
        }
        if (i<ap.length) {
        pat.add(new String("") + ap[i]);
        }
     
        // -- generate the matrix.
        int[][] matrix;
        int rows = s.length();
        int cols = pat.size();      
        matrix = new int[rows][cols];

        for (i=0; i<rows; i++) {
            for (int j=0; j<cols; j++) {
                char cs = s.charAt(i);
                matrix[i][j] = 1;
                if (pat.get(j).length() == 2){
                    if ( pat.get(j).equals(".*") ){
                        matrix[i][j] = 0;
                    }
                    else { //"c*"
                        if (pat.get(j).charAt(0) == cs) {
                            matrix[i][j] = 0;
                        }
                    }
                }
                else {
                    if (pat.get(j).equals(".") || pat.get(j).charAt(0) == cs) {
                        matrix[i][j] = 0;
                    }
                }
                System.out.println(matrix[i][j]);
            }
            System.out.println("");
        }
     
       // -- to find a path/match.
        if (matrix[rows-1][cols-1] != 0) return false;
     
        return validateMatch(matrix, 0, 0, pat);
    }
 
    public static boolean validateMatch(int[][] m, int i, int j, List<String> pat) {
        int rows = m.length;
        int cols = m[0].length;
     
        if (i== rows-1 && j==cols-1) return true;

        boolean ret = false;
        if (pat.get(j).length()==2) {
            if (j+1<cols && m[i][j+1] == 0) {
                ret = ret || validateMatch(m, i, j+1, pat);
            }
            if (i+1<rows && m[i+1][j] ==0) {
                ret = ret || validateMatch(m, i+1, j, pat);
            }
        }
     
        if (j+1<cols && i+1<rows && m[i+1][j+1] ==0) {
                ret = ret || validateMatch(m, i+1, j+1, pat);
        }
     
        return ret;  
    }
 
public static void main(String args[])
{
String s = "aasdfasdfasdfasdfas";
String p =  "aasdf.*asdf.*asdf.*asdf.*s";

boolean a = Solution.isMatch(s, p);
System.out.println("a:" + a);
}
}


Generate Permutations

[Question]
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]
Do it in a recursive way.

[Solution]

public static List<List<Integer>> generatePermutations ( List<Integer> nums ) {
List<List<Integer>> result = new ArrayList<List<Integer>> ();

if (nums.isEmpty()) return null;

if (nums.size()==1) {
result.add(nums);
return result;
}

Integer a = nums.get(0);
int numSize = nums.size();
nums.remove(0);
List<List<Integer>> subPerms = generatePermutations(nums);

for (int i=0; i<subPerms.size(); i++) {
for (int j=0; j<numSize-1; j++) {
List<Integer> newPerm = new ArrayList<Integer> (subPerms.get(i));
newPerm.add(j, a);
result.add(newPerm);
}
List<Integer> newPerm = new ArrayList<Integer> (subPerms.get(i));
newPerm.add(a);
result.add(newPerm);
}
return result;
}

Minimum Triplet from 3 Arrays

[Question]
You are given with three sorted arrays ( in ascending order), you are required to find a triplet (one element from each array) such that distance is minimum.

Distance is defined like this :
If a[i], b[j] and c[k] are three elements then
      distance=max(abs(a[i]-b[j]),abs(a[i]-c[k]),abs(b[j]-c[k]))

Please give a solution in O(n) time complexity.



[Analysis]
Approach 1:
Assume the triplet with minimum distance is (a[i], b[j], c[k[) and b[j] is the median of the three numbers. This implies a[i] is the closest to b[j] among array a, and c[k] is the closest to b[j] among c[k]. 


So for a given j, if b[j] closest a[i] and closest c[j] are a[i]<=b[j]<=c[k] or c[k]<=b[j]<=a[i], then this triplet is one of the candidates for the minimum distance. 


The algorithm can scan array a, b, c and examine those candidates for the final result. The scan is in linear time. But this requires multiple scans which can be redundant.


Approach 2:
Suppose data in 3 arrays line up, the minimum triplet can must have the pattern of (X, Y, Y*, Z) where X, Y, Z come from different arrays (* denotes 0 or n appearances). So we can collect all such triplets that meet such pattern, measure their distances (which is Z-X) and find the minimum.


In code, a priority queue (that mimics heap) of size 3 is used. It ensures we get data from 3 arrays in a non-descending order. Then we found the pattern triplets (X, Y, Y*, Z) using class Pattern. Finally, the distances of all such triplets are calculated and measured. 

[Solution]

import java.util.*;


public class MinimumTripletTest {
public class Triplet {
int i, j, k;
}


public class Node implements Comparable{
char  arrId;   // 0-a, 1-b, 2-c
int  index;   // array index;
int  value;

Node (char t, int i, int v) {
arrId = t;
index = i;
value = v;
}


@Override
public int compareTo(Object a) {
// TODO Auto-generated method stub
if (!(a instanceof Node)) {
return -1;
}
Node n = (Node)a;

if (value< n.value) return -1;
if (value> n.value) return 1;
return 0;
}
}

public class Pattern {
List<Node> nodes;

Pattern() {
nodes = new ArrayList<Node> ();
}

boolean fulfilled() {
return nodes.size() == 3;
}

void add (Node a) {
if (nodes.size()==2 && a.arrId == nodes.get(1).arrId) {
nodes.set(1, a);
}
else if (nodes.size()==2 && a.arrId == nodes.get(0).arrId) {
nodes.set(0, nodes.get(1));
nodes.set(1, a);
}
else if (nodes.size()==1 && a.arrId == nodes.get(0).arrId) {
nodes.set(0,  a);
}
else {
nodes.add(a);
}
}

Node pop () {
if (nodes.size()==0) return null;
return nodes.remove(0);
}

int distance() {
if ( !fulfilled() ) return -1;
return nodes.get(2).value - nodes.get(0).value;
}

Triplet getTriplet() {
if (!fulfilled()) return null;
Triplet t = new Triplet();
for(Node n: nodes) {
if (n.arrId == 'a') {
t.i = n.index;
}
else if (n.arrId == 'b') {
t.j = n.index;
}
else if (n.arrId == 'c') {
t.k = n.index;
}
}
return t;
}
}

public Triplet findMinimumTriplet(int[] a, int[] b, int[] c) {
Pattern pat = new Pattern();
PriorityQueue<Node> pq = new PriorityQueue<Node>();

int min = Integer.MAX_VALUE;
int i,j,k;
i=j=k=0;

pq.add(new Node('a', i, a[i]));
pq.add(new Node('b', j, b[j]));
pq.add(new Node('c', k, c[k]));
Triplet t = null;

while (i<a.length || j<b.length || k<c.length) {
Node next = pq.remove();
pat.add(next);
if (pat.fulfilled()) {
if (pat.distance() < min) {
min = pat.distance();
t = pat.getTriplet();
}
pat.pop();
}

if (next.arrId == 'a' && ++i<a.length) {
pq.add(new Node('a', i, a[i]));

else if (next.arrId == 'b'&& ++j<b.length) {
pq.add(new Node('b', j, b[j]));
}
else if (next.arrId == 'c' && ++k<c.length) {
pq.add(new Node('c', k, c[k]));
}
}

return t;
}

public static void main (String[] args) {
int[] a = {1,3,5,7, 9, 10, 13, 15, 24, 28, 29, 30,};
int[] b = {12, 12, 14, 17, 19, 22, 25, 28};
int[] c = {4, 9, 15, 22, 28, 38, 49, 50};

MinimumTripletTest test = new MinimumTripletTest();
MinimumTripletTest.Triplet t = test.findMinimumTriplet(a, b, c);

System.out.println("result: "+a[t.i]+","+b[t.j]+","+c[t.k]);
}
}

// result: 28,28,28