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
Second,
You can use
Big Mistake:
当计算斜率的时候, long / long 的结果是 long 的, 而不是double的, 因此,要想 得到小数的结果, 需要把一个先转换成double类型, 我艹~~~~~~~~~~~~~~~~~~~ 你SB,~~~~~脑子坏掉了。 Tiger
错误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,只代表思路)----------------------
double
in such kind of problem, you don't need them and they will make you suffer, DO use integersSecond,
<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; } }
No comments:
Post a Comment