【算法学习】链表篇:链表的常用技巧和操作总结

【算法学习】链表篇:链表的常用技巧和操作总结

算法学习:

https://blog.csdn.net/2301_80220607/category_12922080.html?spm=1001.2014.3001.5482

前言:

在各种数据结构中,链表是最常用的几个之一,熟练使用链表和链表相关的算法,可以让我们在处理很多问题上都更加容易,下面我们就开始通过一些经典例题学习链表相关的算法

1. 链表的常用技巧和常用操作1.1 链表的常用技巧1.画图!!! -> 直观 + 形象 + 便于我们理解

2.引入虚拟“头"结点

便于处理边界情况 方便我们对链表操作3.不要吝啬空间,大胆去定义变量

4.快慢双指针

判环找链表中环的入口找链表中倒数第 n个结点在做链表链表题时,我们尽量把思路画下来,能够更直观的看到问题所在,同时能引入虚拟头节点就引入虚拟头节点,可以帮助我们处理边界情况

链表中最基础的算法之一就是快慢双指针的使用,熟练掌握可以帮助我们解决很多问题

1.2 链表的常用操作1.创建一个新节点 new2.尾插3.头插(逆序链表的题经常会用到)

头插法的具体操作:

以上四步要一步一步来,顺序不能有误

2. 有关链表的经典题型2.1 两数相加2. 两数相加

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

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

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

示例 1:

代码语言:javascript复制输入:l1 = [2,4,3], l2 = [5,6,4]

输出:[7,0,8]

解释:342 + 465 = 807.提示:

每个链表中的节点数在范围 [1, 100] 内0 <= Node.val <= 9题目数据保证列表表示的数字不含前导零我们先来结合一个例子理解一下这里的逆序是如何操作的:

其实将数字逆序比正序容易,数字逆序后,我们按照链表相加时,先加个位,再加十位,依次往后相加,符合加法逻辑,所以如果是正序的话我们反倒还需要先将链表颠倒,让它变成逆序后再操作

我们可以结合代码来看一下这个原理图,首先我们需要定义一个整数t,它的作用就是计算链表同一位置两数的和,其实对应的就是加法中的相同权值的位置相加(比如个位+个位),然后将t的个位放入求和链表中的相同位置,t如果十位不为0,代表的其实是要进位,比如上面例子中4+6=10,那么个位是0,我们将0放入求和链表的第二个节点处,t的十位跟随cur1、cur2一起来到权值更高的下一个节点进行新的相加运算

代码实现:

代码语言:javascript复制class Solution {

public:

ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {

ListNode* cur1=l1,*cur2=l2;

//创建一个虚拟头节点,记录最终返回结果

ListNode* newhead=new ListNode(0);

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);

t/=10;

prev=prev->next;

}

prev=newhead->next;

delete newhead;

return prev;

}

};2.2 两两交换链表中的节点24. 两两交换链表中的节点

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

示例 1:

代码语言:javascript复制输入:head = [1,2,3,4]

输出:[2,1,4,3]示例 2:

代码语言:javascript复制输入:head = []

输出:[]示例 3:

代码语言:javascript复制输入:head = [1]

输出:[1]提示:

链表中节点的数目在范围 [0, 100] 内0 <= Node.val <= 100这种解法就比较简单粗暴了,就是直接创建一个全新的链表,将原链表中的值按要求交换后输入这个链表中,这样做的好处就是思路简单,容易想到,缺点就是空间开销大

代码语言:javascript复制class Solution {

public:

ListNode* swapPairs(ListNode* head) {

if(head==nullptr||head->next==nullptr) return head;

ListNode* newhead=swapPairs(head->next->next);

ListNode* tmp=head->next;

head->next->next=head;

head->next=newhead;

//head->next=tmp->next;

//tmp->next=head;

return tmp;

}

};这种解法就是上面的那种解法,观察上面的图,简单点来说就是还是在原链表上进行操作,记得处理好边界情况,记得创建一个头节点(方便我们找到链表的其实位置,返回值时简单)

2.3 重排链表143. 重排链表

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

代码语言:javascript复制L0 → L1 → … → Ln - 1 → Ln请将其重新排列后变为:

