Skip to content

链表

核心:链表的插入和删除操作效率较高,但访问效率较低;数组的访问效率较高,但插入和删除操作效率较低。

链表和数组都是线性数据结构,但它们在内存中的存储方式不同。链表的元素在内存中是通过指针连接的,可以是不连续的;而数组的元素在内存中是连续的。

由于数组的元素是连续存储的,每个元素的内存地址可以通过其索引直接计算出来,因此数组的访问效率较高。而链表的元素是通过指针连接的,访问某个元素需要从头节点开始逐个遍历,访问效率较低。

单链表

单链表的每个节点包含两个部分:

  • 数据域:存储节点的值
  • 指针域:存储下一个节点的地址

单链表的第一个节点称为头节点,最后一个节点称为尾节点。

单链表的遍历,只能从头节点开始,依次访问链表中的每个节点,直到尾节点。

链表结点的创建

js
class ListNode {
  constructor(val) {
    this.val = val;
    this.next = null;
  }

  // 添加节点
  add(val) {
    this.next = val;
  }
}

链表的创建

js
const head = new ListNode(1);
const node2 = new ListNode(2);
const node3 = new ListNode(3);

head.next = node2;
node2.next = node3;

// 1 -> 2 -> 3

链表元素添加

js
const head = new ListNode(1);
const node2 = new ListNode(2);
const node3 = new ListNode(3);

head.next = node2;
node2.next = node3;

// 1 -> 2 -> 3

// 若要在 2 和 3 之间插入一个 4
const node4 = new ListNode(4);
node2.next = node4;
node4.next = node3;

// 1 -> 2 -> 4 -> 3

链表元素删除

js
// 1 -> 2 -> 4 -> 3

// 删除 2
node2.next = node4;

// 1 -> 4 -> 3

涉及到链表删除操作的题目中,重点不是定位目标结点,而是定位目标结点的前驱结点。

js
const deleteNode = (node) => {
  node.next = node.next.next;
};

链表的遍历

js
const traverse = (head) => {
  let current = head;
  while (current) {
    current = current.next;
  }
};

链表和数组的辨析

在大多数编程语言中,数组是一段连续的内存空间,因此在任意位置删除或插入元素时,需要移动该位置之后的所有元素。假设数组长度为 n,删除或插入一个元素的时间复杂度均为 O(n)。

链表的优势在于其元素不需要连续存储,因此在任意位置添加或删除元素时,不需要移动其他元素,操作复杂度为 O(1)。然而,链表的缺点是无法通过索引直接访问元素,查找某个元素时需要从头节点开始逐一遍历,时间复杂度为 O(n)。

基于 MIT 许可发布