单链表反转的方法大致有两种:双指针法和递归法。
双指针法比较好理解就是一步步往前挪修改next的指向,递归法看了一晚上没明白代码的逻辑思路。
不过现在终于理解了,这里就记录下心得。
贴下递归实现代码:
1 class Solution { 2 public ListNode reverseList(ListNode head) { 3 if(head==null || head.next==null) { 4 return head; 5 } 6 ListNode cur = reverseList(head.next); 7 head.next.next = head; 8 head.next = null; 9 return cur; 10 } 11 }
使用LeetCode的在线调试功能,加上日志输出。
执行结果如下:
接下来分段解释下:
if(head==null || head.next==null) { return head; }
最开始的if判断很好理解,当传入的节点是空或者它的下一个节点指针为空时就直接返回它自己,表示它已经是最后一个元素了。
ListNode cur = reverseList(head.next);
接着是递归调用自己,传入head下一个节点的指针。
可以看上面执行结果的截图,右边日志输入可以看到执行过程是从头节点传值进来后连续递归调用了4次reverseList方法,然后再走的后面逻辑,后面的逻辑执行顺序是先从最里面的递归开始一步步往外走的,那就是reverseList(4)=>reverseList(3)=>reverseList(2)=>reverseList(1)的顺序执行。
再看reverseList方法执行后后面的代码
head.next.next = head; head.next = null; return cur;
倒着分析下,return cur很好理解,看上面的执行结果截图可以看到每次递归后返回的cur链表都会倒着多加一个节点,一直到最后变成一个完整的反转链表。
head.next = null; 这一句的作用是每次递归后如果不切断head节点的next指向会造成死循环(因为递归中执行顺序是从内到外的,所以只要保证当前节点的上一个节点的next指向的是当前节点就可以了)。
head.next.next = head; 这一句是将本次head的上级节点的next指针指向head自己。
推导下整个执行过程就是:
//head=4 4.next.next=4; //5.next=4 4.next=null; return cur; //listNodeToString(cur)=>[5,4] //head=3 3.next.next=3; //4.next=3 3.next=null; return cur; //[5,4,3] //head=2 2.next.next=2; //3.next=2 2.next=null; return cur; //[5,4,3,2] //head=1 1.next.next=1; //2.next=1 1.next=null; return cur; //[5,4,3,2,1]