
- V는 정점의 집합입니다.
- R은 정점 쌍을 연결하는 선 집합입니다.




3. 길 찾기 알고리즘(깊이 우선, 너비 우선)
그래프로 수행되는 기본 작업 중 하나는 주어진 꼭지점에서 도달할 수 있는 모든 꼭지점을 결정하는 것입니다. 가능한 환승을 포함하여 한 도시에서 다른 도시로 버스를 타는 방법을 결정하려고 한다고 상상해 보십시오. 일부 도시는 직접 갈 수 있는 반면, 일부 도시는 다른 도시를 거쳐야만 도달할 수 있습니다. 주어진 정점에서 도달할 수 있는 모든 정점을 찾아야 하는 다른 상황도 많이 있습니다. 그래프를 탐색하는 방법에는 깊이 우선과 너비 우선이라는 두 가지 주요 방법이 있습니다. 우리는 이 두 가지를 모두 탐구할 것입니다. 두 방법 모두 연결된 모든 정점을 통과합니다. 깊이 우선 및 너비 우선 알고리즘을 더 자세히 살펴보기 위해 다음 그래프를 사용합니다.
깊이 우선 검색
이는 가장 일반적인 그래프 순회 방법 중 하나입니다. 깊이 우선 전략은 그래프에서 최대한 깊이 들어가는 것입니다. 그런 다음 막다른 골목에 도달한 후 이전에 방문하지 않은 인접한 정점이 있는 가장 가까운 정점으로 돌아갑니다. 이 알고리즘은 막다른 골목에 도달했을 때 반환할 위치에 대한 정보를 스택에 저장합니다. 깊이 우선 검색 규칙:- 이전에 방문하지 않은 인접 정점을 방문하여 방문한 것으로 표시하고 스택에 푸시합니다.
- 이 정점으로 이동합니다.
- 1단계를 반복합니다.
- 1단계가 불가능하면 이전 정점으로 돌아가서 1단계를 수행해 보세요. 이것이 가능하지 않다면 그 이전 정점으로 돌아가는 식으로 순회를 계속할 수 있는 정점을 찾을 때까지 계속됩니다.
- 모든 정점이 스택에 있을 때까지 계속합니다.

public class Graph {
private final int MAX_VERTS = 10;
private Vertex vertexArray[]; // Array of vertices
private int adjMat[][]; // Adjacency matrix
private int nVerts; // Current number of vertices
private Stack stack;
public Graph() { // Initialize internal fields
vertexArray = new Vertex[MAX_VERTS];
adjMat = new int[MAX_VERTS][MAX_VERTS];
nVerts = 0;
for (int j = 0; j < MAX_VERTS; j++) {
for (int k = 0; k < MAX_VERTS; k++) {
adjMat[j][k] = 0;
}
}
stack = new Stack<>();
}
public void addVertex(char lab) {
vertexArray[nVerts++] = new Vertex(lab);
}
public void addEdge(int start, int end) {
adjMat[start][end] = 1;
adjMat[end][start] = 1;
}
public void displayVertex(int v) {
System.out.println(vertexArray[v].getLabel());
}
public void dfs() { // Depth-first search
vertexArray[0].setVisited(true); // Take the first vertex
displayVertex(0);
stack.push(0);
while (!stack.empty()) {
int v = getAdjUnvisitedVertex(stack.peek()); // Return the index of the adjacent vertex: if any, 1; if not, -1
if (v == -1) { // If there is no unvisited adjacent vertex
stack.pop(); // The element is removed from the stack
}
else {
vertexArray[v].setVisited(true);
displayVertex(v);
stack.push(v); // The element goes on top of the stack
}
}
for (int j = 0; j < nVerts; j++) { // Reset the flags
vertexArray[j].visited = false;
}
}
private int getAdjUnvisitedVertex(int v) {
for (int j = 0; j < nVerts; j++) {
if (adjMat[v][j] == 1 && vertexArray[j].visited == false) {
return j; // Returns the first vertex found
}
}
return -1;
}
}
꼭짓점은 다음과 같습니다.
public class Vertex {
private char label; // for example, 'A'
public boolean visited;
public Vertex(final char label) {
this.label = label;
visited = false;
}
public char getLabel() {
return this.label;
}
public boolean isVisited() {
return this.visited;
}
public void setVisited(final boolean visited) {
this.visited = visited;
}
}
특정 꼭짓점을 사용하여 이 알고리즘을 실행하고 올바르게 작동하는지 살펴보겠습니다.
public class Solution {
public static void main(String[] args) {
Graph graph = new Graph();
graph.addVertex('A'); //0
graph.addVertex('B'); //1
graph.addVertex('C'); //2
graph.addVertex('D'); //3
graph.addVertex('E'); //4
graph.addVertex('F'); //5
graph.addVertex('G'); //6
graph.addEdge(0,1);
graph.addEdge(0,2);
graph.addEdge(0,3);
graph.addEdge(1,4);
graph.addEdge(3,5);
graph.addEdge(5,6);
System.out.println("Visits: ");
graph.dfs();
}
}
콘솔 출력:
너비 우선 검색
깊이 우선 탐색과 마찬가지로 이 알고리즘은 가장 간단하고 기본적인 그래프 순회 방법 중 하나입니다. 요점은 현재 정점이 있다는 것입니다. 방문하지 않은 인접한 정점을 모두 대기열에 넣고 현재 정점이 되는 다음 요소(큐의 헤드에 저장된 정점)를 선택합니다. 이 알고리즘을 여러 단계로 나누어 다음 단계를 식별할 수 있습니다.- 현재 정점에 인접한 이전에 방문하지 않은 다음 정점을 방문하고 미리 방문했다고 표시한 후 대기열에 추가합니다.
- 1단계를 수행할 수 없는 경우 큐에서 정점을 제거하고 이를 현재 정점으로 만듭니다.
- 1단계와 2단계를 수행할 수 없으면 순회가 완료된 것입니다. 즉, 모든 정점이 순회된 것입니다(연결된 그래프가 있는 경우).

