leetcode:24. 每k个一组翻转链表

题目来源

题目描述

在这里插入图片描述
在这里插入图片描述

struct ListNode {
    int val;
    ListNode *next;
    ListNode() : val(0), next(nullptr) {}
    ListNode(int x) : val(x), next(nullptr) {}
    ListNode(int x, ListNode *next) : val(x), next(next) {}
};


class Solution {
public:
    ListNode* reverseKGroup(ListNode* head, int k) {

    }
};

题目解析

分析

  • 每k个一组来翻转链表,实际上是把原链表分成若干小端,然后分别对其进行翻转。
  • 那么肯定需要两步:一个分段、一个翻转
  • 怎么分段呢?
    • 把链表节点按照k个一组分组,所以可以使用一个指针head依次执行每组的头结点。
    • 这个指针每次向前移动k步,直到链表结尾
    • 对于每个分组,我们先判断它的长度是否>=k
      • 如果是,反转这部分链表
      • 如果不是,不需要反转
  • 如何反转?
    • 和一般的反转链表不同:对于一个子链表,除了反转本身外,还需要将子链表的头部和上一个子链表连接,以及子链表的尾部与下一个子链表连接
    • 因此,在翻转子链表的时候,我们不仅需要子链表头节点 head,还需要有 head 的上一个节点 pre,以便翻转完后把子链表再接回 pre。

在这里插入图片描述

  • 但是对于第一个子链表,它的头结点head前面是没有pre的。怎么办呢?

    • 方法一:特判
    • 方法二:还可以引入一个dummyHead,比如链表 1->2->3->4->5变为 -1->1->2->3->4->5
      在这里插入图片描述
  • 反复移动head和pre,对head所指向的子链表进行反转,直到结尾,就可以答案。

  • 问题是:链表翻转之后,链表的头节点发生了变化,那么应该返回哪个节点呢?

    • 照理来说,前 k 个节点翻转之后,链表的头节点应该是第 k 个节点。
    • 那么要在遍历过程中记录第 k 个节点吗?但是如果链表里面没有 k 个节点,答案又还是原来的头节点。太麻烦了!!!
    • 可以注意到,我们之前创建了节点 pre 吗?这个节点一开始被连接到了头节点的前面,而无论之后链表有没有翻转,它的 next 指针都会指向正确的头节点。那么我们只要返回它的下一个节点就好了。
-1->1->2->3->4->5
 |        |  |
pre      cur next

-1->3->2->1->4->5
    |     |  |
   cur   pre next

实现

class Solution {
    ListNode* reverseOneGroup(ListNode* pre, ListNode* next) {
        //      x -- > b -- > c --> d
        // pre  last  curr          next    
        ListNode *last = pre->next, *cur = last->next;
        while(cur != next) {
            last->next = cur->next;
            cur->next = pre->next;
            pre->next = cur;
            cur = last->next;
        }
        return last;
    }
public:
    ListNode* reverseKGroup(ListNode* head, int k) {
        if (!head || k == 1) return head;
        ListNode *dummy = new ListNode(-1), *pre = dummy, *cur = head;
        dummy->next = head;
        for (int i = 1; cur; ++i) {
            if (i % k == 0) {
                pre = reverseOneGroup(pre, cur->next);
                cur = pre->next;
            } else {
                cur = cur->next;
            }
        }
        return dummy->next;
    }
};

递归实现


class Solution {
    // head: 记录每段的开始位置
    // next: 记录结束位置的下一个节点
    ListNode* reverseOneGroup(ListNode* head, ListNode* next) {
       ListNode *prev = next;
       while (head != next){
           ListNode *t = head->next;
           head->next = prev;
           prev = head;
           head = t;
       }
       return prev;
    }
public:
    // head: 记录每段的开始位置
    ListNode* reverseKGroup(ListNode* head, int k) {
        ListNode *curr = head;
        for (int i = 0; i < k; ++i) {
            if(curr == nullptr){
                return head;
            }
            curr = curr->next;
        }
        
        ListNode *newHead = reverseOneGroup(head, curr);
        head->next = reverseKGroup(curr, k);
        return newHead;
    }
};

类似题目

题目核心思路
leetcode:24. 两两交换链表中的节点 Swap Nodes in Pairs
leetcode:206. 反转全部链表
leetcode:92. 反转链表区间 [m, n] 的元素
leetcode:143. 重排链表
leetcode:24. 每k个一组反转链表Reverse Nodes in k-Group
leetcode:面试题06. 从尾到头打印链表
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值