题目:
leetcode max-points-on-a-line
题目描述:
Given n points on a 2D plane, find the maximum number of points that lie on the same straight line
思路:
思路1:
* 思路(以线为维度,判断其余点是否在这条线上):
* 1、穷举法
* 2、两点成线,以线为维度,判断余下的点是否在该条线上,性能较低
* 3、如何判断一个点在这条直线上,使用斜率,避免除法的精确度问题,可以使用分母分子相乘
思路2:
* 思路(以点为维度,每次遍历,利用Map, 找到经过当前点的线中包含点最多的线):
* 1、穷举法
* 2、双重循环,第二层循环统计经过这个点的线中包含点最多的线,需要单独统计重合点和垂直点
* 3、如何判断一个点在这条直线上,使用斜率,避免除法的精确度问题,可以使用Map<dx,dy>作为key
代码:
package com.leetcode.exhaustion;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
/**
* max-points-on-a-line
*
* 题目描述:
* Given n points on a 2D plane,
* find the maximum number of points that lie on the same straight line.
*
*/
public class MaxPointsNumber {
static class Point {
int x;
int y;
Point() { x = 0; y = 0; }
Point(int a, int b) { x = a; y = b; }
}
/**
* 思路(以线为维度,判断其余点是否在这条线上):
* 1、穷举法
* 2、两点成线,以线为维度,判断余下的点是否在该条线上,性能较低
* 3、如何判断一个点在这条直线上,使用斜率,避免除法的精确度问题,可以使用分母分子相乘
*/
public int maxPoints(Point[] points) {
if (points == null || points.length <= 0) {
return 0;
}
int len = points.length;
if (len <= 2) {
return len;
}
int maxCount = 0;
// 两点成线,以线为维度,判断余下及两点间(数组的索引在两个点的索引中间)的点是否在该条线上,三重循环,性能较低
for (int i=0; i<len-1; i++) {
Point first = points[i];
List<Point> pointLst = new ArrayList<>();
pointLst.add(first);
int curCount = 1;
for (int j=i+1; j<len; j++) {
Point second = points[j];
// 判断是否为重复元素,重复元素不能成线
if (isLstContainsCurPoint(second, pointLst)) {
curCount++;
// 全部为同一个点时
if (curCount > maxCount) {
maxCount = curCount;
}
continue;
}
// 遍历i->j区间的元素,如果为first的重复元素并且不在first的连续重复区间,则curCount加1
if (j - i > 1) {
for (int interval = i + 1; interval < j; interval++) {
Point cur = points[interval];
Point pre = points[interval - 1];
if (cur.x == first.x && cur.y == first.y && cur.x != pre.x && cur.y != pre.y) {
curCount++;
}
}
}
// 找到不同的元素,添加新元素到列表
pointLst.add(second);
curCount++;
// 判断余下的元素是否在线上
for (int k=j+1; k<len; k++) {
Point pointK = points[k];
// 重复元素,肯定在线上
if (isLstContainsCurPoint(pointK, pointLst)) {
curCount++;
continue;
}
// 不重复的元素,是否在同一直线上
if (isInSameLine(first, second, pointK)) {
// 添加不重复元素到列表中
pointLst.add(pointK);
curCount++;
}
}
if (curCount > maxCount) {
maxCount = curCount;
}
// 缓存重置
pointLst = new ArrayList<>();
pointLst.add(first);
curCount = 1;
}
}
return maxCount;
}
private boolean isLstContainsCurPoint(Point p, List<Point> pointLst) {
for (Point point : pointLst) {
if (p.x == point.x && p.y == point.y) {
return true;
}
}
return false;
}
private boolean isInSameLine(Point a, Point b, Point c) {
int baYDiff = b.y - a.y;
int baXDiff = b.x - a.x;
int cbYDiff = c.y - b.y;
int cbXDiff = c.x - b.x;
return baYDiff * cbXDiff == baXDiff * cbYDiff;
}
/**
* 思路(以点为维度,每次遍历,利用Map, 找到经过当前点的线中包含点最多的线):
* 1、穷举法
* 2、双重循环,第二层循环统计经过这个点的线中包含点最多的线,需要单独统计重合点和垂直点
* 3、如何判断一个点在这条直线上,使用斜率,避免除法的精确度问题,可以使用Map<dx,dy>作为key
*
*/
public int maxPointsFast(Point[] points) {
if (points == null || points.length <= 0) {
return 0;
}
int len = points.length;
if (len <= 2) {
return len;
}
int maxCount = 0;
for (int i=0; i<len-1; i++) {
// 初始化map,key为斜率,value为数量
// key为斜率容易造成float精度误差,所以最好用类似Pair的结构,存储分子、分母(除以公约数后),可用Map
Map<Map<Integer,Integer>, Integer> linePointCount = new HashMap<>();
// 初始化重合的点的数量
int coincide = 0;
// 初始化垂直的点的数量
int vertical = 0;
int baseX = points[i].x;
int baseY = points[i].y;
for(int j=i+1; j<len; j++) {
int curX = points[j].x;
int curY = points[j].y;
int dX = curX - baseX;
int dY = curY - baseY;
// 重合的点记数+1
if (dX == 0 && dY == 0) {
coincide++;
continue;
}
// 垂直的点记数+1
if (dX == 0) {
vertical++;
continue;
}
// 存储有斜率的点
Map<Integer,Integer> curKey = getPairKey(dX, dY);
if (linePointCount.get(curKey) == null) {
linePointCount.put(curKey, 1);
} else {
linePointCount.put(curKey, linePointCount.get(curKey) + 1);
}
}
// 计算经过这个点的直线包含最多的点数量
int maxMapValueCount = 0;
for (Entry<Map<Integer,Integer>, Integer> entry : linePointCount.entrySet()) {
maxMapValueCount = Math.max(maxMapValueCount, entry.getValue());
}
// 更新最大数量(需要加上这个点本身,所以需要+1)
maxCount = Math.max(maxCount, Math.max(maxMapValueCount + coincide + 1, vertical + coincide + 1));
}
return maxCount;
}
// 生成PairKey
private Map<Integer,Integer> getPairKey(int a, int b) {
Map<Integer,Integer> ret = new HashMap<>(1);
int commonDivisor = getCommonDivisor(a, b);
if (commonDivisor == 0) {
return ret;
}
ret.put(a / commonDivisor, b / commonDivisor);
return ret;
}
// 求公约数
private int getCommonDivisor(int a, int b) {
while (b != 0) {
int r = a % b;
a = b;
b = r;
}
return a;
}
private int getCommonDivisor2(int a, int b) {
if (b == 0) {
return a;
} else {
return getCommonDivisor2(b, a % b);
}
}
public static void main(String[] args) {
Point point0 = new Point();
Point point01 = new Point();
Point point1 = new Point(1, 1);
Point point2 = new Point(1, 2);
Point point3 = new Point(2, 2);
Point point4 = new Point(2, 3);
Point point43 = new Point(2, 2);
Point point5 = new Point(3, 3);
Point[] points = {point0, point01, point1, point2, point3, point4, point43, point5};
MaxPointsNumber num = new MaxPointsNumber();
System.out.println(num.maxPoints(points));
System.out.println(num.maxPointsFast(points));
///*
Point point00 = new Point();
Point point11 = new Point(1, 1);
Point point1_1 = new Point(1, -1);
Point[] points1 = {point00, point11, point1_1};
MaxPointsNumber num1 = new MaxPointsNumber();
System.out.println(num1.maxPoints(points1));
System.out.println(num1.maxPointsFast(points1));
// (3,10),(0,2),(0,2),(3,10)
Point point310_1 = new Point(3, 10);
Point point02_1 = new Point(0, 2);
Point point02_2 = new Point(0, 2);
Point point310_2 = new Point(3, 10);
Point[] points2 = {point310_1, point02_1, point02_2, point310_2};
MaxPointsNumber num2 = new MaxPointsNumber();
System.out.println(num2.maxPoints(points2));
System.out.println(num2.maxPointsFast(points2));
// [(-54,-297),(-36,-222),(3,-2),(30,53),(-5,1),(-36,-222),(0,2),(1,3),(6,-47),(0,4),(2,3),(5,0),(48,128),(24,28),(0,-5),(48,128),(-12,-122),(-54,-297),(-42,-247),(-5,0),(2,4),(0,0),(54,153),(-30,-197),(4,5),(4,3),(-42,-247),(6,-47),(-60,-322),(-4,-2),(-18,-147),(6,-47),(60,178),(30,53),(-5,3),(-42,-247),(2,-2),(12,-22),(24,28),(0,-72),(3,-4),(-60,-322),(48,128),(0,-72),(-5,3),(5,5),(-24,-172),(-48,-272),(36,78),(-3,3)]
Point[] points3 = {new Point(-54,-297),new Point(-36,-222),new Point(3,-2),new Point(30,53),new Point(-5,1),new Point(-36,-222),new Point(0,2),new Point(1,3),new Point(6,-47),new Point(0,4),new Point(2,3),new Point(5,0),new Point(48,128),new Point(24,28),new Point(0,-5),new Point(48,128),new Point(-12,-122),new Point(-54,-297),new Point(-42,-247),new Point(-5,0),new Point(2,4),new Point(0,0),new Point(54,153),new Point(-30,-197),new Point(4,5),new Point(4,3),new Point(-42,-247),new Point(6,-47),new Point(-60,-322),new Point(-4,-2),new Point(-18,-147),new Point(6,-47),new Point(60,178),new Point(30,53),new Point(-5,3),new Point(-42,-247),new Point(2,-2),new Point(12,-22),new Point(24,28),new Point(0,-72),new Point(3,-4),new Point(-60,-322),new Point(48,128),new Point(0,-72),new Point(-5,3),new Point(5,5),new Point(-24,-172),new Point(-48,-272),new Point(36,78),new Point(-3,3)};
MaxPointsNumber num3 = new MaxPointsNumber();
System.out.println(num3.maxPoints(points3));
System.out.println(num3.maxPointsFast(points3));
Point[] points4 = {new Point(40,-23),new Point(9,138),new Point(429,115),new Point(50,-17),new Point(-3,80),new Point(-10,33),new Point(5,-21),new Point(-3,80),new Point(-6,-65),new Point(-18,26),new Point(-6,-65),new Point(5,72),new Point(0,77),new Point(-9,86),new Point(10,-2),new Point(-8,85),new Point(21,130),new Point(18,-6),new Point(-18,26),new Point(-1,-15),new Point(10,-2),new Point(8,69),new Point(-4,63),new Point(0,3),new Point(-4,40),new Point(-7,84),new Point(-8,7),new Point(30,154),new Point(16,-5),new Point(6,90),new Point(18,-6),new Point(5,77),new Point(-4,77),new Point(7,-13),new Point(-1,-45),new Point(16,-5),new Point(-9,86),new Point(-16,11),new Point(-7,84),new Point(1,76),new Point(3,77),new Point(10,67),new Point(1,-37),new Point(-10,-81),new Point(4,-11),new Point(-20,13),new Point(-10,77),new Point(6,-17),new Point(-27,2),new Point(-10,-81),new Point(10,-1),new Point(-9,1),new Point(-8,43),new Point(2,2),new Point(2,-21),new Point(3,82),new Point(8,-1),new Point(10,-1),new Point(-9,1),new Point(-12,42),new Point(16,-5),new Point(-5,-61),new Point(20,-7),new Point(9,-35),new Point(10,6),new Point(12,106),new Point(5,-21),new Point(-5,82),new Point(6,71),new Point(-15,34),new Point(-10,87),new Point(-14,-12),new Point(12,106),new Point(-5,82),new Point(-46,-45),new Point(-4,63),new Point(16,-5),new Point(4,1),new Point(-3,-53),new Point(0,-17),new Point(9,98),new Point(-18,26),new Point(-9,86),new Point(2,77),new Point(-2,-49),new Point(1,76),new Point(-3,-38),new Point(-8,7),new Point(-17,-37),new Point(5,72),new Point(10,-37),new Point(-4,-57),new Point(-3,-53),new Point(3,74),new Point(-3,-11),new Point(-8,7),new Point(1,88),new Point(-12,42),new Point(1,-37),new Point(2,77),new Point(-6,77),new Point(5,72),new Point(-4,-57),new Point(-18,-33),new Point(-12,42),new Point(-9,86),new Point(2,77),new Point(-8,77),new Point(-3,77),new Point(9,-42),new Point(16,41),new Point(-29,-37),new Point(0,-41),new Point(-21,18),new Point(-27,-34),new Point(0,77),new Point(3,74),new Point(-7,-69),new Point(-21,18),new Point(27,146),new Point(-20,13),new Point(21,130),new Point(-6,-65),new Point(14,-4),new Point(0,3),new Point(9,-5),new Point(6,-29),new Point(-2,73),new Point(-1,-15),new Point(1,76),new Point(-4,77),new Point(6,-29)};
MaxPointsNumber num4 = new MaxPointsNumber();
System.out.println(num4.maxPoints(points4));
System.out.println(num4.maxPointsFast(points4));
Point[] points5 = {new Point(),new Point(1,2),new Point(1,1),new Point(3,4),new Point(2,2)};
MaxPointsNumber num5 = new MaxPointsNumber();
System.out.println(num5.maxPoints(points5));
System.out.println(num5.maxPointsFast(points5));
//*/
}
}