题目链接:反转链表
这是一道很经典的题,因为在许多其他的链表题中都需要使用(单独实现),所以我单独整理出来,供大家参考,互相学习。
1.迭代法---三指针(双指针)
完成迭代需要三个指针
prev:已反转的链表的头部
cur:未反转链表的头部
curtmp:未反转链表头部的下一个节点
注意:这里的curtmp可以作为临时变量,但为了便于演示就设为变量
举例:这里有四个节点需要反转,我们为其增加到五个
相信大家可以看出prev的作用,就是不用再单独为反转的链表末尾置为null,并且不用考虑反转的链表很少的情况。
接下来说做法:
第一步:将cur指向prev
执行cur.next = prev;
第二步:进行迭代操作,将各个节点向后移动
prev = cur;
cur = curtmp;
curtmp = curtmp.next;
然后重复上述操作(这里就直接上图到最后看结束条件是什么)
注意到这步时不要直接结束,等执行cur.next = prev再结束
此时结束,cur就正好指向反转后链表的头部
while(true) {//反转链表
cur.next = prev;
prev = cur;
if(curtmp == null) {
break;
}
cur = curtmp;
curtmp = curtmp.next;
}
当然前面对curtmp赋值时肯定要判断
2.递归
递归就是解决相同的子问题,这个两步一看就是处理了相同的问题,我们自然就想到用递归解决。
上面说curtmp可以是一个临时变量,那么我们就可以在递归中创建从而减少变量的传递。
那么递归函数的参数就可以设置为两个:prev,cur,意义与迭代相同
因为要返回头节点,所以我们最好把返回值设置为ListNode
class Solution {
public ListNode reverseList(ListNode head) {
if(head == null) {
return null;
}
return recur(head, null); // 调用递归并返回
}
private ListNode recur(ListNode cur, ListNode prev) {
ListNode tmp = cur.next;
cur.next = prev;
prev = cur;
if(tmp == null) {
return cur;
}
cur = tmp;
return recur(cur, prev);
}
}
递归终止条件显然是这种情况
此时在cur反转后判断tmp == null作为递归终止条件
这个是正着向后递归的,所以个人感觉是很好理解的。
下面是参看力扣-Krahets的代码,他是采用从后往前递归的,我还看了好一会,个人感觉不太好理解(也可能是我太笨了)
class Solution {
public ListNode reverseList(ListNode head) {
return recur(head, null); // 调用递归并返回
}
private ListNode recur(ListNode cur, ListNode pre) {
if (cur == null) return pre; // 终止条件
ListNode res = recur(cur.next, cur); // 递归后继节点
cur.next = pre; // 修改节点引用指向
return res; // 返回反转链表的头节点
}
}
他的递归展开第一层就变成从最后一个节点开始,反着向回完成递归
执行cur.next = pre;
递归回到:
并且res没改变过,返回的就是头节点
那么以上就是全部题解了,欢迎大家补充更多解题思路,如有问题也欢迎大家指正!