【高频面试题】反转链表、 K 个一组翻转链表

article/2025/8/17 4:30:27

文章目录

    • 1.反转链表
      • 题目描述
      • 示例 1:
      • 示例 2:
      • 示例 3:
      • 提示:
      • 解法1
      • 解法2
      • 解法3
    • 2.反转链表 II
      • 题目描述
      • 示例 1:
      • 示例 2:
      • 提示:
      • 解法1
      • 解法2
    • 3. K 个一组翻转链表
      • 题目描述
      • 示例 1:
      • 示例 2:
      • 提示:
      • 解法1
      • 解法2
    • 参考

1.反转链表

题目描述

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

示例 1:

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

示例 2:

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

示例 3:

输入:head = []
输出:[]

提示:

  • 链表中节点的数目范围是 [ 0 , 5000 ] [0, 5000] [0,5000]
  • − 5000 < = N o d e . v a l < = 5000 -5000 <= Node.val <= 5000 5000<=Node.val<=5000
  • 进阶:链表可以选用迭代或递归方式完成反转。你能否用两种方法解决这道题?

解法1

迭代法 O ( n ) O(n) O(n)

  • 反转链表即将所有节点的 n e x t next next指针指向前驱节点。由于是单链表没有前驱指针,因此我们需要维护一个额外的指针保存前驱,同时在改变当前节点的 n e x t next next指针前,不要忘记保存他的后继节点。
  • 时空复杂度:时间复杂度 O ( n ) O(n) O(n)空间复复杂度 O ( 1 ) O(1) O(1)
/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode(int x) : val(x), next(NULL) {}* };*/
class Solution {
public:ListNode* reverseList(ListNode* head) {ListNode *prev = nullptr;ListNode *cur = head;while (cur) {ListNode *next = cur->next;cur->next = prev;prev = cur, cur = next;}return prev;}
};

解法2

递归法 O ( n ) O(n) O(n)

  • 这里是一个难点,首先我们先考虑 r e v e r s e L i s t reverseList reverseList 函数能做什么,它可以翻转一个链表,并返回新链表的头节点,也就是原链表的尾节点。
  • 所以我们可以先递归处理reverseList(head->next),这样我们可以将以head->next为头节点的链表翻转,并得到原链表的尾节点 t a i l tail tail,此时head->next是新链表的尾节点,我们令它的next指针指向head,并将head->next指向空即可将整个链表翻转,且新链表的头节点是 t a i l tail tail
  • 对于样例1 2 3 4 5,我们从2为头节点开始递归,调用reverseList() 得到5 4 3 2。但此时的链表并非是1 5 4 3 2,此时的链表是1->25 4 3 2两部分,也就是头节点1依然指向2,要想访问到2,使用head->next即可。这时我们将2指向1,让1指向nullptr即可实现整个链表的反转。如下图
  • 时空复杂度:时间复杂度 O ( n ) O(n) O(n) 空间复复杂度 O ( n ) O(n) O(n)
    在这里插入图片描述
/**  * Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode(int x) : val(x), next(NULL) {}* };*/
class Solution {
public:ListNode* reverseList(ListNode* head) {if (!head || !head->next) return head;ListNode *tail = reverseList(head->next);head->next->next = head;head->next = nullptr;return tail;}
};

解法3

  • 栈维护实现反转
  • 时空复杂度:时间复杂度 O ( n ) O(n) O(n) 空间复复杂度 O ( n ) O(n) O(n)
/**  * Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode(int x) : val(x), next(NULL) {}* };*/
class Solution {
public:ListNode* reverseList(ListNode* head) {stack<ListNode*> st;while (head) {st.push(head);head = head->next;}ListNode *dummy = new ListNode(-1);ListNode *p = dummy;while (!st.empty()) {ListNode *node = st.top();st.pop();node->next = nullptr;p->next = node;p = p->next;}return dummy->next;}
};

2.反转链表 II

题目描述

给你单链表的头指针 head 和两个整数 leftright ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回反转后的链表 。

示例 1:

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

示例 2:

输入:head = [5], left = 1, right = 1
输出:[5]

提示:

  • 链表中节点的数目为 n n n
  • − 500 < = N o d e . v a l < = 500 -500 <= Node.val <= 500 500<=Node.val<=500
  • 进阶:你可以使用一趟扫描完成反转吗?

解法1

  • 一个很直观的做法便是我们把这三部分分别截取出来再将中间部分反转,最后连接即可
  • 时空复杂度:时间复杂度 O ( n ) O(n) O(n) 空间复复杂度 O ( 1 ) O(1) O(1)
