Skip to content

Commit cf97711

Browse files
committed
feat: adds DirectedGraph data structure
1 parent 5679579 commit cf97711

File tree

2 files changed

+197
-0
lines changed

2 files changed

+197
-0
lines changed
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/**
2+
* Directed Graph - consists of nodes (vertices) connected by edges. Connections go only one direction.
3+
*
4+
* Methods and properties:
5+
*
6+
* - addNode: Constant O(1)
7+
* - addEdge: Constant O(1)
8+
* - hadNode: Constant O(1)
9+
* - getNodeNeighbors: Constant O(1)
10+
* - printGraph: O(V + E)
11+
* - breadthFirstSearch: O(V + E)
12+
* - depthFirstSearch: O(V + E)
13+
*/
14+
15+
export class DirectedGraph {
16+
constructor() {
17+
this.nodes = new Map()
18+
}
19+
20+
addNode(node) {
21+
this.nodes.set(node, [])
22+
}
23+
24+
addEdge(node1, node2) {
25+
if (this.nodes.has(node1) && this.nodes.has(node2)) {
26+
this.nodes.get(node1).push(node2)
27+
} else {
28+
throw new Error('One or both nodes do not exist in the graph.')
29+
}
30+
}
31+
32+
hasNode(node) {
33+
return this.nodes.has(node)
34+
}
35+
36+
getNodeNeighbors(node) {
37+
if (this.nodes.has(node)) {
38+
return this.nodes.get(node)
39+
}
40+
41+
throw new Error('Node does not exist in the graph.')
42+
}
43+
44+
printGraph() {
45+
if (this.nodes.size === 0) {
46+
console.log('')
47+
return
48+
}
49+
50+
let printedMessage = ''
51+
for (const [node, neighbors] of this.nodes.entries()) {
52+
printedMessage += `${node} ->`
53+
54+
neighbors.forEach(neighborNode => {
55+
printedMessage += ` ${neighborNode}`
56+
})
57+
58+
printedMessage += '\n'
59+
}
60+
61+
console.log(printedMessage)
62+
}
63+
64+
// eslint-disable-next-line no-unused-vars
65+
breadthFirstSearch(startingNode, searchingForNode) {}
66+
67+
// eslint-disable-next-line no-unused-vars
68+
depthFirstSearch(startingNode, searchingForNode) {}
69+
}
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
import { DirectedGraph } from './directed-graph'
2+
3+
describe('DirectedGraph', () => {
4+
it('can be instantiated with the `new` keyword', () => {
5+
expect(() => new DirectedGraph()).not.toThrow()
6+
})
7+
8+
describe('addNode', () => {
9+
it('allows you to add a new node to an empty graph', () => {
10+
const graph = new DirectedGraph()
11+
expect(() => graph.addNode('A')).not.toThrow()
12+
})
13+
14+
it('allows you to add multiple new nodes to a graph', () => {
15+
const graph = new DirectedGraph()
16+
expect(() => graph.addNode('A')).not.toThrow()
17+
expect(() => graph.addNode('B')).not.toThrow()
18+
expect(() => graph.addNode('C')).not.toThrow()
19+
})
20+
})
21+
22+
describe('addEdge', () => {
23+
it('throws an error if you try to add an edge for two nodes that do not exist', () => {
24+
const graph = new DirectedGraph()
25+
expect(() => graph.addEdge('A', 'B')).toThrow(
26+
'One or both nodes do not exist in the graph.'
27+
)
28+
})
29+
30+
it('throws an error if you try to add an edge for two nodes when the first node does not exist', () => {
31+
const graph = new DirectedGraph()
32+
graph.addNode('B')
33+
expect(() => graph.addEdge('A', 'B')).toThrow(
34+
'One or both nodes do not exist in the graph.'
35+
)
36+
})
37+
38+
it('throws an error if you try to add an edge for two nodes when the second node does not exist', () => {
39+
const graph = new DirectedGraph()
40+
graph.addNode('A')
41+
expect(() => graph.addEdge('A', 'B')).toThrow(
42+
'One or both nodes do not exist in the graph.'
43+
)
44+
})
45+
46+
it('allows you to add an edge between two existing nodes', () => {
47+
const graph = new DirectedGraph()
48+
graph.addNode('A')
49+
graph.addNode('B')
50+
51+
expect(() => graph.addEdge('A', 'B')).not.toThrow()
52+
})
53+
54+
it('allows you to add multiple edges to the graph', () => {
55+
const graph = new DirectedGraph()
56+
graph.addNode('A')
57+
graph.addNode('B')
58+
graph.addNode('C')
59+
60+
expect(() => graph.addEdge('A', 'B')).not.toThrow()
61+
expect(() => graph.addEdge('A', 'C')).not.toThrow()
62+
expect(() => graph.addEdge('B', 'C')).not.toThrow()
63+
})
64+
})
65+
66+
describe('hasNode', () => {
67+
it('returns true if the node exists in the graph', () => {
68+
const graph = new DirectedGraph()
69+
graph.addNode('A')
70+
71+
expect(graph.hasNode('A')).toBe(true)
72+
})
73+
74+
it('returns false if the node does not exist in the graph', () => {
75+
const graph = new DirectedGraph()
76+
graph.addNode('A')
77+
78+
expect(graph.hasNode('B')).toBe(false)
79+
})
80+
})
81+
82+
describe('getNodeNeighbors', () => {
83+
it('throws an error if the node does not exist in the graph', () => {
84+
const graph = new DirectedGraph()
85+
expect(() => graph.getNodeNeighbors('A')).toThrow(
86+
'Node does not exist in the graph.'
87+
)
88+
})
89+
})
90+
91+
describe('printGraph', () => {
92+
it('prints an empty string to the console for an empty graph', () => {
93+
jest.spyOn(console, 'log')
94+
const graph = new DirectedGraph()
95+
graph.printGraph()
96+
expect(console.log).toHaveBeenCalledWith('')
97+
})
98+
99+
it('prints a representation of the graph to the console for a graph with nodes and edges', () => {
100+
jest.spyOn(console, 'log')
101+
const graph = new DirectedGraph()
102+
103+
graph.addNode('A')
104+
graph.addNode('B')
105+
graph.addNode('C')
106+
graph.addNode('D')
107+
108+
graph.addEdge('A', 'B')
109+
graph.addEdge('A', 'C')
110+
graph.addEdge('B', 'C')
111+
graph.addEdge('B', 'D')
112+
graph.addEdge('C', 'D')
113+
114+
graph.printGraph()
115+
expect(console.log).toHaveBeenCalledWith(
116+
`A -> B C
117+
B -> C D
118+
C -> D
119+
D ->
120+
`
121+
)
122+
})
123+
})
124+
125+
describe('breadthFirstSearch', () => {})
126+
127+
describe('depthFirstSearch', () => {})
128+
})

0 commit comments

Comments
 (0)