反转链表应该就是把每个节点的指针方向反过来,对吧?比如说原本是1->2->3->4->null,反转之后变成4->3->2->1->null。
那怎么操作呢?可能需要用三个指针,或者两个指针。我记得之前学的时候有两种方法,迭代和递归。用户可能想知道这两种方法,不过可能先考虑迭代的方式,因为递归有时候可能不太好理解,或者栈溢出问题。
首先,迭代法的话,可能需要一个前驱节点prev,当前节点current,以及下一个节点next。每次把current的next指向prev,然后把prev和current往后移。比如,初始时prev是null,current是head。然后循环,保存current的下一个节点next,把current的next指向prev,然后prev移动到current,current移动到next。直到current为null,这时候prev就是新的头节点。
那这样的话,代码应该怎么写呢?比如在Java里,定义一个链表节点类,然后写反转的方法。比如:
public 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;
}
这个应该是对的。那测试一下,比如例子1->2->3->4。初始prev是null,current是1。进入循环,next是2,current.next指向null,prev变成1,current变成2。下一次循环,next是3,current.next指向1,prev变成2,current变成3。依此类推,最后prev就是4,链表反转成功。
然后递归的方法呢?递归的话,可能要从最后一个节点开始反转。比如递归到最后一个节点,然后每次将当前节点的下一个节点的next指向自己,自己的next置为null。比如:
public 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;
}
这样的话,假设原链表是1->2->3->4。递归到head=3的时候,head.next是4,递归返回4,这时候head.next.next = head,即4的next指向3,然后3的next设为null。然后回到上一层,处理2,这时候2的next是3,反转后3的next指向2,然后2的next设为null。最后处理1,同样的步骤,得到4->3->2->1。这样递归的方法也能实现。
用户可能想知道这两种方法的区别,或者哪一种更优。迭代法是O(n)时间,O(1)空间,而递归是O(n)时间,O(n)栈空间。所以如果链表很长的话,递归可能会栈溢出,这时候应该用迭代法更好。
另外,可能需要处理链表为空的情况,或者只有一个节点的情况,这时候直接返回头节点就可以了。比如在递归的终止条件里,如果head或者head.next是null,就返回head。这样是正确的。
用户有没有其他需求呢?比如是否需要解释每一步的过程,或者是否需要图示?或者是否需要处理循环链表的情况?不过题目应该假设链表是单向无环的。所以按照常规情况处理即可。
总结来说,反转链表的两种方法,迭代和递归,都是可行的,需要根据具体情况选择。代码实现起来也不算复杂,关键是要正确调整指针的指向,同时注意不要丢失节点的引用。
以下是反转链表的详细步骤和代码实现:
方法一:迭代法
思路:使用三个指针(前驱节点 prev
、当前节点 curr
、临时节点 next
)逐个反转节点指向。
步骤:
初始化
prev
为null
,curr
为头节点head
。遍历链表,每次循环中:
保存
curr
的下一个节点到next
。将
curr
的next
指向prev
。prev
和curr
向后移动一位。
当遍历完成时,
prev
即为新链表的头节点。
代码实现(Java):
java
复制
class ListNode { int val; ListNode next; ListNode(int x) { val = x; } } public class Solution { public ListNode reverseList(ListNode head) { ListNode prev = null; ListNode curr = head; while (curr != null) { ListNode next = curr.next; // 临时保存下一个节点 curr.next = prev; // 反转指针方向 prev = curr; // prev 后移 curr = next; // curr 后移 } return prev; // prev 最终指向新头节点 } }
方法二:递归法
思路:递归到链表末端,从后往前逐个反转节点指向。
步骤:
递归终止条件:当前节点为空或为最后一个节点,直接返回。
递归反转后续链表,得到新头节点
newHead
。将当前节点的下一节点的
next
指向当前节点,再将当前节点的next
置空。最终返回新头节点
newHead
。
代码实现(Java):
java
复制
public class Solution { public 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; // 返回新头节点 } }
示例演示
原始链表:1 -> 2 -> 3 -> 4 -> null
反转过程(迭代法):
初始状态:
prev = null
,curr = 1
第一次循环:
curr.next
指向null
,链表变为1 -> null
,prev = 1
,curr = 2
第二次循环:
curr.next
指向1
,链表变为2 -> 1 -> null
,prev = 2
,curr = 3
第三次循环:
curr.next
指向2
,链表变为3 -> 2 -> 1 -> null
,prev = 3
,curr = 4
第四次循环:
curr.next
指向3
,链表变为4 -> 3 -> 2 -> 1 -> null
,prev = 4
,curr = null
最终链表:4 -> 3 -> 2 -> 1 -> null
复杂度分析
时间复杂度:
O(n)
,需遍历所有节点。空间复杂度:
迭代法:
O(1)
,仅使用常量额外空间。递归法:
O(n)
,递归调用栈深度为链表长度。
方法对比
方法 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
迭代法 | 空间效率高(O(1)) | 代码稍复杂 | 常规情况,长链表 |
递归法 | 代码简洁 | 栈空间开销(O(n)) | 短链表,教学示例 |
根据实际需求选择合适方法即可。
可以用动图解释一下,迭代法吗
迭代法反转链表的动图演示
初始状态
复制
链表:1 -> 2 -> 3 -> 4 -> null 指针: - prev = null - curr = 1
第一次循环
保存
curr.next
到next
:复制
next = 2
反转
curr.next
指向prev
:复制
1 -> null
移动
prev
和curr
:复制
prev = 1 curr = 2
状态:
复制
链表:1 -> null 部分反转链表:1 -> null 剩余链表:2 -> 3 -> 4 -> null
第二次循环
保存
curr.next
到next
:复制
next = 3
反转
curr.next
指向prev
:复制
2 -> 1 -> null
移动
prev
和curr
:复制
prev = 2 curr = 3
状态:
复制
链表:2 -> 1 -> null 部分反转链表:2 -> 1 -> null 剩余链表:3 -> 4 -> null
第三次循环
保存
curr.next
到next
:复制
next = 4
反转
curr.next
指向prev
:复制
3 -> 2 -> 1 -> null
移动
prev
和curr
:复制
prev = 3 curr = 4
状态:
复制
链表:3 -> 2 -> 1 -> null 部分反转链表:3 -> 2 -> 1 -> null 剩余链表:4 -> null
第四次循环
保存
curr.next
到next
:复制
next = null
反转
curr.next
指向prev
:复制
4 -> 3 -> 2 -> 1 -> null
移动
prev
和curr
:复制
prev = 4 curr = null
状态:
复制
链表:4 -> 3 -> 2 -> 1 -> null 部分反转链表:4 -> 3 -> 2 -> 1 -> null 剩余链表:null
结束
curr
为null
,循环结束。prev
指向新链表的头节点4
。
动图演示
以下是迭代法反转链表的动图演示:
复制
初始链表: 1 -> 2 -> 3 -> 4 -> null 第一次循环: 1 -> null 2 -> 3 -> 4 -> null 第二次循环: 2 -> 1 -> null 3 -> 4 -> null 第三次循环: 3 -> 2 -> 1 -> null 4 -> null 第四次循环: 4 -> 3 -> 2 -> 1 -> null null 最终链表: 4 -> 3 -> 2 -> 1 -> null
代码回顾
以下是迭代法的代码实现:
java
复制
public ListNode reverseList(ListNode head) { ListNode prev = null; ListNode curr = head; while (curr != null) { ListNode next = curr.next; // 保存下一个节点 curr.next = prev; // 反转指针 prev = curr; // 移动 prev curr = next; // 移动 curr } return prev; // prev 最终指向新头节点 }
总结
迭代法通过三个指针(
prev
、curr
、next
)逐个反转链表节点。每次循环中,反转
curr
的指向,并移动prev
和curr
。最终
prev
指向新链表的头节点。
通过动图和代码结合,可以更直观地理解迭代法反转链表的过程!
解释一下 curr.next = prev; // 反转指针
反转链表的迭代法中,curr.next = prev
是核心操作,它的作用是 反转当前节点的指针方向。下面详细解释这一操作的含义和作用。
1. 代码片段回顾
java
复制
while (curr != null) { ListNode next = curr.next; // 保存下一个节点 curr.next = prev; // 反转指针 prev = curr; // 移动 prev curr = next; // 移动 curr }
2. curr.next = prev
的作用
2.1 反转指针方向
原始链表:每个节点的
next
指针指向下一个节点。复制
1 -> 2 -> 3 -> 4 -> null
反转操作:将当前节点
curr
的next
指针指向prev
,即前一个节点。复制
curr.next = prev;
例如,当
curr
指向2
时,prev
指向1
,执行curr.next = prev
后:复制
2 -> 1
2.2 断开原链表
反转指针后,当前节点
curr
不再指向原来的下一个节点,而是指向前一个节点prev
。这样,原链表被逐步断开,新链表逐步形成。
3. 具体步骤解析
初始状态
复制
链表:1 -> 2 -> 3 -> 4 -> null 指针: - prev = null - curr = 1
第一次循环
保存
curr.next
到next
:复制
next = 2
反转
curr.next
指向prev
:复制
curr.next = null
链表变为:
复制
1 -> null
移动
prev
和curr
:复制
prev = 1 curr = 2
第二次循环
保存
curr.next
到next
:复制
next = 3
反转
curr.next
指向prev
:复制
curr.next = 1
链表变为:
复制
2 -> 1 -> null
移动
prev
和curr
:复制
prev = 2 curr = 3
第三次循环
保存
curr.next
到next
:复制
next = 4
反转
curr.next
指向prev
:复制
curr.next = 2
链表变为:
复制
3 -> 2 -> 1 -> null
移动
prev
和curr
:复制
prev = 3 curr = 4
第四次循环
保存
curr.next
到next
:复制
next = null
反转
curr.next
指向prev
:复制
curr.next = 3
链表变为:
复制
4 -> 3 -> 2 -> 1 -> null
移动
prev
和curr
:复制
prev = 4 curr = null
4. 动图演示
以下是 curr.next = prev
的动图演示:
复制
初始链表: 1 -> 2 -> 3 -> 4 -> null 第一次循环: 1 -> null 2 -> 3 -> 4 -> null 第二次循环: 2 -> 1 -> null 3 -> 4 -> null 第三次循环: 3 -> 2 -> 1 -> null 4 -> null 第四次循环: 4 -> 3 -> 2 -> 1 -> null null
5. 总结
curr.next = prev
是反转链表的核心操作,它将当前节点的next
指针指向前一个节点prev
。通过这一操作,链表的方向被逐步反转,最终形成新的反转链表。
结合
prev
和curr
的移动,整个链表被逐个节点反转。