数据结构之链表(LinkedList)

105 阅读3分钟

链表

1. 概念:

链表(Linked list)是一种上一个元素的引用指向下一个元素的存储结构,链表通过指针来连接元素与元素;

链表是线性表的一种,所谓的线性表包含顺序线性表和链表,顺序线性表是用数组实现的,在内存中有顺序排列,通过改变数组大小实现。而链表不是用顺序实现的,用指针实现,在内存中不连续。意思就是说,链表就是 将一系列不连续的内存联系起来,将那种碎片内存进行合理的利用,解决空间的问题。

所以,链表允许插入和删除表上任意位置上的节点,但是不允许随即存取。链表有很多种不同的类型: 单向链表双向链表循环链表

2. 与数组的区别:

  • 相同:

两种结构均可实现数据的顺序存储,构造出来的模型呈线性结构

  • 不同:
  1. 链表是链式的存储结构;数组是顺序的存储结构
  1. 链表通过指针来连接元素与元素,数组则是把所有元素按次序依次存储。
  1. 链表的插入删除元素相对数组较为简单,不需要移动元素,且较为容易实现长度扩充,但是寻找某个元素较为困难。
  1. 数组寻找某个元素较为简单,但插入与删除比较复杂,由于最大长度需要再编程一开始时指定,故当达到最大长度时,扩充长度不如链表方便。

数组和链表一些操作的时间复杂度对比:

数组:

  • 查找复杂度:O(1)
  • 添加/删除复杂度:O(n)

链表:

  • 查找复杂度:O(n)
  • 添加/删除复杂度:O(1)

3. 生活中的案例:

火车,是由一些列车厢连接起来

寻宝游戏,每个线索都是下一个线索地点的指针。

单向链表的实现

// 单向链表
class Node {
    constructor(element) {
        this.element = element; //当前节点的元素
        this.next = null; //下一个节点链接
    }
}

class LinkedList {
    constructor() {
        this.head = new Node("head");
    }
    // 寻找节点
    find(item) {
        var currNode = this.head;
        while (currNode.element != item) {
            currNode = currNode.next;
        }
        return currNode;
    }
    // 在 item 之后插入 newElement 节点
    insert(newElement, item) {
        var newNode = new Node(newElement);
        var currNode = this.find(item);
        newNode.next = currNode.next;
        currNode.next = newNode;
    }
    // 显示链表
    display() {
        var currNode = this.head;
        while (currNode.next !== null) {
            console.log(currNode.next.element);
            console.log(t);
            currNode = currNode.next;
        }
    }
    // 找前一个
    findPrev(item) {
        var currNode = this.head;
        while (!(currNode.next == null) && currNode.next.element != item) {
            currNode = currNode.next;
        }
        return currNode;
    }
    // 移除
    remove(item) {
        var prevNode = this.findPrev(item);
        if (!(prevNode.next == null)) {
            prevNode.next = prevNode.next.next;
        }
    }
}
var fruits = new LinkedList();

fruits.insert("Apple", "head");
fruits.insert("Banana", "Apple");
fruits.insert("Pear", "Banana");
console.log(fruits);

双向链表的实现

双向链表和普通链表的区别在于,在链表中, 一个节点只有链向下一个节点的链接,而在双向链表中,链接是双向的:一个链向下一个元素, 另一个链向前一个元素

// 链表节点
class Node {
    constructor(element) {
        this.element = element;
        this.prev = null;
        this.next = null;
    }
}

// 双向链表
class DoublyLinkedList {
    constructor() {
        this.head = null;
        this.tail = null;
        this.length = 0;
    }

    // 任意位置插入元素
    insert(position, element) {
        if (position >= 0 && position <= this.length) {
            const node = new Node(element);
            let current = this.head;
            let previous = null;
            let index = 0;
            // 首位
            if (position === 0) {
                if (!head) {
                    this.head = node;
                    this.tail = node;
                } else {
                    node.next = current;
                    this.head = node;
                    current.prev = node;
                }
                // 末位
            } else if (position === this.length) {
                current = this.tail;
                current.next = node;
                node.prev = current;
                this.tail = node;
                // 中位
            } else {
                while (index++ < position) {
                    previous = current;
                    current = current.next;
                }
                node.next = current;
                previous.next = node;
                current.prev = node;
                node.prev = previous;
            }
            this.length++;
            return true;
        }
        return false;
    }

    // 移除指定位置元素
    removeAt(position) {
        if (position > -1 && position < this.length) {
            let current = this.head;
            let previous = null;
            let index = 0;

            // 首位
            if (position === 0) {
                this.head = this.head.next;
                this.head.prev = null;
                if (this.length === 1) {
                    this.tail = null;
                }
                // 末位
            } else if (position === this.length - 1) {
                this.tail = this.tail.prev;
                this.tail.next = null;
                // 中位
            } else {
                while (index++ < position) {
                    previous = current;
                    current = current.next;
                }
                previous.next = current.next;
                current.next.prev = previous;
            }
            this.length--;
            return current.element;
        } else {
            return null;
        }
    }

    // 其他方法...
}