package com.freetymekiyan.algorithms.level.hard; import java.util.HashMap; import java.util.Map; /** * Given n points on a 2D plane, find the maximum number of points that lie on the same straight line. * <p> * Company Tags: LinkedIn, Apple, Twitter * Tags: Hash Table, Math * Similar Problems: (M) Line Reflection */ public class MaxPointsOnALine { /** * Set a point and go through the rest * Use max to record the max points of current loop * Use countSame to track the number of points * Use result to track the max points * Use a map to store lines, key is the feature of the line, value is the * number of points * * @param points points generated, can be same * @return number of max points share a same line */ private static int maxPoints(Point[] points) { if (points.length < 3) { return points.length; // 0/1/2 points } int res = 1; // at least 1 point Map<String, Integer> map = new HashMap<String, Integer>(); // line,count for (int i = 0; i < points.length; i++) { int max = 0; int countSame = 0; // # of same points for (int j = i + 1; j < points.length; j++) { if (points[i].x == points[j].x && points[i].y == points[j].y) { countSame++; // same point } else { String key = normalize(points[i], points[j]); // a|b|c if (map.containsKey(key)) { // on the line int count = map.get(key) + 1; map.put(key, count); // update count if (count > max) { max = count; // update max } } else { // not on any line map.put(key, 1); if (max == 0) { max++; // update max } } } } res = Math.max(res, max + countSame + 1); // +1 for the start point map.clear(); // clear map for next point } return res; } /** * use ax + by = c to represent a line and a|b|c as a key for that line * a, b, c should be normalized, how? * <p> * special case, vertical, horizontal */ private static String normalize(Point p1, Point p2) { int a, b; float c; if (p1.x == p2.x) { // vertical b = 0; a = 1; c = p1.x; } else if (p1.y == p2.y) { // horizontal a = 0; b = 1; c = p2.y; } else { // ax + by = c int dx = p1.x - p2.x; int dy = p1.y - p2.y; /*reduce to simplest*/ int gcd = gcd(Math.abs(dx), Math.abs(dy)); a = dy / gcd; b = dx / gcd; if (a * b < 0) { // force a to be negative a = -1 * Math.abs(a); b = Math.abs(b); } else { // force both positive a = Math.abs(a); b = Math.abs(b); } c = a * p1.x + b * p1.y; // c = ax + by } return a + "|" + b + "|" + c; // key format } /** * recursively calculate the greateset common divisor of two numbers */ private static int gcd(int a, int b) { if (b == 0) { return a; // stop when b == 0, a is the gcd } return gcd(b, a % b); // a <- b, b <- a % b } class Point { int x; int y; Point() { x = 0; y = 0; } Point(int a, int b) { x = a; y = b; } } }