Skip to content

Commit 8cd126d

Browse files
committed
feat(book/linkedlist): linked lists techniques and common patterns
1 parent 0f13f90 commit 8cd126d

30 files changed

+945
-351
lines changed

.eslintrc.js

+15-4
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,35 @@
11
module.exports={
22
extends: 'airbnb-base',
33
env: {
4-
jest: true
4+
jest: true,
55
},
6+
plugins: ['jest'],
67
globals: {
78
BigInt: true,
89
},
10+
11+
// check package.json for files to include
12+
// files: ['src/**/*.js', 'book/interview-questions/*.js'],
13+
914
rules: {
1015
// https://github.com/airbnb/javascript/issues/1089
1116

1217
// https://stackoverflow.com/a/35637900/684957
1318
// allow to add properties to arguments
14-
'no-param-reassign': [2,{'props': false}],
19+
'no-param-reassign': [2,{props: false}],
1520

1621
// https://eslint.org/docs/rules/no-plusplus
1722
// allows unary operators ++ and -- in the afterthought (final expression) of a for loop.
18-
'no-plusplus': [0,{'allowForLoopAfterthoughts': true}],
23+
'no-plusplus': [0,{allowForLoopAfterthoughts: true}],
1924
'no-continue': [0],
2025

2126
// Allow for..of
2227
'no-restricted-syntax': [0,'ForOfStatement'],
23-
}
28+
29+
// jest plugin
30+
// 'jest/no-disabled-tests': 'warn',
31+
'jest/no-focused-tests': 'error',
32+
'jest/no-identical-title': 'warn',
33+
'jest/valid-expect': 'warn',
34+
},
2435
};

book/content/part02/linked-list.asc

+291-12
Large diffs are not rendered by default.

book/images/Find-the-largest-sum.png

-418 Bytes
Loading
Loading

book/images/Words-Permutations.png

4.2 KB
Loading
137 KB
Loading

book/images/cll.png

70.5 KB
Loading
-3.69 KB
Loading
-1.42 KB
Loading
-1.38 KB
Loading

book/images/mll-3-levels.png

30.3 KB
Loading
23.5 KB
Loading

book/images/sllx4.png

5.28 KB
Loading

book/interview-questions/daily-temperatures.spec.js

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/* eslint-disable max-len */
12
const{ dailyTemperatures }=require('./daily-temperatures');
23

