链表中的节点每k个一组进行翻转
题目:
将给出的链表中的节点每k个一组翻转,返回翻转后的链表。
如果链表中的节点数不是k的倍数,将最后剩下的节点保持原样。
不能更改节点中的值,只能更改节点本身。
要求空间复杂度为O(1)
示例:
给定的链表是:1→2→3→4→5
对于k = 2,你应该返回:2→1→4→3→5
对于k = 3,你应该返回:3→2→1→4→5
思路:
① 题目要求我们每k个节点一组进行翻转。所以我们必须要明确每次要翻转的节点的范围。
② 对于我们确定好的范围,我们要进行该范围内的翻转链表。
③ 翻转链表后,又有新的问题,翻转后的链表怎么链到原链表中呢?我们可以通过指针来完成该操作。定义prev指针,用来保存一组待翻转链表的上一个节点,定义start,用来保存待翻转链表的第一个节点。再定义一个指针 next,用来保存待翻转链表的最后一个节点的下一个节点。这样,我们就能轻而易举的将翻转后的链表链到原链表中。
图示:
① 图示为链表初始状态,其中dummy是头结点,无实际意义。end、prev分别指向dummy。我们用 k = 2 来举例。
② 通过end,我们找到了待翻转链表的最后一个节点,然后我们需要找到待翻转链表的第一个节点,定义start指针,指向待翻转链表的第一个节点,然后我们还需要知道待翻转链表的最后一个节点的下一个节点,便于下一次我们找到新的一组待翻转链表的第一个节点。我们使用next指针记录待翻转链表的最后一个节点的下一个节点。如图所示:
此时,翻转start、end范围内的节点,注意在翻转之前,我们应该将end.next置为null。这样才能准确地进行翻转。翻转结果如下图所示:
接着我们将start所指向的节点的next域指向next,**prev指向的节点的next域指向我们新翻转后链表的第一个结点。**如下图所示:
紧接着,我们调整end、prev、start、以便下一组链表翻转。此时应该将prev 指向start。 end指向start。 然后继续翻转下一组链表。继续通过for循环找到下一组链表的最后一个结点,如下图所示:
翻转start、end范围内的结点,如下图所示:
prev.next置为新翻转后的链表的第一个结点,start.next指向next。end、prev都指向start。如下图所示:
继续重复上面的步骤,最后链表为:
代码实现:
public ListNode reverseKGroup (ListNode head, int k) {
if(head == null) return null;
ListNode dummy = new ListNode(0); //链表头结点,无实际意义
ListNode prev = dummy;
ListNode end = dummy;
dummy.next = head;
while(end!=null){
for(int i = 0; i<k && end!=null; i++){
end = end.next;
}
if(end == null) break;
ListNode next = end.next; //记录下一组链表的第一个节点
end.next = null; //这组链表先与后面的链表断开连接。
ListNode start = prev.next; // 记录待翻转链表的第一个节点,用于reverse()函数使用。
prev.next = reverse(start); //reverse()函数返回翻转后链表的第一个节点。
// 记录下一组链表的start、end以及其前一个节点。
start.next = next; //新翻转的链表最后一个节点链接后面的节点。
prev = start;
end = prev;
}
return dummy.next;
}
private ListNode reverse(ListNode head){
if(head == null) return null;
ListNode cur = head;
ListNode next = null;
ListNode prev = null;
while(cur!=null){
next = cur.next;
cur.next = prev;
prev = cur;
cur = next;
}
return prev;
}
}