Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

크루스칼 알고리즘 구현(끝) #48

Merged
merged 11 commits into from
Sep 2, 2020
230 changes: 230 additions & 0 deletions yoonexample/src/main/java/graph/ListWeightGraph.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
package graph;

import java.util.Comparator;
import java.util.StringJoiner;
import list.DummyDoublyLinkedList;
import list.List;
import priorityqueue.HeapPriorityQueue;
import priorityqueue.PriorityQueue;
import queue.LinkedListQueue;
import queue.Queue;
import stack.ListStack;
import stack.Stack;

public class ListWeightGraph implements WeightedGraph {

private final List<Enum<?>>[] vertices;
private final PriorityQueue<WeightEdge> edgePriorityQueue;

public ListWeightGraph(int vertexCount, Class<? extends Enum<?>> clazz) {
edgePriorityQueue = new HeapPriorityQueue<>(Comparator.comparingInt(o -> o.weight));
Enum<?>[] enumConstants = clazz.getEnumConstants();
int min = Math.min(vertexCount, enumConstants.length);

this.vertices = new List[min];
for (int i = 0; i < min; i++) {
this.vertices[i] = new DummyDoublyLinkedList<>();
this.vertices[i].insert(enumConstants[i]);
}
}

@Override
public void addEdge(Enum<?> fromV, Enum<?> toV, int weight) {
WeightEdge edge = new WeightEdge(weight, fromV, toV);
vertices[fromV.ordinal()].insert(toV);
vertices[toV.ordinal()].insert(fromV);

edgePriorityQueue.enqueue(edge);
}

@Override
public String showGraphEdgeWeightInfo() {
List<WeightEdge> weightEdgeList = new DummyDoublyLinkedList<>();
StringBuilder sb = new StringBuilder();

while (!this.edgePriorityQueue.isEmpty()) {
WeightEdge edge = this.edgePriorityQueue.dequeue();
weightEdgeList.insert(edge);
sb.append(edge.showEdgeInfo()).append("\n");
}

for (int i = 0; i < weightEdgeList.size(); i++) {
this.edgePriorityQueue.enqueue(weightEdgeList.get(i));
}

return sb.toString();
}

@Override
public void convertToMST() {
int vertexCount = this.vertices.length;
int edgeCount = this.edgePriorityQueue.size() + 1;
List<WeightEdge> edges = new DummyDoublyLinkedList<>();

// MST가 될 때까지 while문을 반복
while (edgeCount != vertexCount) {
WeightEdge edge = this.edgePriorityQueue.dequeue();
removeEdge(edge.fromVertex, edge.toVertex); // 그래프에서 제거해본다.
edgeCount--;

if (!isConnectedWith(edge.fromVertex, edge.toVertex)) { // 연결되어 있지 않으면 복구
recoverEdge(edge);
edges.insert(edge);
edgeCount++;
}
}

// 간선 정보 복원
for (int i = 0; i < edges.size(); i++) {
this.edgePriorityQueue.enqueue(edges.get(i));
}
}

private boolean isConnectedWith(Enum<?> fromVertex, Enum<?> toVertex) {
boolean[] visited = new boolean[vertices.length];
Stack<Enum<?>> vertexStack = new ListStack<>();
vertexStack.push(fromVertex);

while (!vertexStack.isEmpty()) {
Enum<?> visitV = vertexStack.pop();
if (visitVertex(visited, visitV)) {
if (visitV.equals(toVertex)) {
return true;
}
}

List<Enum<?>> vertexList = vertices[visitV.ordinal()];
for (int i = 0; i < vertexList.size(); i++) {
Enum<?> vertex = vertexList.get(i);
if (!visited[vertex.ordinal()]) {
vertexStack.push(vertex);
}
}
}

return false;
}

private void recoverEdge(WeightEdge edge) {
vertices[edge.fromVertex.ordinal()].insert(edge.toVertex);
vertices[edge.toVertex.ordinal()].insert(edge.fromVertex);
}

private void removeEdge(Enum<?> fromVertex, Enum<?> toVertex) {
removeVertexFromLink(fromVertex, toVertex);
removeVertexFromLink(toVertex, fromVertex);
}

private void removeVertexFromLink(Enum<?> vertexA, Enum<?> vertexB) {
List<Enum<?>> vertexLinkInfo = this.vertices[vertexA.ordinal()];
for (int i = 0; i < vertexLinkInfo.size(); i++) {
if (vertexLinkInfo.get(i).equals(vertexB)) {
vertexLinkInfo.remove(i);
return;
}
}
throw new IllegalArgumentException("해당 정점들은 연결되어있지 않습니다.");
}

@Override
public void addEdge(Enum<?> fromV, Enum<?> toV) {
throw new UnsupportedOperationException("이 메소드는 지원하지 않습니다.");
}

@Override
public String showGraphEdgeInfo() {
StringBuilder sb = new StringBuilder();

for (List<Enum<?>> vertex : vertices) {
if (vertex.size() > 1) {
for (int i = 0; i < vertex.size(); i++) {
sb.append(vertex.get(i));
if (i == 0) {
sb.append(": ");
} else if (i < vertex.size() - 1) {
sb.append(" ");
}
}
sb.append("\n");
}
}

return sb.toString();
}

@Override
public String depthFirstSearch(Enum<?> startV) {
boolean[] visited = new boolean[vertices.length];
StringJoiner sj = new StringJoiner(" ");
Stack<Enum<?>> vertexStack = new ListStack<>();
vertexStack.push(startV);

while (!vertexStack.isEmpty()) {
Enum<?> visitV = vertexStack.pop();

if (visitVertex(visited, visitV)) {
sj.add(visitV.toString());
}
List<Enum<?>> vertexList = vertices[visitV.ordinal()];
for (int i = 0; i < vertexList.size(); i++) {
Enum<?> vertex = vertexList.get(i);
if (!visited[vertex.ordinal()]) {
vertexStack.push(vertex);
}
}
}

return sj.toString();
}

@Override
public String breadthFirstSearch(Enum<?> startV) {
boolean[] visited = new boolean[vertices.length];
StringJoiner sj = new StringJoiner(" ");
Queue<Enum<?>> vertexQueue = new LinkedListQueue<>();
vertexQueue.enqueue(startV);

while (!vertexQueue.isEmpty()) {
Enum<?> visitV = vertexQueue.dequeue();

if (visitVertex(visited, visitV)) {
sj.add(visitV.toString());
}

List<Enum<?>> vertexList = vertices[visitV.ordinal()];
for (int i = 0; i < vertexList.size(); i++) {
Enum<?> vertex = vertexList.get(i);
if (!visited[vertex.ordinal()]) {
vertexQueue.enqueue(vertex);
}
}
}

return sj.toString();
}

private boolean visitVertex(boolean[] visited, Enum<?> vertex) {
if (visited[vertex.ordinal()]) {
return false;
}
visited[vertex.ordinal()] = true;
return true;
}

private static class WeightEdge {

private final int weight;
private final Enum<?> fromVertex;
private final Enum<?> toVertex;

public WeightEdge(int weight, Enum<?> fromVertex, Enum<?> toVertex) {
this.weight = weight;
this.fromVertex = fromVertex;
this.toVertex = toVertex;
}

private String showEdgeInfo() {
return "(" + fromVertex.toString() + "-" + toVertex.toString() + "), " + "w: " + weight;
}
}
}
30 changes: 30 additions & 0 deletions yoonexample/src/main/java/graph/WeightedGraph.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package graph;

