알고리즘

[알고리즘] 투 포인터와 슬라이딩 윈도우 - 파이썬(python)

mine* 2023. 4. 6. 22:33

이번에는 투 포인터와 슬라이딩 윈도우 알고리즘에 대해서 알아보자

 

투 포인터 알고리즘과 슬라이딩 윈도우 알고리즘은 매우 비슷하다.

두 알고리즘 모두 두 개의 포인터를 가지고 있고 포인터가 지나온 곳은 삭제하고 나아간 곳은 추가하는 방식을 가지고 있어 배열에서 원하는 값을 찾을 때 유용하게 사용된다.

 

다만, 투 포인터는 두 개의 포인터가 유동적으로 움직일 수 있지만, 슬라이딩 윈도우는 두 개의 포인터가 하나의 윈도우를 구성하고 있어 포인터 사이의 구간이 일정하게 고정된채로 움직인다.

그림으로 표현하면 이런 느낌이다.

투 포인터

간단한 투 포인터 예시를 살펴보자

# n개의 자연수 중에서 연속된 숫자의 합이 m이 되는 경우의 수는 몇가지일까?

n,m = 10, 5
arr = [1, 2, 3, 4, 2, 5, 3, 1, 1, 2]
cnt,sum = 0, 0
st, ed = 0, 0

while 1:
    if sum >= m or st == n:  # 합이 m보다 크거나 같다면 범위를 좁히기 
        sum -= arr[ed]       # (다음으로 넘어가기 위해 값이 같은 경우도 범위를 좁힌다.)
        ed += 1              # st==n인 경우 st포인터는 끝에 도달했지만 ed포인터도 끝에 도달해야 종료된다.
    elif sum < m:           # 합이 m보다 작다면 범위를 넓히면서 더하기
        sum += arr[st]
        st += 1
    if sum == m:
        cnt += 1
    if ed == n: break
print(cnt)

이렇게 2개의 포인터 st, ed를 가지고 포인터를 움직이고 합을 더하고 빼주는 것이다.

아직 이해가 부족한 것 같다면 실제 문제를 한 번 풀어보자

 

1940번: 주몽

첫째 줄에는 재료의 개수 N(1 ≤ N ≤ 15,000)이 주어진다. 그리고 두 번째 줄에는 갑옷을 만드는데 필요한 수 M(1 ≤ M ≤ 10,000,000) 주어진다. 그리고 마지막으로 셋째 줄에는 N개의 재료들이 가진 고

www.acmicpc.net

백준에서 투 포인터로 분류되는 문제이다.

 

문제는 다음과 같다.

주몽은 철기군을 양성하기 위한 프로젝트에 나섰다. 그래서 야철대장을 통해 철기군이 입을 갑옷을 만들게 하였다. 야철대장은 주몽의 명에 따르기 위하여 연구에 착수하던 중 아래와 같은 사실을 발견하게 되었다.

갑옷을 만드는 재료들은 각각 고유한 번호를 가지고 있다. 갑옷은 두 개의 재료로 만드는데 두 재료의 고유한 번호를 합쳐서 M(1 ≤ M ≤ 10,000,000)이 되면 갑옷이 만들어 지게 된다. 야철대장은 자신이 만들고 있는 재료를 가지고 갑옷을 몇 개나 만들 수 있는지 궁금해졌다. 이러한 궁금증을 풀어 주기 위하여 N(1 ≤ N ≤ 15,000) 개의 재료와 M이 주어졌을 때 몇 개의 갑옷을 만들 수 있는지를 구하는 프로그램을 작성하시오.

 

이 문제는 재료를 두 개 선택하여 합이 M이 되면 갑옷이 만들어지고 총 몇 개의 갑옷을 만들 수 있는지 출력해야 한다. 앞선 예시가 두 개의 포인터 모두 0부터 시작했다면 이번 문제는 정렬된 리스트의 양 끝에 포인터가 위치 하고 있다.

n=int(input())  # 갑옷 재료 개수
m=int(input())  # 갑옷하나 만드는데 필요한 합
lst=list(map(int, input().split())) # 갑옷 재료 번호 입력
# 크기 비교에서 정렬을 사용하면 편리하다.
lst.sort()
st=0
ed=n-1
cnt=0

while st<ed:
    # 시작과 끝의 합이 크다면 합을 감소시켜야 하므로 ed-1
    if lst[st]+lst[ed]>m:
        ed=ed-1
    # 작다면 합을 증가시켜야 하므로 st+1
    elif lst[st]+lst[ed]<m:
        st=st+1
    # 두 개의 합이 m이라면 cnt+1하고 다음으로 넘어가기 위해 st+1
    elif lst[st]+lst[ed]==m:
        cnt+=1
        st+=1
        ed-=1
print(cnt)

투 포인터는 위의 문제들처럼 사용되는 것이 일반적이다.

 

슬라이딩 윈도우

그렇다면 이번에는 슬라이딩 윈도우 알고리즘에 대해 알아보자

슬라이딩 윈도우는 구간합을 구하는 문제에서  자주 사용되는데 직접 한 번 풀어보자

 

2559번: 수열

첫째 줄에는 두 개의 정수 N과 K가 한 개의 공백을 사이에 두고 순서대로 주어진다. 첫 번째 정수 N은 온도를 측정한 전체 날짜의 수이다. N은 2 이상 100,000 이하이다. 두 번째 정수 K는 합을 구하기

www.acmicpc.net

이 문제는 가장 기본적인 슬라이딩 윈도우 문제이다.

 

문제는 다음과 같다.

매일 아침 9시에 학교에서 측정한 온도가 어떤 정수의 수열로 주어졌을 때, 연속적인 며칠 동안의 온도의 합이 가장 큰 값을 알아보고자 한다.

예를 들어, 아래와 같이 10일 간의 온도가 주어졌을 때,

3 -2 -4 -9 0 3 7 13 8 -3

모든 연속적인 이틀간의 온도의 합은 아래와 같다.

이때, 온도의 합이 가장 큰 값은 21이다.

또 다른 예로 위와 같은 온도가 주어졌을 때, 모든 연속적인 5일 간의 온도의 합은 아래와 같으며,

이때, 온도의 합이 가장 큰 값은 31이다.

매일 측정한 온도가 정수의 수열로 주어졌을 때, 연속적인 며칠 동안의 온도의 합이 가장 큰 값을 계산하는 프로그램을 작성하시오.

 

이 문제는 정해진 N개의 리스트에서  연속으로 K개를 더했을 때 최대값을 출력해야 한다.

# N:온도측정한 날짜수 K: 연속적 날짜수
N,K=map(int,input().split())
lst=list(map(int, input().split()))
# 첫 K개의 구간합 구하기
summ=sum(lst[:K])
# 최대값은 첫 구간합
mx=summ
# K개의 묶음의 개수만큼 for문 실행하기
for j in range(N-K):
    summ-=lst[j]
    summ+=lst[K+j]
    mx = max(summ, mx)

print(mx)

슬라이딩 윈도우는 위의 코드에서 보이듯이 매번 구간합을 새로 구하는 것이 아니라 윈도우가 움직이면서 연산하기 때문에 매번 구간합을 구하는 것에 비해 훨씬 짧은 시간으로 해결할 수 있다.

 

지금까지 투 포인터와 슬라이딩 윈도우 문제에 대해서 알아보았다. 두 알고리즘은 비슷하지만 투 포인터는 포인터 간의 간격이 자유로운 반면, 슬라이딩 윈도우는 간격이 일정하다는 것을 알아두자.