class Solution {
public:void reverseList(ListNode* head) {ListNode *prev = nullptr;ListNode *cur = head;while (cur) {ListNode *next = cur->next;cur->next = prev;prev = cur, cur = next;}}ListNode* reverseBetween(ListNode* head, int left, int right) {ListNode *dummy = new ListNode(-1); // 设置哨兵节点 避免left=1时的讨论dummy->next = head; // 关键修复:连接虚拟头节点到原链表ListNode *p = dummy;// 移动p到left位置的前一个节点for (int i = 0; i < left - 1; i++) {p = p->next;}// 走 right - left + 1步 来到right节点ListNode *rightNode = p;for (int i = 0; i < right - left + 1; i++) {rightNode = rightNode->next;}// 截取链表ListNode *leftNode = p->next;ListNode *curr = rightNode->next;// 切断连接p->next = nullptr;rightNode->next = nullptr;// 反转中间部分链表reverseList(leftNode);// 重新连接链表p->next = rightNode; leftNode->next = curr; return dummy->next;}
};

解法2

思路

  • 举个例子:对于示例1,链表为1 2 3 4 5要求我们反转中间的2 3 4部分,我们要先找到2的前驱节点即将指针移动left-1次,然后再对中间长度为right - left + 1的链表进行反转操作,由于上面的反转链表1我们知道将其反转为4 3 2,此时pre指向4,而p->next指向的节点为2,cur为后半部分链表,因此我们执行p->next->next = cur, p->next = pre
  • 注意left = 1时候我们可以添加一个哨兵节点避免空指针
  • 时空复杂度:时间复杂度 O ( n ) O(n) O(n) 空间复复杂度 O ( 1 ) O(1) O(1)
  • 如下图所示(摘自灵神题解)

在这里插入图片描述

class Solution {
public:ListNode* reverseBetween(ListNode* head, int left, int right) {ListNode *dummy = new ListNode(-1); // 设置哨兵节点 避免left=1时的讨论dummy->next = head; // 关键修复:连接虚拟头节点到原链表ListNode *p = dummy;// 移动p到left位置的前一个节点for (int i = 0; i < left - 1; i++) {p = p->next;}// 反转中间部分 [left, right]ListNode *pre = nullptr;ListNode *cur = p->next;for (int i = 0; i < right - left + 1; i++) {ListNode *nxt = cur->next;cur->next = pre;pre = cur;cur = nxt;}// 重新连接链表// 注意必须先执行第一句p->next->next = cur; // 反转后的尾节点接剩余链表p->next = pre;       // 前半部分接反转后的头节点return dummy->next;}
};

3. 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 n n
  • 1 < = k < = n < = 5000 1 <= k <= n <= 5000 1<=k<=n<=5000
  • 0 < = N o d e . v a l < = 1000 0 <= Node.val <= 1000 0<=Node.val<=1000
  • 进阶:你可以使用一趟扫描完成反转吗?

解法1

思路

  • 题目要求我们将链表k个一组反转,小于k的不反转,对于每一组的内部反转,同反转链表I,重要的是在反转后每个部分的重新连接,这里同反转链表II
  • 时空复杂度:时间复杂度 O ( n ) O(n) O(n) 空间复复杂度 O ( 1 ) O(1) O(1)
class Solution {
public:ListNode* reverseKGroup(ListNode* head, int k) {int n = 0;// 求表长for (ListNode* cur = head; cur; cur = cur->next) {n ++;}ListNode *dummy = new ListNode(-1);dummy->next = head;ListNode *p0 = dummy;ListNode *pre = nullptr;ListNode *cur = head;while (n >= k) { // k个一组反转n -= k;// 同迭代版 反转链表for (int i = 0; i < k; i++) {ListNode *nxt = cur->next;cur->next = pre;pre = cur;cur = nxt;}// 与后面的合并ListNode *nxt = p0->next; // 要保存p0->nextnxt->next = cur;p0->next = pre;p0 = nxt;}return dummy->next;}
};

解法2

思路

