JS数据结构初识(三)-链表

1、定义

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

例如寻宝游戏。你有一条线索,这条线索是指向寻找下一条线索的地点的指针。你顺着这条连接去下一个地点,获得另外一条指向再下一处的线索。获得列表中间的线索的惟一办法,就是从起点(第一条线索)顺着列表寻找。也就是链表只有给你头结点,你就能够找到剩下的元素。bash

2、链表函数骨架

function linkedList() {
        const Node = function (ele) {
            this.ele = ele;
            this.next = null;
        }
        const length = 0;
        const head = null;
        
        // 向列表尾部添加一个新的项。
        this.append = (ele) => {}
        // 向列表的特定位置插入一个新的项。
        this.insert = (position, ele) => {}
        // 从列表的特定位置移除一项。
        this.removeAt = (position) => {}
        // 返回元素在列表中的索引。若是列表中没有该元素则返回-1。
        this.indexOf = (ele) => {}
        // 从列表中移除一项。
        this.remove = (ele) => {}
        // 若是链表中不包含任何元素,返回true,若是链表长度大于0则返回false。
        this.isEmpty = () => {}
        // 返回链表包含的元素个数。与数组的length属性相似。
        this.size = () => {}
        // 因为列表项使用了Node类,就须要重写继承自JavaScript对象默认的toString方法,让其只输出元素的值。
        this.toString = () => {}
        // 打印全部节点
        this.print = () => {}
    }
复制代码

逐个实现各个方法app

2.1 向链表尾部追加元素

向LinkedList对象尾部添加一个元素时,可能有两种场景:列表为空,添加的是第一个元 素,或者列表不为空,向其追加元素。函数

this.append = (ele) => {
        const newNode = new Node(ele);
        let current; // 当前节点对象

        if (head === null) { // 无头结点
            head = newNode;
        } else {
            let head = current;
            while(current.next) {
                current = current.next;
            }
            current.next = newNode;
        }
        return ++length;
    } 
复制代码

2.2 向列表的特定位置插入一个新的项

// 向列表的特定位置插入一个新的项。
    this.insert = (position, ele) => {
        if (position < 0 || position > length) {
            return false;
        }
        const newNode = new Node(ele);
        let current = head;
        let prevNode;
        // let
        if (position === 0) { // 插入的位置是 0 头节点的位置
            NewNode.next = current;
            head = newNode;
        } else {
            while(index < position) {
                prevNode = current;
                current = current.next;
                index++;
            }
            prevNode.next = newNode;
            newNode.next = current;
        }
        length++;
        return true;
    }
复制代码

2.3 从列表的特定位置移除一项

// 从列表的特定位置移除一项。
    this.removeAt = (position) => {
        if (position < 0 || position > length) {
            return false;
        }
        const newNode = new Node();
        let current = head;
        let prevNode;
        let index = 0;
        
        if (position === 0) {
            head = newNode;
        } else {
            while (index++ < position) {
                prevNode = current;
                current = current.next;
            }
            prevNode.next = current.next; // 跳过当前current
        }
        length--;
        return true;
    }
复制代码

2.4 从列表中移除一项。

// 移除指定节点
    this.remove1 = function(ele) {
        const index = this.indexOf(ele);
        return this.removeAt(index);
    }
    
    this.remove = (ele) => {
        let index = 0;
        let current = head;
        let prevNode;
    
        while (current) {
            if (current.ele === ele) {
                break;
            }
            prevNode = current;
            current = current.next;
        }
        if (current.ele !== ele) {
            return false;
        }
        prevNode.next = current.next;
        return true;
    }
复制代码

3. 整合

