1. 程式人生 > >[Algorithoms] Linked List Data Structure in JavaScript

[Algorithoms] Linked List Data Structure in JavaScript

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');
    });
});