- 中间栈
- 双指针
- 递归
题目描述
定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。
示例:
解题思路
方法一:中间栈
利用中间栈来反转链表,但效率极低:
算法流程:
- 当链表不为空,将链表中的值依次拿到中间栈中;
- 当中间栈不为空,将栈中的数值依次返回给链表,由于栈先进后出的特性,以此实现反转链表的目的;
- 返回链表。
代码
//利用中间栈来反转链表,但效率极低
class Solution {
public ListNode reverseList(ListNode head) {
ListNode p = head, q = head;
LinkedList<Integer> stack = new LinkedList<Integer>();
while(p != null){
stack.addLast(p.val);
p = p.next;
}
while(!stack.isEmpty()){
q.val = stack.removeLast();
q = q.next;
}
return head;
}
}
方法二:双指针+临时指针
考虑遍历链表,并在访问各节点时修改 next 引用指向。
算法流程:
- 初始化 p , q,分别指向 头节点 和 null ;
- 暂存后继节点;
- 修改 next 引用指向;
- 暂存当前节点;
- 访问下一节点。
双指针+临时指针
class Solution {
public ListNode reverseList(ListNode head) {
ListNode p = head, q = null;
while(p != null){
ListNode temp = p.next; // 暂存后继节点 p.next
p.next = q; // 修改 next 引用指向
q = p; // q 暂存 p
p = temp; // p 访问下一节点
}
return q;
}
}
复杂度分析:
- 时间复杂度 O(N) : 遍历链表使用线性大小时间。
- 空间复杂度 O(1) : 变量 p 和 q 使用常数大小额外空间。
方法三:递归
考虑使用递归法遍历链表,当越过尾节点后终止递归,在回溯时修改各节点的 next 引用指向。
算法流程:
recur(cur, pre) 递归函数:
- 终止条件:当 cur 为空,则返回尾节点 pre (即反转链表的头节点);
- 递归后继节点,记录返回值(即反转链表的头节点)为 res ;
- 修改当前节点 cur 引用指向前驱节点 pre ;
- 返回反转链表的头节点 res ;
reverseList(head) 函数:
调用并返回 recur(head, null) 。传入 null 是因为反转链表后, head 节点指向 null ;
代码:
class Solution {
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) 大小额外空间。