[10828] 스택
백준 Silver IV | 10828 | Python | 문제 링크
문제 설명
정수를 저장하는 스택을 구현한 다음, 입력으로 주어지는 명령을 처리하는 프로그램을 작성하시오.
명령은 총 다섯 가지이다.
push X: 정수 X를 스택에 넣는 연산이다.pop: 스택에서 가장 위에 있는 정수를 빼고, 그 수를 출력한다. 만약 스택에 들어있는 수가 없는 경우에는 -1을 출력한다.size: 스택에 들어있는 정수의 개수를 출력한다.empty: 스택이 비어있으면 1, 아니면 0을 출력한다.top: 스택의 가장 위에 있는 정수를 출력한다. 만약 스택에 들어있는 수가 없는 경우에는 -1을 출력한다.
입력
첫째 줄에 주어지는 명령의 수 N (1 ≤ N ≤ 10,000)이 주어진다. 둘째 줄부터 N개의 줄에는 명령이 하나씩 주어진다. 주어지는 정수는 1보다 크거나 같고, 100,000보다 작거나 같다.
출력
출력이 있는 명령에 대해서 출력을 한다.
입출력 예
| 입력 | 출력 |
|---|---|
| 14 push 1 push 2 top size empty pop pop pop push 10 top empty pop size empty |
2 2 0 2 1 -1 10 0 10 1 0 |
나의 풀이
풀이 1 - 첫 번째 제출과 버그 수정
import sys
input = sys.stdin.readline
n = input()
stack = []
for line in sys.stdin:
command = []
command = line.split()
if command[0] == "push":
stack.append(command[1])
elif command[0] == "pop":
stack.pop() if stack else print(-1)
elif command[0] == "size":
print(len(stack))
elif command[0] == "empty":
print(0 if stack else 1)
elif command[0] == "top":
print(stack[-1] if stack else -1)
제출하고 채점 결과를 기다리면서 이미 맞은 것처럼 생각하고 있었다. 스택 비어있을 때 -1 출력하는 것도 처리했고, 명령어 파싱도 됐으니까. 그런데 틀렸다가 떴다.
처음엔 이해가 안 됐다. 어디서 틀렸는지 바로 보이지 않았다. 코드를 다시 한 줄씩 읽었다.
버그 1 — pop이 아무것도 출력하지 않았다
stack.pop() if stack else print(-1)
이 줄이 문제였다. 스택이 비어있으면 -1을 출력하는데, 값이 있으면 꺼내기만 하고 출력을 하지 않는다. pop은 꺼낸 값을 반환하는데, 반환값을 print로 감싸지 않았다.
고치는 건 간단했다. print()로 전체를 감싸면 된다.
print(stack.pop() if stack else -1)
이 두 줄이 얼마나 달라 보이지 않는지가 당황스러웠다. stack.pop() if stack else print(-1)과 print(stack.pop() if stack else -1). 읽으면서 같은 동작을 한다고 착각했던 것 같다.
pop 버그를 찾고 나서 코드를 다시 처음부터 읽었다. 틀렸다는 게 확인된 이상, 다른 곳에도 비슷한 실수가 있을 것 같았다.
버그 2 — n을 int로 변환하지 않았다
n = input()
sys.stdin.readline으로 읽으면 문자열 "10\n" 같은 형태가 들어온다. 이걸 int()로 변환하지 않으면 n은 숫자가 아니라 문자열이다. 이 문제에서는 n을 반복문이나 산술 연산에 사용하지 않아서 실제 오답으로 이어지지는 않았다. 하지만 n을 range(n)이나 비교 연산에 쓰는 순간 TypeError가 발생한다.
버그 3 — command = []가 아무 의미가 없었다
command = []
command = line.split()
빈 리스트로 초기화하고 바로 다음 줄에서 line.split() 결과로 덮어씌운다. 초기화가 의미 없는 코드이다. 파이썬에서는 그냥 바로 대입하면 된다.
버그 4 — push할 때 정수로 변환하지 않았다
stack.append(command[1])
command[1]은 문자열 "5" 같은 형태이다. 이 문제에서는 스택에 넣은 값을 그대로 출력만 하기 때문에 문자열이어도 정답으로 처리된다. 하지만 스택에 넣은 값을 더하거나 비교하는 문제에서는 바로 오답이 난다.
빈 스택 예외처리도 반드시 해야 한다. pop()과 top에서 스택이 비어 있을 때 예외처리를 빠뜨리면 IndexError가 나서 런타임 에러로 처리된다.
stack.pop() if stack else -1
stack[-1] if stack else -1
처음에는 이것만 신경 써도 충분하다고 생각했는데, 알고 보니 출력을 빠뜨리는 더 기본적인 실수를 하고 있었다.
풀이 2 - 최종 제출 코드
import sys
input = sys.stdin.readline
n = int(input())
stack = []
for line in sys.stdin:
command = line.split()
if command[0] == "push":
stack.append(int(command[1]))
elif command[0] == "pop":
print(stack.pop() if stack else -1)
elif command[0] == "size":
print(len(stack))
elif command[0] == "empty":
print(0 if stack else 1)
elif command[0] == "top":
print(stack[-1] if stack else -1)
풀이 3 - 결과를 모아서 한 번에 출력
import sys
input = sys.stdin.readline
n = int(input())
stack = []
result = []
for _ in range(n):
command = input().split()
if command[0] == "push":
stack.append(int(command[1]))
elif command[0] == "pop":
result.append(stack.pop() if stack else -1)
elif command[0] == "size":
result.append(len(stack))
elif command[0] == "empty":
result.append(0 if stack else 1)
elif command[0] == "top":
result.append(stack[-1] if stack else -1)
print('\n'.join(map(str, result)))
풀이 2와 차이가 두 가지 있다.
하나는 for line in sys.stdin 대신 for _ in range(n)을 쓴다. 읽어들인 n을 실제로 활용하는 방식이다.
다른 하나는 출력을 매번 print()로 하는 대신 result 리스트에 모아두고, 마지막에 '\n'.join(map(str, result))로 한 번에 출력한다. print()를 반복 호출하면 그만큼 I/O가 발생하는데, 한 번에 출력하면 그 횟수를 줄일 수 있다. 입력 데이터가 많은 문제에서 차이가 난다.