그래프 - 위상정렬

2022. 1. 31. 19:41·자료구조
728x90
반응형

# [C언어로 쉽게 풀어쓴 자료구조(천인국)]를 공부하고 주요 내용을 정리하고자 작성하는 글입니다.

# 해당 게시글에 대한 모든 피드백 환영합니다.

 

위상 정렬 Topological Sort

각 정점들의 선행순서를 위반하지 않으면서 모든 정점을 나열하는 것

생각의 순서

1. 진입차수가 0인 정점을 선택한다. (정점이 여러 개인 경우, 임의로 하나를 고른다.)

2. 선택된 정점과 이 정점에 부착된 간선들을 모두 삭제한다. 

3. 2번에서 선택된 정점과 인접했던 정점들의 진입차수를 -1 한다.

4. 모든 정점이 사라질 때까지 1~3 과정을 반복한다.


알고리즘

1
2
3
4
5
6
7
topo_sort(G)
    if (모든 정점의 진입차수가 1 이상이면)
        then 위상정렬 불가
    while 정점이 존재할때 do
        진입차수가 0인 정점 v를 선택
        v 를 출력
        v와 v에서 나온 모든 간선들을 그래프에서 삭제 // v의 인접정점의 진입차수 -1
cs

그림으로 이해하기

아래의 방향 그래프에 대해 위상정렬을 수행해보자.

단, 핵심 키워드가 진입차수라는 점을 유의한다. 

1. 정점 1과 1에 부착된 모든 간선들을 삭제한다.

==> 1의 인접정점들의 진입차수를 모두 -1한다.

2. 정점 0과 0에 부착된 모든 간선들을 삭제한다.

==> 0의 인접정점들의 진입차수를 모두 -1한다.

3. 정점 2와 2에 부착된 모든 간선들을 삭제한다.

==> 2의 인접정점들의 진입차수를 모두 -1한다.

4. 정점 3과 3에 부착된 모든 간선들을 삭제한다.

==> 3의 인접정점들의 진입차수를 모두 -1한다.

5. 정점 4과 4에 부착된 모든 간선들을 삭제한다.

==> 4의 인접정점들의 진입차수를 모두 -1한다.

6. 정점 5와 5에 부착된 모든 간선들을 삭제한다.

==> 5의 인접정점들의 진입차수를 모두 -1한다.

 

7. 그래프 내 모든 정점들이 삭제되었으므로 종료한다.


c언어로 구현

위상정렬을 구현하기 위해서는 스택stack과 연결리스트로 표현된 그래프를 이용한다.

 

  1. 우선, 그래프에서 진입차수가 0인 정점들을 모두 push한다.
  2. 스택에서 pop하여 정점을 하나 가져온다.
  3. 그 정점의 연결리스트에 가서 모든 인접정점의 진입차수를 1씩 뺄셈 연산을 수행한다. 뺄셈 연산을 수행한 직후에 진입차수가 0이 되는 정점은 바로 스택에 push한다. 
  4. 이 과정을 스택이 공백상태가 될 때까지 진행한다.

앞서 예시로 들었던 방향그래프에 대해, 스택을 이용해 풀면 다음과 같다.

이를 한번 코드로 구현해보자.

#include <stdio.h>
#include <stdlib.h>

/****************************** graph **********************************/
#define MAX_VERTICE 50

// 각 정점마다 하나의 연결리스트가 필요하다
// 정점의 개수만큼의 포인터 배열(헤더노드)이 필요하다
// 하나의 노드 ==> GraphNode
// 포인터 배열의 이름 ==> adj_list
typedef struct GraphNode {
	int vertex; // 정점의 값을 저장할 곳
	struct GraphNode* link;  // 인접정점을 가리킬 포인터 
	// 가리킬 변수의 타입이 하나의 정점이기 때문에 GraphNode 타입으로 선언해야 한다.
} GraphNode;

typedef struct GraphType {
	int n;	// 해당 그래프의 정점의 개수
	GraphNode* adj_list[MAX_VERTICE]; // 정점들을 관리할 리스트; 헤더노드 집합
} GraphType;

