leetcode max-points-on-a-line(Java)

题目:

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));   
        
        //*/
    }
 
}

猜你喜欢

转载自blog.csdn.net/zangdaiyang1991/article/details/89199917