/**
* 가중치 그래프의 인터페이스
*
* @author dion
*/
public interface WeightedGraph extends Graph {

/**
* 매개변수 fromV와 toV로 전달된 정점을 연결하는 간선에 가중치를 부여하여 그래프에 추가합니다.
*
* @param fromV 시작하는 정점
* @param toV 도달하는 정점
* @param weight 간선의 가중치
*/
void addEdge(Enum<?> fromV, Enum<?> toV, int weight);

/**
* 그래프의 간선정보 및 가중치를 반환합니다.
*
* @return 그래프의 간선정보 및 가중치
*/
String showGraphEdgeWeightInfo();

/**
* 크루스칼 알고리즘을 이용해 최소신장 트리로 변환합니다.
*/
void convertToMST();
}
5 changes: 5 additions & 0 deletions yoonexample/src/main/java/heap/ArrayUsefulHeap.java
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@ public E delete() {
return retData;
}

@Override
public int size() {
return this.numOfData;
}

/**
* 부모 노드의 인덱스 반환 요청
*
Expand Down
7 changes: 7 additions & 0 deletions yoonexample/src/main/java/heap/UsefulHeap.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,11 @@ public interface UsefulHeap<E> {
* @return 힙에 저장되어 있던 최우선순위 데이터
*/
E delete();

/**
* 힙에 저장되어 있는 데이터의 수를 반환합니다.
*
* @return 힙에 저장되어 있는 데이터의 수
*/
int size();
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,9 @@ public E dequeue() {
}
return this.heap.delete();
}

@Override
public int size() {
return this.heap.size();
}
}
7 changes: 7 additions & 0 deletions yoonexample/src/main/java/priorityqueue/PriorityQueue.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,11 @@ public interface PriorityQueue<E> {
* @return 우선순위가 가장 높은 데이터
*/
E dequeue();

/**
* 우선순위 큐에 저장되어 있는 데이터의 수를 반환합니다.
*
* @return 우선순위 큐에 저장되어 있는 데이터의 수
*/
int size();
}
2 changes: 1 addition & 1 deletion yoonexample/src/test/java/graph/GraphTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ void setUp() {
}

@Test
void 방향_그래프_정점_연결_테스트() {
void 무방향_그래프_정점_연결_테스트() {
graph.addEdge(Point.A, Point.B);
assertThat(graph.showGraphEdgeInfo()).isEqualTo("A: B\nB: A\n");

Expand Down
Loading