본문 바로가기

Study/Data Structure

[자료구조 스터디] chap.10 그래프 I

10.1 그래프란?

10.2 그래프의 정의와 용어

10.3 그래프의 표현 방법

10.4 그래프의 탐색

10.5 깊이 우선 탐색

10.6 너비 우선 탐색 


 

10.1 그래프란?

 

그래프(graph)

- 연결되어 있는 객체 간의 관계를 표현하는 자료구조 

- ex) 트리, 전기회로 소자 연결, 지도 도시 연결

 

오일러 정리 

- 오일러 문제: 모든 다리 "한번씩만" 건너서 처음 출발 장소로 return 

- bridge = edge or link 간선

- region = vertex or node 정점

- 모든 정점에 연결된 간선의 수가 짝수이면 오일러 경로 존재!!

 

 

따라서 이건 오일러 경로 존재 x

 


 

10.2 그래프의 정의와 용어 

 

그래프 정의 

- G = (V, E)

  • 정점 (vertices) = node

= 여러가지 특성 가질 수 있는 객체를 의미

- V(G) : 그래프 G의 정점들 집합

- |V| = 총 정점 개수 

 

  • 간선 (edge) = link

= 정점들 간의 관계를 의미

- E(G) : 그래프 G의 간선들 집합

- |E| = 총 간선 개수 

 

 

그래프 종류 

  • undirected graph 무방향 그래프 

 

V(G1) = {0, 1, 2, 3}

E(G1) = {(0,1), (0,2), (0,3), (1,2), (2,3)} 

ㄴ 방향 x  undirected graph

 

- 무방향 그래프 정점의 차수 : 하나의 정점에 연결된 다른 정점 수 

ㄴ 정점 0의 차수: 3 

∑ 정점차수 = 간선수 X 2

 

- 인접정점 : 하나의 정점에서 간선에 의해 직.접. 연결된 정점

ㄴ 정점 0의 인접 정점: 정점1, 2, 3

 

  • directed graph 방향 그래프 

 

V(G3) = {0, 1, 2}

E(G3) = {<0,1>, <1,0>,<1,2>} 

ㄴ 방향 ㅇ  directed graph

 

- 방향 그래프 정점의 차수

=> 진입 차수: 외부에서 오는 간선의 수 / 진출 차수: 외부로 향하는 간선의 수

정점 1의 차수 : 내차수(in-degree) 1 , 외차수(out-degree) 2 

∑ 진입 or 진출 차수 = 간선수 

(the higher degree, the higher connectivity)

 

 

  • weighted graph 가중치 그래프 (=네트워크)

- 간선에 비용(cost)나 가중치(weight)가 할당

 

<== ex)

정점: 각 도시

간선: 도시 연결하는 도로

가중치: 도로의 길이 

 

 

  • subgraph 부분 그래프 

 

- V(G)와 E(G)의 부분집합으로 이루어진 그래프 

- 그래프 G1의 부분 그래프들

 

V(S) ⊆ V(G)

E(S) ⊆ E(G)

 

 

 

 

그래프의 경로(path) 

 

- 무방향 그래프의 정점 s로부터 정점 e까지의 경로 

=> 정점의 나열 s, v1, v2, ..., vk, e

=> 나열된 정점들 간에 반드시 간선 (s,v1), (v1, v2), ..., (vk, e) 존재 

 

- 단순 경로(simple path) : 경로 중에서 반복되는 노드 X 경로

- 사이클(cycle) : 단순 경로의 시작 정점 = 종료 정점 

 

- 단순 사이클(simple cycle) : 시작점 = 종료점 경로 , 반복 노드 X

**오일러사이클 (모든 간선 한번만 건너 처음으로 돌아오는) 은 simple cycle (X)

**해밀턴사이클 (모든 정점 한번만 방문하여 제자리 돌아오는)은 simple cycle (O)

 

 

 그래프의 연결 정도

- 연결 그래프(connected graph) : 무방향 그래프의 모든 정점 쌍에 대하여 항상 경로 존재 

- bridge : 없어지면 비연결 그래프가 되는 간선 

 

- 완전 그래프(complete graph) : 모든 정점이 서로 연결되어 있는 그래프 

=> n개의 정점 무방향 완전그래프의 간선 수 : n X (n-1) / 2 

 

 

그래프 표현 방법 2가지 

  • 인접 행렬 방법 (2차원 배열 사용)

