Skip to content

Commit 48fe1cc

Browse files
committed
Added hard/weighted_path
1 parent bbb01a8 commit 48fe1cc

File tree

2 files changed

+296
-0
lines changed

2 files changed

+296
-0
lines changed

hard/weighted_path.js

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
/**
2+
* Have the function weightedPath(strArr) take strArr which will be an array of
3+
* strings which models a non-looping weighted Graph. The structure of the array
4+
* will be as follows: The first element in the array will be the number of
5+
* nodes N (points) in the array as a string. The next N elements will be the
6+
* nodes which can be anything (A, B, C .. Brick Street, Main Street .. etc.).
7+
* Then after the Nth element, the rest of the elements in the array will be the
8+
* connections between all of the nodes along with their weights (integers)
9+
* separated by the pipe symbol (|). They will look like this: (A|B|3, B|C|12 ..
10+
* Brick Street|Main Street|14 .. etc.). Although, there may exist no
11+
* connections at all.
12+
*
13+
* An example of strArr may be:
14+
* ["4","A","B","C","D","A|B|1","B|D|9","B|C|3","C|D|4"]. Your program should
15+
* return the shortest path when the weights are added up from node to node from
16+
* the first Node to the last Node in the array separated by dashes. So in the
17+
* example above the output should be A-B-C-D. Here is another example with
18+
* strArr being
19+
* ["7","A","B","C","D","E","F","G","A|B|1","A|E|9","B|C|2","C|D|1","D|F|2","E|D|6","F|G|2"].
20+
* The output for this array should be A-B-C-D-F-G. There will only ever be one
21+
* shortest path for the array. If no path between the first and last node
22+
* exists, return -1. The array will at minimum have two nodes. Also, the
23+
* connection A-B for example, means that A can get to B and B can get to A. A
24+
* path may not go through any Node more than once.
25+
*
26+
* https://www.coderbyte.com/results/bhanson:Weighted%20Path:JavaScript
27+
*
28+
* @param {array} strArr
29+
* @return {string} or -1 if no path exists
30+
*/
31+
function weightedPath(strArr) {
32+
// Parse input data
33+
const numNodes = parseInt(strArr[0]);
34+
const nodes = strArr.slice(1, numNodes + 1);
35+
const paths = strArr.slice(numNodes + 1, strArr.length);
36+
37+
// Hash table to store entry into all nodes so we can enter the graph anywhere
38+
const map = new Map();
39+
40+
// Add empty nodes
41+
nodes.forEach(node => {
42+
map.set(node, new Node(node));
43+
});
44+
45+
// Add paths
46+
paths.forEach(path => {
47+
let [start, end, weight] = path.split('|');
48+
weight = Number(weight);
49+
map.get(start).addEdge(map.get(end), weight);
50+
map.get(end).addEdge(map.get(start), weight); // bi-directional
51+
});
52+
53+
// Per spec, start and end are first and last node as given respectively
54+
const start = nodes[0];
55+
const end = nodes[nodes.length - 1];
56+
57+
let shortestPath = map.get(start).pathTo(end);
58+
59+
if (shortestPath.length === 0) {
60+
return -1;
61+
}
62+
63+
shortestPath = shortestPath.map(node => node.key);
64+
65+
return shortestPath.join('-');
66+
}
67+
68+
function Node(key) {
69+
this.key = key;
70+
this.edges = [];
71+
}
72+
73+
Node.prototype.addEdge = function(node, weight) {
74+
this.edges.push(new Edge(node, weight));
75+
};
76+
77+
function Edge(node, weight) {
78+
this.node = node;
79+
this.weight = weight;
80+
}
81+
82+
// Returns shortest path as array or [] if no path available
83+
// Guarantees shortest path by trying all possibilities
84+
// TODO: Refactor :)
85+
Node.prototype.pathTo = function(endKey, visited = [], fromWeight = 0) {
86+
if (hasVisited(this.key)) {
87+
return [];
88+
}
89+
90+
if (this.key === endKey) {
91+
return [
92+
{
93+
key: this.key,
94+
weight: fromWeight
95+
}
96+
];
97+
}
98+
99+
const copy = visited.slice();
100+
copy.push({
101+
key: this.key,
102+
weight: fromWeight
103+
});
104+
105+
const childrenPaths = [];
106+
for (let i = 0; i < this.edges.length; i++) {
107+
const edge = this.edges[i];
108+
const children = edge.node.pathTo(endKey, copy, edge.weight);
109+
if (children.length > 0) {
110+
const selfAndChildren = [];
111+
selfAndChildren.push(
112+
{ key: this.key, weight: fromWeight },
113+
...children
114+
);
115+
childrenPaths.push(selfAndChildren);
116+
}
117+
}
118+
119+
childrenPaths.sort((a, b) => {
120+
const aSum = a.reduce((sum, path) => (sum += path.weight), 0);
121+
const bSum = b.reduce((sum, path) => (sum += path.weight), 0);
122+
123+
return aSum - bSum;
124+
});
125+
126+
return childrenPaths.length > 0 ? childrenPaths[0] : [];
127+
128+
function hasVisited(key) {
129+
for (let i = 0; i < visited.length; i++) {
130+
if (visited[i].key === key) {
131+
return true;
132+
}
133+
}
134+
return false;
135+
}
136+
};
137+
138+
module.exports = weightedPath;

