Skip to content

Commit 52cb8a2

Browse files
committedOct 15, 2017
capter 05: [LinkedLists]
1 parent 17cfa43 commit 52cb8a2

18 files changed

+1821
-7
lines changed
 

‎README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@ Work in Progress.
1616
* 04: [Queues and Deques](https://github.com/loiane/javascript-datastructures-algorithms/tree/third-edition/examples/chapter04)
1717
* 05: [LinkedLists](https://github.com/loiane/javascript-datastructures-algorithms/tree/third-edition/examples/chapter05)
1818

19-
## Thrid Edition Updates
19+
### Third Edition Updates
2020

2121
* Algorithms using ES2015+
22-
* Creation of a Data Structure and Algorithms library that can be used in the browser or with Node.js
22+
* Creation of a Data Structures and Algorithms library that can be used in the browser or with Node.js
2323
* Algorithms tested with Mocha + Chai (test code available in `test` directory)
2424
* TypeScript version of the source code included
2525

‎examples/PacktDataStructuresAlgorithms.min.js

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import { defaultEquals } from '../util';
2+
import LinkedList from './linked-list';
3+
import { Node } from './models/linked-list-models';
4+
5+
export default class CircularLinkedList extends LinkedList {
6+
constructor(equalsFn = defaultEquals) {
7+
super(equalsFn);
8+
}
9+
getLastElement() {
10+
let current = this.head;
11+
for (let i = 2; i <= this.size() && current != null; i++) {
12+
current = current.next;
13+
}
14+
return current;
15+
}
16+
push(element) {
17+
const node = new Node(element);
18+
let current;
19+
if (this.head == null) {
20+
this.head = node;
21+
} else {
22+
current = this.getLastElement();
23+
current.next = node;
24+
}
25+
// set node.next to head - to have circular list
26+
node.next = this.head;
27+
this.count++;
28+
}
29+
insert(element, index) {
30+
if (index >= 0 && index <= this.count) {
31+
const node = new Node(element);
32+
let current = this.head;
33+
if (index === 0) {
34+
if (this.head == null) {
35+
// if no node in list
36+
this.head = node;
37+
node.next = this.head;
38+
} else {
39+
node.next = current;
40+
current = this.getLastElement();
41+
// update last element
42+
this.head = node;
43+
current.next = this.head;
44+
}
45+
} else {
46+
const previous = this.getElementAt(index - 1);
47+
node.next = previous.next;
48+
previous.next = node;
49+
}
50+
this.count++;
51+
return true;
52+
}
53+
return false;
54+
}
55+
removeAt(index) {
56+
if (index >= 0 && index < this.count) {
57+
let current = this.head;
58+
if (index === 0) {
59+
const removed = this.head;
60+
if (this.size() === 1) {
61+
this.head = undefined;
62+
} else {
63+
current = this.getLastElement();
64+
this.head = this.head.next;
65+
current.next = this.head;
66+
current = removed;
67+
}
68+
} else {
69+
// no need to update last element for circular list
70+
const previous = this.getElementAt(index - 1);
71+
current = previous.next;
72+
previous.next = current.next;
73+
}
74+
this.count--;
75+
return current.element;
76+
}
77+
return undefined;
78+
}
79+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
import { defaultEquals } from '../util';
2+
import LinkedList from './linked-list';
3+
import { DoublyNode } from './models/linked-list-models';
4+
5+
export default class DoublyLinkedList extends LinkedList {
6+
constructor(equalsFn = defaultEquals) {
7+
super(equalsFn);
8+
}
9+
push(element) {
10+
const node = new DoublyNode(element);
11+
if (this.head == null) {
12+
this.head = node;
13+
this.tail = node; // NEW
14+
} else {
15+
// attach to the tail node // NEW
16+
this.tail.next = node;
17+
node.prev = this.tail;
18+
this.tail = node;
19+
}
20+
this.count++;
21+
}
22+
insert(element, index) {
23+
if (index >= 0 && index <= this.count) {
24+
const node = new DoublyNode(element);
25+
let current = this.head;
26+
if (index === 0) {
27+
if (this.head == null) {
28+
// NEW
29+
this.head = node;
30+
this.tail = node;
31+
} else {
32+
node.next = this.head;
33+
this.head.prev = node; // NEW
34+
this.head = node;
35+
}
36+
} else if (index === this.count) {
37+
// last item // NEW
38+
current = this.tail; // {2}
39+
current.next = node;
40+
node.prev = current;
41+
this.tail = node;
42+
} else {
43+
const previous = this.getElementAt(index - 1);
44+
current = previous.next;
45+
node.next = current;
46+
previous.next = node;
47+
current.prev = node; // NEW
48+
node.prev = previous; // NEW
49+
}
50+
this.count++;
51+
return true;
52+
}
53+
return false;
54+
}
55+
removeAt(index) {
56+
if (index >= 0 && index < this.count) {
57+
let current = this.head;
58+
if (index === 0) {
59+
this.head = this.head.next; // {1}
60+
// if there is only one item, then we update tail as well //NEW
61+
if (this.count === 1) {
62+
// {2}
63+
this.tail = undefined;
64+
} else {
65+
this.head.prev = undefined; // {3}
66+
}
67+
} else if (index === this.count - 1) {
68+
// last item //NEW
69+
current = this.tail; // {4}
70+
this.tail = current.prev;
71+
this.tail.next = undefined;
72+
} else {
73+
current = this.getElementAt(index);
74+
const previous = current.prev;
75+
// link previous with current's next - skip it to remove
76+
previous.next = current.next; // {6}
77+
current.next.prev = previous; // NEW
78+
}
79+
this.count--;
80+
return current.element;
81+
}
82+
return undefined;
83+
}
84+
indexOf(element) {
85+
let current = this.head;
86+
let index = 0;
87+
while (current != null) {
88+
if (this.equalsFn(element, current.element)) {
89+
return index;
90+
}
91+
index++;
92+
current = current.next;
93+
}
94+
return -1;
95+
}
96+
getHead() {
97+
return this.head;
98+
}
99+
getTail() {
100+
return this.tail;
101+
}
102+
clear() {
103+
super.clear();
104+
this.tail = undefined;
105+
}
106+
toString() {
107+
if (this.head == null) {
108+
return '';
109+
}
110+
let objString = `${this.head.element}`;
111+
let current = this.head.next;
112+
while (current != null) {
113+
objString = `${objString},${current.element}`;
114+
current = current.next;
115+
}
116+
return objString;
117+
}
118+
inverseToString() {
119+
if (this.tail == null) {
120+
return '';
121+
}
122+
let objString = `${this.tail.element}`;
123+
let previous = this.tail.prev;
124+
while (previous != null) {
125+
objString = `${objString},${previous.element}`;
126+
previous = previous.prev;
127+
}
128+
return objString;
129+
}
130+
}

‎src/js/data-structures/linked-list.js

+105
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import { defaultEquals } from '../util';
2+
import { Node } from './models/linked-list-models';
3+
4+
export default class LinkedList {
5+
constructor(equalsFn = defaultEquals) {
6+
this.equalsFn = equalsFn;
7+
this.count = 0;
8+
}
9+
push(element) {
10+
const node = new Node(element);
11+
let current;
12+
if (this.head == null) {
13+
// catches null && undefined
14+
this.head = node;
15+
} else {
16+
current = this.head;
17+
while (current.next != null) {
18+
current = current.next;
19+
}
20+
current.next = node;
21+
}
22+
this.count++;
23+
}
24+
getElementAt(index) {
25+
if (index >= 0 && index <= this.count) {
26+
let node = this.head;
27+
for (let i = 0; i < index && node != null; i++) {
28+
node = node.next;
29+
}
30+
return node;
31+
}
32+
return undefined;
33+
}
34+
insert(element, index) {
35+
if (index >= 0 && index <= this.count) {
36+
const node = new Node(element);
37+
const current = this.head;
38+
if (index === 0) {
39+
node.next = current;
40+
this.head = node;
41+
} else {
42+
const previous = this.getElementAt(index - 1);
43+
node.next = previous.next;
44+
previous.next = node;
45+
}
46+
this.count++;
47+
return true;
48+
}
49+
return false;
50+
}
51+
removeAt(index) {
52+
if (index >= 0 && index < this.count) {
53+
let current = this.head;
54+
if (index === 0) {
55+
this.head = current.next;
56+
} else {
57+
const previous = this.getElementAt(index - 1);
58+
current = previous.next;
59+
previous.next = current.next;
60+
}
61+
this.count--;
62+
return current.element;
63+
}
64+
return undefined;
65+
}
66+
remove(element) {
67+
const index = this.indexOf(element);
68+
return this.removeAt(index);
69+
}
70+
indexOf(element) {
71+
let current = this.head;
72+
for (let i = 0; i < this.size() && current != null; i++) {
73+
if (this.equalsFn(element, current.element)) {
74+
return i;
75+
}
76+
current = current.next;
77+
}
78+
return -1;
79+
}
80+
isEmpty() {
81+
return this.size() === 0;
82+
}
83+
size() {
84+
return this.count;
85+
}
86+
getHead() {
87+
return this.head;
88+
}
89+
clear() {
90+
this.head = undefined;
91+
this.count = 0;
92+
}
93+
toString() {
94+
if (this.head == null) {
95+
return '';
96+
}
97+
let objString = `${this.head.element}`;
98+
let current = this.head.next;
99+
for (let i = 1; i < this.size() && current != null; i++) {
100+
objString = `${objString},${current.element}`;
101+
current = current.next;
102+
}
103+
return objString;
104+
}
105+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
export class Node {
2+
constructor(element, next) {
3+
this.element = element;
4+
this.next = next;
5+
}
6+
}
7+
export class DoublyNode extends Node {
8+
constructor(element, next, prev) {
9+
super(element, next);
10+
this.prev = prev;
11+
}
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { Compare, defaultCompare, defaultEquals } from '../util';
2+
import LinkedList from './linked-list';
3+
4+
export default class SortedLinkedList extends LinkedList {
5+
constructor(equalsFn = defaultEquals, compareFn = defaultCompare) {
6+
super(equalsFn);
7+
this.equalsFn = equalsFn;
8+
this.compareFn = compareFn;
9+
}
10+
push(element) {
11+
if (this.isEmpty()) {
12+
super.push(element);
13+
} else {
14+
const index = this.getIndexNextSortedElement(element);
15+
super.insert(element, index);
16+
}
17+
}
18+
insert(element, index = 0) {
19+
if (this.isEmpty()) {
20+
return super.insert(element, index === 0 ? index : 0);
21+
}
22+
const pos = this.getIndexNextSortedElement(element);
23+
return super.insert(element, pos);
24+
}
25+
getIndexNextSortedElement(element) {
26+
let current = this.head;
27+
let i = 0;
28+
for (; i < this.size() && current; i++) {
29+
const comp = this.compareFn(element, current.element);
30+
if (comp === Compare.LESS_THAN) {
31+
return i;
32+
}
33+
current = current.next;
34+
}
35+
return i;
36+
}
37+
}

‎src/js/index.js

+9
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,15 @@ import StackArray from './data-structures/stack-array';
88
import Stack from './data-structures/stack';
99
import { parenthesesChecker } from './others/balanced-symbols';
1010

11+
import * as _util from './util';
12+
13+
export { default as CircularLinkedList } from './data-structures/circular-linked-list';
14+
export { default as DoublyLinkedList } from './data-structures/doubly-linked-list';
15+
export { default as LinkedList } from './data-structures/linked-list';
16+
export { default as SortedLinkedList } from './data-structures/sorted-linked-list';
17+
18+
export const util = _util;
19+
1120
export {
1221
Stack,
1322
StackArray,

‎src/js/util.js

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
export const Compare = {
2+
LESS_THAN: -1,
3+
BIGGER_THAN: 1
4+
};
5+
6+
export function defaultCompare(a, b) {
7+
if (a === b) {
8+
return 0;
9+
}
10+
return a < b ? Compare.LESS_THAN : Compare.BIGGER_THAN;
11+
}
12+
13+
export function defaultEquals(a, b) {
14+
return a === b;
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,342 @@
1+
import 'mocha';
2+
import { expect } from 'chai';
3+
import { CircularLinkedList } from '../../../src/js/index';
4+
import MyObj from './my-obj';
5+
6+
describe('CircularLinkedList', () => {
7+
let list;
8+
let min;
9+
let max;
10+
11+
beforeEach(() => {
12+
list = new CircularLinkedList();
13+
min = 1;
14+
max = 3;
15+
});
16+
17+
function pushesElements() {
18+
for (let i = min; i <= max; i++) {
19+
list.push(i);
20+
}
21+
}
22+
23+
function verifyList() {
24+
let current = list.getHead();
25+
for (let i = min; i <= max && current; i++) {
26+
expect(current).to.not.be.an('undefined');
27+
if (current) {
28+
// TS strictNullChecks
29+
expect(current.element).to.not.be.an('undefined');
30+
expect(current.element).to.equal(i);
31+
if (i < max) {
32+
expect(current.next).to.not.be.an('undefined');
33+
if (current.next) {
34+
// TS strictNullChecks
35+
expect(current.next.element).to.equal(i + 1);
36+
}
37+
} else {
38+
// circular list
39+
expect(current.next).to.not.be.an('undefined');
40+
expect(current.next).to.equal(list.getHead());
41+
if (current.next) {
42+
expect(current.next.element).to.equal(min);
43+
}
44+
}
45+
current = current.next;
46+
}
47+
}
48+
}
49+
50+
it('starts empty', () => {
51+
expect(list.size()).to.equal(0);
52+
expect(list.isEmpty()).to.equal(true);
53+
expect(list.getHead()).to.be.an('undefined');
54+
});
55+
56+
it('pushes elements', () => {
57+
pushesElements();
58+
verifyList();
59+
});
60+
61+
it('returns element at specific index: invalid position', () => {
62+
// list is empty
63+
expect(list.getElementAt(3)).to.be.an('undefined');
64+
});
65+
66+
it('returns element at specific index', () => {
67+
let node;
68+
69+
pushesElements();
70+
71+
for (let i = min; i <= max; i++) {
72+
node = list.getElementAt(i - 1);
73+
expect(node).to.not.be.an('undefined');
74+
if (node) {
75+
expect(node.element).to.equal(i);
76+
}
77+
}
78+
});
79+
80+
it('inserts elements first position empty list', () => {
81+
const element = 1;
82+
max = element;
83+
expect(list.insert(element, 0)).to.equal(true);
84+
verifyList();
85+
});
86+
87+
it('inserts elements first position not empty list', () => {
88+
max = 2;
89+
expect(list.insert(max, 0)).to.equal(true);
90+
91+
expect(list.insert(min, 0)).to.equal(true);
92+
93+
verifyList();
94+
});
95+
96+
it('inserts elements invalid position empty list', () => {
97+
expect(list.insert(1, 1)).to.equal(false);
98+
});
99+
100+
it('inserts elements invalid position not empty list', () => {
101+
const element = 1;
102+
expect(list.insert(element, 0)).to.equal(true);
103+
expect(list.insert(element, 2)).to.equal(false);
104+
});
105+
106+
it('inserts elements in the middle of list', () => {
107+
expect(list.insert(3, 0)).to.equal(true);
108+
expect(list.insert(1, 0)).to.equal(true);
109+
expect(list.insert(2, 1)).to.equal(true);
110+
verifyList();
111+
});
112+
113+
it('inserts elements at the end of list', () => {
114+
max = 5;
115+
116+
for (let i = min; i <= max; i++) {
117+
expect(list.insert(i, i - 1)).to.equal(true);
118+
}
119+
120+
verifyList();
121+
});
122+
123+
it('returns index of elements', () => {
124+
let index;
125+
126+
pushesElements();
127+
128+
for (let i = min; i <= max; i++) {
129+
index = list.indexOf(i);
130+
expect(index).to.equal(i - 1);
131+
}
132+
133+
expect(list.indexOf(max + 2)).to.equal(-1);
134+
});
135+
136+
it('removes valid elements', () => {
137+
let element;
138+
139+
pushesElements();
140+
141+
const minIndex = min;
142+
for (let i = minIndex; i <= max; i++) {
143+
element = list.remove(i);
144+
expect(element).to.not.be.an('undefined');
145+
expect(element).to.equal(i);
146+
min++;
147+
verifyList();
148+
}
149+
});
150+
151+
it('removes invalid elements', () => {
152+
let element;
153+
154+
pushesElements();
155+
156+
for (let i = max + 2; i <= max + 4; i++) {
157+
element = list.remove(i);
158+
expect(element).to.be.an('undefined');
159+
}
160+
});
161+
162+
it('removes element invalid position empty list', () => {
163+
let element;
164+
165+
for (let i = min; i <= max; i++) {
166+
element = list.removeAt(i - 1);
167+
expect(element).to.be.an('undefined');
168+
}
169+
});
170+
171+
it('removes element invalid position not empty list', () => {
172+
let element;
173+
174+
pushesElements();
175+
176+
for (let i = max + 2; i <= max + 4; i++) {
177+
element = list.removeAt(i);
178+
expect(element).to.be.an('undefined');
179+
}
180+
});
181+
182+
it('removes first element list single element', () => {
183+
const value = 1;
184+
list.push(value);
185+
186+
const element = list.removeAt(0);
187+
expect(element).to.not.be.an('undefined');
188+
expect(element).to.equal(value);
189+
190+
expect(list.getHead()).to.be.an('undefined');
191+
expect(list.isEmpty()).to.equal(true);
192+
});
193+
194+
it('removes first element list multiple elements', () => {
195+
pushesElements();
196+
197+
const element = list.removeAt(0);
198+
expect(element).to.not.be.an('undefined');
199+
expect(element).to.equal(min);
200+
201+
min = 2;
202+
verifyList();
203+
});
204+
205+
it('removes element from middle of list', () => {
206+
pushesElements(); // 1, 2, 3
207+
208+
const element = list.removeAt(1); // element 2
209+
expect(element).to.not.be.an('undefined');
210+
expect(element).to.equal(2);
211+
212+
// list needs to be [1, 3]
213+
let current = list.getHead();
214+
215+
// element 1
216+
expect(current).to.not.be.an('undefined');
217+
if (current) {
218+
expect(current.element).to.not.be.an('undefined');
219+
expect(current.element).to.equal(min);
220+
expect(current.next).to.not.be.an('undefined');
221+
if (current.next) {
222+
expect(current.next.element).to.equal(max);
223+
current = current.next;
224+
}
225+
}
226+
227+
// element 3
228+
expect(current).to.not.be.an('undefined');
229+
if (current) {
230+
expect(current.element).to.not.be.an('undefined');
231+
expect(current.element).to.equal(max);
232+
expect(current.next).to.not.be.an('undefined');
233+
expect(current.next).to.equal(list.getHead());
234+
if (current.next) {
235+
expect(current.next.element).to.equal(min);
236+
}
237+
}
238+
});
239+
240+
it('removes element from end of list', () => {
241+
let element;
242+
243+
pushesElements();
244+
245+
const maxIndex = max;
246+
for (let i = maxIndex; i >= min; i--) {
247+
element = list.removeAt(i - 1);
248+
expect(element).to.not.be.an('undefined');
249+
expect(element).to.equal(i);
250+
max--;
251+
verifyList();
252+
}
253+
});
254+
255+
it('returns the head of the list', () => {
256+
expect(list.getHead()).to.be.an('undefined');
257+
258+
list.push(1);
259+
expect(list.getHead()).to.not.be.an('undefined');
260+
});
261+
262+
it('returns the correct size', () => {
263+
expect(list.size()).to.equal(0);
264+
265+
for (let i = min; i <= max; i++) {
266+
list.push(i);
267+
expect(list.size()).to.equal(i);
268+
}
269+
270+
const size = max;
271+
for (let i = min; i <= max; i++) {
272+
list.remove(i);
273+
expect(list.size()).to.equal(size - i);
274+
}
275+
276+
expect(list.size()).to.equal(0);
277+
});
278+
279+
it('returns if it is empty', () => {
280+
expect(list.isEmpty()).to.equal(true);
281+
for (let i = min; i <= max; i++) {
282+
list.push(i);
283+
expect(list.isEmpty()).to.equal(false);
284+
}
285+
286+
for (let i = min; i < max; i++) {
287+
list.remove(i);
288+
expect(list.isEmpty()).to.equal(false);
289+
}
290+
list.remove(max);
291+
expect(list.isEmpty()).to.equal(true);
292+
293+
pushesElements();
294+
expect(list.isEmpty()).to.equal(false);
295+
296+
list.clear();
297+
expect(list.isEmpty()).to.equal(true);
298+
});
299+
300+
it('clears the list', () => {
301+
expect(list.size()).to.equal(0);
302+
list.clear();
303+
expect(list.size()).to.equal(0);
304+
pushesElements();
305+
expect(list.size()).to.greaterThan(0);
306+
list.clear();
307+
expect(list.size()).to.equal(0);
308+
});
309+
310+
it('returns toString primitive types', () => {
311+
expect(list.toString()).to.equal('');
312+
313+
list.push(1);
314+
expect(list.toString()).to.equal('1');
315+
316+
list.push(2);
317+
expect(list.toString()).to.equal('1,2');
318+
319+
list.clear();
320+
expect(list.toString()).to.equal('');
321+
});
322+
323+
it('returns toString primitive types: string', () => {
324+
const ds = new CircularLinkedList();
325+
ds.push('el1');
326+
expect(ds.toString()).to.equal('el1');
327+
328+
ds.push('el2');
329+
expect(ds.toString()).to.equal('el1,el2');
330+
});
331+
332+
it('returns toString objects', () => {
333+
const ds = new CircularLinkedList();
334+
expect(ds.toString()).to.equal('');
335+
336+
ds.push(new MyObj(1, 2));
337+
expect(ds.toString()).to.equal('1|2');
338+
339+
ds.push(new MyObj(3, 4));
340+
expect(ds.toString()).to.equal('1|2,3|4');
341+
});
342+
});

‎test/js/data-structures/doubly-linked-list.spec.js

+406
Large diffs are not rendered by default.
+330
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,330 @@
1+
import 'mocha';
2+
import { expect } from 'chai';
3+
import { LinkedList, util } from '../../../src/js/index';
4+
import MyObj from './my-obj';
5+
6+
describe('LinkedList', () => {
7+
let list;
8+
let min;
9+
let max;
10+
11+
beforeEach(() => {
12+
list = new LinkedList(util.defaultEquals);
13+
min = 1;
14+
max = 3;
15+
});
16+
17+
function pushesElements() {
18+
for (let i = min; i <= max; i++) {
19+
list.push(i);
20+
}
21+
}
22+
23+
function verifyList() {
24+
let current = list.getHead();
25+
for (let i = min; i <= max && current; i++) {
26+
expect(current).to.not.be.an('undefined');
27+
if (current) {
28+
// TS strictNullChecks
29+
expect(current.element).to.not.be.an('undefined');
30+
expect(current.element).to.equal(i);
31+
if (i < max) {
32+
expect(current.next).to.not.be.an('undefined');
33+
if (current.next) {
34+
// TS strictNullChecks
35+
expect(current.next.element).to.equal(i + 1);
36+
}
37+
} else {
38+
expect(current.next).to.be.an('undefined');
39+
}
40+
current = current.next;
41+
}
42+
}
43+
}
44+
45+
it('starts empty', () => {
46+
expect(list.size()).to.equal(0);
47+
expect(list.isEmpty()).to.equal(true);
48+
expect(list.getHead()).to.be.an('undefined');
49+
});
50+
51+
it('pushes elements', () => {
52+
pushesElements();
53+
verifyList();
54+
});
55+
56+
it('returns element at specific index: invalid position', () => {
57+
// list is empty
58+
expect(list.getElementAt(3)).to.be.an('undefined');
59+
});
60+
61+
it('returns element at specific index', () => {
62+
let node;
63+
64+
pushesElements();
65+
66+
for (let i = min; i <= max; i++) {
67+
node = list.getElementAt(i - 1);
68+
expect(node).to.not.be.an('undefined');
69+
if (node) {
70+
expect(node.element).to.equal(i);
71+
}
72+
}
73+
});
74+
75+
it('inserts elements first position empty list', () => {
76+
const element = 1;
77+
max = element;
78+
expect(list.insert(element, 0)).to.equal(true);
79+
verifyList();
80+
});
81+
82+
it('inserts elements first position not empty list', () => {
83+
max = 2;
84+
expect(list.insert(max, 0)).to.equal(true);
85+
86+
expect(list.insert(min, 0)).to.equal(true);
87+
88+
verifyList();
89+
});
90+
91+
it('inserts elements invalid position empty list', () => {
92+
expect(list.insert(1, 1)).to.equal(false);
93+
});
94+
95+
it('inserts elements invalid position not empty list', () => {
96+
const element = 1;
97+
expect(list.insert(element, 0)).to.equal(true);
98+
expect(list.insert(element, 2)).to.equal(false);
99+
});
100+
101+
it('inserts elements in the middle of list', () => {
102+
expect(list.insert(3, 0)).to.equal(true);
103+
expect(list.insert(1, 0)).to.equal(true);
104+
expect(list.insert(2, 1)).to.equal(true);
105+
verifyList();
106+
});
107+
108+
it('inserts elements at the end of list', () => {
109+
max = 5;
110+
111+
for (let i = min; i <= max; i++) {
112+
expect(list.insert(i, i - 1)).to.equal(true);
113+
}
114+
115+
verifyList();
116+
});
117+
118+
it('returns index of elements', () => {
119+
let index;
120+
121+
pushesElements();
122+
123+
for (let i = min; i <= max; i++) {
124+
index = list.indexOf(i);
125+
expect(index).to.equal(i - 1);
126+
}
127+
128+
expect(list.indexOf(max + 2)).to.equal(-1);
129+
});
130+
131+
it('removes valid elements', () => {
132+
let element;
133+
134+
pushesElements();
135+
136+
for (let i = min; i <= max; i++) {
137+
element = list.remove(i);
138+
expect(element).to.not.be.an('undefined');
139+
expect(element).to.equal(i);
140+
}
141+
});
142+
143+
it('removes invalid elements', () => {
144+
let element;
145+
146+
pushesElements();
147+
148+
for (let i = max + 2; i <= max + 4; i++) {
149+
element = list.remove(i);
150+
expect(element).to.be.an('undefined');
151+
}
152+
});
153+
154+
it('removes element invalid position empty list', () => {
155+
let element;
156+
157+
for (let i = min; i <= max; i++) {
158+
element = list.removeAt(i - 1);
159+
expect(element).to.be.an('undefined');
160+
}
161+
});
162+
163+
it('removes element invalid position not empty list', () => {
164+
let element;
165+
166+
pushesElements();
167+
168+
for (let i = max + 2; i <= max + 4; i++) {
169+
element = list.removeAt(i);
170+
expect(element).to.be.an('undefined');
171+
}
172+
});
173+
174+
it('removes first element list single element', () => {
175+
const value = 1;
176+
list.push(value);
177+
178+
const element = list.removeAt(0);
179+
expect(element).to.not.be.an('undefined');
180+
expect(element).to.equal(value);
181+
182+
expect(list.getHead()).to.be.an('undefined');
183+
expect(list.isEmpty()).to.equal(true);
184+
});
185+
186+
it('removes first element list multiple elements', () => {
187+
pushesElements();
188+
189+
const element = list.removeAt(0);
190+
expect(element).to.not.be.an('undefined');
191+
expect(element).to.equal(min);
192+
193+
min = 2;
194+
verifyList();
195+
});
196+
197+
it('removes element from middle of list', () => {
198+
pushesElements(); // 1, 2, 3
199+
200+
const element = list.removeAt(1); // element 2
201+
expect(element).to.not.be.an('undefined');
202+
expect(element).to.equal(2);
203+
204+
// list needs to be [1, 3]
205+
let current = list.getHead();
206+
207+
// element 1
208+
expect(current).to.not.be.an('undefined');
209+
if (current) {
210+
expect(current.element).to.not.be.an('undefined');
211+
expect(current.element).to.equal(1);
212+
expect(current.next).to.not.be.an('undefined');
213+
if (current.next) {
214+
expect(current.next.element).to.equal(3);
215+
current = current.next;
216+
}
217+
}
218+
219+
// element 3
220+
expect(current).to.not.be.an('undefined');
221+
if (current) {
222+
expect(current.element).to.not.be.an('undefined');
223+
expect(current.element).to.equal(3);
224+
expect(current.next).to.be.an('undefined');
225+
}
226+
});
227+
228+
it('removes element from end of list', () => {
229+
let element;
230+
231+
pushesElements();
232+
233+
const maxIndex = max;
234+
for (let i = maxIndex; i >= min; i--) {
235+
element = list.removeAt(i - 1);
236+
expect(element).to.not.be.an('undefined');
237+
expect(element).to.equal(i);
238+
max--;
239+
verifyList();
240+
}
241+
});
242+
243+
it('returns the head of the list', () => {
244+
expect(list.getHead()).to.be.an('undefined');
245+
246+
list.push(1);
247+
expect(list.getHead()).to.not.be.an('undefined');
248+
});
249+
250+
it('returns the correct size', () => {
251+
expect(list.size()).to.equal(0);
252+
253+
for (let i = min; i <= max; i++) {
254+
list.push(i);
255+
expect(list.size()).to.equal(i);
256+
}
257+
258+
const size = max;
259+
for (let i = min; i <= max; i++) {
260+
list.remove(i);
261+
expect(list.size()).to.equal(size - i);
262+
}
263+
264+
expect(list.size()).to.equal(0);
265+
});
266+
267+
it('returns if it is empty', () => {
268+
expect(list.isEmpty()).to.equal(true);
269+
for (let i = min; i <= max; i++) {
270+
list.push(i);
271+
expect(list.isEmpty()).to.equal(false);
272+
}
273+
274+
for (let i = min; i < max; i++) {
275+
list.remove(i);
276+
expect(list.isEmpty()).to.equal(false);
277+
}
278+
list.remove(max);
279+
expect(list.isEmpty()).to.equal(true);
280+
281+
pushesElements();
282+
expect(list.isEmpty()).to.equal(false);
283+
284+
list.clear();
285+
expect(list.isEmpty()).to.equal(true);
286+
});
287+
288+
it('clears the list', () => {
289+
expect(list.size()).to.equal(0);
290+
list.clear();
291+
expect(list.size()).to.equal(0);
292+
pushesElements();
293+
expect(list.size()).to.greaterThan(0);
294+
list.clear();
295+
expect(list.size()).to.equal(0);
296+
});
297+
298+
it('returns toString primitive types', () => {
299+
expect(list.toString()).to.equal('');
300+
301+
list.push(1);
302+
expect(list.toString()).to.equal('1');
303+
304+
list.push(2);
305+
expect(list.toString()).to.equal('1,2');
306+
307+
list.clear();
308+
expect(list.toString()).to.equal('');
309+
});
310+
311+
it('returns toString primitive types: string', () => {
312+
const ds = new LinkedList();
313+
ds.push('el1');
314+
expect(ds.toString()).to.equal('el1');
315+
316+
ds.push('el2');
317+
expect(ds.toString()).to.equal('el1,el2');
318+
});
319+
320+
it('returns toString objects', () => {
321+
const ds = new LinkedList();
322+
expect(ds.toString()).to.equal('');
323+
324+
ds.push(new MyObj(1, 2));
325+
expect(ds.toString()).to.equal('1|2');
326+
327+
ds.push(new MyObj(3, 4));
328+
expect(ds.toString()).to.equal('1|2,3|4');
329+
});
330+
});

‎test/js/data-structures/my-obj.js

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export default class MyObj {
2+
constructor(el1, el2) {
3+
this.el1 = el1;
4+
this.el2 = el2;
5+
}
6+
toString() {
7+
return `${this.el1.toString()}|${this.el2.toString()}`;
8+
}
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,340 @@
1+
import 'mocha';
2+
import { expect } from 'chai';
3+
import { SortedLinkedList, util } from '../../../src/js/index';
4+
import MyObj from './my-obj';
5+
6+
describe('SortedLinkedList', () => {
7+
let list;
8+
let min;
9+
let max;
10+
11+
beforeEach(() => {
12+
list = new SortedLinkedList();
13+
min = 1;
14+
max = 3;
15+
});
16+
17+
function pushesElements() {
18+
for (let i = max; i >= min; i--) {
19+
list.push(i);
20+
}
21+
}
22+
23+
function verifyList() {
24+
let current = list.getHead();
25+
for (let i = min; i <= max && current; i++) {
26+
expect(current).to.not.be.an('undefined');
27+
if (current) {
28+
// TS strictNullChecks
29+
expect(current.element).to.not.be.an('undefined');
30+
expect(current.element).to.equal(i);
31+
if (i < max) {
32+
expect(current.next).to.not.be.an('undefined');
33+
if (current.next) {
34+
// TS strictNullChecks
35+
expect(current.next.element).to.equal(i + 1);
36+
}
37+
} else {
38+
expect(current.next).to.be.an('undefined');
39+
}
40+
current = current.next;
41+
}
42+
}
43+
}
44+
45+
it('starts empty', () => {
46+
expect(list.size()).to.equal(0);
47+
expect(list.isEmpty()).to.equal(true);
48+
expect(list.getHead()).to.be.an('undefined');
49+
});
50+
51+
it('pushes elements', () => {
52+
pushesElements();
53+
verifyList();
54+
});
55+
56+
it('returns element at specific index: invalid position', () => {
57+
// list is empty
58+
expect(list.getElementAt(3)).to.be.an('undefined');
59+
});
60+
61+
it('returns element at specific index', () => {
62+
let node;
63+
64+
pushesElements();
65+
66+
for (let i = min; i <= max; i++) {
67+
node = list.getElementAt(i - 1);
68+
expect(node).to.not.be.an('undefined');
69+
if (node) {
70+
expect(node.element).to.equal(i);
71+
}
72+
}
73+
});
74+
75+
it('inserts elements first position empty list', () => {
76+
const element = 1;
77+
max = element;
78+
expect(list.insert(element, 0)).to.equal(true);
79+
verifyList();
80+
});
81+
82+
it('inserts elements first position not empty list', () => {
83+
max = 2;
84+
expect(list.insert(max)).to.equal(true);
85+
86+
expect(list.insert(min, 0)).to.equal(true);
87+
88+
verifyList();
89+
});
90+
91+
it('inserts elements invalid position empty list', () => {
92+
// sorted list will ignore the index position
93+
expect(list.insert(1, 1)).to.equal(true);
94+
});
95+
96+
it('inserts elements invalid position not empty list', () => {
97+
// sorted list will ignore the index position
98+
const element = 1;
99+
expect(list.insert(element, 0)).to.equal(true);
100+
expect(list.insert(element, 2)).to.equal(true);
101+
});
102+
103+
it('inserts elements in the middle of list', () => {
104+
expect(list.insert(3, 0)).to.equal(true);
105+
expect(list.insert(1, 0)).to.equal(true);
106+
expect(list.insert(2, 1)).to.equal(true);
107+
verifyList();
108+
});
109+
110+
it('inserts elements at the end of list', () => {
111+
max = 5;
112+
113+
for (let i = min; i <= max; i++) {
114+
expect(list.insert(i, i - 1)).to.equal(true);
115+
}
116+
117+
verifyList();
118+
});
119+
120+
it('returns index of elements', () => {
121+
let index;
122+
123+
pushesElements();
124+
125+
for (let i = min; i <= max; i++) {
126+
index = list.indexOf(i);
127+
expect(index).to.equal(i - 1);
128+
}
129+
130+
expect(list.indexOf(max + 2)).to.equal(-1);
131+
});
132+
133+
it('removes valid elements', () => {
134+
let element;
135+
136+
pushesElements();
137+
138+
for (let i = min; i <= max; i++) {
139+
element = list.remove(i);
140+
expect(element).to.not.be.an('undefined');
141+
expect(element).to.equal(i);
142+
}
143+
});
144+
145+
it('removes invalid elements', () => {
146+
let element;
147+
148+
pushesElements();
149+
150+
for (let i = max + 2; i <= max + 4; i++) {
151+
element = list.remove(i);
152+
expect(element).to.be.an('undefined');
153+
}
154+
});
155+
156+
it('removes element invalid position empty list', () => {
157+
let element;
158+
159+
for (let i = min; i <= max; i++) {
160+
element = list.removeAt(i - 1);
161+
expect(element).to.be.an('undefined');
162+
}
163+
});
164+
165+
it('removes element invalid position not empty list', () => {
166+
let element;
167+
168+
pushesElements();
169+
170+
for (let i = max + 2; i <= max + 4; i++) {
171+
element = list.removeAt(i);
172+
expect(element).to.be.an('undefined');
173+
}
174+
});
175+
176+
it('removes first element list single element', () => {
177+
const value = 1;
178+
list.push(value);
179+
180+
const element = list.removeAt(0);
181+
expect(element).to.not.be.an('undefined');
182+
expect(element).to.equal(value);
183+
184+
expect(list.getHead()).to.be.an('undefined');
185+
expect(list.isEmpty()).to.equal(true);
186+
});
187+
188+
it('removes first element list multiple elements', () => {
189+
pushesElements();
190+
191+
const element = list.removeAt(0);
192+
expect(element).to.not.be.an('undefined');
193+
expect(element).to.equal(min);
194+
195+
min = 2;
196+
verifyList();
197+
});
198+
199+
it('removes element from middle of list', () => {
200+
pushesElements(); // 1, 2, 3
201+
202+
const element = list.removeAt(1); // element 2
203+
expect(element).to.not.be.an('undefined');
204+
expect(element).to.equal(2);
205+
206+
// list needs to be [1, 3]
207+
let current = list.getHead();
208+
209+
// element 1
210+
expect(current).to.not.be.an('undefined');
211+
if (current) {
212+
expect(current.element).to.not.be.an('undefined');
213+
expect(current.element).to.equal(1);
214+
expect(current.next).to.not.be.an('undefined');
215+
if (current.next) {
216+
expect(current.next.element).to.equal(3);
217+
current = current.next;
218+
}
219+
}
220+
221+
// element 3
222+
expect(current).to.not.be.an('undefined');
223+
if (current) {
224+
expect(current.element).to.not.be.an('undefined');
225+
expect(current.element).to.equal(3);
226+
expect(current.next).to.be.an('undefined');
227+
}
228+
});
229+
230+
it('removes element from end of list', () => {
231+
let element;
232+
233+
pushesElements();
234+
235+
const maxIndex = max;
236+
for (let i = maxIndex; i >= min; i--) {
237+
element = list.removeAt(i - 1);
238+
expect(element).to.not.be.an('undefined');
239+
expect(element).to.equal(i);
240+
max--;
241+
verifyList();
242+
}
243+
});
244+
245+
it('returns the head of the list', () => {
246+
expect(list.getHead()).to.be.an('undefined');
247+
248+
list.push(1);
249+
expect(list.getHead()).to.not.be.an('undefined');
250+
});
251+
252+
it('returns the correct size', () => {
253+
expect(list.size()).to.equal(0);
254+
255+
for (let i = min; i <= max; i++) {
256+
list.push(i);
257+
expect(list.size()).to.equal(i);
258+
}
259+
260+
const size = max;
261+
for (let i = min; i <= max; i++) {
262+
list.remove(i);
263+
expect(list.size()).to.equal(size - i);
264+
}
265+
266+
expect(list.size()).to.equal(0);
267+
});
268+
269+
it('returns if it is empty', () => {
270+
expect(list.isEmpty()).to.equal(true);
271+
for (let i = min; i <= max; i++) {
272+
list.push(i);
273+
expect(list.isEmpty()).to.equal(false);
274+
}
275+
276+
for (let i = min; i < max; i++) {
277+
list.remove(i);
278+
expect(list.isEmpty()).to.equal(false);
279+
}
280+
list.remove(max);
281+
expect(list.isEmpty()).to.equal(true);
282+
283+
pushesElements();
284+
expect(list.isEmpty()).to.equal(false);
285+
286+
list.clear();
287+
expect(list.isEmpty()).to.equal(true);
288+
});
289+
290+
it('clears the list', () => {
291+
expect(list.size()).to.equal(0);
292+
list.clear();
293+
expect(list.size()).to.equal(0);
294+
pushesElements();
295+
expect(list.size()).to.greaterThan(0);
296+
list.clear();
297+
expect(list.size()).to.equal(0);
298+
});
299+
300+
it('returns toString primitive types', () => {
301+
expect(list.toString()).to.equal('');
302+
303+
list.push(1);
304+
expect(list.toString()).to.equal('1');
305+
306+
list.push(2);
307+
expect(list.toString()).to.equal('1,2');
308+
309+
list.clear();
310+
expect(list.toString()).to.equal('');
311+
});
312+
313+
function stringCompare(a, b) {
314+
return a.localeCompare(b);
315+
}
316+
317+
it('returns toString primitive types: string', () => {
318+
const ds = new SortedLinkedList(util.defaultEquals, stringCompare);
319+
ds.push('el2');
320+
expect(ds.toString()).to.equal('el2');
321+
322+
ds.push('el1');
323+
expect(ds.toString()).to.equal('el1,el2');
324+
});
325+
326+
function myObjCompare(a, b) {
327+
return a.toString().localeCompare(b.toString());
328+
}
329+
330+
it('returns toString objects', () => {
331+
const ds = new SortedLinkedList(util.defaultEquals, myObjCompare);
332+
expect(ds.toString()).to.equal('');
333+
334+
ds.push(new MyObj(3, 4));
335+
expect(ds.toString()).to.equal('3|4');
336+
337+
ds.push(new MyObj(1, 2));
338+
expect(ds.toString()).to.equal('1|2,3|4');
339+
});
340+
});

‎test/ts/data-structures/circular-linked-list.spec.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ describe('CircularLinkedList', () => {
88
let min: number;
99
let max: number;
1010

11-
beforeEach(function() {
11+
beforeEach(() => {
1212
list = new CircularLinkedList<number>();
1313
min = 1;
1414
max = 3;

‎test/ts/data-structures/doubly-linked-list.spec.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ describe('DoublyLinkedList', () => {
88
let min: number;
99
let max: number;
1010

11-
beforeEach(function() {
11+
beforeEach(() => {
1212
list = new DoublyLinkedList<number>();
1313
min = 1;
1414
max = 3;

‎test/ts/data-structures/linked-list.spec.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ describe('LinkedList', () => {
88
let min: number;
99
let max: number;
1010

11-
beforeEach(function() {
11+
beforeEach(() => {
1212
list = new LinkedList<number>(util.defaultEquals);
1313
min = 1;
1414
max = 3;

‎test/ts/data-structures/sorted-linked-list.spec.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ describe('SortedLinkedList', () => {
88
let min: number;
99
let max: number;
1010

11-
beforeEach(function() {
11+
beforeEach(() => {
1212
list = new SortedLinkedList<number>();
1313
min = 1;
1414
max = 3;

0 commit comments

Comments
 (0)
Please sign in to comment.