if ( 간선 (i, j) 존재) M[i][j] = 1;

else                       M[i][j] = 0;

 

=> (b) 

degree(i) = ∑(j=0~2) M[i][j]

 

=> (c)

out_degree(i) = ∑(j=0~2) M[i][j]

in_degree(j) = ∑(i=0~2) M[i][j]

 

인접행렬의 크기 = |V|^2

인접행렬 값 1 항의 개수 = 2|E| (무방향) or |E| (방향)

 

 

인접행렬 알고리즘 

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

#define MAX_VERTICES 50 //최대 50개 노드
typedef struct GraphType
{
   int n; //정점 개수 n=|V| <= MAX_VERTICES(50)
   int adj_mat[MAX_VERTICES][MAX_VERTICES];
} GraphType;


//그래프 초기화
void init(GraphType* g)
{
   int r,c;
   g->n = 0; //정점 총 개수 0개
   for (r=0; r < MAX_VERTICES; r++)
     for (c=0; c < MAX_VERTICES; c++)
        g->adj_mat[r][c] = 0; //간선이 전혀 없다고 초기화 
}


//정점 삽입 연산 
void insert_vertex(GraphType* g, int v)
{
   if (((g->n) + 1) > MAX_VERTICES) //정점개수+1 이 50보다 작으면
   {
      fprintf(stderr, "그래프: 정점의 개수 초과");
      return;
   }
   g->n++;
}


//간선 삽입 연산
void insert_edge(Graphtype* g, int start, int end)
{
   if (start >= g->n || end >= g->n)
   {
      fprintf(stderr,"그래프: 정점 번호 오류");
      return;
   }
   g->adj_mat[start][end]=1; 
   g->adj_mat[end][start]=1; //undirected graph에서 
}


//main 함수 
void main() 
{
   GraphType* g;
   g = (GraphType*)malloc(sizeof(GraphType));
   init(g);
   for(int i=0; i < 4; i++) 
      insert_vertex(g,i);
   
   insert_edge(g,0,1);
   insert_edge(g,0,2);
   insert_edge(g,0,3);
   insert_edge(g,1,2);
   insert_edge(g,2,3);
   print_adj_mat(g);

   free(g);
}

 

 

  • 인접 리스트 방법 (연결 리스트 사용)

- 각 정점에 인접한 정점들을 연결리스트로 표현 

- 인접리스트 총 노드(data+link) 개수 = 2|E| (무방향) or |E| (방향)

 

인접리스트 알고리즘 

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

#define MAX_VERTICES 50 //최대 50개 노드
typedef struct GraphNode
{
   int vertex; 
   struct GraphNode *link;
} GraphNode;

typedef struct GraphType
{
   int n; //정점 개수 n=|V| <= MAX_VERTICES(50)
   GraphNode* adj_list[MAX_VERTICES];
} GraphType;


//그래프 초기화
void init(GraphType* g)
{
   int v;
   g->n = 0; //정점 총 개수 0개
   for (v=0; v < MAX_VERTICES; v++)
      g->adj_list[v] = NULL; 
}


//정점 삽입 연산 
void insert_vertex(GraphType* g, int v)
{
   if (((g->n) + 1) > MAX_VERTICES) //정점개수+1 이 50보다 작으면
   {
      fprintf(stderr, "그래프: 정점의 개수 초과");
      return;
   }
   g->n++;
}


//간선 삽입 연산, v를 u의 인접 리스트에 삽입 
void insert_edge(Graphtype* g, int u, int v)
{
   GraphNode* node;
   if (u >= g->n || v >= g->n)
   {
      fprintf(stderr,"그래프: 정점 번호 오류");
      return;
   }
   node = (GraphNode*)malloc(sizeof(GraphNode));
   node->vertex = v;
   node->link = g->adj_list[u];
   g->adj_list[u] = node;
}


void print_adj_list(GraphType* g)
{
  for (int i=0; i<g->n; i++) {
    GraphNode* p = g->adj_list[i];
    printf("정점 %d의 인접 리스트 ", i);
    while (p!=NULL) {
       printf("-> %d ", p->vertex);
       p=p->link;
    }
  }
}


