前言
本文将用两种方式来解题
- 迭代法(依次改变指针指向)
- 递归法(递推公式的作用是 将拿到的链表反转 并返回新的头节点)
一、题目描述
题目地址 | 代码地址
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
输入: head = [1,2,3,4,5]
输出: [5,4,3,2,1]
二、解题
2.1 迭代法
2.1.1 迭代思路:
- 迭代,依次改变每个节点的指向
- 改变节点指向时,如何做到后续节点不丢失?声明变量保存即可
- 什么时候迭代结束?尾节点
- 边界处理-什么样的链表不需要反转?空链表和只有一个节点的链表
2.1.2 迭代分解图-根据代码划分
未进入循环
反转后的头newHead + 待改节点cur
进入循环第一行代码
保存后续节点,以防丢失
进入循环第二行代码
改变当前节点指向,此时不用担心后续节点丢失,因为第一行代码已经保存
进入循环第三行代码
反转后的头节点已经改变,newHead应该指向新的头节点
进入循环第四行代码
上面其实已经完成一次节点的指向改变了,我们要处理下一个节点了
2.2.3 迭代法的代码
let reverseList = function (head) {
// 边界处理 --- 如果链表为空,或者链表只有一个节点,则不需要反转,直接返回即可
if(head === null || head.next === null) return head;
// 存储反转后的头节点
let newHead = null;
// 当前 待改变指向 节点
let cur = head;
while(cur) {
// 用来保存当前节点的下一个节点,目的保证后续节点不丢失
const curNext = cur.next;
// 改变当前节点的指向, 指向反转后的头节点
cur.next = newHead;
// 反转后的头节点已经变成cur了
newHead = cur;
// 处理后当前节点,现在要处理后面一个节点了
cur = curNext;
}
// 整个循环结束,返回反转后的头节点
return newHead;
}
2.2 递归法
2.2.1 递归思路
- 首先明白递推公式的作用:把已拿到的链表反转,并返回反转后链表的头节点
- 递归结束的条件:到了尾节点
2.2.2 递归图解
递推公式
let newHead = reverseList(head.next)
在反转后的链表后新增一个节点,这里新增的就是1节点
head.next.next = head;
处理循环引用
head.next = null;
2.2.3 递归代码
let reverseList = function (head) {
// 递归结束条件是到了尾节点
if(head === null || head.next === null) return head;
// 递推公式 - 将拿到的节点反转,并返回新的头节点;
let newHead = reverseList(head.next);
// 这行代码实际是改变反转后的链表尾节点的指向,指向当前的head
head.next.next = head;
// 当前 head 指向 null,
head.next = null;
return newHead;
}