思路一:头插法
维护一个新的空链表,新建一个空节点,指向当前链表头,不断从当前链表将节点摘下,并插入新链表头部
代码如下:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode reverseList(ListNode head) {
if(head==null || head.next==null)
return head;
ListNode newhead=null;
ListNode node=null;
while(head!=null)
{
node=head;
head=head.next;//节点断开
node.next=newhead;//节点插在新链表头部
newhead=node;//新链表头变更为当前插入节点
}
return newhead;
}
}
空间复杂度O(n)新链表空间
时间复杂度O(n)
思路二:双指针,原地翻转
思路:创建一个前指针pre指向当前节点的前一节点,初始化为null,创建一个当前之前cur指向当前节点,链表向后移动,将当前节点断开,当前节点指向前一节点,更改链表头pre
class Solution {
public ListNode reverseList(ListNode head) {
if(head==null || head.next==null)
return head;
ListNode pre=null;
ListNode cur=head;
while(cur!=null)
{
head=head.next;//这一步顺序不能错,要不然链表就断了
cur.next=pre;
pre=cur;
cur=head;
}
return pre;
}
}
方式三:递归
为什么可以使用递归?
1、大问题可以拆解成两个子问题
除了head节点,剩余的节点完成翻转
除了head节点,剩余的节点完成翻转(子链表的head节点)
除了head节点,剩余的节点完成翻转(子链表的head节点)
除了head节点,剩余的节点完成翻转(子链表的head节点)
............
剩下一个节点不需要翻转
2、子问题求解方式和大问题一样
3、存在终止条件(最小子问题)
调用递归函数前是递做的事,调用递归函数后是归做的事
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode reverseList(ListNode head) {
if(head==null || head.next==null)
return head;
ListNode cur= reverseList(head.next);//head=4,cur=5
head.next.next=head;
head.next=null;
return cur;
}
}