[209. Minimum Size Subarray Sum] (알고리즘)
Sliding Window
문제
Given an array of positive integers nums
and a positive integer target
, return the minimal length of a
subarray
whose sum is greater than or equal to target
. If there is no such subarray, return 0
instead.
Example 1:
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.
Example 2:
Input: target = 4, nums = [1,4,4] Output: 1
Example 3:
Input: target = 11, nums = [1,1,1,1,1,1,1,1] Output: 0
Constraints:
1 <= target <= 109
1 <= nums.length <= 105
1 <= nums[i] <= 104
Follow up: If you have figured out the O(n)
solution, try coding another solution of which the time complexity is O(n log(n))
.
스스로 고민
접근법
- 배열에서 요소들의 합을 조합해서 타겟 넘버와 같거나 크면된다.
- 요소의 합에 대한 제한은 없다
- 만약 답이 없다면 리턴은 0
- 답이 있다면 몇 개가 조합 되었는지 리턴하면 된다.
- 조합은 가장 적은 숫자의 갯수로 이루어져야한다.
아는 패턴
- Slide Window 를 활용하는 방법이 잘 떠오르지도 않고 감도 안잡혔다.
- 기존에 했던 투 포인터와 해시맵을 이용해서 문제를 먼저 풀어보고 답이 나온다면 Slide Window 패턴을 활용할 방법이 생각날거라고 생각해서 일단 풀어봤다.
의사코드
- 우선 배열을 정렬해준다. <- (문제를 틀리고 나서 보니 정렬해주면 안된다..😑)
- 1차적으로 타겟 넘버가 배열에 있는지 확인한다. 있으면 1을 리턴
- 배열에 숫자가 한 개만 있으며 타겟넘버와 같다면 1개 리턴
- 비어있는 배열일 경우 0 리턴
- 해시맵과 반복문으로 2개의 조합만으로 답이 있는지 찾는다.
- 반복문이 끝났다면 해시맵이 완성되어 있으므로 해당 해시맵으로 3개 조합 4개 조합 이런식으로 찾는다.
- 예를 들어 만들어진 해시맵의 가장 작은 값과 가장 큰 값의 합은 어차피 타겟 값을 못 넘고 문제 자체가 어차피 같거나 크기만 하면 되니까 가장 큰 수를 순서대로 더해주면 된다.
구현 코드
class Solution:
def minSubArrayLen(self, target: int, nums: List[int]) -> int:
nums.sort()
if target in nums:
return 1
if sum(nums) == target:
return len(nums)
if len(nums) < 2:
return 0
num_dict = {}
for i, num in enumerate(nums):
complement = target - num
if num >= complement or complement in num_dict:
return 2
num_dict[i] = num
last_key = list(num_dict.keys())[-1]
new_target = num_dict[last_key-1] + num_dict[last_key]
count = 3
for i in range(last_key-2, 0, -1):
if new_target + num_dict[i] >= target:
return count
else:
count +=1
new_target += num_dict[i]
return 0
- 틀렸다..
- 문제 내에서 연속된 말이 따로 없어서 정렬부터 하고 문제를 풀었는데 뭔가 이상했다.
[12,28,83,4,25,26,25,2,25,25,25,12]
, target = 213 일 경우 답이 8로 나왔다 답은 83, 28, 26, 25, 25, 25, 25 이렇게 만 합하면 213을 넘으니 7개가 답인거 같은데 …- 문제에 파란색 글씨로
subarray
가 있어서 설마…하고 클릭해 보니A subarray is a contiguous non-empty sequence of elements within an array.
라고 나온다 😑
- 문제에 파란색 글씨로
- 처음 문제를 봤을 때 Window Slide 패턴을 어떻게 적용해야 될 지 감이 안잡혔는데 그래도 틀린 덕분에 어떻게 패턴을 적용해야될지 감이 잡혔다.
class Solution:
def minSubArrayLen(self, target: int, nums: List[int]) -> int:
for i in range(len(nums)):
for r in range(len(nums)):
if sum(nums[r:r+i+1]) >= target:
return i+1
return 0
- 시간 초과됨
class Solution:
def minSubArrayLen(self, target: int, nums: List[int]) -> int:
total = 0
count = 1
length = len(nums)
while total != length + 1:
for i in range(length+1):
if i + count > length:
pass
else:
if sum(nums[i:i + count]) >= target:
return count
if count == length:
count = 1
count += 1
total += 1
return 0
- 답은 정확히 나오는거 같은데 계속 시간초과가 나온다.
- while 이랑 for를 각 각 한 번씩 사용해서 O(n^2) 인줄 알았는데 sum 연산도 들어가면 O(n^3) 이 될 수 있다고 한다.
- 반복문을 어떻게 줄일 수 있는지 곰곰히 생각해봤다.
최종 의사코드
sum 에대해서 매번 실행되는걸 줄일라면 window slide를 넓히거나 좁힐 때만 값을 변경해 주는 식으로 해줘야함
- 배열의 길이 선언
- 처음 시작표시 변수 선언
- 뽑아낸 배열의 최소 크기
- 현재 뽑아낸 배열 원소의 합
- 반복문을 통해 오른쪽 값을 하나 씩 합산
- 합산할 때 마다 타겟 값을 넘는지 체크
- 타겟 값보다 현재의 합산 값이 더 많다면 while 문 실행
- 연속된 배열에서 필요없는 부분들을 쏙 빼야되니까 이제 왼쪽부터 하나씩 제거
- 이전보다 더 작은 최소배열크기를 반환
- 마지막에 최소배열크기가 n+1 (나올 수 없는 값)이면 0으로 반환 아니면 업데이트된 최소 배열크기 반환
최종 구현코드
class Solution:
def minSubArrayLen(self, target: int, nums: List[int]) -> int:
n = len(nums)
left = 0
length = n + 1
current_sum = 0
for right in range(n):
current_sum += nums[right]
while current_sum >= target:
length = min(length, right - left + 1)
current_sum -= nums[left]
left += 1
return length if length != n + 1 else 0
시간 복잡도와 공간 복잡도
-
시간 복잡도: 주어진 배열의 길이를
n
이라고 할 때, 코드는 한 번의 배열 순회를 수행하며, 각 위치에서 최대 한 번의 연산을 수행한다. 따라서 시간 복잡도는 O(n) 이다. 내부의 while 루프가 한 번에 최대 n번 돌기 때문에 연산이 총 O(n)번 실행된다. -
공간 복잡도: 추가적인 공간을 사용하지 않고 주어진 배열 내에서만 변수를 조작하므로, 공간 복잡도는 O(1) 다. 입력 배열 외에는 상수 개의 변수만 사용하므로 추가적인 공간을 할당하지 않는다.
회고 과정
- 우선 문제를 정확히 보는게 제일 중요한거 같다. 근데 한국어로 쓰여 있어도 문제가 너무 길면 잘 파악이 안된다.
- 그래서 그냥 무지성으로 풀어보고 틀릴 때 마다 고치면서 교집합을 만들어서 해결하는 방법도 좋은 거 같다.
다른 사람 코드를 보고 그 기준으로 어떤 부분을 개선 할 수 있는지 최종 회고
class Solution:
def minSubArrayLen(self, target: int, nums: List[int]) -> int:
if sum(nums) < target: return 0
s, l, count = 0, 0, len(nums)
for r, val in enumerate(nums):
s += val
while s >= target:
s -= nums[l]
count = min(count, r - l + 1)
l += 1
return count
- 정말 신기하게도 다른 코드들 모두 논리는 비슷하게 풀어내는거 같다. 다만 다른 사람 코드를 보면서 같은 논리여도 어떻게 쓰느냐에 따라 가독성이 많이 달라지는거 같다.
- 가독성, 소흘히 하지 말자
댓글남기기