[路飞]_前端算法第十弹-K 个一组翻转链表

101 阅读2分钟

「这是我参与11月更文挑战的第7天,活动详情查看:2021最后一次更文挑战

经过了前两篇文章的反转链表反转部分链表的练习,已经对反转链表有了一定的理解,这篇文章就是反转链表的最后一篇,也是难度最大的一篇,是我迄今为止写的第一篇难度为困难的题,如果说直接看这道题,可能还会有所困惑,但是经过了前两篇文章之后,我想拿到这个题,应该足以得心应手。

25. K 个一组翻转链表

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

k 是一个正整数,它的值小于或等于链表的长度。

如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。

进阶:

  • 你可以设计一个只使用常数额外空间的算法来解决此问题吗?

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

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

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

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

    输入:head = [1], k = 1 输出:[1]

看到这道题,似乎和上一道翻转部分链表相似,如果不是每K节点翻转一次,而是从头开始隔K节点翻转一次,只反转一次的话,那就是上一篇文章中,left=1,right=k。所以我们能想到的就是把链表分段,首先遍历链表得到链表的长度,然后确定可以进行几次K个节点的反转,第一次反转1-K个节点,再反转K+1-2K个节点,直至反转完成。

var reverseKGroup = function (head, k) {
    // 按节点数为k把链表分段,每段翻转,最后一段判断节点长度
    let link = head;
    let n = 0;
    // 计算链表的长度
    while (link) {
        link = link.next;
        n++
    }
    // 获取需要反转几次
    let m = Math.floor(n / k)
    // 进行m次反转
    for (let i = 0; i < m; i++) {
        head = reverseBetween(head, i * k + 1, k)
    }
    return head
};

// 这就是上一篇文章的部分链表反转,
// 如有不懂可查阅“[反转部分链表]:(https://juejin.cn/post/7028817441731903502)”
var reverseBetween = function (head, left, k) {
    let dummyNode = new ListNode(-1);
    dummyNode.next = head;
    let prev = dummyNode;
    for (let i = 0; i < left - 1; i++) {
        prev = prev.next
    }
    let curr = prev.next;
    for (let i = 0; i < k - 1; i++) {
        const next = curr.next;
        curr.next = next.next;
        next.next = prev.next;
        prev.next = next
    }
    return dummyNode.next
}