Question Link:

https://leetcode.com/problems/max-points-on-a-line/


Brute-force Solution

By given points, there are most different lines in the 2D space. To solve the problem, we just check all possible lines, and for each line count the points on it. This native approach takes time.

Using Hash Table

We can use a hash table to store the lines to improve the brute-force solution, where the time complexity can be .

More Fancy Algorithm

There is another way to solve the problem by converting the 2D space to its dual space. In a work, map the (point, line)-space into a (line, point)-space. The points are converted into lines in the dual space. Then, the intersection of lines in the dual space corresponds to the lines passing through those points in the original space. Therefore, instead of “finding the line pass through most points by given points”, we are asked to solve problem of “finding the intersection points that most lines pass through by given lines”. However, this algorithm is still in time.

Dynamic Programming

Finally, I decide to solve the problem using Dynamic Programming. Let be the solution to the problem with points . Then we have the recursive function as follows:

Suppose we already have points and the solution line with points, there are only two non-overlapped cases: (1) The solution line passes through the point $P_n$; or (2) The solution line does not pass through the point . For case (1), we only need to find the number of points lie on the line that also passes through (function BestSolutionPassingPoint, which is of time by using hash technique). For case (2), the solution of is just the solution for .

Line representation and Hash

The key of the algorithm is how to represent the line passing through two points and hash it. Let and be two points, then we can represent the line as .

Notice that this representation has a drawback that it cannot cover the case of . Another representation is to use ax+by=c, but personally I do not like it since it requires gcd and more values to hash.

Therefore, the pair is unique to a line, which can be used as the hash key. In DP approach, all lines we concern are passing through a same point. Then we can consider this point as the origin, and all lines can represented as or for lines parallel to the x-axis. So we can use the value of $k$ only as the hash key. Note that the line should be considered separately.

Duplicate points

We have to consider the case that there exist same points in the point set. If we use brute-force approach, we need to scan the points and count the duplicates first. For DP approach, it becomes much easier to handle the duplicates. To calculate BestSolutionPassingPoint(P, S), if there exist same point to the point in points set , lets say . What we need to do is just to add 1 to every count since all lines must pass through since they passes through .

C++ Implement

The C++ code should like:

[-] C++ code of LeetCode 150
/**
 * Definition for a point.
 * struct Point {
 *     int x;
 *     int y;
 *     Point() : x(0), y(0) {}
 *     Point(int a, int b) : x(a), y(b) {}
 * };
 */
#include <unordered_map>

class Solution {
public:
    static const int EPSILON = 1000000;  // float precision purpose
    std::unordered_map<int, int> lines;  // We use the unordered_map, key is the slope of the line and value is the count of points
    int find_sub_max_points(vector<Point> &points, int last) {
        lines.clear();  // Clear the hash table
        int num_same_x = 0;  // count the number of points with same x-coordinate of Point[last]
        int num_same_point = 1;
        const int x0 = points[last].x;
        const int y0 = points[last].y;
        int x,y,tmp;
        int res = 0;
        for (int i = last-1; i >= 0; i--) {
            x = points[i].x;
            y = points[i].y;
            if (x == x0) {
                if (y == y0) num_same_point++;
                else if(++num_same_x > res)
                    res = num_same_x;
            }
            else {
                tmp = int( (y-y0) * EPSILON ) / (x-x0);
                if (++lines[tmp] > res) res = lines[tmp];
                /* the line above is equal to following lines
                std::unordered_map<int,int>::const_iterator got = lines.find (tmp);
                if (got == lines.end())
                    lines[tmp] = 1;
                else
                    lines[tmp]++;
                if (lines[tmp]>res) res = lines[tmp];
                */
            }
        }
        return res+num_same_point; // Point[last] itself should be counted
    }
    int maxPoints(vector<Point> &points) {
        int n = points.size();
        if (n <= 2) return n;
        int res = 2;
        int tmp = 0;
        for(int i=2; i<n; i++) {
            tmp = find_sub_max_points(points, i);
            if (tmp > res) res = tmp;
        }
        return res;
    }
};

C++ Basics

In C++, we can use <unordered_map> as hash table data structure. And there is a very quick way to do the following update:

if (++lines[tmp] > res) res = lines[tmp];

if the key is not in contained in the hash table, then set the key with value 1; otherwise, add 1 to the key’s value. This update can be done in one line since the HashTable[key] is set to 0 if the key does not exist.