[LeetCode-739] 백지 재작성 - '확인'과 '꺼내기'는 같은 줄에 있으면 안 된다
첫 풀이 글에 이어지는 백지 재작성 검증 글이다. LC496, LC503, LC901까지 몬로토닉 스택 문제를 네 개 풀고도 매번 힌트가 필요해서, 감독관 판정으로 이미 푼 문제(739)를 완전히 지우고 다시 짜보는 검증에 들어갔다.
- 문제 링크: https://leetcode.com/problems/daily-temperatures/
- 파일 경로:
src/main/java/coding_test/스택_큐/LC0739_DailyTemperatures.java - 난이도: Medium
문제 설명
일별 기온 배열이 주어지면, 각 날짜마다 더 따뜻한 날이 오기까지 며칠을 기다려야 하는지 배열로 반환한다. (자세한 설명은 첫 풀이 글 참고)
시행착오
기존 코드를 완전히 지우고 백지에서 다시 시작했다. 이번엔 첫 풀이 때와는 다른 지점에서 두 번 막혔다.
1. while 조건 안에서 pop()을 직접 호출함.
1
while (!stack.isEmpty() && temperatures[stack.pop()] < temperatures[i]) {
조건을 평가하는 그 순간 pop()이 실행돼버려서, 조건이 거짓으로 판정되더라도 이미 스택에서 원소가 사라진 뒤였다. “아직 확인만 하는 단계”와 “확인했으니 진짜로 꺼내는 단계”가 한 줄에 섞여 있어서, 조건에 안 맞는 원소까지 스택에서 통째로 날아갔다. stack.peek()으로 먼저 들여다보기만 하고, 조건이 참일 때만 stack.pop()을 부르도록 분리해서 고쳤다.
2. 정답을 채울 위치를 별도 카운터(idx)로 관리함.
1
2
3
int count = i - stack.pop();
ans[idx] = count;
idx++;
stack.pop()으로 꺼낸 값이 “몇 번째 날의 정답을 채워야 하는지” 그 자체인데, 정작 배열에 쓸 때는 idx라는 별개의 카운터(그냥 1씩 증가)를 썼다. i=5에서 pop()으로 인덱스 4를 꺼냈는데, 그 시점에 idx가 우연히 2여서 ans[4]가 아니라 ans[2]에 엉뚱하게 답이 들어갔다. pop()으로 꺼낸 값을 변수에 담아 그 자체를 배열 인덱스로 쓰도록 고쳤다.
이 두 가지 다 힌트를 받고서야 고칠 수 있었다.
최종 코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public int[] dailyTemperatures(int[] temperatures) {
Deque<Integer> stack = new ArrayDeque<>();
int[] ans = new int[temperatures.length];
for (int i = 0; i < temperatures.length; i++) {
while (!stack.isEmpty() && temperatures[stack.peek()] < temperatures[i]) {
int poppedIdx = stack.pop();
ans[poppedIdx] = i - poppedIdx;
}
stack.push(i);
}
return ans;
}
오늘 배운 내용
“확인만 하는 것”과 “확인 후 실제로 상태를 바꾸는 것”은 항상 분리된 두 단계여야 한다. while 조건 안에 pop()을 직접 넣으면, 조건 평가 자체가 상태를 바꿔버려서 “확인했는데 조건에 안 맞으면 그대로 둔다”는 게 불가능해진다. peek()으로 먼저 보고, 조건을 통과했을 때만 pop()으로 실제로 꺼내는 두 단계로 나눠야 한다.
그리고 자료구조에서 꺼낸 값 자체가 “어디에 결과를 채울지”를 말해주고 있다면, 그 값을 그대로 쓰면 된다. 별도의 카운터를 만들어서 “몇 번째로 처리됐는지”를 따로 세는 건, 처리 순서와 실제 데이터의 위치가 다를 수 있다는 걸 놓친 접근이다.
오답노트
- 틀렸던 패턴 1:
while조건 안에서stack.pop()을 직접 호출 — 조건이 거짓이어도 이미 원소가 빠져나감. - 왜 틀렸나: “확인”과 “꺼내기”를 같은 동작으로 묶어서 생각했다.
- 고친 패턴:
stack.peek()으로 먼저 확인하고, 조건이 참일 때만while본문 안에서stack.pop(). - 틀렸던 패턴 2: 정답을 채울 배열 인덱스를 별도 카운터(
idx)로 관리. - 왜 틀렸나:
pop()으로 꺼낸 값 자체가 이미 “어디에 쓸지”를 알려주고 있다는 걸 놓치고, 처리 순서를 세는 카운터와 실제 위치를 혼동했다. - 고친 패턴:
pop()한 값을 변수에 담아 그대로 배열 인덱스로 사용. - 다음에 떠올릴 시점: 스택/큐에서 값을 꺼내는 코드를 짤 때, “이 꺼낸 값 자체가 다음에 쓸 정보를 이미 담고 있는가”부터 확인한다. 별도 카운터를 새로 만들기 전에, 이미 손에 들고 있는 값으로 충분한지 먼저 점검한다.
감독관 메모
백지 재작성인데도 힌트 없이는 못 풀었다. 다만 첫 풀이 때 막혔던 지점(값/인덱스 혼동으로 인한 Map 설계)과는 다른 지점(조건 안에서 부작용을 일으키는 pop, 카운터와 실제 인덱스 혼동)에서 막혔다는 게 의미가 있다 — 완전히 같은 실수를 반복한 게 아니라 매번 새로운 디테일에서 걸리고 있다는 뜻이다. 이건 “이해를 못 하고 있다”보다는 “코드를 짤 때 확인해야 할 체크리스트가 아직 자동화되지 않았다”에 가깝다. 다음 세션에서 739를 한 번 더 백지로 재작성해서, 오늘 걸렸던 두 지점(조건 안 부작용, 카운터 대신 실제 값 사용)이 이번엔 자동으로 나오는지 확인한다.