본문 바로가기
Computer Science/알고리즘

[알고리즘] ep5-2+) DFS, BFS 구현

by 클레어몬트 2024. 7. 22.

문제1) DFS 구현

입력으로 주어지는 그래프의 DFS 순회 결과를 출력하는 프로그램을 작성하시오.

 

입력 그래프의 성질:

  • n (1 ≤ n ≤ 100) 개의 정점과 m (1 ≤ m ≤ 1,000) 개의 간선으로 구성
  • 정점은 1 ~ n 사이의 정수로 번호가 매겨져 있고, 정점의 번호는 모두 다름
  • 모든 간선은 무방향 간선이고, 한 정점에서 임의의 다른 정점으로 가는 경로는 반드시 존재

구현 조건:

  • 그래프는 인접리스트 구조를 사용하여 표현해야 한다.
  • 인접 정점의 조사 순서: 정점 u의 인접 정점(or 부착 간선)들을 번호가 작은 정점부터 조사한다. (즉, 아래 DFS 의사 코드의 for 문에서 인접 정점들을 번호가 작은 정점부터 큰 순서대로 조사하라. 조사 순서에 따라 방문 결과가 달라질 수 있음에 유의할 것)

입력:

  • 첫 줄에 정점의 개수 n, 간선의 개수 m, 순회 시작 정점 번호 s가 주어진다.
  • 이후 m개의 줄에 한 줄에 하나씩 간선의 정보(간선의 양 끝 정점 번호)가 주어진다.
  • 간선은 임의의 순서로 입력되고, 중복 입력되는 간선은 없다.
  • (무방향 간선이므로 간선 (u, v)와 (v, u)는 동일한 간선으로 취급) 

 출력:

  • 출발정점 s에서 출발하는 DFS의 방문 순서대로 정점 번호를 출력한다.

 

test case1
test case1의 결과

 

test case2
test case2의 결과

 

 

(전체코드)

// DFS: 인접리스트 - 연결리스트 (재귀)
#include <stdio.h>
#include <stdlib.h>

typedef struct INCIDENCE {
    int edgeIdx;
    struct INCIDENCE* next;
} INCIDENCE;

typedef struct VERTEX {
    int num;
    int label;
    INCIDENCE* incidence;
} VERTEX;

typedef struct EDGE {
    int u, v;
    int label;
} EDGE;

typedef struct GRAPH {
    VERTEX* vertex; // 정점 배열 (편의를 위해 index를 1부터 시작)
    EDGE* edge; // 간선 배열 (최대 간선 개수: 6 + 5 + 4 + 3 + 2 + 1 = 21)
    int vertexSize; // 정점의 수
    int curEdgeSize; // 현재 간선의 개수
} GRAPH;

GRAPH* graph;

void initGraph(int n, int m);
INCIDENCE* getIncidence(int edgeIdx);
void insertIncidence(int u, int v, INCIDENCE* incidence);
void insertEdge(int u, int v);
void DFS(int start);
void rDFS(int start);
void freeGraph();

int main() {
    int n, m, start;
    int u, v;

    scanf("%d %d %d", &n, &m, &start);
    initGraph(n, m);
    for (int i = 0; i < m; i++) {
        scanf("%d %d", &u, &v);
        insertEdge(u, v);
    }

    DFS(start);

    freeGraph();
    return 0;
}

void initGraph(int n, int m) {
    graph = (GRAPH*)malloc(1 * sizeof(GRAPH));
    graph->vertexSize = n;
    graph->curEdgeSize = 0;

     // 정점 배열을 1부터 사용하기 위해 n + 1크기로 할당
    graph->vertex = (VERTEX*)malloc((n + 1) * sizeof(VERTEX));
    graph->edge = (EDGE*)malloc(m * sizeof(EDGE));
    for (int i = 1; i <= graph->vertexSize; i++) {
        graph->vertex[i].incidence = NULL;
        graph->vertex[i].num = i;
    }
}

INCIDENCE* getIncidence(int edgeIdx) {
    INCIDENCE* node = (INCIDENCE*)malloc(1 * sizeof(INCIDENCE));
    node->next = NULL;
    node->edgeIdx = edgeIdx;
    return node;
}

