본 게시물은 코드잇의(codeit) 자료구조 시리즈 강의 세번째 주제인 '그래프의 구조와 탐색'을 듣고 정리한 게시물임을 알려드립니다.
- 강의 url : https://www.codeit.kr/topics/graphs
그래프의 구조와 탐색 - 알고리즘 · 자료구조 강의 | 코드잇
교통 앱에서 지하철역간 최단 거리를 알려주거나 SNS에서 사람들의 친구 관계를 볼 수 있는 것처럼, 우리 주변에는 다양한 연결 관계를 표현하는 데이터가 있습니다. 그래프는 연결 관계를 가지
www.codeit.kr
1. 그래프 탐색이란?
그래프 탐색
보통 자료구조에서 말하는 탐색 : 주어진 조건을 만족하는 데이터를 찾아내는 것
그래프 탐색 : 하나의 노드를 시작점으로 해서 연결된 노드들을 모두 찾는 것을 의미
- 그래프를 탐색할 때 시작점 노드는 우리가 정하기 나름
- 그리고 이 시작점으로부터 다른 노드들을 찾아가는 것
- 그래프의 구조에 대해 의미 있는 정보들을 알아낼 수 있음
순회 : 자료 구조 안에 저장된 모든 데이터를 도는 것
- 그래프 탐색도 하나의 노드에서 연결된 모든 노드를 다 돌기 때문에 어쩔 때는 그래프 순회라고도 부른다.
- 그래프 탐색, 그래프 순회 둘 다 같은 말이지만 본 강의에서는 '탐색'으로 표현할 예정
그래프 탐색 활용 예시
① 주어진 두 노드가 경로를 통해 연결 됐는지 안됐는지를 알아낼 수 있음
A와 B,C,D는 서로 연결되지 않았음
② 그래프에서 두 노드 사이의 최단 경로를 구함
- 최단경로 : 두 노드 사이에 존재하는 모든 경로 중 가장 거리가 짧은 경로
- 아래와 같이 친구 관계 그래프가 있으면 두 사람 사이 몇 다리를 건너서 친구인지를 알아낼 수 있는 것
③ 기타 정렬 등에서도 활용됨
BFS와 DFS
- 각 노드를 어떤 순서로 탐색하는지에 따라 BFS, DFS로 나뉨 (본 장 주제)
2. BFS(Breadth First Search) 개념
BFS란?
- Breadth는 너비를 뜻하는데, BFS는 너비를 우선적으로 놓고 탐색하는 알고리즘임.
그래프에서 너비를 우선적으로 탐색하는 과정
- 노드 A에서 탐색 시작
- 회색 : 탐색 마친 노드들
- 탐색한 노드들의 인접한 노드들을 계속 탐색 : A → B → C → ... → J
- 시작점에서 가까운 노드들을 먼저 탐색
- 그래프를 봤을 때 수평적으로, 깊이 보다는 너비 우선적으로 탐색
3. 큐
BFS 알고리즘에 대해서 본격적으로 시작하기 전에 미리 알아야 할 추상 자료형 : 큐
추상자료형 큐 : 선입선출(FIFO : First-in-first-out)
큐는
- 맨 뒤에 데이터 삽입
- 맨 앞 데이터 삭제
- 맨 앞 데이터 접근
이런 기능들을 약속하는 추상 자료형으로, 데이터 흐름이 선입선출(First-in-first-out)
파이썬에서 큐 사용하기
파이썬에서는 자료형 deque으로 큐를 씀
from collections import deque
queue = deque()
변수 queue에 빈 deque를 만들어줌
# 큐 맨 끝에 뒤에 데이터 추가
queue.append("태호")
queue.append("영훈")
queue.append("현승")
queue.append("지웅")
# 큐 데이터를 출력한다
print(queue) # 태호, 영훈, 현승, 지웅
그리고 이렇게 문자열 “태호”, “영훈”, “현승”, “지웅”을 저장
print 함수를 써서 queue를 출력하면 데이터가 저장한 순서대로 있는 걸 확인 가능
# 큐 마지막 데이터를 삭제한다
# 보통 큐에서도 마지막 데이터를 삭제하면 해당 메소드가 삭제한 데이터를 리턴한다
print(queue.popleft()) # 태호
print(queue.popleft()) # 영훈
print(queue.popleft()) # 현승
# 큐 데이터를 출력한다
print(queue) # 지웅
데이터를 삭제할 때는 popleft 메소드를 사용하면 가장 앞에 있는 데이터를 삭제 가능
보통 큐는 삭제하는 데이터를 리턴하기 때문에 가장 앞 데이터를 “추출한다” 아니면 “꺼내온다”라고도 할 수 있음
4.BFS 알고리즘
BFS 알고리즘 단계
예시를 통한 설명
- 시작점 : A
- 알고리즘을 진행하면서 이미 방문한 노드들 : 회색 , 방문하지 않은 노드들 : 노란색
- 방문했다고 표시되면 큐에 들어감
- 큐에 들어있는 노드가 없어질 때까지 반복문을 돌릴 것
- 가장 앞에 있었던 A를 꺼내어 인접한 모든 노드들은 돈다. 이때 이미 방문한 노드들은 건너뛴다.
A와 인접한 노드 처리
- 노드 B는 A에 인접해있고, 아직 방문하지 않았으니 이를 방문 노드로 표시 후 큐에 넣는다.
- C도 마찬가지
- 처리 완료된 노드 A는 이제 삭제한다.
큐에 가장 앞 노드인 B를 꺼내어 반복 (선입선출로 C보다 빨리 들어온 B부터 탐색)
- A는 회색으로 건너뜀
- D는 아직 방문하지 않은 노드이므로 회색으로 표시 후 큐에 넣음
- 이런식의 과정을 큐에 아무 노드도 없을 때까지 반복 → A에서 연결된 노드들을 모두 찾음
탐색 노드 순서
- A B C D E F G H
- 그래프가 위아래보다 옆으로 먼저 탐색
5. (실습) BFS로 연결된 역 찾기
6. (실습) BFS 알고리즘 시간 복잡도 분석
7. DFS(Depth First Search) 개념
DFS
- 깊이를 우선적으로 놓고 탐색
- A를 시작점으로 하고 탐색 -> A에 인접한 노드인 B, D, G
-> 더 이상 갈 노드가 없으면 길이 있는 노드까지 다시 올라가서 E, H, J → C,F, I
8. 스택
BFS에서 큐가 중요했던 거처럼 DFS에서도 추상자료형 '스택'이 중요함
추상 자료형 스택 : 후입선출(Last-in-first-out)
- 맨 뒤에 데이터 추가
- 맨 뒤 데이터 삭제
- 맨 뒤 데이터 접근
이런 기능을 약속하는 추상 자료형
맨 뒤에 데이터를 추가하고, 맨 뒤에서 데이터를 삭제할 수 있는 추상 자료형
- 가장 마지막에 들어간 데이터가 가장 먼저 나오기 때문에 이런 행동을 LIFO, Last-in-first-out이라고 부르기도 함
파이썬에서 스택 사용하기
스택도 큐와 마찬가지로 파이썬에서 자료형 deque로 씁니다. deque를 통해 스택의 기능을 복습해 볼게요.
from collections import deque
stack = deque()
변수 stack에 빈 deque를 만들어줬습니다.
# 스택 뒤에 정보 추가: "Taeho!"
stack.append("T")
stack.append("a")
stack.append("e")
stack.append("h")
stack.append("o")
# 스택 정보를 출력한다
print(stack) # T, a, e, h, o
그리고 이렇게 문자열 “T”, “a”, “e”, “h”, “o”을 저장했다고 할게요. print 함수를 써서 stack을 출력하면 데이터가 저장된 순서대로 있는 걸 확인할 수 있습니다.
# 스택 마지막 정보를 삭제한다
# 보통 스택에서 마지막 데이터를 삭제하면 해당 메소드가 삭제한 데이터를 리턴한다
print(stack.pop()) # o
print(stack.pop()) # h
print(stack.pop()) # e
# 스택 정보를 출력한다
print(stack) # T, a
스택은 가장 뒤 데이터를 삭제할 수 있습니다. 파이썬 deque에서는 pop 메소드를 이용하면 뒤에서부터 데이터를 삭제할 수 있습니다. 지금은 이 메소드를 세 번 호출했으니까, 가장 최근에 타이핑한 세 글자를 입력 취소 하는 거죠. 큐를 쓸 때 배웠던 popleft 메소드와 같이 pop 메소드도 삭제하는 데이터를 리턴합니다. 마지막에는 남은 스택을 보면 가장 뒤 데이터 3개가 삭제된 것을 확인할 수 있습니다.
9. DFS 알고리즘
DFS 알고리즘 단계
예시를 통한 설명
1) 시작노드 A를 옅은 회색으로 표시한 후 스택에 넣는다 : 스택에 있는 노드가 없을 때까지 반복문을 돌릴 것
2) 스택의 가장 위 노드 A를 꺼낸 후 A를 방문한 노드로 처리
- A에 인접한 모든 노드들을 본다. 회색 노드 즉, 스택에 있거나 이미 방문한 노드는 건너뜀.
- C, B는 처음 보는 노드이므로 표시 후 스택에 넣는다. A와 인접한 모든 노드는 처리했음
3) 스택 가장 위에서 B 노드를 꺼낸 후 진한 회색(방문한 노드) 처리 -> B를 중심으로 인접 노드 탐색
- B와 인접한 노드들을 돌 것이며, 회색 노드는 건너뜀.
- A는 건너뛰며, D는 처음 보는 노드이므로 표시 후 스택에 넣어줌. B와 인접한 노드들은 모두 처리함
4) 스택에서 가장 위인 노드 D를 꺼냄 (D : 방문한 노드로 처리)
- E, G는 처음보는 노드이므로 표시를 해준 후 스택에 넣어줌
5) 스택의 가장 위 노드인 G를 꺼냄 (G를 방문한 노드로 처리)
- 회색 노드는 건너뛰며, 아무것도 없이 처리 끝남.
6) 이런식으로 스택에 아무 노드도 남지 않을 때까지 실행하면 A에서 연결된 노드들을 모두 찾음
- 노드들이 방문 처리된 순서 : A, B, D, G, E, H, C, F, I (즉, 깊이 우선적으로 탐색)
10. (실습) DFS로 연결된 역 찾기
11. DFS 알고리즘 시간 복잡도 분석
'IT 일반 > 자료구조(개념) - [인강] 코드잇, [대학원] 전공 수업' 카테고리의 다른 글
[대학원-자료구조 3] 기타 정렬 알고리즘 - MergeSort, QuickSort (1) | 2024.10.20 |
---|---|
[대학원-자료구조 1~2] Tree와 Graph, Heap (2) | 2024.10.19 |
[코드잇-자료구조3 : 그래프의 구조와 탐색] 1. 그래프 (6) | 2024.10.19 |
[코드잇-자료구조2 : 트리의 구조와 탐색] 2. 힙 (1) | 2024.10.19 |
[코드잇-자료구조2 : 트리의 구조와 탐색] 1. 트리란? (4) | 2024.10.19 |