1. 자료구조 기초
1-1. 스택 (Stack)
- 선입후출, 입구 == 출구.
- 컴퓨터에서 함수 호출 시 실행되는 구조와 유사함
- list로 구현 가능
# 삽입(5) - 삽입(2) - 삽입(3) - 삽입(7) - 삭제() - 삽입(1) - 삽입(4) - 삭제()
stack = [] # # 스택 생성
stack.append(5) # 5
stack.append(2) # 5 2
stack.append(3) # 5 2 3
stack.append(7) # 5 2 3 7
stack.pop() # 5 2 3
stack.append(1) # 5 2 3 1
stack.append(4) # 5 2 3 1 4
stack.pop() # 5 2 3 1
print(stack) # 5 2 3 1
# 뒤집어 출력
print(stack[::-1]) # 1 3 2 5
1-2. 큐 (queue)
- FIFO(선입선출). 터널 구조
- 시간복잡도를 위해 deque(덱)을 사용하는 것이 적절함
# 삽입(5) - 삽입(2) - 삽입(3) - 삽입(7) - 삭제() - 삽입(1) - 삽입(4) - 삭제()
from collections import deque
queue = deque() # # 큐 생성
queue.append(5) # 5
queue.append(2) # 5 2
queue.append(3) # 5 2 3
queue.append(7) # 5 2 3 7
queue.popleft() # 2 3 7 # queue에서 데이터 꺼내기
queue.append(1) # 2 3 7 1
queue.append(4) # 2 3 7 1 4
queue.popleft() # 3 7 1 4 # queue에서 데이터 꺼내기
# 먼저 들어온 순서대로 출력
print(queue) # 3 7 1 4
# 나중에 들어온 원소부터 출력
queue.reverse()
print(queue) # 4 1 7 3
1-3. 재귀 알고리즘 (Recursive algorithm)
- 함수 내에서 자기 자신을 호출하는 알고리즘
- 모든 재귀 알고리즘은 반복문으로 구성이 가능함
대표적인 예 : 유클리드 호제법. 최대공약수(GCD)를 구하기 위한 방법 중 하나
1-4. DFS(Depth-First Search)
- 그래프에서 깊은 부분을 우선적으로 탐색하는 알고리즘
- 스택 자료 구조 혹은 재귀 함수를 통해 구현 가능함
- 구체적인 동작 과정
- 탐색 시작 노드를 스택에 삽입. 방문 처리
- 스택에서 노드를 꺼내 해당 노드의 인접 노드 중 방문하지 않은 인접 노드가 있으면, 그 노드를 스택에 넣고 방문 처리
- 방문하지 않은 인접 노드가 없으면 스택에서 최상단 노드를 꺼냄
- 2,3 과정을 수행할 수 없을 때까지 반복

정답 풀이
def dfs(graph, v, visited): visited[v] = True print(v, end= ' ') for i in graph[v]: # 현재 노드(graph[v])에서 연결된 노드들(i) if not visited[i]: # 방문하지 않은 곳이면 dfs(graph, i, visited) # 재귀함수 호출하여 방문하고 반복 # 각 노드 연결 정보 graph = [ [], [2,3,8], [1,7], [1,4,5], [3,5], [3,4], [7], [2,6,8], [1,7] ] # 각 노드 방문 정보 (방문O-True / 방문X - False) visited = [False] * 9 dfs(graph,1,visited) # Tip # 1. 탐색 시작 노드를 스택에 삽입하고 방문 처리한다. # 2. 스택 최상단 노드에 방문하지 않은 인접 노드가 하나라도 있으면 그 노드를 스택에 넣고 방문 처리한다. # 방문하지 않은 인접 노드가 없으면 최상단 노드 꺼낸다. # 3. 더이상 2번 과정을 수행할 수 없을 떄까지반복
1-5. BFS ( Breadth-First Search )
- 그래프에서 가까운 노드부터 우선적으로 탐색하는 알고리즘
- 큐 자료구조를 이용함
- 구체적인 동작 과정
- 탐색 시작 노드를 큐에 삽입. 방문 처리
- 큐에서 노드를 꺼내 해당 노드의 인접 노드 중 방문하지 않은 모든 인접 노드를 큐에 삽입하고 방문 처리
- 더 이상 2를 수행할 수 없을 때까지 반복

