在JavaScript中处理链表相关问题时,反转链表是最基础也最常见的操作之一,掌握它的实现逻辑对理解数据结构的操作方式很有帮助。链表是由多个节点组成的数据结构,每个节点包含自身的值和指向下一个节点的指针,反转链表就是要把所有节点的指针方向调转,让原本的尾节点变成头节点,原本的头节点变成尾节点。

链表的基础结构定义
在JavaScript中,我们通常通过构造函数或者类来定义链表节点,每个节点有两个属性,一个是存储当前节点的值,另一个是指向下一个节点的引用。首先我们先定义链表节点的结构:
// 定义链表节点类
class ListNode {
constructor(val, next = null) {
this.val = val; // 节点存储的值
this.next = next; // 指向下一个节点的指针,默认是null
}
}
我们可以通过以下方式创建一个简单的链表:1 -> 2 -> 3 -> 4 -> null,代码如下:
// 创建链表 1->2->3->4->null const node1 = new ListNode(1); const node2 = new ListNode(2); const node3 = new ListNode(3); const node4 = new ListNode(4); node1.next = node2; node2.next = node3; node3.next = node4;
迭代法反转链表
迭代法是反转链表最常用的实现方式,核心思路是通过三个指针依次遍历链表,逐步调整每个节点的指针方向。三个指针的作用分别是:
- prev:指向当前节点的前一个节点,初始值为null,因为反转后原来的头节点会变成尾节点,尾节点的next是null
- curr:指向当前正在处理的节点,初始值是链表的头节点
- next:指向当前节点的下一个节点,用来保存遍历时的后续节点,避免调整指针后丢失后续链表
迭代的具体步骤是:先保存当前节点的下一个节点,然后把当前节点的next指向prev,接着把prev移动到当前节点,当前节点移动到之前保存的下一个节点,重复这个过程直到当前节点为null,此时prev就是反转后的链表头节点。
迭代法的完整实现代码如下:
function reverseListIterative(head) {
let prev = null; // 前一个节点初始为null
let curr = head; // 当前节点从头部开始
while (curr !== null) {
const next = curr.next; // 保存下一个节点
curr.next = prev; // 反转当前节点的指针
prev = curr; // prev移动到当前节点
curr = next; // 当前节点移动到下一个节点
}
return prev; // 循环结束后prev就是新的头节点
}
我们可以测试一下迭代法的效果:
const reversedHead = reverseListIterative(node1);
// 遍历反转后的链表,输出结果应该是4 3 2 1
let temp = reversedHead;
while (temp !== null) {
console.log(temp.val);
temp = temp.next;
}
递归法反转链表
除了迭代法,我们还可以用递归的方式实现链表反转,递归的核心思路是先递归到链表的最后一个节点,然后从后往前逐步调整指针方向。递归的终止条件是当前节点为null或者当前节点的next为null,此时当前节点就是原链表的尾节点,也就是反转后的头节点。
递归的实现逻辑是:假设我们已经反转了当前节点之后的所有节点,那么只需要把当前节点的下一个节点的next指向当前节点,然后把当前节点的next设为null,就可以完成整个链表的反转。
递归法的完整实现代码如下:
function reverseListRecursive(head) {
// 终止条件:链表为空或者只有一个节点,直接返回
if (head === null || head.next === null) {
return head;
}
// 递归反转后面的链表,得到反转后的头节点
const newHead = reverseListRecursive(head.next);
// 把当前节点的下一个节点的next指向当前节点,实现指针反转
head.next.next = head;
// 当前节点的next设为null,避免形成环
head.next = null;
return newHead;
}
同样测试递归法的效果:
// 重新创建原链表,因为之前的链表已经被反转过了
const n1 = new ListNode(1);
const n2 = new ListNode(2);
const n3 = new ListNode(3);
const n4 = new ListNode(4);
n1.next = n2;
n2.next = n3;
n3.next = n4;
const reversedHead2 = reverseListRecursive(n1);
let temp2 = reversedHead2;
while (temp2 !== null) {
console.log(temp2.val);
temp2 = temp2.next;
}
两种方法的对比
迭代法和递归法都可以实现链表反转,两者各有特点:
| 方法 | 时间复杂度 | 空间复杂度 | 特点 |
|---|---|---|---|
| 迭代法 | O(n),n是链表长度,需要遍历一次链表 | O(1),只使用了固定的几个指针变量 | 逻辑直观,效率更高,没有递归调用栈的开销 |
| 递归法 | O(n),需要递归调用n次 | O(n),递归调用栈的深度是链表长度 | 代码更简洁,但是链表过长时可能出现栈溢出问题 |
在实际开发中,如果链表长度不确定或者较长,优先选择迭代法实现反转链表,避免递归带来的栈溢出风险。如果是处理较短的链表,并且希望代码更简洁,递归法也是不错的选择。
JavaScript反转链表链表数据结构修改时间:2026-07-09 13:45:33