25. K 个一组翻转链表 - 力扣(LeetCode)

打印 上一主题 下一主题

主题 1737|帖子 1737|积分 5211

标题:
        给你链表的头节点 head ,每 k 个节点一组进行翻转,请你返回修改后的链表。k 是一个正整数,它的值小于或等于链表的长度。假如节点总数不是 k 的整数倍,那么请将末了剩余的节点保持原有次序。
        你不能只是单纯的改变节点内部的值,而是需要现实进行节点交换。
示例 1:


  1. 输入:head = [1,2,3,4,5], k = 2
  2. 输出:[2,1,4,3,5]
复制代码
示例 2:


  1. 输入:head = [1,2,3,4,5], k = 3
  2. 输出:[3,2,1,4,5]
复制代码
提示:


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

思绪如下:
        本题的思绪是基于链表的分组反转操纵。具体来说,就是将链表的节点每 k 个为一组进行反转。首先统计链表的节点个数,如许可以知道是否尚有充足的节点来进行分组反转。创建一个虚拟头节点 dummy ,这个虚拟头节点的 next 指向原链表的头节点,然后初始化三个指针:p0 指向当前处理组的前一个节点(初始为虚拟头节点),pre 用于保存当前节点的前一个节点(初始为 None),cur 用于遍历链表(初始为头节点 head)。
        在分组反转的过程中,每次处理 k 个节点的一组,直到剩余节点数不敷 k 个。在每次处理时,通过一个循环逐个反转节点,保存当前节点的下一个节点到 nxt,将当前节点的 next 指向 pre 实现反转,然后更新 pre 和 cur 指针。
        反转完成后,需要将反转后的子链表毗连到主链表中。这里通过 p0.next 指向反转后的组头节点 pre,而反转前的组头节点(nxt)的 next 指向当前 cur(下一组的第一个节点),末了更新 p0 到下一组的前一个节点。
        这里用表示图俩表示题解实例中完成第一个 k 的反转后的链表,以便参考:


题解如下:
  1. # Definition for singly-linked list.
  2. # class ListNode:
  3. #     def __init__(self, val=0, next=None):
  4. #         self.val = val
  5. #         self.next = next
  6. class Solution:
  7.    def reverseKGroup(self, head: Optional[ListNode], k: int) -> Optional[ListNode]:
  8.        # 统计链表的节点个数
  9.        n = 0
  10.        cur = head
  11.        
  12.        while cur:
  13.            n += 1
  14.            cur = cur.next
  15.        # 创建一个虚拟头节点
  16.        p0 = dummy = ListNode(next=head)
  17.        # pre用于记录当前反转部分的前一个节点,cur用于遍历链表
  18.        pre = None
  19.        cur = head
  20.        # 每次处理k个节点的一组
  21.        while n >= k:
  22.            # 减少剩余需要处理的节点数
  23.            n -= k
  24.            # 反转当前组的k个节点
  25.            for i in range(k):
  26.                # 保存当前节点的下一个节点
  27.                nxt = cur.next
  28.                # 将当前节点的next指向前一个节点pre,实现反转
  29.                cur.next = pre
  30.                # 更新pre和cur的位置
  31.                pre = cur
  32.                cur = nxt
  33.            # 连接反转后的子链表到主链表中
  34.            # nxt是p0的下一个节点,即反转前的组头节点
  35.            nxt = p0.next
  36.            # 将反转后的组尾节点(nxt)的next指向当前cur(下一组的第一个节点)
  37.            nxt.next = cur
  38.            # 将p0的next指向反转后的组头节点pre
  39.            p0.next = pre
  40.            # 更新p0到下一组的前一个节点(即当前组的尾节点nxt)
  41.            p0 = nxt
  42.        # 返回虚拟头节点的next,即新的链表头
  43.        return dummy.next
复制代码
 

题解示例:
  
  1. 假设链表为 1 -> 2 -> 3 -> 4 -> 5 -> None,k=2。
  2. 统计节点个数:n = 5。
  3. 创建虚拟头节点:dummy.next = 1 -> 2 -> 3 -> 4 -> 5 -> None。
  4. 初始化指针:p0 指向 dummy,pre = None,cur = 1。
  5. 第一次处理 k=2 个节点:
  6. 反转 1 -> 2,得到 2 -> 1。
  7. 连接反转后的子链表:
  8. nxt = 1(p0.next)。
  9. 1.next = 3(当前 cur)。
  10. p0.next = 2(反转后的组头节点)。
  11. 更新 p0 到 1。
  12. 第二次处理 k=2 个节点:
  13. 反转 3 -> 4,得到 4 -> 3。
  14. 连接反转后的子链表:
  15. nxt = 3(p0.next)。
  16. 3.next = 5(当前 cur)。
  17. p0.next = 4(反转后的组头节点)。
  18. 更新 p0 到 3。
  19. 剩余节点数不足 k,停止处理。
  20. 最终链表为 2 -> 1 -> 4 -> 3 -> 5 -> None。
复制代码

逻辑梳理:
1. 统计链表节点个数
        首先,遍历链表统计节点总数 n,这有助于判定是否尚有充足的节点进行分组反转。
2. 创建虚拟头节点
        创建一个虚拟头节点 dummy,其 next 指向原链表的头节点。这可以简化界限条件的处理,好比当链表为空或者只有一个节点时的操纵。
3. 初始化指针


  • p0:指向当前处理组的前一个节点,初始为虚拟头节点。
  • pre:用于保存当前节点的前一个节点,初始为 None。
  • cur:用于遍历链表,初始为头节点 head。
4. 分组反转逻辑
每次处理 k 个节点的一组,直到剩余节点数不敷 k 个:


  • 减少剩余节点数:n -= k,表示已经处理了一组 k 个节点。
  • 反转当前组的 k 个节点

    • 使用一个循环,遍历 k 次,逐个反转节点。
    • 在每次循环中:

      • 保存当前节点的下一个节点到 nxt。
      • 将当前节点的 next 指向 pre,实现反转。
      • 更新 pre 和 cur 指针。


5. 毗连反转后的子链表


  • nxt 是 p0 的下一个节点,即反转前的组头节点。
  • 将反转后的组尾节点(nxt)的 next 指向当前 cur(下一组的第一个节点)。
  • 将 p0 的 next 指向反转后的组头节点 pre。
  • 更新 p0 到下一组的前一个节点(即当前组的尾节点 nxt)。
6. 返回效果
        返回虚拟头节点的 next,即新的链表头。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

千千梦丶琪

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表