Monday, November 25, 2013

Insertion Sort List

Q:
Sort a linked list using insertion sort.
A:
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
public class Solution {
    public ListNode insertionSortList(ListNode head) {
        ListNode header = new ListNode(Integer.MIN_VALUE);
        while(head!=null){
            ListNode tmp = head;
            head = head.next;
            
            ListNode runner = header;
            while(runner.next!=null && runner.next.val < tmp.val)
                runner = runner.next;
            
            tmp.next = runner.next;
            runner.next = tmp;
        }
        return header.next;
    }
}

Mistakes:
1: 当插入到比tail更前的的时候,  忘了break来跳出循环。
---------------第二遍--------------
1: 忘了把preNode 在whileloop的时候,先置为 newHeader
2:  after  checking preNode == null,    Mistakenly wrote
 tmp.next = preNode.next.next ;                        what the hell, can you make this mistake???????

Learned:



Friday, November 8, 2013

Wildcard Matching ~~~~~~~~~~~~~~~~~~~~~~~

Implement wildcard pattern matching with support for '?' and '*'.
'?' Matches any single character.
'*' Matches any sequence of characters (including the empty sequence).

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", "*") → true
isMatch("aa", "a*") → true
isMatch("ab", "?*") → true
isMatch("aab", "c*a*b") → false
A:
1: 先用递归,估计会嫌太慢。先做出来再说吧。

 嗯,确实很慢。问题主要出在* 上。 下面是递归的解法。

public class Solution {
    public boolean isMatch(String s, String p) {
        if(s.length() ==0){
            if(p.length()==0)
                return true;
            else
                return p.charAt(0) =='*'? isMatch(s, p.substring(1)) : false;
        }
        if(p.length() ==0)
            return false;

        if(s.charAt(0) == p.charAt(0) || p.charAt(0) == '?')
            return isMatch(s.substring(1), p.substring(1));
        else if( p.charAt(0) == '*' )
            return isMatch(s,p.substring(1)) || isMatch(s.substring(1),p);
        else
            return false;
    }
}
上面这个解法,对于输入:
"babbbaabbaaaaabbababaaaabbbbbbbbbbabbaaaabbababbabaa", "**a****a**b***ab***a*bab" 
就会严重超时。

问题就出在 倒数第二个return 语句上,|| 后面那个helper()。当s只match了一个。这样效率很低。

  Solution 2
贪心算法,只需要依据连续的’*',将p分割成一些不带’*'的子串。然后在s中依次匹配就好了,只不过需要特殊照顾一下首尾两个子串:
1.对于开头不是’*'的p,第一个子串必须从s[0]开始匹配
2.对于结尾不是’*'的p,最后一个子串必须在s的尾巴上匹配

--------------------------确实快了很多,可是,当*后面的第一个字符串(非通配符)在s中match的次数过多的时候, 速度会显著降低。-------------------这时候,我们就不需要匹配所有的。 只需要找到匹配的即可。 嗯,如果是最后的字串,需要匹配最后的位置。
还需要继续优化。




-------------------solution 3 -----------------------
我们提出solution 3,  也就是把? 和其余的字符串放一起。
因此, p = "a???*a??b"被我们分成  [a,  ???, * ,   a??b] 四个子pattern。 这样出了第一个前面没有*外, 别的子pattern前面都有*, 可以随便移动位置。 (当然,需要自己写 字符匹配的函数。这个很简单)
 ----------------这里深入分析了8种解法----------- (还有代码)
--------------------- 这是第二遍的实现--------------思路跟solution3 一样-------只是更简洁了----

public class Solution {
    public boolean isMatch(String s, String p) {
        if (s == null || p == null) // should not occur
            return false;
        if (s.length() == 0)
            return p.length() == 0 || (p.length() == 1 && p.charAt(0) == '*');
        if (p.length() == 0)// if run this line, s.length() != 0
            return false;
        // check the first is *
        if (p.charAt(0) != '*') {
            int firstStartIndex = p.indexOf("*");
            if (firstStartIndex < 0) { // if p does not contains *
                return s.length() == p.length() && findFirstMatch(s, p) == 0;
            } else { // / p contains *
                String partP = p.substring(0, firstStartIndex);
                int matchPos = findFirstMatch(s, partP);
                if (matchPos != 0) { // if not fint at the first place
                    return false;
                } else { // we have find match at first place
                    s = s.substring(partP.length());
                    p = p.substring(partP.length());
                }
            }
        }
        String[] patternP = p.split("\\*");
        // all patternP[i] have a * ahead
        for (int i = 0; i < patternP.length; i++) {
            String nonStarP = patternP[i];
            int matchPos = findFirstMatch(s, nonStarP);
            if (matchPos < 0) {
                return false;
            } else {
                s = s.substring(matchPos + nonStarP.length());
            }
        }
        // if last have *,  or s is all matched
        return p.charAt(p.length() - 1) == '*' || s.length() == 0;
    }

