149.Max Points on a Line

Tag: [Math], [Map], [Double], [gcd]

Com: {t}

Hard: [###]

Link: https://leetcode.com/problems/max-points-on-a-line/\#/description

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


Solution: Map, GCD

/**
 * Definition for a point.
 * class Point {
 *     int x;
 *     int y;
 *     Point() { x = 0; y = 0; }
 *     Point(int a, int b) { x = a; y = b; }
 * }
 */
public class Solution {
    public int maxPoints(Point[] points) {
        if (points == null || points.length == 0) {
            return 0;
        }

        List<Point> pointList = new ArrayList<>(Arrays.asList(points));
        Collections.sort(pointList, new Comparator<Point>() {
            @Override
            public int compare(Point p1, Point p2) {
                return p1.x - p2.x;
            }
        });

        int max = 0;
        int size = pointList.size();
        for (int i = 0; i < size; i ++) {
            Point p1 = pointList.get(i);
            Map<String, Integer> counting = new HashMap<>();
            int numOfSamePoints = 0;
            int numOfPointsOnVerticalLine = 0;
            int numOfPointsOnHorizontalLine = 0;

            for (int j = i + 1; j < size; j ++) {
                Point p2 = pointList.get(j);

                if (p1.x == p2.x && p1.y == p2.y) {
                    numOfSamePoints ++;
                } else if (p1.x == p2.x) {
                    numOfPointsOnVerticalLine ++;
                } else if (p1.y == p2.y) {
                    numOfPointsOnHorizontalLine ++;
                } else {
                    int y = p2.y - p1.y;
                    int x = p2.x - p1.x;

                    int gcd = getGCD(x, y);
                    y /= gcd;
                    x /= gcd;

                    String key = String.format("%d:%d", y, x);

                    if (counting.containsKey(key)) {
                        counting.put(key, counting.get(key) + 1);
                    } else {
                        counting.put(key, 1);
                    }
                }
            }

            int currMax = 0;
            for (Map.Entry<String, Integer> en : counting.entrySet()) {
                currMax = Math.max(currMax, en.getValue());
            }

            currMax = Math.max(currMax, numOfPointsOnVerticalLine);
            currMax = Math.max(currMax, numOfPointsOnHorizontalLine);
            currMax += numOfSamePoints + 1; // add the same points and the p1 itself.

            max = Math.max(max, currMax);
        }

        return max;
    }

    private int getGCD(int a, int b) {
        a = Math.abs(a);
        b = Math.abs(b);
        if (b == 0) {
            return a;
        }

        return getGCD(b, a % b);
    }
}

Revelation:

  • 不做出发避免两数相除后产生无限不限不循环小数,因为由于Double可以表示精度的限制,两个无限不循环小数本来应该不相等,但Double可以表示的精度有限,Java或其他语言就可能认为他们相等了,要解决无限不循环小数就要避免除法,那么我们其实可以不用slope作为map的key,我们可以就用y的差和x的差来作为key,但有这种case存在,例如一组y的差和x的差为(1, 2), 另一组y的差和x的差为(2, 4), 但我们知道 1/2 == 2/4, 所以我们要对y的差和x的差计算gcd(最大公约数), 他们都除以最大公约数后再记入map.
  • sort points based on x 的好处是对于每个p1 只要看他右侧的点,不用计算左侧点和p1的slope (也就是内层循环不用每次都从index= 0 开始了). 因为p1左侧的点和p1之间的slope都已经计算过了.

Note:


Failed Solution:

/**
 * Definition for a point.
 * class Point {
 *     int x;
 *     int y;
 *     Point() { x = 0; y = 0; }
 *     Point(int a, int b) { x = a; y = b; }
 * }
 */
public class Solution {
    public int maxPoints(Point[] points) {
        if (points == null || points.length == 0) {
            return 0;
        }

        List<Point> pointList = new ArrayList<>(Arrays.asList(points));
        Collections.sort(pointList, new Comparator<Point>() {
            @Override
            public int compare(Point p1, Point p2) {
                return p1.x - p2.x;
            }
        });

        int max = 0;
        int size = pointList.size();
        for (int i = 0; i < size; i ++) {
            Point p1 = pointList.get(i);
            Map<Double, Integer> counting = new HashMap<>();
            int numOfSamePoints = 0;
            int numOfPointsOnVerticalLine = 0;
            int numOfPointsOnHorizontalLine = 0;

            for (int j = i + 1; j < size; j ++) {
                Point p2 = pointList.get(j);

                if (p1.x == p2.x && p1.y == p2.y) {
                    numOfSamePoints ++;
                } else if (p1.x == p2.x) {
                    numOfPointsOnVerticalLine ++;
                } else if (p1.y == p2.y) {
                    numOfPointsOnHorizontalLine ++;
                } else {
                    double slope = (double)(p2.y - p1.y) / (double)(p2.x - p1.x);
                    long roundSlope = Math.round(slope * 1000000000000000000L);

                    if (counting.containsKey(slope)) {
                        counting.put(slope, counting.get(slope) + 1);
                    } else {
                        counting.put(slope, 1);
                    }
                }
            }

            int currMax = 0;
            for (Map.Entry<Double, Integer> en : counting.entrySet()) {
                currMax = Math.max(currMax, en.getValue());
            }

            currMax = Math.max(currMax, numOfPointsOnVerticalLine);
            currMax = Math.max(currMax, numOfPointsOnHorizontalLine);
            currMax += numOfSamePoints + 1; // add the same points and the p1 itself.

            max = Math.max(max, currMax);
        }

        return max;
    }
}

Revelation:

  • Cannot pass the case: points = [[0, 0], [94911151, 94911150], [94911152, 94911151]], 因为Double的精度有限无法完整的表示无限不循环小数.

results matching ""

    No results matching ""