代码语言:javascript复制L0 → Ln → L1 → Ln - 1 → L2 → Ln - 2 → …不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

示例 1:

代码语言:javascript复制输入:head = [1,2,3,4]

输出:[1,4,2,3]示例 2:

代码语言:javascript复制输入:head = [1,2,3,4,5]

输出:[1,5,2,4,3]提示:

链表的长度范围为 [1, 5 * 104]1 <= node.val <= 1000算法原理:

主要思路如图所示,这个题目像让我们做的就是链表头取出一个数字,然后再从链表尾取一个数字直到最后取到中间为止,所以我们可以如上图所示,把链表从中间分为两个,然后将后面的逆序然后合并两个链表即可

代码实现:

代码语言:javascript复制class Solution {

public:

void reorderList(ListNode* head) {

//处理边界情况

if(head->next==nullptr||head->next->next==nullptr) return;

//1. 找到链表的中间节点

ListNode* slow=head,*fast=head;

while(fast&&fast->next)

{

slow=slow->next;

fast=fast->next->next;

}

//2. 把slow后面的部分给逆序 -- 头插法

ListNode* head2=new ListNode(0);

ListNode* cur=slow->next;

slow->next=nullptr; //注意把两个链表给断开

while(cur)

{

ListNode* next=cur->next;

cur->next=head2->next;

head2->next=cur;

cur=next;

}

//3. 合并两个链表 --双指针

ListNode* ret=new ListNode(0);

ListNode* prev=ret;

ListNode* cur1=head,*cur2=head2->next;

while(cur1)

{

//先放第一个链表

prev->next=cur1;

prev=prev->next;

cur1=cur1->next;

//再放第二个链表

if(cur2){

prev->next=cur2;

prev=prev->next;

cur2=cur2->next;

}

}

head=ret->next;

}

};2.4 K个一组翻转链表25. K 个一组翻转链表

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

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

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

示例 1:

代码语言:javascript复制输入:head = [1,2,3,4,5], k = 2

输出:[2,1,4,3,5]示例 2:

代码语言:javascript复制输入:head = [1,2,3,4,5], k = 3

输出:[3,2,1,4,5]提示:

链表中的节点数目为 n1 <= k <= n <= 50000 <= Node.val <= 1000算法原理:

主要的解题方法就是上面的那两步

但是这里也有一些细节需要我们注意:

每一轮逆序结束后,我们在下一轮头插时记得要在返回链表的尾部进行头插在逆序结束后,如果还有剩余的节点记得带上代码实现:

代码语言:javascript复制class Solution {

public:

ListNode* reverseKGroup(ListNode* head, int k) {

//1. 计算链表中节点个数

int n=0;

ListNode* cur=head;

while(cur)

{

n++;

cur=cur->next;

}

int count=n/k;

ListNode* ret=new ListNode(0);

ListNode* prev=ret;

cur=head;

while(count--)

{

ListNode* tmp;

for(int i=0;i

ListNode* next=cur->next;

cur->next=prev->next;

prev->next=cur;

if(i==0) tmp=cur;

cur=next;

}

prev=tmp;

}

while(cur){

prev->next=cur;

prev=prev->next;

cur=cur->next;

}

return ret->next;

}

};3. 总结 以上就是几个经典的链表的例题,通过这几道题,其实我们应该明白的是,要想做好链表相关的题,就一定要把有关链表的基础算法给掌握好,比如如何逆序链表,如何实现头插尾插,如何使用快慢双指针等,把这些基础算法掌握好,然后结合题意,大部分链表题还是能够拿下的

本篇笔记:

感谢各位大佬观看,创作不易,还望各位大佬点赞支持!!!

相关推荐

世俱杯黄牌红牌罚单曝光:一张红牌最高罚款2.13万欧元!
组织机构 – 米兰设计周
office365怎么登陆

组织机构 – 米兰设计周

📅 10-31 👁️ 7111
怎么换win7桌面,win7换桌面的快捷方法
bte365娱乐场

怎么换win7桌面,win7换桌面的快捷方法

📅 07-19 👁️ 6810