Skip to content

Commit bacfd56

Browse files
committedOct 14, 2017
chapter 05: [LinkedLists]
1 parent 2414e5f commit bacfd56

14 files changed

+1625
-13
lines changed
 

‎.vscode/settings.json

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"typescript.tsdk": "node_modules/typescript/lib"
3+
}

‎README.md

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ Work in Progress.
1414
* 02: [Arrays](https://github.com/loiane/javascript-datastructures-algorithms/tree/third-edition/examples/chapter02)
1515
* 03: [Stacks](https://github.com/loiane/javascript-datastructures-algorithms/tree/third-edition/examples/chapter03)
1616
* 04: [Queues and Deques](https://github.com/loiane/javascript-datastructures-algorithms/tree/third-edition/examples/chapter04)
17+
* 05: [LinkedLists](https://github.com/loiane/javascript-datastructures-algorithms/tree/third-edition/examples/chapter05)
1718

1819
## Thrid Edition Updates
1920

‎examples/PacktDataStructuresAlgorithms.min.js

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

‎package.json

+6-6
Original file line numberDiff line numberDiff line change
@@ -55,24 +55,24 @@
5555
"@types/mocha": "^2.2.42",
5656
"babel-cli": "^6.26.0",
5757
"babel-core": "^6.26.0",
58-
"babel-eslint": "^8.0.0",
58+
"babel-eslint": "^8.0.1",
5959
"babel-loader": "^7.1.2",
6060
"babel-plugin-add-module-exports": "^0.2.1",
6161
"babel-plugin-transform-es2015-modules-umd": "^6.24.1",
6262
"babel-preset-env": "^1.6.0",
6363
"chai": "^4.1.2",
6464
"codecov": "^2.3.0",
65-
"eslint": "^4.6.1",
66-
"eslint-config-airbnb-base": "^12.0.0",
65+
"eslint": "^4.8.0",
66+
"eslint-config-airbnb-base": "^12.0.2",
6767
"eslint-plugin-import": "^2.7.0",
6868
"istanbul": "^v1.1.0-alpha.1",
69-
"mocha": "^3.5.0",
69+
"mocha": "^4.0.1",
7070
"mochawesome": "^2.3.1",
7171
"nyc": "^11.2.1",
7272
"ts-node": "^3.3.0",
7373
"tslint": "^5.7.0",
74-
"typescript": "^2.5.2",
75-
"webpack": "^3.6.0",
74+
"typescript": "^2.6.0-dev.20171014",
75+
"webpack": "^3.7.1",
7676
"yargs": "^9.0.1"
7777
}
7878
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
import { defaultEquals, IEqualsFunction } from '../util';
2+
import { Node } from './models/linked-list-models';
3+
4+
export default class CircularLinkedList<T> {
5+
private count = 0;
6+
private head: Node<T> | undefined;
7+
8+
constructor(private equalsFn: IEqualsFunction<T> = defaultEquals) { }
9+
10+
private getLastElement() {
11+
let current = this.head;
12+
for (let i = 2; i <= this.size() && current; i++) {
13+
current = current.next;
14+
}
15+
return current;
16+
}
17+
18+
push(element: T) {
19+
const node = new Node(element);
20+
let current;
21+
22+
if (this.head === undefined || this.head === null) {
23+
this.head = node;
24+
} else {
25+
current = this.getLastElement();
26+
if (current) {
27+
current.next = node;
28+
}
29+
}
30+
31+
// set node.next to head - to have circular list
32+
node.next = this.head;
33+
34+
this.count++;
35+
}
36+
37+
getElementAt(index: number) {
38+
if (index >= 0 && index <= this.count) {
39+
let node = this.head;
40+
for (let i = 0; i < index; i++) {
41+
if (node) {
42+
node = node.next;
43+
}
44+
}
45+
return node;
46+
}
47+
return undefined;
48+
}
49+
50+
insert(index: number, element: T) {
51+
if (index >= 0 && index <= this.count) {
52+
const node = new Node(element);
53+
let current = this.head;
54+
55+
if (index === 0) {
56+
if (!this.head) {
57+
// if no node in list
58+
this.head = node;
59+
node.next = this.head;
60+
} else {
61+
node.next = current;
62+
current = this.getLastElement();
63+
// update last element
64+
this.head = node;
65+
if (current) {
66+
current.next = this.head;
67+
}
68+
}
69+
} else {
70+
const previous = this.getElementAt(index - 1);
71+
if (previous) {
72+
node.next = previous.next;
73+
previous.next = node;
74+
}
75+
}
76+
this.count++;
77+
return true;
78+
}
79+
return false;
80+
}
81+
82+
removeAt(index: number) {
83+
if (index >= 0 && index < this.count) {
84+
let current = this.head;
85+
86+
if (index === 0) {
87+
const removed = this.head;
88+
89+
if (this.size() === 1) {
90+
this.head = undefined;
91+
} else {
92+
current = this.getLastElement();
93+
if (this.head) {
94+
this.head = this.head.next;
95+
}
96+
if (current) {
97+
current.next = this.head;
98+
}
99+
current = removed;
100+
}
101+
} else {
102+
// no need to update last element for circular list
103+
const previous = this.getElementAt(index - 1);
104+
if (previous) {
105+
current = previous.next;
106+
if (current) {
107+
previous.next = current.next;
108+
}
109+
}
110+
}
111+
if (current) {
112+
this.count--;
113+
return current.element;
114+
}
115+
}
116+
return undefined;
117+
}
118+
119+
remove(element: T) {
120+
const index = this.indexOf(element);
121+
return this.removeAt(index);
122+
}
123+
124+
indexOf(element: T) {
125+
let current = this.head;
126+
127+
for (let i = 1; i <= this.size() && current; i++) {
128+
if (this.equalsFn(element, current.element)) {
129+
return i - 1;
130+
}
131+
current = current.next;
132+
}
133+
134+
return -1;
135+
}
136+
137+
isEmpty() {
138+
return this.size() === 0;
139+
}
140+
141+
size() {
142+
return this.count;
143+
}
144+
145+
getHead() {
146+
return this.head;
147+
}
148+
149+
clear() {
150+
this.head = undefined;
151+
this.count = 0;
152+
}
153+
154+
toString() {
155+
if (this.head === undefined) {
156+
return '';
157+
}
158+
let objString = `${this.head.element}`;
159+
let current = this.head.next;
160+
for (let i = 2; i <= this.size() && current; i++) {
161+
objString = `${objString},${current.element}`;
162+
current = current.next;
163+
}
164+
return objString;
165+
}
166+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
import { defaultEquals, IEqualsFunction } from '../util';
2+
import { DoublyNode } from './models/linked-list-models';
3+
4+
export default class DoublyLinkedList<T> {
5+
private count = 0;
6+
private head: DoublyNode<T> | undefined;
7+
private tail: DoublyNode<T> | undefined;
8+
9+
constructor(private equalsFn: IEqualsFunction<T> = defaultEquals) { }
10+
11+
push(element: T) {
12+
const node = new DoublyNode(element);
13+
14+
if (this.head === undefined || this.head === null) {
15+
this.head = node;
16+
this.tail = node; // NEW
17+
} else {
18+
// attach to the tail node // NEW
19+
if (this.tail !== undefined && this.tail !== null) {
20+
this.tail.next = node;
21+
node.prev = this.tail;
22+
this.tail = node;
23+
}
24+
}
25+
this.count++;
26+
}
27+
28+
getElementAt(index: number) {
29+
if (index >= 0 && index <= this.count) {
30+
let node = this.head;
31+
for (let i = 0; i < index; i++) {
32+
if (node) {
33+
node = node.next;
34+
}
35+
}
36+
return node;
37+
}
38+
return undefined;
39+
}
40+
41+
insert(index: number, element: T) {
42+
if (index >= 0 && index <= this.count) {
43+
const node = new DoublyNode(element);
44+
let current = this.head;
45+
46+
if (index === 0) {
47+
if (this.head === undefined || this.head === null) {
48+
// NEW
49+
this.head = node;
50+
this.tail = node;
51+
} else {
52+
node.next = this.head;
53+
this.head.prev = node; // NEW
54+
this.head = node;
55+
}
56+
} else if (index === this.count) {
57+
// last item // NEW
58+
59+
current = this.tail; // {2}
60+
if (current) {
61+
current.next = node;
62+
node.prev = current;
63+
this.tail = node;
64+
}
65+
} else {
66+
const previous = this.getElementAt(index - 1);
67+
if (previous) {
68+
current = previous.next;
69+
node.next = current;
70+
previous.next = node;
71+
72+
if (current) {
73+
current.prev = node; // NEW
74+
node.prev = previous; // NEW
75+
}
76+
}
77+
}
78+
this.count++;
79+
return true;
80+
}
81+
return false;
82+
}
83+
84+
removeAt(index: number) {
85+
if (index >= 0 && index < this.count) {
86+
let current = this.head;
87+
88+
if (index === 0) {
89+
if (this.head) {
90+
this.head = this.head.next; // {1}
91+
// if there is only one item, then we update tail as well //NEW
92+
if (this.count === 1) {
93+
// {2}
94+
this.tail = undefined;
95+
} else {
96+
if (this.head) {
97+
this.head.prev = undefined; // {3}
98+
}
99+
}
100+
}
101+
} else if (index === this.count - 1) {
102+
// last item //NEW
103+
current = this.tail; // {4}
104+
if (current) {
105+
this.tail = current.prev;
106+
if (this.tail) {
107+
this.tail.next = undefined;
108+
}
109+
}
110+
} else {
111+
current = this.getElementAt(index);
112+
if (current) {
113+
const previous = current.prev;
114+
if (previous) {
115+
// link previous with current's next - skip it to remove
116+
previous.next = current.next; // {6}
117+
if (current && current.next) {
118+
current.next.prev = previous; // NEW
119+
}
120+
}
121+
}
122+
}
123+
if (current) {
124+
this.count--;
125+
return current.element;
126+
}
127+
}
128+
return undefined;
129+
}
130+
131+
remove(element: T) {
132+
const index = this.indexOf(element);
133+
return this.removeAt(index);
134+
}
135+
136+
indexOf(element: T) {
137+
let current = this.head;
138+
let index = 0;
139+
140+
while (current) {
141+
if (this.equalsFn(element, current.element)) {
142+
return index;
143+
}
144+
index++;
145+
current = current.next;
146+
}
147+
148+
return -1;
149+
}
150+
151+
getHead() {
152+
return this.head;
153+
}
154+
155+
getTail() {
156+
return this.tail;
157+
}
158+
159+
isEmpty() {
160+
return this.size() === 0;
161+
}
162+
163+
clear() {
164+
this.head = undefined;
165+
this.tail = undefined;
166+
this.count = 0;
167+
}
168+
169+
size() {
170+
return this.count;
171+
}
172+
173+
toString() {
174+
if (this.head === undefined) {
175+
return '';
176+
}
177+
let objString = `${this.head.element}`;
178+
let current: DoublyNode<T> | undefined = this.head.next;
179+
while (current) {
180+
objString = `${objString},${current.element}`;
181+
current = current.next;
182+
}
183+
return objString;
184+
}
185+
186+
inverseToString() {
187+
if (this.tail === undefined) {
188+
return '';
189+
}
190+
let objString = `${this.tail.element}`;
191+
let previous: DoublyNode<T> | undefined = this.tail.prev;
192+
while (previous) {
193+
objString = `${objString},${previous.element}`;
194+
previous = previous.prev;
195+
}
196+
return objString;
197+
}
198+
}

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

+136
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
import { defaultEquals, IEqualsFunction } from '../util';
2+
import { Node } from './models/linked-list-models';
3+
4+
export default class LinkedList<T> {
5+
private count = 0;
6+
private head: Node<T> | undefined;
7+
8+
constructor(private equalsFn: IEqualsFunction<T> = defaultEquals) { }
9+
10+
push(element: T) {
11+
const node = new Node(element);
12+
let current;
13+
14+
if (this.head === undefined || this.head === null) {
15+
this.head = node;
16+
} else {
17+
current = this.head;
18+
19+
while (current.next) {
20+
current = current.next;
21+
}
22+
23+
current.next = node;
24+
}
25+
this.count++;
26+
}
27+
28+
getElementAt(index: number) {
29+
if (index >= 0 && index <= this.count) {
30+
let node = this.head;
31+
for (let i = 0; i < index; i++) {
32+
if (node) {
33+
node = node.next;
34+
}
35+
}
36+
return node;
37+
}
38+
return undefined;
39+
}
40+
41+
insert(index: number, element: T) {
42+
if (index >= 0 && index <= this.count) {
43+
const node = new Node(element);
44+
const current = this.head;
45+
46+
if (index === 0) {
47+
node.next = current;
48+
this.head = node;
49+
} else {
50+
const previous = this.getElementAt(index - 1);
51+
if (previous) {
52+
node.next = previous.next;
53+
previous.next = node;
54+
}
55+
}
56+
this.count++;
57+
return true;
58+
}
59+
return false;
60+
}
61+
62+
removeAt(index: number) {
63+
if (index >= 0 && index < this.count) {
64+
let current = this.head;
65+
66+
if (index === 0) {
67+
if (this.head) {
68+
this.head = this.head.next;
69+
}
70+
} else {
71+
const previous = this.getElementAt(index - 1);
72+
if (previous) {
73+
current = previous.next;
74+
if (current) {
75+
previous.next = current.next;
76+
}
77+
}
78+
}
79+
if (current) {
80+
this.count--;
81+
return current.element;
82+
}
83+
}
84+
return undefined;
85+
}
86+
87+
remove(element: T) {
88+
const index = this.indexOf(element);
89+
return this.removeAt(index);
90+
}
91+
92+
indexOf(element: T) {
93+
let current = this.head;
94+
let index = 0;
95+
96+
while (current) {
97+
if (this.equalsFn(element, current.element)) {
98+
return index;
99+
}
100+
index++;
101+
current = current.next;
102+
}
103+
104+
return -1;
105+
}
106+
107+
isEmpty() {
108+
return this.size() === 0;
109+
}
110+
111+
size() {
112+
return this.count;
113+
}
114+
115+
getHead() {
116+
return this.head;
117+
}
118+
119+
clear() {
120+
this.head = undefined;
121+
this.count = 0;
122+
}
123+
124+
toString() {
125+
if (this.head === undefined) {
126+
return '';
127+
}
128+
let objString = `${this.head.element}`;
129+
let current: Node<T> | undefined = this.head.next;
130+
while (current) {
131+
objString = `${objString},${current.element}`;
132+
current = current.next;
133+
}
134+
return objString;
135+
}
136+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
export class Node<T> {
2+
constructor(public element: T, public next?: Node<T>) {}
3+
}
4+
5+
export class DoublyNode<T> extends Node<T> {
6+
constructor(
7+
public element: T,
8+
public next?: DoublyNode<T>,
9+
public prev?: DoublyNode<T>
10+
) {
11+
super(element, next);
12+
}
13+
}

‎src/ts/index.ts

+10-6
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1-
import { hotPotato } from './others/hot-potato';
1+
export { default as CircularLinkedList } from './data-structures/circular-linked-list';
2+
export { default as DoublyLinkedList } from './data-structures/doubly-linked-list';
3+
export { default as LinkedList } from './data-structures/linked-list';
4+
5+
import * as _util from './util';
6+
export const util = _util;
7+
8+
/* import { hotPotato } from './others/hot-potato';
29
import { palindromeChecker } from './others/palindrome-checker';
310
import Deque from './data-structures/deque';
411
import Queue from './data-structures/queue';
@@ -7,7 +14,7 @@ import { baseConverter, decimalToBinary } from './others/base-converter';
714
import StackArray from './data-structures/stack-array';
815
import Stack from './data-structures/stack';
916
import { parenthesesChecker } from './others/balanced-symbols';
10-
import { Compare, defaultCompare, ICompareFunction } from './util';
17+
1118
1219
export {
1320
Stack,
@@ -17,11 +24,8 @@ export {
1724
decimalToBinary,
1825
hanoi,
1926
hanoiStack,
20-
ICompareFunction,
21-
defaultCompare,
22-
Compare,
2327
Queue,
2428
Deque,
2529
hotPotato,
2630
palindromeChecker
27-
};
31+
}; */

‎src/ts/util.ts

+6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
export type ICompareFunction<T> = (a: T, b: T) => number;
22

3+
export type IEqualsFunction<T> = (a: T, b: T) => boolean;
4+
35
export enum Compare {
46
LESS_THAN = -1,
57
BIGGER_THAN = 1
@@ -11,3 +13,7 @@ export function defaultCompare<T>(a: T, b: T): number {
1113
}
1214
return a < b ? Compare.LESS_THAN : Compare.BIGGER_THAN;
1315
}
16+
17+
export function defaultEquals<T>(a: T, b: T): boolean {
18+
return a === b;
19+
}
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/ts/index';
4+
import MyObj from './my-obj';
5+
6+
describe('CircularLinkedList', () => {
7+
let list: CircularLinkedList<number>;
8+
let min: number;
9+
let max: number;
10+
11+
beforeEach(function() {
12+
list = new CircularLinkedList<number>();
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(0, element)).to.equal(true);
84+
verifyList();
85+
});
86+
87+
it('inserts elements first position not empty list', () => {
88+
max = 2;
89+
expect(list.insert(0, max)).to.equal(true);
90+
91+
expect(list.insert(0, min)).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(0, element)).to.equal(true);
103+
expect(list.insert(2, element)).to.equal(false);
104+
});
105+
106+
it('inserts elements in the middle of list', () => {
107+
expect(list.insert(0, 3)).to.equal(true);
108+
expect(list.insert(0, 1)).to.equal(true);
109+
expect(list.insert(1, 2)).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 - 1, i)).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 = max; 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<string>();
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<MyObj>();
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/ts/data-structures/doubly-linked-list.spec.ts

+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/ts/index';
4+
import MyObj from './my-obj';
5+
6+
describe('LinkedList', () => {
7+
let list: LinkedList<number>;
8+
let min: number;
9+
let max: number;
10+
11+
beforeEach(function() {
12+
list = new LinkedList<number>(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(0, element)).to.equal(true);
79+
verifyList();
80+
});
81+
82+
it('inserts elements first position not empty list', () => {
83+
max = 2;
84+
expect(list.insert(0, max)).to.equal(true);
85+
86+
expect(list.insert(0, min)).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(0, element)).to.equal(true);
98+
expect(list.insert(2, element)).to.equal(false);
99+
});
100+
101+
it('inserts elements in the middle of list', () => {
102+
expect(list.insert(0, 3)).to.equal(true);
103+
expect(list.insert(0, 1)).to.equal(true);
104+
expect(list.insert(1, 2)).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 - 1, i)).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 = max; 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<string>();
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<MyObj>();
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/ts/data-structures/my-obj.ts

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export default class MyObj {
2+
constructor(public el1: any, public el2: any) { }
3+
toString() {
4+
return `${this.el1.toString()}|${this.el2.toString()}`;
5+
}
6+
}
7+

0 commit comments

Comments
 (0)
Please sign in to comment.