//main 함수 
void main() 
{
   GraphType* g;
   g = (GraphType*)malloc(sizeof(GraphType));
   init(g);
   for(int i=0; i < 4; i++) 
      insert_vertex(g,i);
   
   insert_edge(g,0,1);
   insert_edge(g,1,0);
   insert_edge(g,0,2);
   insert_edge(g,2,0);
   insert_edge(g,0,3);
   insert_edge(g,3,0);
   insert_edge(g,1,2);
   insert_edge(g,2,1);
   insert_edge(g,2,3);
   insert_edge(g,3,2);
   print_adj_list(g);

   free(g);
   return 0;
}

 

실행결과 

정점 0의 인접 리스트 -> 3 -> 2 -> 1

정점 1의 인접 리스트 -> 2 -> 0

정점 2의 인접 리스트 -> 3 -> 1 -> 0

정점 3의 인접 리스트 -> 2 -> 0

 

 

인접 행렬 VS 인접 리스트

 


 

10.4 그래프 탐색 

- 하나의 정점에서 시작하여 차례대로 모든 정점들을 한번씩 방문

 

그래프 탐색 알고리즘 2가지 -> DFS, BFS

 


10.5 깊이 우선 탐색 (DFS - depth first search)

 

 

- 한 방향(source node에서 멀어지는 방향)으로 갈 수 있을 때까지 가다가 더 이상 못가면 가장 가까운 갈림길로 돌아와서(후입선출)이 곳으로부터 다른 방향으로 다시 탐색 진행 

 

- 두가지 방법 구현 가능 (1. 순환 호출, 2. 명시적 스택으로 저장하고 꺼내는것)

- 되돌아가기 위해서는 스택 필요 (순환함수 호출로 묵시적인 스택 이용)

 

 

 

 

 

depth_first_search(v): //v는 source node
1. v를 방문되었다고 표시; //필요한 연산(printf) 수행
2. for all u ∈ (v에 인접한 정점) do
3.        if (u가 아직 방문되지 않았으면) 
4.            then depth_first_search(u)    //v의 인접 행렬인 u가 다시 새로운 소스노드가 되는 것!!      

**dfs-tree unique (X)

 

DFS 인접 행렬 버전 프로그램 ========>> O(n^2)

//인접 행렬로 표현한 그래프에 대한 깊이 우선 탐색 
void dfs_mat(GraphType* g, int v) 
{
   int w;
   visted[v] = TRUE; //정점 v 방문 표시
   printf("정점 %d -> ", v) //방문한 정점 출력
   
   for (w=0; w < g->n; w++) //인접 정점 탐색
      if (g->adj_mat[v][w] && !visited[w])
          dfs_mat(g,w); //정점 w에서 DFS 새로 시작 
}


//main 함수 
int main(void)
{
   GraphType* g;
   g = (GraphType*)malloc(sizeof(GraphType));
   init(g);
   for (int i=0; i < 4; i++)
      insert_vertex(g,i);
      
   insert_edge(g,0,1);
   insert_edge(g,0,2);
   insert_edge(g,0,3);
   insert_edge(g,1,2);
   insert_edge(g,2,3);
   
   printf("깊이 우선 탐색\n");
   dfs_mat(g,0);
   printf("\n");
   free(g);
   return 0;
}

 

실행결과

깊이 우선 탐색

정점 0 -> 정점 1 -> 정점 2 -> 정점 3 ->  

 

즉,

아직 방문 안된 인접노드(v)가 있다면 push(v) 하고 

아직 방문 안된 인접노드가 없다면 pop(return) 

 

DFS 인접 리스트 버전 프로그램 ========>> O(n+e)

연결리스트의 노드는 데이터필드(인접 정점 번호 저장) + 링크 필드(다음 인접 정점 가리키는 포인터 저장)로 이루어짐

//struct 정의 추가
typedef struct GraphNode {
  int vertex; 
  struct GraphNode* link;
} GraphNode;

typedef struct GraphType {
  int n;
  GraphNode* adj_list[MAX_VERTICES]l
}GraphType;


int visited[MAX_VERTICES];


//인접 리스트로 표현한 그래프에 대한 깊이 우선 탐색 
void dfs_list(GraphType* g, int v) 
{
   GraphNode* w;
   visted[v] = TRUE; //정점 v 방문 표시
   printf("정점 %d -> ", v) //방문한 정점 출력
 
   for (w = g->adj_list[v]; w; w = w->link) //인접 정점 탐색
      if (!visited[w->vertex])
          dfs_list(g,w->vertex); //정점 w에서 DFS 새로 시작 
}

 

 

명시적인 스택을 이용한 깊이 우선탐색 구현 