hard/weighted_path.test.js

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
const weightedPath = require('./weighted_path');
2+
3+
describe('weightedPath()', () => {
4+
test('passes Coderbyte.com tests', () => {
5+
expect(weightedPath(['3', 'A', 'B', 'C', 'B|C|13', 'A|B|2'])).toBe(
6+
'A-B-C'
7+
);
8+
9+
expect(
10+
weightedPath([
11+
'6',
12+
'A',
13+
'B',
14+
'C',
15+
'D',
16+
'E',
17+
'F',
18+
'B|A|2',
19+
'A|F|12',
20+
'A|C|4',
21+
'B|D|1',
22+
'D|E|1',
23+
'C|D|4',
24+
'F|E|1'
25+
])
26+
).toBe('A-B-D-E-F');
27+
28+
expect(
29+
weightedPath([
30+
'6',
31+
'A',
32+
'B',
33+
'C',
34+
'D',
35+
'E',
36+
'F',
37+
'B|A|2',
38+
'A|F|3',
39+
'A|C|4',
40+
'B|D|1',
41+
'D|E|1',
42+
'C|D|4',
43+
'F|E|1'
44+
])
45+
).toBe('A-F');
46+
47+
expect(
48+
weightedPath([
49+
'6',
50+
'D',
51+
'B',
52+
'C',
53+
'A',
54+
'E',
55+
'F',
56+
'B|A|2',
57+
'A|F|3',
58+
'A|C|4',
59+
'B|D|1',
60+
'D|E|12',
61+
'C|D|4',
62+
'F|E|1'
63+
])
64+
).toBe('D-B-A-F');
65+
66+
expect(weightedPath(['3', 'AA', 'BB', 'CC', 'CC|BB|102'])).toBe(-1);
67+
68+
expect(
69+
weightedPath([
70+
'8',
71+
'C',
72+
'B',
73+
'A',
74+
'D',
75+
'E',
76+
'F',
77+
'G',
78+
'H',
79+
'C|D|1',
80+
'D|F|2',
81+
'G|F|2',
82+
'G|E|1',
83+
'E|B|1',
84+
'H|B|1',
85+
'C|A|13',
86+
'B|A|2',
87+
'C|E|9'
88+
])
89+
).toBe('C-D-F-G-E-B-H');
90+
91+
expect(
92+
weightedPath([
93+
'7',
94+
'D',
95+
'A',
96+
'N',
97+
'I',
98+
'E',
99+
'L',
100+
'B',
101+
'D|A|1',
102+
'A|N|2',
103+
'L|B|22'
104+
])
105+
).toBe(-1);
106+
107+
expect(
108+
weightedPath([
109+
'3',
110+
'GG',
111+
'HH',
112+
'JJ',
113+
'GG|JJ|6',
114+
'GG|HH|2',
115+
'JJ|HH|2'
116+
])
117+
).toBe('GG-HH-JJ');
118+
119+
expect(
120+
weightedPath([
121+
'5',
122+
'c',
123+
'a',
124+
'b',
125+
'd',
126+
'e',
127+
'c|a|3',
128+
'a|b|2',
129+
'a|d|34',
130+
'b|e|15',
131+
'e|d|2'
132+
])
133+
).toBe('c-a-b-e');
134+
135+
expect(
136+
weightedPath([
137+
'8',
138+
'C',
139+
'B',
140+
'A',
141+
'D',
142+
'E',
143+
'F',
144+
'G',
145+
'H',
146+
'C|D|1',
147+
'D|F|2',
148+
'G|F|2',
149+
'G|E|1',
150+
'E|B|1',
151+
'H|B|1',
152+
'C|A|13',
153+
'B|A|2',
154+
'C|E|1'
155+
])
156+
).toBe('C-E-B-H');
157+
});
158+
});

0 commit comments

Comments
 (0)