LeetCode발행일 2025. 2. 23.원본 https://blog.naver.com/jword_/223770598515 ↗

배열 최대, 최소 탐색 (one-way search)

배열 최대, 최소 탐색 (one-way search) — #LeetCode #개발자의도구들 only 파이썬 목표 참고 : 여기 배열의 one-way search [a, b, c, d, e, f, g....

#LeetCode#Naver Blog

#LeetCode #개발자의도구들

​

​

  • only 파이썬
  • 목표 참고 : 여기

배열의 one-way search

Best time to buy and sell stock

\[a, b, c, d, e, f, g. ..\] 에서 (최대 - 최소)의 최고값을 찾는다.

  • one direction으로만 이동이 가능한 상황
  • 단, 최대의 idx는 항상 최소의 idx보다 커야한다.
javascript 코드 예제
                                    🤔 단순히 O(n²)으로  풀 수 있다.

>>> 하지만 입력값이 10,000이라서 사용하기에는 무리다.

❓❔ 더 효율적인 방법은 무엇인가...

O(n²) 알고리즘

javascript 코드 예제
                                    class Solution(object):
    def maxProfit(self, prices):
        """
        :type prices: List[int]
        :rtype: int
        """
        profit = -99
        for i in range(len(prices)):
            buy = prices[i]
            for j in range(i + 1, len(prices)):
                sell = prices[j]

                if sell - buy >= 0:
                    profit = max(profit, sell - buy)

        if profit < 0:
            profit = 0
        return profit
  • classic한 O(n²)알고리즘이다.
  • 당연하지만 시간초과가 생긴다.

O(nlogn)으로 만들어보자

O(n²)을 O(nlogn)으로 만드려면 1. 정렬, 2. biary search이다. 근데 둘다 적용해서 문제를 푸는게 쉽지가 않다..

javascript 코드 예제
                                    # 정렬 ?
[7,1,5,3,6,4] 최소 값 idx 순서대로 정렬

# idx
[1, 3, 5, 2, 4, 0]
이렇게 되면 1에는 최소 0에는 최대가 들어있다.

>>> 문제 특성상 최소 값 보다 작은 최대 값 idx를 고를 수 없다.
>>> 그럼 이번 문제는 1, 4가 선택되고 결과는 최소 idx = 1, 최대 idx = 4가 되어 정답이 5이다.

❓🤔 O(nlogn)인가?
>>> case 1 [5, 4, 3, 2, 1] 정렬되어 있다고 하면, 최솟값 5를 고르고 끝에서 전체 탐색
그 다음 최솟값 4를 고르고 끝에서 전체 탐색이라서 최종 탐색 시간은 n(n-1)이 된다.

>>> 여전히 최악의 경우 시간 복잡도는 O(n²)을 벗어나지 못한다...

❓ 해당 케이스만 예외적으로 처리하면?
>>> 생각해보면 해당 케이스는 문제에서 요구되는 -1이 return되는 경우다.
>>> 이 경우는 항상 이전 날의 값이 다음날 값 보다 작다는 특징을 가지고 있다.
>>> 이를 사전에 미리 체크해서 따로 처리한다면 O(n) 속도로 처리가 가능하다.

🤔 그럼에도 벗어날 수 없다.
[max, max-1, max -2, max -3 ....., ,..... min] 형태에서 중간 어떤 부분에서 두 값이 바뀌는
경우가 존재한다.

모든 배열은 항상 앞의 idx가 크다가, 어느 한 순간 앞이 뒤보다 작은 경우가 생길 수 있다.
[...  2, 3  ....]
하지만 이 경우에도 앞에서 뒤로 계속해서 탐색이 이루어져야 하기 때문에 여전히 O(n²)이다.

>>> 그럼에도 여전히 O(n²)

O(n)으로 해결 가능하다.

with chat gpt-o1

너무 O(nlogn)에 집찹해서 그런지 좀 더 단순한 알고리즘을 생각해내지 못했다. 생각해보면 O(n)으로 충분히 가능하다.

javascript 코드 예제
                                    0. min_price = 가격의 최댓값으로 설정, profit = -1
1. 배열 순회를 시작한다.
>>> price가 min_price보다 작다 -> min_price를 갱신한다.
>>> 크다 profit을 갱신한다.
javascript 코드 예제
                                    class Solution(object):
    def maxProfit(self, prices):
        """
        :type prices: List[int]
        :rtype: int
        """
        min_price = 10001
        profit = -1
        for i in range(len(prices)):
            if prices[i] < min_price:
                min_price = prices[i]
                continue
            else:
                profit = max(profit, prices[i] - min_price)

        if profit < 0:
            profit = 0
        return profit
  • 최솟값이 결정되는 위치에서 더 이상 이전 값들을 고려하지 않아도 된다.
  • 최소는 항상 해당 배열에서의 최솟값으로 결정된다.
  • 아무리 계산해봐도 최소값 보다 큰 경우가 정답의 최솟값으로 결정되는 일이 없다.
  • 나는 이부분을 간과했다. 최솟값이 변할줄 알았다...
  • 최댓값은 이전을 고려하지 않기 때문에(one-way) 항상 차후에 나오는 최대값이 최대값이 된다.

6배 빠른 코드

  • 그냥 참고만 ... 깊게 x
javascript 코드 예제
                                    class Solution(object):
    def maxProfit(self, prices):
        """
        :type prices: List[int]
        :rtype: int
        """
        price_min = prices[0]
        price_max = 0
        profit = 0
        for price in prices:
            if price < price_min:
                price_min = price
                price_max = price
            elif price > price_max:
                price_max = price
                profit = max(price_max - price_min, profit)
        return profit
  • 분명 같은 로직인데 이 코드가 6배나 더 빠르다.
  • 이유가 무엇일까?
  • 1. for i in range vs for price in prices
  • i생성 이후 인덱스 접근 < iterator에서 price 요소 바로 꺼내오기