  • 递归实现,把每个组看作一个子问题,递归调用
  • 时空复杂度:时间复杂度 O ( n ) O(n) O(n) 空间复复杂度 O ( n ) O(n) O(n)
/*** 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 *p0 = head;for (int i = 0; i < k; i++) { if (!p0) return head; // 不够k个不反转p0 = p0->next;}// 反转链表ListNode *pre = nullptr;ListNode *cur = head;for (int i = 0; i < k; i++) {ListNode *nxt = cur->next;cur->next = pre;pre = cur;cur = nxt;}// 检索下一组head->next = reverseKGroup(cur, k);return pre;}
};

参考

  • 灵神题解-反转链表【基础算法精讲 06】
  • 【25. K 个一组翻转链表】C++ 解法:「递归」

http://www.hkcw.cn/article/RtlgcCDScm.shtml

相关文章

俄侦委会:两起桥梁坍塌均系爆炸引起

俄侦委会:两起桥梁坍塌均系爆炸引起 已展开刑事调查当地时间6月1日,俄罗斯联邦侦查委员会官方发言人彼得连科发布声明称,布良斯克州与库尔斯克州事故相关刑事案件已移交至侦委会侦查总局。彼得连科表示,2025年5月31日22时50分,布良斯克州维戈尼奇-皮利希诺铁路段因爆炸导致…

巴以冲突后已有超5万名儿童死亡 人间地狱中的无辜生命

5月27日,联合国儿童基金会表示,自2023年10月本轮巴以冲突爆发以来,加沙已有超过5万名儿童伤亡,当地被形容为“人间地狱”。英国外交官卡里乌基称,加沙已成为世界上儿童生存最危险的地方。据半岛电视台报道,平均每45分钟就有一名孩子死去。战前,加沙约有230万人口,其中一…

视频监控联网系统GB28181协议中事件通知流程详解以及通知失败常见原因

文章目录 9.11.2 事件通知9.11.2.1 基本要求9.11.2.2 命令流程 国标28181协议事件通知失败常见原因1. 协议规范不符合2. 网络与配置问题 智联视频超融合平台介绍 9.11.2 事件通知 9.11.2.1 基本要求 事件订阅通知满足以下基本要求&#xff1a; a) 事件源接受事件订阅后&…

余承东:我把牛吹在这 鸿蒙智驾第一 引领智能驾驶新时代

2025(第三届)未来汽车先行者大会于5月31日至6月1日在深圳国际会展中心(宝安)举办。华为常务董事、终端BG董事长余承东出席并发表演讲。在谈到智能驾驶时,余承东表示:“鸿蒙的智能驾驶不是第一阵营,而是第一名。我们有信心能持续保持并扩大领先优势。”他还提到,华为曾经…

从翻译后修饰角度解析人工合成途径与底盘细胞的适配性-文献精读136

Compatibility between synthetic pathway and chassis cells from the viewpoint of post-translational modifications 从翻译后修饰角度解析人工合成途径与底盘细胞的适配性 摘要 揭示工程化设计的人工合成途径与底盘细胞整体代谢网络的交互作用及适配性机制是合成生物学研…

中国足球队勇夺世界杯(非虚构) 机器人赛场上的辉煌

中国队大胜德国队,勇夺世界杯。不过这是机器人世界杯。今年3月,“清华火神队”以9:0战胜东道主德国Sweaty队,夺得RoboCup机器人世界杯德国公开赛类人组(成人尺寸组别)的冠军。这支冠军足球队的“球员”是北京加速进化科技有限公司研发的加速T1机器人。不只是踢足球,今年可…

孙思蓓自由式小轮车世界杯夺冠 中国选手包揽佳绩

自由式小轮车世界杯法国站女子决赛于当地时间5月31日落幕,孙思蓓以93.10分的成绩夺冠。邓雅文、周钰薇、夏皇耀和赵淑华也进入了决赛,分别获得了第4、7、8和12名。责任编辑:zhangxiaohua

【 Redis | 完结篇 缓存优化 】

前言&#xff1a;本节包含常见redis缓存问题&#xff0c;包含缓存一致性问题&#xff0c;缓存雪崩&#xff0c;缓存穿透&#xff0c;缓存击穿问题及其解决方案 1. 缓存一致性 我们先看下目前企业用的最多的缓存模型。缓存的通用模型有三种&#xff1a; 缓存模型解释Cache Asi…

深入理解8086汇编:串传送、寻址、循环与引导扇区编程

在计算机底层编程中&#xff0c;汇编语言是不可或缺的工具。今天我们将通过一个8086汇编的引导扇区程序&#xff0c;详细讲解串传送指令、寄存器与寻址方式、循环控制以及标志寄存器等核心知识点。这个程序会在屏幕上显示一段文本和一个数字&#xff08;初始为0&#xff09;&am…

87万元现金遗落高铁车站 20分钟极速寻回

端午佳节,南粤大地人潮涌动,深圳北站迎来了52万人次的出发到达旅客。在这场流动的节日图景中,一场关乎87万元现金的“物品寻亲记”悄然上演。铁路工作人员以20分钟的高效协作,让失散的巨额财物与主人重新团聚,在传统节日里书写下新的温暖刻度。5月31日9时41分,深圳北站客…

德国汉堡一医院发生火灾致3死54伤

△图片来源:HamburgNews德国汉堡市霍恩费尔德区一医院6月1日凌晨发生火灾,造成3名患者死亡、54人受伤。目前,火灾发生原因仍在调查中。据德新社报道,大火发生在该医院专门诊治老年患者的科室一楼,并蔓延至二楼。除3人死亡外,还有两名伤者生命垂危,另有16人重伤、36人轻伤…

女子吃了半根甘蔗确诊美女病 颞下颌关节紊乱

23岁的杭州姑娘小吴自从两个月前啃了半根甘蔗后,嘴巴忽然张不开了。她只能吃软的、流质的食物,一嚼就痛,已经两个月没能好好吃饭了。在杭州市中医院针灸康复科的诊室里,小吴愁眉苦脸,因为疼痛,说话也有些痛苦。磁共振检查结果显示,小吴颞下颌关节盘不可复性前移位,属于…

长春一龙舟S型走位直冲岸边 引发游客欢笑

5月31日,正值端午佳节,长春2025端午龙舟赛在伊通河上拉开帷幕。比赛河畔长约300米,挤满了前来观赛的游客。上午11时左右,随着哨声响起,两条龙舟敲鼓出击。1号龙舟一马当先,几秒钟内便遥遥领先。然而,此时岸边传来阵阵笑声,4号龙舟驶离起点后竟然走起了S型路线。许多游客…

孙中山长孙女在美去世 享年103岁 家人举办追思会

2025年5月,孙中山的长孙女孙穗瑛在美国加州举行了追思会。孙穗瑛于2025年3月24日在美国去世,享年103岁。家人为她举办了追思会,回顾了她的一生。孙穗瑛1922年1月16日出生于中国广州,父亲是孙中山长子、时任广州市长的孙科,母亲为陈淑英。她的两位兄长孙治平、孙治强及妹妹…

Blender的一些设置

1. 将Blender长度单位改为毫米(mm), 并设置guides Grid的缩放系数&#xff0c;避免网格不见了。 2. 布尔操作的(Apply)应用按钮在哪里&#xff1f;好吧&#xff0c;在这里&#xff1a; 可以按下 CTRL A 快捷键。 3. 移动、旋转、缩放快捷键: G&#xff0c;R&#xff0c;S

34岁姑娘去世捐出能用的器官 爱与希望的传递

“别人救过我的命,我也想帮帮别人!”这是34岁的武汉姑娘宁馨生前最常说的一句话。10个月前,一场肾移植手术将她从尿毒症的死亡边缘拉回。5月30日凌晨4时50分,她的生命定格,却以另一种方式重启——捐出肝脏、心脏和一对角膜,至少让3人重获新生。手术开始前,全体医务人员面…

强降雨致云南一道路中断 有游客被困 丙中洛成“孤岛”

端午节期间,云南怒江到丙察察G219路段因持续降雨引发多处塌方。5月31日,多名网友在社交平台发帖称,219国道云南贡山县段因泥石流导致前往丙中洛镇的交通中断。一位丙中洛镇民宿老板表示,他在5月30日从自家民宿到贡山县办事,由于大雨导致道路中断,无法返回。他提到,丙中洛…

中国队亚锦赛女子接力摘金 收官日四金闪耀

第26届亚洲田径锦标赛在韩国龟尾市落幕,中国队在最后一个比赛日共收获4枚金牌。5月31日,中国队选手朱俊颖、陈妤颉、梁小静、李玉婷在女子4x100米接力决赛中以43秒28的成绩夺得冠军。同日,陈妤颉在女子200米决赛中以22秒97的成绩夺冠,并创造个人最佳成绩,李玉婷获得季军。…

尊界S800上市1小时大定过1000辆 超预期热销

华为与江淮联合推出的鸿蒙智行系列最贵车型尊界S800正式上市。这款瞄准迈巴赫和劳斯莱斯的超豪华车型,起售价定为70.8万元,最高版本为101.8万元,相比预售时喊出的100至150万元的价格范围更为务实。华为与江淮希望尊界S800能在竞争激烈的新车市场中抢占奔驰S、宝马7系和奥迪A…

比亚迪韩冰谈智驾系统与安全 构建全方位安全保障

在有条件自动驾驶模式下,安全是产业技术的核心和瓶颈。当智能驾驶系统开始接管方向盘时,必须确保安全无虞。为此,需要采用冗余设计、交互安全策略和协同安全设计等方法来构建一个全面的安全体系。6月1日,在未来汽车先行者大会智能网联汽车商业化论坛上,比亚迪新技术研究院…