Skip to content

Commit 8cf0feb

Browse files
committed
Added solution to ch2-8 and associated tests. Update ch2 helpers and made use of new methods in ch2-7 tests.
1 parent 5955ef8 commit 8cf0feb

File tree

4 files changed

+140
-15
lines changed

4 files changed

+140
-15
lines changed

src/chapter2/ch2-q7.spec.js

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,11 @@ import * as helpers from './helpers';
33
import * as funcs from './ch2-q7';
44

55
function generateList(length) {
6-
let head, tail;
7-
6+
let list = helpers.createLinkedList();
87
for (let i = length; i > 0; --i) {
9-
let node = helpers.createNode(100 + Math.random() * 999999);
10-
if (!head) {
11-
head = tail = node;
12-
}
13-
else {
14-
tail.next = node;
15-
tail = node;
16-
}
8+
helpers.pushSingle(list, 100 + Math.random() * 999999);
179
}
18-
19-
return {
20-
head: head,
21-
tail: tail
22-
};
10+
return list;
2311
}
2412

2513
for (let key in funcs) {

src/chapter2/ch2-q8.js

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
'use strict';
2+
3+
/**
4+
* This algorithm simply stores each node it sees into a Set structure, if there
5+
* is a cycle then the first repeat we see will be the start of that cycle and
6+
* the value we should return.
7+
*
8+
* N = |list|
9+
* Time: O(N) - assumes Set is hashmap based so O(1) costs
10+
* Additional space: O(N)
11+
*/
12+
export function findStartOfLoopSet(list) {
13+
let visited = new Set(),
14+
node = list;
15+
16+
while (node) {
17+
if (visited.has(node)) {
18+
return node;
19+
}
20+
visited.add(node);
21+
node = node.next;
22+
}
23+
24+
return null;
25+
}
26+
27+
/**
28+
* This algorithm uses the runners principle where we have a slow and fast (2x)
29+
* runners that both traverse the list. If at some point both runners point to
30+
* the same node then there is a cycle. Due to the different rate at which they
31+
* travel they will meet k steps 'before' the start of the cycle so if you reset
32+
* the slow runner back to the start and leave the faster runner at the meeting
33+
* point, when they start moving forward again they will eventually meet at the
34+
* start of the cycle.
35+
*
36+
* N = |list|
37+
* Time: O(N)
38+
* Additional space: O(1)
39+
*/
40+
export function findStartOfLoop(list) {
41+
if (!list) {
42+
return null;
43+
}
44+
45+
let slow = list,
46+
fast = list;
47+
48+
while (slow.next && fast.next && fast.next.next) {
49+
slow = slow.next;
50+
fast = fast.next.next;
51+
if (fast === slow) {
52+
break;
53+
}
54+
}
55+
56+
if (!slow || slow !== fast) { // no loop
57+
return null;
58+
}
59+
60+
slow = list;
61+
while (slow !== fast) {
62+
slow = slow.next;
63+
fast = fast.next;
64+
}
65+
66+
return fast;
67+
}

src/chapter2/ch2-q8.spec.js

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { expect } from 'chai';
2+
import * as helpers from './helpers';
3+
import * as funcs from './ch2-q8';
4+
5+
for (let key in funcs) {
6+
let func = funcs[key];
7+
8+
describe('ch2-q8: ' + key, function() {
9+
10+
beforeEach(function() {
11+
this.list = helpers.createLinkedList();
12+
});
13+
14+
it('returns null with empty list', function() {
15+
expect(func(this.list.head)).to.be.null;
16+
});
17+
18+
it('returns null when there is no loop', function() {
19+
helpers.push(this.list, 1, 2, 3, 4, 5, 6);
20+
expect(func(this.list.head)).to.be.null;
21+
});
22+
23+
it('returns node when there is a loop 1', function() {
24+
helpers.push(this.list, 1, 2, 3, 4, 5, 6);
25+
let node = this.list.head;
26+
this.list.tail.next = node;
27+
expect(func(this.list.head)).to.equal(node);
28+
});
29+
30+
it('returns node when there is a loop 2', function() {
31+
helpers.push(this.list, 1, 2, 3, 4, 5, 6);
32+
let node = this.list.head.next.next.next;
33+
this.list.tail.next = node;
34+
expect(func(this.list.head)).to.equal(node);
35+
});
36+
37+
it('returns node when there is a loop 3', function() {
38+
helpers.push(this.list, 1, 2, 3, 4, 5, 6);
39+
let node = this.list.tail;
40+
this.list.tail.next = node;
41+
expect(func(this.list.head)).to.equal(node);
42+
});
43+
44+
});
45+
}

src/chapter2/helpers.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,28 @@ export function getLength(list) {
4040
}
4141
return length;
4242
}
43+
44+
export function createLinkedList() {
45+
return {
46+
head: null,
47+
tail: null
48+
};
49+
}
50+
51+
export function pushSingle(list, value) {
52+
let node = createNode(value);
53+
if (list.head) {
54+
list.tail.next = node;
55+
list.tail = node;
56+
}
57+
else {
58+
list.head = node;
59+
list.tail = node;
60+
}
61+
}
62+
63+
export function push(list) {
64+
for (let i = 1; i < arguments.length; ++i) {
65+
pushSingle(list, arguments[i]);
66+
}
67+
}

0 commit comments

Comments
 (0)