链表是一种通过指针相连的线性数据结构,是算法中的核心概念。

链表 (Linked List) 数据结构

链表是一种线性数据结构,元素通过指针相连。与数组不同,链表元素在内存中不必连续存储。

// 链表节点定义

class ListNode<T> {
  value: T;              // 数据域
  next: ListNode | null; // 指针域

  constructor(value: T) {
    this.value = value;
    this.next = null;
  }
}

优点

  • 动态大小,无需预分配
  • 插入/删除 O(1)(已知位置)
  • 内存利用灵活

缺点

  • 随机访问 O(n)
  • 额外的指针存储开销
  • 缓存不友好

示例 1: 链表的基本操作

HEAD
1
[0]
2
[1]
3
[2]
NULL

链表长度: 3

提示: 悬停在节点上可以看到删除按钮

链表结构: 每个节点包含两部分:数据域(存储值)和指针域(指向下一个节点)

示例 2: 反转链表(LeetCode 206)

原始链表:
1
2
3
4
5
NULL
function reverseList(head) {
  let prev = null;
  let current = head;

  while (current !== null) {
    const next = current.next; // 保存下一个节点
    current.next = prev;       // 反转指针
    prev = current;            // prev 前进
    current = next;            // current 前进
  }

  return prev; // 新的头节点
}

示例 3: 检测环形链表(LeetCode 141)

使用快慢指针(Floyd 判圈算法)检测链表中是否存在环。 快指针每次移动两步,慢指针每次移动一步。如果有环,它们必定相遇。

1
[0]
2
[1]
3
[2]
4
[3]
5
[4]
6
[5]
回到 [2]
慢指针 快指针 相遇点 环中节点
function hasCycle(head) {
  if (!head || !head.next) return false;

  let slow = head;
  let fast = head;

  while (fast && fast.next) {
    slow = slow.next;       // 慢指针走一步
    fast = fast.next.next;  // 快指针走两步

    if (slow === fast) {
      return true; // 相遇说明有环
    }
  }

  return false; // 快指针到达末尾,无环
}

示例 4: 合并两个有序链表(LeetCode 21)

链表 1:
1
3
5
7
链表 2:
2
4
6
8
合并结果:
等待合并...
function mergeTwoLists(l1, l2) {
  const dummy = new ListNode(0);
  let current = dummy;

  while (l1 && l2) {
    if (l1.val <= l2.val) {
      current.next = l1;
      l1 = l1.next;
    } else {
      current.next = l2;
      l2 = l2.next;
    }
    current = current.next;
  }

  // 连接剩余部分
  current.next = l1 || l2;

  return dummy.next;
}