算法——链表(1)

在这里插入图片描述

T04BF

👋专栏: 算法|JAVA|MySQL|C语言

🫵 小比特 大梦想

此篇文章与大家分享链表专题的第一部分
如果有不足的或者错误的请您指出!

1.链表常用技巧总结

1.1引入虚拟头结点

在力扣上,基本提供的链表题目都是"无头的",但是针对无头链表,我们最怕的就是参数链表是一个空链表,就容易出现对null的访问.当我们引入虚拟头结点后,我们并不关心这个"头结点"里面放的是什么值,而这个节点只是起到一个哨兵的左右
举一个例子,如果我们要合并两个链表,引入第三链表将两个链表合并
在没有引入虚拟头结点的情况下:
在这里插入图片描述
那么我们的第三个链表就要先判断是否已经有节点了??如果有,直接插在后面即可;如果没有就要将插入的节点作为头结点
但是如果我们引入虚拟头结点
在这里插入图片描述
此时我们就不需要判断了,只需要将两个链表的节点依次插入到newHead后面即可
引入虚拟头结点的优势还有很多,我们在后面的题目中更能感受到

1.2不要吝啬空间

在链表专题,如果我们吝啬空间,有时候会给我们带来很大麻烦
举个例子:
我们通常会做到这样一种题目:在prve和prev.next节点之间插入cur节点
在这里插入图片描述
我们通常会这样做:
①cur.next = prev.next
②prev.next.prev = cur
③prev.next = cur
④cur.prev = prev
这四步操作中,前两步是一定要在后两个之前的
但是如果我们直接将prev.next用用一个临时节点存储起来:
在这里插入图片描述
这是就没必要去担心什么顺序问题了,直接针对三个节点进行操作即可

1.3快慢双指针的利用

快慢双指针在链表操作中经常遇到,而最常用的是我们下面这三种操作,最重要的是这三种操作往往只是作为一道题目里面的一小部分存在

1.3.1判断链表是否有环

题目:判断链表是否有环
在这道题中,我们定义一个fast指针和slow指针,fast指针一次走两步,slow指针一次走两步,那么如果链表没环,那么fast指针一定会走到null,如果链表没环,那么fast指针和slow指针一定会在环的某一点相遇
代码呈现:

public class Solution {public boolean hasCycle(ListNode head) {ListNode fast = head;ListNode slow = head;while(fast != null && fast.next != null){fast = fast.next.next;slow = slow.next;if(fast == slow){return true;}}return false;}
}

1.3.2找到链表环的入口

题目:找到环的入口
在上一题的基础上,如果链表有环,我们需要找到环的入口
实际上这道题有一个数学推导出来的结论:在fast指针和slow指针相遇点,此时这个相遇点距离环的入口,与起跑点距离环的入口,距离是一样的
在这里插入图片描述
即蓝色路程和红色路程是一样的
那么我们再定义一个节点node在起跑点位置,slow和node同时移动,二者相遇点就是环的入口
代码呈现:

public class Solution {private ListNode isCircle(ListNode head){ListNode fast = head;ListNode slow = head;while(fast != null && fast.next != null){fast = fast.next.next;slow = slow.next;if(fast == slow){return slow;}}return null;}public ListNode detectCycle(ListNode head) {ListNode node = isCircle(head);if(node == null || node.next == null){return null;}ListNode cur = head;while(cur != node){cur = cur.next;node = node.next;}return cur;}
}

1.3.3删除链表的倒数第k个节点

同样利用双指针,我们让fast指针先走k步,此时fast和slow之间的距离就是k;接着让fast和slow指针同时走,直到fast为null,此时slow和空节点的距离就是k,即为倒数第k个节点
代码呈现:

