# 实现链表

链表存储有序的元素集合,但不同于数组,链表中的元素在内存中并不是连续放置的。每个元素由一个存储元素本身的节点和一个指向下一个元素的引用(也称指针或链接)组成。

相对于传统的数组,链表的一个好处在于,添加或移除元素的时候不需要移动其他元素。然而,链表需要使用指针,因此实现链表时需要额外注意。在数组中,我们可以直接访问任何位置的任何元素,而要想访问链表中间的一个元素,则需要从起点(表头)开始迭代链表直到找到所 需的元素。

# 单链表

class Node {
    constructor(element) {
        this.element = element
        this.next = undefined
    }
}

const defaultEquals = (a, b) => a === b

class LinkedList {
    constructor(equalsFn = defaultEquals) {
        this.count = 0;
        this.head = undefined
        this.equalsFn = equalsFn
    }

    /**
     * 向链表尾部添加一个新元素
     * @param {*} element 
     */
    push(element) {
        const node = new Node(element)
        let current
        if (this.head == null) {
            this.head = node
        } else {
            current = this.head
            // 获得最后一项
            while (current.next != null) {
                current = current.next
            }
            // 将其 next作为新元素,建立链接
            current.next = node
        }
        this.count++
    }

    /**
     * 向链表的特定位置插入一个新元素
     * @param {*} element 
     * @param {*} index 
     */
    insert(element, index) {
        if (index >= 0 && index <= this.count) {
            const node = new Node(element)
            if (index === 0) {
                const current = this.head
                node.next = current
                this.head = node
            } else {
                const previous = this.getElementAt(index - 1)
                const current = previous.next
                node.next = current
                previous.next = node
            }
            this.count++
            return true
        }
        return false
    }

    /**
     * 返回链表中特定位置的元素
     * @param {*} index 
     */
    getElementAt(index) {
        if (index >= 0 && index <= this.count) {
            let node = this.head
            for (let i = 0; i < index && node != null; i++) {
                node = node.next
            }
            return node
        }
        return undefined
    }

    /**
     * 从链表中移除一个元素。
     * @param {*} element 
     */
    remove(element) {
        const index = this.indexOf(element)
        return this.removeAt(index)
    }

    /**
     * 返回元素在链表中的索引。如果链表中没有该元素则返回-1
     * @param {*} element 
     */
    indexOf(element) {
        let current = this.head
        for (let i = 0; i < this.count && current != null; i++) {
            if (this.equalsFn(element, current.element)) {
                return i
            }
            current = current.next
        }
        return -1
    }

    /**
     * 从链表的特定位置移除一个元素
     * @param {*} index 
     */
    removeAt(index) {
        // 检查越界值
        if (index >= 0 && index < this.count) {
            let current = this.head

            // 移除第一项
            if (index === 0) {
                this.head = current.next
            } else {
                let previous = this.getElementAt(index - 1)
                current = previous.next
                // 将 previous 与 current 的下一项链接起来:跳过 current,从而移除它
                previous.next = current.next
            }
            this.count--
            return current.element
        }
        return undefined
    }

    /**
     * 如果链表中不包含任何元素,返回 true,如果链表长度大于 0则返回 false。
     */
    isEmpty() {
        return this.size() === 0
    }

    /**
     * 返回链表包含的元素个数
     */
    size() {
        return this.count
    }

    /**
     * 返回链表
     */
    getHead() {
        return this.head
    }

    /**
     * :返回表示整个链表的字符串。
     */
    toString() {
        if (this.head == null) {
            return ''
        }
        let objString = `${this.head.element}`;
        let current = this.head.next
        for (let i = 1; i < this.size() && current != null; i++) {
            objString = `${objString},${current.element}`;
            current = current.next;
        }
        return objString;
    }
}

# 双向链表

在链表中, 一个节点只有链向下一个节点的链接;而在双向链表中,链接是双向的:一个链向下一个元素, 另一个链向前一个元素 DoublyNode

class DoublyNode extends Node {
    constructor(element, next, prev) {
        super(element, next)
        this.prev = prev;
    }
}

class DoublyLinkedList extends LinkedList {
    constructor(equalsFn = defaultEquals) {
        super(equalsFn);
        this.tail = undefined;
    }

    insert(element, index) {
        if (index >= 0 && index <= this.count) {
            let node = new DoublyLinkedList(element);
            let current = this.head;
            if (index == 0) {
                if (this.head == null) {
                    this.head = node;
                    this.tail = node
                } else {
                    node.next = this.head;
                    current.prev = node;
                    this.head = node;
                }
            } else if (index == this.count) {
                current = this.tail;
                current.next = node;
                node.prev = current;
                this.tail = node;
            } else {
                const previous = this.getElementAt(index - 1);
                current = previous.next;
                node.next = current;
                previous.next = node;
                current.prev = node;
                node.prev = previous;
            }
            this.count++;
            return false
        }
        return false
    }

    removeAt(index) {
        if (index >= 0 && index < this.count) {
            let current = this.head;
            if (index == 0) {
                this.head = current.next;
                // 如果只有一项,更新 tail
                if (this.count == 1) {
                    this.tail = undefined
                } else {
                    this.head.prev = undefined
                }
            } else if (index == this.count - 1) { //最后一项
                current = this.tail;
                this.tail = current.prev;
                this.tail.next = undefined;
            } else {
                current = this.getElementAt(index);
                const previous = current.prev;
                // 将previous与current的下一项链接起来——跳过current
                previous.next = current.next;
                current.next.prev = previous;
            }
            this.count--;
            return current.element
        }
        return undefined
    }
}

# 循环链表

循环链表可以像链表一样只有单向引用,也可以像双向链表一样有双向引用。循环链表和链表之间唯一的区别在于,最后一个元素指向下一个元素的指针(tail.next)不是引用 undefined,而是指向第一个元素(head)

CircularLinkedList

class CircularLinkedList extends LinkedList {
    constructor(equalsFn = defaultEquals) {
        super(equalsFn)
    }

    insert(element, index) {
        if (index >= 0 && index < this.count) {
            const node = new Node(element);
            let current = this.head;
            if (index == 0) {
                if (this.head == null) {
                    this.head == node;
                    node.next = this.head;
                } else {
                    node.next = current;
                    current = this.getElementAt(this.size());
                    this.head = node;
                    // 最后一个节点(current) 指向新的头部节点
                    current.next = this.head;
                }
            } else {
                const previous = this.getElementAt(index - 1);
                node.next = previous.next;
                previous.next = node;
            }
            this.count++;
            return true;
        }
        return false
    }

    removeAt(index) {
        if (index >= 0 && index < this.count) {
            let current = this.head;
            if (index == 0) {
                if (this.size() == 1) {
                    this.head == undefined
                } else {
                    const removed = this.head;
                    current = this.getElementAt(this.size());
                    this.head = this.head.next;
                    current.next = this.head;
                    // 作为返回项
                    current = removed;
                }
            } else {
                // 不需要修改循环链表最后一个元素
                const previous = this.getElementAt(index - 1);
                current = previous.next;
                previous.next = current.next;
            }

            this.count--;
            return current.element;
        }
        return undefined;
    } 
}
更新时间: 6/29/2021, 4:23:20 PM