    private int findFirstMatch(String s, String p) {
        // p does not contains *
        for (int i = 0; i <= s.length() - p.length(); i++) {
            // test substirng and p
            boolean isMatch = true;
            for (int j = 0; j < p.length(); j++) {
                char chS = s.charAt(i + j);
                char chP = p.charAt(j);
                if (chP != '?' && chS != chP) {
                    isMatch = false;
                    break;
                }
            }
            if (isMatch) { // if found match
                return i;
            }
        }
        return -1;
    }
}


Mistakes:
1: 当方法2的时候,没有考虑这种情况
Input: "", ""
Output: false
Expected: true
2:方法2的时候, 由于没有考虑到 p = "*"的情况。
而我们初始条件的startIndex = 0 是第一个valid情况。 而每次startIndex跳到下一个情况(或者越出右边界一位)
这样,就产生了错误。

3: 在用s的startIndex ==0 的初始情况的时候,没有考虑,当s为空的情况。 导致error.

4: when the substring is "?", at first ,we simply skip to next, but it is WRONG. we need also check whether s has the character with it.

5: 当遇到?的时候,我们仅仅startIndex跳到下一个了, 可是,没有注意
s="aa"
p ="*?"的情况时,我们需要多跳几个。  因此,还是需要把*?的顺序,调整成?*

Learned:
1: 好像在leetcode里,不用考虑,输入是null  的情况。
2:
         s = "aa";
         System.out.println( s.lastIndexOf("a", 0));
这里,很诡异的,输出竟然是0  而不是1.  ----------------原因如下。
public int lastIndexOf(String str, int fromIndex)
Returns the index within this string of the last occurrence of the specified substring, searching backward starting at the specified index. 


 -----------------第二遍-----------
1: 用 * 来split ,  命令要写成:   str.split("\\*");    -------- 要用双反斜线
2: 注意, split函数,是可以得到数组的某个元素为 “”的。


  --------------------------------下面 是 别人的代码------------ C++------------

Analysis:

For each element in s
If *s==*p or *p == ? which means this is a match, then goes to next element s++ p++.
If p=='*', this is also a match, but one or many chars may be available, so let us save this *'s position and the matched s position.
If not match, then we check if there is a * previously showed up,
       if there is no *,  return false;
       if there is an *,  we set current p to the next element of *, and set current s to the next saved s position.

e.g.

abed
?b*d**

a=?, go on, b=b, go on,
e=*, save * position star=3, save s position ss = 3, p++
e!=d,  check if there was a *, yes, ss++, s=ss; p=star+1
d=d, go on, meet the end.
check the rest element in p, if all are *, true, else false;

Note that in char array, the last is NOT NULL, to check the end, use  "*p"  or "*p=='\0'".
class Solution {
public:
    bool isMatch(const char *s, const char *p) {
        const char* star=NULL;
        const char* ss=s;
        while (*s){
            if ((*p=='?')||(*p==*s)){s++;p++;continue;}
            if (*p=='*'){star=p++; ss=s;continue;}
            if (star){ p = star+1; s=++ss;continue;}
            return false;
        }
        while (*p=='*'){p++;}
        return !*p;
    }
};


-------------------------------------------------第三遍----------------想利用上面的想法, 也用递归来做的。结果还是超时。----------------------------然后就又是按照* 来split,然后线性查找即可--------  因此,这道题目,复杂度应该是在O(n)的--------------

对于前后的non- start string 的问题,我们事先匹配后,消去即可。

