链表 OJ(一)
移除链表元素
题目连接:
https://leetcode.cn/problems/remove-linked-list-elements/description/
使用双指针法,开始时,一个指针指向头节点,另一个指针指向头节点的下一个结点,然后开始遍历链表删除结点。
这里要注意如果是空链表的话,使用双指针第二个指针会发生空指针异常,所以要判断一下
最后程序运行到最后,我们还差头节点没有判断,最后加上即可。
class Solution {public ListNode removeElements(ListNode head, int val) {if(head == null) {return head;}ListNode prev = head;ListNode cur = head.next;while(cur != null) {if(cur.val == val) {prev.next = cur.next;} else {prev = cur;}cur = cur.next;}if(head.val == val) {head = head.next;}return head;}
}
反转链表
题目连接:
https://leetcode.cn/problems/reverse-linked-list/description/
我们还是使用双指针,不过这次有一个指针就是头指针,因为反转链表之后的头指针会发生改变,那还不如直接让头指针一起移动,先将另一个指针指向头指针的下一个结点,然后开始遍历链表,把每一个结点的指针指向的对象变为前面的对象。
class Solution {public ListNode reverseList(ListNode head) {if(head == null) {return head;}ListNode cur = head.next;head.next = null;while(cur != null) {ListNode tmp = cur.next;cur.next = head;head = cur;cur = tmp;}return head;}
}
链表的中间结点
题目链接:
https://leetcode.cn/problems/middle-of-the-linked-list/description/
使用快慢指针,快指针每次走两步,慢指针每次走一步,最后慢指针所指向的结点就是 中间结点
原理:fast = 2slow = 总路程
这里要注意循环的结束条件:
当fast== null 并且 fast.next == null时,才能进入循环,并且 fast==null,要放在前面,因为只用fast不是null时才能进行 fast.next 的判断
class Solution {public ListNode middleNode(ListNode head) {ListNode fast = head;ListNode slow = head;while(fast != null && fast.next != null) {fast = fast.next.next;slow = slow.next;}return slow;}
}
返回倒数第 k 个节点
题目链接:
https://leetcode.cn/problems/kth-node-from-end-of-list-lcci/description/
还是使用快慢指针,先让快指针走 k-1 步,然后慢指针和快指针一起走,每次走一步,最后快指针走到最后一个结点时,慢指针所指向的节点就是倒数第 k 个节点。
原理就是让slow和fast的差值j就是k
class Solution {public int kthToLast(ListNode head, int k) {ListNode fast = head;ListNode slow = head;for(int i=0; i<k-1; i++) {fast = fast.next;}while(fast.next != null) {slow = slow.next;fast = fast.next;}return slow.val;}
}
合并两个有序的链表
题目链接:
https://leetcode.cn/problems/merge-two-sorted-lists/description/
创建一个新的头节点,list1和list2开始遍历各自的链表,然后比较,插入到新链表中,最后再看一下哪些链表没有遍历完,然后把没有遍历完的链表直接插入即可。
class Solution {public ListNode mergeTwoLists(ListNode list1, ListNode list2) {if(list1 == null) {return list2;}if(list2 == null) {return list1;}ListNode newHead = new ListNode();ListNode cur = newHead;while(list1 != null && list2 != null) {if(list1.val < list2.val) {cur.next = list1;list1 = list1.next;} else {cur.next = list2;list2 = list2.next;}cur = cur.next;}if(list1 != null) {cur.next = list1;}if(list2 != null) {cur.next = list2;}return newHead.next;}
}
分割链表
题目链接:
https://www.nowcoder.com/practice/0e27e0b064de4eacac178676ef9c9d70?tpId=8&&tqId=11004&rp=2&ru=/activity/oj&qru=/ta/cracking-the-coding-interview/question-ranking
我们可以将单链表拆成两个链表,一个链表存储小于x 的结点,另一个链表存储大于等于x 的结点,然后将两个链表合并
这里建议使用带头链表,也就是我们常说的带哨兵位的链表,这个链表最大的好处就是可以避免头插的复杂情况,既然使用了哨兵位,就要知道最后哨兵位是要被丢弃的。
public class Partition {public ListNode partition(ListNode pHead, int x) {ListNode headA = new ListNode(-1);ListNode aLast = headA;ListNode headB = new ListNode(-1);ListNode bLast = headB;ListNode cur = pHead;while(cur != null) {if(cur.val < x) {aLast.next = cur;aLast = aLast.next;} else {bLast.next = cur;bLast = bLast.next;}cur = cur.next;}aLast.next = headB.next;bLast.next = null;return headA.next;}
}
链表的回文结构
题目链接:
https://www.nowcoder.com/practice/d281619e4b3e4a60a2cc66ea32855bfa?tpId=49&&tqId=29370&rp=1&ru=/activity/oj&qru=/ta/2016test/question-ranking
解题思路:
我们可以使用快慢指针来遍历链表找到中间结点,然后就可以把后半部分的链表进行反转,之后从这个链表头尾结点开始向中间遍历。
易错点:
1.链表的结点个数有奇数和偶数两种类型,我们需要分别讨论
2.区分奇数个结点还是偶数个结点可以从一开始找完中间结点的时候,从fast入手,因为找中间结点的循环结束就是fast最后指向尾节点还是null。
3.要一定要保存好fast的信息,因为后面在反转链表的时候fast其实已经发生了改变。
public class PalindromeList {public boolean chkPalindrome(ListNode A) {//如果是空链表返回trueif(A == null) {return true;}ListNode slow = A;ListNode fast = A;//找到中间结点while(fast != null && fast.next != null) {fast = fast.next.next;slow = slow.next;}//保存fast的结点信息,避免后面反转链表丢失信息boolean isOdd = true;//是否是奇数个结点if(fast == null) {isOdd = false;}//反转后半部分的链表ListNode prev = slow;ListNode cur = slow.next;while(cur != null) {ListNode curN = cur.next;cur.next = prev;prev = cur;cur = curN;}//前后遍历链表ListNode pA = A;ListNode last = prev;//偶数个结点if(!isOdd) {while(pA.next != last) {if(pA.val != last.val) {return false;}pA = pA.next;last = last.next;}if(pA.val != last.val) {return false;}}if (isOdd) { //奇数个结点while(pA != last) {if(pA.val != last.val) {return false;}pA = pA.next;last = last.next;}}return true;}
}
相交链表
题目链接:
https://leetcode.cn/problems/intersection-of-two-linked-lists/
由于是相交链表,在公共结点之前两个链表可能有一个差值,如果我们能找到这个差值,让长的链表先走差值不属,然后两个链表一起遍历,直到相遇到公共结点。
这个差值就是两个链表的长度的差。
public class Solution {public ListNode getIntersectionNode(ListNode headA, ListNode headB) {int lenA = 0;int lenB = 0;ListNode cur = headA;while(cur != null) {lenA++;cur = cur.next;}cur = headB;while(cur != null) {lenB++;cur = cur.next;}int k = 0;ListNode pl = null;ListNode ps = null;if(lenA > lenB) {k = lenA - lenB;pl = headA;ps = headB;} else {k = lenB - lenA;pl = headB;ps = headA;}while(k != 0) {pl = pl.next;k--;}while(pl != ps) {pl = pl.next;ps = ps.next;}return pl;}
}
环形链表
题目链接:
https://leetcode.cn/problems/linked-list-cycle/
这里使用快慢指针,快指针一次走两步,慢指针一次走一步,当快指针运动到fast == null 或者 fast .next == null 这个条件时,说明链表不存在环,如果有链表存在环,那么快指针就会先进入到环中一直做圆周运动,一定会存在某一个时刻快慢指针会相遇,这时候返回true即可。
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步,走4步,…n步行吗?
快指针如果每次走三步,假设两个指针的位置如下:
那二者永远都不会相遇
其他情况由读者自行探讨~~
环形链表Ⅱ (找入口点)
题目链接:
https://leetcode.cn/problems/linked-list-cycle-ii/description/
我们还是使用快慢指针来做。
假设起点与入口点的距离为 x ,环的周长为 C ,慢指针与快指针相遇点离起点的距离为L :
慢指针所走的路程为 x + L
快指针所走的路程为 x + L + nC (n为正整数,n = 1,2,3,4…)
这里解释一下两者的路程计算问题:
在慢指针还没有进入口点的时候,就已经至少路过一次相遇点,所以当慢指针刚刚进入环中的时候,快指针最少已经走了一圈,最多走了 n 圈。
因为慢指针刚进入环中的时候,快指针和它最多相差一个圈的距离,并且快指针的速度比慢指针的速度要快,所以慢指针是不可能走完一圈的(假设慢指针走完一圈,那快指针一定走完一圈再多一点,所以慢指针在完成一圈之前一定会被追上),所以慢指针所走的路程为 x + L,快指针所走的路程为 x + L + nC (n为正整数,n = 1,2,3,4…)中的 nC 是遇到慢指针之前就已经完成好的
当快慢指针在环中相遇时,由于快指针的速度是慢指针的速度的两倍,所以慢指针的路程是快指针的一半,即 2 * (x + L) = x + L + nC,化简整理得 x + L = nC 即 x = nC - L
推导结论:
让一个指针从头开始遍历,另一个指针从相遇点开始遍历,两个指针每次走一步,一定会在入口点相遇。
public class Solution {public ListNode detectCycle(ListNode head) {ListNode slow = head;ListNode fast = head;while(fast != null && fast.next != null) {fast = fast.next.next;slow = slow.next;if(slow == fast) {break;}}//没有环if(fast == null || fast.next == null) {return null;}ListNode meet = fast;ListNode str = head;while(meet != str) {meet = meet.next;str = str.next;}return str;}
}
相关文章:

链表 OJ(一)
移除链表元素 题目连接: https://leetcode.cn/problems/remove-linked-list-elements/description/ 使用双指针法,开始时,一个指针指向头节点,另一个指针指向头节点的下一个结点,然后开始遍历链表删除结点。 这里要注…...
《Linux与Windows文件系统的区别》
Linux与Windows文件系统的区别 在计算机操作系统领域,Linux和Windows是两种广泛使用的操作系统,它们在文件系统方面有许多显著的差异。这篇博客将详细介绍这两种操作系统文件系统的区别,帮助读者更好地理解它们各自的特点和优势。 类别Linu…...
批量修改Git历史commit信息中的username
之前很长一段时间GitHub上的提交都在使用工作账户, 导致私人仓库中的提交者比较混乱. 在StackOver里面找到了一个bash脚本可以批量修改username, 在这里记录一下. 修改的步骤一共两步: 执行修改脚本将本地修改同步到Git服务器 首先我们来看脚本: #!/bin/shgit filter-branch…...

LabVIEW与ABB工业机器人据监控
1. 前言 随着工业自动化的发展,工业机器人在制造业中的应用越来越广泛。为了实现对工业机器人的高效监控和控制,本文介绍了利用OPC(OLE for Process Control)服务器将ABB工业机器人与LabVIEW连接起来的解决方案。通过OPC服务器…...
c++栈内存和堆内存的基本使用
c栈内存和堆内存的基本使用 #include <iostream>// 定义一个简单的结构体 struct Person {std::string name;int age; };int main() {// 栈内存分配int a 10; // 基本数据类型的栈内存分配Person person; // 结构体的栈内存分配person.name "John";person.a…...

快速入门,springboot知识点汇总
学习 springboot 应该像学习一门编程语言一样,首先要熟练掌握常用的知识,而对于不常用的内容可以简单了解一下。先对整个框架和语言有一个大致的轮廓,然后再逐步补充细节。 前序: Spring Boot 通过简化配置和提供开箱即用的特性,…...

Ubuntu20.04系统非root用户安装GAMIT10.71
(测试环境:20240701升级包和20240701数据,解算通过) QQ:8212714 群:302883438群文件(source安装包20240701升级包) 1、首先在计算机中安装VMware Workstation 16 Pro。建议:分配…...

stm32 开发板可以拿来做什么?
STM32开发板可以用来做许多不同的事情,具体取决于您的应用需求和编程能力。我收集归类了一份嵌入式学习包,对于新手而言简直不要太棒,里面包括了新手各个时期的学习方向编程教学、问题视频讲解、毕设800套和语言类教学,敲个22就可…...

latex英文转中文word,及一些latex相关工具分享
前言:想要转换latex生成的英文pdf文件为中文word文件 一、主要步骤 1、文字翻译:直接使用谷歌翻译等辅助将英文翻译成中文即可; 支持英文pdf文件全文翻译,再用迅捷PDF转换器之类的转成word,再手动调整。 https://app…...
EasyOCR: 简单易用的多语言OCR工具
EasyOCR: 简单易用的多语言OCR工具 1. 什么是EasyOCR?2. 使用场景3. 基本使用方法安装示例代码代码解释 4. 结语 1. 什么是EasyOCR? EasyOCR是一个基于Python的开源光学字符识别(OCR)工具,它支持80多种语言的文本识别。该项目由JaidedAI开发,旨在提供一个简单易用但功能强大…...

arm架构安装chrome
在ARM架构设备上安装谷歌软件或应用通常涉及到几个步骤,这取决于你要安装的具体谷歌产品,比如谷歌浏览器、Google Play服务或者是其他谷歌开发的软件。下面我会给出一些常见的指导步骤,以安装谷歌浏览器为例: 在Linux ARM64上安装…...

ETAS工具导入Com Arxml修改步骤
文章目录 前言Confgen之前的更改Confgen之后的修改CANCanIfComComMEcuM修改CanNmCanSMDCMCanTp生成RTE过程报错修改DEXT-诊断文件修改Extract问题总结前言 通讯协议栈开发一般通过导入DBC实现,ETAS工具本身导入DBC也是生成arxml后执行cfggen,本文介绍直接导入客户提供的arxml…...
Apache Kylin模型构建全解析:深入理解大数据的多维分析
引言 Apache Kylin是一个开源的分布式分析引擎,旨在为大数据提供快速的多维分析能力。它通过预计算技术,将数据转化为立方体模型(Cube),从而实现对Hadoop大数据集的秒级查询响应。本文将详细介绍Kylin中模型构建的全过…...

element-plus的文件上传组件el-upload
el-upload组件 支持多种风格,如文件列表,图片,图片卡片,支持多种事件,预览,删除,上传成功,上传中等钩子。 file-list:上传的文件集合,一定要用v-model:file-…...
等保测评视角下的哈尔滨智慧城市安全框架构建
随着智慧城市的兴起,哈尔滨作为东北地区的重要城市,正在积极探索和实践智慧城市安全框架的构建,以确保在数字化转型的过程中,既能享受科技带来的便利,又能有效防范和应对各类网络安全风险。 本文将从等保测评的视角出…...
Java中的数据缓存技术及其应用
Java中的数据缓存技术及其应用 大家好,我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编,也是冬天不穿秋裤,天冷也要风度的程序猿! 在现代应用程序中,数据缓存是一种重要的技术手段,用于提…...
SQL 索引
一、索引的基本概念 **索引(Index)**是数据库中一种特殊的数据结构,用于帮助数据库管理系统(DBMS)快速访问数据表中的特定信息。索引类似于书籍的目录,可以加快数据检索的速度。 二、索引的作用 提高查询…...
free第一次成功,第二次失败
问题描述: 在一个函数中存在free,第一次进入此函数没有问题,但是第二次出错 strncpy(pdd_all_data[i].sensor_name,white_list[j].dev_name,strlen(pdd_all_data[i].sensor_name)); 上面代码都是使用strncpy不小心导致double free or corrup…...
各种音频处理器
在HiFi(高保真)音频系统中,通常需要使用一些特定类型的音频处理器,以确保音频信号的高保真和优质输出。以下是一些常见的音频处理器类型及其在HiFi系统中的应用: DAC(数模转换器): …...
深度学习探秘:Transformer模型跨框架实现大比拼
深度学习探秘:Transformer模型跨框架实现大比拼 自2017年Transformer模型问世以来,它在自然语言处理(NLP)领域引发了一场革命。其独特的自注意力机制为处理序列数据提供了全新的视角。随着深度学习框架的不断发展,Tra…...

19c补丁后oracle属主变化,导致不能识别磁盘组
补丁后服务器重启,数据库再次无法启动 ORA01017: invalid username/password; logon denied Oracle 19c 在打上 19.23 或以上补丁版本后,存在与用户组权限相关的问题。具体表现为,Oracle 实例的运行用户(oracle)和集…...
内存分配函数malloc kmalloc vmalloc
内存分配函数malloc kmalloc vmalloc malloc实现步骤: 1)请求大小调整:首先,malloc 需要调整用户请求的大小,以适应内部数据结构(例如,可能需要存储额外的元数据)。通常,这包括对齐调整,确保分配的内存地址满足特定硬件要求(如对齐到8字节或16字节边界)。 2)空闲…...
Linux链表操作全解析
Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表?1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...
三维GIS开发cesium智慧地铁教程(5)Cesium相机控制
一、环境搭建 <script src"../cesium1.99/Build/Cesium/Cesium.js"></script> <link rel"stylesheet" href"../cesium1.99/Build/Cesium/Widgets/widgets.css"> 关键配置点: 路径验证:确保相对路径.…...