public class Graph {
private final int MAX_VERTS = 10;
private Vertex vertexList[]; // Array of vertices
private int adjMat[][]; // Adjacency matrix
private int nVerts; // Current number of vertices
private Queue queue;
public Graph() {
vertexList = new Vertex[MAX_VERTS];
adjMat = new int[MAX_VERTS][MAX_VERTS];
nVerts = 0;
for (int j = 0; j < MAX_VERTS; j++) {
for (int k = 0; k < MAX_VERTS; k++) { // Fill the adjacency matrix with zeros
adjMat[j][k] = 0;
}
}
queue = new PriorityQueue<>();
}
public void addVertex(char lab) {
vertexList[nVerts++] = new Vertex(lab);
}
public void addEdge(int start, int end) {
adjMat[start][end] = 1;
adjMat[end][start] = 1;
}
public void displayVertex(int v) {
System.out.println(vertexList[v].getLabel());
}
public void bfc() { // Depth-first search
vertexList[0].setVisited(true);
displayVertex(0);
queue.add(0);
int v2;
while (!queue.isEmpty()) {
int v = queue.remove();
while((v2 = getAdjUnvisitedVertex(v))!=-1) {// The loop runs until every adjacent vertex is found and added to the queue
vertexList[v2].visited = true;
displayVertex(v2);
queue.add(v2);
}
}
for (int j = 0; j < nVerts; j++) { // Reset the flags
vertexList[j].visited = false;
}
}
private int getAdjUnvisitedVertex(int v) {
for (int j = 0; j < nVerts; j++) {
if (adjMat[v][j] == 1 && vertexList[j].visited == false) {
return j; // Returns the first vertext found
}
}
return -1;
}
}
Vertex 클래스는 깊이 우선 검색 알고리즘에 사용한 클래스와 동일합니다. 이 알고리즘을 실행해 보겠습니다.
public class Solution {
public static void main(String[] args) {
Graph graph = new Graph();
graph.addVertex('A'); //0
graph.addVertex('B'); //1
graph.addVertex('C'); //2
graph.addVertex('D'); //3
graph.addVertex('E'); //4
graph.addVertex('F'); //5
graph.addVertex('G'); //6
graph.addEdge(0,1);
graph.addEdge(0,2);
graph.addEdge(0,3);
graph.addEdge(1,4);
graph.addEdge(3,5);
graph.addEdge(5,6);
System.out.println("Visits: ");
graph.bfc();
}
}
콘솔 출력:
4. Dijkstra 알고리즘
앞서 언급했듯이 그래프는 방향이 지정되거나 방향이 지정되지 않을 수 있습니다. 그리고 가중치도 부여될 수 있다는 것을 기억하실 것입니다. 가중 방향 그래프는 실생활에서 흔히 볼 수 있는 관계를 모델화합니다. 예를 들어 도시가 정점이고 그 사이의 가장자리가 방향 가장자리 방향으로 흐르는 일방통행 교통의 도로인 도시 지도입니다. 당신이 화물 회사이고 멀리 떨어져 있는 두 도시 사이의 최단 경로를 찾아야 한다고 가정해 보겠습니다. 어떻게 하시겠습니까? 두 정점 사이의 최단 경로를 찾는 것은 가중치 그래프를 사용하여 해결되는 가장 일반적인 문제 중 하나입니다. 이 문제를 해결하기 위해 Dijkstra 알고리즘을 사용합니다. 일단 실행하면 주어진 초기 정점에서 다른 모든 정점까지의 최단 경로를 알 수 있습니다. 이 알고리즘의 단계는 무엇입니까? 나는 이 질문에 대답하려고 노력할 것입니다. Dijkstra 알고리즘의 단계:- 1단계: 탐색 비용이 가장 낮은 인접 노드를 찾습니다(가장 낮은 간선 가중치). 당신은 맨 처음에 서서 어디로 갈지 생각하고 있습니다: 노드 A 또는 노드 B. 이러한 각 노드로 이동하는 데 드는 비용은 얼마입니까?
- 2단계: В에서 가장자리를 횡단할 때 알고리즘이 아직 방문하지 않은 노드 В의 모든 이웃까지 이동하는 거리를 계산합니다. 새 거리가 이전 거리보다 작으면 가장자리 B를 통과하는 경로가 이 정점에 대한 새로운 최단 경로가 됩니다.
- 3단계: 정점 B를 방문한 것으로 표시합니다.
- 4단계: 1단계로 이동합니다.