class Solution {public ListNode removeNthFromEnd(ListNode head, int n) {if(head == null || head.next == null){return null;}ListNode node = new ListNode(-1,head);//解决头结点被删的情况ListNode fast = node;ListNode slow = node;while(n > 0){n--;fast = fast.next;}while(fast.next != null){fast = fast.next;slow = slow.next;}slow.next = slow.next.next;return node.next;}
}

1.3.4寻找中间节点

题目:链表的中间节点
固定套路:定义fast指针和slow指针,fast一次走两步,slow一次走一步,当fast为空或者fast.next 为空时,slow所在位置就是中间节点的位置
代码:

class Solution {public ListNode middleNode(ListNode head) {ListNode fast = head.next;ListNode slow = head;while(fast != null){if(fast.next != null){fast = fast.next.next;}else{fast = fast.next;}slow = slow.next;}return slow;}
}

1.4反转链表

题目:反转链表
这道题如果引入"虚拟头结点",那么在时间复杂度为O(n)的情况下就能完成
即直接往虚拟头结点后面进行头插即可

class Solution {public ListNode reverseList(ListNode head) {ListNode newHead = new ListNode(0);ListNode cur = head;while(cur != null){ListNode tmp = cur.next;cur.next = newHead.next;newHead.next = cur;cur = tmp;}return newHead.next;}
}

2.两数相加

题目:两数相加

2.1解析

思路比较简单,用两个指针遍历两个链表,每次将两个节点里面的值相加,将得到的数的个位数作为新节点的值,把新节点插入到引入的虚拟头结点,同时记录下此时两数相加的进位,在下次计算的时候要加上进位

2.2题解

class Solution {public ListNode addTwoNumbers(ListNode l1, ListNode l2) {ListNode cur1 = l1;ListNode cur2 = l2;int tmp = 0;ListNode newHead = new ListNode(0);ListNode cur = newHead;//记录新链表的最后一个节点while(cur1 != null || cur2 != null || tmp != 0){//tmp != 0 是为了解决最后计算完还有进位的情况if(cur1 != null){tmp += cur1.val;cur1 = cur1.next;}if(cur2 != null){tmp += cur2.val;cur2 = cur2.next;}ListNode node = new ListNode(tmp % 10);tmp /= 10;cur.next = node;cur = cur.next;}return newHead.next;}
}

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

题目:两两交换链表中的节点

3.1解析

这道题就能体现出我们之前说过的"不要吝啬变量"的好处
为了方便,我们还是引入"虚拟头节点"
在这里插入图片描述
对此链表进行两两翻转操作
要翻转两个节点,不仅需要记录这两个节点,还要记录前一个节点和后一个节点
因此我们直接引入这些变量:
在这里插入图片描述
就是对cur和next进行翻转操作,就很简单明了了,直接让prev.next = next; next.next = cur; cur.next = nnext;
在这里插入图片描述
此时前两个就翻转完了
接下来就要让prev指向当前cur的位置,继续进行刚才的操作
在这里插入图片描述
那么循环什么时候结束呢??会有两种情况
(1)就像我们上面这一组一样,当我们再次把prev指向cur的时候
在这里插入图片描述
此时,没翻转的节点就一个,那么就不用翻转了,即当next == null的时候是循环结束
(2)假设我们没有最后一个节点
在这里插入图片描述
那么此时已经没有节点了,自然就不需要翻转.因此cur == null也是循环结束的标志

3.2题解

class Solution {public ListNode swapPairs(ListNode head) {if(head == null || head.next == null){return head;}ListNode newHead = new ListNode(0,head);ListNode prev = newHead;ListNode cur = prev.next;ListNode next = cur.next;ListNode nnext = next.next;while(cur != null &&  next != null){prev.next = next;next.next = cur;cur.next = nnext;prev = cur;cur = prev.next;if(cur != null){next = cur.next;if(next != null){nnext = next.next;}}}return newHead.next;}
}

4.重排链表

题目:重排链表

4.1解析

此题最简单的思路就是将数组划分为两部分,并将右边的部分逆置,随后依次插入新的链表中
在这里插入图片描述
而其中的操作就是寻找中间节点、链表逆置、合并链表,我们在一开始都是讲过的,因此我们直接代码呈现\

4.2