public class Solution { 
    public boolean isMatch(String s, String p) {
        if(p.length()==0)
            return s.length()==0;
        if(s.length()==0)
            return p.charAt(0)=='*' && isMatch(s,p.substring(1));
        if(p.charAt(0) !='*'){// delete all before first *
            return (p.charAt(0)=='?' || p.charAt(0)==s.charAt(0)) && isMatch(s.substring(1),p.substring(1));
        }
        if(p.charAt(p.length()-1) !='*'){ // delete all after last *
            char ch = p.charAt(p.length()-1);
            return (ch=='?' || ch ==s.charAt(s.length()-1)) && isMatch(s.substring(0,s.length()-1),p.substring(0,p.length()-1));
        }
        // now at the two end of p,  are *
        String[] P = p.split("\\*");
        int firstMatchPos = 0;
        for(int i =0;i< P.length; i++){
            if( P[i].length()==0)
                continue;
            firstMatchPos = findMatch(s, firstMatchPos,P[i]);
            if(firstMatchPos<0)
                return false;
            firstMatchPos += P[i].length();
        }
        return true;
    }
    private int findMatch(String s, int start, String p){// this p does not contain *
        for(int i =start;i <= s.length()-p.length();i++){
            //check the whole p
            boolean found = true;
            for(int j=0;j<p.length();j++){
                if( p.charAt(j) !='?' && p.charAt(j) != s.charAt(j+i)){
                    found = false;
                }
            }
            if(found)
                return i;
        }
        return -1;
    }
}

-------------------------------又一种思路---------------------
贪心的策略,能匹配就一直往后遍历,匹配不上了就看看前面有没有'*'来救救场,再从'*'后面接着试。
这个解法,其实就是我们最上面的哪种,但是,用两个index而不用递归,大大节省了时间。

public class Solution {
    public boolean isMatch(String s, String p) {
        int i = 0, j = 0;
        int star = -1, mark = -1;
        while (i < s.length()) {
            if (j < p.length()
                    && (p.charAt(j) == '?' || p.charAt(j) == s.charAt(i))) {
                ++i;
                ++j;
            } else if (j < p.length() && p.charAt(j) == '*') {
                star = j++;
                mark = i;
            } else if (star != -1) {
                j = star + 1;
                i = ++mark;
            } else {
                return false;
            }
        }
        while (j < p.length() && p.charAt(j) == '*') {
            ++j;
        }
        return j == p.length();
    }
}



Thursday, November 7, 2013

145. Binary Tree Postorder Traversal -----------E

Given the root of a binary tree, return the postorder traversal of its nodes' values.

Follow up: Recursive solution is trivial, could you do it iteratively?

 

Example 1:

Given the root of a binary tree, return the postorder traversal of its nodes' values.

 

Example 1:

Input: root = [1,null,2,3]

Output: [3,2,1]

Explanation:

Example 2:

Input: root = [1,2,3,4,5,null,8,null,null,6,7,9]

Output: [4,6,7,5,2,9,8,3,1]

Explanation:

Example 3:

Input: root = []

Output: []

Example 4:

Input: root = [1]

Output: [1]

 

Constraints:

  • The number of the nodes in the tree is in the range [0, 100].
  • -100 <= Node.val <= 100

 

Follow up: Recursive solution is trivial, could you do it iteratively?
A:
-----------------递归解法-----------

/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
vector<int> res;
helper(root, res);
return res;
}

private:
void helper(TreeNode* root, vector<int>& res) {
if (!root)
return;
helper(root->left, res);
helper(root->right, res);
res.push_back(root->val);
}
};

不论是用两个stack (还是每次加到前面,最后reverse回来)原理都是一样的。 
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
vector<int> res;
stack<TreeNode*> S;
if(root)
S.push(root);
while(!S.empty()){
auto tmp = S.top();
S.pop();
res.push_back(tmp->val);
if(tmp->left)
S.push(tmp->left);
if(tmp->right)
S.push(tmp->right);
}
reverse(res.begin(), res.end());
return res;
}
};


Mistakes:
1: 忘了先check root为null 的情况
--------------------------第三遍------(用了一个stack,一个set(set来记录是否被visited过))--------------------
2:  把if写成了while   ,  哎,丢死人了
3:  写成了先  压栈右边,再压栈左边的了。     这个, 当时是考虑到正常顺序,是要先遍历左子树,再遍历右子树的。   但是, 但是没有考虑到, 其实,我们是reversely 加入到al的最前面的。 因此,  要先遍历右子树的。


