题意描述:
反转一个单链表。
进阶: 你可以迭代或递归地反转链表。你能否用两种方法解决这道题?
示例:
示例一:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
解题思路:
Alice: 我记得你在PAT那套题目里面的反转链表用了一种比较奇怪的方法写的。没有修改链表的指向关系,对吗 ?
Bob: 你这么一说我倒是想起来了,我是先把链表中所有的值都抠了下来,然后反转存储着这些值的列表,最后修改整个链表中节点的值,而不是节点之间的指向关系。
Alice: 这样倒是很好理解,就是空间复杂度有点高,需要先把所有的值都存一遍,然后再反转这些值的。
Bob: 可以直接遍历,修改指向关系的。 取三个变量吧,pre, node, after, 用 after 遍历整个链表, 每次循环修改 pre 和 node 之间的指向关系。然后 pre = node, node = after, after = after.next , 最后再修改 链表头部和尾部的指向关系,返回原来的链表尾结点就好了。
Alice: 现在想的倒是听明白的嘛,当初怎么没有想出来呢 ?题目中不是说还有进阶的做法可以用递归实现吗?
Bob: 我。。。┓( ´∀` )┏ 还是看题解吧。
------------------------------------------------------------------------------- Alice & Bob -------------------------------------------------------------------------------
Alice: 递归就是每次调用只修改两个节点之间的指向关系,然后要注意将原来的指向关系去掉,最后注意递归的终止条件应该是遇到原始链表的尾结点。别的应该就没了,看过别人的答案才觉得自己应该也能写出来的。
Bob: hhh, 我还看到了迭代解法的更好的版本,逻辑都是一模一样的,只是在代码实现上稍有差别,原来还可以写的更简洁一些。
Alice: 是哟,别人把两个 if 语句都拿掉了哦,应该会运行的更快吧。emmm,只需要 prev
和 curr
两个节点,每次修改 curr
和 prev
之间的指向关系。 prev
初始是 null
, curr
初始是头结点,最后curr
指向null
, prev
指向原始链表的尾节点,返回 prev
。这样 就能 handle 空链表和 只有一个节点的情况了,妙啊!!
Bob:
代码:
Python 方法一: 存储链表中的值到列表,然后反转链表,不修改链表指向关系,修改链表总的值。空间复杂度O(n),时间复杂度O(n),好处就是简单,容易写。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def reverseList(self, head: ListNode) -> ListNode:
data = []
node = head
while node != None:
data.append(node.val)
node = node.next
data.reverse()
node = head
index = 0
while node != None:
node.val = data[index]
index += 1
node = node.next
return head
Python 方法二: 遍历整个链表, 修改节点之间的指向关系。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def reverseList(self, head: ListNode) -> ListNode:
if head == None:
# 空链表
return None
if head.next == None:
# 仅有一个元素的链表
return head
# if head.next.next == None:
# # 只有两个元素的链表
# ans = head.next
# head.next.next = head
# head.next = None
# return ans
# 这一段可以和下面的代码合并
pre = head
node = head.next
after = head.next.next
# 初始化 pre node after 三个遍历
while after != None:
# 每次循环,修改 pre 和 node 之间的指向关系。
node.next = pre
pre = node
node = after
after = after.next
# 实际上是用 after 在遍历整个链表
node.next = pre
head.next = None
# 搞定链表末尾 和 头部的指向关系
return node
Java 方法二 : 遍历整个链表,修改节点之间的指向关系。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode reverseList(ListNode head) {
if(head == null){
return null;
}
if(head.next == null){
return head;
}
ListNode pre = head;
ListNode node = head.next;
ListNode after = head.next.next;
while (after != null){
node.next = pre;
pre = node;
node = after;
after = after.next;
}
node.next = pre;
head.next = null;
return node;
}
}
Java 方法二: 遍历链表,修改指向关系。标准答案。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode reverseList(ListNode head) {
ListNode prve = null;
ListNode curr = head;
// 每次遍历两个节点,修改 prve 和 curr 之间的指向关系。
while (curr != null){
ListNode next = curr.next;
// 预先保留下一个节点
curr.next = prve;
prve = curr;
curr = next;
}
return prve;
}
}
Java 方法三:递归修改节点之间的指向关系。每次递归修改 head.next 和 head 之间的指向关系,同时要去除掉 head.next 和 head 之间最原始的指向关系,防止形成环形链表。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode reverseList(ListNode head) {
if(head == null){
// 处理边界值
return null;
}
if(head.next == null){
return head;
// 遇到原始链表的尾部
}else{
ListNode end = reverseList(head.next);
// 反转 以 head.next为头结点的链表
// end 在递归栈中被接力传递,它的值就是 原始链表的尾结点
head.next.next = head;
// 修改 head 和 head.next 之间的指向关系。
head.next = null;
// 两个节点之间不能互相指着对方,不然就是环形链表了。
return end;
}
}
}
易错点:
[1,2,3,4,5]
[1]
[1, 2]
[]
[5,4,3,2,1]
[1]
[2,1]
[]
总结: