대표노드를 저장하는 1차원 배열을 선언한다. 처음에는 노드가 연결되어 있지 않으므로 각 노드가 대표 노드가 된다.
int[] parent = new int[N+1];
for (int i = 1; i <= N; i++) {
parent[i] = i;
}
2개의 노드를 선택하고 각각의 대표 노드를 찾아 연결하는 union 연산을 수행한다.
private static void union(int a, int b) {
a = find(a);
b = find(b);
if (a != b) {
parent[b] = a;
}
}
선택한 노드가 속한 집합의 대표 노드를 찾는 find 연산을 수행한다.
private static int find(int x) {
if (x == parent[x])
return x;
else {
return parent[x] = find(parent[x]);
}
}
ArrayList로 그래프를 표현한다. 이 때 진입차수(in-degree) 값을 함께 계산한다.
int N = scan.nextInt();
int M = scan.nextInt();
int[] D = new int[N+1];
ArrayList<Integer>[] adj = new ArrayList[N+1];
for (int i = 1; i <= N; i++) {
adj[i] = new ArrayList<>();
}
for (int i = 1; i <= M; i++) {
int x = scan.nextInt();
int y = scan.nextInt();
adj[x].add(y);
D[y]++;
}
진입 차수 배열에서 진입 차수가 0인 노드를 선택하고 선택된 노드를 정렬 배열에 저장한다. 인접 리스트에서 선택된 노드가 가리키는 노드들의 진입 차수를 1씩 뺀다. 모든 노드가 정렬될 때까지 반복한다.
Queue<Integer> queue = new LinkedList<>();
for (int i = 1; i <= N; i++) {
if (D[i] == 0)
queue.add(i);
}
while (!queue.isEmpty()) {
int x = queue.poll();
System.out.println(x);
for (int y : adj[x]) {
D[y]--;
if (D[y] == 0)
queue.add(y);
}
}
인접 리스트로 그래프를 구현한다. 연결한 배열의 자료형은 (노드, 가중치)와 같은 형태로 선언한다.
class Node {
int targetNode;
int value;
public Node(int targetNode, int value) {
this.targetNode = targetNode;
this.value = value;
}
}
int V = scan.nextInt();
int E = scan.nextInt();
int K = scan.nextInt();
ArrayList<Node> adj = new ArrayList[V+1];
for (int i = 1; i <= V; i++) {
adj[i] = new ArrayList<>();
}
for (int i = 1; i <= E; i++) {
int from = scan.nextInt();
int to = scan.nextInt();
int d = scan.nextInt();
adj[from].add(new Node(to, d));
}
최단 거리 배열을 초기화한다. 출발 노드는 0, 이외의 노드는 무한(적당히 큰 값)으로 초기화한다.
int[] dist = new int[V+1];
for (int i = 1; i <= V; i++) {
dist[i] = 3000001;
}
dist[K] = 0;
값이 가장 작은 노드를 고른다. 값이 0인 출발 노드에서 시작한다.
PriorityQueue<Node> pq = new PriorityQueue<>((Comparator.comparingInt(o -> o.value)));
pq.add(new Node(K, 0));
최단 거리 배열을 업데이트한다. 현재 선택된 노드에 연결된 에지들을 탐색하고 업데이트한다.
Min(선택(출발) 노드의 최단 거리 배열의 값 + 에지 가중치
, 연결(도착) 노드의 최단 거리 배열의 값
)
if (dist[now.targetNode] + next.value < dist[next.targetNode]) {
dist[next.targetNode] = dist[now.targetNode] + next.value;
pq.add(new Node(next.targetNode, dist[next.targetNode]));
}
과정 3~4를 반복해 최단 거리 배열을 완성한다. 과정 4에서 이미 선택했던 노드가 다시 선택되지 않도록 방문 배열을 만들어 처리하고, 모든 노드가 선택될 때까지 반복한다.
while (!pq.isEmpty()) {
Node now = pq.poll();
if (visit[now.targetNode]) continue;
visit[now.targetNode] = true;
for (Node next : adj[now.targetNode]) {
if (dist[now.targetNode] + next.value < dist[next.targetNode]) {
dist[next.targetNode] = dist[now.targetNode] + next.value;
pq.add(new Node(next.targetNode, dist[next.targetNode]));
}
}
}
에지 리스트로 그래프를 구현하고 최단 경로 배열을 초기화한다. 최단 경로 배열의 출발 노드는 0, 나머지 노드는 무한대로 초기화한다.
int N = scan.nextInt();
int M = scan.nextInt();
Edge[] edgeList = new Edge[M+1];
long[] dist = new long[N+1];
for (int i = 1; i <= M; i++) {
int A = scan.nextInt();
int B = scan.nextInt();
int C = scan.nextInt();
edgeList[i] = new Edge(A, B, C);
}
dist[1] = 0;
for (int i = 2; i <= N; i++) {
dist[i] = Integer.MAX_VALUE;
}
모든 에지를 확인하여 정답 배열을 업데이트한다. 노드 개수가 N이고, 음수 사이클이 없을 경우 특정 두 노드의 최단 거리를 구성하는 에지의 최대 개수는 N-1이므로, 최단 거리 배열 업데이트 반복 횟수는 N-1이다.
D[s] ≠ ∞
이며, D[e] > D[s] + w
일 때, D[e] = D[s] + w
로 업데이트for (int i = 1; i <= N-1; i++) {
updateDistance();
}
private static void updateDistance() {
for (int i = 1; i <= M; i++) {
int S = edgeList[i].start;
int E = edgeList[i].end;
int W = edgeList[i].weight;
if (dist[S] != Integer.MAX_VALUE && dist[E] > dist[S] + W) {
dist[E] = dist[S] + W;
}
}
}
음수 사이클 유무를 확인한다. 모든 에지를 한번씩 다시 사용해 업데이트되는 노드가 발생하는지 확인한다. 만약 업데이트되는 노드가 있다면 음수 사이클이 있다는 뜻이며, 최단 거리를 찾을 수 없는 그래프이다.
for (int i = 2; i <= N; i++) {
if (lastDist[i] != dist[i]) {
System.out.println("음수 사이클 존재");
return;
}
}
distance[S][K] == 1 && distance[K][E] == 1
이라면, distance[S][E] = 1
최단 거리 배열을 선언하고 초기화한다. S == E 인 경우는 0, 다른 경우는 ∞로 초기화한다.
int N = scan.nextInt();
int M = scan.nextInt();
long D = new long[N+1][N+1];
for (int i = 1; i <= N; i++) {
for (int j = 1; j <= N; j++) {
if (i == j) {
D[i][j] = 0;
} else {
D[i][j] = Integer.MAX_VALUE;
}
}
}
최단 거리 배열에 그래프 데이터를 저장한다. D[S][E] = W로 에지 정보를 배열에 저장한다.
for (int i = 1; i <= M; i++) {
int a = scan.nextInt();
int b = scan.nextInt();
long c = scan.nextLong();
if (D[a][b] > c) {
D[a][b] = c;
}
}
점화식을 3중 ofr 문 형태로 반복하면서 배열을 업데이트한다.
for (int k = 1; k <= N; k++) {
for (int s = 1; s <= N; s++) {
for (int e = 1; e <= N; e++) {
D[s][e] = Math.min(D[s][e], D[s][k] + D[k][e]);
}
}
}
에지 리스트로 그래프를 구현하고 유니온 파인드 배열을 초기화한다.
int V = scan.nextInt();
int E = scan.nextInt();
PriorityQueue<Edge> edges = new PriorityQueue<>();
for (int i = 0; i < E; i++) {
int s = scan.nextInt();
int e = scan.nextInt();
int v = scan.nextInt();
edges.add(new Edge(s, e, v));
}
에지 리스트에 담긴 그래프 데이터를 가중치 기준 오름차순 정렬한다.
class Edge implements Comparable<Edge>{
int start;
int end;
int value;
public Edge(int start, int end, int value) {
this.start = start;
this.end = end;
this.value = value;
}
@Override
public int compareTo(Edge o) {
return this.value - o.value;
}
}
가중치가 낮은 에지부터 순서대로 선택해 연결을 시도한다. 에지를 연결했을 때, find 연산을 이용해 사이클 형성 유무를 확인한 후, 사이클이 형성되지 않을 때만 union 연산으로 두 노드를 연결한다.
int[] parent = new int[V+1];
for (int i = 1; i <= V; i++) {
parent[i] = i;
}
if (find(s) != find(e)) {
union(s, e);
ans += edge.value;
useEdge++;
}
private static void union(int a, int b) {
a = find(a);
b = find(b);
if (a != b) {
parent[b] = a;
}
}
private static int find(int x) {
if (x == parent[x]) {
return x;
} else {
return parent[x] = find(parent[x]);
}
}
전체 노드의 개수가 N개이면 연결한 에지의 개수가 N-1이 될 때까지 과정 3을 반복한다.
int ans = 0;
int useEdge = 0;
while (useEdge < V - 1) {
Edge edge = edges.poll();
int s = edge.start;
int e = edge.end;
if (find(s) != find(e)) {
union(s, e);
ans += edge.value;
useEdge++;
}
}
에지의 개수가 N-1이 되면 완성된 최소 신장 트리의 총 에지 비용을 출력한다.