题目链接
题意
中文题,反转链表,给出链表头,要求输出一个链表头,oj会遍历以判定是否反转。
题解
简单的方法是迭代,然后反向输出。或者说利用额外空间储存遍历链表的数据,然后在构造一个链表头,反向输出额外空间储存数据来创建一个新链表。但是链表的遍历本来就是利用指针指向一层一层向下遍历,并且是反向输出,很自然能想到利用递归的方法。在回溯的时候输出就可以了。但是这不是ACM,可以看到方法的返回值是一个ListNode对象,也就是要求构造出一个链表,并返回链表头。如果在回溯的时候构造很麻烦,因为还得回溯下一个引用指向的对象。所以我们可以在原来的链表上进行修改。
这里先上一下代码,下面是图解:
public ListNode reverseList(ListNode head) {
if(head == null || head.next == null){
return head;
}
ListNode next = head.next;
head.next = null;
ListNode ret = reverseList(next);
next.next = head;
return ret;
}
那么首先是递归过程:
可以看到,在递归的时候,ret在等待回溯结果,等到第三层递归,返回最后一个节点,之前先声明每个节点的next是null。因为反转以后最后一个节点的next必须是null。
接下来是回溯的过程:
可以看到,回溯过程实际上每次向上传递的永远都是最后一个节点,那么最后返回的结果就是最后一个节点了,而这也是反转链表的head。那么除了这个过程,可以看到主要做了3件事:
1.当前节点.next = null
2.下层节点.next = 当前节点
第一步的作用是防止最后一个节点的next不是null,而第二步的作用实际上是翻转链表。因为递归的时候已经遍历过的节点就可以删除联系了,因为没有用了。最终层层向上回溯的只是最后一个节点。但是在递归的时候从第一个节点开始已经完成了翻转,第一个节点的next也变成了null。这样,就能在不额外大量开辟空间(实际上是多了next这个对象)的情况下进行反转操作。
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 || head.next == null){
return head;
}
ListNode next = head.next;
head.next = null;
ListNode ret = reverseList(next);
next.next = head;
return ret;
}
}