    private ListNode findCenter(ListNode head){ListNode fast = head;ListNode slow = head;while(fast != null && fast.next != null){fast = fast.next.next;slow = slow.next;}return slow;}private ListNode reverse(ListNode head){ListNode newHead = new ListNode(1);ListNode cur = head;while(cur != null){ListNode tmp = cur.next;cur.next = newHead.next;newHead.next = cur;cur = tmp;}return newHead.next;}public void reorderList(ListNode head) {ListNode center = findCenter(head);ListNode node = reverse(center.next);//让中心节点的后面部分逆置center.next = null;//断开两部分ListNode cur1 = head;//遍历前一部分ListNode newHead = new ListNode(0);ListNode cur = newHead;//遍历新链表//合并while(cur1 != null){//前面的部分一定比后面的部分长cur.next = cur1;cur1 = cur1.next;cur = cur.next;if(node != null){cur.next = node;node = node.next;cur = cur.next;}}head = newHead.next;}

感谢您的访问!!期待您的关注!!!

在这里插入图片描述

T04BF

🫵 小比特 大梦想

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

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

相关文章

STM32CubeMX配置步骤详解六 —— 时钟及其它内部参数配置(1)

接前一篇文章:STM32CubeMX配置步骤详解五 —— 基础配置(2) 本文内容主要参考: STM32CUBEMX配置教程(一)基础配置-CSDN博客 野火STM32系列HAL库开发教程 —— 第12讲 STM32的复位和时钟控制(第…

短毛猫也能吃得好!揭秘宠物店推荐猫粮的秘密!

短毛猫通常毛发短而浓密,性格温顺,容易打理。那么,对于我们这些爱护短毛猫的朋友们来说,选择一款合适的猫粮就显得尤为重要了。今天,我要向大家推荐一款我个人非常喜欢的猫粮——福派斯三文鱼益生菌猫粮。 &#x1f41…

通信光缆主要敷设方式有哪些

由于建设条件和建设要求不同,通信光缆在不同场景下会采取不同的敷设方式,常见敷设方式包括:直埋、架空、管道、水底及局内等。 1 直埋敷设 直埋,也就是直接埋设,是指把光缆直接埋设于地下土壤中的敷设方式。通常&…

考研高数(平面图形的面积,旋转体的体积)

1.平面图形的面积 纠正:参数方程求面积 2.旋转体的体积(做题时,若以x为自变量不好计算,可以求反函数,y为自变量进行计算)

【Linux】使用cloudreve搭建个人网盘并传输文件

Cloudreve 是一个开源的个人网盘系统,能够帮助用户搭建属于自己的私有云存储服务。它支持多种存储后端,包括本地存储、远程FTP/SFTP存储、以及云存储服务如阿里云OSS、腾讯云COS和Amazon S3等。Cloudreve具有友好的用户界面和丰富的功能,比如…

Leetcode 第 389 场周赛题解

Leetcode 第 389 场周赛题解 Leetcode 第 389 场周赛题解题目1:3083. 字符串及其反转中是否存在同一子字符串思路代码复杂度分析 题目2:3084. 统计以给定字符开头和结尾的子字符串总数思路代码复杂度分析 题目3:3085. 成为 K 特殊字符串需要删…

Android14之智能指针的弱引用、强引用、弱指针、强指针用法区别及代码实例(二百零五)

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 优质专栏:多媒…

5560.树的直径

蛮不错的一道题目&#xff0c;你要利用树的性质分析出&#xff0c;你只需要维护上一次的树的直径的两个端点就好了 #include<iostream>using namespace std; using ll long long; using pii pair<int,int>; const int N 6e510; const int inf 0x3f3f3f3f; cons…

AIGC实战——ProGAN(Progressive Growing Generative Adversarial Network)

AIGC实战——ProGAN 0. 前言1. ProGAN2. 渐进式训练3. 其他技术3.1 小批标准差3.2 均等学习率3.3 逐像素归一化 4. 图像生成小结系列链接 0. 前言 我们已经学习了使用生成对抗网络 (Generative Adversarial Network, GAN) 解决各种图像生成任务。GAN 的模型架构和训练过程具有…

python接入AI 实现微信自动回复

import numpy as np # 引入numpy库&#xff0c;目的是将读取的数据转换为列表 import pandas as pd # 引入pandas库&#xff0c;用来读取csv数据 from uiautomation import WindowControl # 引入uiautomation库中的WindowControl类&#xff0c;用来进行图像识别和模拟操作 i…

组合ZKP代价:探索ZKP中non-native域运算 最新进展

1. 引言 前序博客&#xff1a; 递归证明——cycles of curves是必选项&#xff1f; ‘Foreign field’ 或 ‘non-native field’ 算术在ZKP&#xff08;zero knowledge proof零知识证明&#xff09;系统中随处可见。若想使用 ZKP 进行&#xff1a; 布尔运算公钥密码学或 证…

机器学习(五) -- 监督学习(3) -- 朴素贝叶斯

系列文章目录及链接 目录 前言 一、朴素贝叶斯通俗理解及定义 二、原理理解及公式 1、概率基础 2、贝叶斯公式 3、拉普拉斯平滑系数 三、**算法实现 四、接口实现 1、新闻数据集介绍 2、API 3、流程 3.1、获取数据 3.2、数据预处理 3.3、特征工程 3.4、朴素贝叶…