LeetCode92——反转链表 II
2022年2月5日 · 365 字 · 2 分钟
题目
给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 。
示例 1:
输入:head = [1,2,3,4,5], left = 2, right = 4 输出:[1,4,3,2,5]
示例 2:
输入:head = [5], left = 1, right = 1 输出:[5]
来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/reverse-linked-list-ii 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
转数组
大部分的链表题只要未要求原节点上操作,都可以转数组处理,缺点是空间复杂度会额外增加达到O(n)。
- 将原链表按顺序转为数组
- 遍历数组,翻转left ~ right之间的数组,此处用双指针即可
- 定义left和right指针,交换left和right的值
- 两个指针同时向中间移动,left++,right–
- 将数组构造为链表返回
// 数组法
// 1. 链表转换为数组
// 2. 翻转left ~ right的数据
// 3. 重新构造链表
func reverseBetween(head *ListNode, left int, right int) *ListNode {
array := convertListToArray(head)
reverseArrayPart(array, left-1, right-1)
return buildLinkList(array)
}
// 翻转数组指定区间
func reverseArrayPart(array []int, left, right int) {
for left < right {
array[left], array[right] = array[right], array[left]
left++
right--
}
}
// 链表构造为数组
func convertListToArray(head *ListNode) []int {
array := make([]int, 0)
for head != nil {
array = append(array, head.Val)
head = head.Next
}
return array
}
链表操作
链表相关的操作主要是要掌握定位任意节点的指针。
- 定位以下节点
- leftPrev: 翻转区间开始节点的上一个节点,最终要连接翻转后的头节点,所以需要保留
- rightNext: 翻转区间结束节点的后一个节点,最终要连接到最终链表,所以需要保留
- left和right节点,翻转开始和翻转结束节点
- 定义子函数翻转left和right,返回right,翻转思路如下
- 定义prev和curr指针,prev初始指向null,curr指向head
- 开始迭代,迭代结束条件curr不等于rightNode
- 保存curr的下一个节点
next:=curr.Next
- 此时我们有3个节点的指针,prev,curr,next
- 当前节点指向上一个,
curr.Next=prev
- prev上一个节点指针后移,
prev=curr
- curr后移,
curr=next
- 保存curr的下一个节点
- 迭代结束后将
curr
指向上一个节点curr.Next=prev
- 按以下顺序连接所有节点:leftPrev -> right -> left -> rightNext,边界情况如下:
- left是头结点,此时leftPrevNode是空,最终结果链表right节点成为新头结点
- right是尾节点,此时rightNextNode是空,无需特殊处理
- left == right,无需翻转
func reverseBetween(head *ListNode, left int, right int) *ListNode {
if left == right {
return head
}
leftPrevNode, leftNode, rightNode, rightNextNode := locateNodes(head, left-1, right-1)
rightNode = reverse(leftNode, rightNode)
// left是头节点,链表连接顺序,right,left,rightNext
if leftPrevNode == nil {
leftNode.Next = rightNextNode
return rightNode
}
// 正常连接,连接顺序 leftPrevNode->right->left->rightNextNode
leftPrevNode.Next = rightNode
leftNode.Next = rightNextNode
return head
}
// 翻转left ~ right之间的节点
// 迭代翻转即可
// p 指向当前节点
// prev 指向上一个节点
// next指向p的next节点
// 如果p == rightNode则结束翻转并返回right
func reverse(leftNode *ListNode, rightNode *ListNode) *ListNode {
var (
prev *ListNode
p = leftNode
)
for p != rightNode {
next := p.Next
p.Next = prev // p 指向 上一个节点
prev = p // 上一个节点后移
p = next // p后移
}
// p 此时向rightNode,需要连接p和上一个节点
p.Next = prev
return p
}
// 定位
func locateNodes(head *ListNode, left, right int) (*ListNode, *ListNode, *ListNode, *ListNode) {
var (
index = 0
prevNode *ListNode
leftPrevNode *ListNode
leftNode *ListNode
rightNode *ListNode
rightNextNode *ListNode
)
for head != nil {
// 找到左节点
if index == left {
leftPrevNode = prevNode
leftNode = head
}
// 找到右节点
if index == right {
rightNode = head
rightNextNode = head.Next
}
// 指针移动
index++
prevNode = head
head = head.Next
}
return leftPrevNode, leftNode, rightNode, rightNextNode
}