模拟法:分段+反转

public ListNode reverseKGroup(ListNode head, int k) {
        ListNode dum = new ListNode(0);//构造虚拟节点,跟踪头节点
        dum.next = head;//指向最初的头节点
        ListNode prev = dum;//作为prev节点记录,方便对接后段反转后的子链表

        while (head!=null){//【段头】指针

            ListNode tail = prev;//【段尾】指针
            for (int i = 0; i < k; i++) {
                tail = tail.next;//移动
                if (tail==null) return dum.next;//段尾节点为空说明当前长度不够,不用再反转
            }
            ListNode[] nodes = reverse(head,tail);//分段反转
            head = nodes[1];//因反转,需纠正段首和段尾的指针位置
            tail = nodes[0];//同上
            prev.next = head;//链接上一端链表的节点
            //1:↑,注意,第一段的链表反转后,因dum和prev同一引用,所以prev指向新的头节点时dum也会生效:dum.next更新
            prev = tail;//移动prev,为下一分段准备
            //2:↑,注意:执行完这句后prev就和hair不再是同一个引用了,后续分段的反转则不会再对dum.next有影响
            head = prev.next;//移动head,为下一分段准备

        }
        return dum.next;
    }

    public ListNode[] reverse(ListNode head, ListNode tail) {
        //↑:1(head)-2(tail)-3-4-5 或 2-1-3(head)-4(tail)-5
        ListNode dum = tail.next;
        //↑:tail.next作为最后节点,即能完成head-tail的反转,也能方便连接下一分段的链表
        ListNode cur = head;
        while (dum!=tail){
            //↑:不用cur!=tail.next,tail会被改变指向导致进入死循环
            ListNode next = cur.next;
            cur.next = dum;
            dum = cur;
            cur = next;
        }
        //↑:2(tail)-1(head)-3-4-5 或 2-1-4(head)-3(tail)-5
        return new ListNode[]{head,tail};
        //↑:返回两个指针,方便外面交换
    }