Description

給定一組二維平面上的點 points,找出最多有多少個點在同一條直線上。

Example:

Input: points = [[1,1],[2,2],[3,3]] Output: 3

Intuition#

核心思路:固定一個點,用斜率分組其他點,找出同斜率最多的點數。

  • 兩點確定一條直線,三個以上的點共線意味著任兩點間的斜率相同
  • 固定一個點 i,計算它與其他所有點的斜率,用 HashMap 計數
  • 斜率用分數(約分後的 dy/dx)表示,避免浮點數精度問題
  • 需要特別處理垂直線(dx=0)和水平線(dy=0)

Approaches#

1: 暴力三重迴圈#

  • 概念: 枚舉所有三點組合,檢查是否共線
  • 時間複雜度: O(n^3) - 三重迴圈
  • 空間複雜度: O(1)
class Solution {
    fun maxPoints(points: Array<IntArray>): Int {
        val n = points.size
        if (n <= 2) return n
        var result = 2
        for (i in 0 until n) {
            for (j in i + 1 until n) {
                var count = 2
                for (k in j + 1 until n) {
                    // 用叉積判斷共線:(y2-y1)(x3-x1) == (y3-y1)(x2-x1)
                    val cross = (points[j][1] - points[i][1]).toLong() *
                                (points[k][0] - points[i][0]) -
                                (points[k][1] - points[i][1]).toLong() *
                                (points[j][0] - points[i][0])
                    if (cross == 0L) count++
                }
                result = maxOf(result, count)
            }
        }
        return result
    }
}

⭐2: HashMap 斜率分組#

  • 概念: 固定每個點,用約分後的斜率作為 key 分組,找出最大共線數
  • 時間複雜度: O(n^2) - 兩層迴圈
  • 空間複雜度: O(n) - HashMap
class Solution {
    fun maxPoints(points: Array<IntArray>): Int {
        val n = points.size
        if (n <= 2) return n
        var result = 2

        for (i in 0 until n) {
            val slopeMap = HashMap<String, Int>()
            for (j in i + 1 until n) {
                var dy = points[j][1] - points[i][1]
                var dx = points[j][0] - points[i][0]

                // 約分
                val g = gcd(Math.abs(dy), Math.abs(dx))
                if (g != 0) {
                    dy /= g
                    dx /= g
                }

                // 統一符號:讓 dx 為正,或 dx=0 時讓 dy 為正
                if (dx < 0) {
                    dx = -dx
                    dy = -dy
                } else if (dx == 0) {
                    dy = Math.abs(dy)
                }

                val key = "$dy/$dx"
                slopeMap[key] = (slopeMap[key] ?: 0) + 1
            }

            for (count in slopeMap.values) {
                result = maxOf(result, count + 1) // +1 包含點 i 自己
            }
        }
        return result
    }

    private fun gcd(a: Int, b: Int): Int {
        return if (b == 0) a else gcd(b, a % b)
    }
}

🔑 Takeaways#

  • Pattern: 固定一個元素 + HashMap 分組是處理「共性」問題的常見手法
  • 關鍵技巧: 用約分後的分數表示斜率避免浮點精度問題;用叉積判斷共線也是常用替代方案