그래프는 정점과 간선으로 이루어져야 된다.
만약 간선, 정점 둘 중 하나라도 존재하지 않으면 그래프라고 볼 수 없음
어떤 특정 상황에서 대부분 상황을 표현할 수 있으니깐.
노드의 개수만큼의 공간을 확보한 뒤에 각 노드를 기준으로 연결된 노드를 저장하는 방식
-> 예를 들어 음 노드 4개가 있으면 대충 이런느낌
graph = [[] for _ in range(4)
# 노드 0은 노드 1과 노드2와 인접하다는 가정
graph[0].append(1)
graph[0].append(2)
# 노드 1은 노드 3과 인접하다는 가정
graph[1].append(3)
# 노드 2가 인접한 것
graph[2].append(3)
# 노드 3이 인접한 것
graph[3].append(0)
만약 양방향 그래프면 각 노드에 각각 추가하면 된다. 예를 들어 노드 0과 노드 2 가 양방향 그래프로 연결되어 있으면
graph[0].append(2)
graph[2].append(0)
만약 가중치 그래프라면 노드 0과 노드 2 사이의 가중치가 3이라면 뒤에 가중치를 추가해주면 된다.
graph[0].append(([2, 3]))
graph[2].append(([0, 3]))
'노드의 개수 * 노드의 개수' 만큼의 행렬 공간을 만든 후에 간선을 표현하는 방식
간선의 정보만을 표현하는 방식으로 간선에 대한 정보만(노드 2개로 표현)을 저장하는 방식
-> 근데 거의 인접리스트만 사용함 가끔 인접 행렬? 간선리스트 사용 잘안함
말 그대로 방향 존재 유무에 따라 방향, 양방향 그래프가 존재하고 가중 그래프는 노드 사이의 간선에 가중치가 값?이 들어가있는 느낌임
선형 자료구조는 배열과 같이 원소들을 하나씩 순차적으로 나열시킨 형태이다. 비선형 자료구조는 반면 하나의 자료 뒤에 여러 개의 자료가 존재할 수 있는 형태로 1:N, N:N의 관계를 나타낸다. 그럼 트리와 그래프는? --> 당연히 비선형 자료구조이다.
현재 탐색 중인 노드를 기준으로 연결된 노드를 즉시 방문하는 방식
N = 5
def dfs(node):
# Base case
if visited[node]:
return
visited[node] = True
print(node, end=" ")
# Recursive case
for next_node in graph[node]:
dfs(next_node)
graph = [[] for _ in range(N)]
visited = [False] * N
graph[0].append(1); graph[0].append(2)
graph[1].append(4); graph[1].append(3)
graph[2].append(3)
graph[3].append(0)
dfs(0)
시작노드를 기준으로 거리가 가까운 노드를 먼저 방문하는 방식 => '거리가 가깝다'라는 의미는 시작노드에서 해당 노드까지 갈 때 거친 간선의 개수가 적다는 것을 의미한다.
from collections import deque
N = 5
def bfs(node):
global visited, graph
queue = deque([node])
visited[node] = True
while queue:
node = queue.popleft()
print(node, end=" ")
for next_node in graph[node]:
if not visited[next_node]:
visited[next_node] = True
queue.append(next_node)
graph = [[] for _ in range(N)]
visited = [False] * N
graph[0].append(1); graph[0].append(2)
graph[1].append(4); graph[1].append(3)
graph[2].append(3)
graph[3].append(0)
bfs(0)
결론은 BFS / DFS 둘 중 그래프 순회할 때는 자신있는 알고리즘을 사용하면 된다. 근데 만약 시작노드로부터 거리가 가까운 순으로 탐색해야 하는 경우라면 BFS를 사용하는 게 좋음. 그런 경우가 아닐 땐 DFS를 사용하는 편이다.
일반적인 그래프 탐색을 할 때는 둘 중 아무거나 사용해도 되지만, 앞서 언급한대로시작노드로부터 가까운 노드 순으로 방문해야 될 때는 DFS를 사용할 수 없다.
즉, BFS는 간선의 가중치가 없는(=간선의 가중치가 모두 동일한) 그래프에서의 최단 경로 알고리즘으로 활용할 수 있다.
반면, 간선의 가중치가 존재하는 경우에는 다익스트라, 벨만포드, 플로이드 워셜과 같은 최단경로 알고리즘을 사용해야 한다.
N = 7
def dfs(node):
global visited, graph, node_set
# Base case
if visited[node]:
return
visited[node] = True
node_set.add(node)
# Recursive case
for next_node in graph[node]:
dfs(next_node)
graph = [[] for _ in range(N+1)]
visited = [False] * (N+1)
graph[1].append(5); graph[5].append(1)
graph[1].append(3); graph[3].append(1)
graph[3].append(7); graph[7].append(3)
graph[4].append(6); graph[6].append(4)
count = 0
connect_components = []
for node in range(1, N+1):
node_set = set()
if not visited[node]:
dfs(node)
count += 1
connect_components.append(node_set)
print(count) # 7
print(connect_components) # [{1, 3, 5, 7}, {2}, {4, 6}]
N = int(input())
M = int(input())
visited = [False] * (N+1)
graph = [[] for _ in range(N+1)]
result = -1
for _ in range(M):
start, end = map(int,input().split())
graph[start].append(end)
graph[end].append(start)
def solution():
global result
dfs(1)
print(result)
def dfs(node):
global visited, graph, result
# Base case
if visited[node]:
return
visited[node] = True
result += 1
# Recursive case
for next_node in graph[node]:
if not visited[next_node]:
dfs(next_node)
solution()