1.题目
给定单向链表,以k个为一组,组内链表逆序,最后输出链表首节点。例如有单链表A->B->C->D->E->F->G->H,当k=3时,A->B->C逆序为C->B->A,同理D->E->F逆序为F->E->D,G->H逆序为H->G,最后链表就是C->B->A->F->E->D->H->G。
2.解法
因为要按k个一组,因此要有一个变量记录已经遍历了多少个,遍历到K个或者链表结束的时候,就要开始新的一组遍历。因每组的第一个结点逆序后都会成为该组的最后结点,也就是下一组第一个结点的前一个结点,因此要记录上一组的最初第一个结点是什么(暂且叫lastFirstNode),以便本组逆序结束后,能链接在lastFirstNode后,还要考虑每组第一个节点的next值要先赋空,不然逆序后会和第二个节点成环等问题,具体看代码及注释:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
public static ListNode reverse(ListNode head, int k) {
if (head == null) {
return head;
}
ListNode firstNode = head;// 本组第一个结点
ListNode lastFirstNode = null;// 上组逆序前第一个结点
ListNode nowNode = head.next;// 正在遍历到的结点
ListNode finnalHead = head;// 最终输出的链表头
// 只有一个结点时,直接输出该节点即可
if (nowNode == null) {
return head;
}
ListNode nextNode = nowNode.next;// 正在遍历到的结点的下一个节点,可能为null
ListNode priousNode = head; // 正在遍历到的结点的前一个节点
// 因最终链表的头结点必然为第一组链表逆序后的首节点,因此设置一标志位以判定是否正在处理的是第一组链表
boolean firstK = true;
// 因最初正在遍历的结点我们设置的是第二个节点,因此无需遍历k次,而是k-1次。
// 此处需判断nowNode != null的原因是
for (int i = 0; i < k - 1 && nowNode != null; i++) {
nextNode = nowNode.next;
// 上组逆序结束后,会使firstNode和nowNode指向本组首节点,此时将nowNode指向后移,并做是否为空判断,
// 若为空,则说明本组只有一个结点,直接赋到上组逆序后的最后一个结点(也就是逆序前第一个节点)后即可
if (firstNode.equals(nowNode)) {
nowNode = nowNode.next;
if (nowNode == null) {
lastFirstNode.next = priousNode;
break;
}
nextNode = nowNode.next;
}
// 在遍历本组链表的开始时就需要将首节点的next值设为null,否则会出现和第二个节点成环的现象
if (i == 0) {
firstNode.next = null;
}
// 两个节点逆序
nowNode.next = priousNode;
// 将正在遍历的结点后移,这里就体现出保持下一个节点引用的作用了:
// 若不保持,则在逆序后,无法找到原本的下一个节点了。
priousNode = nowNode;
nowNode = nextNode;
// 如果遍历到本组最后的结点
if (i == k - 2 || nowNode == null) {
if (firstK == true) {
// 记录最终链表的首节点
finnalHead = priousNode;
firstK = false;
}
// 因在本次for循环结束后,i会自动加1,因此这里赋为-1,以便本次for循环结束后,i变为0
i = -1;
// 除第一组链表外,都需链接到上一组逆序后的链表末尾,因此这里对是否第一组做判断
if (lastFirstNode != null) {
lastFirstNode.next = priousNode;
}
// 将本组链表链接到上一组末尾后,将上组未逆序时首节点赋值为本组未逆序时首节点,
// 准备开始下一组的遍历逆序工作:此时nextNode指向的是下一组链表的第一个节点,
// firstNode和priousNode也都赋值为下一组第一个节点,
lastFirstNode = firstNode;
firstNode = nowNode;
priousNode = nowNode;
}
}
return finnalHead;
}