본문 바로가기
🏫학부 공부/알고리즘

[알고리즘] 방향그래프

by meteorfish 2023. 11. 21.
728x90

방향그래프

- 모든 간선이 방향간선

 

방향그래프의 속성

- 모든 간선은 한 방향으로만

- G가 단순(간선이 많지 않음) : M <= N(N-1)

- 진입간선, 진출간선을 인접리스트로 이용 시, 크기에 비례해 조사 가능

 

방향 DFS

- DFS와 BFS를 방향그래프로 표현 가능

- 트리간선, 후향간선, 전향 간선, 교차간선

1. 트리간선: 자식을 가리키는 기본적인 간선

2. 후향간선: 조상을 가리키는 간선

3. 전향간선: 

4. 교차간선

 

도달가능성

u->v인 방향경로가 존재하면, "u는v에 도달한다" , "v는 u로부터 도달 가능하다"

 

강연결성

어느 두 정점 u, v 간에 u에서 v 도달가능 이고 v에서u 로 도달가능이면 강연결 

 

강연결 검사 알고리즘

1. 임의 정점 v 선택

2. v에서 DFS 실행

3. 간선들 방향을 역행시킨 그래프 G'을 얻음

4. G'에서 v에서부터 DFS 수행

 

-> O(n+m)

 

 

강연결 요소

- 최대한 모든 정점을 도달할 수 있는 부그래프

a->f로 간다고 하면, a로 다시 올 수 없음.

 


 

이행적폐쇄

 

다음 성질을 만족.

 

1. G'은 G와 동일한 정점들로 구성

2. G의 u에서 v(v!=u)로의 경로가 존재하면 u에서 v로의 방향간선을 추가해줌.

 

한줄: v에서 u로의 경로가 존재하면 도달가능하다는 간선을 추가해줌.

 

방향그래프에 관한 도달 가능성 정보를 제공하는 것임.

 

 

 

이행적폐쇄 계산

 

[ 방법 ]

1. 각 정점에서 DFS 수행

    -> O(n(n+m)) : n * (n+m) = 총 정점 * DFS

2. 플로이드-와샬 알고리즘 (DP)

 

 

플로이드-와샬 이행적폐쇄

1. 정점을 1~n까지 번호 매김

2. 1~n까지 Loop 돌면서 k를 경유 정점으로 쓰는 경로를 고려

 

[ 의사 코드 ]

Alg Floyd-Warshall(G)

G[0] <- G
for k<-1 to n
	G[k] <- G[k-1]
    for i <-1 to n, i!=k
    	for j<-1 to n, j!=i,k
        	if(G[k-1].are Adjacent(v[i], v[k]) 
            	&& G[k-1].areAdjacent(v[k],v[j]))
                	if(!G[k].areAdjacent(v[i],v[j]))
                    	G[k].insertDirectedEdge(v[i],v[j],k)
 return G[n]

 

 


동적 프로그래밍

[ 조건 ]

- 부문제 단순성 : 부문제들이 몇 개의 변수로 정의될 수 있는 경우

- 부분제 최적성 : 전체 최적치가 최적의 부문제들에 의해 정의될 수 있는 경우

- 부문제 중복성 : 부문제들이 독립적이 아니라 상호 겹쳐질 경우 (상향식으로 구축되야함)

 

## DP vs. Divide And Conqual

  DP 분할통치법
공통점 원점 - 목표점 구조

원점: 문제의 초기 또는 기초 지점
목표점: 최종해가 요구되는 지점
차이점 원점  ->  목표점 (단방향) 목표점 -> 원점 -> 목표점(양방향)
(단 해를 구하기 위한 연산 진행은
원점 -> 목표점)
성능 단방향 때문에 종종 효율적 분할 회수 및 중복연산 수행 회수

 

 


방향 비싸이클 그래프 (DAG)

: 방향싸이클이 존재하지 않는 방향그래프

 

## DAG와 위상정렬

- 방향그래프의 위상순서

: 모든 i<j인 간선(v[i], v[j])에 대해 정점들을 번호로 나열

 

- 방향그래프가 DAG면 위상순서를 가지며, 그 역도 참!

 

 

위상 정렬

: DAG로부터 위상순서를 얻는 절차

 

정점의 진입차수 이용하는 위상 정렬

 

[ 의사 코드 ]

Alg topologicalSort(G)

forEach u <- G.vertices()
	
    //in[u]: u의 진입차수
	in[u] <- u.inDegree
    
    // 진입차수가 0 이면 enque
    if(in[u] == 0)
    	Q.enqueue(u)
    
    // 위상정렬 인덱스
    i<-1
    while(!Q.isEmpty())
    	// 진입차수 0인 정점 큐에서 가져옴
    	u<-Q.dequeue()
        i<-i+1
        
        // u에서 나가는 간선돌림
        forEach e<-G.outIncidentEdges(u)
        	// 도착 간선을 가져와서 진입차수를 -1함
        	w<-G.opposite(u,e)
            in[w] <- in[w] - 1
            // 진입차수 0되면 큐에 넣음
            if(in[w] == 0)
            	Q.enqueue(w)
    
    // 위상정렬 인덱스가 모든 정점에 매겨지지 않으면 사이클 있다고 알림
    if(i<=n)
    	write("G에서 사이클 발견!")
    return

 

 

DFS를 특화한 위상 정렬

 

[ 의사 코드 ]

Alg topologicalSortDFS(G)

n <- G.verticeCount
forEach u<-G.vertices()
	l(u) <- Fresh

forEach v<-G.vertices()
	if(l(v) == Fresh)
    	rTopologicalSortDFS(G,v)
 return
Alg rTopologicalSortDFS(G,v)

l(v)<- Visited
forEach e<-G.outIncidentEdges(v)
	w<-opposite(v,e)
    if(l(w) == Fresh)
    	rTopologicalSortDFS(G, w)
    else if ("w 가 위상정렬로 번호 안메겨졌으면")
    	write("그래프에서 싸이클 발견")

v를 n으로 번호매김
n<-n-1

 

 

위상 정렬 알고리즘 분석

두 갭의 버전 모두

시간복잡도: O(n+m)

공간복잡도: O(n)

 

 

 

728x90

'🏫학부 공부 > 알고리즘' 카테고리의 다른 글

[알고리즘] 최소신장트리 (작성중)  (0) 2023.11.23
이진탐색트리  (0) 2023.10.08
합병 정렬과 퀵정렬  (0) 2023.10.04