- 스택을 하나 생성해서 시작 정점을 스택에 넣고, 다시 꺼내서 탐색.

DFS - iterative(G, v):
1. 스택 S 생성
2. S.push(v)
3. while (not is_empty(S)) do 
4.       v = S.pop()
5.       if (v가 방문되지 않았으면)
6.             v를 방문되었다고 표시 //출력
7.             for all u ∈ (v에 인접한 정점) do
8.                    if (u가 아직 방문되지 않았으면)
9.                         S.push(u)

 


10.6 너비 우선 탐색 (BFS - breadth first search) 

 

 

 

- 시작 정점으로부터 가까운 정점을 (선입선출)  먼저 방문하고 멀리 떨어져 있는 정점을 나중에 방문하는 순회 방법

 

- 로 구현 

 

 

 

 

 

 

breadth_first_search(v):
1. v를 방문되었다고 표시; //visted[v] = TRUE;
2. 큐 Q에 정점 v를 삽입;
3. while (Q가 공백이 아니면) do
4.       Q에서 정점 w를 삭제;
5.       for all u ∈ (w에 인접한 정점) do
6.                if (u가 아직 방문되지 않았으면) 
7.                           then u를 큐에 삽입;
8.                                   u를 방문되었다고 표시; 

 

BFS 인접 행렬 버전 프로그램 ========>> O(n^2)

//인접 행렬로 표현한 그래프에 대한 너비 우선 탐색 
void bfs_mat(GraphType* g, int v) 
{
   int w;
   QueueType q;
   
   queue_init(&q); //큐 초기화 
   visted[v] = TRUE; //정점 v 방문 표시
   printf("정점 %d -> ", v) //방문한 정점 출력
   enqueue(&q, v); //시작 정점을 큐에 저장 
   
   while (!is_empty(&q)) {
      v = dequeue(&q); //큐에 정점 추출
      for (w=0; w<g->n; w++) //인접 정점 탐색
         if (g->adj_mat[v][w] && !visited[w]) {
             visited[w] = TRUE;
             printf("%d 방문 -> ", w); 
             enqueue(&q, w); //방문한 정점을 큐에 저장 
         }
   }
}


//main 함수 
int main(void)
{
   GraphType* g;
   g = (GraphType*)malloc(sizeof(GraphType));
   graph_init(g);
   for (int i=0; i < 6; i++)
      insert_vertex(g,i);
      
   insert_edge(g,0,2);
   insert_edge(g,2,1);
   insert_edge(g,2,3);
   insert_edge(g,0,4);
   insert_edge(g,4,5);
   insert_edge(g,1,5);
   
   printf("너비 우선 탐색\n");
   bfs_mat(g,0);
   printf("\n");
   free(g);
   return 0;
}

 

실행결과

너비 우선 탐색

0 방문 -> 2 방문 -> 4 방문 -> 1 방문 ->  3 방문 -> 5 방문 ->

 

BFS 인접 리스트 버전 프로그램 ========>> O(n+e)

//struct 정의 추가
typedef struct GraphNode {
  int vertex; 
  struct GraphNode* link;
} GraphNode;

typedef struct GraphType {
  int n; //|V|
  GraphNode* adj_list[MAX_VERTICES]
}GraphType;


//인접 리스트로 표현한 그래프에 대한 너비 우선 탐색 
void bfs_list(GraphType* g, int v) 
{
   GraphNode* w;
   Queuetype q;
   
   init(&q); //큐 초기화
   visted[v] = TRUE; //정점 v 방문 표시
   printf("%d 방문 -> ", v) //방문한 정점 출력
   enqueue(&q, v); //시작정점을 큐에 저장
   while (!is_empty(&q)) {
     v = dequeue(&q); //큐에 저장된 정점 선택
     for (w=g->adj_list[v]; w; w=w->link) //인접 정점 탐색
        if (!visited[w->vertex]) { //미방문 정점 탐색 
            visited[w->vertex] = TRUE; //방문 표시
            printf("%d 방문 -> ", w->vertex); 
            enqueue(&q, w->vertex); //정점을 큐에 삽입 
        }
  }
}

 

 

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

자료구조 전체  (0) 2023.12.08
[자료구조 스터디] chap.13 탐색  (0) 2023.12.01
[자료구조 스터디] chap.12 정렬  (0) 2023.11.21
[자료구조 스터디] chap.11 그래프 II  (0) 2023.11.16