Bootstrap

[leetcode] 25. K 个一组翻转链表

题目描述

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

k 是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。

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

示例 1:
在这里插入图片描述

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

示例 2:
在这里插入图片描述

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

提示:

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

进阶:你可以设计一个只用 O ( 1 ) O(1) O(1) 额外内存空间的算法解决此问题吗?

解题方法

此题目是[leetcode] 24. 两两交换链表中的节点的进阶版,没做过这道题的童鞋可以先看这道。

方法一:迭代

既然题目提示只用** O ( 1 ) O(1) O(1)** 额外内存空间,那就不能存储排序结点。具体要怎么实现呢?我简单说一下思路。

第一,我们要做的是每遍历 k k k个结点,就要将 k k k个结点翻转,这只需要三个指针就可以做到。一个是 f r o n t front front指针;一个是 a f t e r after after指针,代表原链表 f r o n t front front后面的结点;一个是 n e x t A f t e r nextAfter nextAfter指针,代表原链表 a f t e r after after后面的结点。为了实现翻转节点,我们只需要将 a f t e r . n e x t after.next after.next指向 f r o n t front front,再将 f r o n t front front指向 a f t e r after after a f t e r after after指向 n e x t A f t e r nextAfter nextAfter,重复上面的步骤直到翻转k个结点。

第二,我们记结果链表的尾结点为 t a i l tail tail。我们需要做的是将 t a i l tail tail指向翻转 k k k个结点后的头结点,其实就是遍历完成后的 f r o n t front front指针。然后再用 t m p T a i l tmpTail tmpTail指针记录翻转 k k k个结点后的尾结点, t a i l . n e x t tail.next tail.next指向 f r o n t front front之后,再将 t a i l tail tail指向 t m p T a i l tmpTail tmpTail

第三,遍历到最后时会出现剩余结点不到 k k k个结点的情况,此时就不需要翻转剩余结点。那我们怎么判断后面有多少结点呢?我们可以设置一个 f a s t fast fast指针,初始时 f a s t fast fast就往后移动 k k k个结点。在下一次遍历k个结点之前,先判断 f a s t fast fast指针是否为空。如果为空,则代表剩余结点不到 k k k个,此时只需要将 t a i l . n e x t tail.next tail.next指向 a f t e r after after(翻转完 k k k个结点后, a f t e r after after刚好指向剩余结点的头结点);若不为空,说明剩余结点不少于 k k k个,则需要按步骤一进行后续结点翻转遍历。

java代码

ListNode结构

public class ListNode {
    public int val;
    public ListNode next;

    public ListNode() {
    }

    public ListNode(int val) {
        this.val = val;
    }

    public ListNode(int val, ListNode next) {
        this.val = val;
        this.next = next;
    }
}

翻转链表方法

public ListNode reverseKGroup(ListNode head, int k) {
    if (head == null || head.next == null || k == 1) {
        return head;
    }
    //fast结点每次都比上k个结点翻转后的头结点多往后遍历k个结点
    ListNode fast = head;
    for (int i = 0; i < k - 1; i++) {
        if (fast != null) {
            fast = fast.next;
        }
    }
    //说明链表没有k个结点,直接返回头结点
    if (fast == null) {
        return head;
    }
    ListNode dummyNode = new ListNode(0, null);
    ListNode tail = dummyNode;
    //每次翻转k个结点后的头结点
    ListNode front = head;
    ListNode after = head.next;
    while (true) {
        //记录每次翻转k个结点后的尾结点
        ListNode tmpTail = null;
        for (int i = 0; i < k - 1; i++) {
            if (i == 0) {
                //翻转k个结点后的尾结点
                tmpTail = front;
            }
            if (fast != null) {
                fast = fast.next;
            }
            //after.next指向front,after和front各往后移动一个结点
            ListNode nextAfter = after.next;
            after.next = front;
            front = after;
            after = nextAfter;
        }
        //当前结果链表的尾结点的next指针指向k个结点翻转后的头结点
        tail.next = front;
        if (fast != null) {
            fast = fast.next;
        }
        if (fast == null) {
            //剩余未遍历的结点不到k个,当前k个结点翻转后的尾结点的next指针指向剩余结点的头结点
            tmpTail.next = after;
            break;
        }
        //下次翻转k个结点之前,先将结果链表的尾结点指向当前翻转k个结点后的尾结点
        tail = tmpTail;
        front = after;
        after = after.next;

    }
    return dummyNode.next;
}

复杂度分析

时间复杂度: O ( N ) O(N) O(N) N N N为链表长度,需要遍历链表一次。
空间复杂度: O ( 1 ) O(1) O(1),只有常数级别的指针数量需要存储。

相似题目

[leetcode] 24. 两两交换链表中的节点


  • 个人公众号
    个人公众号
  • 个人小游戏
    个人小游戏
;