反转链表(Reversing a Linked List)是链表操作中一个比较常见且重要的问题。反转链表的原理和实现思路并不复杂,但理解其背后的思想有助于更好地掌握链表的操作以及指针操作的逻辑。
反转链表的思想主要是通过逐步反转链表节点的指针方向,使链表的头节点变为尾节点,尾节点变为头节点,实现整个链表的“翻转”。
反转链表的基本思路
反转链表的思路可以概括为以下几步:
-
初始化指针:
-
初始化三个指针:
prev
指向前一个节点,current
指向当前节点,next
指向当前节点的下一个节点。 -
初始时,
prev
为null
,current
为链表的头节点。
-
-
遍历链表:
-
在遍历过程中,将
current
节点的next
指针指向prev
,实现当前节点指针的反转。 -
更新
prev
和current
指针,继续处理下一个节点。
-
-
更新头节点:
-
当
current
遍历完链表时,prev
就是新的头节点,将其返回作为反转后的链表。
-
图解示意
考虑一个链表:1 -> 2 -> 3 -> 4 -> 5
。
-
初始状态:
prev = null current = 1 next = 2
-
第一步反转:
1 -> null prev = 1 current = 2 next = 3
-
第二步反转:
2 -> 1 -> null prev = 2 current = 3 next = 4
-
持续反转直到链表末尾:
3 -> 2 -> 1 -> null prev = 3 current = 4 next = 5
-
最终状态:
5 -> 4 -> 3 -> 2 -> 1 -> null prev = 5 (新的头节点)
代码实现
以下是反转单链表的Java实现:
class ListNode { int val; ListNode next; ListNode(int val) { this.val = val; } } public class ReverseLinkedList { public static ListNode reverseList(ListNode head) { ListNode prev = null; ListNode current = head; while (current != null) { // 暂存下一节点 ListNode next = current.next; // 反转当前节点的指针 current.next = prev; // 向前移动指针 prev = current; current = next; } // 新的头节点 return prev; } public static void printList(ListNode head) { ListNode current = head; while (current != null) { System.out.print(current.val + " "); current = current.next; } System.out.println(); } public static void main(String[] args) { // 创建测试链表:1 -> 2 -> 3 -> 4 -> 5 ListNode head = new ListNode(1); head.next = new ListNode(2); head.next.next = new ListNode(3); head.next.next.next = new ListNode(4); head.next.next.next.next = new ListNode(5); System.out.print("Original List: "); printList(head); // 反转链表 ListNode reversedHead = reverseList(head); System.out.print("Reversed List: "); printList(reversedHead); } }
代码解释
-
ListNode类:定义了链表节点类
ListNode
。-
val
表示节点的值。 -
next
表示指向下一个节点的指针。
-
-
reverseList函数:
-
初始化
prev
为null
,current
为head
。 -
在循环中,对于每一个
current
节点:-
暂存
current.next
为next
。 -
将
current.next
指向prev
完成当前节点的指针反转。 -
更新
prev
和current
,移动到下一个节点。
-
-
循环结束后,返回
prev
作为新的头节点。
-
-
printList函数:用于打印链表,用于验证结果。
-
main函数:
-
创建一个测试链表
1 -> 2 -> 3 -> 4 -> 5
。 -
打印原始链表。
-
调用
reverseList
反转链表。 -
打印反转后的链表。
-
递归方式的反转链表
让我们更详细地解释一下递归方式如何反转链表。递归的思想是通过不断调用自身来解决问题,每次递归调用处理链表的一部分,逐步解决问题,直到达到终止条件。
递归反转链表的过程
假设我们有一个链表 1 -> 2 -> 3 -> 4 -> 5
,我们希望将其反转为 5 -> 4 -> 3 -> 2 -> 1
。
递归方法可以分为以下几个步骤:
-
终止条件:当链表为空(
head == null
)或只有一个节点时(head.next == null
),直接返回头节点。这是链表反转的基本情况。 -
递归处理剩余部分:对链表的其余部分进行递归调用,使其反转。例如,当前链表
1 -> 2 -> 3 -> 4 -> 5
中,递归处理2 -> 3 -> 4 -> 5
部分。 -
调整节点指向:将当前节点的
next
节点(即反转后的链表的尾部)指向当前节点,并将当前节点的next
设为null
,断开指向。
代码实现
以下是反转链表的递归代码实现及详细注释:
class ListNode { int val; ListNode next; ListNode(int val) { this.val = val; } } public class ReverseLinkedListRecursive { // 递归反转链表 public static ListNode reverseListRecursive(ListNode head) { // 终止条件:链表为空或只有一个节点时,直接返回头节点 if (head == null || head.next == null) { return head; } // 递归处理剩余链表 ListNode newHead = reverseListRecursive(head.next); // 当递归返回时,处理当前节点和其下一个节点的连接关系 head.next.next = head; head.next = null; // 返回新的头节点 return newHead; } // 打印链表 public static void printList(ListNode head) { ListNode current = head; while (current != null) { System.out.print(current.val + " "); current = current.next; } System.out.println(); } public static void main(String[] args) { // 创建测试链表:1 -> 2 -> 3 -> 4 -> 5 ListNode head = new ListNode(1); head.next = new ListNode(2); head.next.next = new ListNode(3); head.next.next.next = new ListNode(4); head.next.next.next.next = new ListNode(5); System.out.print("Original List: "); printList(head); // 反转链表 ListNode reversedHead = reverseListRecursive(head); System.out.print("Reversed List: "); printList(reversedHead); } }
代码详解
-
基础节点结构:
class ListNode { int val; ListNode next; ListNode(int val) { this.val = val; } }
ListNode
类定义了链表节点。 -
递归反转链表的主函数:
public static ListNode reverseListRecursive(ListNode head) { if (head == null || head.next == null) { return head; } ListNode newHead = reverseListRecursive(head.next); head.next.next = head; head.next = null; return newHead; }
-
终止条件:
if (head == null || head.next == null) { return head; }
如果链表为空或只有一个节点,将直接返回头节点,这是递归终止条件。
-
递归调用:
ListNode newHead = reverseListRecursive(head.next);
处理当前节点的后续节点的反转。
-
调整节点链接:
head.next.next = head; head.next = null;
将当前节点的下一个节点的
next
指向当前节点,将当前节点的next
设为null
,以断开当前节点原来的指向,防止形成环。 -
返回新头节点:
return newHead;
最后返回新头节点,即反转后的最末节点。
-
例子解析
-
第一次进入递归:
-
链表:
1 -> 2 -> 3 -> 4 -> 5
-
处理
2 -> 3 -> 4 -> 5
-
-
第二次进入递归:
-
链表:
2 -> 3 -> 4 -> 5
-
处理
3 -> 4 -> 5
-
-
第三次进入递归:
-
链表:
3 -> 4 -> 5
-
处理
4 -> 5
-
-
第四次进入递归:
-
链表:
4 -> 5
-
处理
5
-
-
终止条件:
-
链表:
5
-
返回
5
作为新头节点
-
-
开始回溯和链接调整:
-
处理第四层返回:
4.next.next = 4
,即5 -> 4
,然后4.next = null
-
处理第三层返回:
3.next.next = 3
,即4 -> 3
,然后3.next = null
-
处理第二层返回:
2.next.next = 2
,即3 -> 2
,然后2.next = null
-
处理第一层返回:
1.next.next = 1
,即2 -> 1
,然后1.next = null
-
最终返回新的头节点5
,得到反转后的链表:5 -> 4 -> 3 -> 2 -> 1
。
希望这个详细解释能帮助你理解递归反转链表的过程和实现!如果还有任何问题,请随时提问。