LeetCode.25K个一组翻转链表详解

问题描述

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

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

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

问题理解

给定一个链表和一个数字 k,任务是将链表中的每 k 个节点做一次翻转,如果链表的长度不是 k 的整数倍,则剩余的节点保持原有顺序。这是一个典型的链表操作问题,涉及到链表的遍历、节点的重新链接等操作。

解题思路1

1. 确定操作单位

在理解了问题之后,首先明确操作的基本单位是“节点组”,每组包含 k 个节点。操作的核心是对这些节点组进行翻转。如果当前组节点不足 k 个,直接跳过该组,保持原样。

2. 理解翻转操作

链表的翻转是基础操作,但在此问题中,我们不是翻转整个链表,而是翻转部分连续的节点。这要求我们能够准确识别每个待翻转的节点组的起始点和终点。

3. 操作过程的细化

为了实现这一操作,我们需要细化操作过程:

  • 定位节点组:从链表头部开始,向后数 k 个节点,标记为一个待翻转的节点组。
  • 执行翻转:对每个节点组执行翻转操作。翻转涉及断开节点的原有连接,然后按逆序重新连接。
  • 处理边界:每次翻转完成后,需要正确处理该节点组与前后节点的连接关系,确保链表的完整性。
4. 算法的选择

为了方便处理链表的头部可能变化的情况,采用了一个虚拟头节点(dummy node)。这个节点在实际链表的头部之前,使得链表头部的处理逻辑与其他部分一致。

5. 连接与继续

完成一个节点组的翻转后,需要将翻转后的尾部(翻转前的头部)连接到下一个节点组的头部。同时,更新操作指针,继续识别和处理下一个节点组。

代码实现