void insertIncidence(int u, int v, INCIDENCE* incidence) {
    EDGE* edge = graph->edge;
    VERTEX* vertex = graph->vertex;
    INCIDENCE* ptr = vertex[u].incidence;
    if (ptr == NULL) { // 첫 노드
        vertex[u].incidence = incidence;
        return;
    }

    // 오름차순 삽입
    int other;
    if (edge[ptr->edgeIdx].u == u) {
        other = edge[ptr->edgeIdx].v;
    }
    else {
        other = edge[ptr->edgeIdx].u;
    }

    if (other > v) { // head에서 교환
        incidence->next = ptr;
        vertex[u].incidence = incidence;
        return;
    }
    while (ptr->next != NULL) {
        if (edge[ptr->next->edgeIdx].u == u) {
            other = edge[ptr->next->edgeIdx].v;
        }
        else {
            other = edge[ptr->next->edgeIdx].u;
        }

        if (other > v) {
            break;
        }
        ptr = ptr->next;
    }
    incidence->next = ptr->next;
    ptr->next = incidence;
}

void insertEdge(int u, int v) {
    EDGE* edge = graph->edge;

    // edge list에 중복 검사 및 추가
    for (int i = 0; i < graph->curEdgeSize; i++) {
        if (edge[i].u == u && edge[i].v == v ||
        edge[i].u == v && edge[i].v == u) {
            return;
        }
    }

    int cur = graph->curEdgeSize;

    edge[cur].u = u;
    edge[cur].v = v;

    // incidence node 생성 및 추가
    INCIDENCE* incidence1 = getIncidence(cur);
    INCIDENCE* incidence2 = getIncidence(cur);
    insertIncidence(u, v, incidence1);
    if (u != v) {
        insertIncidence(v, u, incidence2);
    }

    graph->curEdgeSize++;
}

void DFS(int start) {
    // 모든 정점을 방문하지 않은 상태로 초기화
    for (int i = 1; i <= graph->vertexSize; i++) {
        graph->vertex[i].label = 0;
    }
    // 모든 간선을 방문하지 않은 상태로 초기화
    for (int i = 0; i < graph->curEdgeSize; i++) {
        graph->edge[i].label = 0;
    }

    rDFS(start);
}

void rDFS(int cur) {
    EDGE* edge = graph->edge;
    graph->vertex[cur].label = 1; // 방문 표시
    printf("%d\n", cur); // 현재 정점 출력

    INCIDENCE* temp = graph->vertex[cur].incidence;
    int other;
    while (temp != NULL) {
        if (graph->edge[temp->edgeIdx].label == 0) { // 방문하지 않은 간선인 경우
            if (edge[temp->edgeIdx].u == graph->vertex[cur].num) {
                other = edge[temp->edgeIdx].v;
            }
            else {
                other = edge[temp->edgeIdx].u;
            }
            
            if (graph->vertex[other].label == 0) { // 방문하지 않은 정점인 경우
                graph->edge[temp->edgeIdx].label = 1; // 간선 방문 표시
                rDFS(other);
            }
        }
        temp = temp->next;
    }
}

void freeGraph() {
    for (int i = 1; i <= graph->vertexSize; i++) {
        INCIDENCE* cur = graph->vertex[i].incidence;
        while (cur != NULL) {
            INCIDENCE* temp = cur;
            cur = cur->next;
            free(temp);
        }
    }
    free(graph->vertex);
    free(graph->edge);
    free(graph);
}

/*
5 7 1
1 2
1 4
5 1
3 5
4 3
3 1
2 3

8 12 7
1 2
2 4
4 7
3 6
6 1
7 6
7 8
1 3
2 7
1 4
2 5
7 5
*/

 

 

 

 

 

 

 

 

문제2) BFS 구현

입력으로 주어지는 그래프의 BFS 순회 결과를 출력하는 프로그램을 작성하시오.

 

입력 그래프의 성질:

  • 문제 1과 동일

구현 조건:

  • 그래프는 인접행렬 구조를 사용하여 표현해야 한다.
  • 인접 정점의 조사 순서: 문제 1과 동일하게 정점의 인접 정점(or 부착 간선)들을 번호가 작은 정점부터 조사한다.

입력: 문제 1과 동일

 

출력: 출발정점 s에서 출발하는 BFS의 방문 순서대로 정점 번호를 출력한다.

 

 

