给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
示例 1:

输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]
示例 2:

输入:head = [1,2]
输出:[2,1]
示例 3:
输入:head = []
输出:[]
提示:
- 链表中节点的数目范围是
[0, 5000] -5000 <= Node.val <= 5000
1. 算法核心思想:利用栈的 LIFO 特性
反转链表的核心需求是将节点顺序完全颠倒。数据结构中的“栈(Stack)”天然具备“后进先出(LIFO)”的特性。 本解法的思路非常直观:将单向链表中的节点指针从头到尾依次压入栈中,此时原链表的尾节点位于栈顶。随后不断进行出栈操作,依次将弹出的节点重新连接,即可自然地完成链表的逆序重组。
2. 逻辑拆解
- 空节点防御: 在代码起始处加入
if (head == NULL)的判断。这是因为后续操作依赖sta.top()初始化新链表头节点,若不在开头拦截空链表,会导致对空栈执行.top()操作,进而引发程序崩溃。 - 入栈与断链(解绑): 在第一个
while循环中,不仅将节点压入栈中,还通过p->next = NULL将每个节点与其后继节点断开。这是一个必要的处理:如果不提前打断原有的单向链条,在后续重新拼接时,很容易因为旧指针未清理干净而产生环形链表(死循环)。 - 出栈与重组(重建): 循环结束后,栈顶元素即为原链表的最后一个节点。将其弹出并赋给
ans作为新链表的头节点。接着进入第二个while循环,依次取出栈顶节点并连接到temp->next,直到栈为空,完成整个链表的反转拼接。
class Solution {
public:
ListNode* reverseList(ListNode* head) {
stack<ListNode*> sta;
// 边界处理:空链表直接返回 NULL,防止后续空栈访问越界
if (head == NULL) {
return NULL;
}
// 第一阶段:遍历原链表,全部压栈并断开原有连接
ListNode* p = head;
while (head) {
sta.push(head);
p = head;
head = head->next;
p->next = NULL; // 关键步骤:断开节点间的原有指针,防止后续重组时产生环
}
// 第二阶段:利用栈的后进先出特性,重组链表
ListNode* temp = sta.top();
sta.pop();
ListNode* ans = temp; // 记录原链表的尾节点,即反转后的新头节点
while (!sta.empty()) {
temp->next = sta.top(); // 将当前节点指向栈顶节点
sta.pop(); // 弹出已连接的节点
temp = temp->next; // 指针后移
}
return ans;
}
};
3. 复杂度分析
- 时间复杂度:O(N)。其中 N 为链表的长度。算法包含两次独立的循环:一次遍历原链表将节点压栈,一次循环将节点出栈并重连,整体耗时与链表长度呈线性关系。
- 空间复杂度:O(N)。为了实现逆序,额外使用了一个
std::stack来存储所有链表节点的指针,内存占用与节点数量成正比。