function linkedList() {
        const Node = function (ele) {
            this.ele = ele;
            this.next = null;
        }
        const length = 0; // 链表的长度
        const head = null; // 头节点
        
        // 向列表尾部添加一个新的项。向LinkedList对象尾部添加一个元素时,可能有两种场景:列表为空,添加的是第一个元 素,或者列表不为空,向其追加元素。
        this.append = (ele) => {
            const newNode = new Node(ele);
            let current; // 当前节点对象

            if (head === null) { // 无头节点
                head = newNode;
            } else {
                let head = current;
                while(current.next) {
                    current = current.next;
                }
                current.next = newNode;
            }
            return ++length;
        }
        // 向列表的特定位置插入一个新的项。第一种场景: 须要在列表的起点添加一个元素,也就是第一个 位置。第二种场景: 在列表中间或尾部添加一个元素。
        this.insert = (position, ele) => {
            if (!this.inRange(position)) {
                return false;
            }
            const newNode = new Node(ele);
            let current = head;
            let prevNode;
            // let
            if (position === 0) { // 插入的位置是 0 头节点的位置
                NewNode.next = current;
                head = newNode;
            } else {
                while(index < position) {
                    prevNode = current;
                    current = current.next;
                    index++;
                }
                prevNode.next = newNode;
                newNode.next = current;
            }
            length++;
            return true;
        }
        // 从列表的特定位置移除一项。状况1: 咱们要从列表中移除第一个元素(position === 0); 状况2:移除列表的最后一项或者中间某一项
        this.removeAt = (position) => {
            if (!this.inRange(position)) {
                return false;
            }
            const newNode = new Node();
            let current = head;
            let prevNode;
            let index = 0;
            
            if (position === 0) {
                head = newNode;
            } else {
                while (index++ < position) {
                    prevNode = current;
                    current = current.next;
                }
                prevNode.next = current.next; // 跳过当前current
            }
            length--;
            return true;
        }
        // 返回元素在列表中的索引。若是列表中没有该元素则返回-1。
        this.indexOf = (ele) => {
            let current = head;
            let index = -1;
            while (current) {
                if (current.ele === ele) {
                    return index;
                }
                current = current.next;
                index++;
            }
            return -1;
        }
        // 从列表中移除一项。
        this.remove = (ele) => {
            let index = 0;
            let current = head;
            let prevNode;

            while (current) {
                if (current.ele === ele) {
                    break;
                }
                prevNode = current;
                current = current.next;
            }

            if (current.ele !== ele) {
                return false;
            }
            prevNode.next = current.next;
            return true;
        }

        this.remove1 = function(ele) {
            const index = this.indexOf(ele);
            return this.removeAt(index);
        }
        
        // 若是链表中不包含任何元素,返回true,若是链表长度大于0则返回false。
        this.isEmpty = () => length === 0;
        // 返回链表包含的元素个数。与数组的length属性相似。
        this.size = () => length;
        // 因为列表项使用了Node类,就须要重写继承自JavaScript对象默认的toString方法,让其只输出元素的值。
        this.toString = () => {
            let current = head;
            let resultStr = '';
            while (current) {
                resultStr += current;
            }
            return resultStr;
        }
        // 打印全部节点
        this.print = () => {
            let current = head;
            let results = [];
            while (current) {
                results.push(current.ele);
            }
            console.log(results);
            return results;
        }
        this.inRange = (position) => {
            return position >= 0 && position <=length;
        }
    }
复制代码

3、双向链表

双向链表和普通链表的区别在于,在链表中, 一个节点只有链向下一个节点的连接,而在双向链表中,连接是双向的:一个链向下一个元素, 另外一个链向前一个元素;普通链表只有一个next指针指向后一个元素, 而双向链表则后面的节点有一个prev指向前面节点的指针ui

function linkedList() { const Node = function (ele) { this.ele = ele; this.next = null; this.prev = null; } const length = 0; // 链表的长度 const head = null; // 头节点 this.tail = null; }

4、循环链表

循环链表能够像链表同样只有单向引用,也能够像双向链表同样有双向引用。循环链表和链表之间惟一的区别在于,最后一个元素指向下一个元素的指针(tail.next)不是引用null, 而是指向第一个元素(head),以下图所示。this

4.1 单向循环链表

4.2 双向循环链表

相关文章
相关标签/搜索