정답 풀이
덱을 통해 구현한 BFS (시간복잡도를 고려하여 deque을 사용하는 것이 좋음)
from collections import deque def bfs(graph, start, visited): # 시작점 queue에 삽입/ 방문 처리 queue = deque([start]) queue.append(start) visited[start]= True # queue가 빌 때까지 반복 while queue: # 큐에서 원소 하나 뽑아 출력 v = queue.popleft() print(v, end=' ') # 방문하지 않은 인접 노드 큐에 삽입 / 방문 처리 for i in graph[v]: if not visited[i]: queue.append(i) visited[i] = True graph = [ [], [2,3,8], [1,7], [1,4,5], [3,5], [3,4], [7], [2,6,8], [1,7] ] visited = [False] * 9 print(bfs(graph, 1, visited)) # Tip # 1. 탐색 시작 노드를 큐에 삽입하고 방문 처리 한다. # 2. 큐에서 노드를 꺼낸 뒤 해당 노드의 인접 노드 중 방문하지 않은 노드를 모듀 큐에 넣고 방문 처리한다. # 3. 더이상 2번의 과정 수행할 수 없을 때까지 반복
2. 예제
2-1. 음료수 얼려먹기
[문제]
N × M 크기의 얼음 틀이 있다. 구멍이 뚫려 있는 부분은 0, 칸막이가 존재하는 부분은 1로 표시된다. 구멍이 뚫려 있는 부분끼리 상, 하, 좌, 우로 붙어 있는 경우 서로 연결되어 있는 것으로 간주한다. 이때 얼음 틀의 모양이 주어졌을 때 생성되는 총 아이스크림의 개수를 구하는 프로그램을 작성하라. 다음의 4 × 5 얼음 틀 예시에서는 아이스크림이 총 3개가 생성된다

[입력]
첫 번째 줄에 얼음 틀의 세로 길이 N과 가로 길이 M이 주어진다. (1 <= N, M <= 1,000)
두 번째 줄부터 N + 1 번째 줄까지 얼음 틀의 형태가 주어진다.
이때 구멍이 뚫려있는 부분은 0, 그렇지 않은 부분은 1이다.
입력예시
4 5
00110
00011
11111
00000
[출력]
한 번에 만들 수 있는 아이스크림의 개수를 출력한다.
출력예시
3
정답 풀이
n, m = map(int, input().split()) #세로 n, 가로 m graph = [] for i in range(n): graph.append(list(map(int, input()))) # 범위 # n : 0 ~ (n-1) # m : 0 ~ m-1 def dfs(x, y): # 1. 상하좌우 살펴보기 if x <= -1 or x >= n or y <= -1 or y >= m: # 경계 넘으면 False return False # 2. 값이 0이면서 방문하지 않은 지점이면 방문 / 덩어리(아이스크림) 만들기 if graph[x][y] == 0: # 방문하지 않은 지점이면 방문 graph[x][y] = 1 # 방문 처리 dfs(x+1, y) # 방문한 지점에서 상하좌우 방문 dfs(x-1, y) dfs(x, y+1) dfs(x, y-1) return True # 한 덩어리 모두 돌았으면 return True return False # 이미 방문한 곳이면 False / else: False로 해도 됨 cnt = 0 for i in range(n): for j in range(m): if dfs(i, j) == True: cnt += 1 print(cnt)
2-2. 미로 탈출
[문제]
N x M 크기의 직사각형 형태의 미로에 여러 마리의 괴물이 있어 이를 피해 탈출해야 한다. 현재 위치는 (1, 1)이고 미로의 출구는 (N,M)의 위치에 존재하며 한 번에 한 칸씩 이동할 수 있다. 괴물이 있는 부분은 0으로, 괴물이 없는 부분은 1로 표시되어 있다. 미로는 반드시 탈출할 수 있는 형태로 제시된다. 탈출하기 위해 움직여야 하는 최소 칸의 개수를 구하라. 칸을 셀 때는 시작 칸과 마지막 칸을 모두 포함해서 계산한다.
[입력]
첫째 줄에 두 정수 N, M(4 <= N, M <= 200)이 주어진다.
다음 N개의 줄에는 각각 M개의 정수(0혹은 1)로 미로의 정보가 주어진다. 각각의 수들은 공백 없이붙어서 입력으로 제시된다. 또한 시작 칸과 마지막 칸은 항상 1이다.
입력예시
5 6
101010
111111
000001
111111
111111
[출력]
첫째 줄에 최소 이동 칸의 개수를 출력한다.
출력예시
10
정답 풀이
# 최단 거리를 찾는 것이 목적이므로 BFS를 활용하여 접근 from collections import deque # N, M을 공백을 기준으로 구분하여 입력 받기 n, m = map(int, input().split()) # 2차원 리스트의 맵 정보 입력 받기 graph = [] for i in range(n): graph.append(list(map(int, input()))) # 이동할 네 가지 방향 정의 (상, 하, 좌, 우) dx = [-1, 1, 0, 0] dy = [0, 0, -1, 1] # BFS 소스코드 구현 def bfs(x, y): # 큐(Queue) 구현을 위해 deque 라이브러리 사용 queue = deque() queue.append((x, y)) # 큐가 빌 때까지 반복하기 while queue: 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: graph[nx][ny] = graph[x][y] + 1 queue.append((nx, ny)) # 가장 오른쪽 아래까지의 최단 거리 반환 return graph[n - 1][m - 1] # BFS를 수행한 결과 출력 print(bfs(0, 0))
'Algorithm' 카테고리의 다른 글
| 구현 알고리즘 (Implementation Algorithm) (3) | 2024.04.03 |
|---|---|
| 그리디 알고리즘 (Greedy Algorithm) (2) | 2024.04.01 |
| 실전에서 유용한 표준 라이브러리 (0) | 2024.04.01 |
| 파이썬 문법 - 입출력 (0) | 2024.04.01 |
| 파이썬 문법 - 자료형(리스트, 튜플, 딕셔너리, 집합) (1) | 2024.04.01 |