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 get
s 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 get
s in a linked list an operation that takes O(n)
time.
While get
s 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'); }); });