LeetCode题练习与总结:K个一组翻转链表

一、题目

给你链表的头节点 head ,每 k 个节点一组进行翻转,请你返回修改后的链表。

k 是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。

你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。

示例 1:

输入:head = [1,2,3,4,5], k = 2
输出:[2,1,4,3,5]

示例 2:

输入:head = [1,2,3,4,5], k = 3
输出:[3,2,1,4,5]

提示:

  • 链表中的节点数目为 n
  • 1 <= k <= n <= 5000
  • 0 <= Node.val <= 1000

二、解题思路

1. 创建一个虚拟头节点 dummy,它的 next 指向原链表的头节点 head。这样可以在反转链表时不影响原链表的头节点。

2. 创建一个指针 prev 用于跟踪当前要反转的子链表的前一个节点,初始化为 dummy

3. 创建一个指针 current 用于遍历链表,初始化为 head

4. 使用一个循环来处理每个长度为 k 的子链表,直到 current 到达链表末尾或无法组成完整的 k 个节点的子链表:

  • 在每次循环开始时,检查是否有足够的节点来反转(即 current 至少向前 k 个节点)。
  • 如果有足够的节点,开始反转 k 个节点:
    • 初始化三个指针 nextnewHeadtailnext 用于临时存储下一个节点,newHead 用于指向新反转的子链表的头节点,tail 用于指向当前子链表的最后一个节点。
    • 在反转过程中,不断更新这三个指针的值,直到反转完成。
  • 更新 prev.next 指向新反转的子链表的头节点,prev 指向当前子链表的尾节点,current 指向下一组的头节点(如果有的话)。

5. 返回虚拟头节点的 next,即反转后的链表的头节点。

三、具体代码

class Solution {
    public ListNode reverseKGroup(ListNode head, int k) {
        if (head == null || k == 1) {
            return head;
        }

        ListNode dummy = new ListNode(0);
        dummy.next = head;
        ListNode prev = dummy;
        ListNode current = head;
        ListNode next;

        while (current != null && current.next != null) {
            // 检查是否有足够的节点来反转
            next = current;
            for (int i = 0; i < k; i++) {
                if (next == null) {
                    break;
                }
                next = next.next;
            }
            if (next == null) {
                break;
            }

            // 反转k个节点
            ListNode newHead = prev.next;
            ListNode tail = current;
            for (int i = 0; i < k; i++) {
                next = current.next;
                current.next = newHead;
                newHead = current;
                current = next;
            }
            prev.next = newHead;
            tail.next = current; // 连接反转后的子链表的尾节点到下一组的头节点
            prev = tail; // 更新prev指针
        }

        return dummy.next;
    }
}

四、时间复杂度和空间复杂度

1. 时间复杂度
  • 外层循环遍历链表中的每个节点,对于每个节点,内层循环最多执行 k 次(尝试找到 k 个连续的节点)。
  • 内层循环中的操作是常数时间操作,因此内层循环的时间复杂度是 O(k)。
  • 外层循环的时间复杂度是 O(n),其中 n 是链表的总节点数。
  • 综合起来,总的时间复杂度是 O(kn),这是因为每个节点最多被访问两次(一次在外层循环中,一次在内层循环中)。
2. 空间复杂度
  • 代码中使用了常数数量的额外空间来存储指针(dummy, prev, current, next, newHead, tail)。
  • 所有这些指针都是独立的,不依赖于输入链表的大小。
  • 因此,空间复杂度是 O(1),即常数空间复杂度。

五、总结知识点

1. 虚拟头节点(Dummy Head):

  • 创建一个虚拟头节点 dummy,它的 next 指向原链表的头节点 head。这是一种常用的技巧,用于简化边界条件,特别是在处理链表的头节点时。

2. 链表遍历和反转:

  • 使用一个 while 循环来遍历链表,寻找长度为 k 的子链表。
  • 在找到的子链表中,使用三个指针(prevcurrentnext)来辅助完成反转操作。
  • 反转过程中,通过改变节点的 next 指针方向来实现链表的反转。

3. 指针操作:

  • 在反转过程中,prev 指针始终指向当前子链表的前一个节点。
  • current 指针用于遍历链表,找到需要反转的节点。
  • next 指针临时存储下一个节点,以便在反转过程中更新 current.next

4. 条件检查:

  • 在每次尝试反转之前,代码检查是否有足够的节点来形成一个长度为 k 的子链表。如果不足 k 个节点,则跳出循环,不再进行反转。

5. 连接反转后的子链表:

  • 反转完成后,将反转后的子链表的头节点(newHead)连接到前一个子链表的尾节点(prev)。
  • 同时,更新 tail 指针(原子链表的尾节点)的 next 指针,使其指向下一个子链表的头节点(如果有的话)。

6. 返回结果:

  • 最后,返回虚拟头节点的 next 指针,即反转后链表的新头节点。

以上就是解决这个问题的详细步骤,希望能够为各位提供启发和帮助。

相关推荐

  1. LeetCode100】【K 翻转

    2024-03-18 10:48:05       13 阅读
  2. leetcodeHOT 25. K 翻转

    2024-03-18 10:48:05       24 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-03-18 10:48:05       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-03-18 10:48:05       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-03-18 10:48:05       19 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-03-18 10:48:05       20 阅读

热门阅读

  1. 管理的常识--什么是管理

    2024-03-18 10:48:05       22 阅读
  2. CSS中水平垂直居中的实现

    2024-03-18 10:48:05       18 阅读
  3. 5.72 BCC工具之wakeuptime.py解读

    2024-03-18 10:48:05       25 阅读
  4. mysql转达梦的python脚本

    2024-03-18 10:48:05       19 阅读
  5. python中pyinstaller打包带资源的程序-pgzreo

    2024-03-18 10:48:05       20 阅读
  6. 阻塞和异步

    2024-03-18 10:48:05       20 阅读
  7. 使用verilog实现井字棋游戏设计及其testbench

    2024-03-18 10:48:05       22 阅读
  8. VSCODE的常用插件

    2024-03-18 10:48:05       20 阅读
  9. js基础语法大全(时间戳,uuid,字符串转json)

    2024-03-18 10:48:05       20 阅读
  10. 【无标题】

    2024-03-18 10:48:05       20 阅读