Skip to content

Commit cf312fe

Browse files
committed
Introduce a linked list implementation
1 parent 1e9889f commit cf312fe

File tree

6 files changed

+576
-0
lines changed

6 files changed

+576
-0
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ A data structure is a data organization, management, and storage format that ena
1111
* [Queue](src/data-structures/queue) - a data structure to follow the FIFO principle;
1212
* [Graph](src/data-structures/graph) - a set of vertices and edges;
1313
* [Stack](src/data-structures/stack) - a data structure to follow the LIFO principle;
14+
* [Linked list](src/data-structures/linked-list) - a data structure to follow the FIFO principle;
1415

1516
## Algorithms
1617

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Linked list
2+
3+
In computer science, a linked list is a linear collection of data elements whose order is not given by their physical placement in memory. Instead, each element points to the next. It is a data structure consisting of a collection of nodes which together represent a sequence. In its most basic form, each node contains: data, and a reference (in other words, a link) to the next node in the sequence. This structure allows for efficient insertion or removal of elements from any position in the sequence during iteration. More complex variants add additional links, allowing more efficient insertion or removal of nodes at arbitrary positions. A drawback of linked lists is that access time is linear (and difficult to pipeline). Faster access, such as random access, is not feasible. Arrays have better cache locality compared to linked lists.
4+
5+
![alt text](https://upload.wikimedia.org/wikipedia/commons/thumb/6/6d/Singly-linked-list.svg/408px-Singly-linked-list.svg.png)
6+
7+
A linked list whose nodes contain two fields: an integer value and a link to the next node. The last node is linked to a terminator used to signify the end of the list.
8+
9+
## References
10+
11+
- [Wikipedia](https://en.wikipedia.org/wiki/Linked_list)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
1+
import { LinkedList } from '../linkedList';
2+
3+
describe('LinkedList', () => {
4+
it('should create empty linked list', () => {
5+
const linkedList = new LinkedList();
6+
expect(linkedList.toString()).toBe('');
7+
});
8+
9+
it('should append node to linked list', () => {
10+
const linkedList = new LinkedList();
11+
12+
expect(linkedList.head).toBeNull();
13+
expect(linkedList.tail).toBeNull();
14+
15+
linkedList.append(1);
16+
linkedList.append(2);
17+
18+
expect(linkedList.toString()).toBe('1,2');
19+
expect(linkedList.tail.next).toBeNull();
20+
});
21+
22+
it('should prepend node to linked list', () => {
23+
const linkedList = new LinkedList();
24+
25+
linkedList.prepend(2);
26+
expect(linkedList.head.toString()).toBe('2');
27+
expect(linkedList.tail.toString()).toBe('2');
28+
29+
linkedList.append(1);
30+
linkedList.prepend(3);
31+
32+
expect(linkedList.toString()).toBe('3,2,1');
33+
});
34+
35+
it('should delete node by value from linked list', () => {
36+
const linkedList = new LinkedList();
37+
38+
expect(linkedList.delete(5)).toBeNull();
39+
40+
linkedList.append(1);
41+
linkedList.append(1);
42+
linkedList.append(2);
43+
linkedList.append(3);
44+
linkedList.append(3);
45+
linkedList.append(3);
46+
linkedList.append(4);
47+
linkedList.append(5);
48+
49+
expect(linkedList.head.toString()).toBe('1');
50+
expect(linkedList.tail.toString()).toBe('5');
51+
52+
const deletedNode = linkedList.delete(3);
53+
expect(deletedNode.value).toBe(3);
54+
expect(linkedList.toString()).toBe('1,1,2,4,5');
55+
56+
linkedList.delete(3);
57+
expect(linkedList.toString()).toBe('1,1,2,4,5');
58+
59+
linkedList.delete(1);
60+
expect(linkedList.toString()).toBe('2,4,5');
61+
62+
expect(linkedList.head.toString()).toBe('2');
63+
expect(linkedList.tail.toString()).toBe('5');
64+
65+
linkedList.delete(5);
66+
expect(linkedList.toString()).toBe('2,4');
67+
68+
expect(linkedList.head.toString()).toBe('2');
69+
expect(linkedList.tail.toString()).toBe('4');
70+
71+
linkedList.delete(4);
72+
expect(linkedList.toString()).toBe('2');
73+
74+
expect(linkedList.head.toString()).toBe('2');
75+
expect(linkedList.tail.toString()).toBe('2');
76+
77+
linkedList.delete(2);
78+
expect(linkedList.toString()).toBe('');
79+
});
80+
81+
it('should delete linked list tail', () => {
82+
const linkedList = new LinkedList();
83+
84+
linkedList.append(1);
85+
linkedList.append(2);
86+
linkedList.append(3);
87+
88+
expect(linkedList.head.toString()).toBe('1');
89+
expect(linkedList.tail.toString()).toBe('3');
90+
91+
const deletedNode1 = linkedList.deleteTail();
92+
93+
expect(deletedNode1.value).toBe(3);
94+
expect(linkedList.toString()).toBe('1,2');
95+
expect(linkedList.head.toString()).toBe('1');
96+
expect(linkedList.tail.toString()).toBe('2');
97+
98+
const deletedNode2 = linkedList.deleteTail();
99+
100+
expect(deletedNode2.value).toBe(2);
101+
expect(linkedList.toString()).toBe('1');
102+
expect(linkedList.head.toString()).toBe('1');
103+
expect(linkedList.tail.toString()).toBe('1');
104+
105+
const deletedNode3 = linkedList.deleteTail();
106+
107+
expect(deletedNode3.value).toBe(1);
108+
expect(linkedList.toString()).toBe('');
109+
expect(linkedList.head).toBeNull();
110+
expect(linkedList.tail).toBeNull();
111+
});
112+
113+
it('should delete linked list head', () => {
114+
const linkedList = new LinkedList();
115+
116+
expect(linkedList.deleteHead()).toBeNull();
117+
118+
linkedList.append(1);
119+
linkedList.append(2);
120+
121+
expect(linkedList.head.toString()).toBe('1');
122+
expect(linkedList.tail.toString()).toBe('2');
123+
124+
const deletedNode1 = linkedList.deleteHead();
125+
126+
expect(deletedNode1.value).toBe(1);
127+
expect(linkedList.toString()).toBe('2');
128+
expect(linkedList.head.toString()).toBe('2');
129+
expect(linkedList.tail.toString()).toBe('2');
130+
131+
const deletedNode2 = linkedList.deleteHead();
132+
133+
expect(deletedNode2.value).toBe(2);
134+
expect(linkedList.toString()).toBe('');
135+
expect(linkedList.head).toBeNull();
136+
expect(linkedList.tail).toBeNull();
137+
});
138+
139+
it('should be possible to store objects in the list and to print them out', () => {
140+
const linkedList = new LinkedList();
141+
142+
const nodeValue1 = { value: 1, key: 'key1' };
143+
const nodeValue2 = { value: 2, key: 'key2' };
144+
145+
linkedList
146+
.append(nodeValue1)
147+
.prepend(nodeValue2);
148+
149+
const nodeStringifier = (value) => `${value.key}:${value.value}`;
150+
debugger;
151+
expect(linkedList.toString(nodeStringifier)).toBe('key2:2,key1:1');
152+
});
153+
154+
it('should find node by value', () => {
155+
const linkedList = new LinkedList();
156+
157+
expect(linkedList.find({ value: 5 })).toBeNull();
158+
159+
linkedList.append(1);
160+
expect(linkedList.find({ value: 1 })).toBeDefined();
161+
162+
linkedList
163+
.append(2)
164+
.append(3);
165+
166+
const node = linkedList.find({ value: 2 });
167+
168+
expect(node.value).toBe(2);
169+
expect(linkedList.find({ value: 5 })).toBeNull();
170+
});
171+
172+
it('should find node by callback', () => {
173+
const linkedList = new LinkedList();
174+
175+
linkedList
176+
.append({ value: 1, key: 'test1' })
177+
.append({ value: 2, key: 'test2' })
178+
.append({ value: 3, key: 'test3' });
179+
180+
const node = linkedList.find({ callback: (value) => value.key === 'test2' });
181+
182+
expect(node).toBeDefined();
183+
expect(node.value.value).toBe(2);
184+
expect(node.value.key).toBe('test2');
185+
expect(linkedList.find({ callback: (value) => value.key === 'test5' })).toBeNull();
186+
});
187+
188+
it('should create linked list from array', () => {
189+
const linkedList = new LinkedList();
190+
linkedList.fromArray([1, 1, 2, 3, 3, 3, 4, 5]);
191+
192+
expect(linkedList.toString()).toBe('1,1,2,3,3,3,4,5');
193+
});
194+
195+
it('should find node by means of custom compare function', () => {
196+
const comparatorFunction = (a, b) => {
197+
if (a.customValue === b.customValue) {
198+
return 0;
199+
}
200+
201+
return a.customValue < b.customValue ? -1 : 1;
202+
};
203+
204+
const linkedList = new LinkedList(comparatorFunction);
205+
206+
linkedList
207+
.append({ value: 1, customValue: 'test1' })
208+
.append({ value: 2, customValue: 'test2' })
209+
.append({ value: 3, customValue: 'test3' });
210+
211+
const node = linkedList.find({
212+
value: { value: 2, customValue: 'test2' },
213+
});
214+
215+
expect(node).toBeDefined();
216+
expect(node.value.value).toBe(2);
217+
expect(node.value.customValue).toBe('test2');
218+
expect(linkedList.find({value: { value: 2, customValue: 'test5' }})).toBeNull();
219+
});
220+
221+
it('should find preferring callback over compare function', () => {
222+
const greaterThan = (value, compareTo) => (value > compareTo ? 0 : 1);
223+
224+
const linkedList = new LinkedList(greaterThan);
225+
linkedList.fromArray([1, 2, 3, 4, 5]);
226+
227+
let node = linkedList.find({ value: 3 });
228+
expect(node.value).toBe(4);
229+
230+
node = linkedList.find({ callback: (value) => value < 3 });
231+
expect(node.value).toBe(1);
232+
});
233+
234+
it('should convert to array', () => {
235+
const linkedList = new LinkedList();
236+
linkedList.append(1);
237+
linkedList.append(2);
238+
linkedList.append(3);
239+
expect(linkedList.toArray().join(',')).toBe('1,2,3');
240+
});
241+
242+
it('should reverse linked list', () => {
243+
const linkedList = new LinkedList();
244+
245+
// Add test values to linked list.
246+
linkedList
247+
.append(1)
248+
.append(2)
249+
.append(3);
250+
251+
expect(linkedList.toString()).toBe('1,2,3');
252+
expect(linkedList.head.value).toBe(1);
253+
expect(linkedList.tail.value).toBe(3);
254+
255+
// Reverse linked list.
256+
linkedList.reverse();
257+
expect(linkedList.toString()).toBe('3,2,1');
258+
expect(linkedList.head.value).toBe(3);
259+
expect(linkedList.tail.value).toBe(1);
260+
261+
// Reverse linked list back to initial state.
262+
linkedList.reverse();
263+
expect(linkedList.toString()).toBe('1,2,3');
264+
expect(linkedList.head.value).toBe(1);
265+
expect(linkedList.tail.value).toBe(3);
266+
});
267+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { LinkedListNode } from '../linkedListNode';
2+
3+
describe('LinkedListNode', () => {
4+
it('should create list node with value', () => {
5+
const node = new LinkedListNode(1);
6+
7+
expect(node.value).toBe(1);
8+
expect(node.next).toBeNull();
9+
});
10+
11+
it('should create list node with object as a value', () => {
12+
const nodeValue = { value: 1, key: 'test' };
13+
const node = new LinkedListNode(nodeValue);
14+
15+
expect(node.value.value).toBe(1);
16+
expect(node.value.key).toBe('test');
17+
expect(node.next).toBeNull();
18+
});
19+
20+
it('should link nodes together', () => {
21+
const node2 = new LinkedListNode(2);
22+
const node1 = new LinkedListNode(1, node2);
23+
24+
expect(node1.next).toBeDefined();
25+
expect(node2.next).toBeNull();
26+
expect(node1.value).toBe(1);
27+
expect(node1.next.value).toBe(2);
28+
});
29+
30+
it('should convert node to string', () => {
31+
const node = new LinkedListNode(1);
32+
33+
expect(node.toString()).toBe('1');
34+
35+
node.value = 'string value';
36+
expect(node.toString()).toBe('string value');
37+
});
38+
39+
it('should convert node to string with custom stringifier', () => {
40+
const nodeValue = { value: 1, key: 'test' };
41+
const node = new LinkedListNode(nodeValue);
42+
const toStringCallback = (value) => `value: ${value.value}, key: ${value.key}`;
43+
44+
expect(node.toString(toStringCallback)).toBe('value: 1, key: test');
45+
});
46+
});

0 commit comments

Comments
 (0)