安宝特方案丨XRSOP人员作业标准化管理平台:AR智慧点检验收套件
在选煤厂、化工厂、钢铁厂等过程生产型企业,其生产设备的运行效率和非计划停机对工业制造效益有较大影响。 随着企业自动化和智能化建设的推进,需提前预防假检、错检、漏检,推动智慧生产运维系统数据的流动和现场赋能应用。同时,…...

【配置 YOLOX 用于按目录分类的图片数据集】
现在的图标点选越来越多,如何一步解决,采用 YOLOX 目标检测模式则可以轻松解决 要在 YOLOX 中使用按目录分类的图片数据集(每个目录代表一个类别,目录下是该类别的所有图片),你需要进行以下配置步骤&#x…...
.Net Framework 4/C# 关键字(非常用,持续更新...)
一、is 关键字 is 关键字用于检查对象是否于给定类型兼容,如果兼容将返回 true,如果不兼容则返回 false,在进行类型转换前,可以先使用 is 关键字判断对象是否与指定类型兼容,如果兼容才进行转换,这样的转换是安全的。 例如有:首先创建一个字符串对象,然后将字符串对象隐…...
DeepSeek 技术赋能无人农场协同作业:用 AI 重构农田管理 “神经网”
目录 一、引言二、DeepSeek 技术大揭秘2.1 核心架构解析2.2 关键技术剖析 三、智能农业无人农场协同作业现状3.1 发展现状概述3.2 协同作业模式介绍 四、DeepSeek 的 “农场奇妙游”4.1 数据处理与分析4.2 作物生长监测与预测4.3 病虫害防治4.4 农机协同作业调度 五、实际案例大…...

ABAP设计模式之---“简单设计原则(Simple Design)”
“Simple Design”(简单设计)是软件开发中的一个重要理念,倡导以最简单的方式实现软件功能,以确保代码清晰易懂、易维护,并在项目需求变化时能够快速适应。 其核心目标是避免复杂和过度设计,遵循“让事情保…...

c++第七天 继承与派生2
这一篇文章主要内容是 派生类构造函数与析构函数 在派生类中重写基类成员 以及多继承 第一部分:派生类构造函数与析构函数 当创建一个派生类对象时,基类成员是如何初始化的? 1.当派生类对象创建的时候,基类成员的初始化顺序 …...