public class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
         List<Integer> result = new LinkedList();
         if(root ==null)
            return result;
         Stack<TreeNode> stack = new Stack();
         Set<TreeNode> set = new HashSet();
         stack.push(root);
         set.add(root);
         while(!stack.isEmpty()){
             TreeNode node = stack.pop();
             if(set.contains(node)){// first time to find this node
                stack.push(node);
                if(node.right!=null){
                    stack.push(node.right);
                    set.add(node.right);
                }
                if(node.left!=null){
                    stack.push(node.left);
                    set.add(node.left);
                }
                 set.remove(node);
             }else{// node should be printed directly
                 result.add(node.val);
             }
         }// end of while
         return result;
    }
}


Learned:



128. Longest Consecutive Sequence

Q:
Given an unsorted array of integers, find the length of the longest consecutive elements sequence.
For example,
Given [100, 4, 200, 1, 3, 2],
The longest consecutive elements sequence is [1, 2, 3, 4]. Return its length: 4.
Your algorithm should run in O(n) complexity.

A:
由于自己很少很少用HashMap, 因此,遇到了这个用2个HashMap 连用的情况,还是很抓瞎的。 花了近3个小时,才弄出来。哎

思想比较简单: for each   val = num[i],  we map val to the key value, where key is the start of Interval, where val lies.( if val does not find ,we insert.)
and in the second HashMap, we map the key to the interval.
步骤如下:
 if val is not found ,
case 1) check if (val -1 ) and (val +1) exist, if it is, then combine these two intervals
case 2) find (val-1),  add val to this left interval , and interval.end++
case 3) find (val+1) , add val to this right interval, and  interval.start--
case 4) val is isolate,  simply add this list.
always update the maxLen of this list.
 代码如下:
public class Solution {
   public int longestConsecutive(int[] num) {
        // map the num[i] value to a key ( used in next HashMap) , which is the start of interval 
        HashMap<Integer, Integer> map2Key = new HashMap<Integer, Integer>();
        // map key value to interval 
        HashMap<Integer, Interval> map2Interval = new HashMap<Integer, Interval>();
        int maxLen = 0;
        for (int i = 0; i < num.length; i++) {
            int val = num[i];
            if(! map2Key.containsKey(val)) {// first time to find value
                int key;// check val -1 and val +1, 
                if(map2Key.containsKey(val -1)  && map2Key.containsKey(val +1)){// val is in middle
                    // merge two list, and delete the val+1 map
                    int keyLow = map2Key.get(val -1);
                    int keyHigh = map2Key.get(val + 1);
                    map2Interval.get(keyLow).end = map2Interval.get(keyHigh).end;
                    //update all the map2Key values
                    for(int j = val; j<= map2Interval.get(keyHigh).end ;j++){
                        map2Key.put(j, keyLow );
                    }
                    map2Interval.remove(keyHigh);                    
                    key = keyLow;
                }else if(map2Key.containsKey(val -1)){ // val is at right
                    map2Key.put(val, map2Key.get(val-1));
                    key = map2Key.get(val-1);
                    map2Interval.get(key).end++;
                }else if(map2Key.containsKey(val +1)){
                    map2Key.put(val, map2Key.get(val+1));
                    key = map2Key.get(val + 1);
                    map2Interval.get(key).start--;
                }else{ // the new value is isolate
                    key = val;
                    map2Key.put(val, val);
                    map2Interval.put(key, new Interval(val,val));
                }
                // update maxLen
                maxLen = Math.max(maxLen, map2Interval.get(key).end - map2Interval.get(key).start+1);
            }
        }
        return maxLen;
    }
}

--------------------------第二遍-------------看了网上的,别人的思路了------
 思路如下:----
  • Put the number into HashSet, O(n);
  • Check the num[] from the first to the last
    • get value of num[i],remove it from set, low = num[i]-1; high = num[i]+1;
    • check if the low and high are in the set
      • if yes. low or high expand 1; remove this number from set
      • if no. continue;
    • calculate the value of low and high. get longest
  • return longest
public class Solution {
   public int longestConsecutive(int[] num) {
        // use only one hashMap
        Set<Integer> set = new HashSet<Integer>();
        
        for(int i = 0; i< num.length;i++)
            set.add(num[i]);
        int maxLength =0;
        for(int i = 0; i< num.length;i++){
            if( !set.contains(num[i]))// it is already find
                continue;
            int low = num[i]-1;
            int high = num[i] +1;
            while(set.contains(low)){
                set.remove(low);
                low--;
            }
            while(set.contains(high)){
                set.remove(high);
                high++;
            }
            maxLength = Math.max(maxLength, high-low -1);
        }
        return maxLength;
    }
} 

