Sunday, December 8, 2013

Max Points on a Line !!!!!!!!!!!!!!!!!!

Q:
Given n points on a 2D plane, find the maximum number of points that lie on the same straight line.

A:
现在的思路,就是,先根据slope 分组,  具有相同(很接近)斜率 slope的点放到一组。
然后,对每一组, 再找出具有相同(很接近) 的截距(intersect)的点来。 --记录最大的list的长度,然后返回即可。--------------理论上来讲,是O(n^2)
注意: 重复的点,也算不同的点来count
哎,就TM知道, 不能用这种屌丝办法,会超时,  现在好了,真的超时了。废了一晚上的时间。~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~主要的时间开销,就在Step1 里的双重循环上了。(对每个点,找它的所有的slope),其目的是: 将具有same  slope 的组合 放到一起。(虽然,可能不是相同的节点)
-----------------------------------------代码见本页最后--------------------------------



----------------其实,不是算法复杂度的问题, 是利用了O(n^2)的空间的问题。  也存储了太多的,冗余的slope。------------------------

--------------------------下面是别人的思路-------------------------------------------------
分析:首先要注意的是,输入数组中可能有重复的点。由于两点确定一条直线,一个很直观的解法是计算每两个点形成的直线,然后把相同的直线合并,最后 包含点最多的直线上点的个数就是本题的解。我们知道表示一条直线可以用斜率和y截距两个浮点数(垂直于x轴的直线斜率为无穷大,截距用x截距),同时还需 要保存每条直线上的点(避免重复)。听起来就很麻烦,但是基于这个思想有一种简单的实现方式:
  • 以某点O为中心,计算它和其他点的斜率,如果有两个点A、B和O点形成的斜率相等,那么ABO三点是共线的,如果有多个点和O的斜率相等,那么这 多个点和O也是共线的,因此我们可以求出包含O的所有直线中点数最多的直线,会得到一个最大共线点数k(O),如果和O点重复的点有n个(除了O本身), 那么K(O) = K(O) + n。这一步的计算中,为了提高效率,我们可以用哈希表来保存某个斜率对应的点的数目。
  • 对数组中的每一个点i,按照第一步计算得到每个点最大点数K(i)
  • 从k(i)中选取最大的就是本题的解
  • 注意:为了避免重复计算,以数组中第i个点为中心时,只计算数组中它右边的所有点        

public class MaxPointsOnALine2 {

    public int maxPoints(Point[] points) {
        // for each point, we calculate its slope with others,
        // if they are the same, we save them into a Hashtable
        if (points.length <= 1) { // pay attention to length == 1
            return points.length;
        }
        int nMaxPoints = 0;

        for (int i = 0; i < points.length - 1; i++) {
            // for slope Double, we map its count of integers that could form
            // this slope with points[i]

            Map<Long, Integer> map = new HashMap<Long, Integer>();
            Point pLeft = points[i];
            int nDuplicate = 1; // how many points that having the same (x,y)
                                // value with pLeft(inclusive)
            for (int j = i + 1; j < points.length; j++) {
                Point pRight = points[j];
                if (pLeft.x == pRight.x && pLeft.y == pRight.y) {
                    nDuplicate++;
                    continue;
                } else {
                    Long longSlope;  // is is 10000* actuall slope
                    // calculate their slope and save into a the HashMap
                    if (pLeft.x == pRight.x) {
                        longSlope = Long.MAX_VALUE;
                    } else {
                        double slope = (double) (pRight.y - pLeft.y) / (pRight.x - pLeft.x);
//                        slope = BigDecimal.valueOf(slope).setScale(5, BigDecimal.ROUND_HALF_DOWN)
//                                .doubleValue();// round the slope
                        longSlope =Math.round(slope*10000);
                    }
                    // insert the slope into the hashMap
                    Integer objCount = map.get(longSlope);
                    if (objCount == null) {
                        map.put(longSlope, 1);
                    } else {
                        map.put(longSlope, objCount + 1);
                    }
                }
            }// enf of for loop ---- j
            nMaxPoints = Math.max(nMaxPoints, nDuplicate); // in case that
                                                            // HashMap is empty
            // search all slopes passing pLeft, and update the nMaxPoints
            Iterator<Entry<Long, Integer>> iter = map.entrySet().iterator();
            while (iter.hasNext()) { // for each slope, need find the max point
                Map.Entry<Long, Integer> entry = (Map.Entry<Long, Integer>) iter.next();
                Integer curCount = (Integer) entry.getValue();
                nMaxPoints = Math.max(nMaxPoints, curCount + nDuplicate);
            } // end of while
        } // end of for loop ----- ivalue

        return nMaxPoints;
    }
}

