A linked list is a collection of items where each item points to the next one in the list. Because of this structure, linked lists are very slow when searching for an item at a particular index. An array, by comparison, has quick gets when searching for an index, but a linked list must start at the beginning, often called the "head", and loop through each item's next property until we arrive at the item. This makes gets in a linked list an operation that takes O(n) time.
While gets might be slow in a linked list, it's other operations, like push and delete come with some great benefits we will see in the lesson.
/** * Linked list * * API: * push * pop * get * delete * isEmpty * print */ function createNode(value) { return { value, next: null } } function createLinkedList() { return { head: null, tail: null, length: 0, push(value) { /**Key takeaway: * Assign new node to current tail's next value * Then * Reassign the tail to new node */ // Create Node const node = createNode(value); // If this is the first one if (this.head === null) { this.head = node this.tail = node this.length++; return node; } // if there already has nodes this.tail.next = node; this.tail = node; this.length++; return node; }, pop() { const node = this.tail; // if this is no node if (!this.head) { return null; } // if there is one node if (this.head === this.tail) { this.head = null; this.tail = null; return node; } let current = this.head; let penultimate = null; while (current) { const {next} = current; if (next && next == this.tail) { penultimate = current; break; } current = current.next; } penultimate.next = null; this.tail = penultimate; this.length--; return node; }, get(index = 0) { // no node in the list, return null if (!this.head) { return null; } // if the index < 0 or > length - 1, out of range if (index < 0 || index > this.length - 1) { return null; } // if index = 0, then return the first if (index === 0) { return this.head; } let current = this.head; let i = 0; while (i < index) { i++; current = current.next; } return current; }, delete(index = 0) { /** * Key takewawy: * If we delete tail, we need to reassign the tail */ // no node in the list, return null if (!this.head) { return null; } // if the index < 0 or > length - 1, out of range if (index < 0 || index > this.length - 1) { return null; } // if index = 0, then return the first if (index === 0) { const node = this.head; this.head = node.next; this.length--; return node; } let i = 0; let current = this.head; let previous = null; while (i < index) { i++; previous = current; current = current.next; } const deleted = current; previous.next = deleted.next; // If we delete the tail, we need to reassign tail if (previous.next === null) { this.tail = previous; } this.length--; return deleted; }, isEmpty() { return this.length === 0; }, print() { /**Key takeway: * remember to assign next node to current * Move the while loop * */ let nodes = []; if (!this.head) { return 'Empty list'; } let current = this.head; while (current) { nodes.push(current.value); current = current.next; } return nodes.join(' => '); } }; } module.exports = {createLinkedList}
test:
const {createLinkedList} = require('../src/linked-list');
describe('linked list', () => {
test('push: should add node into array', () => {
const l = createLinkedList();
// linked list should be empty
expect(l.isEmpty()).toBe(true);
// push a new node
l.push('a');
expect(l.isEmpty()).toBe(false);
expect(l.length).toEqual(1);
expect(l.print()).toEqual('a');
// push a second node
l.push('b');
expect(l.length).toEqual(2);
expect(l.print()).toEqual('a => b');
});
test('pop: should remove the last node from the list', () => {
const l = createLinkedList();
l.push('a');
l.push('b');
l.push('c');
expect(l.length).toEqual(3);
const p = l.pop();
expect(p.value).toEqual('c');
expect(l.length).toEqual(2);
});
test('get: should return the node for the given index', () => {
const l = createLinkedList();
// empty list, return null
expect(l.get(0)).toBeNull();
l.push('a');
l.push('b');
l.push('c');
expect(l.length).toEqual(3);
// out of index, retur null
expect(l.get(-1)).toBeNull();
expect(l.get(4)).toBeNull();
// return the head
expect(l.get(0).value).toEqual('a');
// index in range not head
expect(l.get(2).value).toEqual('c');
});
test('delete: should delete the node from the given index', () => {
const l = createLinkedList();
// empty list, return null
expect(l.delete(0)).toBeNull();
l.push('a');
l.push('b');
l.push('c');
expect(l.length).toEqual(3);
// out of index, retur null
expect(l.delete(-1)).toBeNull();
expect(l.delete(4)).toBeNull();
// return the head
expect(l.delete(0).value).toEqual('a');
expect(l.length).toEqual(2);
// delete the tail, reassign the tail
expect(l.delete(1).value).toEqual('c');
expect(l.tail.value).toEqual('b');
});
});