当前位置: 首页 > news >正文

算法沉淀——链表(leetcode真题剖析)

在这里插入图片描述

算法沉淀——链表

  • 01.两数相加
  • 02.两两交换链表中的节点
  • 03.重排链表
  • 04.合并 K 个升序链表
  • 05.K个一组翻转链表

链表常用技巧

1、画图->直观形象、便于理解

2、引入虚拟"头节点"

3、要学会定义辅助节点(比如双向链表的节点插入)

4、快慢双指针(判断链表是否有环、找到环的入口、找链表中倒数第n个节点等)

链表常用操作

1、创建新节点

2、头插(比如逆序链表)

3、尾插

01.两数相加

题目链接:https://leetcode.cn/problems/add-two-numbers/

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

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

你可以假设除了数字 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) {ListNode* cur1=l1, *cur2 =l2;ListNode* newhead=new ListNode();ListNode* prev=newhead;int t=0;while(cur1||cur2||t){if(cur1){t+=cur1->val;cur1=cur1->next;}if(cur2){t+=cur2->val;cur2=cur2->next;}prev->next=new ListNode(t%10);prev=prev->next;t/=10;}prev=newhead->next;delete newhead;return prev;}
};

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

题目链接:https://leetcode.cn/problems/swap-nodes-in-pairs/

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

示例 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) {if(!head||!head->next) return head;ListNode* newhead=new ListNode();newhead->next=head;ListNode* prev=newhead,*cur=prev->next,*next=cur->next,*nnext=next->next;while(cur&&next){prev->next=next;next->next=cur;cur->next=nnext;prev=cur;cur=nnext;if(cur) next=nnext->next;if(next) nnext=next->next;}prev=newhead->next;delete newhead;return prev;}
};

03.重排链表

题目链接:https://leetcode.cn/problems/reorder-list/

给定一个单链表 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 * 104]
  • 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) {if(!head||!head->next||!head->next->next) return;ListNode* slow=head,*fast=head;while(fast&&fast->next){slow=slow->next;fast=fast->next->next;}ListNode* newhead=new ListNode();ListNode* cur=slow->next;slow->next=nullptr;while(cur){ListNode* next=cur->next;cur->next=newhead->next;newhead->next=cur;cur=next;}ListNode* ret=new ListNode();ListNode* prev=ret;ListNode* cur1=head,*cur2=newhead->next;while(cur1){prev->next=cur1;cur1=cur1->next;prev=prev->next;if(cur2){prev->next=cur2;cur2=cur2->next;prev=prev->next;}}delete newhead;delete ret;}
};

04.合并 K 个升序链表

题目链接:https://leetcode.cn/problems/merge-k-sorted-lists/

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

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

示例 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

思路1

这里最简单也是比较高效的一种写法,就是使用一个小根堆,将这k个链表的头节点放进去,逐个将堆顶的最小链表节点拼接,再逐个入堆,最后就可以将k个有序链表完成拼接

代码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 {struct comp{bool operator()(const ListNode* l1,const ListNode* l2){return l1->val>l2->val;}};
public:ListNode* mergeKLists(vector<ListNode*>& lists) {priority_queue<ListNode*,vector<ListNode*>,comp> heap;for(auto li:lists) if(li) heap.push(li);ListNode* ret=new ListNode();ListNode* prev=ret;while(!heap.empty()){ListNode* t=heap.top();heap.pop();prev->next=t;prev=t;if(t->next) heap.push(t->next);}prev=ret->next;delete ret;return prev;}
};

思路2

第二种就是使用分治思想中的归并,也可以同样效率完成合并

代码

/*** 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* l1=merge(lists,left,mid);ListNode* l2=merge(lists,mid+1,right);return mergeTlist(l1,l2);}ListNode* mergeTlist(ListNode* l1,ListNode* l2){if(l1==nullptr) return l2;if(l2==nullptr) return l1;ListNode* head=new ListNode();ListNode* cur1=l1,*cur2=l2,*prev=head;head->next=nullptr;while(cur1&&cur2){if(cur1->val<=cur2->val){prev=prev->next=cur1;cur1=cur1->next;}else{prev=prev->next=cur2;cur2=cur2->next;}}if(cur1) prev->next=cur1;if(cur2) prev->next=cur2;return head->next;}
};

05.K个一组翻转链表

题目链接:https://leetcode.cn/problems/reverse-nodes-in-k-group/

给你链表的头节点 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

思路

这里我们可以先计算要翻转的子链表个数,也就是要反转的次数,再使用头插法逐个翻转拼接即可

代码

/*** 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;ListNode* newhead=new ListNode();ListNode* prev=newhead;cur=head;for(int i=0;i<n;++i){ListNode* tmp=cur;for(int j=0;j<k;++j){ListNode* next=cur->next;cur->next=prev->next;prev->next=cur;cur=next;}prev=tmp;}prev->next=cur;cur=newhead->next;delete newhead;return cur;}
};
http://www.lryc.cn/news/299312.html

相关文章:

  • Flink从入门到实践(一):Flink入门、Flink部署
  • python分离字符串 2022年12月青少年电子学会等级考试 中小学生python编程等级考试二级真题答案解析
  • Excel练习:折线图突出最大最小值
  • 鸿蒙(HarmonyOS)项目方舟框架(ArkUI)之MenuItem组件
  • Mockito测试框架中的方法详解
  • Atcoder ABC339 A - TLD
  • 企业级DevOps实战
  • C++中的new和delete
  • rtt设备io框架面向对象学习-dac设备
  • 腾讯云幻兽帕鲁服务器配置怎么选择合适?
  • 796. 子矩阵的和
  • 如何在 Python 中处理 Unicode
  • CSDN文章导出PDF整理状况一览
  • jmeter-05变量(用户定义变量,用户参数,csv文档参数化)
  • CSS之水平垂直居中
  • 2.8日学习打卡----初学RabbitMQ(三)
  • Unity学习笔记(零基础到就业)|Chapter02:C#基础
  • 容器化的基础概念:不可变基础设施解释:将服务器视为乐高积木,而非橡皮泥。
  • 智胜未来,新时代IT技术人风口攻略-第二版(弃稿)
  • Git分支和迭代流程
  • 数据库管理-第150期 Oracle Vector DB AI-02(20240212)
  • MySQL双写机制
  • uniapp的配置和使用
  • 【ES】--Elasticsearch的分词器深度研究
  • 【Langchain Agent研究】SalesGPT项目介绍(三)
  • Java安全 URLDNS链分析
  • 【网站项目】026校园美食交流系统
  • 使用raw.gitmirror.com替换raw.githubusercontent.com以解决brew upgrade python@3.12慢的问题
  • 深度学习的进展
  • [高性能] - 缓存架构