题目:
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
示例 1:
输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]
示例 2:
输入:head = [1,2]
输出:[2,1]
示例 3:
输入:head = []
输出:[]
(一)经典迭代
用while循环遍历列表,将每一个节点的指向改为前一个节点,把最后一个节点当作一个新的头节点返回。在遍历的过程中,由于当前节点的指向改变,我们就无法找到下一个节点。因此我们需要提前储存下一个节点。
详细代码及动态图:
public class Solution {
public ListNode ReverseList(ListNode head) {
//定义cur节点代表当前需要反转的节点,cur一开始处于头节点的位置
ListNode cur=head;
//定义prev为反转节点的前一个节点,即左边的节点,初始化为null
ListNode prev=null;
//循环条件为cur节点走完全部的节点
while(cur!=null){
//定义一个curNext节点,永远指向cur的下一个节点,防止丢失
ListNode curNext=cur.next;
//反转关键:将当前节点的指向改为前一个节点
cur.next=prev;
//prev向后到cur位置
prev=cur;
//cur向后到下一个需要反转的节点
cur=curNext;
}
//prev最后所在的位置就是新的头节点。
return prev;
}
}
(二)用栈解决
栈的特点是先进后出。
反转链表可以利用这个特点,正向依次放入节点,最后依次弹出节点。
public class Solution {
public ListNode ReverseList(ListNode head) {
Stack<ListNode> stack=new Stack<>();
//遍历列表,依次放入栈中
while(head!=null){
stack.push(head);
head=head.next;
}
//如果是空链表,返回null
if(stack.isEmpty()) return null;
//node节点代表正在存放的节点
ListNode node=stack.pop();
//dummy作为新链表的头节点
ListNode dummy=node;
while(!stack.isEmpty()){
//定义一个tmpNode节点暂时接受下一个弹出的节点
ListNode tmpNode=stack.pop();
//让node节点指向下一个节点
node.next=tmpNode;
//更新node节点
node=node.next;
}
//置空最后一个节点的next,否则最后一个节点和倒数第二个节点会构成环
node.next=null;
return dummy;
}
}
(三)双链表求解
创建一个新的链表,每次把原链表的头节点摘掉,放到新链表里当作头节点。过程如下图:
详细代码及解释:
public class Solution {
public ListNode ReverseList(ListNode head) {
//新链表头节点
ListNode newHead=null;
while(head!=null){
//定义tmp暂时储存head的下一个节点
ListNode tmp=head.next;
//将head的指向改为新链表的头节点
//就是将新链表挂到挂到head的后面
head.next=newHead;
//更新头节点
newHead=head;
//此时旧链表头节点的下一个节点成为头节点
head=tmp;
}
return newHead;
}
}
(四)递归求解
递归解法最关键的一点是 :
每次递归后的返回都是将后序链表反转并返回头节点的引用。
详细过程如下:
代码如下:
class Solution {
public ListNode reverseList(ListNode head) {
//递归的终止条件
if (head == null || head.next == null) {
return head;
}
//新的头节点
ListNode newHead = reverseList(head.next);
//将当前需要反转的节点的下一个节点的引用指向自己
head.next.next = head;
//防止成环
head.next = null;
return newHead;
}
}