본문 바로가기
C/알고리즘

강한 결합 요소(Strongly Connected Components)

by 덤더리덤떰 2023. 8. 15.

강한 결합 요소

: 그래프 안에서 강하게 결합된 정점 집합

 

cf> scc 활용

: scc마다 묶으면 cyclic 존재하지 않는 방향 그래프이기에 위상정렬 수행 가능하다


1. 알고리즘

  • 코사라주 알고리즘(Kosaraju's Algorithm)
  • 타잔 알고리즘(Tarjan's Algorithm)

(1) 타잔 알고리즘

: 모든 정점에 대해 DFS(Depth-First Search, 깊이 우선탐색)을 수행하여 scc 찾는 알고리즘

: 부모로 돌아올 수 있어야 scc 성립

: 각 정점마다의 부모 노드가 무엇인지 기록(초기값은 자기자신) => union-find algorithm 개념과 비슷

 

(2) 작동 방식 

1) 정점번호가 낮은 순으로 DFS 탐색 (이때, 연결리스트 통해 오름차순으로 정렬하였음)

2) 현재 정점의 부모 노드 설정(초깃값 : 자기자신)하고 stack에 push

3) 현재 정점으로부터 도달가능한 정점으로 DFS 수행 (연결되어있고, finished==false인 정점)

=> finished : DFS 수행하다가 stack에서 pop하면서 true로 변경 , visited : DFS 수행 시작 여부

4) 재귀함수 통해 부모 값을 계속해서 반환하며 update한다. 그러다가 최종적으로 p[x] == x인 정점이 나오게 되면 pop한 데이터가 x가 될 때까지 stack에서 pop

(이때, finished 배열 true로 변경)

5) 따로 scc 그룹 저장할 배열 만들어놓고 pop한 데이터들(하나의 scc그룹) 저장

6) DFS 완전히 끝날때까지 과정 반복

 

 

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
#include <memory.h>
#define Min(a,b) ((a)>(b)?(b):(a))
#define VERTEX_SIZE 101



typedef struct _NODE_ {
	int data;
	struct _NODE_* link;
}NODE;

typedef struct _GRAPH_TYPE_ {
	NODE* head[VERTEX_SIZE];
	int n; // 정점의 갯수
}GRAPH_TYPE;

typedef struct _STACK_ {
	int top;
	int stack[VERTEX_SIZE];
}STACK;

void init(STACK* st) {
	st->top = -1;
	memset(st->stack, 0, VERTEX_SIZE * sizeof(int));
}

int IsEmpty(STACK* st) {
	return st->top == -1;
}

int IsFull(STACK* st) {
	return st->top == VERTEX_SIZE;
}
void push(STACK* st, int item) {
	if (IsFull(st)) {
		printf("더이상 스택에 push 할 수 없습니다\n");
		return;
	}
	else {
		st->top++;
		st->stack[st->top] = item;
	}
}

int pop(STACK* st) {
	if (IsEmpty(st)) {
		printf("pop할 데이터가 존재하지 않습니다\n");
		return;
	}
	else {
		int popdata = st->stack[st->top];
		st->top--;
		return popdata;
	}
}

void PrintStack(STACK* st) {
	printf("\n스택 출력: ");
	for (int i = 0; i <= st->top; i++) {
		printf("%d ", st->stack[i]);
	}
	printf("\n");
}

int DFS(GRAPH_TYPE* graph, STACK *st,int vertex,bool * finished, int * parent, bool * visited) {
	visited[vertex] = true;
	parent[vertex] = vertex;
	push(st, vertex);
	PrintStack(st);
	static int cnt = 1;
	printf("\n%d탐색시작",vertex);

	NODE* curNode = graph->head[vertex];
	while (curNode != NULL) {
		if (visited[curNode->data] != true) {
			int new =DFS(graph, st, curNode->data, finished, parent, visited);
			if (parent[vertex] > new) {
				parent[vertex] = new;
			}
		}
		else {
			if (finished[curNode->data] != true) {
				parent[vertex] = parent[curNode->data];
			}
		}
		curNode = curNode->link;
	}

	printf("\n부모값 출력: ");
	for (int i = 1; i <= graph->n; i++) {
		printf("%d ", parent[i]);
	}
	printf("\n");

	if (parent[vertex] == vertex) {
		printf("\n%d번째 scc : {", cnt++);
		while (!IsEmpty(st)) {
			int popdata = pop(st);
			finished[popdata] = true;
			printf("%d ", popdata);
			if (popdata == vertex) {
				break;
			}
		}
		printf("}\n");
	}
	return parent[vertex];
}

