[Stack/Queue/Recursive Function] 꼭 필요한 자료구조 기초
카테고리: Algorithm
1. 꼭 필요한 자료구조 기초
탐색이란 많은 양의 데이터 중에서 원하는 데이터를 찾는 과정
을 의미한다. DSF와 BFS를 제대로 이해하려면 기본 자료구조인 스택과 큐에 대한 이해가 전제되어야 하므로 사전 학습이 필요하다.
자료구조란 데이터를 표현하고 관리하고 처리하기 위한 구조
를 의미한다. 그 중 스택과 큐는 자료구조의 기초 개념으로 다음의 두 핵심적인 함수로 구성된다.
- 삽입(push) : 데이터를 삽입한다.
- 삭제(pop) : 데이터를 삭제한다.
스택과 큐를 사용할 때는 삽입과 삭제 이외에도 오버플로와 언더플로를 고민해야 한다.
오버플로는 특정한 자료구조가 수용할 수 있는 데이터의 크기를 이미 가득 찬 상태에서 삽입연산을 수행할 때 발생한다.
언더플로는 데이터가 전혀 들어 있지 않은 상태에서 삭제 연산을 수행할 때 발생한다.
스택
스택(stack)은 박스 쌓기에 비유할 수 있다. 박스를 쌓을 때에는 위에서부터 차곡차곡 쌓지만 박스를 치울때는 위에서부터 하나씩 빼야한다. 이러한 구조를 선입후출(First In Last Out)
또는 후입선출(Last In First Out)
구조라고 한다.
한번 스택의 과정을 예로 들어보자!
삽입(5) - 삽입(2) - 삽입(3) - 삽입(7) - 삭제() - 삽입(1) - 삽입(4) - 삭제()
step 0 초기 단계
step 1 삽입(5) - 삽입(2) - 삽입(3) - 삽입(7) - 삭제() - 삽입(1) - 삽입(4) - 삭제()
5
step 2 삽입(5) - 삽입(2) - 삽입(3) - 삽입(7) - 삭제() - 삽입(1) - 삽입(4) - 삭제()
5 2
step 3 삽입(5) - 삽입(2) - 삽입(3) - 삽입(7) - 삭제() - 삽입(1) - 삽입(4) - 삭제()
5 2 3
step 4 삽입(5) - 삽입(2) - 삽입(3) - 삽입(7) - 삭제() - 삽입(1) - 삽입(4) - 삭제()
5 2 3 7
step 5 삽입(5) - 삽입(2) - 삽입(3) - 삽입(7) - 삭제() - 삽입(1) - 삽입(4) - 삭제()
5 2 3
step 6 삽입(5) - 삽입(2) - 삽입(3) - 삽입(7) - 삭제() - 삽입(1) - 삽입(4) - 삭제()
5 2 3 1
step 7 삽입(5) - 삽입(2) - 삽입(3) - 삽입(7) - 삭제() - 삽입(1) - 삽입(4) - 삭제()
5 2 3 1 4
step 8 삽입(5) - 삽입(2) - 삽입(3) - 삽입(7) - 삭제() - 삽입(1) - 삽입(4) - 삭제()
5 2 3 1
이를 파이썬 코드로 표현하면 다음과 같다.
stack = []
# 삽입(5) - 삽입(2) - 삽입(3) - 삽입(7) - 삭제() - 삽입(1) - 삽입(4) - 삭제()
stack.append(5)
stack.append(2)
stack.append(3)
stack.append(7)
stack.pop()
stack.append(1)
stack.append(4)
stack.pop()
print(stack) # 최하단 원소부터 출력
print(stack[::-1]) # 최상단 원소부터 출력
별도의 라이브러리 필요없이 append()와 pop() 함수를 이용하면 스택 자료구조를 구현할 수 있다.
append()는 리스트 가장 뒤쪽에 데이터를 삽입하는 것이고, pop()은 리스트의 가장 뒤쪽에서 데이터를 꺼내는 것이다.
큐
큐(Queue)는 대기 줄에 비유할 수 있다. 예를 들어 놀이동산에서 줄을 설 경우 먼저 온 사람이 먼저들어가고, 나중에 온 사람은 나중에 들어온다. 이와 같은 구조를 선입선출(First In First Out)
라고 한다.
한번 큐의 과정을 예로 들어보자.
삽입(5) - 삽입(2) - 삽입(3) - 삽입(7) - 삭제() - 삽입(1) - 삽입(4) - 삭제()
step 0 초기 단계
step 1 삽입(5) - 삽입(2) - 삽입(3) - 삽입(7) - 삭제() - 삽입(1) - 삽입(4) - 삭제()
5
step 2 삽입(5) - 삽입(2) - 삽입(3) - 삽입(7) - 삭제() - 삽입(1) - 삽입(4) - 삭제()
2 5
step 3 삽입(5) - 삽입(2) - 삽입(3) - 삽입(7) - 삭제() - 삽입(1) - 삽입(4) - 삭제()
3 2 5
step 4 삽입(5) - 삽입(2) - 삽입(3) - 삽입(7) - 삭제() - 삽입(1) - 삽입(4) - 삭제()
7 3 2 5
step 5 삽입(5) - 삽입(2) - 삽입(3) - 삽입(7) - 삭제() - 삽입(1) - 삽입(4) - 삭제()
7 3 2
step 6 삽입(5) - 삽입(2) - 삽입(3) - 삽입(7) - 삭제() - 삽입(1) - 삽입(4) - 삭제()
1 7 3 2
step 7 삽입(5) - 삽입(2) - 삽입(3) - 삽입(7) - 삭제() - 삽입(1) - 삽입(4) - 삭제()
4 1 7 3 2
step 8 삽입(5) - 삽입(2) - 삽입(3) - 삽입(7) - 삭제() - 삽입(1) - 삽입(4) - 삭제()
4 1 7 3
이를 파이썬 코드로 표현하면 아래와 같다.
from collections import deque
# Queue 구현을 위해 deque 라이브러리 사용
queue = deque()
# 삽입(5) - 삽입(2) - 삽입(3) - 삽입(7) - 삭제() - 삽입(1) - 삽입(4) - 삭제()
queue.append(5)
queue.append(2)
queue.append(3)
queue.append(7)
queue.popleft()
queue.append(1)
queue.append(4)
queue.popleft()
print(queue) # 먼저 들어온 순서대로 출력
queue.reverse() # 다음 출력을 위해 역순으로 바꾸기
print(queue) # 나중에 들어온 원소부터 출력
collections 모듈에서 제공하는 deque는 스택과 큐의 장점을 모두 채택한 것인데 데이터를 넣고 빼는 속도가 리스트 자료형에 비해서 효율적이며 queue 라이브러리를 이용하는 것보다 더 간단하다. 대부분의 코딩테스트에서 기본 라이브러리 사용 허용
재귀 함수
DFS와 BFS를 구현하려면 재귀 함수도 이해하고 있어야 한다. 재귀 함수(Recursive Function)란 자기 자신을 다시 호출하는 함수
를 의미한다.
def recursive_function():
print("재귀 함수를 호출합니다.")
recursive_function()
recursive_function()
재귀 함수의 종료 조건
재귀 함수를 문제 풀이에서 사용할 때는 무한 호출을 방지하기 위해서 재귀 함수가 언제 끝날지. 종료 조건을 꼭 명시해야 한다.
def recursive_function(i):
# 100번째 출력했을 때 종료되도록 종료 조건 명시
if i == 100:
return
print(i, '번째 재귀 함수에서', i + 1, '번째 재귀 함수를 호출합니다.')
recursive_function(i + 1)
print(i, '번째 재귀 함수를 종료합니다.')
recursive_function(1)
컴퓨터 내부에서 재귀 함수의 수행은 스택 자료구조를 이용한다. 그 이유는 함수를 계속 호출했을 때 가장 마지막에 호출한 함수가 먼저 수행을 끝내야 그 앞의 함수 호출이 종료되기 때문이다.
재귀함수는 내부적으로 스택 자료구조와 동일하다.
재귀 함수를 이용하는 대표적인 예제로는 팩토리얼(Factorial)문제가 있다. 아래 코드는 반복적으로 구현한 방식과 재귀적으로 구현한 두 방식이다.
# 반복적으로 구현한 n!
def factorial_iterative(n):
result = 1
# 1부터 n까지의 수를 차례대로 곱하기
for i in range(1, n + 1):
result *= i
return result
# 재귀적으로 구현한 n!
def factorial_recursive(n):
if n <= 1: # n이 1 이하인 경우 1을 반환
return 1
# n! = n * (n - 1)!를 그대로 코드로 작성하기
return n * factorial_recursive(n - 1)
반복적으로 구현하는 방법보다 재귀적으로 구현했을 때 얻을 수 있는 장점은 무엇일까?
코드의 간결성
간결해진 이유는 재귀 함수가 수학의 점화식을 그대로 소스코드로 옮겼기 때문이다. 추후, 다이나믹 프로그래밍
으로 이어진다.
🐢 현재 공부하고 있는 이것이 취업을 위한 코딩 테스트다 with 파이썬 - 나동빈 저자
의 책을 학습하며 기록 및 정리를 하기위한 내용들입니다. 🐢
감사합니다.😊
댓글남기기