Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[LeetCode] 25. Reverse Nodes in k-Group #25

Open
grandyang opened this issue May 30, 2019 · 1 comment
Open

[LeetCode] 25. Reverse Nodes in k-Group #25

grandyang opened this issue May 30, 2019 · 1 comment

Comments

@grandyang
Copy link
Owner

grandyang commented May 30, 2019


请点击下方图片观看讲解视频
Click below image to watch YouTube Video
Video

Given the head of a linked list, reverse the nodes of the list k at a time, and return the modified list.

k is a positive integer and is less than or equal to the length of the linked list. If the number of nodes is not a multiple of k then left-out nodes, in the end, should remain as it is.

You may not alter the values in the list's nodes, only nodes themselves may be changed.

Example 1:

**Input:** head = [1,2,3,4,5], k = 2
**Output:** [2,1,4,3,5]

Example 2:

**Input:** head = [1,2,3,4,5], k = 3
**Output:** [3,2,1,4,5]

Constraints:

  • The number of nodes in the list is n.
  • 1 <= k <= n <= 5000
  • 0 <= Node.val <= 1000

Follow-up: Can you solve the problem in O(1) extra memory space?

这道题让我们以每k个为一组来翻转链表,实际上是把原链表分成若干小段,然后分别对其进行翻转,那么肯定总共需要两个函数,一个是用来分段的,一个是用来翻转的,以题目中给的例子来看,对于给定链表 1->2->3->4->5,一般在处理链表问题时,大多时候都会在开头再加一个 dummy node,因为翻转链表时头结点可能会变化,为了记录当前最新的头结点的位置而引入的 dummy node,加入 dummy node 后的链表变为 -1->1->2->3->4->5,如果k为3的话,目标是将 1,2,3 翻转一下,那么需要一些指针,pre 和 next 分别指向要翻转的链表的前后的位置,然后翻转后 pre 的位置更新到如下新的位置:

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


-1->3->2->1->4->5  

|     |  |  

cur   pre next

以此类推,只要 cur 走过k个节点,那么 next 就是 cur->next,就可以调用翻转函数来进行局部翻转了,注意翻转之后新的 cur 和 pre 的位置都不同了,那么翻转之后,cur 应该更新为 pre->next,而如果不需要翻转的话,cur 更新为 cur->next,代码如下所示:

解法一:

class Solution {
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;
    }
    ListNode* reverseOneGroup(ListNode* pre, ListNode* 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;
    }
};

我们也可以在一个函数中完成,首先遍历整个链表,统计出链表的长度,然后如果长度大于等于k,交换节点,当 k=2 时,每段只需要交换一次,当 k=3 时,每段需要交换2此,所以i从1开始循环,注意交换一段后更新 pre 指针,然后 num 自减k,直到 num<k 时循环结束,参见代码如下:

解法二:

class Solution {
public:
    ListNode* reverseKGroup(ListNode* head, int k) {
        ListNode *dummy = new ListNode(-1), *pre = dummy, *cur = pre;
        dummy->next = head;
        int num = 0;
        while (cur = cur->next) ++num;
        while (num >= k) {
            cur = pre->next;
            for (int i = 1; i < k; ++i) {
                ListNode *t = cur->next;
                cur->next = t->next;
                t->next = pre->next;
                pre->next = t;
            }
            pre = cur;
            num -= k;
        }
        return dummy->next;
    }
};

我们也可以使用递归来做,用 head 记录每段的开始位置,cur 记录结束位置的下一个节点,然后调用 reverse 函数来将这段翻转,然后得到一个 new_head,原来的 head 就变成了末尾,这时候后面接上递归调用下一段得到的新节点,返回 new_head 即可,参见代码如下:

解法三:

class Solution {
public:
    ListNode* reverseKGroup(ListNode* head, int k) {
        ListNode *cur = head;
        for (int i = 0; i < k; ++i) {
            if (!cur) return head;
            cur = cur->next;
        }
        ListNode *new_head = reverse(head, cur);
        head->next = reverseKGroup(cur, k);
        return new_head;
    }
    ListNode* reverse(ListNode* head, ListNode* tail) {
        ListNode *pre = tail;
        while (head != tail) {
            ListNode *t = head->next;
            head->next = pre;
            pre = head;
            head = t;
        }
        return pre;
    }
};

Github 同步地址:

#25

类似题目:

Swap Nodes in Pairs

参考资料:

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

https://leetcode.com/problems/reverse-nodes-in-k-group/discuss/11435/C%2B%2B-Elegant-and-Small

https://leetcode.com/problems/reverse-nodes-in-k-group/discuss/11457/20-line-iterative-C%2B%2B-solution

https://leetcode.com/problems/reverse-nodes-in-k-group/discuss/11440/Non-recursive-Java-solution-and-idea

https://leetcode.com/problems/reverse-nodes-in-k-group/discuss/11423/Short-but-recursive-Java-code-with-comments

LeetCode All in One 题目讲解汇总(持续更新中...)

(欢迎加入博主的知识星球,博主将及时答疑解惑,并分享刷题经验与总结,快快加入吧~)

知识星球 喜欢请点赞,疼爱请打赏❤️~.~

微信打赏

|

Venmo 打赏


---|---

@lld2006
Copy link

lld2006 commented Jun 23, 2020

解法2中 pre指向每一段(长度k)的结尾, cur指向每一段的开头, nxt是cur的next
做k-1次置换, 每次把nxt 换到pre后面, cur 指向nxt->next, pre 指向 nxt. 非常巧妙。
k次置换做完, cur其实是这一段的最后一个node, 赋给pre

另,在discussion中看到如下去除内存泄漏的方法。
ListNode t(-1, head);
ListNode* pre =&t,

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants