본문 바로가기
Algorithm/리트코드(LeetCode)

[LeetCode] Minimum Size Subarray Sum

by 컴돈AI 2024. 4. 10.

 

 

목차

    문제

    • 연속된 배열에서 특정 구간의 합이 target보다 커지는 구간 중 구간 길이가 최소인 길이를 구하는 문제입니다.
    • 예시
      • Input: target = 7, nums = [2,3,1,2,4,3]
        Output: 2
        Explanation: The subarray [4,3] has the minimal length under the problem constraint.
      • Input: target = 4, nums = [1,4,4]
        Output: 1
      • Input: target = 11, nums = [1,1,1,1,1,1,1,1]
        Output: 0

    문제 풀이

    풀이 1 : 완전탐색

    • 풀이
      • 단순하게 모든 i~j 까지에 대해서 Sum을 계산하는 방식입니다. 
      • i, j ,sum함수 -> n^3의 시간복잡도를 가집니다.
    • 코드
      • class Solution:
            def minSubArrayLen(self, target: int, nums: List[int]) -> int:
                answer = float('inf')
                for i in range(len(nums)):
                    for j in range(i,len(nums)):
                        sum_value = 0
                        for k in range(i,j+1):
                            sum_value+=nums[k]
                        if sum_value>=target:
                            answer=min(answer,j-i+1)
                            break
                if answer==float('inf'):
                    return 0
                else:
                    return answer
    • 시간복잡도
      • O(n^3)

    풀이 2 : 개선된 완전탐색

    • 풀이
      • 위 문제에서 sum 계산을 없애주기 위하여 누적합을 이용합니다.
      • sum 계산이 제거되어 O(n^2)이 됩니다.
    • 코드
      • class Solution:
            def minSubArrayLen(self, target: int, nums: List[int]) -> int:
                answer = float('inf')
        
                sum_arr = [0 for _ in range(len(nums)+1)]
                value = 0
                for i in range(1,len(nums)+1):
                    value+=nums[i-1]
                    sum_arr[i]=value
                for i in range(len(nums)):
                    for j in range(i+1,len(nums)+1):
                        sum_value = sum_arr[j]-sum_arr[i]
                        if sum_value>=target:
                            answer=min(answer,j-i)
                            break
                if answer==float('inf'):
                    return 0
                else:
                    return answer
    • 시간복잡도
      • O(n^2)

    풀이 3 : 이분탐색

    • 풀이
      • 위 코드에서 for j in range(i,len(nums)) 부분을 이분탐색으로 변경해 주면 O(n^2)을 O(nlogn)의 시간복잡도로 줄일 수 있습니다.
    • 코드
      • from bisect import bisect_left
        
        class Solution:
            def minSubArrayLen(self, target: int, nums: List[int]) -> int:
                answer = float('inf')
        
                sum_arr = [0 for _ in range(len(nums)+1)]
                value = 0
                for i in range(1,len(nums)+1):
                    value+=nums[i-1]
                    sum_arr[i]=value
        
                for i in range(len(nums)):
                    # target <= sum_arr[j]-sum_arr[i]여야합니다.
                    # 따라서 target+sum_arr[i]가 sum_arr[j]보다 작아야합니다.
                    # 즉 bisect_left 결과 target+sum_arr[i]가 sum_arr의 j번재 인덱스에 존재해야합니다.
                    left_idx = bisect_left(sum_arr,target+sum_arr[i])
                    if left_idx<len(sum_arr):
                        answer = min(answer,left_idx-i)
        
                if answer==float('inf'):
                    return 0
                else:
                    return answer
    • 시간복잡도
      • O(nlogn)

    풀이 4 : 투 포인터 사용

    • 풀이
      • 오른쪽 값을 하나씩 증가해 나가면서 값을 증가하다가 만약 target 값보다 그 값이 커지면 더해졌던 left값을 하나씩 제거해 봅니다.(합이 target보다 작아질 때까지) 이런 식으로 쭈욱 체크해 나가면 O(2n)만에 해결이 가능합니다.
    • 코드
      • class Solution:
            def minSubArrayLen(self, target: int, nums: List[int]) -> int:
                left = 0
                answer = float('inf')
                sum_value = 0
                for i in range(len(nums)):
                    sum_value +=nums[i]
                    while sum_value>=target:
                        answer = min(answer,i-left+1)
                        sum_value-=nums[left]
                        left+=1
                if answer!=float("inf"):
                    return answer
                else:
                    return 0
    • 시간복잡도
      • O(N)

    알게 된 내용

    • 특정 구간에 대한 합을 구할 때(sum함수) 매번 합을 구하는 과정을 누적합을 이용하여 그 값들의 차를 이용하면 O(N)으로 계산되던 시간복잡도를 O(1)로 줄일 수 있습니다.
    • 특정 조건을 만족하는 부분을 찾기 위해서 for문을 사용할 때 for문 대신 이분탐색을 사용하면 O(N)으로 탐색하던 과정을 O(logN)만에 탐색이 가능해집니다. (이분탐색은 항상 단조증가나 단조감소처럼 규칙이 있을 때만 사용하여 시간을 줄일 수 있습니다.)
    • 인접한 sub-array를 찾는 문제는 sliding window (투포인터)를 사용하여 접근해 볼 수 있습니다.

    출처

    'Algorithm > 리트코드(LeetCode)' 카테고리의 다른 글

    [LeetCode] Pascal's Triangle II  (0) 2024.04.15
    [LeetCode] Rotate Array  (0) 2024.04.15
    [LeetCode] Two Sum II  (0) 2024.04.10
    [LeetCode] Implement strStr()  (0) 2024.04.10
    [LeetCode] Add Binary  (0) 2024.04.08