----------------------第三遍 -----------------------------
比第二遍的改进就是,  low= num[i] , 这样就可以同时删除num[i] 这个节点了。



Mistakes:
1:  in case 1, when two list are merged.
     we should point all the val values in the high part of the list,, with their mapping key value to  leftInterval.start


Learned:



129. Sum Root to Leaf Numbers --M

You are given the root of a binary tree containing digits from 0 to 9 only.

Each root-to-leaf path in the tree represents a number.

  • For example, the root-to-leaf path 1 -> 2 -> 3 represents the number 123.

Return the total sum of all root-to-leaf numbers. Test cases are generated so that the answer will fit in a 32-bit integer.

leaf node is a node with no children.

 

Example 1:

Input: root = [1,2,3]
Output: 25
Explanation:
The root-to-leaf path 1->2 represents the number 12.
The root-to-leaf path 1->3 represents the number 13.
Therefore, sum = 12 + 13 = 25.

Example 2:

Input: root = [4,9,0,5,1]
Output: 1026
Explanation:
The root-to-leaf path 4->9->5 represents the number 495.
The root-to-leaf path 4->9->1 represents the number 491.
The root-to-leaf path 4->0 represents the number 40.
Therefore, sum = 495 + 491 + 40 = 1026.

 

Constraints:

  • The number of nodes in the tree is in the range [1, 1000].
  • 0 <= Node.val <= 9
  • The depth of the tree will not exceed 10.
A:    
Note:   A leaf node has no children.
思路不是很清晰,先用一个最简单的方法。实现了,看看有什么问题需要注意的吧。

/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
int sumNumbers(TreeNode* root) {
int total = 0;
helper(root, 0, total);
return total;
}

private:
void helper(TreeNode* root, int pre, int& total) {
if (!root)
return;
if (!root->left && !root->right)
total += pre * 10 + root->val;
helper(root->left, pre * 10 + root->val, total);
helper(root->right,pre * 10 + root->val, total);
}
};

----------------------------因此我们分别计算左右子树,再加起来,就accept了--------------------
public class Solution {
      public int sumNumbers(TreeNode root) {
        if (root == null) {
            return 0;
        }
        return getSum(root,  0);
    }

    private int getSum(TreeNode node,  int curSum) {
        // return the new sum of the of the current
        if (node.left == null && node.right == null) {// find leaf
            return  (node.val + curSum * 10);            
        }
        int sumLeft =0, sumRight =0;
        if (node.left != null) { // go further left
            sumLeft = getSum(node.left,  curSum * 10 + node.val);
        }
        if (node.right != null) {// go further left
            sumRight = getSum(node.right, curSum * 10 + node.val);
        }
        return sumLeft + sumRight;
    }
}



Mistakes:
1 :  思路不清晰。   刚开始走了弯路, 去生成新的tree去了。其实是不需要新的tree的。
当然,也可以在getSum()方法中,传递一个 preSum 参数,来记录前序节点的和。


Learned:
leetcode 中,添加了新的类变量,会导致一系列问题。
 见买买提链接

use a data member.
and the result are accumulated...


I think your algorithm is right. But you use static vars, which might cause problem. Because maybe OJ just creates one instance of class solution, and uses it on many test cases. So you had better reset them. This is the only reason I can come up with. I am not sure. You can use a vector to track the node instead.
 ---------------1337
yes. that's the problem.
and i initialized it to 0 in the function and here we go.

【 在 stupidrobot (robot) 的大作中提到: 】
: 谨慎估计你添加新的类变量了


下面的解法,是不对的。Tiger 自己想想,是哪里不对了。
public class SumRootToLeafNumbers2 {
    public int sumNumbers(TreeNode root) {
        if (root == null) {
            return 0;
        }
        return getSum(root, 0, 0);
    }

    private int getSum(TreeNode node, int totalSum, int curSum) {
        // return the new sum of the of the current
        if (node.left == null && node.right == null) {// find leaf
            totalSum += (node.val + curSum * 10);
            return totalSum;
        }
        if (node.left != null) { // go further left
            totalSum += getSum(node.left, totalSum,curSum * 10 + node.val);
        }
        if (node.right != null) {
            totalSum += getSum(node.right,totalSum, curSum * 10 + node.val);
        }
        return totalSum;
    }
}







答案就在于,左右子树的结果,不应该是 totalSum +=   , 而仅仅 totalSum= 就可以了。