「这是我参与11月更文挑战的第 5 天,活动详情查看:2021最后一次更文挑战」
K个一组翻转链表
题目来源:LeetCode-K 个一组翻转链表
题目描述
给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表
k 是一个正整数,它的值小于或等于链表的长度
如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序
进阶:
- 你可以设计一个只使用常数额外空间的算法来解决此问题吗?
- 你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。
示例
示例 1
输入:head = [1,2,3,4,5], k = 2
输出:[2,1,4,3,5]
示例 2
输入:head = [1,2,3,4,5], k = 3
输出:[3,2,1,4,5]
示例 3
输入:head = [1,2,3,4,5], k = 1
输出:[1,2,3,4,5]
示例 4
输入:head = [1], k = 1
输出:[1]
提示:
- 列表中节点的数量在范围
sz内 1 <= sz <= 50000 <= Node.val <= 10001 <= k <= sz
解题
其实这道题跟翻转链表思路差不多,现在只不过是分段翻转,如果你不太清楚翻转链表过程是什么样的,建议先回顾一下(快速通道)
思路
创建一个哨兵结点(dummy),哨兵结点的next指针指向链表的头结点,对原链表进行每k组一次的遍历,并将每k个元素按照头插法插入到哨兵结点的后边。每插入k个结点后,更新哨兵结点到改组(每k个一组)结点的尾节点,然后再从原链表中遍历k个节点过来,重复下去,直到遍历到结尾(不足k的部分不翻转)
- 定义start、end结点,start指向每组结点的头结点,end指向每组结点的尾节点
- 定义pre结点、next结点,pre结点指向start结点的前驱结点,next指向end的后继结点
- 每翻转一组,更新pre为翻转完成的链表的尾节点、start为pre的后继结点,将翻转完成部分的链表和待反转部分的链表连接
- 期间如果end为空,则终止
图中只画了一轮,其余过程都是一样的(链表的题不画图的话,稍微复杂一点,就很容易指错)
代码
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func reverseKGroup(head *ListNode, k int) *ListNode {
dummy := &ListNode{}
pre := dummy
end := dummy
dummy.Next = head
for end.Next != nil {
for i :=0; i < k && end != nil; i++ {
end = end.Next
}
if end == nil {
break
}
start := pre.Next
next := end.Next
end.Next = nil //这个是为了方便反转这个k个节点
pre.Next = reverseList(start) //前边部分与刚反转完的这部分连接
start.Next = next // 反转之后start变成了这个k组中的最后一个结点的位置,所以让他的Next指针指向next结点,将后边未翻转的连接起来
pre = start
end = pre
}
return dummy.Next
}
func reverseList(head *ListNode) *ListNode {
if head == nil || head.Next == nil{
return head
}
newHead := &ListNode{}
newHead.Next = head
prevNode := newHead.Next
currentNode := prevNode.Next
for currentNode != nil {
prevNode.Next = currentNode.Next
currentNode.Next = newHead.Next
newHead.Next = currentNode
currentNode = prevNode.Next
}
head = newHead.Next
return head
}