// 그래프 초기화
void graph_init(GraphType* g) {
	int v;
	g->n = 0; //그래프 정점의 개수 0으로 초기화
	for (v = 0; v < MAX_VERTICE; v++) {
		g->adj_list[v] = NULL;
	}
}
// 정점 삽입 연산: 값이 v인 정점을 삽입한다.
void insert_vertex(GraphType* g, int v) { // v: 삽입합 노드의 값
	if ((g->n) + 1 > MAX_VERTICE) {
		fprintf(stderr, "그래프: 정점의 개수 초과\n");
		return;
	}
	g->n++;
}
// 간선 삽입 연산: 정점 u와 v를 연결한다 : 정점 u에 간선 (u,v)를 맨 처음에 삽입
void insert_edge(GraphType* g, int u, int v) {
	if (u >= g->n || v >= g->n) {
		fprintf(stderr, "그래프: 정점 번호 오류\n");
		return;
	}
	GraphNode* node = (GraphNode*)malloc(sizeof(GraphNode));
	node->vertex = v;
	node->link = g->adj_list[u];
	g->adj_list[u] = node;	// adj_list의 한 원소는 GraphNode를 가리키는 포인터 타입이니까 할당 가능
}

/********************************** stack ***************************************/
#define MAX_STACK_SIZE 10
typedef int element;
typedef struct {
	element stack[MAX_STACK_SIZE];
	int top;
} StackType;

void init(StackType* s)
{
	s->top = -1;
}
int is_empty(StackType* s)
{
	return (s->top == -1);
}
int is_full(StackType* s) 
{
	return (s->top == (MAX_STACK_SIZE - 1));
}
void push(StackType* s, element item)
{
	if (is_full(s)) {
		fprintf(stderr, "Stack is Full\n");
		return;
	}
	else
		s->stack[++(s->top)] = item;
}
int pop(StackType* s)
{
	if (is_empty(s)) {
		fprintf(stderr, "Stack is Empty\n");
		return;
	}
	else
	{
		return s->stack[(s->top)--];
	}
}
void topo_sort(GraphType* g)
{
	int i;
	StackType s;
	GraphNode* node;

	int* in_degree = (int*)malloc(g->n * sizeof(int));
	for (i = 0; i < g->n; i++)
		in_degree[i] = 0;
	for (i = 0; i < g->n; i++) {
		GraphNode* node = g->adj_list[i];
		while(node != NULL) {
			in_degree[node->vertex]++;		// 정점 i가 가리키고 있는 노드의 진입차수 1 증가
			node = node->link;
		}
	}
	init(&s);
	for (i = 0; i < g->n; i++) {
		if (in_degree[i] == 0)
			push(&s, i);
	}
	int w;
	while (!is_empty(&s))
	{
		w = pop(&s);
		printf("정점 %d -> ", w);

		node = g->adj_list[w];
		while (node != NULL) {
			int u = node->vertex;
			in_degree[u]--;
			if (in_degree[u] == 0) push(&s, u);
			node = node->link;
		}
	}
	free(in_degree);
	printf("\n");
}
int main()
{
	GraphType g;

	graph_init(&g);
	insert_vertex(&g, 0);
	insert_vertex(&g, 1);
	insert_vertex(&g, 2);
	insert_vertex(&g, 3);
	insert_vertex(&g, 4);
	insert_vertex(&g, 5);

	insert_edge(&g, 0, 2);
	insert_edge(&g, 0, 3);

	insert_edge(&g, 1, 3);
	insert_edge(&g, 1, 4);

	insert_edge(&g, 2, 3);
	insert_edge(&g, 2, 5);

	insert_edge(&g, 3, 5);

	insert_edge(&g, 4, 5);

	topo_sort(&g);
	return 0;
}

결과

 

728x90
반응형
저작자표시 비영리 변경금지 (새창열림)

'자료구조' 카테고리의 다른 글

트리 - 이진트리 정의/ 성질/ 분류/ 표현법  (1) 2022.02.02
트리 - 개념/ 용어정리  (0) 2022.02.01
그래프 - Floyd 알고리즘  (2) 2022.01.31
그래프 - Dijkstra 알고리즘  (0) 2022.01.31
그래프 - 최단경로  (0) 2022.01.31
'자료구조' 카테고리의 다른 글
  • 트리 - 이진트리 정의/ 성질/ 분류/ 표현법
  • 트리 - 개념/ 용어정리
  • 그래프 - Floyd 알고리즘
  • 그래프 - Dijkstra 알고리즘
heeya16
heeya16
개발 공부 냠냠
  • heeya16
    개발자 희야
    heeya16
  • 전체
    오늘
    어제
    • 분류 전체보기 (105)
      • 코딩테스트 (66)
        • 프로그래머스 (38)
        • SWEA (2)
        • BAEKJOON (26)
      • 알고리즘 (7)
      • 자료구조 (19)
      • 프로젝트 (5)
      • 취업 주르륵 (2)
      • 데이터베이스 (0)
      • IT지식 (2)
  • 블로그 메뉴

    • 홈
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    1003
    10448
    10773
    10월
    10진수
    11047
    11399
    11403
    11866
    1449
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.0
heeya16
그래프 - 위상정렬
상단으로

티스토리툴바