罪魁祸首找到啦~~~~~~~~~~~~~~, 就是
//                        slope = BigDecimal.valueOf(slope).setScale(5, BigDecimal.ROUND_HALF_DOWN)
//                                .doubleValue();// round the slope
把这句话注释掉,就可以了。  ~~~~~~~加上这句话,  运行时间,变成了10倍多。
~~~~~~~~可是,不是说double不能完全一样的值吗???????????
嗯,出现问题了,  去掉这句话之后, 出现  -0.0 和0.0 的slope不一样的问题。怎么办????

解决方法就是: 把double的slope * 10000, 转换成long 类型,就行了。

因此,方法一,也如此解决pass。    具体修改,可以见Eclipse中的代码。  (而且,上面的代码,也没有处理duplicates 的问题。  因此,还是只看解法2吧。


Mistakes:
1:  刚开始,没有考虑,输入数组的长度为0, 为1 的情况。  反而在题目中,直接默认为长度超过2了。  直接调用了, slopeList[0] 啊什么的

2:: 对于slope 为正无穷的情况, 在求 difference  of intersect的时候,要要单独处理,  其各自的intersect 可以用point.x 的值代替。

 3: 注意: 重复的点,也算不同的点来count。  但是,在实际

4: 一开始,设
        double minDiff = 0.0000001;
结果太小了,计算机的round error还是超过了这个值。 更改为
        double minDiff = 0.00001;  

5: 方法二的时候, update nMaxPoints 在第二个for循环后面,而且,刚开始,弄成iter 必须是非空才能update 的。因此,对于输入  ”(0,0),(0,0)“  就不能update nMaxPoints了。


Learned:
1:  round  a double data,   可以用下面的代码:
        double slope = 2.1234567;
        slope = BigDecimal.valueOf(slope).setScale(5, BigDecimal.ROUND_HALF_UP).doubleValue();
        System.out.println(slope);
打印的结果是   2.12346
2:  在leetcode中,也可以在class的前面,加入import 语句。

3: 如何遍历一个HashMap
Map map = new HashMap();
Iterator iter = map.entrySet().iterator();
while (iter.hasNext()) {
    Map.Entry entry = (Map.Entry) iter.next();
    Object key = entry.getKey();
    Object val = entry.getValue();
}
效率高,以后一定要使用此种方式!
3.5------------------另一种方式,更简单 ----------------
HashMap<Integer,Integer> map = new HashSet<Integer,Integer>();
for(int key : map.keySet()
        // do something

4:  表示直线,
First, DO NOT use double in such kind of problem, you don't need them and they will make you suffer, DO use integers
Second, <k, b> is not a good idea simply because you cannot represent all the possible line using it.
You can use <a, b, c> to represent all the possible line, i.e., ax + by + c = 0 where a, b, c are all integers.

--------------------------------------------第二遍-----------------------------------------

没有去遍历map,而直接是在建立的时候,查询并保存了结果。


Big Mistake: 
        当计算斜率的时候,  long / long  的结果是 long 的, 而不是double的, 因此,要想 得到小数的结果, 需要把一个先转换成double类型,  我艹~~~~~~~~~~~~~~~~~~~  你SB,~~~~~脑子坏掉了。 Tiger


import java.util.*;

public class Solution {
    public int maxPoints(Point[] points) {
        if (points.length <= 1)
            return points.length;
        int maxPointOnLine = 0;
        HashMap<Long, Integer> map = new HashMap<Long, Integer>();// map scale to count
        for (int i = 0; i < points.length; i++) {
            map.clear();
            int duplicate = 1;
            int maxCount = 0;
            for (int j = i + 1; j < points.length; j++) {
                Long ratio = 0L;
                if (points[j].x == points[i].x && points[j].y == points[i].y) {
                    duplicate++;
                } else {
                    if (points[j].x == points[i].x)
                        ratio = Long.MAX_VALUE;
                    else
                        ratio = (long) ( (double)(points[j].y - points[i].y) / (points[j].x - points[i].x) * 100000);
                    int count  =1;
                    if (map.get(ratio) != null)
                        count = map.get(ratio) +1;
                    map.put(ratio, count);
                    maxCount = Math.max(maxCount, count);
                }
            }
            maxPointOnLine = Math.max(maxPointOnLine, duplicate + maxCount);
        }
        return maxPointOnLine;
    }
}


错误2 :     2遍都有这个错误。 
另外,  在update maxPointOnLine 的时候, 不能把              maxPointOnLine 放到for  ( int   j) 这个循环的里边, 会导致  update的时候,  count 和 nDuplicate 不同时是最大的。  因此,要放到外边去。里层循环, 记录斜率的maxCount  .

哎,Tiger,先不要节省代码行数, 即使超出30行又能如何? 要保证正确才对。

错误3: 
曾经写过这样   count  = (map.get(key) == null)? 0 : map.get(key)   + 1 ;  这样的结果不是预期的。   不知道为什么  ~~~~~~~~





--------------------------------解法1 的代码(没pass,只代表思路)----------------------

import java.math.BigDecimal;
import java.util.Map;
import java.util.Map.Entry;

public class Solution {
public int maxPoints(Point[] points) {
        // first divided by slope,
        // NOTE: due to the fact that digital numbers cannot be too concise, we
        // use approximatin
        if (points == null || points.length == 0)
            return 0;
        if (points.length == 1)
            return 1;
        double minDiff = 0.00001;
        Map<Double, Integer> map = new HashMap<Double, Integer>();
        // a map from slope into the position of List of Points
        ArrayList<HashSet<Point>> slopeList = new ArrayList<HashSet<Point>>();

        // Step1: for each pair of points, calculte their slope and add them
        // into the hashMap
        for (int i = 0; i < points.length - 1; i++) {
            for (int j = i + 1; j < points.length; j++) {
                Point pLeft = points[i].x < points[j].x ? points[i] : points[j];
                Point pRight = points[i].x >= points[j].x ? points[i] : points[j];
                double slope;
                if (pRight.x - pLeft.x == 0) {
                    slope = Double.MAX_VALUE;
                } else {
                    slope =( (double)(pRight.y - pLeft.y)) / (pRight.x - pLeft.x);
                    slope = BigDecimal.valueOf(slope).setScale(5, BigDecimal.ROUND_HALF_DOWN)
                            .doubleValue();// round the slope
                }
                Integer index = map.get(slope);
                if (index == null) { // if current slope is not found
                    index = slopeList.size();
                    map.put(slope, slopeList.size()); // add at last position
                    HashSet<Point> tmp = new HashSet<Point>();
                    slopeList.add(tmp); // add one element at slopeList
                }
                HashSet<Point> tmpSet = slopeList.get(index);
                tmpSet.add(pLeft);
                tmpSet.add(pRight);
            }
        }

        // Step 2: now all list contains points of same slope, with some of
        // its neighgbors,
        // we try to find , if using same slope, check the intersect difference
        int maxPoints = 0;
        Iterator<Entry<Double, Integer>> iter = map.entrySet().iterator();
        while (iter.hasNext()) { // for each slope, need find the max point
                                    // through a line ,coz they might parrelel
            Map.Entry<Double, Integer> entry = (Map.Entry<Double, Integer>) iter.next();
            Double slope = (Double) entry.getKey();
            Integer index = (Integer) entry.getValue();

            HashSet<Point> curSet = slopeList.get(index);
            if (curSet.size() <= maxPoints) {
                continue;
            }
            // each element contains lists that having same slope and intersect 
            ArrayList<ArrayList<Point>> all = new ArrayList<ArrayList<Point>>();
            for (Point eachPoint : curSet) { // check the difference with
                                                // existing ones
                boolean findGroup = false;
                // check all points in the list, that might belongs to same
                // group
                for (int j = 0; j < all.size(); j++) {
                    // if found the node that with intercept small enough
                    // arraylist, with same slope and same intersect
                    ArrayList<Point> al = all.get(j);
                    double curIntercept, newIntersect;
                    if (slope == Double.MAX_VALUE) {
                        curIntercept = al.get(0).x;
                        newIntersect = eachPoint.x;
                    } else {
                        curIntercept = al.get(0).y - slope * al.get(0).x;
                        newIntersect = eachPoint.y - slope * eachPoint.x;
                    }
                    if (Math.abs(curIntercept - newIntersect) < minDiff) {
                        al.add(eachPoint);
                        findGroup = true;
                        break;
                    }
                }
                // if not find its group, we append this point to all
                if (findGroup == false) {
                    ArrayList<Point> al = new ArrayList<Point>();
                    al.add(eachPoint);
                    all.add(al);
                }
            }
            // find the maximun number of List, for current slope,
            for (int i = 0; i < all.size(); i++) {
                ArrayList<Point> al = all.get(i);
                if (al.size() > maxPoints) {
                    maxPoints = al.size();
                }
            }
        }
        return maxPoints;
    }
}

Saturday, December 7, 2013

String to Integer (atoi)

Q:
Implement atoi to convert a string to an integer.
Hint: Carefully consider all possible input cases. If you want a challenge, please do not see below and ask yourself what are the possible input cases.
Notes: It is intended for this problem to be specified vaguely (ie, no given input specs). You are responsible to gather all the input requirements up front.
Requirements for atoi: The function first discards as many whitespace characters as necessary until the first non-whitespace character is found. Then, starting from this character, takes an optional initial plus or minus sign followed by as many numerical digits as possible, and interprets them as a numerical value.
The string can contain additional characters after those that form the integral number, which are ignored and have no effect on the behavior of this function.
If the first sequence of non-whitespace characters in str is not a valid integral number, or if no such sequence exists because either str is empty or it contains only whitespace characters, no conversion is performed.
If no valid conversion could be performed, a zero value is returned. (这里是句号)
 If the correct value is out of the range of representable values, INT_MAX (2147483647) or INT_MIN (-2147483648) is returned.

A:
一开始,竟然没敢把题目看完,就不敢做了。哎,真是可笑,竟然留到了最后才做,这么一道简单的题目。 ╮(╯▽╰)╭    可悲啊, Tiger

思路很简单,就是一个个地数呗。

public class Solution {
   public int atoi(String str) {
        // Note: The Solution object is instantiated only once and is reused by
        // each test case.
        int INT_MAX = 2147483647;
        int INT_MIN = -2147483648;
        StringBuffer buf = new StringBuffer(str.trim());// ignore space at two
                                                        // ends
        int sign = 1;
        if (buf.length() == 0)
            return 0;
        if (buf.charAt(0) == '+') { // delete +
            buf.delete(0,1);
        } else if (buf.charAt(0) == '-') {
            sign = -1;
            buf.delete(0, 1);
        }
        str = buf.toString();
        long sum = 0;
        // adding from back to begin
        for (int i = 0; i < str.length(); i++) {
            char ch = str.charAt(i);
            if (ch < '0' || ch > '9')
                break;
            int val = ch - '0';
            sum = sum * 10 + val;
            if (sign == 1) {
                if (sum > INT_MAX) {
                    return  INT_MAX;
                }
            } else { // sum should be negative
                if (sign * sum < INT_MIN) {
                    return   INT_MIN;
                }
            }
        }
        return (int) (sign * sum);

    }
}



Mistakes:
1:  题意理解错误, : 当超出范围时, 返回最大或最小的。 而不是返回  0.
2;  自己宽泛了题意。 当 + -符合后面,是不能再有空格的。

1: 要注意:
return (int) (sign * sum);   这里, 由于sum 是long 型的,   如果仅仅写成
return (int) sign * sum;     是不对的,  会先只转变 sign 的类型。    


--------------第二遍--------------
1: 刚开始,直接开始 考虑str[0] 的值。  而没有先考虑,是否存在 0 位置。

*********************每次计算之前判断是否越界****************

public class Solution { 
    public int myAtoi(String str) {
        boolean isNeg = false;
        str = str.trim();
        if(str.length()==0)
            return 0;
        if(str.charAt(0)=='+' || str.charAt(0)=='-'){
            isNeg = str.charAt(0)=='-';
            str = str.substring(1);
        }
        int sum = 0;
        for(int i =0;i<str.length();i++){
            char ch = str.charAt(i);
            if(Character.isDigit(ch)==false)
                break;
            if(isNeg && (sum < Integer.MIN_VALUE/10 || (sum == Integer.MIN_VALUE/10 && ch>= '8') ))
                return Integer.MIN_VALUE;
            if( !isNeg && (-sum > Integer.MAX_VALUE/10  || ( -sum == Integer.MAX_VALUE/10  && ch>='7')))
                return Integer.MAX_VALUE;
            sum = sum * 10 - (ch -'0');
        }
        return isNeg?sum:-sum;
    }
}

这里,或者直接用long 来记录结果,然后对比即可。

146. LRU Cache -------M !!!!!!!!!!!

Design and implement a data structure for Least Recently Used (LRU) cache. It should support the following operations: get and put.
get(key) - Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1.
put(key, value) - Set or insert the value if the key is not already present. When the cache reached its capacity, it should invalidate the least recently used item before inserting a new item.
The cache is initialized with a positive capacity.
Follow up:
Could you do both operations in O(1) time complexity?
Example:
LRUCache cache = new LRUCache( 2 /* capacity */ );

cache.put(1, 1);
cache.put(2, 2);
cache.get(1);       // returns 1
cache.put(3, 3);    // evicts key 2
cache.get(2);       // returns -1 (not found)
cache.put(4, 4);    // evicts key 1
cache.get(1);       // returns -1 (not found)
cache.get(3);       // returns 3
cache.get(4);       // returns 4

A:

------------------用doubly-linked list做更新 + HashMap 来做cache---------
class LRUCache {
public:
    LRUCache(int capacity) {        
        cap = capacity;
        header = new Node(0,0);
        tail = new Node(0,0);
        header->next = tail;
        tail->pre = header;
    }
    
    int get(int key) {
        if(map.find(key)==map.end()){
            return -1;
        }
        Node* tmp = map[key];
        detachAndPutBegin(tmp);
        return tmp->val;
    }
    
    void put(int key, int value) {
        Node * tmp;
        if(map.find(key) == map.end()){
            tmp = new Node(key, value);
            if(map.size()==cap){
                auto delNode = tail->pre;
                delNode->pre->next = tail;
                tail->pre = delNode->pre;
                map.erase(delNode->key);
                delete(delNode);
            }
            map.insert({key, tmp});
            // insert at the begining of the list
            detachAndPutBegin(tmp);
        }else{
            map[key]->val = value;
            tmp = map[key];
            detachAndPutBegin(tmp);
        }
    }
private:    
    struct Node{
        int key, val; // need key to point back to delete the last Node* from map
        Node *pre, *next;
        Node(int key, int v):key(key), val(v), pre(NULL),next(NULL){}
    };
    Node *header, *tail;
    int cap =0;
    unordered_map<int,Node*> map;
    
    int detachAndPutBegin(Node * tmp){
        if(tmp->next){ // if in the list
            tmp->pre->next = tmp->next;
            tmp->next->pre = tmp->pre;
        }        
        // insert into the beginning of the list
        tmp->next = header->next;
        tmp->pre = header;
        header->next->pre = tmp;
        header->next = tmp;
        return tmp->val;
    }
};

/**
 * Your LRUCache object will be instantiated and called as such:
 * LRUCache* obj = new LRUCache(capacity);
 * int param_1 = obj->get(key);
 * obj->put(key,value);
 */


Mistakes:
1:刚开始 删除list的时候,是在Dlist中做的。  可是,还需要把HashMap里的也删掉。

2:   考虑到要在Map中删除节点, 可是,Map只能按照Key 来删。
而我们在ListNode中,每次要删的是最后一个Node,  因此,我们需要在Node中保存每个点的key 值。

还有一个trick 就是double linked list 的前后都加了dummy 节点。这样就不需要处理边界情况。

Wednesday, December 4, 2013

Maximal Rectangle !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

Q:
Given a 2D binary matrix filled with 0's and 1's, find the largest rectangle containing all ones and return its area.

A:
简单的seading算法,(就是假装某点为左上角的corner,然后grow) 应该是太慢了。要注意利用rectangle这个词儿。------------O(n^3)

哎,学会了就行了。 那么纠结干啥?
你就是费劲巴力自己做出来, 有个屁用啊?Tiger, 你SB
这道题,是自己直接上网搜的思路。  看这里, 和这里
具体就是:利用求histogram的思路, 一层层地求最大面积。
 build a cache to make this problem to a list of "Largest Rectangle in Histogram" in M layers 
will take O(M*N^2) time and O(M) space

--------------------第二遍------------------

public class Solution {
    public int maximalRectangle(char[][] matrix) {
        if (matrix.length == 0)
            return 0;
        int m = matrix.length, n = matrix[0].length,maxArea = 0;
        int[] hist = new int[n];
        for (int i = 0; i < m; i++) { // for each row
            for (int j = 0; j < n; j++)
                hist[j] = matrix[i][j] == '0'? 0: hist[j]+1 ;
            maxArea = Math.max(maxArea, getMaxArea(hist));
        }
        return maxArea;
    }
    public int getMaxArea(int[] hist) { // calculate the max-area of a histogram
        int maxArea = 0, n = hist.length;
        Stack<int[]> stack = new Stack<>();
        for (int i = 0; i < n; i++) {
            int preIndex = i;
            while( ! stack.isEmpty()){
                int[] tmp = stack.peek();
                if( hist[i] < tmp[1]  ){ // calculate area before i
                    stack.pop();
                    maxArea = Math.max(maxArea, tmp[1] * (i-tmp[0]));
                    preIndex = tmp[0];
                }else{
                    break;
                }
            }
            stack.push( new int[]{preIndex,hist[i]} );
        }
        while( ! stack.isEmpty() ){
            int[] tmp = stack.pop();
            maxArea = Math.max(maxArea, tmp[1] * (n-tmp[0]) );
        }
        return maxArea;
    }
}


Mistakes:
这里的核心还是利用histgram 求矩形面积。
其难点在2个地方:
1: 每次发现一个更高的, 则push stack。 否则,计算其前面的比他小的矩形的面积(不包括现在的位置)    2:  计算完毕,重新push的时候,需要存入 最远的index,和现在的高度。(因此我们需要记录 preIndex)  


Learned:
1: 用极大化思想解决最大子矩形问题