方法一:笨比方法,两重循环翻转,笨的原因是一次翻转只移动一步,类似冒泡排序的交换
class ListNode {
int val;
ListNode next;
ListNode() {}
ListNode(int val) { this.val = val; }
ListNode(int val, ListNode next) { this.val = val; this.next = next; }
}
public class Solution {
int num;//多少个为一组
public ListNode reverseKGroup(ListNode head, int k) {
num = k;
ListNode tmp = new ListNode();//虚拟表头,因为反转的时候需要知道前驱节点,而表头没有前驱节点,所以虚拟一个
tmp.next = head;
reverseKGroup(tmp);
return tmp.next;
}
public void reverseKGroup(ListNode head) {
ListNode tmp = head;//当前head为表头的前驱节点
//判断待翻转节点数是否大于等于num,否则不需要翻转
for (int i = 0; i < num; i++) {
if (tmp != null)
tmp = tmp.next;
else
return ;
}
if (tmp == null)
return ;
//将前驱节点保存
ListNode pre = head;
//将表头保存
head = head.next;
ListNode tmpPre = pre;
ListNode tmpHead = head;
//需要进行num-1轮翻转,每次翻转前num个节点,第1次翻转前num个节点,最后一次翻转前2个节点
for (int length = num - 1; length > 0; length--) {
//每经过一轮都需要从前驱节点重新开始
tmpHead = pre.next;//需要注意tmpHead != head
tmpPre = pre;
for (int i = 0; i < length; i++) {
//下面操作将tmpHead和tmpHead位置互换
tmp = tmpHead.next;
tmpHead.next = tmp.next;
tmp.next = tmpHead;
tmpPre.next = tmp;
tmpPre = tmp;
}
}
//对剩下的节点进行翻转
reverseKGroup(head);
}
public static void main(String[] args) {
ListNode head = new ListNode(1);
head.next = new ListNode(2);
head.next.next = new ListNode(3);
head.next.next.next = new ListNode(4);
head.next.next.next.next = new ListNode(5);
new Solution().reverseKGroup(head,3);
}
}
方法二:首先改成了非递归,其次在翻转子链表的时候不再是一次移动一步,而且把翻转子链表提取成了一个函数,入口参数为表头和表尾的后继。翻转完成后返回表头和表尾。
重点在于翻转子链表时的动作,它是每次将head.next赋为新获得链表的表头。初始时,新获得链表表头为待翻转子链表的表尾的后继。这样每次都会讲最前面的节点移到后面。例如:1->2->3->4,首先会变成2->3->4, 1->4,然后变成3->4,2->1->4。最终变为3->2->1->4。
效率由5%到100%
class Solution {
int num;
public ListNode reverseKGroup(ListNode head, int k) {
num = k;
//虚拟头结点
ListNode hair = new ListNode(0);
hair.next = head;
ListNode pre = hair;
ListNode tail;
while (head != null) {
tail = pre;//每一轮循环开始时,tail都指向待翻转子链表表头的前驱
//判断待翻转子链表节点数是否足够
for (int i = 0; i < k; i++) {
tail = tail.next;
if (tail == null)
return hair.next;
}
ListNode tailnext = tail.next;//保存子链表的尾节点的后继节点
//翻转子链表并返回一个节点数组,0处为翻转后的表头,1为翻转后的表尾
ListNode[] arr = myReverse(head, tailnext);
head = arr[0];
tail = arr[1];
pre.next = head;
//tail.next = tailnext;
head = tailnext;
pre = tail;
}
return hair.next;
}
public ListNode[] myReverse(ListNode head, ListNode tailnext) {
//翻转head->tail的整条链表的节点顺序
ListNode nextReverse = head.next;
ListNode pre = tailnext;//每次都将head.next置为pre,pre为新链表的头,初始时新链表只包含tailnext一个节点
ListNode newTail = head;//头节点会成为翻转后的尾节点
while (true) {
head.next = pre;
pre = head;//最新翻转的节点成为新链表的头,即pre
//最后翻转的节点在翻转之前他的next就是tailnext,而nextReverse就是翻转前的next,所以相等时表明翻转完成
if (nextReverse == tailnext)
break;
else {
head = nextReverse;//当前待翻转节点为上个循环的nextReverse
nextReverse = head.next;//nextReverse为翻转前的nextReverse的next
}
}
//最后一个反转的节点是新链表的头
return new ListNode[]{head, newTail};
}
}