剑指 Offer 24. 反转链表
题目
定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。
示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
限制:
0 <= 节点个数 <= 5000
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/fan-zhuan-lian-biao-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
前置知识:
标签:
思路
1.自己的思路
使用 ArrayList存储链表节点,最后逆序重组链表
代码
class Offer24V0 {
public ListNode reverseListV0(ListNode head) {
ArrayList<ListNode> list = new ArrayList<>();
ListNode curNode = head;
//先遍历到链表末尾
while (curNode != null){
list.add(curNode);
curNode = curNode.next;
}
ListNode res = new ListNode(0);
curNode = res;
for (int i = list.size() - 1; i >= 0 ; i--) {
curNode.next = list.get(i);
curNode = curNode.next;
}
curNode.next = null;
return res.next;
}
}
复杂度分析
- 时间复杂度:O(n)
- 空间复杂度:O(n)
2.题解思路
2.1 迭代(双指针)
考虑遍历链表,并在访问各节点时修改 next
引用指向,算法流程见注释。
代码
class Solution {
public ListNode reverseList(ListNode head) {
ListNode cur = head, pre = null;
while(cur != null) {
ListNode tmp = cur.next; // 暂存后继节点 cur.next
cur.next = pre; // 修改 next 引用指向
pre = cur; // pre 暂存 cur
cur = tmp; // cur 访问下一节点
}
return pre;
}
}
复杂度分析
- 时间复杂度:O(n)
- 空间复杂度:O(1)
2.2 递归
考虑使用递归法遍历链表,当越过尾节点后终止递归,在回溯时修改各节点的 next 引用指向。
recur(cur, pre)
递归函数:
- 终止条件:当
cur
为空,则返回尾节点pre
(即反转链表的头节点); - 递归后继节点,记录返回值(即反转链表的头节点)为
res
; - 修改当前节点
cur
引用指向前驱节点pre
; - 返回反转链表的头节点
res
;
reverseList(head)
函数:
调用并返回 recur(head, null)
。传入 null 是因为反转链表后, head 节点指向 null ;
代码
class Offer24V2 {
public ListNode reverseList(ListNode head) {
// 调用递归并返回
return recur(head, null);
}
private ListNode recur(ListNode cur, ListNode pre) {
// 终止条件
if (cur == null) {
return pre;
}
// 终止条件
ListNode res = recur(cur.next, cur);
// 修改节点引用指向
cur.next = pre;
// 返回反转链表的头节点
return res;
}
}
复杂度分析
- 时间复杂度 :O(n), 遍历链表使用线性大小时间。
- 空间复杂度 : O(n), 遍历链表的递归深度达到 n ,系统使用 O(n) 大小额外空间。
总结
涉及到链表的操作,一定要在纸上把过程先画出来,再写程序。