void SCC(GRAPH_TYPE * graph,STACK *st) {
	bool* finished = (bool*)calloc((graph->n) + 1, sizeof(bool));
	assert(finished != NULL);
	int* parent = (int*)calloc((graph->n) + 1, sizeof(int));
	assert(parent != NULL);
	bool* visited = (bool*)calloc((graph->n) + 1, sizeof(bool));
	assert(visited != NULL);

	for (int i = 1; i <= graph->n; i++) {
		if (finished[i] != true) {
			DFS(graph, st, i, finished, parent, visited);
		}
	}
}



GRAPH_TYPE* makeGraph() {
	GRAPH_TYPE* graph = (GRAPH_TYPE*)calloc(1, sizeof(GRAPH_TYPE));
	assert(graph != NULL);
	return graph;
}

void InsertVertex(GRAPH_TYPE* g, int vertex) {
	if (g->n >= VERTEX_SIZE-1) {
		printf("더이상 정점을 삽입할 수 없습니다\n");
		return;
	}
	else {
		g->n++;
	}
}

void InsertEdge(GRAPH_TYPE* g, int u, int v) {
	if (u > g->n || v > g->n || u==v) {
		return;
	}
	else {
		NODE* newNode = NULL;
		newNode = (NODE*)calloc(1, sizeof(NODE));
		assert(newNode != NULL);
		newNode->data = v;
		newNode->link = NULL;
		if (g->head[u] == NULL) {
			g->head[u] = newNode;
		}
		else {
			NODE* curNode = g->head[u];
			NODE* preNode = curNode;
			while (curNode != NULL) {
				preNode = curNode;
				if (curNode->data > newNode->data) {
					break;
				}
				curNode = curNode->link;
			}
			newNode->link = preNode->link;
			preNode->link = newNode;
		}
	}
}

void PrintGraph(GRAPH_TYPE* g) {
	NODE* curNode = NULL;
	for (int i = 1; i <= g->n; i++) {
		curNode = g->head[i];
		printf("\n[Head: %d] ", i);
		while (curNode != NULL) {
			printf("-> %d ", curNode->data);
			curNode = curNode->link;
		}
	}
}

int main() {
	GRAPH_TYPE* graph = makeGraph();
	STACK* st = (STACK*)calloc(1, sizeof(STACK));
	assert(st != NULL);
	init(st);
	for (int i = 1; i <= 11; i++) {
		InsertVertex(graph, i);
	}

	InsertEdge(graph, 1, 2);
	InsertEdge(graph, 2, 3);
	InsertEdge(graph, 3, 1);
	InsertEdge(graph, 4, 2);
	InsertEdge(graph, 4, 5);
	InsertEdge(graph, 5, 7);
	InsertEdge(graph, 6, 5);
	InsertEdge(graph, 7, 6);
	InsertEdge(graph, 8, 5);
	InsertEdge(graph, 8, 9);
	InsertEdge(graph, 9, 10);
	InsertEdge(graph, 10, 11);
	InsertEdge(graph, 11, 3);
	InsertEdge(graph, 11, 8);


	/*InsertEdge(graph, 1, 5);
	InsertEdge(graph, 2, 3);
	InsertEdge(graph, 3, 2);
	InsertEdge(graph, 3, 1);
	InsertEdge(graph, 4, 3);
	InsertEdge(graph, 5, 4);
	InsertEdge(graph, 5, 6);
	InsertEdge(graph, 6, 7);
	InsertEdge(graph, 7, 6);
	InsertEdge(graph, 8, 10);
	InsertEdge(graph, 9, 7);
	InsertEdge(graph, 9, 8);
	InsertEdge(graph, 10, 9);*/

	PrintGraph(graph);
	SCC(graph,st);



	getchar();
	return 0;
}

'C > 알고리즘' 카테고리의 다른 글

스택 응용 : 미로문제(Maze problem)  (0) 2023.09.15
스택 응용(수식 계산)  (0) 2023.09.15
위상정렬(Topology sort)  (0) 2023.08.14
Floyd Washall Algorithm(플로이드 와샬 알고리즘)  (0) 2023.08.14
Dijkstra Algorithm[C]  (0) 2023.08.11