DataScience/Data Structure

[자료구조] 그래프

Grace 2023. 12. 6. 15:59

개념 및 용어

그래프 G는 하나 이상의 정점(혹은 노드)을 포함하는 집합 V와 두 정점의 쌍으로 구성되는 간선을 포함하는 집합 E의 순서쌍으로 정의함 (G = (V, E))

  • 간선(인접한다)
    • 두 정점을 연결하는 선
    • 두 정점 쌍으로 나타냄
  • 무방향 그래프
    • 간선의 방향성이 없음
    • 정점 쌍이 순서를 나타낼 필요가 없으므로 {v1, v2}로 나타냄
    • n개의 정점을 갖을 때 최대 간선 개수: n(n-1)/2
    • 차수: 정점에 연결된 간선들의 개수
  • 방향 그래프
    • 간선의 방향성이 있음
    • 순서쌍 (v1, v2)
    • n개의 정점을 갖을 때 최대 간선 개수: n(n-1)
    • 진입 차수: 주어진 정점으로 향한 간선의 개수
    • 진출 차수: 주어진 정점에서 시작하는 간선의 개수
    • 정점의 차수: 진출 차수와 진입 차수의 합
  • 다중 그래프: 두 정점을 잇는 간선이 여러 개인 그래프
  • 방향 다중 그래프: 방향성을 갖는 간선으로 이루어진 다중 그래프
  • 무방향 다중 그래프: 방향성이 없는 간선으로 이루어진 다중 그래프
  • 가중 그래프: 간선이 가중치를 갖는 그래프
  • 완전 그래프: 모든 정점들이 간선으로 서로 연결된 그래프
  • 독립 정점: 다른 어떤 정점과도 인접하지 않은 정점
  • 널(null) 그래프: 독립 정점만으로 구성한 그래프( E가 공집합 )
  • 경로: 임의의 두 정점을 연결하는 어떤 간선의 끝 정점(해당 간선의 머리)에서 그 간선의 시작 정점(해당 간선의 꼬리)으로 이러이즌 간선의 연속(열)
  • 경로 길이: 경로에 있는 간선의 수
  • 단순 경로: 경로 상에 있는 모든 정점이 서로 다른 경로
  • 기본 경로: 경로 상에 있는 모든 간선이 서로 다른 경로
  • 사이클: 출발점과 도착점이 동일한 단순 경로
  • 사이클 그래프: 사이클이 있는 그래프
  • 루프: 간선의 시작점과 끝점이 같은 정점인 길이가 1인 그래프
  • 무사이클 그래프: 사이클이 없는 그래프 (혹은 트리)

추상 자료형

  • 객체의 정의: 정점과 간선의 유한 집합
  • 연산:
    • Graph Create() ::= 그래프 생성
    • Destroy(Graph) ::= 그래프 기억장소 반납
    • Graph Copy_Tree(Graph) ::= 그래프 복사
    • InsertVertex() ::= 그래프에 정점 삽입 InsertEdge() ::= 그래프에 간선 추가
    • DeleteVertex() ::= 정점 삭제 DeleteEdge() ::= 간선 삭제
    • Search() ::= 정점 탐색
    • IsAdjacent() ::= 인접 정점 여부 확인
    • ExistPath() ::= 경로가 존재하는지 확인
    • PathLength() ::= 경로 길이 계산
    • BFS() ::= 너비 우선 탐색
    • DFS() ::= 깊이 우선 탐색

그래프 탐색

그래프 G=(V, E)와 V(G)에 있는 정점 v가 주어졌을 때, 정점 v에 도달할 때까지 G의 정점을 방문하는 연산 → 만일 그래프 내에 정점이 v가 없다면, 그래프의 모든 정점을 방문한 후 종료함

깊이 우선 탐색(Depth First Search, DFS)

  • 시작: 출발점 v를 방문
  • 다음으로 v에 인접하고 아직 방문하지 않은 정점 w를 선택하여 w를 출발점으로 다시 깊이 우선 탐색(인접하고 아직 방문하지 않은 정점을 선택)
  • 위의 두 과정을 모든 정점을 한 번씩 방문할 때까지 반복
  • 만약 인접한 모든 정점들이 이미 방문한 정점인 경우, 가장 최근에 방문했던 정점 중에서 방문하지 않은 정점 w를 가진 정점을 선택하여 정점 w로부터 다시 깊이 우선 탐색 시작 → 스택을 사용하여 가장 최근에 선택의 지점에 있던 정점을 찾아냄
  • 방문하지 않은 정점이 없으면 탐색 종료
// 순환 호출
void DFS(Graph* g, int s) {
	int i, adjacent;
	g -> visited[s] = 1;
	printf("%d", s);

	for(adjacent = 0; adjacent < g -> nv; adjacent++) {
		if(g -> adj[s][adjacent] && !g -> visited[adjacent]) {
			g -> visited[adjacent] = 1;
			DFS(g, adjacent);
		}
	}
}

