FILO(First In Last Out) 형태의 자료구조
stack = []
# 삽입(5) - 삽입(2) - 삽입(3) - 삽입(7) - 삭제() - 삽입(1) - 삽입(4) - 삭제()
stack.append(5) # push
stack.append(2) # push
stack.append(3) # push
stack.append(7) # push
stack.pop() # pop - 7 삭제
stack.append(1) # push
stack.append(4) # push
stack.pop() # pop - 4 삭제
print(stack) # 최하단 원소부터 출력
print(stack[::-1]) # 최상단 원소부터 출력
[5, 2, 3, 1].
[1, 3, 2, 5]
FIFO = First In First Out
from collections import deque
# 큐(Queue) 구현을 위해 deque 라이브러리 사용
queue = deque()
# 삽입(5) - 삽입(2) - 삽입(3) - 삽입(7) - 삭제() - 삽입(1) - 삽입(4) - 삭제()
queue.append(5) # enqueue
queue.append(2) # enqueue
queue.append(3) # enqueue
queue.append(7) # enqueue
queue.popleft() # dequeue - 5 삭제
queue.append(1) # enqueue
queue.append(4) # enqueue
queue.popleft() # dequeue - 7 삭제
print(queue) # 먼저 들어온 순서대로 출력
queue.reverse() # 다음 출력을 위해 역순으로 바꾸기
print(queue) # 나중에 들어온 원소부터 출력
deque([3, 7, 1, 4])
deque([4, 1, 7, 3])
자기 자신을 다시 호출하는 함수
def recursive_function(i):
# 5번째 호출을 했을 때 종료되도록 종료 조건 명시
if i == 5:
return
print(i, '번째 재귀함수에서', i + 1, '번째 재귀함수를 호출합니다.')
recursive_function(i + 1)
print(i, '번째 재귀함수를 종료합니다.')
recursive_function(1)
1 번째 재귀함수에서 2 번째 재귀함수를 호출합니다.
2 번째 재귀함수에서 3 번째 재귀함수를 호출합니다.
3 번째 재귀함수에서 4 번째 재귀함수를 호출합니다.
4 번째 재귀함수에서 5 번째 재귀함수를 호출합니다.
4 번째 재귀함수를 종료합니다.
3 번째 재귀함수를 종료합니다.
2 번째 재귀함수를 종료합니다.
1 번째 재귀함수를 종료합니다.
# 반복적으로 구현한 n!
def factorial_iterative(n):
result = 1
# 1부터 n까지의 수를 차례대로 곱하기
for i in range(1, n + 1):
result *= i
return result
print('반복적으로 구현:', factorial_iterative(5))
# 재귀적으로 구현한 n!
def factorial_recursive(n):
if n <= 1: # n이 1 이하인 경우 1을 반환
return 1
# n! = n * (n - 1)!를 그대로 코드로 작성하기
return n * factorial_recursive(n - 1)
print('재귀적으로 구현:', factorial_recursive(5))
반복적으로 구현: 120
재귀적으로 구현: 120
유클리드 호제법이란 두 자연수에 대한 최대 공약수를 구하는 방법
1. 두 자연수 A, B에 대하여 A를 B로 나눈 나머지를 R이라고 한다. (A > B)
2. 이때 A와 B의 최대 공약수는 B와 R의 최대 공약수와 같다.
GCD(192, 162)
1st 192 % 162 = 30
2nd 162 % 30 = 12
3rd 30 % 12 = 6
4th 12 % 6 = 0
따라서 6이 192와 162의 최대 공약수이다.
def gcd(a, b):
# a > b이여야 하므로
if a <= b:
c = a
a = b
b = c
# 나머지가 0이면 b가 최대 공약수이다.
if a % b == 0:
return b
# b > a % b이므로
else:
return gcd(b, a % b)
print(gcd(192, 162))
6
모든 재귀 함수는 반복문을 이용하여 동일하게 구현할 수 있다.
재귀 함수와 반복문 중 어떤 것이 유리한지 판단 해야한다.
DFS는 깊이 우선 탐색으로 깊은 부분을 우선적으로 탐색하는 알고리즘이다.
DFS는 스택 자료구조(혹은 재귀 함수)를 이용한다.
# DFS 함수 정의
def dfs(graph, v, visited):
# 현재 노드를 방문 처리
visited[v] = True
print(v, end=' ')
# 현재 노드와 연결된 다른 노드를 재귀적으로 방문
for i in graph[v]:
# 방문하지 않은 노드를 계속해서 탐색 -> 깊이 우선 탐색
if not visited[i]:
dfs(graph, i, visited)
# 인접 리스트 (2차원 리스트)
graph = [
[], # node 0
[2, 3, 8], # node 1
[1, 7], # node 2
[1, 4, 5], # node 3
[3, 5], # node 4
[3, 4], # node 5
[7], # node 6
[2, 6, 8], # node 7
[1, 7] # node 8
]
# 방문 리스트 (1차원 리스트)
visited = [False] * 9
# 정의된 DFS 함수 호출 - 1번 노드부터 탐색 시작
dfs(graph, 1, visited)
1 2 7 6 8 3 4 5
BFS는 너비 우선 탐색으로 그래프에서 가까운 노드부터 우선적으로 탐색한다.
BFS는 큐 자료구조를 이용한다.
from collections import deque
# BFS 함수 정의
def bfs(graph, start, visited):
# 큐(Queue) 구현을 위해 deque 라이브러리 사용
queue = deque([start])
# 현재 노드를 방문 처리
visited[start] = True
# 큐가 빌 때까지 반복
while queue:
# 큐에서 하나의 원소를 뽑아 출력
v = queue.popleft()
print(v, end=' ')
# 해당 원소와 연결된, 아직 방문하지 않은 원소들을 큐에 삽입
for i in graph[v]:
if not visited[i]:
queue.append(i)
visited[i] = True
# 인접 리스트 (2차원 리스트)
graph = [
[], # node 0
[2, 3, 8], # node 1
[1, 7], # node 2
[1, 4, 5], # node 3
[3, 5], # node 4
[3, 4], # node 5
[7], # node 6
[2, 6, 8], # node 7
[1, 7] # node 8
]
# 방문 리스트 (1차원 리스트)
visited = [False] * 9
# 정의된 BFS 함수 호출 - 1번 노드부터 탐색 시작
bfs(graph, 1, visited)
1 2 3 8 7 4 5 6
BFS : 0 1 2 3 4 5 6
DFS : 0 1 3 4 2 5 6
N X M 크기의 얼음 틀이 있다.
구멍이 뚫려 있는 부분은 0, 칸막이가 존재하는 부분은 1로 표시된다.
구멍이 뚫려 있는 부분끼리는 상하좌우로 붙어 있는 경우 서로 연결되었다고 한다.
이때, 얼음 틀의 모양이 주어졌을 때 생성되는 총 아이스크림의 개수를 구하라.
(1 <= N, M <= 1,000)
풀이)
모든 0인 점에 대해서 상하좌우로 연결되어 있는 모든 점을 탐색 후
개수를 하나 증가시키고 이를 모든 노드를 탐색할 때까지 반복한다.
아래의 모법 답안에서는 DFS로 문제를 해결하였다.
그 이유는 DFS가 구현하기에 BFS보다 쉽기 때문이다.
# N, M을 공백을 기준으로 구분하여 입력 받기
n, m = map(int, input().split())
# 2차원 리스트의 맵 정보 입력 받기
graph = []
for _ in range(n):
graph.append(list(map(int, input())))
# DFS로 특정한 노드를 방문한 뒤에 연결된 모든 노드들도 방문
def dfs(x, y):
# 주어진 범위를 벗어나는 경우에는 즉시 종료
if x <= -1 or x >= n or y <= -1 or y >= m:
return False
# 현재 노드를 아직 방문하지 않았다면
if graph[x][y] == 0:
# 해당 노드 방문 처리 -> 1은 갈 수 없는 공간이므로
graph[x][y] = 1
# 상, 하, 좌, 우의 위치들도 모두 재귀적으로 호출
dfs(x, y - 1) # 상
dfs(x, y + 1) # 하
dfs(x - 1, y) # 좌
dfs(x + 1, y) #우
# 갈 수 있는 최대한으로 탐색 후 True 반환
return True
return False
# 모든 노드(위치)에 대하여 음료수 채우기
result = 0
for i in range(n):
for j in range(m):
# 현재 위치에서 DFS 수행
# True가 반환되었다는 것은 최대한의 탐색이 끝났다는 의미
if dfs(i, j) == True:
result += 1
print(result) # 정답 출력
3 3
001
010
101
3
상하좌우로 연결된 노드만 탐색하면 되는 문제이므로 BFS로도 구현이 가능하다.
from collections import deque
# N, M을 공백을 기준으로 구분하여 입력 받기
n, m = map(int, input().split())
# 2차원 리스트의 맵 정보 입력 받기
graph = []
for _ in range(n):
graph.append(list(map(int, input())))
def bfs(x, y):
# bfs를 위한 queue 생성
queue = deque()
# enqueue
queue.append((x, y))
# 큐가 빌 때까지 반복하기
while queue:
# dequeue
x, y = queue.popleft()
# 주어진 범위를 벗어나는 경우에는 생략
if x <= -1 or x >= n or y <= -1 or y >= m:
continue
# 현재 노드를 아직 방문하지 않았다면
if graph[x][y] == 0:
# 방문 처리
graph[x][y] = 1
# 상, 하, 좌, 우에 대하여 모든 노드 enqueue
queue.append((x, y - 1)) # 상
queue.append((x, y + 1)) # 하
queue.append((x - 1, y)) # 좌
queue.append((x + 1, y)) # 우
result = 0
# 모든 노드에 대하여
for i in range(n):
for j in range(m):
# 방문하지 않은 노드의 경우에만 bfs 실행 후 개수 증가
if graph[i][j] == 0:
bfs(i, j)
result += 1
print(result) # 정답 출력
3 3
001
010
101
3
N X M 크기의 직사각형 형태의 미로에 갇혔다.
플레이어의 위치는 (1, 1)이며 미로의 출구는 (N, M)이며, 한 번에 한 칸씩 이동한다.
미로는 갈 수 없는 부분은 0으로 갈 수 있는 부분은 1로 표시되어있다.
이때 플레이어가 탈출하기 위해서 움직여야 하는 최소 칸 수를 구하라.
시작 칸과 마지막 칸은 항상 1이며,
시작 칸과 마지막 칸을 모두 포함해서 개수를 계산해야 한다.
(4 <= N, M <= 200)
(풀이)
최단 거리를 구하는 문제는 BFS를 사용하면 된다!
(1, 1)에서부터 인접한 노드에 대해서 이동 횟수를 기록해주면 된다.
from collections import deque
# N, M을 공백을 기준으로 구분하여 입력 받기
n, m = map(int, input().split())
# 2차원 리스트의 맵 정보 입력 받기
graph = []
for _ in range(n):
graph.append(list(map(int, input())))
# 이동할 네 가지 방향 정의 (상, 하, 좌, 우)
dx = [0, 0, -1, 1]
dy = [-1, 1, 0, 0]
# BFS 소스코드 구현
def bfs(x, y):
# 큐(Queue) 구현을 위해 deque 라이브러리 사용
queue = deque()
# enqueue
queue.append((x, y))
# 큐가 빌 때까지 반복하기
while queue:
# dequeue
x, y = queue.popleft()
# 현재 위치에서 4가지 방향으로의 위치 확인
for i in range(4):
nx = x + dx[i]
ny = y + dy[i]
# 미로 찾기 공간을 벗어난 경우 무시
if nx < 0 or nx >= n or ny < 0 or ny >= m:
continue
# 벽인 경우 무시
if graph[nx][ny] == 0:
continue
# 해당 노드를 처음 방문하는 경우
if graph[nx][ny] == 1:
# 이전 노드의 이동거리에 1을 더해준 값을 노드 값을 지정한다.
graph[nx][ny] = graph[x][y] + 1
# enqueue
queue.append((nx, ny))
# 가장 오른쪽 아래까지의 이동거리 반환
return graph[n - 1][m - 1]
# (0, 0) 그래프로 초기화했으므로 (1, 1) 대신 (0, 0)을 대입
print(bfs(0, 0))
10
실행 후의 그래프 형태는 아래와 같다.
[3, 0, 5, 0, 7, 0].
[2, 3, 4, 5, 6, 7].
[0, 0, 0, 0, 0, 8].
[14, 13, 12, 11, 10, 9].
[15, 14, 13, 12, 11, 10]
이코테 2021 강의 3편
이것이 코딩 테스트다 교재