diff --git a/javascript/code-challenges/graphs/README.md b/javascript/code-challenges/graphs/README.md new file mode 100644 index 0000000..ba46b9f --- /dev/null +++ b/javascript/code-challenges/graphs/README.md @@ -0,0 +1,54 @@ +# Graphs + + +## Challenge +Implement your own Graph. The graph should be represented as an adjacency list. +The Graph class should have the following methods: `addVertex`, `addEdge`, `getVertices`, +`getNeighbors`, `getSize` + +## Approach & Efficiency +| Method | Time Complexity | +| ---- | ---- | +| `addVertex` | O(1) | +| `addEdge` | O(1) | +| `getVertices` | O(n) | +| `getNeighbors` | O(n) | +| `getSize` | O(n)| + +## Methods + +`addVertex` + +- Adds a new vertex to the graph and returns the added vertex. + +`addEdge` + +- Adds an edge between two vertices and returns nothing. + +`getVertices` + +- Returns all the vertices in the graph. + +`getNeighbors` + +- Returns all the vertices connected to a give vertex. + +`getSize` + +- Returns the total number of vertices in the graph + +### Run Solution + +```sh +# Install dependencies +npm install + +# run jest tests for undirected-graph.js +npm test undirected-graph.test.js +``` +### Sources + +[Spread Syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax) + +[JavaScript Algorithms and Data Structures Masterclass - Colt Steele](https://www.udemy.com/course/js-algorithms-and-data-structures-masterclass/) + diff --git a/javascript/code-challenges/graphs/undirected-graph.js b/javascript/code-challenges/graphs/undirected-graph.js new file mode 100644 index 0000000..1eea552 --- /dev/null +++ b/javascript/code-challenges/graphs/undirected-graph.js @@ -0,0 +1,46 @@ +class UndirectedGraph { + constructor(){ + this.adjacencyList = {} + } + addVertex(vertex) { + if(!this.adjacencyList[vertex]) { + this.adjacencyList[vertex] = [] + return vertex + } + } + removeVertex(vertex) { + while(this.adjacencyList[vertex].length){ + const adjacentVertex = this.adjacencyList[vertex].pop() + this.removeEdge(vertex, adjacentVertex) + } + + delete this.adjacencyList[vertex] + } + addEdge(vertex1, vertex2) { + // Push values + this.adjacencyList[vertex1].push(vertex2) + this.adjacencyList[vertex2].push(vertex1) + // Remove duplicates + this.adjacencyList[vertex1] = [...new Set(this.adjacencyList[vertex1])] + this.adjacencyList[vertex2] = [...new Set(this.adjacencyList[vertex2])] + } + removeEdge(vertex1, vertex2) { + this.adjacencyList[vertex1] = this.adjacencyList[vertex1].filter( + edge => edge !== vertex2 + ) + this.adjacencyList[vertex2] = this.adjacencyList[vertex2].filter( + edge => edge !== vertex1 + ) + } + getVertices() { + return Object.keys(this.adjacencyList) + } + getNeighbors(vertex) { + return this.adjacencyList[vertex] + } + getSize() { + return Object.keys(this.adjacencyList).length + } +} + +module.exports = UndirectedGraph diff --git a/javascript/code-challenges/graphs/undirected-graph.test.js b/javascript/code-challenges/graphs/undirected-graph.test.js new file mode 100644 index 0000000..34dd105 --- /dev/null +++ b/javascript/code-challenges/graphs/undirected-graph.test.js @@ -0,0 +1,61 @@ +const { describe, it } = require('eslint/lib/rule-tester/rule-tester') +const UndirectedGraph = require('./undirected-graph') + +let testGraph + + +describe('Given Graphs', () => { + describe('When happy path', () => { + beforeEach(() => { + testGraph = new UndirectedGraph() + + // Add vertices before adding edge + testGraph.addVertex('Seattle') + testGraph.addVertex('Tokyo') + }) + + it('Vertex can be successfully added to the graph', () => { + testGraph.addVertex('NYC') + expect(testGraph.adjacencyList['NYC']).toEqual([]) + expect(testGraph.adjacencyList['Seattle']).toEqual([]) + expect(testGraph.adjacencyList['Tokyo']).toEqual([]) + }) + + it('An edge can be successfully added to the graph', () => { + testGraph.addEdge('Seattle', 'Tokyo') + expect(testGraph.adjacencyList['Seattle']).toEqual(['Tokyo']) + expect(testGraph.adjacencyList['Tokyo']).toEqual(['Seattle']) + }) + + it('A collection of all vertices can be properly retrieved from the graph', () => { + expect(testGraph.getVertices() === ['Seattle', 'Tokyo']) + }) + + it('All appropriate neighbors can be retrieved from the graph', () => { + testGraph.addEdge('Seattle', 'Tokyo') + expect(testGraph.getNeighbors('Seattle')).toEqual(['Tokyo']) + expect(testGraph.getNeighbors('Tokyo')).toEqual(['Seattle']) + }) + + // TODO handle weighted edges + it.todo('Neighbors are returned with the weight between vertices included') + + it('The proper size is returned, representing the number of vertices in the graph', () => { + expect(testGraph.getSize()).toEqual(2) + }) + }) + + describe('When edge case', () => { + it('A graph with only one vertex and edge can be properly returned', () => { + // How would a graph with one vertex have an edge? + let testGraph = new UndirectedGraph() + testGraph.addVertex('Seattle') + expect(testGraph.getVertices()).toEqual(['Seattle']) + }) + + it('An empty graph properly returns size', () => { + let testGraph = new UndirectedGraph() + expect(testGraph.getSize()).toEqual(0) + }) + }) +})