34
describe('Stack: Daily Temperatures',()=>{
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// tag::fn[]
2+
/**
3+
* Find where the cycle starts or null if no loop.
4+
* @param {Node} head - The head of the list
5+
* @returns {Node|null}
6+
*/
7+
functionfindCycleStart(head){
8+
letslow=head;
9+
letfast=head;
10+
while(fast&&fast.next){
11+
slow=slow.next;// slow moves 1 by 1.
12+
fast=fast.next.next;// slow moves 2 by 2.
13+
if(fast===slow){// detects loop!
14+
slow=head;// reset pointer to begining.
15+
while(slow!==fast){// find intersection
16+
slow=slow.next;
17+
fast=fast.next;// move both pointers one by one this time.
18+
}
19+
returnslow;// return where the loop starts
20+
}
21+
}
22+
returnnull;// not found.
23+
}
24+
// end::fn[]
25+
26+
// tag::brute[]
27+
functionfindCycleStartBrute(head){
28+
constvisited=newSet();
29+
letcurr=head;
30+
while(curr){
31+
if(visited.has(curr))returncurr;
32+
visited.add(curr);
33+
curr=curr.next;
34+
}
35+
returnnull;
36+
}
37+
// end::brute[]
38+
39+
module.exports={ findCycleStart, findCycleStartBrute };
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
const{ findCycleStart, findCycleStartBrute }=require('./linkedlist-find-cycle-start');
2+
const{ LinkedList }=require('../../src/index');
3+
4+
[findCycleStart,findCycleStartBrute].forEach((fn)=>{
5+
describe(`findCycleStart: ${fn.name}`,()=>{
6+
it('should work without loop',()=>{
7+
consthead=newLinkedList([1,2,3]).first;
8+
expect(fn(head)).toEqual(null);
9+
});
10+
11+
it('should work with loop on first',()=>{
12+
constlist=newLinkedList([1,2,3]);
13+
constn1=list.first;
14+
list.last.next=n1;
15+
expect(fn(list.first)).toEqual(n1);
16+
});
17+
18+
it('should work with loop on second',()=>{
19+
constlist=newLinkedList([1,2,3]);
20+
constn2=list.first.next;
21+
list.last.next=n2;
22+
expect(fn(list.first)).toEqual(n2);
23+
});
24+
});
25+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// tag::fn[]
2+
/**
3+
* Flatten a multi-level to a single level
4+
* @param {Node} head
5+
* @return {Node}
6+
*/
7+
functionflatten(head){
8+
for(letcurr=head;curr;curr=curr.next){
9+
if(!curr.child)continue;
10+
11+
letlast=curr.child;
12+
while(last&&last.next)last=last.next;// find "child"'s last
13+
if(curr.next){// move "next" to "child"'s last postion
14+
last.next=curr.next;
15+
curr.next.previous=last;
16+
}
17+
curr.next=curr.child;// override "next" with "child".
18+
curr.child.previous=curr;
19+
curr.child=null;// clean "child" pointer.
20+
}
21+
22+
returnhead;
23+
}
24+
// end::fn[]
25+
26+
// tag::fn2[]
27+
functionflattenBrute(head){
28+
conststack=[];
29+
for(letcurr=head;curr;curr=curr.next){
30+
if(!curr.next&&stack.length){
31+
curr.next=stack.pop();// merge main thread with saved nodes.
32+
curr.next.previous=curr;
33+
}
34+
if(!curr.child)continue;
35+
if(curr.next)stack.push(curr.next);// save "next" nodes.
36+
curr.next=curr.child;// override next pointer with "child"
37+
curr.child.previous=curr;
38+
curr.child=null;// clear child pointer (was moved to "next").
39+
}
40+
41+
returnhead;
42+
}
43+
// end::fn2[]
44+
45+
module.exports={ flatten, flattenBrute };
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/* eslint-disable one-var, one-var-declaration-per-line, prefer-destructuring */
2+
const{ flatten, flattenBrute }=require('./linkedlist-flatten-multilevel');
3+
const{ LinkedList }=require('../../src/index');
4+
const{ ListNode }=require('../../src/index');
5+
6+
classNodeextendsListNode{
7+
constructor(value){
8+
super(value);
9+
this.child=null;
10+
}
11+
}
12+
13+
// print linked list node with (previous and child)
14+
consttoString=(head)=>{
15+
constarr=[];
16+
for(leti=head;i;i=i.next){
17+
arr.push(`${i.value}(${(i.previous&&i.previous.value)||''},${(i.child&&i.child.value)||''})`);
18+
}
19+
return`{ ${arr.join(' -> ')} }`;
20+
};
21+
22+
constll=(nums)=>Array.from(newLinkedList(nums,Node));
23+
24+
[flatten,flattenBrute].forEach((fn)=>{
25+
describe(`flatten: ${fn.name}`,()=>{
26+
letl1,l2,l3,l4;
27+
28+
beforeEach(()=>{
29+
l1=ll([1,2,3]);
30+
l2=ll([10,12,14,16]);
31+
l3=ll([21,23]);
32+
l4=ll([36,37]);
33+
});
34+
35+
it('works with flat 1 level',()=>{
36+
// 1--- 2--- 3
37+
expect(toString(fn(l1[0]))).toEqual('{ 1(,) -> 2(1,) -> 3(2,) }');
38+
});
39+
40+
it('works with flat 2 levels',()=>{
41+
// 21--23
42+
// |
43+
// 36--37
44+
l3[1].child=l4[0];
45+
expect(toString(l3[0])).toEqual('{ 21(,) -> 23(21,36) }');
46+
expect(toString(fn(l3[0]))).toEqual('{ 21(,) -> 23(21,) -> 36(23,) -> 37(36,) }');
47+
});
48+
49+
fit('works with flat 2 levels and reminder',()=>{
50+
// 1--- 2--- 3
51+
// |
52+
// 36--37
53+
l1[1].child=l4[0];
54+
expect(toString(l1[0])).toEqual('{ 1(,) -> 2(1,36) -> 3(2,) }');
55+
56+
expect(toString(fn(l1[0]))).toEqual('{ 1(,) -> 2(1,) -> 36(2,) -> 37(36,) -> 3(37,) }');
57+
});
58+
59+
it('should flatten 3 levels',()=>{
60+
// 1--- 2--- 3
61+
// |
62+
// 10---12---14---16
63+
// | |
64+
// | 36---37
65+
// |
66+
// 21--23
67+
l1[1].child=l2[0];
68+
l2[1].child=l3[0];
69+
l2[2].child=l4[0];
70+
71+
// verify list children are present
72+
expect(toString(l1[0])).toEqual('{ 1(,) -> 2(1,10) -> 3(2,) }');
73+
expect(toString(l2[0])).toEqual('{ 10(,) -> 12(10,21) -> 14(12,36) -> 16(14,) }');
74+
75+
// run
76+
expect(toString(fn(l1[0]))).toEqual('{ 1(,) -> 2(1,) -> 10(2,) -> 12(10,) -> 21(12,) -> 23(21,) -> 14(23,) -> 36(14,) -> 37(36,) -> 16(37,) -> 3(16,) }');
77+
});
78+
});
79+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// tag::fn[]
2+
functionisPalindrome(head){
3+
letslow=head;
4+
letfast=head;
5+
while(fast){// use slow/fast pointers to find the middle.
6+
slow=slow.next;
7+
fast=fast.next&&fast.next.next;
8+
}
9+
10+
constreverseList=(node)=>{// use 3 pointers to reverse a linked list
11+
letprev=null;
12+
letcurr=node;
13+
while(curr){
14+
const{ next }=curr;// same as: "const next = curr.next;"
15+
curr.next=prev;
16+
prev=curr;
17+
curr=next;
18+
}
19+
returnprev;
20+
};
21+
22+
constreversed=reverseList(slow);// head of the reversed half
23+
for(leti=reversed,j=head;i;i=i.next,j=j.next)if(i.value!==j.value)returnfalse;
24+
returntrue;
25+
}
26+
// end::fn[]
27+
28+
// tag::fn2[]
29+
functionisPalindromeBrute(head){
30+
constarr=[];
31+
for(leti=head;i;i=i.next)arr.push(i.value);// <1>
32+
letlo=0;
33+
lethi=arr.length-1;
34+
while(lo<hi)if(arr[lo++]!==arr[hi--])returnfalse;// <2>
35+
returntrue;
36+
}
37+
// end::fn2[]
38+
39+
module.exports={ isPalindrome, isPalindromeBrute };
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
const{ isPalindrome, isPalindromeBrute }=require('./linkedlist-is-palindrome');
2+
const{ LinkedList }=require('../../src');
3+
4+
consttoList=(arr)=>newLinkedList(arr).first;
5+
6+
[isPalindrome,isPalindromeBrute].forEach((fn)=>{
7+
describe(`isPalindrome: ${fn.name}`,()=>{
8+
it('should work',()=>{
9+
expect(fn()).toEqual(true);
10+
});
11+
12+
it('should work different cases',()=>{
13+
expect(fn(toList([1,2,3]))).toEqual(false);
14+
expect(fn(toList([1,2,3,2,1]))).toEqual(true);
15+
expect(fn(toList([1,1,2,1]))).toEqual(false);
16+
expect(fn(toList([1,2,2,1]))).toEqual(true);
17+
});
18+
});
19+
});

book/interview-questions/max-subarray.data.js

+2-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

book/interview-questions/network-delay-time.spec.js

+3-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

book/interview-questions/recent-counter.js

+2-3
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ const { Queue } = require('../../src/index');
2020
classRecentCounter{
2121
// end::description[]
2222
// tag::solution[]
23-
queue=newQueue();
2423
// end::solution[]
2524
// tag::description[]
2625
/**
@@ -31,6 +30,7 @@ class RecentCounter {
3130
// end::description[]
3231
// tag::solution[]
3332
this.window=maxWindow;
33+
this.queue=newQueue();
3434
// end::solution[]
3535
// tag::description[]
3636
}
@@ -44,8 +44,7 @@ class RecentCounter {
4444
// end::description[]
4545
// tag::solution[]
4646
this.queue.enqueue(timestamp);
47-
while(timestamp-this.queue.peek()>this.window)
48-
this.queue.dequeue();
47+
while(timestamp-this.queue.peek()>this.window)this.queue.dequeue();
4948

5049
returnthis.queue.size;
5150
// end::solution[]

book/interview-questions/sort-colors.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/* eslint-disable no-return-assign */
12
// const { } = require('../../src/index');
23

34
// tag::description[]
@@ -35,7 +36,8 @@ function sortColors(nums) {
3536

3637
// tag::compact[]
3738
functionsortColorsCompact(nums){
38-
leti=0,lo=0,hi=nums.length-1;
39+
leti=0;letlo=0;let
40+
hi=nums.length-1;
3941
constswap=(k,j)=>[nums[k],nums[j]]=[nums[j],nums[k]];
4042

4143
while(i<=hi){

0 commit comments

Comments
 (0)
close