그래프는 정점(node 또는 vertex)과 간선(edge)로 이루어진 자료구조이다.
무방향 그래프
방향 그래프
가중치 그래프
완전 그래프
그래프는 인접 행렬 또는 인접 리스트 로 구현할 수 있다.
1
, 아니라면 0
을 저장한다.장점
단점
장점
단점
AdjacencyMatrixGraph
package com.example.datastructure.Graph;
import java.util.*;
public class AdjacencyMatrixGraph implements IGraph {
private Integer[][] matrix;
private Set<Integer> vertexes;
private Map<Integer, Integer> indegrees;
public AdjacencyMatrixGraph(int numOfVertex) {
this.vertexes = new HashSet<>();
this.indegrees = new HashMap<>();
this.matrix = new Integer[numOfVertex][];
for (int i = 0; i < numOfVertex; i++) {
this.matrix[i] = new Integer[numOfVertex];
}
}
@Override
public void add(int from, int to, Integer distance) {
vertexes.add(from);
vertexes.add(to);
int count = indegrees.getOrDefault(to, 0);
indegrees.put(to, count + 1);
matrix[from][to] = distance;
}
@Override
public void add(int from, int to) {
vertexes.add(from);
vertexes.add(to);
int count = indegrees.getOrDefault(to, 0);
indegrees.put(to, count + 1);
matrix[from][to] = 1;
}
@Override
public Integer getDistance(int from, int to) {
return this.matrix[from][to];
}
@Override
public Map<Integer, Integer> getIndegrees() {
return this.indegrees;
}
@Override
public Set<Integer> getVertexes() {
return this.vertexes;
}
@Override
public List<Integer> getNodes(int vertex) {
List<Integer> ret = new ArrayList<>();
for (int i = 0; i < matrix[vertex].length; i++) {
if (matrix[vertex][i] != null) {
ret.add(i);
}
}
return ret;
}
}
AdjacencyListGraph
package com.example.datastructure.Graph;
import java.util.*;
public class AdjacencyListGraph implements IGraph {
private List<List<Node>> graph;
private Set<Integer> vertexes;
private Map<Integer, Integer> indegrees;
public AdjacencyListGraph(int numOfVertex) {
this.vertexes = new HashSet<>();
this.indegrees = new HashMap<>();
this.graph = new ArrayList<>(numOfVertex);
for (int i = 0; i < numOfVertex; i++) {
this.graph.add(new ArrayList<>());
}
}
@Override
public void add(int from, int to, Integer distance) {
vertexes.add(from);
vertexes.add(to);
int count = indegrees.getOrDefault(to, 0);
indegrees.put(to, count + 1);
List<Node> neighbors = this.graph.get(from);
neighbors.add(new Node(from, to, distance));
}
@Override
public void add(int from, int to) {
vertexes.add(from);
vertexes.add(to);
int count = indegrees.getOrDefault(to, 0);
indegrees.put(to, count + 1);
List<Node> neighbors = this.graph.get(from);
neighbors.add(new Node(from, to));
}
@Override
public Integer getDistance(int from, int to) {
for (Node node : this.graph.get(from)) {
if (node.to.equals(to)) {
return node.weight;
}
}
return -1;
}
@Override
public Map<Integer, Integer> getIndegrees() {
return this.indegrees;
}
@Override
public Set<Integer> getVertexes() {
return this.vertexes;
}
@Override
public List<Integer> getNodes(int vertex) {
List<Integer> nodes = new ArrayList<>();
for (Node node : this.graph.get(vertex)) {
nodes.add(node.to);
}
return nodes;
}
private class Node {
Integer from;
Integer to;
int weight;
Node(int from, int to) {
this.from = from;
this.to = to;
this.weight = 1;
}
Node(int from, int to, int weight) {
this.from = from;
this.to = to;
this.weight = weight;
}
}
}
해당 그래프가 있다고 하자.
A B C D E F G H I J 순으로 탐색한다.
public static List<Integer> bfs(IGraph graph, int from) {
// 최종 아웃풋 결과를 출력하기 위한 list
List<Integer> result = new ArrayList<>();
// 방문했던 vertex, node 를 재방문 하지 않기 위해 set 에 방문했던 노드 저장
Set<Integer> visited = new HashSet<>();
// bfs 를 위한 queue
IQueue<Integer> queue = new MyLinkedQueue<>();
visited.add(from);
queue.offer(from); // from 부터 시작
while (!queue.isEmpty()) { // 큐가 빌 때까지
Integer next = queue.poll();
result.add(next); // 큐에서 노드를 꺼내오면서 방문
for (Integer n : graph.getNodes(next)) {
if (!visited.contains(n)) {
visited.add(n);
queue.offer(n);
}
}
}
return result;
}
A B D H I E J C F G 순으로 탐색한다.
public static List<Integer> dfs(IGraph graph, int from) {
List<Integer> result = new ArrayList<>();
Set<Integer> visited = new HashSet<>();
IStack<Integer> stack = new MyStack<>();
visited.add(from);
stack.push(from);
while (stack.size() > 0) {
Integer next = stack.pop();
result.add(next);
for (Integer n : graph.getNodes(next)) {
if (!visited.contains(n)) {
visited.add(n);
stack.push(n);
}
}
}
return result;
}
A B C D F E G 순으로 탐색
// 1. 모든 vertax의 indegree 수를 센다
// 2. 큐에 indegree가 0인 vertax 삽입
// 3. 큐에서 vertex를 꺼내 연결된(나가는 방향) edge 제거
// 4. 3번으로 인해 indegree가 0이 된 vertex를 큐에 삽입
// 5. 큐가 빌 때까지 3~4 반복
// queue 를 이용한 진입차수 방식
public static List<Integer> topologicalSortIndegree(IGraph graph) {
Map<Integer, Integer> indegreeCounter = graph.getIndegrees();
IQueue<Integer> queue = new MyLinkedQueue<>();
List<Integer> ret = new LinkedList<>();
for (int v : graph.getVertexes()) {
int count = indegreeCounter.getOrDefault(v, 0);
if (count == 0) {
queue.offer(v);
}
}
while (!queue.isEmpty()) {
int node = queue.poll();
ret.add(node);
for (int nn : graph.getNodes(node)) {
if (indegreeCounter.containsKey(nn)) {
int count = indegreeCounter.get(nn);
if (count - 1 == 0) {
queue.offer(nn);
}
indegreeCounter.put(nn, count - 1);
}
}
}
return ret;
}
// stack 구현
public static List<Integer> topologicalSort(IGraph graph) {
List<Integer> result = new ArrayList<>();
IStack<Integer> stack = new MyStack<>();
Set<Integer> visited = new HashSet<>();
Set<Integer> vertexes = graph.getVertexes();
for (Integer vertex : vertexes) {
if (!visited.contains(vertex)) {
topologicalSort(graph, vertex, visited, stack);
}
}
while (stack.size() > 0) {
result.add(stack.pop());
}
return result;
}
private static void topologicalSort(IGraph graph, int vertex, Set<Integer> visited, IStack<Integer> stack) {
visited.add(vertex);
List<Integer> nodes = graph.getNodes(vertex);
for (Integer n : nodes) {
if (!visited.contains(n)) {
topologicalSort(graph, n, visited, stack);
}
}
stack.push(vertex);
}
과정
1. 출발 노드와 도착 노드를 설정한다.
2. '최단 거리 테이블'을 초기화한다.
3. 현재 위치한 노드의 인접 노드 중 방문하지 않은 노드를 구별하고, 방문하지 않은 노드 중 거리가 가장 짧은 노드를 선택한다. 그 노드를 방문 처리한다.
4. 해당 노드를 거쳐 다른 노드로 넘어가는 간선 비용(가중치)을 계산해 '최단 거리 테이블'을 업데이트한다.
5. 3~4의 과정을 반복한다.
우선순위 큐
// 다익스트라 최단거리 알고리즘
// src 출발 노드
// dst 도착 노드
// return 출발 노드로부터 도착 노드까지의 최단 거리
public static int dijkstraShortestPath(IGraph graph, int src, int dst) {
int size = 0;
for (int n : graph.getVertexes()) {
if (size < n) {
size = n;
}
}
int[] dist = new int[size + 1];
for (int i = 0; i < dist.length; i++) {
dist[i] = Integer.MAX_VALUE;
}
dist[src] = 0;
PriorityQueue<Pair> pq = new PriorityQueue<>();
pq.add(new Pair(src, 0));
while (!pq.isEmpty()) {
Pair top = pq.poll();
if (dist[top.vertex] < top.distance) {
continue;
}
for (int node : graph.getNodes(top.vertex)) {
if (dist[node] > dist[top.vertex] + graph.getDistance(top.vertex, node)) {
dist[node] = dist[top.vertex] + graph.getDistance(top.vertex, node);
pq.add(new Pair(node, dist[node]));
}
}
}
return dist[dst];
}
private static class Pair implements Comparable<Pair> {
int vertex;
int distance;
public Pair(int vertex, int distance) {
this.vertex = vertex;
this.distance = distance;
}
@Override
public int compareTo(Pair o) {
return distance - o.distance;
}
}