→ Problems

[Algorithm] LeetCode - Container With Most Water

Swift librarian 2025. 5. 8. 18:32

📠 문제

  • Container With Most Water
  • 난이도: Medium
  • 주어진 배열에서 양 끝점으로 한 가장 큰 직사각형의 넓이를 구하는 문제
Input: height = [1,8,6,2,5,4,8,3,7]
Output: 49
Explanation: The above vertical lines are represented by array [1,8,6,2,5,4,8,3,7]. In this case, the max area of water (blue section) the container can contain is 49.

💡 풀이

첫 번째로 가장 무식하게 모든 경우의 수를 체크하는 방법이 있다. 물론 n의 입력값이 10^5이기 때문에 시간초과!

func maxArea(_ height: [Int]) -> Int {
    let count = height.count
    var result = 0
    
    for start in 0..<count {
        for end in (start..<count) {
            let area = (end - start) * min(height[start], height[end])
            result = max(result, area)
        }
    }

    return result
}

어떻게 효율적으로 접근할 수 있을까? 🤩 이 문제가 정말 재밌었던 게 조금만 생각해 보면 쉬운 방법이 나온다. 조건 하나만 추가해 주면 문제가 풀린다.

바로바로 아래의 조건 한줄을 루프에 추가해 주면 된다. 그 이유는 만약에 양끝에서 시작해서 height[start]height[end]보다 적다면 종료해 주면 된다. 그때가 바로 최대일 수밖에 없다. start의 높이는 고정이 되어있기 때문에 height[start]의 높이를 사용해서 넓이를 쟀을 때가 최대이다.

if height[start] < height[end] { break }

최종 코드는 아래와 같다.

func maxArea(_ height: [Int]) -> Int {
    let count = height.count
    var result = 0
    
    for start in 0..<count {
        for end in (start..<count).reversed() {
            let area = (end - start) * min(height[start], height[end])
            result = max(result, area)
            if height[start] < height[end] { break }
        }
    }

    return result
}

🔨 풀이 개선

하지만 아쉬웠던 건 92ms로 풀긴 했지만 Beats 5.19%... 그리고 위의 풀이는 결국 O(n^2)의 시간복잡도이다. 결국 이것보다 훨씬 효율적인 방법이 있다는 뜻이다. 바로바로 투포인터 알고리즘! left, right부터 시작해서 높이가 더 높은곳으로 나아가는 방식으로 코드를 작성하면 O(n)의 시간복잡도로 풀 수 있다.

func maxArea(_ height: [Int]) -> Int {
    var left = 0
    var right = height.count - 1
    var result = 0

    while left < right {
        let area = (right - left) * min(height[left], height[right])
        result = max(result, area)

        if height[left] < height[right] {
            left += 1
        } else {
            right -= 1
        }
    }

    return result
}

오케이... 3ms로 약 30배 빠르게 개선했다. 하지만 내앞에 계신 60%분들도 제치고 싶다...!

O(n)보다 개선하는 것은 조건을 추가해주지 않는 이상 불가능하다. 우선 불필요한 연산을 줄이고자 max함수를 쓰지 않고 아래와 같이 코드를 작성했더니 바로 개선이 되었다.

func maxArea(_ height: [Int]) -> Int {
    var left = 0
    var right = height.count - 1
    var result = 0

    while left < right {
        let area = (right - left) * min(height[left], height[right])
        if area > result { result = area } // max 함수를 쓰지 않음

        if height[left] < height[right] {
            left += 1
        } else {
            right -= 1
        }
    }

    return result
}

0ms... 굳...👍 계산상 ♾️배 개선했다.