[前端]力扣(hard)K个一组翻转链表

368 阅读4分钟

本文已参与「掘力星计划」,赢取创作大礼包,挑战创作激励金。


K 个一组翻转链表

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

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

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

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

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

示例:

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

image.png

这是力扣的一道困难题 . 虽然我第一次做的时候毫不犹豫看了题解 , 但我还是希望诸君可以先进行思考 , 尽力而为 .


首先 , 翻转链表 , 我们得了解且掌握 .

反转链表

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

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

链表 , 在js中以对象的形式来实现 .

 function ListNode(val, next) {
      this.val = (val===undefined ? 0 : val)
      this.next = (next===undefined ? null : next)
  }

这段创建链表的代码相信各位都能看懂 .

接下来我们开始解决一个普通的翻转链表 .

var reverseList = function(head) {
    //先初始化前驱结点
    let pre = null;
    //初始化目标结点为头结点
    let cur = head;
    while (cur !== null) {
    //用next记录cur的后继结点,在循环中不断可以存着当前结点的后继结点
        let next = cur.next;
        //翻转指针
        cur.next = pre;
        //pre往前走一步
        pre = cur;
        //cur往前走一步
        cur = next;
    }
    //翻转之后 pre就变成了头结点
    return pre
};

回到我们本篇的问题 , K个一组翻转链表 , 我们已经完成了翻转链表 , 剩下的就是K个一组了 .

此时真正的难题来临了 :

1.链表进行分组 .

2.对分组的链表进行判断是否满足翻转的数量 .

3.进行局部翻转

4.思考分组链表翻转后的结点的关系 .


1.分组 翻转

因为已经确认了要翻转的链表长度K , 所以我们选择for循环而不是while

for循环内就是基础的翻转链表操作 .

    let pre = null,cur = head;
 //对该k个链表元素进行反转
    for(let i = 0;i<k;i++){
        let next = cur.next;
        cur.next = pre;
        pre = cur;
        cur = next;
    }
  1. 判断分组
    //K为翻转数
    let p = head;
    //查找长度是否满足反转的数量
    for(let i = 0;i<k;i++){
        if(p == null) return head;
        p = p.next;
    }
  1. 思考结点之间的关系 . 在每次翻转链表时 , 我们始终let了一个next来存着最后一个翻转元素的下一个 , 而局部的翻转只是切断了这个关系 , 但当局部翻转完成后 , head.next已经到了最末尾 , 它指向下一次翻转的第一个点 . cur即是下一个翻转的第一个元素 , pre则为翻转完成链表中最后一个元素 .

  2. 递归 .

该有的判断也有了 , 该进行的翻转也有了 , 剩下的只是由小见大 , 把局部转换为整体 . 递归无疑是最容易想到的一个操作 . 就像遍历二叉树一样 , 递归是如此易于理解 .

最后的代码 .

var reverseKGroup = function(head, k) {
    let pre = null,cur = head;
    let p = head;
    for(let i = 0;i<k;i++){
        if(p == null) return head;
        p = p.next;
    }
    for(let j = 0;j<k;j++){
        let next = cur.next;
        cur.next = pre;
        pre = cur;
        cur = next;
    }
    head.next = reverseKGroup(cur,k);
    return pre;
};

虽然思路上还是容易让人接受 , 但实际在复现思路的过程中也是充满艰辛 . 唯有多次练习 , 从 206. 反转链表 , 92. 反转链表 II , 开始练习 , 地基打好 , 才能将思路转化为代码 , 将思维转化为本能 .

我们一起努力 , 哪有什么天纵奇才 , 只不过是一群秃子在负重前行 .

「欢迎在评论区讨论,掘金官方将在掘力星计划活动结束后,在评论区抽送100份掘金周边,抽奖详情见活动文章」