数据结构(Java版)第八期:LinkedList与链表(三)

数据结构(Java版)第八期:LinkedList与链表(三)
专栏:数据结构(Java版)

个人主页:手握风云

目录

一、链表中的经典面试题

1.1. 链表分割

1.2. 链表的回文结构

1.3. 相交链表

1.4. 环形链表


一、链表中的经典面试题

1.1. 链表分割

        题目中要求不能改变原来的数据顺序,也就是如上图所示。我们先定义一个cur引用去遍历这个链表,用每个结点的val值与x进行比较,然后利用尾插的方法把结点插入进两个链表中。我们先定义bs、be、as、ae四个引用来表示两个链表的头尾,在尾插的时候需要注意,利用ae.next = node;ae = node,记录下尾结点,保证ae永远指向最后一个结点,同时be.next=as,连接上两个链表。

class ListNode{ int val; ListNode next = null; public ListNode(int val) { this.val = val; } } public class Partition { public ListNode partition(ListNode pHead, int x){ ListNode bs = null; ListNode be = null; ListNode as = null; ListNode ae = null; ListNode cur = pHead; while(cur != null){ if(cur.val < x){ }else{ } } } }

        代码的大体框架已经写好,这时我们需要考虑一下当第一段插入第一个节点时,第一个节点既是头又是尾。这时有需要分两种情况,第一次插入与下一次插入,来移动be引用。

 while(cur != null){ if(cur.val < x){ //第一次插入 if(bs == null){ be = bs = cur; }else { be.next = cur; be = cur; } }else{ //第一次插入 if(as == null){ ae = as = cur; }else { ae.next = cur; ae = cur; } } cur = cur.next;

       这时的代码还存在一种我们没有想到的情况,如果给定的测试用例都大于x的值呢。这是我们就需要返回as。我们还需要分情况。

 if(bs == null){ return as; }

       如果这是我们放到OJ进行测试,发现报出异常。

        这个异常的原因比较隐蔽。因为bs为空,尾节点ae会返回bs,如果ae之前的地址指向之前的某个节点,就会造成链表的死循环,此时我们要将排列之后的链表最后一个节点手动置为null。

完整代码: 

class ListNode{ int val; ListNode next = null; public ListNode(int val) { this.val = val; } } public class Partition { public ListNode partition(ListNode pHead, int x){ ListNode bs = null; ListNode be = null; ListNode as = null; ListNode ae = null; ListNode cur = pHead; while(cur != null){ if(cur.val < x){ //第一次插入 if(bs == null){ be = bs = cur; }else { be.next = cur; be = cur; } }else{ //第一次插入 if(as == null){ ae = as = cur; }else { ae.next = cur; ae = cur; } } cur = cur.next; } if(bs == null){ return as; } be.next = as;//连接上两个链表 if(as != null){ ae.next = null; } return bs; } }

1.2. 链表的回文结构

       第一种思路,我们可以使用双引用算法,一个left引用从左开始向右移动,另一个right引用从右开始向左移动。但由于这个链表是单链表,只能从一个方向遍历。

       第二种思路,把链表里的val值取出,存进一个数组中,但题目要求空间复杂度为

O(n)

 。

       第三种思路,翻转一半的链表。过程分为三步,第一步,找到链表的中间部分;第二步,将链表的一半进行翻转。我们在上一期中,已经实现了翻转链表和寻找链表的中间节点。

while(cur != null){ curN = cur.next; cur.next = slow; slow = cur; cur = curN; }

        利用上面的代码我们就可以来翻转链表,第三步,head从前往后,slow从后往前,比较head.val是否与slow.val相等,直到slow引用与头节点相遇为止。这里我们讨论的是奇数个节点的链表,如果是有偶数个节点的链表,那么fast为空。

       当链表的节点为偶数时,我们不能按照之前的做法,当head.next = slow时,直接返回true。

完整代码:

import java.util.Scanner; class ListNode{ int val; ListNode next; public ListNode(int val) { this.val = val; } } public class PalindromeList { public boolean chkPalindrome(ListNode A){ //1、找到链表的中间节点 ListNode fast = A; ListNode slow = A; while(fast != null && fast.next != null){ fast = fast.next.next; slow = slow.next; } //2、反转链表 ListNode cur = slow.next; while(cur != null){ ListNode curN = cur.next; cur.next = slow; slow = cur; cur = curN; } //3、引用A与slow同时移动 while(A != slow){ if(A.val != slow.val){ return false; } //判断偶数个节点情况 if(A.next == slow){ return true; } A = A.next; slow = slow.next; } return true; } }

1.3. 相交链表

       对于链表相交的问题,我们首先要考虑明白,到底是X形相交,还是Y形相交?

       如上图所示,很明显两个链表相交之后呈Y形。要解决问题,我们同样利用双引用的算法。第一步,先求出两个链表的长度len1、len2;第二步求出两个链表的长度差len=len1-len2;第三步,让长的链表先走len步;第四步,headA与headA同时走,相遇之后就是公共交节点。

      这个题的思路其实很简单,但是其中也有一些比较麻烦的步骤。在第二步中,如果说len1<len2,那么len<0。第三步中,我们又怎么知道哪个链表更长?

class ListNode{ int val; ListNode next; ListNode(int x){ val = x; next = null; } } public class Solution { public ListNode getIntersectionNode(ListNode headA, ListNode headB){ ListNode pl = headA;//先假设链表A是长的 ListNode ps = headB; //求两个链表的长度 int len1 = 0,len2 = 0; while(pl != null){ len1++; pl = pl.next; } while(ps != null){ len2++; ps = ps.next; } pl = headA; ps = headB; //求链表的长度差 int len = len1 - len2; if(len < 0){ pl = headB; ps = headA; len = len2-len1; } //让pl先走len步 while(len != 0){ pl = pl.next; len--; } //pl与ps同时走,知道相遇。 while(pl != ps){ pl = pl.next; ps = ps.next; } //如果没有公共节点,直接返回null if(pl == null){ return null; } return pl; } }

1.4. 环形链表

        快慢引用,即慢引用⼀次⾛⼀步,快引用⼀次⾛两步,两个引用从链表起始位置开始运⾏,如果链表带环则⼀定会在环中相遇,否则快引用率先⾛到链表的末尾。与现实生活中不同,两人相遇有可能是擦肩而过。而引用确实一步一步走的。

        为什么要让快引用走两步,慢引用走一步呢?我们想象一种最极限的情况,当fast刚走完一圈时,slow刚进入环内,两个引用的距离差刚好是一个环的距离。当fast与slow每走一次,它们的距离就越接近一个环。

class ListNode{ int val; ListNode next; ListNode(int x){ val = x; next = null; } } 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; } } 

Read more

AIGC时代大模型幻觉问题深度治理:技术体系、工程实践与未来演进

AIGC时代大模型幻觉问题深度治理:技术体系、工程实践与未来演进

文章目录 * 一、幻觉问题的多维度透视与产业冲击 * 1.1 幻觉现象的本质特征与量化评估 * 1.2 产业级影响案例分析 * 二、幻觉问题的根源性技术解剖 * 2.1 数据污染的复合效应 * 2.1.1 噪声数据类型学分析 * 2.1.2 数据清洗技术实现 * 2.2 模型架构的先天缺陷 * 2.2.1 注意力机制的局限性 * 2.2.2 解码策略的博弈分析 * 2.3 上下文处理的边界效应 * 三、多层次解决方案体系构建 * 3.1 数据治理体系升级 * 3.1.1 动态数据质量监控 * 3.1.2 领域知识图谱构建 * 3.

By Ne0inhk
2025年AI代码生成工具推荐:从Copilot到Cursor全面测评

2025年AI代码生成工具推荐:从Copilot到Cursor全面测评

在人工智能技术飞速发展的2025年,AI编程助手已经成为开发者必不可少的工具。孙睿团队基于大量用户反馈,对当前主流的AI代码生成工具进行了全面分析,旨在为开发者提供客观的参考依据。 工具核心价值分析 AI代码生成工具的核心价值在于提升开发效率、降低编码门槛。根据孙睿团队的观察,优秀的工具应当具备以下特质: * 精准的代码生成能力 * 流畅的开发者体验 * 高效的错误检测机制 * 灵活的定制化选项 * 合理的资源消耗 主流工具深度评测 1. Lynx AI:智能全栈开发平台 核心特点 Lynx AI作为自然语言驱动的智能全栈应用开发平台,能够将自然语言需求转化为完整的Web应用。用户只需描述业务需求,系统即可自动生成包括响应式前端、后端逻辑和数据库结构在内的全功能项目。 技术优势 * 无代码开发环境,支持零基础用户 * 深度集成可交互的Mock数据 * 多端自适应布局 * 一键部署全站 * 与主流开源CMS框架深度整合 适用场景 * 个人博客快速搭建 * 中小企业业务系统开发 * 产品原型快速验证 * 教育培训场景演示 2. Code

By Ne0inhk

了解ASR(自动语音识别)和模型Whisper

ASR是自动语音识别技术,现代端到端的主流ASR架构为: 音频 → [预处理 → 神经网络编码 → 解码] → 文本                ↑                                           ↑            信号处理                          深度学习 Whisper 是由 OpenAI 于 2022 年发布的开源语音识别模型。它是一个基于 Transformer 架构的端到端模型,具有以下核心特点:多任务模型、多语言支持、多种格式、强鲁棒性和无需微调开箱即用。 一、ASR 音频输入与预处理一般通过ffmpeg与VAD配合完成 1、特征提取与编码 现在的ASR通常使用声学特征直接输入神经网络。 常见的声学特征有以下四种,但是现在一般直接使用神经网络自动学习特征,例如Conformer编码器就是神经网络组成的。 * MFCC(梅尔频率倒谱系数):13-40维 * 梅尔频谱(Mel-Spectrogram):80-128维   * 滤波器组(Filter Bank):40-80维 * 原

By Ne0inhk
停止把项目扔在GitHub吃灰:为你的AIGC工作流,找一个技术买家和变现平台

停止把项目扔在GitHub吃灰:为你的AIGC工作流,找一个技术买家和变现平台

如果你的LangChain脚本、精调模型或提示词工程库,始终无法跨越从“个人项目”到“商业产品”的鸿沟,那么你错失的不只是收入,更是技术价值的定义权。 作为一名开发者,你是否也陷入了这个典型的技术-商业断层? 在GitHub上:你拥有一个获得几百Star的AIGC项目。它设计精良,README详细,解决了某个垂直领域(如自动化代码审查、智能运维日志分析)的真实痛点。Issue区零星有人问:“这个怎么用?能商业合作吗?” 在现实中:每次沟通都像是从零开始。你需要解释环境配置、API密钥、参数调优,甚至为不同客户定制输入输出格式。这些工程支持消耗的时间,远超项目开发本身。最终,你的技术价值被稀释成“劳务费”,而那个精巧的技术架构,始终未能成为可以独立销售的数字资产。 核心问题浮出水面:开发者的AIGC解决方案被困在 “可运行的项目” 与 “可交易的产品” 之间。缺少的,是一套能将你的技术能力标准化、封装化、并自动化交付的 “技术资产化基础设施”。 聚量库的工程化解法:为你的代码构建“商业接口” 我们旨在成为AIGC开发者的

By Ne0inhk