反转链表
题目描述
给定一个单链表的头结点pHead,长度为n,反转该链表后,返回新链表的表头。
数据范围:1≤n≤1000
要求:空间复杂度 O(1) ,时间复杂度 O(n) 。
如当输入链表{1,2,3}时,
经反转后,原链表变为{3,2,1},所以对应的输出为{3,2,1}。
以上转换过程如下图所示:
示例
示例1
js
输入:{1,2,3}
输出:{3,2,1}
示例2
js
输入:{}
输出:{}
说明:空链表则输出空
代码
代码1 暴力,先转数组,再转链表
- 先遍历链表,将链表转换为数组
- 使用
while
循环,因为是翻转的链表,构造数组时,直接先翻转好,使用unshift
- 使用
- 再通过数组,构建翻转后的链表
- 边界判断,如果数组长度为
0
,直接返回null
- 不为
0
,开始for
循环遍历数组,构建新链表
- 边界判断,如果数组长度为
js
function ReverseList(pHead){
// 1. 先转换为翻转好的数组
let tempArr = []
while(pHead){
tempArr.unshift(pHead.val)
pHead = pHead.next
}
let len = tempArr.length
// 2. 如果数组长度为零,直接返回null
if(len === 0 ) return null
// 3. 使用翻转好的数组开始构造链表
let newHead = new ListNode(tempArr[0]) // 等待返回的新头结点
let prevNode = newHead // 前一个节点,默认先是头节点
// 从第二个节点开始遍历
for(let i = 1; i < len; i++){
let currentNode = new ListNode(tempArr[i])
// 4. 如果是最后一个节点
if(i === len - 1 ) {
prevNode.next = currentNode
currentNode.next = null
}else {
// 5. 如果不是最后一个节点
prevNode.next = currentNode // 上一个节点的next指向把当前遍历的节点
prevNode = prevNode.next
}
}
return newHead
}
代码2 三个指针来回倒
使用三个指针
- prev = null
- current = Phead
- next = null
使用while遍历,遍历时,遍历current
- 先保存下一个节点 next = current.next
- 然后把当前节点的next指向前一个节点(翻转链表的指向) current.next = prev
- 把前一个节点prev指向当前的current,prev = current
- 改变current,开始翻转下一个节点 current = next
js
function ReverseList(pHead){
if(!pHead) return null
let current = pHead
let prev = null
let next = null
while(current){
next = current.next
current.next = prev
prev = current
current = next
}
return prev
}
代码3 递归
- 使用递归函数,一直递归到链表的最后一个结点,该结点就是反转后的头结点
- 此后,每次函数在返回的过程中,让当前结点的下一个结点的 next 指针指向当前节点。
- 同时让当前结点的 next 指针指向NULL ,从而实现从链表尾部开始的局部反转
- 当递归函数全部出栈后,链表反转完成。
js
function ReverseList(pHead){
// 递归的出口,遍历到最后一个节点时,返回
if(pHead === null || pHead.next === null){
return pHead
}
// 每次递归都会保存当前的状态,比如{1,2,3}
// 第一次 1
// 第二次 2
// 第三次 3
// 遍历到3的时候,有返回值了,返回的就是最后一个节点
let vHead = ReverseList(pHead.next)
// 让当前节点下一个节点的next指向当前节点
pHead.next.next = pHead
// 同时让当前结点的 next 指针指向NULL ,从而实现从链表尾部开始的局部反转
pHead.next = null
return vHead
}