/**
 * Definition for singly-linked list.
 * 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) {
        if (head == nullptr || head->next == nullptr || k == 1) {
            return head;
        }

        ListNode dummy(0);
        dummy.next = head;
        ListNode* prevGroupEnd = &dummy;

        while (true) {
            // 检查是否有足够的节点来进行翻转
            ListNode* current = prevGroupEnd;
            for (int i = 0; i < k && current != nullptr; i++) {
                current = current->next;
            }
            if (current == nullptr)
                break; // 如果不够 k 个,就不进行翻转

            ListNode* groupStart = prevGroupEnd->next;
            ListNode* nextGroupStart = current->next;

            // 翻转当前的 k 个节点
            ListNode* prev = nextGroupStart;
            ListNode* curr = groupStart;
            while (curr != nextGroupStart) {
                ListNode* temp = curr->next;
                curr->next = prev;
                prev = curr;
                curr = temp;
            }

            // 将翻转后的子链表接回主链表
            prevGroupEnd->next = prev;
            prevGroupEnd = groupStart;
        }

        return dummy.next;
    }
};

进阶

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

解题思路

1. 链表遍历和长度计算

首先,我们需要遍历整个链表来确定其长度,这样可以判断出能够完整进行多少次k节点的翻转。这一步是必要的,因为如果链表长度未达到k,则不应进行翻转。

2. 翻转链表的k个节点

对于链表翻转操作,我们通常会使用三个指针:prevcurr(当前节点)、nex(当前节点的下一个节点)。通过这三个指针可以实现链表的局部翻转:

  • prev 指针始终指向当前k个节点组的前一个节点,以便在翻转后将翻转的部分重新连接到主链表上。
  • curr 是当前正在处理的节点,也是每次k组翻转的第一个节点。
  • nex 用于存储curr的下一个节点,确保在翻转过程中不会丢失链表的其余部分。

具体翻转步骤是:对于每个k节点组,我们将curr的下一个节点移到prev之后,这样原来的第二个节点就成了新的第一个节点,重复这个操作k-1次,便完成了一组的翻转。

3. 连接翻转后的部分

每次翻转完成后,需要更新prev指针到当前k组的末尾,这样在下一次循环时,它可以正确地指向下一组待翻转节点组的前一个节点。同时,更新剩余节点计数,减去已翻转的k个节点。

4. 处理不足k个的节点

在遍历链表时,如果发现剩余节点数小于k,则不进行翻转,直接将这些节点连接到已翻转部分的末尾。

5. 虚拟头节点的使用

在处理链表问题时,使用虚拟头节点可以大大简化边界条件的处理,例如当链表头部节点需要被移动或删除时。在这个问题中,虚拟头节点帮助我们更方便地处理头k个节点的翻转,以及最终返回新的头节点。

通过这样的步骤,我们可以实现题目要求的每k个节点一组的翻转,而不足k个的部分保持不变。这种方法的时间复杂度为O(n),空间复杂度为O(1),因为我们没有使用额外的数据结构,仅通过修改节点间的链接来达到翻转的目的。

代码实现

/**
 * Definition for singly-linked list.
 * 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) {
        ListNode* dummy = new ListNode(0);
        dummy->next = head;
        ListNode *prev = dummy, *curr = head, *nex = nullptr;

        // 首先计算链表长度
        int count = 0;
        while (curr) {
            curr = curr->next;
            count++;
        }

        // 当链表中至少有k个节点时进行翻转
        while (count >= k) {
            curr = prev->next;
            nex = curr->next;
            for (int i = 1; i < k; i++) {
                curr->next = nex->next;
                nex->next = prev->next;
                prev->next = nex;
                nex = curr->next;
            }
            prev = curr;
            count -= k;
        }

        ListNode* ret = dummy->next;
        delete dummy; // 释放虚拟头节点
        return ret;
    }
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/751437.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

哈希表 | 哈希查找 | 哈希函数 | 数据结构 | 大话数据结构 | Java

&#x1f64b;大家好&#xff01;我是毛毛张! &#x1f308;个人首页&#xff1a; 神马都会亿点点的毛毛张 &#x1f4cc;毛毛张今天分享的内容&#x1f586;是数据结构中的哈希表&#xff0c;毛毛张主要是依据《大话数据结构&#x1f4d6;》的内容来进行整理&#xff0c;不…

vue的学习--day2

如有错误&#xff0c;烦请指正~ 目录 一、什么是单页面应用程序 二、使用工具&#xff1a;node.js 三、工具链 易错点 一、什么是单页面应用程序 多个组件&#xff08;例如登录、注册等以vue结尾的都叫做组件&#xff09;在一个页面显示&#xff0c;叫单页面应用…

Linux C 程序 【02】创建线程

1.开发背景 上一个篇章&#xff0c;基于 RK3568 平台的基础上&#xff0c;运行了最简单的程序&#xff0c;然而我们使用了 Linux 系统&#xff0c;系统自带的多线程特性还是比较重要的&#xff0c;这个篇章主要描述线程的创建。 2.开发需求 设计实验&#xff1a; 创建一个线程…

极验行为式验证码适配HarmonyOS 鸿蒙SDK下载

现阶段&#xff0c;越来越多的开发者正在积极加入鸿蒙生态系统。随着更多开发者的参与&#xff0c;早在去年9月&#xff0c;极验就成为首批拥有鸿蒙NEXT内测版本和手机系统测试机会的验证码供应商。 为了提高各开发者及企业客户集成鸿蒙版本行为验4.0的效率&#xff0c;方便大家…

Android 通知组

一. 通知组简介 从 Android 7.0&#xff08;API 级别 24&#xff09;开始&#xff0c;您可以在一个组中显示相关通知。如下所示: 图 1. 收起&#xff08;顶部&#xff09;和展开&#xff08;底部&#xff09;的通知组。 注意 &#xff1a;如果应用发出 4 条或更多条通知且未…

大数据平台需要存算分离吗?某保险集团:以 ZBS 优化资源利用率,缩短业务用时超一半

金融机构普遍采用“存算一体”架构支撑基于 Hadoop 框架的大数据平台。而随着金融业务的多元化发展&#xff0c;不同业务对计算和存储的需求差异较大&#xff0c;由于“存算一体”架构共享存储与计算资源&#xff0c;经常会出现资源需求不均衡、资源利用率低下、难以灵活调度等…

工具篇:鸿蒙DevEco Studio5.0版本下载及安装

1、下载中心地址 下载中心 | 华为开发者联盟-HarmonyOS开发者官网&#xff0c;共建鸿蒙生态 2、安装 DevEco Studio支持Windows和macOS系统&#xff0c;下面将针对两种操作系统的软件安装方式分别进行介绍。 Windows环境 运行环境要求 为保证DevEco Studio正常运行&#…

Mysql需要知道的点

目录 一、数据库的三范式是什么 二、Mysql数据库引擎有哪些 三、说说Innodb与MYISAM的区别 四、数据库的事务 五、索引是什么 六、优化手段有哪些 七、简单说一说 drop&#xff0c;delete与truncate的区别 八、什么是视图 九、什么是内连接、左外连接、右外连接&#x…

Ubuntu20.04使用Samba

目录 一、Samba介绍 Samba 的主要功能 二、启动samba 三、主机操作 四、Ubuntu与windows系统中文件互联 五、修改samba路径 一、Samba介绍 Samba 是一个开源软件套件&#xff0c;用于在 Linux 和 Unix 系统上实现 SMB&#xff08;Server Message Block&#xff09;协议…

[行业原型] Web端原型案例:康欣医疗后台管理系统

​医疗管理系统是一个业务复杂&#xff0c;功能庞大的系统&#xff0c;以下为HIS医院管理系统的常见模块&#xff0c;供大家参考。 本周为大家带来Web端原型案例&#xff1a;康欣医疗后台管理系统&#xff0c;先上原型&#xff1a; 完整文档加班主任微信号 添加班主任回复 “1…

ansible常用模块详解

一、Ansible 1.1 简介 Ansible是自动化运维工具&#xff0c;能实现跨主机对应用编排管理部署。 Ansible能批量配置、部署、管理上千台主机&#xff0c;是应用级别的跨主机编排工具。 比如以前需要切换到每个主机上执行的一或多个操作&#xff0c;使用Ansible只需在固定的一…

练习实践:ubuntu18.04安装、配置Nginx+PHP环境,两种配置方式,多站点

参考来源&#xff1a; https://help.aliyun.com/document_detail/464753.html https://www.cnblogs.com/laosan007/p/12803287.html https://blog.csdn.net/qq_55364077/article/details/132207083 【安装同版本7.2的php】 需要知道对应php和nginx的安装版本 需要安装php-fpm…

stl之string

构造函数 void test1() {string s1;//不传参cout << s1 << endl;string s2("123456");cout << s2 << endl;string s3(s2);cout << s3 << endl;string s4(s2, 1, 5);cout << s4 << endl;string s5("123456&quo…

PHP 网络通信底层原理分析

大家好&#xff0c;我是码农先森。 引言 我们日常的程序开发大多数都是以业务为主&#xff0c;很少会接触到底层逻辑。对于我们程序员来说&#xff0c;了解程序的底层运行逻辑&#xff0c;更有助于提升我们对程序的理解。我相信大多数的人&#xff0c;每天基本上都是完成业务…

丝杆支撑座:滚珠丝杆稳定运行的守护者!

丝杆支撑座是丝杆和电机之间连接的重要组成部分&#xff0c;发挥着非常重要的功能。提到丝杆支撑座和滚珠丝杆&#xff0c;很多人都会想到支撑关系&#xff0c;但丝杆支撑座作为滚珠丝杆系统中至关重要的角色&#xff0c;其作用远不止于简单的支撑。 丝杆支撑座安装过程非常简单…

第30课 绘制原理图——放置网络标签

什么是网络标签&#xff1f; 我们在很多电路图中都能看到&#xff0c;为了让图纸更加简洁&#xff0c;并不是每一根导线都要确确实实地画出来。可以在导线悬空的一端添加一个名称标签&#xff0c;接着在另一根导线的悬空一端添加上一个同名的名称标签&#xff0c;那么就可以让…

【自监督-MIM】系列方法学习二

Masked image modeling 是一种训练深度学习模型的技术,尤其是在视觉领域,类似于自然语言处理中的掩码语言建模(Masked Language Modeling)。它通过在输入图像中随机遮挡(或称为掩码)部分区域,然后训练模型来预测这些被遮挡部分的内容,从而提高模型的视觉理解能力。 Ma…

IDEA无法输入中文,怎么破

1.导航栏处&#xff0c;点击help菜单&#xff0c;选择Edit Custom VM Options.. 2.编辑文件&#xff0c;在文件末尾添加&#xff1a; -Drecreate.x11.input.methodtrue 3.保存文件即可&#xff0c;如果还是不行&#xff0c;就关闭所有Idea程序&#xff0c;重新启动Idea

机器学习之集成学习

一&#xff1a;概念 顾名思义集成学习就是用多个其他的算法结合起来使用 对于“其他算法”有同类和同质的区别&#xff0c;同质指的是所用的算法都是同一类型的&#xff0c;比如决策树和神经网络&#xff0c;这种也叫基学习器。反之亦然&#xff0c;但一般使用的是同质的。 …

网络治理新模式:Web3时代的社会价值重构

随着Web3技术的崛起&#xff0c;传统的网络治理模式正在经历革新&#xff0c;这不仅仅是技术的进步&#xff0c;更是对社会价值观念的挑战和重构。本文将深入探讨Web3时代的网络治理新模式&#xff0c;其背后的技术基础、社会影响以及未来的发展方向。 1. 引言 Web3时代&#…