최소 신장 트리
신장 트리
신장 트리는 그래프에서 모든 정점에 대한 최소한의 연결만을 남긴 그래프이다. 한 곳으로 도달하는 경우가 두 개 이상 존재하는 경우, 즉 사이클이 존재하는 경우에는 최소한의 연결이라 말할 수 없기 때문에, 모든 위치 하나에서 다른 곳으로 이동하는 경우는 단 한 가지로 결정되도록 항상 트리의 형태를 나타낸다. 최소 비용 신장 트리는 이러한 신장 트리들 중 간선의 가중치 합이 가장 작은 트리이다. -출처 나무위키
책에 있는 설명이 이해하기 쉽지 않아서 나무위키를 찾아봤다.
최소 신장 트리
각 간선이 가지고 있는 가중치의 합이 최소가 되는 신장 트리이다.
최소 신장 트리를 만드는 알고리즘은 "최소한의 비용으로 모든 도시를 연결하는 도로를 건설할 방법을 찾아라" 라는 문제가 주어졌을 때 이를 해결할 아주 좋은 도구가 된다.
새로 건설할 호텔의 배관을 최소의 비용으로 구축하는 일에도 응용할 수 있고, 복잡하게 책상이 놓여 있는 사무실에 전화선을 연결할 때도 최소한의 노력으로 작업을 마칠 수 있는 방법을 얻을 수 있다.
프림 알고리즘
프림 알고리즘(Prim's Algorithm)은 그래프에서 최소 신장 트리를 만들어내는 알고리즘 중 하나다.
모든 정점이 최소신장트리에 삽입될 때까지, 최소신장트리에 삽입된 정점들이 가진 가장 가중치가 작은 간선의 target을 최소신장트리에 삽입한다.
사이클이 생기면 삽입하지 않는다.
프림 알고리즘의 구현
프림 알고리즘의 구현은 일견 간단해 보이지만 여기엔 고려해야 할 다음과 같은 두 가지 문제가 있다.
첫 번째 문제는 최소 신장트리의 자료구조로 무엇을 사용할 것인가 하는 문제.
두 번째 문제는 최소 간선을 골라낼 때 드는 시간비용.
첫 번째 문제는 그래프 사용
두 번째 문제는 우선순위 큐로 해결
크루스칼 알고리즘
크루스칼 알고리즘은 프림 알고리즘과 마찬가지로 그래프에서 최소 신장 트리를 만들어내는 알고리즘이다.
간선을 가중치로 정렬한 뒤, 사이클이 생기지 않는 선에서 순서대로 삽입한다.
크루스칼 알고리즘의 구현
크루스칼 알고리즘을 구현할 때 이 정점을 삽입하면 사이클이 생기는지 검사는 분리집합으로 한다.
연결된 정점들끼리 분리집합을 만든다.
분리집합은 효율적으로 중복된 정점을 허용하지 않게 할 수 있다.
다음은 소스코드
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 | #include "MST.h" void Prim(Graph* G, Vertex* StartVertex, Graph* MST) { int i = 0; PQNode StartNode = { 0, StartVertex }; PriorityQueue* PQ = PQ_Create(10); Vertex* CurrentVertex = NULL; Edge* CurrentEdge = NULL; int* Weights = (int*)malloc(sizeof(int) * G->VertexCount); Vertex** MSTVertices = (Vertex**)malloc(sizeof(Vertex*) * G->VertexCount); Vertex** Fringes = (Vertex**)malloc(sizeof(Vertex*) * G->VertexCount); Vertex** Precedences = (Vertex**)malloc(sizeof(Vertex*) * G->VertexCount); CurrentVertex = G->Vertices; while (CurrentVertex != NULL) { Vertex* NewVertex = CreateVertex(CurrentVertex->Data); AddVertex(MST, NewVertex); Fringes[i] = NULL; Precedences[i] = NULL; MSTVertices[i] = NewVertex; Weights[i] = MAX_WEIGHT; CurrentVertex = CurrentVertex->Next; i++; } PQ_Enqueue(PQ, StartNode); Weights[StartVertex->Index] = 0; while (!PQ_IsEmpty(PQ)) { PQNode Popped; PQ_Dequeue(PQ, &Popped); CurrentVertex = (Vertex*)Popped.Data; Fringes[CurrentVertex->Index] = CurrentVertex; CurrentEdge = CurrentVertex->AdjacencyList; while (CurrentEdge != NULL) { Vertex* TargetVertex = CurrentEdge->Target; if (Fringes[TargetVertex->Index] == NULL && CurrentEdge->Weight < Weights[TargetVertex->Index]) { PQNode NewNode = { CurrentEdge->Weight, TargetVertex }; PQ_Enqueue(PQ, NewNode); Precedences[TargetVertex->Index] = CurrentEdge->From; Weights[TargetVertex->Index] = CurrentEdge->Weight; } CurrentEdge = CurrentEdge->Next; } } for (int i = 0; i < G->VertexCount; i++) { int FromIndex, ToIndex; if (Precedences[i] == NULL) { continue; } FromIndex = Precedences[i]->Index; ToIndex = i; AddEdge(MSTVertices[FromIndex], CreateEdge(MSTVertices[FromIndex], MSTVertices[ToIndex], Weights[i])); AddEdge(MSTVertices[ToIndex], CreateEdge(MSTVertices[ToIndex], MSTVertices[FromIndex], Weights[i])); } free(Fringes); free(Precedences); free(MSTVertices); free(Weights); PQ_Destroy(PQ); } void Kruskal(Graph* G, Graph* MST) { int i; Vertex* CurrentVertex = NULL; Vertex** MSTVertices = (Vertex**)malloc(sizeof(Vertex*) * G->VertexCount); DisjointSet** VertexSet = (DisjointSet**)malloc(sizeof(Vertex*) * G->VertexCount); PriorityQueue* PQ = PQ_Create(10); i = 0; CurrentVertex = G->Vertices; while (CurrentVertex != NULL) { Edge* CurrentEdge; VertexSet[i] = DS_MakeSet(CurrentVertex); MSTVertices[i] = CreateVertex(CurrentVertex->Data); AddVertex(MST, MSTVertices[i]); CurrentEdge = CurrentVertex->AdjacencyList; while (CurrentEdge != NULL) { PQNode NewNode = { CurrentEdge->Weight, CurrentEdge }; PQ_Enqueue(PQ, NewNode); CurrentEdge = CurrentEdge->Next; } CurrentVertex = CurrentVertex->Next; i++; } while (!PQ_IsEmpty(PQ)) { Edge* CurrentEdge; int FromIndex; int ToIndex; PQNode Popped; PQ_Dequeue(PQ, &Popped); CurrentEdge = (Edge*)Popped.Data; FromIndex = CurrentEdge->From->Index; ToIndex = CurrentEdge->Target->Index; if (DS_FindSet(VertexSet[FromIndex]) != DS_FindSet(VertexSet[ToIndex])) { AddEdge(MSTVertices[FromIndex], CreateEdge(MSTVertices[FromIndex], MSTVertices[ToIndex], CurrentEdge->Weight)); AddEdge(MSTVertices[ToIndex], CreateEdge(MSTVertices[ToIndex], MSTVertices[FromIndex], CurrentEdge->Weight)); DS_UnionSet(VertexSet[FromIndex], VertexSet[ToIndex]); } } for (int i = 0; i < G->VertexCount; i++) { DS_DestroySet(VertexSet[i]); } free(VertexSet); free(MSTVertices); } | cs |