보통 c++ 로 짠다면, l과 r이 두개의 인덱스를 저장하고 있고, while 루프 안에서 조건에 따라서 l또는 r이 갱신되는 방식으로 구현할 것이다. 또는 인덱스 대신 포인터를 저장하는 식으로 짤 수도 있을것이다. 아무튼 이게 직관적이다
하지만 python에서는 배열을 인덱스로 접근하는 것이 효율성에서 좋지 못하다. 그러다보니 다른 방식을 시도하게 되는데, 예를 들면 한쪽포인터를 움직이는 것을 for 루프로 잡고, 그 안에서 다른쪽 포인터를 움직이는 while루프를 만드는 것이다. 파이썬 특성에 따르면 이게 좀더 효율적이기는 할텐데, 문제는 알고리즘이 한눈에 바로 들어오지 않는다는 것이다
우선 배열의 인덱스 접근을 피하는 방법은, iterator를 만들어서 next()로 움직이는 방법이 있다. 이 방법은 보기에도 깔끔하다. 이 방법을 사용하자.
그다음에.. while True 루프 안에서 l또는 r을 움직일것인가, 이중 루프를 쓸것인가인데.. 우선 코드 예시를 들어보자. 합이 N인 부분수열의 갯수를 구하는 코드이다
while True:
try:
if num_sum == N:
answer += 1
if num_sum <= N:
num_sum += next(right_iter)
else:
num_sum -= next(left_iter)
except StopIteration:
break
for num in right_iter:
num_sum += num
while num_sum > N:
num_sum -= next(left_iter)
if num_sum == N:
answer += 1
전자가 코드가 좀더 길긴 하지만, 대칭적이라서 좀더 알아보기 쉬운 느낌이다. 전자의 형식으로 코딩하는걸로 결정..
전자의 방식으로 단일 루프로 이터레이터를 갖고서 이터레이션을 돌리는 경우, 종료조건을 정하는 것이 조금 헷갈릴수 있다.
양쪽 끝에서 출발하는 투 포인터의 경우, 두 포인터가 만날때까지 돌려야 한다. 이경우에는 그냥 반복 횟수가 n-1이 될때까지 반복하면 된다.
한쪽 끝에서 출발하는 투 포인터의 경우, 오른쪽 포인터가 끝까지 갈때까지 돌려야 한다.
while True로 반복시키고 그냥 try-except에로 StopIteration 예외를 잡아서 빠져나올수도 있지만, 코드가 덜 예쁘다. while right is not None 으로 반복시키고, right=next(r_iter, None) 로 기본값을 넣어서 next를 호출하는 방식으로 하자.
-
(TODO) 생각해보니.. 리스트와 두개의 인덱스 또는 두개의 이터레이터를 갖고 다니는 방법 대신에 그냥 데이터를 deque에 저장하고 양쪽에서 pop해버리면서 처리하는것도 가능할것 같다. 코드는 더 깔끔해질거 같은데 속도가 얼마나 떨어지려나.. 한번 테스트해보자.