对于链表反转其核心是通过将单链表原有的指针方向进行反转;
可以通过双指针,也可以通过递归两种方式实现反转
双指针
因此可以通过双指针迭代来实现,通过双指针每递进一次则执行一次反转操作,当迭代完成后,就完成了全部节点的指针方向反转;
对于每次方向反转重点在于通过 维护前一个指针指向的节点实现反转的操作,并通过维护下一个指针指向的节点实现遍历的操作;
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode reverseList(ListNode head) {
ListNode first = null;
ListNode second = head;
while(second != null){
// 记录当前存在原始关联关系的节点
ListNode temp = second.next;
// 将当前second指针指向的节点的next指针指向前一个指针first
second.next = first;
//将first指针往后移动一位
first = second;
//将second 指针往后移动一位
second = temp;
}
// 当执行到这里说明当前链表已遍历结束
return first;
}
}
其具体的步骤为
1: first = null; second = head;初始化维护 first 和 second 两个指针
2:temp = second.next; 将second 节点指向的next节点数据使用临时变量进行缓存
3:second.next = first; 将原有的 second.next 指向当前second的前一个节点first
4:first = second; second = temp;同时将first和second 进行后移一位,对于first之前的节点指向方向已完全反转,对于second之后的节点代表的是等待反转的原数据;
5:并直到second 指向为null 就表示当前链表遍历结束,且指针方向反转也已结束;
递归
递归实际是利用到了 栈的特性,先进后出,通过先压栈,再出栈操作实现链表从尾到头的遍历操作
/**
* 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 ret = reverseList(head.next);
// 当执行到该处时说明此时的ret节点为最后一个节点,而head为ret前一个节点
// 此时需要从最后一个节点位置开始执行指针反转操作
// 当前操作目的 为 head.next 表示当前head节点的next节点; head.next.next = head,表示将当前head.next节点的next指针指向当前head节点;
head.next.next = head;
// 断开原有的head.next指针指向关系
head.next = null;
// 这里不能直接使用 ret.next修改的原因为,对于使用递归操作时压入栈中的是head节点
// 对于递归真正的执行过程实际就是出栈的过程,实际就是链表反向遍历的过程
// 对于每一次出栈过程实际就是head 及其 next节点 关联关系反转的过程
// 利用栈实际上就是间接维护了head->prev 的关系,对于出栈实际就是 head = head->prev
// 关于递归实际就是维护了一个head指针,通过压栈操作顺序遍历,通过出栈操作实现反向遍历
return ret;
}
}
对于递归中的ret 变量只需要将其当做结果变量即可,真正作为指针移动的实际是head
通过出栈操作间接实现了前指针的虚拟关系(head->prev 实际就是当前在栈中存储下一个出栈节点)
//TODO : 示意图补充, 关于递归 入栈操作以及出栈操作时指针反转