题目描述

CF94DC1E-558E-442D-8EF5-F326FCCF981A_1_201_a.jpeg

题目链接

https://leetcode.cn/problems/reverse-nodes-in-k-group/

思路

本题的目标非常清晰易懂,不涉及复杂的算法,但是实现过程中需要考虑的细节比较多,容易写出冗长的代码。主要考查面试者设计的能力。

我们需要把链表节点按照【25】K 个一组翻转链表 - 图2个一组分组,所以可以使用一个指针【25】K 个一组翻转链表 - 图3依次指向每组的头节点。这个指针每次向前移动【25】K 个一组翻转链表 - 图4步,直至链表结尾。对于每个分组,我们先判断它的长度是否大于等于【25】K 个一组翻转链表 - 图5。若是,我们就翻转这部分链表,否则不需要翻转。

接下来的问题就是「如何翻转一个分组内的子链表」。翻转一个链表并不难,具体过程如下:
1.gif
具体代码如下:

  1. public ListNode overturn(ListNode head) {
  2. ListNode pre = null;
  3. ListNode cur = head;
  4. while (cur != null) {
  5. ListNode tmp = cur.next;
  6. cur.next = pre;
  7. pre = cur;
  8. cur = tmp;
  9. }
  10. return pre;
  11. }

但是对于一个子链表来说,除了翻转其本身之外,还需要记录子链表的前驱和后继节点,方便子链表翻转完成后把已翻转部分和未翻转部分通过翻转后的子链表连接起来。具体过程如下图所示:

866b404c6b0b52fa02385e301ee907fc015742c3766c80c02e24ef3a8613e5ad-k个一组翻转链表.png

代码实现

  1. public ListNode reverseKGroup(ListNode head, int k) {
  2. ListNode dummy = new ListNode(0, head);
  3. ListNode pre = dummy;
  4. ListNode end = dummy;
  5. while (end.next != null) {
  6. // end为子链表的尾节点
  7. for (int i = 0; i < k && end != null; i++) {
  8. end = end.next;
  9. }
  10. // 如果待翻转的链表节点数小于k,不执行翻转
  11. if (end == null) {
  12. break;
  13. }
  14. // start为子链表的头节点
  15. ListNode start = pre.next;
  16. // next为下一个子链表的头节点
  17. ListNode next = end.next;
  18. // 翻转 start -> end
  19. end.next = null;
  20. pre.next = overturn(start);
  21. // 翻转后start为子链表的尾节点
  22. start.next = next;
  23. pre = start;
  24. end = pre;
  25. }
  26. return dummy.next;
  27. }

复杂度分析

  • 时间复杂度:【25】K 个一组翻转链表 - 图8,其中【25】K 个一组翻转链表 - 图9为链表的长度。【25】K 个一组翻转链表 - 图10指针会在【25】K 个一组翻转链表 - 图11个节点上停留,每次停留需要进行一次【25】K 个一组翻转链表 - 图12的翻转操作。
  • 空间复杂度:【25】K 个一组翻转链表 - 图13,我们只需要建立常数个变量。