// 스택 직접 운영
void DFS(int s) {
	int v, w;
	extern struct stack *st;
	extern int visited[];
	InitializeStack(st);
	push(st, s);
	visited[s] = 1;
	while((v=pop(st)) >= 0) {
		visited[v] = 1;
		while(v에 인접한 모든 노드 w)
			if(!visited[w]) push(st, w);
	}
}

너비 우선 탐색(Breadth First Search, BFS)

  • 시작: 출발점 v를 방문
  • 다음으로 v에 인접한 정점 w를 모두 방문한 후, 다시 w에 인접한 방문하지 않은 정점들을 차례로 방문
  • 두 과정을 모든 정점을 한 번씩 방문할 때까지 반복
  • 인접 정점을 모두 방문하기 때문에 큐를 사용
void BFS(Graph* g, int s) {
	int i, adjacent;
	int visited[MAX_VERTICES];
	for(i=0; i< g->nv; i++) visited[i]=0;
	
	int queue[MAX_VERTICES];
	int front=0, rear=0;

	visited[s]=1;
	queue[rear++]=s;

	while(front != rear) {
		s=queue[front++];
		printf("%d", s);
		for(adjacent=0; adjacent < g->nv; adjacent++) {
			if(g->adj[s][adjacent] && !visited[adjacent]) {
				visited[adjacent]=1;
				queue[rear++]=adjacent;
			}
		}
	}
}

최소 비용 신장 트리

  • 트리: 사이클이 없는 단순 그래프
  • 트리는 그래프이기는 하지만 루트를 가지기 때문에 (일반 그래프에는 없는) 계층 개념이 있고, 사이클이 없어서 한 정점에서 다른 정점으로 가는 경로가 유일한 구조
  • 신장 트리(spanning tree): 그래프 G의 모든 정점과 간선의 일부(또는 전부)를 포함하는 트리
    • 주어진 그래프의 정점을 모두 포함함
    • n-1개의 간선으로 구성한 그래프
  • G의 최소 부분 그래프: 그래프 G의 부분 그래프 중에서 간선의 수가 가장 작은 것
  • 최소 비용 신장 트리: 가중치 그래프 G의 가중치가 작은 간선을 선택하여 구성된 신장 트리

프림(Prim) 알고리즘

n개의 정점을 갖는 연결 그래프 G에 대한 최소 비용 신장 트리 T를 구하는 알고리즘

  • 그래프 전체에서 최소 비용을 갖는 간선 {u, v}를 선택하여 이 간선을 T에 추가함
  • 이 간선을 T에 추가할 때 사이클을 형성하지 않으면 추가하고 아니면 무시함
void prim() {
	T = Ø;
	W = Ø;
	E로부터 최소 비용인 간선 {v, w}를 선택;
	while(T는 n-1개 이하의 간선 포함 && E는 공집합 아님) {
		E에서 간선 {v, w}를 제거;
		if({v, w}가 T 내에서 사이클을 생성 안함) {
			T = T ∪ {{v, w}}; // 선택한 간선 추가
			W = W ∪ {v, w}; // 선택한 정점 추가
		}
		else 간선 {v, w}를 버림;
		E로부터 W 내의 정점과 최소 비용으로 연결된 간선 {v, w}를 선택;
	}
}

크루스컬(Kruskal) 알고리즘

  • 남은 간선 중에서 무조건 최소 비용인 간선을 선택한 후 사이클을 형성하지 않으면 그 간선을 선택함
  • 중간 과정에 있는 T는 하나의 트리가 아니고 여러 개의 분산된 트리, 즉 숲일 수 있음
void kruskal() {
	while(T는 n-1개 이하의 간선 포함 && E는 공집합 아님) {
		E로부터 최소 비용인 간선 {v, w}를 선택;
		E에서 간선 {v, w}를 제거;
		if({v, w}가 T 내에서 사이클을 생성 안함) T = T ∪ {{v, w}};
		else 간선 {v, w}를 버림;
	}
}

솔린(Sollin) 알고리즘

  • 간선이 하나도 없는 그래프의 모든 정점들로 구성된 숲에서 시작함
  • 단계가 거듭되면서 숲 내의 트리들이 최소 비용을 갖는 간선으로 연결
void sollin() {
	// 집합 E의 초기 상태는 주어진 그래프의 간선 집합
	// 집합 F의 초기 상태는 그래프의 모든 정점들로 구성된 간선이 없는 숲

	T=Ø;
	while(T는 완전한 하나의 트리가 아님 && E는 공집합 아님) {
		for(F 내의 각각의 트리 T에 대하여) {
			T와 다른 트리를 연결하는 E의 간선 {v, w} 중에서 최소비용 간선 {v, w}를 선택;
			T = T ∪ {{v, w}};
			E에서 간선 {v, w}를 제거;
		}
		T에 새로 추가된 간선을 포함하여 F를 수정;
	}
}

'DataScience > Data Structure' 카테고리의 다른 글

[자료구조] 멀티웨이 탐색 트리  (1) 2023.12.06
[자료구조] BS, Splay, AVL, BB  (1) 2023.12.05
[자료구조] 선택 트리, 숲, 이진 트리 개수  (0) 2023.12.04
[자료구조] 힙  (1) 2023.12.04
[자료구조] 트리  (0) 2023.11.30