공부방

그래프 기본 본문

알고리즘/그래프

그래프 기본

코딩 화이팅 2023. 3. 27. 14:29

그래프

  • 아이템(사물 또는 추상적 개념)들과 이들 사이의 연결 관계 표현
  • 정점(Vertex)들의 집합과 이들을 연결하는 간선(Edge)들의 집합으로 구성된 자료구조
  • 선형자료구조(1:1)나 트리자료구조(1:N)로 표현하기 어려운 M:N관계를 표현

가중치 그래프(Weighted Graph) : 간선에 값이 있는 그래프

순환 그래프(Cycle graph): 사이클이 있는 그래프

완전 그래프

정점에 대해 가능한 모든 간선들을 가진 그래프

간선 의 수 : N(N-1)/2개

부분 그래프

원래 그래프에서 일부의 정점이나 간선을 제외한 그래프

인접

  • 두 개의 정점에 간선이 존재(연결됨)하면 서로 인접해 있다고 한다.
  • 완전 그래프에 속한 임의의 두 정점들은 모두 인접해 있다.

경로

간선들을 순서대로 나열한 것

간선 : (0,2),(2,4),(4,6)

정점 : 0-2-4-6

단순경로 : 경로 중 한 정점을 최대한 한번만 지나는 경로

0-2-4-6, 0-1-6

사이클 : 시작한 정점에서 끝나는 경로

1-3-5-1

그래프 표현 방법

간선의 정보를 저장하는 방식, 메모리나 성능을 고려해서 결정

인접 행렬

  • V*V(정점의 수) 크기의 2차원 배열을 이용하여 간선 정보를 저장
  • 배열의 배열
  • 두 정점을 연결하는 간선의 유무를 행렬로 표현
    • 행 번호와 열 번호는 그래프의 정점에 대응
    • 두 정점이 인접되어 있으면 1, 그렇지 않으면 0으로 표현
  • 무향 그래프
    i번째 행의 합=i번째 열의 합=Vi의 차수
  • 유향 그래프
    • 행 i의 합=Vi의 진출 차수
    • 열 i의 합=Vi의 진입 차수
import java.util.Scanner;

public class 그래프_01_인접행렬 {
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);

		int V = sc.nextInt(); // 정점의 개수를 입력을 받는다. (6 이다. 시작정점이 0번인지 / 1번인지)
		int E = sc.nextInt(); // 간선의 수가 입력 된다.

		int[][] adjArr = new int[V + 1][V + 1];

		// 간선을 입력을 받자.
		for (int i = 0; i < E; i++) {
			int st = sc.nextInt();
			int ed = sc.nextInt();

			//유향 그래프라면 20번라인은 필요없다.
			adjArr[st][ed] = 1;
			//무향그래프일때 아래도 같이 작성을 해준다.
			adjArr[ed][st] = 1;
			
			adjArr[st][ed] = adjArr[ed][st] = 1; //한줄적기 가넝
		}
	}
}

인접 행렬의 장점

  • 연결 여부 확인 빠름 : 인접 행렬에서 두 정점 간의 연결 여부를 확인하는데 상수 시간(O(1))이 소요됩니다. 배열 인덱스를 통해 바로 확인할 수 있기 때문입니다.
  • 구현이 간단하고 직관적 : 인접 행렬은 2차원 배열로 구현되어 있어 코드가 간결하고 이해하기 쉽습니다.

인접 행렬의 단점

  • 메모리 낭비 : 간선이 적은 희소 그래프의 경우 인접 행렬은 메모리 낭비가 클 수 있습니다. 이는 정점 수의 제곱에 해당하는 공간이 필요하기 때문입니다.
  • 간선 추가/삭제 비효율적 : 인접 행렬에서 간선을 추가하거나 삭제하는 작업은 느리고 복잡할 수 있습니다.

인접 리스트

  • 각 정점마다 해당 정점으로 나가는 간선의 정보를 저장
  • 각 정점에 대한 인접 정점들을 순차적으로 표현
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Scanner;
import java.util.concurrent.ConcurrentHashMap;

public class 그래프_02_인접리스트 {
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);

		int V = sc.nextInt(); // 정점의 개수를 입력을 받는다. (6 이다. 시작정점이 0번인지 / 1번인지)
		int E = sc.nextInt(); // 간선의 수가 입력 된다.

		List<Integer>[] adjList = new ArrayList[V+1];
		
		for(int i = 0 ; i<V+1; i++) {
			adjList[i] = new ArrayList<>();
		}
		
		
		// 간선을 입력을 받자.
		for (int i = 0; i < E; i++) {
			int st = sc.nextInt();
			int ed = sc.nextInt();

			//유향 그래프라면 20번라인은 필요없다.
			adjList[st].add(ed);
			//무향그래프일때 아래도 같이 작성을 해준다.
			adjList[ed].add(st);
		}
		
		for(int i = 0 ; i<V+1; i++) {
			Collections.sort(adjList[i]);
		}
	}
}

인접 리스트의 장점

  • 메모리 효율적 : 간선이 적은 희소 그래프(sparse graph)의 경우 인접 리스트는 메모리 사용량이 적습니다. 인접 리스트는 실제 연결된 간선만 저장하기 때문입니다.
  • 간선 추가/삭제 용이 : 인접 리스트에서는 간선을 추가하거나 삭제하기 쉽습니다. 새로운 간선을 리스트에 추가하거나 삭제하기만 하면 됩니다.

인접 리스트의 단점

두 정점 간의 연결 확인 느림 : 인접 리스트에서 두 정점 간에 간선이 존재하는지 확인하려면 연결된 정점 목록을 탐색해야 합니다. 이 과정은 O(n)의 시간이 소요됩니다.

인접 행렬/인접 리스트

인접 행렬이 더 효율적인 경우 : 밀집 그래프(dense graph), 두 정점 간의 연결 여부를 자주 확인해야 하는 경우

인접 리스트가 더 효율적인 경우 : 간선이 적은 희소 그래프(sparse graph)

간선의 배열

  • 간선(시작 정점, 끝 정점)을 배열에 연속적으로 저장
  • 간선을 표현하는 두 정점의 정보를 배열 혹은 객체로 저장할 수 있음
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class 그래프_03_간선배열 {
	static class Edge {
		int st, ed;

		public Edge(int st, int ed) {
			this.st = st;
			this.ed = ed;
		}
	}

	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		int V = sc.nextInt(); // 정점의 개수를 입력을 받는다. (6 이다. 시작정점이 0번인지 / 1번인지)
		int E = sc.nextInt(); // 간선의 수가 입력 된다.

		// 2가지
		Edge[] edges = new Edge[E];
		List<Edge> edges2 = new ArrayList<>();
		
		int[][] edges3 = new int[E][2];   //[i][0] : 시작정점, [i][1] : 끝정점

		for (int i = 0; i < E; i++) {
			int st = sc.nextInt();
			int ed = sc.nextInt();

			edges[i] = new Edge(st, ed);

		}
	}
}

'알고리즘 > 그래프' 카테고리의 다른 글

위상정렬  (0) 2023.04.03
프림/다익스트라 알고리즘  (0) 2023.03.30
그래프 심화/최소 신장 트리  (0) 2023.03.29
그래프 탐색  (0) 2023.03.28