本人是刷《代码随想录》这本书时记录一下自己的学习笔记
思路
这道题有三个思路:
1、建立一个新链表,用头插法不断往新链表插入数据;
2、使用原链表,用双指针法;
3、递归;
建议先使用双指针的方法写完,再写递归的方法,会使自己思路更清晰
头插法
定义一个新链表,然后使用头插法不断建立节点
public ListNode reverseList(ListNode head) {
//采用头插法反转
ListNode h = new ListNode(-1);//建立头结点,为了返回新链表
while(head!=null){
ListNode temp = new ListNode(head.val);//建立新链表的节点
temp.next = h.next;
h.next = temp;
head = head.next;
}
return h.next;
}
优点:容易理解
缺点:新建一个链表,浪费内存空间
双指针法
反转链表的本质就是改变链表的next指针的指向
我们定义在链表里定义两个指针,一个是准备改变next节点的指针cur,一个是cur需要指向的节点的指针pre(原链表中cur的前一个节点),然后使cur.next指向pre,这样就达到了反转后链表节点的指向。
举一个例子:
原来的链表1->2 ->null , pre = 1 , cur = 2
改变中: cur.next = pre (2-->1) , 像这样翻转前我们需要再cur.next指针改变前保存cur的下一个节点,下图是《代码随想录》中的示意图
代码:
public ListNode reverseList(ListNode head) {
//双指针,在原有链表上进行操作
//pre cur 使cur的next指针指向pre
//口->口->口->口->口
//口<-口<-口<-口<-口
ListNode pre = null;
ListNode cur = head;
while(cur!=null){
ListNode temp = cur.next;//保存cur的下一个节点,以防链表丢失
cur.next = pre;
pre = cur;
cur = temp;
}
return pre;
}
递归法
从前往后改变指针
递归方法的思路和双指针很像,大概就是把循环的条件变成if,然后不需要手动赋值下一个节点。
从前往后改变指针的意思是,先改变next指针,在向下递归,最后返回原链表的尾结点
public ListNode reverseList(ListNode head) {
//递归写法
return reverse(null,head);
}
public ListNode reverse(ListNode pre,ListNode cur){
if(cur==null){
return pre;
}
ListNode temp = cur.next;
cur.next = pre; //改变next指针的指向
return reverse(cur,temp); //与双指针里的循环很像
}
从后往前改变指针
从后往前递归的意思是,先递归到原链表的尾结点,然后改变其next指针的指向,注意一个的是这个方法的返回值一直都是原链表的尾结点。
public ListNode reverseList(ListNode head) {
// 边缘条件判断
if(head == null) return null;
if (head.next == null) return head;
// 递归调用,翻转第二个节点开始往后的链表
ListNode last = reverseList(head.next);
// 翻转头节点与第二个节点的指向
head.next.next = head;
// 此时的 head 节点为尾节点,next 需要指向 NULL
head.next = null;
return last; //返回的是同一个节点,就是原链表的尾结点
}