스택과 큐는 프로그래밍이라는 개념이 탄생할 때부터 사용된 가장 고전적인 자료구조 중 하나로 이 중에서도 스택은 거의 모든 응용 프로그램을 만들 때 사용되는 자료구조이다.
스택은 LIFO(Last In First Out)로 처리되며 접시가 쌓이는 것을 생각하면 쉽게 이해할 수 있다.
큐는 FIFO(First In First Out)로 처리되며 줄을 서서 버스에 타는 것을 생각하면 쉽게 이해할 수 있다.
파이썬은 스택, 큐에 대한 자료형을 따로 제공하지는 않지만 리스트에서 스택과 큐의 연산을 모두 제공한다. 다만 리스트는 동적 배열로 구현되어 있기 때문에 큐의 연산을 수행하는데에 있어 효율적이지는 않다. 큐를 효율적으로 사용하기 위해서는 Deque 자료형을 사용해야 한다. 그러나 굳이 성능을 고려하지 않는다면 스택과 큐 모두 리스트로 처리가 가능하다.
괄호로 된 입력값이 올바른지 판별하라.
이 문제는 우선 답을 안보고 스택을 이용해서 먼저 풀어보았다.
class Solution:
def isValid(self, s: str) -> bool:
stack=[]
s_list=list(s)
while s_list:
tmp=s_list.pop()
if tmp==')' or tmp==']' or tmp=='}':
stack.append(tmp)
else:
if not stack:
return False
if tmp=='(':
if stack.pop()==')':
continue
else:
return False
if tmp=='[':
if stack.pop()==']':
continue
else:
return False
if tmp=='{':
if stack.pop()=='}':
continue
else:
return False
if stack:
return False
return True
전형적인 스택 문제이다. (괄호를 판별하는 문제는 바로 스택으로 접근하면 된다.) 이 Solution에서는 내가 접근한 방식과 조금은 다르게 (, [, {를 스택에 푸쉬하고 ),],}를 만나면 팝한 결과를 매핑 테이블 결과와 매칭되는지 확인하는 방식으로 해결하였다.
class Solution:
def isValid(self, s: str) -> bool:
stack=[]
table={
')':'(',
']':'[',
'}':'{',
}
for char in s:
if char not in table:
stack.append(char)
elif not stack or table[char]!=stack.pop():
return False
return len(stack)==0
내가 직접 작성한 코드보다 훨씬 깔끔하다. 매핑 테이블 사용을 처음 접해봤는데 잘 사용하면 매우 유용할 것 같다. 마지막에 반환 내용 또한 stack이 비어있어야 True가 반환되는 것을 저렇게 표현하여 코드 길이가 줄었다. 실행시간 또한 훨씬 좋았다.
중복된 문자를 제외하고 사전식 순서로 나열하라.
우선 문제에 대한 이해가 필요하다. 중복을 제거한 후 사전식으로 정렬하는 것이 아니라 전체 문자열에서 중복을 제거했을 때 최대한 사전식으로 정렬되도록 만드는 문제이다. cbacdcbc를 예로 들어보자.
class Solution:
def removeDuplicateLetters(self, s: str) -> str:
for char in sorted(set(s)):
suffix=s[s.index(char):]
if set(s)==set(suffix):
return char+self.removeDuplicateLetters(suffix.replace(char, ''))
return ''
재귀함수는 확실히 눈으로만 보면 이해하기 어려운 것 같다.
스택을 활용하기 위해서 우선 Counter(s)를 통해 s에 존재하는 원소들의 갯수를 나타내는 딕셔너리를 만들고 스택으로 이용할 리스트를 하나 만들어야 한다. 그리고 현재 문자가 스택에 쌓여있는 문자이고 (이전 문자보다 앞서는 문자) 뒤에 더 존재한다면 (카운터가 0 이상인 경우) 쌓아둔 현재 문자를 꺼내서 지운다. 이는 cbacdcbc에 a가 들어올 때에 이미 c와 b가 제거되어 acdb의 형태를 띄게 된다. 카운터가 0 이상인 c와 b는 뒤에서 다시 붙일 수 있기 때문이다.
if char in seen:
continue
여기서 seen은 이미 처리된 문자 여부를 확인하기 위해 사용하였다. 이렇게 이미 처리된 문자의 경우에는 스킵하고 넘어간다.
class Solution:
def removeDuplicateLetters(self, s: str) -> str:
counter, seen, stack = collections.Counter(s), set(), []
for char in s:
counter[char]-=1
if char in seen:
continue
while stack and char<stack[-1] and counter[stack[-1]]>0:
seen.remove(stack.pop())
stack.append(char)
seen.add(char)
return ''.join(stack)
재귀 풀이 코드가 훨씬 간결하고 이쁘지만 스택 풀이가 더 빠르다.
매일의 화씨 온도 리스트 T를 입력받아서, 더 따뜻한 날씨를 위해서는 며칠을 더 기다려야 하는지를 출력하라.
예제 입력값을 우선 도식화 한다. 그러면 그래프가 하나 그려진다. 현재의 인덱스를 스택에 쌓아두고 이전보다 상승하는 지점에서 현재 온도와 스택에 쌓아둔 인덱스 지점의 온도 차이를 비교하여 더 높다면 스택의 값을 팝하고 현재 인덱스와 팝한 인덱스의 차이를 정답으로 처리한다.
while stack and cur>temperatures[stack[-1]]:
last=stack.pop()
answer[last]=i-last
stack.append(i)
73, 74, 75, 71, 69, 72, 76, 73으로 시뮬레이션을 돌려보면
이와 같은 과정을 거치게 된다.
class Solution:
def dailyTemperatures(self, temperatures: List[int]) -> List[int]:
answer=[0]*len(temperatures)
stack=[]
for i, cur in enumerate(temperatures):
while stack and cur>temperatures[stack[-1]]:
last=stack.pop()
answer[last]=i-last
stack.append(i)
return answer
To be continue ...
왜 남자들이 내 영상을 보고 xx합니까? 🔞