티스토리 뷰
이제 시작이다.. 여기서 부터는 알면 알수록 많은 풀이법을 생각 해낼 수 있고, 시뮬레이션이나 시간 복잡도를 개선하는 방식들이 있기 때문에 알고리즘 문제를 푸는데 실질적으로 도움이 많이 될 형태들이다. 가보자고.
스택 (=Stack)
: 데이터를 후입 선출 구조 (LIFO : Last In, First Out 구조)이며 가장 최근에 들어온 데이터가 가장 먼저 빠진다는 개념이다. 실생활 예시로는 설거지한 컵들을 정리할 때, 가장 위에 있는 컵이 가장 먼저 사용될 것이고, 마지막 맨 아래의 컵은 마지막에 사용된다고 생각하면 좋을 것 같다.
- 스택에 저장된 데이터들은 선형 구조(데이터 간의 관계가 1대1 관계)의 특성을 띄고있다
- 스택에 데이터를 삽입하거나 스택에서 데이터를 빼낼 수 있다
스택을 프로그램에서 사용하기 위해서는 자료구조와 연산이 필요하다.
자료구조
: 자료를 선형으로 저장할 저장소
- 배열을 사용할 수 있다
- 저장소 자체를 스택이라고도 한다
- 스택에서 가장 마지막에 삽입된 데이터의 위치를 'top' 이라 부른다
연산
- 삽입 : 저장소에 데이터를 저장한다. 보통 push라고 불리며 파이썬 list에서는 append() 함수를 사용한다.
- 삭제 : 저장소에서 데이터를 꺼낸다. 꺼낸 데이터는 삽입한 자료의 역순으로 꺼내며 보통 pop이라고 불리며 함수역시 pop() 을 사용한다.
- 스택 내부가 비어있는지 확인하는 연산은 isEmpty. 파이썬에서는 len(stack)으로 내부 데이터 유무를 확인할 수 있다.
- 스택의 top에 있는 데이터를 반환하는 연산은 peak. 파이썬에서는 pop()을 사용하면 자동적으로 peak를 연산한다.
파이썬 형식으로 바꿔서 나타내 보겠다.
파이썬으로 구현하면 아래와 같다. 파이썬은 이보다 더 간결하게 가능하지만 굳이 전개한 이유는, 곧 나올 DP, DFS 때문에 큰 틀을 연습해두는 것이 좋다고 생각한다. 다음 시간만 되어도 무슨 뜻인지 이해할 수 있다.
굉장히 지저분해졌는데, 아래의 코드가 일반적으로 파이썬에서 사용하는 코드라고 생각된다.
원래 Stack에서 고려해야할 사항이 있다. 앞서 한번 설명 했는데, 스택의 크기를 변경하기 어렵다라는 단점은 파이썬에서는 동적 list를 사용하기 때문에 크게 상관없다!
Stack의 응용
처음 배운다면 스택을 어떻게 써야하는지 감이 안잡힐텐데 굉장히 유용하게 사용할 수 있다. 대표 유형인 괄호검사를 통해 알아보도록 하자. 백준에 괄호만 검색해도 다양한 난이도로 접할 수 있다. https://www.acmicpc.net/problem/9012가 가장 일반적인 유형이라고 생각한다.
기본 조건을 먼저 알고가보자. 괄호의 종류는 총 3가지로
[ ] : 대괄호
{ } : 중괄호
( ) : 소괄호 가 있으며
우선순위는 기본적으로 소괄호( ) - 중괄호{ } - 대괄호[ ] 순이다
문제는 주로 괄호를 사용했을때 문제가 있는 계산식인지 아닌지를 판단하는 것이다.
예를들어 (a(b), a{b(c[d]e}f) 등 괄호가 완전한 짝을 이루지 않거나, 괄호끼리 닫히는 순서가 알맞지 않을 경우 등이 있다. 이러한 경우에 stack을 사용하여 열린 괄호가 나온다면 스택에 담아두었다가 짝이 맞는 닫는 괄호가 나올때를 판단하여 문제를 해결할 수 있다.
- 문자열에 있는 괄호를 차례대로 조사하면서 왼쪽 괄호를 만나면 Stack에 삽입하고, 오른쪽 괄호를 만나면 Stack에서 'top부분=stack[-1]' 을 삭제 및 꺼낸 후 오른쪽 괄호와 짝이 맞는지 검사한다.
- 이때 빼려한 스택이 비어있다면(왼쪽 짝이 없다면) 조건에 위배되고 다른 짝이 나오거나 방향이 다르다면 또한 조건에 위배된다.
- 마지막까지 문자열을 조사 했는데, 모두 사라졌어야 할 Stack에 데이터가 남아있다면 짝이 맞지 않는다는 의미로 조건에 위배된다.
더 나아가 중위 계산식에서 후위 계산식으로 변환 및 계산할 때도 사용할 수 있다.
갑자기 중위 계산식 후위 계산식이 왜 튀어나오냐? 그것이 공부니까..
- 중위 표기법 (infix notation) : 연산자를 피연산자 가운데 표기하는 방법
- 후위 표기법 (postfix notation) : 연산자를 피 연산자 뒤에 표기하는 방법
흔히 우리가 아는 표기법은 중위 표기법이다. 근데 굳이 이걸 왜 하는가 싶지만, 컴퓨터는 천재라서 연산자를 피연산자 뒤에 표기한다. 진지하게는 컴퓨터 계산기는 피연산자와 연산자로 나뉘어 계산을 하기 때문이다. 놀랍지만 전위 표기법도 있지만 크게 다루진 않았다.
사실 ' + ', ' - ', ' * ', ' / ' 만 있으면 쉽겠지만, 여기서부터는 괄호도 출연하게 된다.
텍스트로 단계를 보여주자면
ex ) A * B - C / D
이번엔 다른 예시로 코드를 살펴보자
위의 식 말고도 여러가지 방법이 있다. 예를들어 우선순위를 적용한 알고리즘으로 operation에 대한 정보를 딕셔너리에 우선순위로 저장하고 연산자가 나올때 stack에 쌓여있는 연산을 쏟아내는 방식이 있다.
- 입력 받은 중위 표기식에서 데이터를 순차적으로 읽는다.
- 데이터가 피연산자면 데이터를 바로 출력(저장)한다
- 데이터가 연산자(괄호포함)일 때, 해당 데이터가 스택에 저장되어있는 top 연산자보다 우선순위가 높으면 스택에 push. 그렇지 않다면 top의 연산자가 우선순위가 높은 데이터의 우선순위보다 작아질 때까지 스택에서 pop한 후(+출력저장)에 데이터의 연산자를 스택에 push한다. 만약 스택에 데이터가 없다면 push한다.
- 데이터가 오른쪽 괄호 ' ) '이면 스택에 top 부분에 왼쪽 괄호 ' ( '가 올 때까지 스택에 pop 연산을 실행하고, 출력 저장한다. 왼쪽 괄호를 만난다면 pop만 하고 출력은 진행하지 않는다.
- 중위 표기식의 끝까지 도달했다면 종료한다.
- 스택에 남아있는 연산자를 모두 pop하고 출력저장을 한다.
딕셔너리에 계산식을 넣는 방법인 lambda 식을 적용하여 코드의 길이를 완전히 줄여버리는 case
사실 lambda는 아직 제대로 다룰 줄 몰라서 정말 간단한 것 아닌 이상은 자주 쓰진 않는다. 실제로 시간복잡도에는 크게 상관이 없다고 알고있고, 쉬운 식이 아닌 이상 직관적이진 않아서 자주 쓰진 않다보니 더 안쓰게 되는거 같다.
후위 계산식으로 바꾸었다면 해당 연산을 진행도 해봐야 하지 않겠는가? 다행히 중위에서 후위를 바꾸는 것보다 훨씬 간단하다.
이번에는 피연산자를 스택에 넣고 연산자를 만나게 된다면 피연산자 데이터 2개를 꺼내어 연산 후에 다시 스택에 넣어준다. 여기서 주의할 점은 pop을 두번 진행할 때, 데이터의 순서에 따라 출력이 달라질 수 있음에 주의하자. 예를들어 a-b 혹은 a/b 해야하는 연산에서 b-a 와 b/a로 잘못 계산되어 틀리는 경우가 종종있다. 나도 백준풀 때 그랬다.
여기까지는 아직 stack을 이용한 순한맛 응용이다. 다음 시간에 배울 Backtracking, 부분집합, 순열 등이 있는데.. 벌써 한숨나온다. 아자아자 화이자...
'일상코딩 > 노트' 카테고리의 다른 글
Python : 깊이 우선 탐색(Depth First Search, DFS) (1) (0) | 2024.02.12 |
---|---|
Python : 재귀호출, Memoization, Dynamic Programming (0) | 2024.02.11 |
Python : 완전 탐색과 그리디 알고리즘 (Baby-gin) (0) | 2024.02.01 |
Python : 1차원 배열 (정렬[Bubble Sort, Counting Sort]) (0) | 2024.01.30 |
Python Functions(2) [재귀함수, 유용한 내장함수] (0) | 2024.01.27 |
- Total
- Today
- Yesterday
- baby-gin
- vue
- 삼성청년SW아카데미
- SQLite
- views.py
- ChatGPT
- HTML
- dfs
- 연산자
- app
- Sequence types
- basic syntax
- 재귀
- Component
- honeymoney
- Python
- JavaScript
- refactoring
- 카운팅정렬
- Authentication System
- ssafy
- CodeTree
- SQL
- 함수
- Method
- 백준
- 순열
- vue3
- Django
- Database
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 |