[Algorithm] 그래프

권오흠 교수님의 '영리한 알고리즘' 강좌 참고

- 그래프
G = (v, m) 으로 나타낸다.
v = 정점 수 / m = 간선 수

- 그래프 표시 방식
1. 인접행렬
알고리즘 4-1강. 깊이 우선 탐색(Depth First Search)
v 개의 정점이 있는 그래프라고 할 때 인접행렬을 통해 나타내는 그래프는 n X n 행렬로 나타낸다. 두 정점이 연결되어있는 경우 1로 나타내고 연결되어있지 않은 경우 0 으로 표시한다.
- 시간복잡도
1. 노드 v의 모든 인접 노드 찾기 : O(n) 
    행렬에서 n 번 만큼 순회해야 모두 찾을 수 있다.
2. 노드 v1가 v2와 인접행렬인지 찾기 : O(1)
    행렬 g[v1][v2] 가 1인지 0인지만 확인하면 된다.

2. 인접리스트
정점의 집합 배열과, 인접 정점들을 연결 리스트로 나타내는 방식이다.
그래프] Adjacency List by Vector(인접리스트)

이 때 A 정점이 정점 B와 연결된 상태가 A 연결 리스트와 B 연결리스트에 순서만 바뀐상태로 2번 입력된다. 따라서 연결 리스트의 수는 전체 간선 m 수의 두 배가 된다.

- 시간복잡도
1. 노드 v의 인접 노드 찾기 : O(degree(v))
    degree는 정점 v의 인접리스트의 개수이다. 그 개수만큼만 순회하면 모두 찾을 수 있다.
2. 노드 v1과 v2가 인접해있는지 찾기 : O(degree(u))
 
인접 행렬과 인접리스트로 나타낼 경우의 장단점이 있으므로 상황에 맞게 사용하는 것이 좋겠다. 하지만 복잡한 알고리즘 계산이 필요할 경우 인접리스트를 사용하는게 더 효율적이 경우가 많다고 한다.

- 그래프의 순회
1. BFS ( 너비우선탐색 )
인접행렬 : O(N^2)
인접리스트 : O(N+M)

가까운 레벨에 있는 노드들 부터 탐색하는 순회 방법이다. Queue를 사용해서 구현할 수 있다. 방식은,
1. 출발점 노드를 큐에 넣는다.
2. 큐에 노드가 없어질 때 까지 반복을 시작한다.
3. 큐에서 노드를 pop 하고 해당 노드의 인접 노드들이 방문된 적이 없다면 큐에 push 한다. ( 방문했는지 확인하기 위해 visited 리스트를 사용한다.)
4. 반복이 종료되고 visited 리스트를 출력하면 방문한 노드의 순서를 확인할 수 있다.

파이썬 코드

#그래프 전체 순회 (BFS)
def BFS_graph(graph, start):
    q = deque([start])
    visited = [start]
    while q:
        node = q.popleft()
        for i in graph[node]:
            if i not in visited:
                q.append(i)
                visited.append(i)
    print(visited)
 
BFS_graph(graph, 1)
cs

- BFS를 이용한 최단거리 탐색
BFS는 같은 레벨에 있는 노드를 우선적으로 방문하기 때문에 이를 활용해 두 노드 사이의 최단 거리를 구할 수 있다.

- BFS 이진트리
BFS트리에서 s에서 v 까지의 경로는 s에서 v까지 가는 최단경로이다. 어떤 edge도 2개의 layer를 건너가지 않는다.  

파이썬 코드

# s에서 v 까지의 경로 출력하기
# s에서 모든 노드로의 pi를 구해뒀다는 전제 하에
def BFS_print_path(graph, s, v):
    if v == s:
        print(s)
    elif pi[v]==-1:
        print('no path from s to v exists')
    else:
        BFS_print_path(graph, s, pi[v])
        print(v)
BFS_print_path(graph, 1,8)
cs

2. DFS(깊이우선탐색)
인접한 노드의 아래 layer로 타고 들어가서 더이상 인접한 노드가 없을 때까지 깊이 들어가는 방식으로 탐색하는 순회 방식이다.

파이썬 코드

# DFS
visited = [0]*(len(graph)+1)
dfs_s = []
def DFS_search(G,v):
    dfs_s.append(v)
    visited[v] = 1
    for node in G[v]:
        if visited[node]==0:
            DFS_search(G,node)
DFS_search(graph,1)
print(dfs_s)
cs

No comments:

Powered by Blogger.