思路:翻转K个节点区间内的链表,然后获得新的区间头尾结点,再与前后区间拼接。

function reverseKGroup( head ,  k ) {
  const root = { next: head }; // 虚拟节点,保存头节点
  let prev = root; // 上一部分的尾节点
  let start = head; // 翻转区间的开始节点
  let end = head; // 翻转区间的结束节点
  let count = 1; // 用于翻转计数
  while(end) {
    if(count === k) {
      // 缓存
      const cacheDoneEnd = start; // 缓存当前区间翻转后的尾结点,即为未翻转前的开始节点

      /** 区间翻转开始 **/
      // 翻转前区间为:prev -> n1(cacheDoneEnd) -> ... -> nk -> cacheNextStart
      // 翻转后:prev -> nk -> ... -> n1(cacheDoneEnd) -> cacheNextStart
      // 下一区间:prev(n1(cacheDoneEnd)) -> cacheNextStart(start) -> ...
      let tp = end.next; // 翻转后的节点指向的节点,第一个翻转的节点变为最后一个节点,所以要指向下一区间的开始节点
      while(count > 1) {
        // 当前节点指向前一个节点
        // 前一个节点更新为当前节点
        // 当前节点更新为下一个节点
        [start.next, tp, start] = [tp, start, start.next];
        count--; // 翻转 k - 1 次,即 k 个节点
      }
      start.next = tp; // 将翻转后的最后一个节点指向前一个节点
      /** 区间翻转完成 **/

      // 更新下一区间
      prev.next = start; // 将上一区间的尾节点指向当前区间的开头节点,start 迭代到区间的末尾节点,因为链表翻转,又成为区间的开始节点
      prev = cacheDoneEnd; // 将当前区间的尾结点更新为下一区间的上一区间尾结点
      start = prev.next; // 更新下一区间的开始节点
      end = start; // 重新设置下一区间的尾节点
    } else {
      // 区间范围不足,扩大区间
      end = end.next;
      count++;
    }
  }
  return root.next;
}