力扣刷题总结--链表部分

链表部分

通用操作

  • 链表的两种操作方式

 不用虚拟头结点用虚拟头节点
  • 为什么要定义cur指针

 因为操作完链表以后,是要返回头节点的。如果你上来就操作头节点,那么头节点的值都改了,返回head就不是我们最开始的链表了。
  • 写while循环的时候,只要考虑清楚极端条件就可以了

 考虑清楚头结点和尾节点的判断条件当处理的是头结点的时候考虑:1.会不会出现空指针。2.操作的是不是头结点
  • 尾部节点的判断条件

 cur.next==null; -- 表示当前指针已指向尾部节点​while(cur.next!=null) -- 表示如果循环没有到尾部节点,就一直进行循环
  • 指针指向下一个节点

 cur=cur.next;
  • 如果要操作当前节点,一定要能获取到前一个节点指针位置

  • 节点定义和单链表定义

 class MyLinkedList {int size;ListNode head;...}​class ListNode {int val;ListNode next;​public ListNode(int val) {this.val = val;}}
  • 寻找到操作节点前一个节点的指针代码(带头结点)

 for (int i = 0; i < index; i++) {//如果是不带头结点,i初始值从1开始pred = pred.next; //pred最开始指向虚拟节点,循环结束后,pred已指向前一个节点}
  • 寻找到操作节点指针代码(和上面的区别在于<=)(带头结点)

 for (int i = 0; i <= index; i++) {//如果是不带头结点,i初始值从1开始pred = pred.next; //pred最开始指向虚拟节点,循环结束后,pred已指向当前节点}
  • 获得链表的长度

 ListNode curA = headA; // headA为链表实际头结点int lenA = 0;while (curA != null) { // 求链表A的长度lenA++;curA = curA.next;}

力扣203.移除表元素

关键点:

  • 如果cur.next==val,移除。否则cur向后移动一位,注意是否则!

    • 如果cur=cur.next写在if外面,那么如果cur.next==val时,cur会移动两位,到最后一个元素就会报空指针

 class Solution {public ListNode removeElements(ListNode head, int val) {//输入:head = [1,2,6,3,4,5,6], val = 6//输出:[1,2,3,4,5]ListNode dummyNode=new ListNode(-1);dummyNode.next=head;ListNode cur=dummyNode;while(cur.next!=null){if(cur.next.val==val){cur.next=cur.next.next;}else{cur=cur.next;}}return dummyNode.next;}}

力扣707.设计链表

删除节点

  • 要删除第n个节点,cur指向前一个节点,第n个节点一定是cur.next。

 不是cur,因为如果你第n个节点是cur,那你又怎么知道你前一个节点呢?
  • 删除中间某个节点

我们通过操作cur来删掉

 cur指向前一个节点cur.next指向第n个节点(当前要删的节点)​通过cur.next=cur.next.next,这样就把cur指针的下一个节点(cur.next,即n节点)就删掉了
  • 如果删除节点的同时要改变链表的长度

要size--

  • 如果直接删除头节点

 head=head.next;
 class Solution {public ListNode removeElements(ListNode head, int val) {//输入:head = [1,2,6,3,4,5,6], val = 6//输出:[1,2,3,4,5]ListNode dummyNode=new ListNode(-1);//将虚拟节点和头结点关联起来dummyNode.next=head;//这句很关键ListNode cur=dummyNode;while(cur.next!=null){if(cur.next.val==val){cur.next=cur.next.next;}else{cur=cur.next;}}return dummyNode.next;}}

插入节点

  • 插入,一定要先new出新节点newNode

  • 指针改变的顺序:一定是先更新newNode指向第n个节点的指针,再更新cur指向第n个节点的指针。

 如果是在头结点插入,插入顺序:newNode.next=dummyNode.next; -- 1.dummyNode.next=newNode; -- 2.-- 注意:1和2的顺序一定不能反了如果不在头结点插入,插入顺序:newNode.next=cur.next; -- 1.cur.next=newNode; -- 2.-- 注意:1和2的顺序一定不能反了  否则如果先将cur的指针指向newNode节点,那就丢了cur.next指针(即n节点)的位置,newNode就无法指向n节点了
  • 在第n个节点前插入新节点,一定要能获取到该节点的前一节点的指针

第n个节点用cur.next表示
  • 如果增加节点的改变链表长度要做size++

完整代码

注意,下面是3个常出错的点:

  1. get方法中 if(index<0 || index>=size){...} 这里一定是>=,因为从0开始,走不到size,当index==size时会报空指针异常

  2. get方法中的for循环,注意是i<=index,因为当i=0是,依旧执行循环,cur从虚拟头指向真正第一个节点

  3. deleteAtIndex方法中 if(index>=size){..} ,同样是>=,因为也index==size时,指针同样也指向null

  4. 定义节点时,构造函数不要忘了 ListNode(int val){ this.val=val;}

class MyLinkedList {//定义链表头节点和长度int size;ListNode head;public MyLinkedList() {//初始化链表头结点和长度head=new ListNode(0);size=0;}public int get(int index) {//对index进行合法性判断if(index<0 || index>=size){return -1;}ListNode cur=head;//先将指针移动到要操作的当前节点for(int i=0;i<=index;i++){cur=cur.next;}return cur.val;}public void addAtHead(int val) {addAtIndex(0,val);}public void addAtTail(int val) {addAtIndex(size,val);}public void addAtIndex(int index, int val) {//先判断index合法性if(index<0 || index>size){//这因为节点从0开始,size其实不在节点里,但在插入操作时,如果cur指向size,插入是合理的return;}size++;ListNode cur=head;for(int i=0;i<index;i++){//这才是将指针遍历到操作节点的前一个cur=cur.next;}//造个要插入的新节点ListNode addNode=new ListNode(val);addNode.next=cur.next;cur.next=addNode;	}public void deleteAtIndex(int index) {//先判断index合法性if(index>=size){//这因为节点从0开始,size其实不在节点里,但在插入操作时,如果cur指向size,插入是合理的return;}size--;//如果index小于0,删除就删除头结点index=Math.max(0,index);ListNode cur=head;for(int i=0;i<index;i++){//这才是将指针遍历到操作节点的前一个cur=cur.next;}//这里的特殊情况:如果index=0,删除头结点,不满足上面for循环,此时cur依然是dummyNodecur.next=cur.next.next;}
}class ListNode {//定义存储内容和指针int val;ListNode next;ListNode(int val){ //这个每次不要忘了this.val=val;}
}

力扣206.反转链表操作

有双指针和递归两种操作方法

关键点:

  • 要用temp保存cur.next,否则下次操作找不到下一节点

  • 向后移动两个指针,应该是pre=cur在前,cur=temp在后

    • 如果cur=temp在前,pre和cur就指向的是同一个位置了(死循环了)

  • 循环结束后,cur指向null,pre指向最后一次反转的节点(头结点),因此返回pre就行

双指针解法

class Solution {public ListNode reverseList(ListNode head) {//初始化两个指针ListNode cur=head;//指向头结点ListNode pre=null;//指向空指针//pre.next=cur;这行代码是错的,因为pre已经是空了,再对其进行操作就会报空指针异常while(cur!=null){ListNode tmp=cur.next;//关键代码//1.必须给个临时节点tmp用于存cur.nexttmp=cur.next;//2.翻转指针(每次指向前一位,这样每次都将当前节点指针有后指向了前)cur.next=pre;//3.将cur和pre都后移一位,下面这两行代码顺序不能反了pre=cur;cur=tmp;}return pre;}
}

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

关键点:

  • 要做操作两个节点,需要每次将指针指向这两个节点的前一个节点

  • dummyNode先指向第二个节点后,再用第二个节点指向第一个节点时,第二个节点这样表示:cur.next。注意不是cur.next.next

  • 要将第一,三两个节点用两个临时指针temp和temp1保存起来

    • 因为dummy指向节点2时,就已经找不到节点了

    • 节点2指向节点时,就已经找不到节点3了

双指针解法

class Solution {public ListNode swapPairs(ListNode head) {//造虚拟头结点ListNode dummyNode=new ListNode(-1);dummyNode.next=head;ListNode cur=dummyNode;//循环,奇偶节点都要判断while(cur.next!=null && cur.next.next!=null){//保存两个临时节点ListNode temp=cur.next;//保存第一个节点ListNode temp1=cur.next.next.next;//保存第三个节点//虚拟头节点先指向节点2cur.next=cur.next.next;//再将节点2指针指向节点1cur.next.next=temp;//注意注意:此时节点2已变为cur.next,cur.next.next表示2节点的指针,它指向1节点//再将节点1的指针指向节点3temp.next=temp1;//temp是1节点//此时cur还并没有动,依旧指向dummyNode节点(前面操作的都是cur.next或cur.next.next,并未直接操作cur),将cur往后移动2位//移动两位以后,cur指针依旧指向下次要操作的两个节点的前一个节点cur=cur.next.next;//此时一轮循环结束,改变了两个节点指针的指向。后面每两个指针进行相同操作}//返回dummyNode的nextreturn dummyNode.next;}
}

递归解法

class Solution {public ListNode reverseList(ListNode head) {return reverse(null,head);//对应双指针中赋初始值:cur=head;prev=null}private ListNode reverse(ListNode prev, ListNode cur) {if(cur==null){return prev;}ListNode temp=cur.next;cur.next=prev;//反转指针return reverse(cur,temp);//对应双指针中给双指针各向后移动一位:pre=cur;cur=temp}
}

力扣19.删除链表的倒数第 N 个结点

关键点:

  • dummyNode.next=head;//不关联会报操作空指针异常

  • 快指针和慢指针距离差n个节点

    • 这样快指针指向null时,慢节点正好指向操作的前一节点

双指针解法

class Solution {public ListNode removeNthFromEnd(ListNode head, int n) {//输入:head = [1,2,3,4,5], n = 2//输出:[1,2,3,5]ListNode dummyNode=new ListNode(-1),cur=dummyNode;dummyNode.next=head;//快慢指针同时指向头结点ListNode fast=dummyNode;ListNode slow=dummyNode;//先让快指针移动n个节点for(int i=0;i<=n;i++){fast=fast.next;}//再让快慢指针同时开始向后移动节点while(fast!=null){fast=fast.next;slow=slow.next;}//此时的slow指向删除节点的前一节点slow.next=slow.next.next;//返回虚拟头节点的nextreturn dummyNode.next}
}

力扣160.链表相交

下面这个视频讲的不错的

[160. 相交链表 | 手写图解版思路 + 代码讲解]  https://www.bilibili.com/video/BV1xS4y1v7pp/?spm_id_from=333.337.search-card.all.click&amp;vd_source=3c0c84befaea55842fa8622aa89e5300 

关键点:

  • 要让两个链表走过相同长度

  • 三元运算符

    • 判断条件要用变量接收,不能直接写p==null?

public class Solution {public ListNode getIntersectionNode(ListNode headA, ListNode headB) {ListNode p=headA,q=headB;//本题关键思路:p和q要走过相同的长度while(p!=q){//p和q不相等时,两个指针同时向后移动//关键代码://三元运算:p(q)此时是否走到空(末尾),如果没有,继续往下走。走到末尾,p(q)的指针指向另一个链表的头结点位置p=p==null?headB:p.next;q=q==null?headA:q.next;}	// while循环结束时,保证了两个链表一定会走过相同距离。//当走过相同距离时,看它两指针指向的是什么。//如果是null,表示两节点不想交。//如果指向某个内存地址,表示两个节点在该处相交。此时返回任意指针即可。return p;}
}

力扣142.环形链表||

  • 思路

环形链表需要先定义一快一慢两个指针,,然后循环[终止条件:快指针没有走到null],快指针一次走两格,慢指针一次走一格。然后是关键:看快慢指针是否会相等[即走到一起,此刻已说明该链表是个环形链表],如果会,再定义一个临时指针指向头节点,一个临时指针指向此刻相交节点[fast,slow都行]。然后在子循环中让两个临时指针再每次以相同步速向后移动[此时的终止条件也是返回条件,即任意一个临时指针,即环链表的入口],然后这个临时指针即可。
如果外层循环结束后,仍然没有返回临时指针,说明该链表不是环链表,返回null。

关键点:

  • 在链表中,有一个指针cur指向当前节点,如果节点下一位不为空,但下下一位为空,那么cur.next.next会报空指针异常么

如果 cur.next 不为空(即 cur 的下一个节点存在),那么你可以安全地访问 cur.next.next(即 cur 的下下个节点)。“节点下一位不为空,但下下一位为空”,这意味着 cur.next 存在但 cur.next.next 为 null。这种情况下,尝试访问 cur.next.next 不会报空指针异常,因为它只是返回一个 null 值,表示没有下下个节点。但是,如果你尝试对 cur.next.next 进行某些操作(比如解引用它),并且没有检查它是否为 null,那么可能会导致空指针异常。如果 cur.next 为空(即 cur 的下一个节点不存在),那么尝试访问 cur.next.next 将会导致空指针异常,因为 cur.next 是 null,【你不能在 null 上调用 .next。】
  • while(fast!=null && fast.next!=null){...}

加粗红色的这个也要加,因为如果链表本身为空,对null.next会报空指针异常

/*** Definition for singly-linked list.* class ListNode {*     int val;*     ListNode next;*     ListNode(int x) {*         val = x;*         next = null;*     }* }*/
public class Solution {public ListNode detectCycle(ListNode head) {ListNode fast=head;ListNode slow=head;while(fast!=null && fast.next!=null){fast=fast.next.next;slow=slow.next;if(fast==slow){ListNode index1=fast;ListNode index2=head;while(index1!=index2){index1=index1.next;index2=index2.next;}return index1;}// return null;这行代码不能加,因为如果快慢指针不想等,外面这一层循环还没走完}return null;}
}

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

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

相关文章

相机模型的内参、外参

相机模型的内参、外参 文章目录 相机模型的内参、外参1. 针孔模型、畸变模型&#xff08;内参&#xff09;2. 手眼标定&#xff08;外参&#xff09; Reference 这篇笔记主要参考&#xff1a;slam十四讲第二版&#xff08;高翔&#xff09; 相机将三维世界中的坐标点&#xff…

JVM线程和内存溢出问题排查思路

一、工具 Arthas&#xff1a;Arthas 是一款能在线查看应用 load、内存、gc、线程 等状态信息&#xff0c;并对业务问题进行诊断的工具&#xff0c;支持 JDK 6 和 Linux/Mac/Windows 系统。 jstack&#xff1a;jstack是JVM自带的Java堆栈跟踪工具&#xff0c;它用于打印出给定…

PDK安装及简介

目录 PDK简介 pdk安装 Standard Cell Library简介 IO Library简介 PDK简介 PDK&#xff1a;全称Process Design Kit&#xff0c;是工艺设计工具包的缩写&#xff0c;是制造和设计之间的沟通桥梁&#xff0c;是模拟电路设计的起始点。 具体来说&#xff0c;PDK是代工厂(FAB…

使用Docker进行Jmeter分布式搭建

大家好&#xff0c;随着技术的不断发展&#xff0c;对性能测试的要求也日益提高。在这样的背景下&#xff0c;如何利用 Docker 来巧妙地搭建 Jmeter 分布式成为了关键所在。现在&#xff0c;就让我们开启这场探索之旅&#xff0c;揭开其神秘的面纱。前段时间给大家分享了关于 L…

通信指挥类装备(多链路聚合设备)-应急通信指挥解决方案

现场通信指挥系统是一种功能全面的便携式音视频融合指挥通信平台&#xff0c;可实现现场应急救援指挥、多种通信手段融合、现场通信组网等功能&#xff0c;是现场指挥系统的延伸。 多链路聚合设备&#xff0c;是一款通信指挥类装备&#xff0c;具有 4G/5G&#xff0c;专网&…

Golang开发:切片的两种创建方式及区别

在Go语言中&#xff0c;var和make都可以用来初始化切片&#xff08;slice&#xff09;&#xff0c;但它们之间有一些重要的区别。 初始化方式&#xff1a; 使用var关键字声明的切片会被初始化为nil&#xff0c;即没有底层数组&#xff0c;长度为0&#xff0c;容量为0。使用make…

Free RTOS中Semaphore(二值信号量)的使用介绍

目录 概述 1 使用STM32Cube 配置信号量 1.1 引子 1.2 STM32Cube中配置FreeRTOS 1.3 STM32Cube生成Project 2 cmsis_os中信号量接口函数 2.1 函数&#xff1a;osSemaphoreNew 2.2 函数&#xff1a;osSemaphoreGetName 2.3 函数&#xff1a;osSemaphoreAcquire 2.4 函数…

hashmap数据结构为什么是链表

HashMap 数据结构中&#xff0c;链表通常用于解决哈希冲突。当不同的键映射到相同的哈希桶时&#xff0c;就会发生哈希冲突。链表是一种简单而有效的解决方法。 在 JDK 8 之前的 HashMap 实现中&#xff0c;当发生哈希冲突时&#xff0c;冲突的元素会被存储在同一个哈希桶中&a…

设计模式:外观模式(Facade)

设计模式&#xff1a;外观模式&#xff08;Facade&#xff09; 设计模式&#xff1a;外观模式&#xff08;Facade&#xff09;模式动机模式定义模式结构时序图模式实现在单线程环境下的测试在多线程环境下的测试模式分析优缺点适用场景应用场景模式扩展参考 设计模式&#xff1…

C++STL(queue和list)

3.6 queue 容器 3.6.1 queue 基本概念 概念&#xff1a;Queue是一种先进先出(First In First Out,FIFO)的数据结构&#xff0c;它有两个出口 队列容器允许从一端新增元素&#xff0c;从另一端移除元素队列中只有队头和队尾才可以被外界使用&#xff0c;因此队列不允许有遍历…

【Open AI】GPT-4o深夜发布:视觉、听觉跨越式升级

北京时间5月14日1点整&#xff0c;OpenAI 召开了首场春季发布会&#xff0c;CTO Mira Murati 在台上和团队用短短不到30分钟的时间&#xff0c;揭开了最新旗舰模型 GPT-4o 的神秘面纱&#xff0c;以及基于 GPT-4o 的 ChatGPT&#xff0c;均为免费使用。 本文内容来自OpenAI网站…

大数据面试 --- 六

1、Flink中的三种时间&#xff0c;哪一个性能会比较好 在Flink中主要分成三种时间&#xff1a; 事件时间&#xff08;Event Time&#xff09;注入时间&#xff08;Process Time&#xff09;、摄入时间&#xff08;Ingestion Time&#xff09; 事件时间指的是事件产生的时间…

课时126:awk实践_进阶知识_内置函数1

1.2.5 内置函数1 学习目标 这一节&#xff0c;我们从 基础知识、简单实践、小结 三个方面来学习。 基础知识 简介 在awk内部预制了一些函数&#xff0c;借助于这些函数&#xff0c;我们可以实现相关场景的快速操作。这些内置函数的常见类型有&#xff1a;数值类内置函数int…

人工智能|深度学习——YOLOV8结构图

YoloV8相对于YoloV5的改进点&#xff1a; Replace the C3 module with the C2f module.Replace the first 6x6 Conv with 3x3 Conv in the Backbone.Delete two Convs (No.10 and No.14 in the YOLOv5 config).Replace the first 1x1 Conv with 3x3 Conv in the Bottleneck.Use…

【图神经网络——消息传递】

消息传递机制 画图先&#xff1a;导包&#xff1a;画图&#xff1a; 实现消息传递&#xff1a;例子一&#xff1a;例子二&#xff1a; 画图先&#xff1a; 导包&#xff1a; import networkx as nx import matplotlib.pyplot as plt import torch from torch_geometric.nn im…

Linux操作系统最著名的两大系列Red Hat和Debian

Linux操作系统可以根据其背后的项目或社区分为不同的系列&#xff0c;其中最著名的两大系列是Red Hat系列和Debian系列。 1.著名的两大系列是Red Hat和Debian Red Hat系列&#xff1a; Red Hat Enterprise Linux (RHEL)&#xff1a;这是Red Hat公司推出的企业级操作系统&#…

【LAMMPS学习】十、LAMMPS辅助工具(1)

10. 辅助工具 LAMMPS 被设计为用于执行分子动力学计算的计算内核。设置和分析模拟通常需要额外的预处理和后处理步骤。此类工具的列表可以在 LAMMPS 网页上的以下链接中找到&#xff1a; 前/后处理 外部 LAMMPS 软件包和工具 Pizza.py 工具包 Pizza.py 的最后一个链接是桑迪…

CTFshow misc

第一题1 打开图片直接就是flag 第二题0 放入010发现文件头有png 更换后缀 获得flag 第三题1 下载之后发现是bpg后缀 用在线工具转换为png获得flag 第四题 0 把六个文件后缀都改为png即可获得flag

Visual Studio Code 扩展程序Text Edits

需求 比如把Scarzombie_Monster全部转换为大写或者小写 安装 Text Edits 直接搜索安装即可 使用 假如要把Scarzombie_Monster全部转为大写&#xff0c;选中右键选中 To Upper Case或者直接快捷键shiftAltU即可

使用yolov8 训练coco 和自己的关键点识别数据集的参考

使用yolov8 训练关键点配置理解 1. coco-pose.yaml 修改关键参数kpt_shape: [17, 3]flip_idx: [0, 2, 1, 4, 3, 6, 5, 8, 7, 10, 9, 12, 11, 14, 13, 16, 15]2. yolov8n-pose.yaml 修改kpt_shape3. 编写 train文件4.一个封装的推理代码1. coco-pose.yaml 修改关键参数 kpt_sha…