- 정점 A에는 세 가지 가능한 경로가 있습니다. 가중치가 3인 B, 가중치가 5인 C, 가중치가 7인 D입니다. 알고리즘의 첫 번째 단계에 따라 비용이 가장 낮은 노드(모서리 가중치)를 선택합니다. 이 경우 B.
-
B의 방문하지 않은 유일한 이웃은 정점 Е이므로 이 정점을 통과하면 경로가 무엇인지 확인합니다. 3(AB) + 6(BE) = 9.
따라서 A에서 E까지의 현재 최단 경로는 9라고 기록됩니다.
-
정점 B에 대한 작업이 완료되었으므로 가장자리의 가중치가 최소인 다음 정점을 선택합니다.
정점 A와 B에서 가능한 정점은 D(7), C(5) 또는 E(6)입니다.
С에 대한 가장자리의 가중치가 가장 작으므로 이 정점으로 이동합니다.
-
다음으로, 이전과 마찬가지로 C를 통과할 때 이웃 꼭지점까지의 최단 경로를 찾습니다.
-
AD = 5(AC) + 3(CD) = 8이지만 С를 통해 이전 최단 경로(AC = 7)가 이보다 작으므로 최단 경로(AC = 7)를 변경하지 않고 유지합니다.
-
CE = 5(AC) + 4(CE) = 9. 이 새로운 최단 경로는 이전 경로와 동일하므로 변경하지 않고 그대로 둡니다.
-
-
가장 가까운 접근 가능한 꼭짓점(E와 D)에서 가장자리 가중치가 가장 작은 꼭짓점, 즉 D(3)를 선택합니다.
-
우리는 이웃 F로 가는 최단 경로를 찾습니다.
AF = 7(AD) + 3(DF) = 9
-
가장 가까운 접근 가능한 정점(E 및 F)에서 가장자리 가중치가 가장 작은 정점, 즉 F(3)를 선택합니다.
-
이웃 G까지의 최단 경로를 찾으세요.
AG = 7(AD) + 3(DF) + 4(FG) = 14
그래서 우리는 A에서 G로 가는 길을 찾았습니다.
그러나 이것이 가장 짧은지 확인하려면 정점 E에서도 단계를 수행해야 합니다.
-
정점 G에는 방향이 있는 가장자리가 가리키는 이웃 정점이 없기 때문에 정점 E만 남아 있으므로 이를 선택합니다.
-
이웃 G까지의 최단 경로를 찾습니다.
AG = 3(AB) + 6(BE) + 6(EG) = 15. 이 경로는 이전 최단 경로(AG(14))보다 길기 때문에 이 경로를 변경하지 않고 그대로 둡니다.
G에서 이어지는 정점이 없기 때문에 이 정점에서 알고리즘의 단계를 실행하는 것은 의미가 없습니다. 이는 알고리즘 작업이 완료되었음을 의미합니다.
public class Vertex {
private char label;
private boolean isInTree;
public Vertex(char label) {
this.label = label;
this.isInTree = false;
}
public char getLabel() {
return label;
}
public void setLabel(char label) {
this.label = label;
}
public boolean isInTree() {
return isInTree;
}
public void setInTree(boolean inTree) {
isInTree = inTree;
}
}
정점 클래스는 너비 우선 검색에 사용되는 정점 클래스와 사실상 동일합니다. 최단 경로를 표시하려면 필요한 데이터를 포함하는 새 클래스가 필요합니다.
public class Path { // A class that contains the distance and the previous and traversed vertices
private int distance; // Current distance from the starting vertex
private List parentVertices; // Current parent vertex
public Path(int distance) {
this.distance = distance;
this.parentVertices = new ArrayList<>();
}
public int getDistance() {
return distance;
}
public void setDistance(int distance) {
this.distance = distance;
}
public List getParentVertices() {
return parentVertices;
}
public void setParentVertices(List parentVertices) {
this.parentVertices = parentVertices;
}
}
이 수업에서는 경로의 전체 거리와 최단 경로를 통과할 때 통과하게 될 정점을 볼 수 있습니다. 이제 실제로 그래프를 통해 최단 경로를 탐색하는 클래스를 살펴보겠습니다. 여기에 Graph 클래스가 있습니다.
public class Graph {
private final int MAX_VERTS = 10;// Maximum number of vertices
private final int INFINITY = 100000000; // This number represents infinity
private Vertex vertexList[]; // List of vertices
private int relationMatrix[][]; // Matrix of edges between vertices
private int numberOfVertices; // Current number of vertices
private int numberOfVerticesInTree; // Number of visited vertices in the tree
private List shortestPaths; // List of shortest paths
private int currentVertex; // Current vertex
private int startToCurrent; // Distance to currentVertex
public Graph() {
vertexList = new Vertex[MAX_VERTS]; // Adjacency matrix
relationMatrix = new int[MAX_VERTS][MAX_VERTS];
numberOfVertices = 0;
numberOfVerticesInTree = 0;
for (int i = 0; i < MAX_VERTS; i++) { // Fill the adjacency matrix
for (int k = 0; k < MAX_VERTS; k++) { // with "infinity"
relationMatrix[i][k] = INFINITY; // Assign default values
shortestPaths = new ArrayList<>(); // Assign an empty list
}
}
}
public void addVertex(char lab) {// Assign new vertices
vertexList[numberOfVertices++] = new Vertex(lab);
}
public void addEdge(int start, int end, int weight) {
relationMatrix[start][end] = weight; // Set weighted edges between vertices
}
public void path() { // Choose the shortest path
// Set the initial vertex data
int startTree = 0; // Start from vertex 0
vertexList[startTree].setInTree(true); // Include the first element in the tree
numberOfVerticesInTree = 1;
// Fill out the shortest paths for vertices adjacent to the initial vertex
for (int i = 0; i < numberOfVertices; i++) {
int tempDist = relationMatrix[startTree][i];
Path path = new Path(tempDist);
path.getParentVertices().add(0);// The starting vertex will always be a parent vertex
shortestPaths.add(path);
}
// Until every vertex is in the tree
while (numberOfVerticesInTree < numberOfVertices) { // Do this until the number of of vertices in the tree equals the total number of vertices
int indexMin = getMin();// Get the index of the of the vertex with the smallest distance from the vertices not yet in the tree
int minDist = shortestPaths.get(indexMin).getDistance(); // Minimum distance to the vertices not yet in the tree
if (minDist == INFINITY) {
System.out.println("The graph has an unreachable vertex");
break; // If only unreachable vertices have not been visited, then we exit the loop
} else {
currentVertex = indexMin; // Set currentVert to the index of the current vertex
startToCurrent = shortestPaths.get(indexMin).getDistance(); // Set the distance to the current vertex
}
vertexList[currentVertex].setInTree(true); // Add the current vertex to the tree
numberOfVerticesInTree++; // Increase the count of vertices in the tree
updateShortestPaths(); // Update the list of shortest paths
}
displayPaths(); // Display the results on the console
}
public void clear() { // Clear the tree
numberOfVerticesInTree = 0;
for (int i = 0; i < numberOfVertices; i++) {
vertexList[i].setInTree(false);
}
}
private int getMin() {
int minDist = INFINITY; // The distance of the initial shortest path is taken to be infinite
int indexMin = 0;
for (int i = 1; i < numberOfVertices; i++) { // For each vertex
if (!vertexList[i].isInTree() && shortestPaths.get(i).getDistance() < minDist) { // If the vertex is not yet in the tree and the distance to the vertex is less than the current minimum
minDist = shortestPaths.get(i).getDistance(); // then update the minimum
indexMin = i; // Update the index of the vertex with the minimum distance
}
}
return indexMin; // Returns the index of the vertex with the smallest distance among those not yet in the tree
}
private void updateShortestPaths() {
int vertexIndex = 1; // The initial vertex is skipped
while (vertexIndex < numberOfVertices) { // Run over the columns
if (vertexList[vertexIndex].isInTree()) { // If the column vertex is already in the tree, then we skip it
vertexIndex++;
continue;
}
// Calculate the distance for one element sPath
// Get the edge from currentVert to column
int currentToFringe = relationMatrix[currentVertex][vertexIndex];
// Add up all the distances
int startToFringe = startToCurrent + currentToFringe;
// Determine the distance to the current vertexIndex
int shortPathDistance = shortestPaths.get(vertexIndex).getDistance();
// Compare the distance through currentVertex with the current distance in the vertex with index vertexIndex
if (startToFringe < shortPathDistance) { // If it is smaller, then the vertex at vertexIndex is assigned the new shortest path
List newParents = new ArrayList<>(shortestPaths.get(currentVertex).getParentVertices()); // Create a copy of the list of vertices of vertex currentVert's parents
newParents.add(currentVertex); // And add currentVertex to it as the previous vertex
shortestPaths.get(vertexIndex).setParentVertices(newParents); // Save the new path
shortestPaths.get(vertexIndex).setDistance(startToFringe); // Save the new distance
}
vertexIndex++;
}
}
private void displayPaths() { // A method for displaying the shortest paths on the screen
for (int i = 0; i < numberOfVertices; i++) {
System.out.print(vertexList[i].getLabel() + " = ");
if (shortestPaths.get(i).getDistance() == INFINITY) {
System.out.println("0");
} else {
String result = shortestPaths.get(i).getDistance() + " (";
List parents = shortestPaths.get(i).getParentVertices();
for (int j = 0; j < parents.size(); j++) {
result += vertexList[parents.get(j)].getLabel() + " -> ";
}
System.out.println(result + vertexList[i].getLabel() + ")");
}
}
}
}
이것이 바로 마법입니다 =) 이제 이 알고리즘이 실제로 작동하는 모습을 살펴보겠습니다.
public class Solution {
public static void main(String[] args) {
Graph graph = new Graph();
graph.addVertex('A');
graph.addVertex('B');
graph.addVertex('C');
graph.addVertex('D');
graph.addVertex('E');
graph.addVertex('F');
graph.addVertex('G');
graph.addEdge(0, 1, 3);
graph.addEdge(0, 2, 5);
graph.addEdge(0, 3, 7);
graph.addEdge(1, 4, 6);
graph.addEdge(2, 4, 4);
graph.addEdge(2, 3, 3);
graph.addEdge(3, 5, 3);
graph.addEdge(4, 6, 6);
graph.addEdge(5, 6, 4);
System.out.println("The following nodes form the shortest paths from node A:");
graph.path();
graph.clear();
}
}
콘솔 출력은 다음과 같습니다.

GO TO FULL VERSION