Skip to content

Commit d7b64a5

Browse files
author
Kevin K. Lee
authored
Implement Doubly Linked List exercise (#3)
* Copy doubly linked list skeleton into /src * Update the find method in linked list exercise * Make minor configuration updates * Publish the initial doubly-linked list walkthrough * Update the find method to accept a callback comparator
1 parent 124c9f4 commit d7b64a5

33 files changed

+1242
-754
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
node_modules
2+
.vscode

assets/doubly-linked-list.png

36.7 KB
Loading

jest.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ module.exports = {
1313
coverageDirectory: './coverage/',
1414

1515
// If the test path matches any of the patterns, it will be skipped.
16-
testPathIgnorePatterns: ['<rootDir>/node_modules/', '/solutions'],
16+
testPathIgnorePatterns: ['<rootDir>/node_modules/', '/solutions', '/skeletons'],
1717

1818
// If the file path matches any of the patterns, coverage information will be skipped.
1919
coveragePathIgnorePatterns: ['<rootDir>/node_modules/'],
Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
11
import DoublyLinkedListNode from './DoublyLinkedListNode';
2-
import Comparator from '../../utils/comparator/Comparator';
32

43
export default class DoublyLinkedList {
5-
/**
6-
* @param {Function} [comparatorFunction]
7-
*/
8-
constructor(comparatorFunction) {}
4+
constructor() {}
95

106
/**
117
* @param {*} value
@@ -19,43 +15,43 @@ export default class DoublyLinkedList {
1915
*/
2016
append(value) {}
2117

22-
/**
23-
* @param {*} value
24-
* @return {DoublyLinkedListNode}
25-
*/
26-
delete(value) {}
27-
2818
/**
2919
* @param {Object} findParams
3020
* @param {*} findParams.value
3121
* @param {function} [findParams.callback]
3222
* @return {DoublyLinkedListNode}
3323
*/
3424
find({
35-
value = undefined,
36-
callback = undefined,
25+
value,
26+
callback
3727
}) {}
3828

29+
/**
30+
* @return {DoublyLinkedListNode}
31+
*/
32+
deleteHead() {}
33+
3934
/**
4035
* @return {DoublyLinkedListNode}
4136
*/
4237
deleteTail() {}
4338

4439
/**
40+
* @param {*} value
4541
* @return {DoublyLinkedListNode}
4642
*/
47-
deleteHead() {}
43+
delete(value) {}
4844

4945
/**
5046
* @return {DoublyLinkedListNode[]}
5147
*/
5248
toArray() {
5349
const nodes = [];
50+
let node = this.head;
5451

55-
let currentNode = this.head;
56-
while (currentNode) {
57-
nodes.push(currentNode);
58-
currentNode = currentNode.next;
52+
while (node) {
53+
nodes.push(node);
54+
node = node.next;
5955
}
6056

6157
return nodes;
@@ -65,7 +61,7 @@ export default class DoublyLinkedList {
6561
* @param {function} [callback]
6662
* @return {string}
6763
*/
68-
toString(callback) {
69-
return this.toArray().map(node => node.toString(callback)).toString();
64+
toString() {
65+
return this.toArray().map(node => node.toString()).toString();
7066
}
7167
}
Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
export default class DoublyLinkedListNode {
2-
constructor(value, next = null, previous = null) {}
2+
constructor(value, next = null, previous = null) {
3+
this.value = value;
4+
}
35

4-
toString(callback) {
5-
return callback ? callback(this.value) : `${this.value}`;
6+
toString() {
7+
return this.value.toString();
68
}
79
}
Lines changed: 77 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,79 @@
11
# Doubly Linked List
22

3-
In computer science, a **doubly linked list** is a linked data structure that
4-
consists of a set of sequentially linked records called nodes. Each node contains
5-
two fields, called links, that are references to the previous and to the next
6-
node in the sequence of nodes. The beginning and ending nodes' previous and next
7-
links, respectively, point to some kind of terminator, typically a sentinel
8-
node or null, to facilitate traversal of the list. If there is only one
9-
sentinel node, then the list is circularly linked via the sentinel node. It can
10-
be conceptualized as two singly linked lists formed from the same data items,
11-
but in opposite sequential orders.
12-
13-
![Doubly Linked List](https://upload.wikimedia.org/wikipedia/commons/5/5e/Doubly-linked-list.svg)
14-
15-
The two node links allow traversal of the list in either direction. While adding
16-
or removing a node in a doubly linked list requires changing more links than the
17-
same operations on a singly linked list, the operations are simpler and
18-
potentially more efficient (for nodes other than first nodes) because there
19-
is no need to keep track of the previous node during traversal or no need
20-
to traverse the list to find the previous node, so that its link can be modified.
21-
22-
## References
23-
24-
- [Wikipedia](https://en.wikipedia.org/wiki/Doubly_linked_list)
25-
- [YouTube](https://www.youtube.com/watch?v=JdQeNxWCguQ&t=7s&index=72&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
3+
## Description
4+
5+
Unlike a singly-linked list, a doubly-linked list node keeps a reference to the previous node in addition to the next node. This allows traversal in both directions of the list, towards the head and the tail.
6+
7+
![Linked List](../../../assets/doubly-linked-list.png)
8+
9+
The operations for the doubly linked list is the same a singly linked list.
10+
11+
## Implementation
12+
13+
In this exercise, implement the following functions for the `DoublyLinkedListNode` and `DoublyLinkedList` classes:
14+
15+
- `DoublyLinkedListNode`
16+
- `constructor()`
17+
- Write a method that instantiates the node.
18+
- The node takes `value`, `previous` and `next`.
19+
- `LinkedList`
20+
- `constructor()`
21+
- Write a method that instantiates the list.
22+
- The instance variables `head` and `tail` should be set to `null`.
23+
- `prepend(value)`
24+
- Write a method that inserts the `value` at the beginning of the linked list.
25+
- `append(value)`
26+
- Write a method that inserts the `value` at the end of the linked list.
27+
- `find(value)`
28+
- Write a method that returns the `node` that contains the `value`.
29+
- `deleteHead()`
30+
- Write a method that deletes the first element in the linked list.
31+
- `deleteTail()`
32+
- Write a method that deletes the last element in the linked list.
33+
- `delete(value)`
34+
- Write a method that deletes the `value` in the linked list.
35+
36+
The most important operations are `prepend/append` for adding data, `delete` for removing data, and `find` for retrieving data.
37+
38+
## Detailed Walkthrough
39+
40+
To start, build `DoublyLinkedListNode`. A doubly linked list node keeps a reference to the previous node in addition to the next node. Then, build the constructor the same way as the singly linked list.
41+
42+
> Jest Tip: If you need to filter by the exercise name, press `p`.
43+
> After pressing `p`, enter the string "Doubly" to filter by doubly linked list tests.
44+
45+
### `prepend()`
46+
47+
- The `prepend` method inserts the item at the beginning of the list.
48+
- Operations:
49+
- Create a new `Node`.
50+
- Set the current head's previous reference to the new node.
51+
- Set the new node's next to the current `head`.
52+
- Update `head` to point at the new node.
53+
- Take into consideration where this is the first value in the linked list.
54+
55+
### `append()`
56+
57+
- The `append` method inserts the item at the end of the list.
58+
- Operations:
59+
- Create a new `Node`.
60+
- Set the current tail's `next` to be the new node.
61+
- Set the new node's previous to the current `tail`.
62+
- Update `tail` to point at the new node.
63+
- Take into consideration where this is the first value in the linked list.
64+
65+
### `find(value)`
66+
67+
- The `find` method returns the node with the target value.
68+
- Traverse the array in the same way as a singly linked list.
69+
70+
### `deleteHead()` / `deleteTail()`
71+
72+
- The `deleteHead/Tail` methods are useful utilities methods.
73+
- Take into consideration where there is only one node in the linked list.
74+
75+
### `delete(value)`
76+
77+
- The `delete` method removes the first node with the specified value.
78+
- The delete operation for a doubly linked list is significantly simpler due to having a reference to the previous node.
79+
- Utilize `find` and `deleteHead/Tail` methods written above to write the method.

skeletons/data-structures/doubly-linked-list/__test__/DoublyLinkedList.test.js

Lines changed: 58 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -36,17 +36,70 @@ describe('DoublyLinkedList', () => {
3636
expect(linkedList.toString()).toBe('3,2,1');
3737
});
3838

39+
it('should find node by value', () => {
40+
const linkedList = new DoublyLinkedList();
41+
42+
expect(linkedList.find({
43+
value: 5
44+
})).toBeNull();
45+
46+
linkedList.append(1);
47+
expect(linkedList.find({
48+
value: 1
49+
})).toBeDefined();
50+
51+
linkedList
52+
.append(2)
53+
.append(3);
54+
55+
const node = linkedList.find({
56+
value: 2
57+
});
58+
expect(node.value).toBe(2);
59+
60+
expect(linkedList.find({
61+
value: 5
62+
})).toBeNull();
63+
});
64+
65+
it('should find node by callback', () => {
66+
const linkedList = new DoublyLinkedList();
67+
68+
linkedList
69+
.append({
70+
value: 1,
71+
key: 'test1'
72+
})
73+
.append({
74+
value: 2,
75+
key: 'test2'
76+
})
77+
.append({
78+
value: 3,
79+
key: 'test3'
80+
});
81+
82+
const node = linkedList.find({
83+
callback: value => value.key === 'test2'
84+
});
85+
86+
expect(node).toBeDefined();
87+
expect(node.value.value).toBe(2);
88+
expect(node.value.key).toBe('test2');
89+
90+
expect(linkedList.find({
91+
callback: value => value.key === 'test5'
92+
})).toBeNull();
93+
});
94+
3995
it('should delete node by value from linked list', () => {
4096
const linkedList = new DoublyLinkedList();
4197

4298
expect(linkedList.delete(5)).toBeNull();
4399

44-
linkedList.append(1);
45100
linkedList.append(1);
46101
linkedList.append(2);
47102
linkedList.append(3);
48-
linkedList.append(3);
49-
linkedList.append(3);
50103
linkedList.append(4);
51104
linkedList.append(5);
52105

@@ -55,11 +108,11 @@ describe('DoublyLinkedList', () => {
55108

56109
const deletedNode = linkedList.delete(3);
57110
expect(deletedNode.value).toBe(3);
111+
expect(linkedList.toString()).toBe('1,2,4,5');
58112
expect(linkedList.tail.previous.previous.value).toBe(2);
59-
expect(linkedList.toString()).toBe('1,1,2,4,5');
60113

61114
linkedList.delete(3);
62-
expect(linkedList.toString()).toBe('1,1,2,4,5');
115+
expect(linkedList.toString()).toBe('1,2,4,5');
63116

64117
linkedList.delete(1);
65118
expect(linkedList.toString()).toBe('2,4,5');
@@ -146,79 +199,4 @@ describe('DoublyLinkedList', () => {
146199
expect(linkedList.head).toBeNull();
147200
expect(linkedList.tail).toBeNull();
148201
});
149-
150-
it('should be possible to store objects in the list and to print them out', () => {
151-
const linkedList = new DoublyLinkedList();
152-
153-
const nodeValue1 = { value: 1, key: 'key1' };
154-
const nodeValue2 = { value: 2, key: 'key2' };
155-
156-
linkedList
157-
.append(nodeValue1)
158-
.prepend(nodeValue2);
159-
160-
const nodeStringifier = value => `${value.key}:${value.value}`;
161-
162-
expect(linkedList.toString(nodeStringifier)).toBe('key2:2,key1:1');
163-
});
164-
165-
it('should find node by value', () => {
166-
const linkedList = new DoublyLinkedList();
167-
168-
expect(linkedList.find({ value: 5 })).toBeNull();
169-
170-
linkedList.append(1);
171-
expect(linkedList.find({ value: 1 })).toBeDefined();
172-
173-
linkedList
174-
.append(2)
175-
.append(3);
176-
177-
const node = linkedList.find({ value: 2 });
178-
179-
expect(node.value).toBe(2);
180-
expect(linkedList.find({ value: 5 })).toBeNull();
181-
});
182-
183-
it('should find node by callback', () => {
184-
const linkedList = new DoublyLinkedList();
185-
186-
linkedList
187-
.append({ value: 1, key: 'test1' })
188-
.append({ value: 2, key: 'test2' })
189-
.append({ value: 3, key: 'test3' });
190-
191-
const node = linkedList.find({ callback: value => value.key === 'test2' });
192-
193-
expect(node).toBeDefined();
194-
expect(node.value.value).toBe(2);
195-
expect(node.value.key).toBe('test2');
196-
expect(linkedList.find({ callback: value => value.key === 'test5' })).toBeNull();
197-
});
198-
199-
it('should find node by means of custom compare function', () => {
200-
const comparatorFunction = (a, b) => {
201-
if (a.customValue === b.customValue) {
202-
return 0;
203-
}
204-
205-
return a.customValue < b.customValue ? -1 : 1;
206-
};
207-
208-
const linkedList = new DoublyLinkedList(comparatorFunction);
209-
210-
linkedList
211-
.append({ value: 1, customValue: 'test1' })
212-
.append({ value: 2, customValue: 'test2' })
213-
.append({ value: 3, customValue: 'test3' });
214-
215-
const node = linkedList.find({
216-
value: { value: 2, customValue: 'test2' },
217-
});
218-
219-
expect(node).toBeDefined();
220-
expect(node.value.value).toBe(2);
221-
expect(node.value.customValue).toBe('test2');
222-
expect(linkedList.find({ value: 2, customValue: 'test5' })).toBeNull();
223-
});
224202
});

0 commit comments

Comments
 (0)