- G = (V, E) (vertex (정점), edge (간선))
그래프 vs 트리
표현법
인접 행렬 | 인접 리스트 | |
---|---|---|
1. memory | O(n^2) | O(n + m) |
2. E(u,v)exist? | G[u][v] > 0? O(1) |
G[u].search(v)? O(n) |
3. u에 인접한 모든 노드 v에 대해 |
O(n) | O(인접노드수) |
4. insert E(u,v) | G[u][v] = 1 O(1) |
O(1) |
5. delete E(u,v) | G[u][v] = 1 O(1) |
O(1) |
1. memory? 인접행렬: n^2, 인접 리스트: n(node 수) + m(edge 수)
2. 노드 u, v에 연결된 edge? 인접행렬: O(1), 인접 리스트: O(n) (최악의 경우)
(줄줄이 소세지마냥 한 노드에 다 연결되어 있는 경우)
3. u에 인접한 모드는 노드 v에 대해 동작할 경우? 인접행렬: O(n), 인접 리스트 O(n)
4. 새로운 edge 삽입? 인접행렬: O(1), 인접 리스트: G[u].pushfront(v) O(1)
5. 기존 edge 삭제? 인접행렬: O(1), 인접 리스트: O(n)
3번의 경우 인접 행렬은 노드간 연결이 없어도 O(n)시간이 걸린다, 인접 리스트는 head부터 None까지 가기 때문에 인접한 노드 수만큼 수행하게 된다.
따라서, 메모리 측면에선 인접 리스트가 좋지만, 속도 측면에서는 인접 행렬이 더 빠르다 (단, m이 무수히 많지 않은 경우)
장점
단점
재귀적
비재귀적
# 재귀적 dfs
def dfs(v): # v를 방문중
mark[v] = 'visited'
pre[v] = cur_time # v의 첫번째 방문시간
cur_time += 1
for each edge(v, w): # v의 인접한 모든 노드 w에 대해서
if mark[v] != 'visited':
# 저장된 parent 값을 가지고 DFS tree를 만들 수 있다.
parent[w] = v # 현재 방문 노드의 부모노드
dfs(w)
# v에 인접한 모든 노드를 고려했다.
post[v] = cur_time # v에서 dfs가 완료된 시간
cur_time += 1
return
# 비재귀적 dfs
def dfs(s):
stack.push((p, s)) # p: 부모노드, s: 현재 방문노드
while stack is not empty:
p, v = stack.pop()
if v is unmarked: # 해당 노드를 방문하지 않았다면
mark[v] = 'visited'
parent[v] = p
for each edge(v, w): # v에 인접한 노드에 대해서
if w is unmarked:
stack.push((v, w))