剑指offer21——反转链表
反转链表
定义一个函数,输入一个链表的头结点,反转该链表并输出反转后链表的头结点。
思考题:
- 请同时实现迭代版本和递归版本。
数据范围
链表长度 [ 0 , 30 ] [0,30] [0,30]。
样例
输入:1->2->3->4->5->NULL输出:5->4->3->2->1->NULL
方案一、迭代
翻转即将所有节点的 next
指针指向前驱节点。
由于是单链表,我们在迭代时不能直接找到前驱节点,所以需要一个额外的指针保存前驱节点。
注意:在改变当前节点的 next
指针前,不要忘记保存它的后继节点。
复杂度分析:
- 空间复杂度:遍历时只有 3 个额外变量(前驱节点、当前节点、后继节点),所以额外的空间复杂度是 O(1)。
- 时间复杂度:只遍历一次链表,时间复杂度是 O(n)。
/*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode(int x) : val(x), next(NULL) {}* };*/
class Solution {
public:ListNode* reverseList(ListNode* head) {ListNode *prev = nullptr;ListNode *cur = head;while (cur){ListNode *next = cur->next;cur->next = prev;prev = cur, cur = next;}return prev;}
};
方案一、递归
首先考虑 reverseList
函数的作用:它可以翻转一个链表,并返回新链表的头节点(即原链表的尾节点)。
递归过程:
- 递归处理
reverseList(head->next)
:- 翻转以
head->next
为头节点的子链表,并返回原链表的尾节点tail
。
- 翻转以
- 调整指针:
- 此时
head->next
是新链表的尾节点,将其next
指针指向head
。 - 将
head->next
置空(防止成环)。
- 此时
- 返回新头节点:
- 新链表的头节点是
tail
,最终返回它即可完成整个链表的翻转。
- 新链表的头节点是
复杂度分析:
- 空间复杂度:递归深度为
n
,系统栈空间占用 O(n)。 - 时间复杂度:每个节点仅被访问一次,时间复杂度为 O(n)。
/*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode(int x) : val(x), next(NULL) {}* };*/
class Solution {
public:ListNode* reverseList(ListNode* head) {if (!head || !head->next) return head;ListNode *tail = reverseList(head->next);head->next->next = head;head->next = nullptr;return tail;}
};