Offer必备算法12_链表_五道力扣题详解(由易到难)

目录

①力扣2. 两数相加

解析代码

②力扣24. 两两交换链表中的节点

解析代码

③力扣143. 重排链表

解析代码

④力扣23. 合并 K 个升序链表

解析代码1(小根堆优化)

解析代码2(递归_归并)

⑤力扣25. K 个一组翻转链表

解析代码

本篇完。


①力扣2. 两数相加

2. 两数相加

难度 中等

给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。

请你将两个数相加,并以相同形式返回一个表示和的链表。

你可以假设除了数字 0 之外,这两个数都不会以 0 开头。

示例 1:

输入:l1 = [2,4,3], l2 = [5,6,4]
输出:[7,0,8]
解释:342 + 465 = 807.

示例 2:

输入:l1 = [0], l2 = [0]
输出:[0]

示例 3:

输入:l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9]
输出:[8,9,9,9,0,0,0,1]

提示:

  • 每个链表中的节点数在范围 [1, 100] 内
  • 0 <= Node.val <= 9
  • 题目数据保证列表表示的数字不含前导零
/*** 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* addTwoNumbers(ListNode* l1, ListNode* l2) {}
};

解析代码

        两个链表都是逆序存储数字的,即两个链表的个位数、十位数等都已经对应,可以直接相加。 在相加过程中要注意是否产生进位,产生进位时需要将进位和链表数字一同相加。如果产生进位的位置在链表尾部,即答案位数比原链表位数长一位,还需要再new一个结点储存最高位。

/*** 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* addTwoNumbers(ListNode* l1, ListNode* l2) {ListNode *head = new ListNode(0);ListNode *ret = head;ListNode *cur1 = l1, *cur2 = l2;int carry = 0; // 进位while(cur1 || cur2 || carry){int val = carry;if(cur1){val += cur1->val;cur1 = cur1->next;}if(cur2){val += cur2->val;cur2 = cur2->next;}head->next = new ListNode(val % 10);head = head->next;carry =  val / 10;}return ret->next;}
};

②力扣24. 两两交换链表中的节点

24. 两两交换链表中的节点

难度 中等

给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。

示例 1:

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

示例 2:

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

示例 3:

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

提示:

  • 链表中节点的数目在范围 [0, 100] 内
  • 0 <= Node.val <= 100
/*** 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* swapPairs(ListNode* head) {}
};

解析代码

递归法在下面链接讲过:

Offer必备算法07_递归_五道力扣题详解(由易到难)-CSDN博客

        迭代法就是自己画图,不要吝啬定义指针,直接定义四个指针,在前面new一个头结点视为prev,让cur和next1交换,然后四个指针像后走,结束条件是cur或者next1为空。

/*** 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* swapPairs(ListNode* head) {ListNode *newHead = new ListNode(0);if(head == nullptr || head->next == nullptr)return head;// newHead -> 1 -> 2 -> 3// 1和2换 -> cur和next1换// prev -> cur -> next1 -> next2// cur -> prev -> next1 -> next2ListNode *prev=newHead, *cur=head, *next1=head->next, *next2=next1->next;while(cur && next1){prev->next = next1;next1->next = cur;cur->next = next2;prev = cur;cur = next2;if(cur)next1 = cur->next;if(next1)next2 = next1->next;}cur = newHead->next;delete newHead;return cur;// 递归法// if(head == nullptr || head->next == nullptr)//     return head;// ListNode* tmp = swapPairs(head->next->next); // 把两个结点之外的看成另一部分// head->next->next = head;// auto ret = head->next; // 保存一下要返回的结点// head->next = tmp;// return ret;}
};

③力扣143. 重排链表

难度 中等

给定一个单链表 L 的头节点 head ,单链表 L 表示为:

L0 → L1 → … → Ln - 1 → Ln

请将其重新排列后变为:

L0 → Ln → L1 → Ln - 1 → L2 → Ln - 2 → …

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

示例 1:

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

示例 2:

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

提示:

  • 链表的长度范围为 [1, 5 * 10^4]
  • 1 <= node.val <= 1000
/*** 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:void reorderList(ListNode* head) {}
};

解析代码

在C语言讲的数据结构里讲过这三道OJ:

206. 反转链表

876. 链表的中间结点

21. 合并两个有序链表

这道题就是先找到中间结点,分为两部分链表,然后翻转后部分链表,最后合并两个链表:

/*** 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:void reorderList(ListNode* head) {if(head==nullptr || head->next==nullptr || head->next->next==nullptr)return; // 处理边界情况ListNode *fast = head, *slow = head;while(fast && fast->next) // 找到中间结点{slow = slow->next;fast = fast->next->next;}ListNode* slowNext = slow->next;slow->next = nullptr; // 两个链表断开slow = slowNext;ListNode *reverseList = nullptr;while(slow) // 中间结点后面的头插到翻转链表{slowNext = slow->next;slow->next = reverseList;reverseList = slow;slow = slowNext;}ListNode* tmp = head; // 合并两个链表ListNode *cur1 = head->next, *cur2 = reverseList;while(cur1 && cur2){tmp->next = cur2;tmp = tmp->next;cur2 = cur2->next;tmp->next = cur1;cur1 = cur1->next;tmp = tmp->next;}}
};

④力扣23. 合并 K 个升序链表

23. 合并 K 个升序链表

 难度 困难

给你一个链表数组,每个链表都已经按升序排列。

请你将所有链表合并到一个升序链表中,返回合并后的链表。

示例 1:

输入:lists = [[1,4,5],[1,3,4],[2,6]]
输出:[1,1,2,3,4,4,5,6]
解释:链表数组如下:
[1->4->5,1->3->4,2->6
]
将它们合并到一个有序链表中得到。
1->1->2->3->4->4->5->6

示例 2:

输入:lists = []
输出:[]

示例 3:

输入:lists = [[]]
输出:[]

提示:

  • k == lists.length
  • 0 <= k <= 10^4
  • 0 <= lists[i].length <= 500
  • -10^4 <= lists[i][j] <= 10^4
  • lists[i] 按 升序 排列
  • lists[i].length 的总和不超过 10^4
/*** 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* mergeKLists(vector<ListNode*>& lists) {}
};

解析代码1(小根堆优化)

        之前写过合并两个有序链表的题,此题暴力解法就是依次合并两个有序链表,合并K次。时间复杂度是O(N*K^2),是N^3级别的。

        在暴力解法基础上可以使用小根堆优化,合并 K 个升序链表时,选择 K 个链表中,头结点值最小的那⼀个的插入到新链表,时间复杂度是O(N*K*logK)。下面是小根堆优化的代码:

/*** 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 {struct cmp{bool operator()(const ListNode* l1, const ListNode* l2){return l1->val > l2->val;}};public:ListNode* mergeKLists(vector<ListNode*>& lists) {priority_queue<ListNode*, vector<ListNode*>, cmp> heap;for(auto& e : lists) // 所有头结点进入小根堆{if(e)heap.push(e);}ListNode* newHead = new ListNode(0);ListNode* tmp = newHead;while(!heap.empty()){   // 尾插堆顶结点到新链表,再把旧链表下个结点插入小根堆ListNode* cur = heap.top();heap.pop();tmp->next = cur;tmp = cur;if(cur->next){heap.push(cur->next);}}tmp = newHead->next;delete newHead;return tmp;}
};

解析代码2(递归_归并)

  1. 递归出口:如果当前要合并的链表编号范围左右值相等,无需合并,直接返回当前链表。
  2. 应用二分思想,等额划分左右两段需要合并的链表,使这两段合并后的长度尽可能相等。
  3. 对左右两段分别递归,合并[left,right]范围内的链表。
  4. 最后调用合并两个链表函数进行合并。
/*** 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* mergeKLists(vector<ListNode*>& lists) {return merge(lists, 0, lists.size() - 1);}ListNode* merge(vector<ListNode*>& lists, int left, int right){if(left > right) // 区间不存在return nullptr;if(left == right) // 只有一个链表return lists[left];int mid = (left + right) >> 1;ListNode* list1 = merge(lists, left, mid);ListNode* list2 = merge(lists, mid+1, right);// 上面递归得到一个/两个链表return mergeTwoLists(list1, list2);}ListNode* mergeTwoLists(ListNode* list1, ListNode* list2){if(list1 == nullptr)return list2;if(list2 == nullptr)return list1;if(list1->val <= list2->val){list1->next = mergeTwoLists(list1->next, list2);return list1;}else{list2->next = mergeTwoLists(list2->next, list1);return list2;}}
};

⑤力扣25. K 个一组翻转链表

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) 额外内存空间的算法解决此问题吗?

/*** 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) {}
};

解析代码

        可以把链表按 K 个为一组进行分组,组内进行反转,并且记录反转后的头尾结点,使其可以和前后连接起来。先求出⼀共需要逆序多少组(假设逆序 n 组),然后重复 n 次长度为 k 的链表的逆序即可。

/*** 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) {int n = 0;ListNode* cur = head;while(cur){cur = cur->next;++n;}n /= k;cur = head;ListNode* newHead = new ListNode();ListNode* prev = newHead;while(n--){ListNode* tmp = cur;int cnt = k;while(cnt--){ListNode* prevNext = cur->next;cur->next = prev->next; // cur头插到prevprev->next = cur;cur = prevNext;}prev = tmp;}prev->next = cur;cur = newHead->next;delete newHead;return cur;}
};

本篇完。

下一篇动态规划的类型的路径dp类型的OJ。

下下篇数据结构类型的是哈希表类型的OJ。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MongoDB开启事务

MongoDB开启事务 配置单节点。到路径C:\Program Files\MongoDB\Server\4.0\bin 使用记事本以管理员权限打开文件mongod.cfg添加如下配置&#xff1a; replication:replSetName: rs02. 重启MongoDB服务 3. 重启后执行命令 rs.initiate()

【蓝牙协议栈】【经典蓝牙】【BLE蓝牙】蓝牙协议规范(射频、基带链路控制、链路管理)

目录 1. 蓝牙协议规范&#xff08;射频、基带链路控制、链路管理&#xff09; 1.1 射频协议 1.2 基带与链路控制协议 1.3 链路管理器 1. 蓝牙协议规范&#xff08;射频、基带链路控制、链路管理&#xff09; 蓝牙协议是蓝牙设备间交换信息所应该遵守的规则。与开放系…

vue2 div滚动条下拉到底部时触发事件(懒加载) 超级简易版本的懒加载

文章目录 导文文章重点内容效果展示&#xff1a;代码展示这些方法适用于哪些场景 总结 导文 vue2 div滚动条下拉到底部时触发事件(懒加载) 超级简易版本的懒加载 文章重点 内容效果展示&#xff1a; 当div拉到底部的时候&#xff1a; 编辑器返回&#xff1a; 代码展示 在…

【黑马程序员】Python初始

初始Python Python应用场景 什么是编程语言 安装Python开发环境 Python官网 选择Download 选择对应的Python版本和所要下载的操作系统 下载后直接下一步下一步即可安装成功 测试安装效果 重命名python命令让其使用Python3 echo alias python"python3" >…

登录校验认证

会话技术 会话&#xff1a;用户打开浏览器&#xff0c;访问web服务器的资源&#xff0c;会话建立&#xff0c;直到有一方断开连接&#xff0c;会话结束。在一次会话中可以包含多次请求和响应。 会话跟踪&#xff1a; 一种维护浏览器状态的方法&#xff0c;服务器需要识别多次请…

Aop注解+Redis解决SpringBoot接口幂等性(源码自取)

目录 一、什么是幂等性&#xff1f; 二、哪些请求天生就是幂等的&#xff1f; 三、为什么需要幂等 1.超时重试 2.异步回调 3.消息队列 四、实现幂等的关键因素 关键因素1 关键因素2 五、引入幂等性后对系统的影响 六、Restful API 接口的幂等性 实战Aop注解redis解…

计算机网络——计算机网络的性能

计算机网络——计算机网络的性能 速率带宽吞吐量时延时延宽带积往返时间RTT利用率信道利用率网络利用率 我们今天来看看计算机网络的性能。 速率 速率这个很简单&#xff0c;就是数据的传送速率&#xff0c;也称为数据率&#xff0c;或者比特率&#xff0c;单位为bit/s&#…

神经网络 梯度与神经元参数w、b关系;梯度与导数关系

参考&#xff1a;https://blog.csdn.net/weixin_44259490/article/details/90295146 视频&#xff1a;https://www.bilibili.com/video/BV1a14y167vh 概念 梯度与w的关系可以用梯度下降公式来表示&#xff1a;ww−α ∂ c o s t ∂ w \frac{\partial cost}{\partial w} ∂w∂…

vs创建asp.net core webapi发布到ISS服务器

打开服务器创建test123文件夹&#xff0c;并设置共享。 ISS配置信息&#xff1a; 邮件网站&#xff0c;添加网站 webapi asp.net core发布到ISS服务器网页无法打开解决方法 点击ISS Express测试&#xff0c;可以成功打开网页。 点击生成&#xff0c;发布到服务器 找到服务器IP…

idm对比aria2哪个好 aria2和idm哪个快 Aria2和IDM的原理

一、idm对比aria2哪个好 下面对aria2和idm进行对比&#xff0c;看看哪款更好。 idm: 优势&#xff1a; 1&#xff09;可将下载速度提升5倍以上&#xff1b; 2&#xff09;界面友好&#xff0c;操作简便&#xff1b; 3&#xff09;支持多个主流的浏览器&#xff1b; 4&am…

基于Vue的娱讯移动端APP前端设计与实现

目 录 摘 要 Abstract 引 言 1绪论 1.1课题背景及目的 1.1.1移动端APP发展简介 3 1.1.2移动端APP的优势 3 1.2前端开发相关技术 1.2.1前端开发工具介绍 3 1.2.2 前端开发相关技术介绍 4 1.3本章小结 2系统分析 2.1功能需求分析 2.2系统工作流程 2.3本章小结 3系统设…

【论文阅读】Segment Anything论文梳理

Abstract 我们介绍了Segment Anything&#xff08;SA&#xff09;项目&#xff1a;新的图像分割任务、模型和数据集。高效的数据循环采集&#xff0c;使我们建立了迄今为止最大的分割数据集&#xff0c;在1100万张图像中&#xff0c;共超过10亿个掩码。 该模型被设计和训练为可…

一例APC注入型病毒分析

概述 这个病毒通过可移动存储介质传播&#xff0c;使用了应用层APC注入和dga域名技术&#xff0c;整个执行过程分为4个阶段&#xff0c;首先从资源节中解密出一段shellcode和一个PE&#xff0c;执行shellcode&#xff0c;创建一个同名的傀儡进程&#xff0c;将解密出来的PE注入…

2024蓝桥杯每日一题(归并排序)

一、第一题&#xff1a;火柴排队 解题思路&#xff1a;归并排序 重点在于想清楚是对哪个数组进行归并排序求逆序对 【Python程序代码】 from math import * n int(input()) a list(map(int,input().split())) b list(map(int,input().split())) na,nb [],[] for …

【嵌入式】字体极限瘦身术:Fontmin在嵌入式UI中的魔法应用(附3500常用汉字)

1. 概述 在嵌入式系统的用户界面&#xff08;UI&#xff09;设计中&#xff0c;字体的选择和优化至关重要。一个恰当的字体不仅能够提升用户体验&#xff0c;还能彰显产品特色。然而&#xff0c;由于嵌入式设备常常受限于存储空间和处理能力&#xff0c;大型字体文件可能成为性…

LeetCode的使用方法

LeetCode的使用方法 一、LeetCode是什么&#xff1f;1.LeetCode简介2.LeetCode官网 二、LeetCode的使用方法1.注册账号2.力扣社区力扣编辑器 2.1 讨论发起讨论参与讨论关注讨论 2.2 文章撰写文章关注文章 3.力扣面试官版测评面试招聘竞赛 4.力扣学习LeetBook 书架我的阅读猜您喜…

支付宝开放平台证书验签生成签名接入方式的操作流程之公钥证书,密钥证书的生成

#小李子9479# 调用支付宝接口的安全验证方式均使用sign_type为RSA2的方式&#xff0c;有两种 1。密钥模式&#xff1a;应用公钥、应用私钥、平台公钥生成签名和验签方式 2。证书模式&#xff1a;支付宝根证书、支付宝公钥证书、应用公钥证书、应用私钥&#xff0c;采用RSA20…

【2024.3.8练习】[2015 国 AC] 穿越雷区

题目描述 题目分析 最短步数问题&#xff0c;采用BFS算法即可。 我的代码 #include <iostream> #include <algorithm> #include <queue> #include <cmath> using namespace std; int n; int ans; int flag; const int max_n 102; char map[max_n][m…

Qt初识 - 编写Hello World的两种方式 | 对象树

目录 一、通过图形化方式&#xff0c;在界面上创建出一个控件 二、通过代码方式&#xff0c;创建Hello World 三、Qt 内存泄漏问题 (一) 对象树 一、通过图形化方式&#xff0c;在界面上创建出一个控件 创建项目后&#xff0c;打开双击forms文件夹中的ui文件&#xff0c;可…

【java基础】异常处理机制

目录 1、异常体系介绍 1.1、异常是什么? 1.2、运行时异常和编译时异常的区别? 2、异常的用法 2.1、捕获异常 2.2、异常中的常见方法 2.3、抛出异常 2.4、自定义异常 1、异常体系介绍 1.1、异常是什么? java异常是指在程序运行时可能出现的一些错误&#xff0c;如&am…