单链表和顺序表

112 阅读4分钟

下面从多个方面进行介绍,并给出对应的 JavaScript 示例。

存储结构

  • 顺序表(数组)
    顺序表使用一段连续的内存空间来存储元素。就像一排连着的房子,每个房子(内存单元)依次存放数据元素,通过元素的下标可以直接计算出其在内存中的地址,从而快速访问元素。
  • 单链表
    单链表的节点在内存中是离散存储的,每个节点除了存储数据,还包含一个指向下一个节点的指针。节点之间通过指针相连,就像用绳子串起来的珠子,每个珠子(节点)可以放在不同的地方,通过绳子(指针)依次连接。

访问元素的效率

  • 顺序表(数组)
    可以通过下标直接访问元素,时间复杂度为 。这是因为数组在内存中是连续存储的,根据下标和数组首地址可以快速计算出元素的存储位置。
  • 单链表
    访问元素需要从头节点开始,沿着指针依次遍历,直到找到目标节点。因此,访问第  个元素的时间复杂度为 ,平均时间复杂度为 ,其中  是链表的长度。

插入和删除元素的效率

  • 顺序表(数组)
    在数组中间插入或删除元素时,需要移动后续的元素,时间复杂度为 。因为要为新元素腾出位置或填补删除元素后的空位,需要将大量元素依次向后或向前移动。
  • 单链表
    如果已知要插入或删除位置的前一个节点,插入和删除操作只需要修改指针,时间复杂度为 。但如果不知道前一个节点,需要从头节点开始遍历找到该位置,平均时间复杂度为 。

空间复杂度

  • 顺序表(数组)
    需要预先分配连续的内存空间,可能会造成内存的浪费或不足。如果数组的长度固定,当元素数量超过数组容量时,需要重新分配更大的数组并复制元素。

  • 单链表
    可以动态分配内存,每个节点只在需要时分配空间,不会造成大量的内存浪费。但每个节点需要额外的指针域来存储指向下一个节点的指针,会增加一定的空间开销。

JavaScript 示例代码

// 顺序表(数组)示例 // 创建一个顺序表(数组) const array = [1, 2, 3, 4, 5];

// 访问元素 console.log("顺序表访问元素:", array[2]); // 时间复杂度 O(1)

// 插入元素 array.splice(2, 0, 6); // 时间复杂度 O(n) console.log("顺序表插入元素后:", array);

// 删除元素 array.splice(2, 1); // 时间复杂度 O(n) console.log("顺序表删除元素后:", array);

// 单链表示例 // 定义链表节点类 class Node { constructor(data) { this.data = data; this.next = null; } }

// 定义链表类 class LinkedList { constructor() { this.head = null; }

// 向链表尾部添加节点
append(data) {
    const newNode = new Node(data);
    if (!this.head) {
        this.head = newNode;
    } else {
        let current = this.head;
        while (current.next) {
            current = current.next;
        }
        current.next = newNode;
    }
}

// 访问第 index 个元素
get(index) {
    let current = this.head;
    let count = 0;
    while (current && count < index) {
        current = current.next;
        count++;
    }
    return current? current.data : null; // 时间复杂度 O(n)
}

// 在第 index 个位置插入元素
insert(index, data) {
    const newNode = new Node(data);
    if (index === 0) {
        newNode.next = this.head;
        this.head = newNode;
    } else {
        let current = this.head;
        let count = 0;
        while (current && count < index - 1) {
            current = current.next;
            count++;
        }
        if (current) {
            newNode.next = current.next;
            current.next = newNode;
        }
    }
}

// 删除第 index 个元素
remove(index) {
    if (!this.head) return;
    if (index === 0) {
        this.head = this.head.next;
    } else {
        let current = this.head;
        let count = 0;
        while (current && count < index - 1) {
            current = current.next;
            count++;
        }
        if (current && current.next) {
            current.next = current.next.next;
        }
    }
}

}

//创建一个单链表 const linkedList = new LinkedList(); linkedList.append(1); linkedList.append(2); linkedList.append(3);

// 访问元素 console.log("单链表访问元素:", linkedList.get(2)); // 时间复杂度 O(n)

// 插入元素 linkedList.insert(2, 4); // 时间复杂度 O(n) console.log("单链表插入元素后,第 2 个元素为:", linkedList.get(2));

// 删除元素 linkedList.remove(2); // 时间复杂度 O(n) console.log("单链表删除元素后,第 2 个元素为:", linkedList.get(2));

代码解释

  • 顺序表(数组)部分

    • 使用 JavaScript 的数组来模拟顺序表。通过 [] 直接创建数组,使用下标 [] 访问元素,splice 方法插入和删除元素。
  • 单链表部分

    • 定义了 Node 类表示链表节点,包含数据域 data 和指向下一个节点的指针 next
    • 定义了 LinkedList 类表示链表,包含头节点 head。实现了 append 方法向链表尾部添加节点,get 方法访问指定位置的元素,insert 方法在指定位置插入元素,remove 方法删除指定位置的元素。