diff --git a/.babelrc b/.babelrc
new file mode 100644
index 0000000..8aa924d
--- /dev/null
+++ b/.babelrc
@@ -0,0 +1,3 @@
+{
+ "presets": ["@babel/preset-env"]
+}
\ No newline at end of file
diff --git a/.eslintrc.json b/.eslintrc.json
new file mode 100644
index 0000000..54b9a9b
--- /dev/null
+++ b/.eslintrc.json
@@ -0,0 +1,18 @@
+{
+ "extends": ["airbnb", "prettier"],
+ "env": {
+ "browser": true,
+ "jest/globals": true,
+ "es2020": true
+ },
+ "parser": "@babel/eslint-parser",
+ "parserOptions": {
+ "ecmaVersion": 12,
+ "sourceType": "module"
+ },
+ "plugins": ["prettier", "jest", "@babel"],
+ "rules": {
+ "import/extensions": ["off"],
+ "lines-between-class-members": ["off"]
+ }
+}
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..448cd77
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+.vscode/
+node_modules/
+package.json
+package-lock.json
diff --git a/README.md b/README.md
deleted file mode 100644
index 51abd25..0000000
--- a/README.md
+++ /dev/null
@@ -1 +0,0 @@
-# javascript-subway-final
\ No newline at end of file
diff --git a/babel.config.js b/babel.config.js
new file mode 100644
index 0000000..14906bb
--- /dev/null
+++ b/babel.config.js
@@ -0,0 +1,6 @@
+module.exports = {
+ plugins: [
+ '@babel/plugin-proposal-private-methods',
+ '@babel/plugin-proposal-class-properties',
+ ],
+};
diff --git a/index.css b/index.css
new file mode 100644
index 0000000..d78f42b
--- /dev/null
+++ b/index.css
@@ -0,0 +1,3 @@
+#result {
+ display: none;
+}
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..d100630
--- /dev/null
+++ b/index.html
@@ -0,0 +1,49 @@
+
+
+
+
+ 지하철 길찾기
+
+
+
+
+
지하철 길찾기
+
+
+
+
+
+
+
+
+
결과
+
최단거리
+
+
+
+ | 총 거리 |
+ 총 소요 시간 |
+
+
+
+
+
+
+
+
+
diff --git a/src/classes/station/station.js b/src/classes/station/station.js
new file mode 100644
index 0000000..95d917d
--- /dev/null
+++ b/src/classes/station/station.js
@@ -0,0 +1,11 @@
+import { MIN_STATION_NAME_LENGTH } from '../../constants/constants.js';
+
+export default class Station {
+ static isStationNameTooShort(stationName) {
+ return stationName.length < MIN_STATION_NAME_LENGTH;
+ }
+
+ static isStationNamesSame(startStationName, endStationName) {
+ return startStationName === endStationName;
+ }
+}
diff --git a/src/classes/subwayMap/subwayMap.js b/src/classes/subwayMap/subwayMap.js
new file mode 100644
index 0000000..4387cba
--- /dev/null
+++ b/src/classes/subwayMap/subwayMap.js
@@ -0,0 +1,90 @@
+import { initalLineData, initalStationData } from '../../data/data.js';
+
+export default class SubwayMap {
+ #allLines;
+ #allStations;
+ #pathInfo = { totalDistance: 0, totalTime: 0 };
+ #path; constructor() {
+ this.#allStations = initalStationData;
+ this.#allLines = initalLineData;
+ }
+
+ get allLines() {
+ return this.#allLines;
+ }
+
+ get allStations() {
+ return this.#allStations;
+ }
+
+ setPath(path) {
+ this.#path = path;
+ }
+
+ getTotalDistanceAndTimeFromPath() {
+ this.#pathInfo.totalDistance = 0;
+ this.#pathInfo.totalTime = 0;
+ const initialIndex = 0;
+ const initialDistance = 0;
+ const initialTime = 0;
+
+ this.#traverseSubwayMap(initialIndex, initialDistance, initialTime);
+ return this.#pathInfo;
+ }
+
+ // 첫번째 노드를 바탕으로 역들을 순회
+ // 순회하다가 발견하면 연결된 노드들 중에 index + 1에 해당하는 값이 있는지 확인
+ // 있다면 info 를 더함
+
+ #traverseSubwayMap(index, distance, time) {
+ if (index >= this.#path.length - 1) {
+ this.#pathInfo.totalDistance = distance;
+ this.#pathInfo.totalTime = time;
+ }
+ const stationName = this.#path[index];
+ const newIndex = index + 1;
+ let addedDistance = distance;
+ let addedTime = time;
+
+ const station = this.#allStations[stationName];
+ station.connected.forEach((connectedStation) => {
+ const nextPathStation = this.#path[newIndex];
+ if (connectedStation.station === nextPathStation) {
+ addedDistance += connectedStation.distance;
+ addedTime += connectedStation.time;
+ this.#traverseSubwayMap(newIndex, addedDistance, addedTime);
+ }
+ });
+ }
+
+ isStationNotExist(stationName) {
+ let isExist = true;
+ const allLineNames = Object.keys(this.#allLines);
+ allLineNames.forEach((lineName) => {
+ const line = this.#allLines[lineName];
+ line.forEach((section) => {
+ if (section.station === stationName) {
+ isExist = false;
+ }
+ });
+ });
+
+ return isExist;
+ }
+
+ isStationsConnected(fromStationName, toStationName) {
+ let isConnected = false;
+ const allLineNames = Object.keys(this.#allLines);
+ allLineNames.forEach((lineName) => {
+ const line = this.#allLines[lineName];
+ const fileterdLine = line.filter(
+ (section) => section.station === fromStationName || toStationName
+ );
+ if (fileterdLine.length === 2) {
+ isConnected = true;
+ }
+ });
+
+ return isConnected;
+ }
+}
diff --git a/src/classes/subwayPath/subwayPath.js b/src/classes/subwayPath/subwayPath.js
new file mode 100644
index 0000000..f5faa96
--- /dev/null
+++ b/src/classes/subwayPath/subwayPath.js
@@ -0,0 +1,13 @@
+import DijstraStore from '../../store/dijkstra.js';
+
+const { distanceDijkstra, timeDijkstra } = DijstraStore;
+
+export default class subwayPath {
+ static getMinDistancePath(startStationName, endStationName) {
+ return distanceDijkstra.findShortestPath(startStationName, endStationName);
+ }
+
+ static getMinTimePath(startStationName, endStationName) {
+ return timeDijkstra.findShortestPath(startStationName, endStationName);
+ }
+}
diff --git a/src/classes/subwayPath/subwayPathUI.js b/src/classes/subwayPath/subwayPathUI.js
new file mode 100644
index 0000000..34bb36f
--- /dev/null
+++ b/src/classes/subwayPath/subwayPathUI.js
@@ -0,0 +1,121 @@
+import {
+ MIN_DISTANCE_CHECK_OPTION_NAME,
+ MIN_TIME_CHECK_OPTION_NAME,
+ TOO_SHORT_STATION_NAME_MESSAGE,
+ NOT_CONNECTED_STATIONS_MESSAGE,
+ SAME_STATIONS_MESSAGE,
+ STATION_NOT_EXIST_MESSAGE,
+} from '../../constants/constants.js';
+import {
+ resultElement,
+ resultTableBodyElement,
+ arrivalStationNameInputElement,
+ departureStationNameInputElement,
+ minDistancePathSearchRadioElement,
+ minTimePathSearchRadioElement,
+} from '../../elements/subwayPath.js';
+import SubwayPath from './subwayPath.js';
+import Station from '../station/station.js';
+import { subwayMap } from '../../store/subway.js';
+import { getResultTableBodyTemplate } from '../../templates/table.js';
+
+export default class SubwayPathUI {
+ static getCheckedOptionName() {
+ let checkedOptionName = '';
+ if (minDistancePathSearchRadioElement.checked === true) {
+ checkedOptionName = MIN_DISTANCE_CHECK_OPTION_NAME;
+ } else if (minTimePathSearchRadioElement.checked === true) {
+ checkedOptionName = MIN_TIME_CHECK_OPTION_NAME;
+ }
+
+ return checkedOptionName;
+ }
+
+ static getInvalidInputAlertMessage(startStationName, endStationName) {
+ let alertMessage = '';
+ if (
+ Station.isStationNameTooShort(startStationName) ||
+ Station.isStationNameTooShort(endStationName)
+ ) {
+ alertMessage += TOO_SHORT_STATION_NAME_MESSAGE;
+ }
+ if (Station.isStationNamesSame(startStationName, endStationName)) {
+ alertMessage += SAME_STATIONS_MESSAGE;
+ }
+
+ return alertMessage;
+ }
+
+ static getInvalidSearchAlertMessage(startStationName, endStationName) {
+ let alertMessage = '';
+ if (
+ subwayMap.isStationNotExist(startStationName) ||
+ subwayMap.isStationNotExist(endStationName)
+ ) {
+ alertMessage += STATION_NOT_EXIST_MESSAGE;
+ }
+ if (subwayMap.isStationsConnected(startStationName, endStationName)) {
+ alertMessage += NOT_CONNECTED_STATIONS_MESSAGE;
+ }
+
+ return alertMessage;
+ }
+
+ static showResultToTable({
+ checkedOptionName,
+ departureStationName,
+ arrivalStationName,
+ }) {
+ let resultPath;
+ if (checkedOptionName === MIN_DISTANCE_CHECK_OPTION_NAME) {
+ resultPath = SubwayPath.getMinDistancePath(
+ departureStationName,
+ arrivalStationName
+ );
+ } else if (checkedOptionName === MIN_TIME_CHECK_OPTION_NAME) {
+ resultPath = SubwayPath.getMinTimePath(
+ departureStationName,
+ arrivalStationName
+ );
+ }
+ subwayMap.setPath(resultPath);
+ const {
+ totalDistance,
+ totalTime,
+ } = subwayMap.getTotalDistanceAndTimeFromPath();
+ const resultTableBodyTemplate = getResultTableBodyTemplate(
+ totalDistance,
+ totalTime,
+ resultPath
+ );
+ resultElement.setAttribute('style', 'display: block;');
+ resultTableBodyElement.innerHTML = resultTableBodyTemplate;
+ }
+
+ static showPath() {
+ const departureStationName = departureStationNameInputElement.value;
+ const arrivalStationName = arrivalStationNameInputElement.value;
+ const checkedOptionName = SubwayPathUI.getCheckedOptionName();
+ const invalidInputAlertMessage = SubwayPathUI.getInvalidInputAlertMessage(
+ departureStationName,
+ arrivalStationName
+ );
+ if (invalidInputAlertMessage !== '') {
+ alert(invalidInputAlertMessage);
+ return;
+ }
+ const invalidSearchAlertMessage = SubwayPathUI.getInvalidSearchAlertMessage(
+ departureStationName,
+ arrivalStationName
+ );
+ if (invalidSearchAlertMessage !== '') {
+ alert(invalidSearchAlertMessage);
+ } else {
+ SubwayPathUI.showResultToTable({
+ checkedOptionName,
+ departureStationName,
+ arrivalStationName,
+ });
+ }
+ }
+}
diff --git a/src/constants/constants.js b/src/constants/constants.js
new file mode 100644
index 0000000..f10d98b
--- /dev/null
+++ b/src/constants/constants.js
@@ -0,0 +1,18 @@
+export const MIN_STATION_NAME_LENGTH = 2;
+export const MIN_DISTANCE_CHECK_OPTION_NAME = '최단거리';
+export const MIN_TIME_CHECK_OPTION_NAME = '최소시간';
+export const TOO_SHORT_STATION_NAME_MESSAGE =
+ '입력하신 역들 중 이름이 너무 짧은 역이 있습니다';
+export const SAME_STATIONS_MESSAGE = '출발역과 도착역의 이름이 동일합니다';
+export const NOT_CONNECTED_STATIONS_MESSAGE =
+ '출발역으로부터 도착역에 도달할 수 없습니다';
+export const STATION_NOT_EXIST_MESSAGE = '존재하지 않는 역을 입력하셨습니다'
+
+export default {
+ MIN_STATION_NAME_LENGTH,
+ MIN_DISTANCE_CHECK_OPTION_NAME,
+ MIN_TIME_CHECK_OPTION_NAME,
+ TOO_SHORT_STATION_NAME_MESSAGE,
+ SAME_STATIONS_MESSAGE,
+ NOT_CONNECTED_STATIONS_MESSAGE
+};
diff --git a/src/data/data.js b/src/data/data.js
new file mode 100644
index 0000000..1ad4904
--- /dev/null
+++ b/src/data/data.js
@@ -0,0 +1,62 @@
+export const initalStationData = {
+ 교대: {
+ connected: [
+ { station: '강남', distance: 2, time: 3 },
+ { station: '남부터미널', distance: 3, time: 2 },
+ ],
+ },
+ 강남: {
+ connected: [
+ { station: '교대', distance: 2, time: 3 },
+ { station: '역삼', distance: 2, time: 3 },
+ { station: '양재', distance: 2, time: 8 },
+ ],
+ },
+ 역삼: {
+ connected: [{ station: '강남', distance: 2, time: 3 }],
+ },
+ 남부터미널: {
+ connected: [
+ { station: '교대', distance: 3, time: 2 },
+ { station: '양재', distance: 6, time: 5 },
+ ],
+ },
+ 양재: {
+ connected: [
+ { station: '남부터미널', distance: 3, time: 2 },
+ { station: '매봉', distance: 1, time: 1 },
+ { station: '강남', distance: 2, time: 8 },
+ { station: '양재시민의 숲', distance: 10, time: 3 },
+ ],
+ },
+ 매봉: {
+ connected: [{ station: '양재', distance: 1, time: 1 }],
+ },
+ '양재시민의 숲': {
+ connected: [{ station: '양재', distance: 10, time: 3 }],
+ },
+};
+
+export const initalLineData = {
+ '2호선': [
+ { station: '교대', distance: 0, time: 0 },
+ { station: '강남', distance: 2, time: 3 },
+ { station: '역삼', distance: 2, time: 3 },
+ ],
+ '3호선': [
+ { station: '교대', distance: 0, time: 0 },
+ { station: '남부터미널', distance: 3, time: 2 },
+ { station: '양재', distance: 6, time: 5 },
+ { station: '매봉', distance: 1, time: 1 },
+ ],
+ 신분당선: [
+ { station: '강남', distance: 0, time: 0 },
+ { station: '양재', distance: 2, time: 8 },
+ { station: '양재시민의 숲', distance: 10, time: 3 },
+ ],
+};
+
+export default {
+ initalLineData,
+ initalStationData,
+};
diff --git a/src/docs/README.md b/src/docs/README.md
new file mode 100644
index 0000000..940c615
--- /dev/null
+++ b/src/docs/README.md
@@ -0,0 +1,29 @@
+# 🚇 지하철 노선도 경로 조회 프로그램
+
+- 등록된 지하철 노선도에서 경로를 조회할 수 있는 프로그램입니다.
+
+## 🚀 기능 목록
+
+### 초기 설정
+
+- 역, 노선, 구간 데이터와 인터페이스를 초기 설정
+
+### 경로 조회 기능
+
+
+
+- 출발역과 도착역이 2글자 이상인지 검사
+- 존재하지 않는 역을 출발역 또는 도착역으로 입력했는지 검사
+- 경로 조회 시 출발역과 도착역이 같은지 검사
+- 경로 조회 시 출발역과 도착역이 연결되어 있지 않은지 검사
+- 출발역과 도착역을 입력받아 최단 경로를 조회
+- 출발역과 도착역을 입력받아 최소시간 경로를 조회
+- 최단 거리 또는 최소 시간 옵션을 바탕으로 경로의 총 거리, 총 소요 시간 출력
+
+
+
+## 💻 프로그램 실행 결과
+
+### 경로 조회
+
+
diff --git a/src/elements/subwayPath.js b/src/elements/subwayPath.js
new file mode 100644
index 0000000..e592898
--- /dev/null
+++ b/src/elements/subwayPath.js
@@ -0,0 +1,25 @@
+export const departureStationNameInputElement = document.getElementById(
+ 'departure-station-name-input'
+);
+export const arrivalStationNameInputElement = document.getElementById(
+ 'arrival-station-name-input'
+);
+const searchOptionRadioElements = document.querySelectorAll(
+ 'input[name="search-type"]'
+);
+export const minDistancePathSearchRadioElement = searchOptionRadioElements[0];
+export const minTimePathSearchRadioElement = searchOptionRadioElements[1];
+export const resultTableBodyElement = document.getElementById(
+ 'result-table-body'
+);
+export const resultElement = document.getElementById('result');
+export const searchButtonElement = document.getElementById('search-button');
+
+export default {
+ departureStationNameInputElement,
+ minDistancePathSearchRadioElement,
+ minTimePathSearchRadioElement,
+ searchButtonElement,
+ resultTableBodyElement,
+ resultElement,
+};
diff --git a/src/handlers/subwayPath.js b/src/handlers/subwayPath.js
new file mode 100644
index 0000000..924f2ef
--- /dev/null
+++ b/src/handlers/subwayPath.js
@@ -0,0 +1,9 @@
+import SubwayPathUI from '../classes/subwayPath/subwayPathUI.js';
+
+export const onSearchPath = () => {
+ SubwayPathUI.showPath();
+};
+
+export default {
+ onSearchPath,
+};
diff --git a/src/images/dijkstra_example.png b/src/images/dijkstra_example.png
new file mode 100644
index 0000000..7c75197
Binary files /dev/null and b/src/images/dijkstra_example.png differ
diff --git a/src/images/path_result.gif b/src/images/path_result.gif
new file mode 100644
index 0000000..ee394bd
Binary files /dev/null and b/src/images/path_result.gif differ
diff --git a/src/images/path_result.jpg b/src/images/path_result.jpg
new file mode 100644
index 0000000..40a4bed
Binary files /dev/null and b/src/images/path_result.jpg differ
diff --git a/src/index.js b/src/index.js
new file mode 100644
index 0000000..bb5bc8b
--- /dev/null
+++ b/src/index.js
@@ -0,0 +1,3 @@
+import { setSubwayPathEventListener } from './listeners/subwayPath.js';
+
+setSubwayPathEventListener();
diff --git a/src/listeners/subwayPath.js b/src/listeners/subwayPath.js
new file mode 100644
index 0000000..432b781
--- /dev/null
+++ b/src/listeners/subwayPath.js
@@ -0,0 +1,10 @@
+import { searchButtonElement } from '../elements/subwayPath.js';
+import { onSearchPath } from '../handlers/subwayPath.js';
+
+export const setSubwayPathEventListener = () => {
+ searchButtonElement.addEventListener('click', onSearchPath);
+};
+
+export default {
+ setSubwayPathEventListener,
+};
diff --git a/src/store/dijkstra.js b/src/store/dijkstra.js
new file mode 100644
index 0000000..1889493
--- /dev/null
+++ b/src/store/dijkstra.js
@@ -0,0 +1,29 @@
+import Dijkstra from '../utils/Dijkstra.js';
+
+const distanceDijkstra = new Dijkstra();
+const timeDijkstra = new Dijkstra();
+
+distanceDijkstra.addEdge('교대', '강남', 2);
+distanceDijkstra.addEdge('강남', '역삼', 2);
+
+distanceDijkstra.addEdge('교대', '남부터미널', 3);
+distanceDijkstra.addEdge('남부터미널', '양재', 6);
+distanceDijkstra.addEdge('양재', '매봉', 1);
+
+distanceDijkstra.addEdge('강남', '양재', 2);
+distanceDijkstra.addEdge('양재', '양재시민의', 10);
+
+timeDijkstra.addEdge('교대', '강남', 3);
+timeDijkstra.addEdge('강남', '역삼', 3);
+
+timeDijkstra.addEdge('교대', '남부터미널', 2);
+timeDijkstra.addEdge('남부터미널', '양재', 5);
+timeDijkstra.addEdge('양재', '매봉', 1);
+
+timeDijkstra.addEdge('강남', '양재', 8);
+timeDijkstra.addEdge('양재', '양재시민의', 3);
+
+export default {
+ distanceDijkstra,
+ timeDijkstra,
+};
diff --git a/src/store/subway.js b/src/store/subway.js
new file mode 100644
index 0000000..5c0a0be
--- /dev/null
+++ b/src/store/subway.js
@@ -0,0 +1,10 @@
+import SubwayPath from '../classes/subwayPath/subwayPath.js';
+import SubwayMap from '../classes/subwayMap/subwayMap.js';
+
+export const subwayPath = new SubwayPath();
+export const subwayMap = new SubwayMap();
+
+export default {
+ subwayPath,
+ subwayMap,
+};
diff --git a/src/templates/table.js b/src/templates/table.js
new file mode 100644
index 0000000..b506cc8
--- /dev/null
+++ b/src/templates/table.js
@@ -0,0 +1,30 @@
+const getPathRowTemplate = (nodes) => {
+ let nodesTemplate = '';
+ nodes.forEach((node, index) => {
+ nodesTemplate += node;
+ if (index !== nodes.length - 1) {
+ nodesTemplate += '🡆';
+ }
+ });
+ return `
+ ${nodesTemplate} |
+ `;
+};
+
+export const getResultTableBodyTemplate = (distance, time, path) => {
+ const pathRowTemplate = getPathRowTemplate(path);
+
+ return `
+
+ | ${distance}km |
+ ${time}분 |
+
+
+ ${pathRowTemplate}
+
+ `;
+};
+
+export default {
+ getResultTableBodyTemplate,
+};
diff --git a/src/utils/Dijkstra.js b/src/utils/Dijkstra.js
new file mode 100644
index 0000000..7c6cfd5
--- /dev/null
+++ b/src/utils/Dijkstra.js
@@ -0,0 +1,220 @@
+export default function Dijkstra() {
+ const Node = {
+ init: function (val, priority) {
+ this.val = val;
+ this.priority = priority;
+ },
+ };
+
+ const PriorityQueue = {
+ init: function () {
+ this.values = [];
+ },
+ enqueue: function (val, priority) {
+ const newNode = Object.create(Node);
+ newNode.init(val, priority);
+
+ this.values.push(newNode);
+
+ let idxOfNewNode = this.values.length - 1;
+
+ while (idxOfNewNode > 0) {
+ const idxOfParentNode = Math.floor((idxOfNewNode - 1) / 2);
+
+ const parentNode = this.values[idxOfParentNode];
+
+ if (priority < parentNode.priority) {
+ this.values[idxOfParentNode] = newNode;
+ this.values[idxOfNewNode] = parentNode;
+ idxOfNewNode = idxOfParentNode;
+ continue;
+ }
+ break;
+ }
+ return this.values;
+ },
+ dequeue: function () {
+ if (this.values.length == 0) {
+ return;
+ }
+ const dequeued = this.values.shift();
+ const lastItem = this.values.pop();
+ if (!lastItem) {
+ return dequeued;
+ }
+ this.values.unshift(lastItem);
+
+ let idxOfTarget = 0;
+
+ while (true) {
+ let idxOfLeftChild = idxOfTarget * 2 + 1;
+ let idxOfRightChild = idxOfTarget * 2 + 2;
+ let leftChild = this.values[idxOfLeftChild];
+ let rightChild = this.values[idxOfRightChild];
+
+ function swap(direction) {
+ const idxOfChild =
+ direction == "left" ? idxOfLeftChild : idxOfRightChild;
+ const child = direction == "left" ? leftChild : rightChild;
+ this.values[idxOfChild] = this.values[idxOfTarget];
+ this.values[idxOfTarget] = child;
+ idxOfTarget = idxOfChild;
+ }
+
+ if (!leftChild) {
+ return dequeued;
+ }
+
+ if (!rightChild) {
+ if (leftChild.priority < lastItem.priority) {
+ swap.call(this, "left");
+ continue;
+ }
+ return dequeued;
+ }
+
+ if (leftChild.priority == rightChild.priority) {
+ swap.call(this, "left");
+ continue;
+ }
+
+ if (
+ leftChild.priority < rightChild.priority &&
+ leftChild.priority < lastItem.priority
+ ) {
+ swap.call(this, "left");
+ continue;
+ }
+
+ if (
+ rightChild.priority < leftChild.priority &&
+ rightChild.priority < lastItem.priority
+ ) {
+ swap.call(this, "right");
+ continue;
+ }
+ }
+ },
+ };
+
+ const WeightedGraph = {
+ init: function () {
+ this.adjacencyList = {};
+ this.length = 0;
+ },
+ addVertex: function (vertex) {
+ if (!this.adjacencyList.hasOwnProperty(vertex)) {
+ this.adjacencyList[vertex] = {};
+ this.length++;
+ }
+ },
+ addEdge: function (vertex1, vertex2, weight) {
+ this.addVertex(vertex1);
+ this.addVertex(vertex2);
+ this.adjacencyList[vertex1][vertex2] = weight;
+ this.adjacencyList[vertex2][vertex1] = weight;
+ return this.adjacencyList;
+ },
+ removeEdge: function (vertex1, vertex2) {
+ if (!this.adjacencyList.hasOwnProperty(vertex1)) {
+ return `There's no ${vertex1}`;
+ }
+ if (!this.adjacencyList.hasOwnProperty(vertex2)) {
+ return `There's no ${vertex2}`;
+ }
+
+ function removeHelper(v1, v2) {
+ if (!this.adjacencyList.hasOwnProperty(v1)) {
+ return `There's no edge between ${v1} and ${v2}`;
+ }
+ delete this.adjacencyList[v1][v2];
+ if (Object.keys(this.adjacencyList[v1]).length == 0) {
+ delete this.adjacencyList[v1];
+ }
+ }
+
+ removeHelper.call(this, vertex1, vertex2);
+ removeHelper.call(this, vertex2, vertex1);
+
+ return this.adjacencyList;
+ },
+ removeVertex: function (vertex) {
+ if (!this.adjacencyList.hasOwnProperty(vertex)) {
+ return `There's no ${vertex}`;
+ }
+ const edges = this.adjacencyList[vertex];
+ for (const key in edges) {
+ this.removeEdge(key, vertex);
+ }
+ return this.adjacencyList;
+ },
+ findShortestRoute: function (start, end) {
+ if (!start || !end) {
+ throw Error("출발지와 도착지를 모두 입력해야 합니다.");
+ }
+ const distance = {};
+ const previous = {};
+ const pq = Object.create(PriorityQueue);
+ pq.init();
+ pq.enqueue(start, 0);
+ const visited = {};
+
+ const hashOfVertex = this.adjacencyList;
+ for (const vertexName in hashOfVertex) {
+ const priority = vertexName == start ? 0 : Infinity;
+ distance[vertexName] = priority;
+ previous[vertexName] = null;
+ }
+
+ while (true) {
+ let current = pq.dequeue();
+ if (!current?.val) {
+ return;
+ }
+ current = current.val;
+ if (current == end) {
+ break;
+ }
+ const neighbors = hashOfVertex[current];
+
+ for (const vertexName in neighbors) {
+ if (visited.hasOwnProperty(vertexName)) {
+ continue;
+ }
+ const distFromStart = distance[current] + neighbors[vertexName];
+
+ if (distFromStart < distance[vertexName]) {
+ pq.enqueue(vertexName, distFromStart);
+ distance[vertexName] = distFromStart;
+ previous[vertexName] = current;
+ }
+ }
+ visited[current] = true;
+ }
+
+ let node = end;
+
+ const route = [];
+ while (node) {
+ route.unshift(node);
+ node = previous[node];
+ }
+
+ return route;
+ },
+ };
+
+ this.addEdge = (source, target, weight) => {
+ WeightedGraph.addEdge(source, target, weight);
+ };
+
+ this.findShortestPath = (source, target) => {
+ return WeightedGraph.findShortestRoute(source, target);
+ };
+
+ this.addVertex = (vertex) => {
+ WeightedGraph.addVertex(vertex);
+ };
+
+ WeightedGraph.init();
+}