diff --git a/problem-1/01-undirected-graph.png b/problem-1/01-undirected-graph.png new file mode 100644 index 0000000..01b0afd Binary files /dev/null and b/problem-1/01-undirected-graph.png differ diff --git a/problem-1/02-adjacency-matrix.png b/problem-1/02-adjacency-matrix.png new file mode 100644 index 0000000..9589259 Binary files /dev/null and b/problem-1/02-adjacency-matrix.png differ diff --git a/problem-1/03-adjacency-list.png b/problem-1/03-adjacency-list.png new file mode 100644 index 0000000..dee17e2 Binary files /dev/null and b/problem-1/03-adjacency-list.png differ diff --git a/problem-1/BFS.js b/problem-1/BFS.js index c9a3539..7e0085b 100644 --- a/problem-1/BFS.js +++ b/problem-1/BFS.js @@ -1,24 +1,58 @@ +const { Queue } = require('../data-structure/Queue'); +const { Stack } = require('../data-structure/Stack'); + class BFS { - #marked = []; + #visited = []; - #edgeTo = []; + #previousVertexOf = []; - #s; + #startVertex; - constructor(graph, s) { - this.#marked = Array.from({ length: graph.v() }, () => false); - this.#edgeTo = Array.from({ length: graph.v() }, () => undefined); - this.#s = s; - this.#bfs(graph, s); + constructor(graph, startVertex) { + this.#visited = Array.from({ length: graph.sizeOfVertices() }, () => false); + this.#previousVertexOf = Array.from({ length: graph.sizeOfVertices() }, () => undefined); + this.#startVertex = startVertex; + this.#bfs(graph, startVertex); } - #bfs(graph, s) { + #bfs(graph, startVertex) { + const queue = new Queue(); + + this.#visited[startVertex] = true; + queue.enqueue(startVertex); + + while (!queue.isEmpty()) { + const currentVertex = queue.dequeue(); + + for (const adjacentVertex of graph.adjacencyList(currentVertex)) { + if (!this.#visited[adjacentVertex]) { + this.#previousVertexOf[adjacentVertex] = currentVertex; + this.#visited[adjacentVertex] = true; + queue.enqueue(adjacentVertex); + } + } + } } - hasPathTo(v) { + // 해당 정점이 연결되어 있는지 안 되어 있는지 반환한다. + hasPathTo(vertex) { + return this.#visited[vertex]; } - pathTo(v) { + pathTo(vertex) { + // 해당 정점에 연결된 정점이 없다 = 해당 정점으로 갈 경로가 없다. + if (!this.hasPathTo(vertex)) { + return; + } + + const pathToVertex = new Stack(); + for (let item = vertex; item !== this.#startVertex; item = this.#previousVertexOf[item]) { + pathToVertex.push(item); + } + + pathToVertex.push(this.#startVertex); + + return pathToVertex; } } diff --git a/problem-1/ConnectedComponents.js b/problem-1/ConnectedComponents.js index 43bad56..3c4e8ff 100644 --- a/problem-1/ConnectedComponents.js +++ b/problem-1/ConnectedComponents.js @@ -1,8 +1,36 @@ class ConnectedComponents { + #visited = []; + + #groupIds = []; + + #groupId = 0; + constructor(graph) { + this.#visited = Array.from({ length: graph.sizeOfVertices() }, () => false); + this.#groupIds = Array.from({ length: graph.sizeOfVertices() }, () => undefined); + + for (let i = 0; i < graph.sizeOfVertices(); i++) { + if (!this.#visited[i]) { + this.#dfs(graph, i); + this.#groupId++; + } + } + } + + #dfs(graph, vertex) { + this.#visited[vertex] = true; + this.#groupIds[vertex] = this.#groupId; // 아이디를 기록한다. + + // 인접한 정점을 모두 확인한다. + for (const adjacentVertex of graph.adjacencyList(vertex)) { + if (!this.#visited[adjacentVertex]) { + this.#dfs(graph, adjacentVertex); + } + } } - connected(v, w) { + connected(vertex1, vertex2) { + return this.#groupIds[vertex1] === this.#groupIds[vertex2]; } } diff --git a/problem-1/DFS.js b/problem-1/DFS.js index ed14f13..ab65bf8 100644 --- a/problem-1/DFS.js +++ b/problem-1/DFS.js @@ -1,26 +1,50 @@ const { Stack } = require('../data-structure/Stack'); class DFS { - #marked = []; + #visited = []; - #edgeTo = []; + #previousVertexOf = []; - #s; + #startVertex; - constructor(graph, s) { - this.#marked = Array.from({ length: graph.v() }, () => false); - this.#edgeTo = Array.from({ length: graph.v() }, () => undefined); - this.#s = s; - this.#dfs(graph, s); + constructor(graph, startVertex) { + this.#visited = Array.from({ length: graph.sizeOfVertices() }, () => false); + this.#previousVertexOf = Array.from({ length: graph.sizeOfVertices() }, () => undefined); + this.#startVertex = startVertex; + this.#dfs(graph, startVertex); } - #dfs(grpah, v) { + #dfs(graph, currentVertex) { + // 선택한 정점의 방문 여부 체크 + this.#visited[currentVertex] = true; + + for (const adjacentVertex of graph.adjacencyList(currentVertex)) { + if (!this.#visited[adjacentVertex]) { // 방문한 적 없는 인접 정점일 경우 + this.#previousVertexOf[adjacentVertex] = currentVertex; // currentVertex 로부터 방문했다고 기록한다. + this.#dfs(graph, adjacentVertex); + } + } } - hasPathTo(v) { + // 해당 정점이 연결되어 있는지 안 되어 있는지 반환한다. + hasPathTo(vertex) { + return this.#visited[vertex]; } - pathTo(v) { + pathTo(vertex) { + // 해당 정점에 연결된 정점이 없다 = 해당 정점으로 갈 경로가 없다. + if (!this.hasPathTo(vertex)) { + return; + } + + const pathToVertex = new Stack(); + for (let item = vertex; item !== this.#startVertex; item = this.#previousVertexOf[item]) { + pathToVertex.push(item); + } + + pathToVertex.push(this.#startVertex); + + return pathToVertex; } } diff --git a/problem-1/Graph.js b/problem-1/Graph.js index 37762f0..b7dd3cf 100644 --- a/problem-1/Graph.js +++ b/problem-1/Graph.js @@ -1,35 +1,36 @@ const { Bag } = require('../data-structure/Bag'); class Graph { - #v; + #numberOfVertices; - #e; + #numberOfEdges; - #adj = []; + #adjacencyList = []; - constructor(v) { - this.#v = v; - for (let i = 0; i < this.#v; i++) { - this.#adj[i] = new Bag(); + constructor(numberOfVertices) { + this.#numberOfVertices = numberOfVertices; + + for (let i = 0; i < this.#numberOfVertices; i++) { + this.#adjacencyList[i] = new Bag(); } } - addEdge(v, w) { - this.#adj[v].add(w); - this.#adj[w].add(v); - this.#e++; + addEdge(source, destination) { + this.#adjacencyList[source].add(destination); + this.#adjacencyList[destination].add(source); + this.#numberOfEdges++; } - adj(v) { - return this.#adj[v]; + adjacencyList(vertex) { + return this.#adjacencyList[vertex]; } - v() { - return this.#v; + sizeOfVertices() { + return this.#numberOfVertices; } - e() { - return this.#e; + sizeOfEdges() { + return this.#numberOfEdges; } } diff --git a/problem-1/README.md b/problem-1/README.md index 5154fc5..ab54969 100644 --- a/problem-1/README.md +++ b/problem-1/README.md @@ -20,12 +20,17 @@ 8 1 4 1 ``` +![01-undirected-graph.png](01-undirected-graph.png) 2. 위의 그려진 그래프를 인접 행렬로 표현되는 데이터 구조를 그림으로 그려주세요. +![02-adjacency-matrix.png](02-adjacency-matrix.png) + 3. 위의 그려진 그래프를 인접 리스트로 표현되는 데이터 구조로 그림으로 그려주세요. +![03-adjacency-list.png](03-adjacency-list.png) + 4. 위의 그래프에서 10번 노드에서 6번 노드로 DFS를 할 때 어떤 경로를 통해서 가는지 구하는 함수를 구현해 주세요. 5. 마찬가지로 10번 노드에서 6번 노드로 BFS를 할 때 어떤 경로를 통해서 가는지 함수를 구하는 구현해 주세요. diff --git a/problem-2/Diagraph.js b/problem-2/Diagraph.js index a7e41bb..e559d31 100644 --- a/problem-2/Diagraph.js +++ b/problem-2/Diagraph.js @@ -1,34 +1,34 @@ const { Bag } = require('../data-structure/Bag'); class Diagraph { - #v; + #numberOfVertices; - #e; + #numberOfEdges; - #adj = []; + #adjacencyList = []; - constructor(v) { - this.#v = v; - for (let i = 0; i < this.#v; i++) { - this.#adj[i] = new Bag(); + constructor(numberOfVertices) { + this.#numberOfVertices = numberOfVertices; + for (let i = 0; i < this.#numberOfVertices; i++) { + this.#adjacencyList[i] = new Bag(); } } - addEdge(v, w) { - this.#adj[v].add(w); - this.#e++; + addEdge(source, destination) { + this.#adjacencyList[source].add(destination); + this.#numberOfEdges++; } - adj(v) { - return this.#adj[v]; + adjacencyList(vertex) { + return this.#adjacencyList[vertex]; } - v() { - return this.#v; + sizeOfVertices() { + return this.#numberOfVertices; } - e() { - return this.#e; + sizeOfEdges() { + return this.#numberOfEdges; } } diff --git a/problem-2/DirectedCycle.js b/problem-2/DirectedCycle.js index d08ca09..13d37ee 100644 --- a/problem-2/DirectedCycle.js +++ b/problem-2/DirectedCycle.js @@ -1,27 +1,63 @@ const { Stack } = require('../data-structure/Stack'); class DirectedCycle { - #marked = []; + #visited = []; - #edgeTo = []; + #previousVertexOf = []; - #onStack = []; + #visiting = []; #cycle; constructor(diagraph) { - this.#marked = Array.from({ length: diagraph.v() }, () => false); - this.#edgeTo = Array.from({ length: diagraph.v() }, () => undefined); - this.#onStack = Array.from({ length: diagraph.v() }, () => false); + this.#visited = Array.from({ length: diagraph.sizeOfVertices() }, () => false); + this.#previousVertexOf = Array.from({ length: diagraph.sizeOfVertices() }, () => undefined); + this.#visiting = Array.from({ length: diagraph.sizeOfVertices() }, () => false); + + for (let vertex = 0; vertex < diagraph.sizeOfVertices(); vertex++) { + if (!this.#visited[vertex]) { + this.#dfs(diagraph, vertex); + } + } } - #dfs(diagraph, v) { + #dfs(diagraph, vertex) { + this.#visited[vertex] = true; // 방문했음을 체크 + this.#visiting[vertex] = true; // 현재 방문 중임을 체크 + + // todo : 인접한 정점을 돌면서 순환 경로(cycle)이 있는지 체크하기 + for (const adjacentVertex of diagraph.adjacencyList(vertex)) { + if (this.hasCycle()) { // 이미 순환경로가 있다면 종료 + return; + } + + if (!this.#visited[adjacentVertex]) { // 만약 방문한 적 없는 인접 정점이라면 + this.#previousVertexOf[adjacentVertex] = vertex; // 현재 정점에서 방문했다고 기록 + this.#dfs(diagraph, adjacentVertex); // 해당 인접 정점에 인접하는 정점을 돌면서 재귀적으로 실행 + } else if (this.#visiting[adjacentVertex]) { + // 현재의 인접 정점이 방문도 했고, 현재 방문중인 정점이라면 -> 순환한다는 것 + this.#cycle = new Stack(); // 순환 경로를 기록할 stack 생성 + + for (let v = vertex; v !== adjacentVertex; v = this.#previousVertexOf[v]) { + // 현재 정점이 어디에서부터 왔는지 거슬러 올라가면서 순환 경로 스택에 추가한다. + this.#cycle.push(v); + } + + // 목적지와 출발지 담기 + this.#cycle.push(adjacentVertex); + this.#cycle.push(vertex); + } + } + + this.#visiting[vertex] = false; } hasCycle() { + return !!this.#cycle; } cycle() { + return this.#cycle; } } diff --git a/problem-2/DirectedDFS.js b/problem-2/DirectedDFS.js index 5c9108a..221b6bc 100644 --- a/problem-2/DirectedDFS.js +++ b/problem-2/DirectedDFS.js @@ -1,15 +1,26 @@ class DirectedDFS { - #marked = []; + #visited = []; - constructor(diagraph, s) { - this.#marked = Array.from({ length: diagraph.v() }, () => false); - this.#dfs(diagraph, s); + constructor(diagraph, startVertex) { + this.#visited = Array.from({ length: diagraph.sizeOfVertices() }, () => false); + this.#dfs(diagraph, startVertex); } - #dfs(diagraph, v) { + #dfs(diagraph, vertex) { + // 현재 정점을 방문했다고 표시 + this.#visited[vertex] = true; + + // 인접한 정점에서 체크하기 + for (const adjacentVertex of diagraph.adjacencyList(vertex)) { + // 현재의 인접한 정점에 방문한 적이 없을 경우 + if (!this.#visited[adjacentVertex]) { + this.#dfs(diagraph, adjacentVertex); + } + } } - reachable(v) { + reachable(vertex) { + return this.#visited[vertex]; // 방문한 적이 있는 정점이라면 도달 가능한 것. } } diff --git a/problem-2/README.md b/problem-2/README.md index f81c573..902b2bc 100644 --- a/problem-2/README.md +++ b/problem-2/README.md @@ -21,6 +21,7 @@ 8 1 4 1 ``` +![directed-graph.png](directed-graph.png) 2. 도달성을 확인하는 객체 `DirectedDFS.js`를 완성해 주세요. diff --git a/problem-2/directed-graph.png b/problem-2/directed-graph.png new file mode 100644 index 0000000..dc3136f Binary files /dev/null and b/problem-2/directed-graph.png differ