test case1
test case1의 결과
test case2
test case2의 결과

 

 

(전체코드)

// BFS: 인접행렬 - 배열
#include <stdio.h>
#include <stdlib.h>

typedef struct EDGE {
	int u, v;
	int label; // 1 or -1
} EDGE;

typedef struct VERTEX {
	int num;
	int label; // 1 or -1
} VERTEX;

typedef struct GRAPH {
	VERTEX* vertex;
	EDGE* edge;
	int** adjacency; // 인접행렬
	int vertexSize;
	int curEdgeSize;
} GRAPH;

GRAPH* graph;

void initGraph(int n, int m);
void insertEdge(int u, int v);
void BFS(int start);
void freeGraph(int n);

int main() {
	int n, m, start;
	int u, v;

	scanf("%d %d %d", &n, &m, &start);
	initGraph(n, m);
	for (int i = 0; i < m; i++) {
		scanf("%d %d", &u, &v);
		insertEdge(u, v);
	}

	BFS(start);

	return 0;
}

void initGraph(int n, int m) {
    graph = (GRAPH*)malloc(1 * sizeof(GRAPH));
	graph->vertexSize = n;
	graph->curEdgeSize = 0;

	graph->vertex = (VERTEX*)malloc(n * sizeof(VERTEX));
	graph->edge = (EDGE*)malloc(m * sizeof(EDGE));
	graph->adjacency = (int**)malloc(n * sizeof(int*));
	for (int i = 0; i < n; i++) {
		graph->adjacency[i] = (int*)malloc(n * sizeof(int));
	}

	for (int i = 0; i < n; i++) {
		graph->vertex[i].num = i + 1;
		graph->vertex[i].label = -1;
		for (int j = 0; j < n; j++) {
			graph->adjacency[i][j] = -1; // 가중치가 0일 수 있으니 -1로 초기화
		}
	}
	for (int i = 0; i < m; i++) {
		graph->edge[i].label = -1;
	}
}

void insertEdge(int u, int v) {
	// edge list에 중복 검사 및 추가
	EDGE* edge = graph->edge;
	for (int i = 0; i < graph->curEdgeSize; i++) {
		if (edge[i].u == u && edge[i].v == v
            || edge[i].u == v && edge[i].v == u) {
            return;
		}
	}

	int idx = graph->curEdgeSize;
	edge[idx].u = u;
	edge[idx].v = v;
	edge[idx].label = 1;

	// 인접행렬 (무방향 그래프이므로 배열 양쪽에 추가)
	graph->adjacency[u - 1][v - 1] = idx;
	graph->adjacency[v - 1][u - 1] = idx;

	graph->curEdgeSize++;
}

void BFS(int start) {
    int front = 0, rear = 0;
    int* queue = (int*)malloc(graph->vertexSize * sizeof(int));
    queue[rear++] = start;
    graph->vertex[start - 1].label = 1;

    printf("%d\n", start);
    while (front < rear) { // 큐가 빌 때까지 반복
        int cur = queue[front++]; // 현재 방문할 정점을 큐에서 꺼낸다
        for (int i = 0; i < graph->vertexSize; i++) {
            if (graph->adjacency[cur - 1][i] != -1 && graph->vertex[i].label == -1) {
				// 인접행렬에 체크가 되어있고 방문라벨이 안되어 있으면 출력
                printf("%d\n", graph->vertex[i].num);

                graph->vertex[i].label = 1;
                queue[rear++] = graph->vertex[i].num;
            }
        }
    }

    free(queue);
}

void freeGraph(int n) {
    for (int i = 0; i < n; i++) {
        free(graph->adjacency[i]);
    }
    free(graph->adjacency);
    free(graph->vertex);
    free(graph->edge);
    free(graph);
}

/*
6 9 1
3 5
1 3
4 1
2 3
3 4
6 4
3 6
1 2
2 5

8 12 4
1 2
2 4
4 7
3 6
6 1
7 6
7 8
1 3
2 7
1 4
2 5
7 5
*/

 

 

 

 

 

 

 

 

 

 

 

 

출처 및 참고: 알고리즘-원리와 응용(국형준교수님), Algorithm Design(Jon Kleinberg, Eva Tardos), Foundations of Algorithms using C++(Richard Neapolitan)