反转链表在解决需要从尾节点开始遍历到头节点的地方很实用,是一种常用的解题技巧。在反转时,我们可以考虑从前向后反转和从后向前反转两种方式。
法一:递归
每次将链表的头节点的下一个节点作为新的头节点,然后对剩余部分调用递归函数。这样递归调用会一直执行,直到到达链表的尾部,从链表的尾部开始,逆序地将节点连接起来。
public ListNode reverseList(ListNode head) {
// 如果链表为空或只有一个节点,直接返回该链表,因为反转一个节点或空链表都是其本身
if (head == null || head.next == null) {
return head;
}
// 将链表的头节点的下一个节点作为新的头节点
ListNode resetHead = head.next;
// 对剩余部分调用递归函数,得到反转后的链表
ListNode reversedReset = reverseList(resetHead);
// 将原头节点的下一个节点指向头节点,形成反转后的链表
resetHead.next = head;
// 将原头节点的下一个节点指向 null,断开原头节点和反转后的链表的连接
head.next = null;
// 返回反转后的链表,即为新的头节点
return reversedReset;
}
该方法的时间复杂度是O(n),空间复杂度是O(n)。
法二:三指针
在从前向后反转链表时,如果将某一节点的next指针指向它的前一个节点,那么余下尚未反转的部分就会因链表断开而丢失,所以为了保证不丢失,我们需要指针去记录它们。因此,需要三个指针,分别指向当前遍历到的节点,它的前一个节点和后一个节点。
public ListNode reverseList(ListNode head) {
// 初始化前一个节点为 null,当前节点为链表头
ListNode prev = null;
ListNode cur = head;
// 遍历链表,直到当前节点为 null
while (cur != null) {
// 保存当前节点的下一个节点,以防失去链表连接
ListNode next = cur.next;
// 将当前节点的 next 指针指向前一个节点,实现反转操作
cur.next = prev;
// 更新 prev 为当前节点,cur 更新为下一个节点
prev = cur;
cur = next;
}
// 返回反转后链表的头节点
return prev;
}
该方法的时间复杂度是O(n),空间复杂度是O(1)。