双向链表常见操作

Posted by franki on November 14, 2020

双向链表

双向链表顾名思义,有两条链,即是每个节点都包含一个 prev,next 指向,下面是双向链表的常见操作,手写双向链表的感觉有点嗨!(参考 google 后大神写的代码)

ps: 之前没有想过可以不断改变 tail 的指向来达到不断添加新的节点到链表结尾,学到了,哈哈!

双向链表图示:

doublylinkedlist

代码

// DoublyLinklistNode
class DoublyLinkedListNode {
    constructor(data) {
        this.data = data;
        this.prev = null;
        this.next = null;
    }
}

const head = Symbol("head");
const tail = Symbol("tail");

// DoublyLinkedList
class DoublyLinkedList {
    constructor() {
        this[head] = null;
        this[tail] = null;
        this.length = 0;
    }

    add(data) {
        const newNode = new DoublyLinkedListNode(data);

        if (this[head] === null) {
            this[head] = newNode;
        } else {
            this[tail].next = newNode;
            newNode.prev = this[tail];
        }

        this[tail] = newNode;
        this.length++;
    }

    addHead(data) {
        return this.insert(0, data);
    }

    addTail(data) {
        return this.insert(this.size(), data);
    }

    insert(index, data) {
        const newNode = new DoublyLinkedListNode(data);
        // add to head
        if (index === 0) {
            if (this[head] !== null) {
                newNode.next = this[head];
                this[head].prev = newNode;
            }
            this[head] = newNode;
            return data;
        }
        // add to tail
        if (index === this.size()) {
            this[tail].next = newNode;
            newNode.prev = this[tail];
            this[tail] = newNode;
            return data;
        }
        // add index between 0 ~ this.size()
        let current = this[head];
        let i = 0;
        while (current && i < index) {
            current = current.next;
            i++;
        }
        newNode.prev = current.prev;
        newNode.next = current;
        current.prev.next = newNode;
        current.prev = newNode;
        return data;
    }

    get(index) {
        let i = 0;
        let current = this[head];
        while (current && i < index) {
            current = current.next;
            i++;
        }
        return current ? current.data : undefined;
    }

    remove(index) {
        if (index < 0) {
            throw RangeError(`Index ${index} does in the list.`);
        }

        if (index === 0) {
            const data = this[head].data;
            this[head] = this[head].next;

            if (this[head] === null) {
                this[tail] = null;
            } else {
                this[head].prev = null;
            }
            this.length--;
            return data;
        } else {
            let current = this[head];

            let i = 0;
            while (current && i < index) {
                current = current.next;
                i++;
            }

            if (current) {
                current.prev.next = current.next;
                if (current === this[tail]) {
                    this[tail] = current.prev;
                } else {
                    current.next.prev = current.prev;
                }
                this.length--;
                return current.data;
            }

            throw RangeError(`Index ${index} does not in the list.`);
        }
    }

    *values() {
        let current = this[head];
        while (current) {
            yield current.data;
            current = current.next;
        }
    }

    *reverse() {
        let current = this[tail];
        while (current) {
            yield current.data;
            current = current.prev;
        }
    }

    size() {
        return this.length;
    }
}

复杂度分析

  • 时间复杂度: add: O(1) insert: O(1~n) remove: O(1~n) get: O(1~n)
  • 空间复杂度: O(1)