《链表面试基础看点:这里不止“快慢指针”的完美实现,更懂“哨兵节点”的巧妙运用》

《链表面试基础看点:这里不止“快慢指针”的完美实现,更懂“哨兵节点”的巧妙运用》

🔥@晨非辰Tong:个人主页 

👀专栏:《C语言》《数据结构与算法》《数据结构与算法刷题集》

💪学习阶段:C语言、数据结构与算法初学者

⏳“人理解迭代,神理解递归。”


引言:链表刷题进行时。寻找中间结点,看似简单,但你的解法是否考虑了所有边界情况?本文手把手带你用“快慢指针”写出完美解,以及“哨兵节点”对合并链表的简化实现。

目录

1.  876. 链表的中间结点 - 力扣(LeetCode)(快慢指针)

2.  21. 合并两个有序链表 - 力扣(LeetCode)


1.  876. 链表的中间结点 - 力扣(LeetCode)(快慢指针)

方法一:遍历链表计算总大小,算出mid,将首节点指针向后mid个节点。(容易想到)

方法二:使用快、慢指针,慢指针动一步、快指针动两步。

/** * Definition for singly-linked list. * struct ListNode { * int val; * struct ListNode *next; * }; */ typedef struct ListNode ListNode; struct ListNode* middleNode(struct ListNode* head) { //创建快慢指针 ListNode* fast, *slow; //首先都指向头节点 fast = head; slow = head; //循环条件将两种情况都包含 while(fast != NULL && fast->next != NULL)//条件换顺序? { //慢指针移动一节点 slow = slow->next; //快指针移动两个节点 fast = fast->next->next; } //返回slow return slow; }
--为什么慢指针移动一个节点,快指针移动两个节点,是固定的吗?
        :很好理解,因为是找 mid ,以慢指针所在节点为关注,那么快指针移动的是慢指针的两倍—>当快指针移动到尾节点或空,那么慢指针就指向 mid 。

--循环条件为什么不是或的关系?
        :在循环体中实现的的是奇数或偶数个节点链表的寻找,那么只要两个条件只有都满足才能进行。

    

--上面提到,循环条件可以换位置吗?

        :对偶数个节点进行分析


2.  21. 合并两个有序链表 - 力扣(LeetCode)(“哨兵”位)

  • 方法一:创建新链表,比较大小,小的尾插新链表
/** * Definition for singly-linked list. * struct ListNode { * int val; * struct ListNode *next; * }; */ typedef struct ListNode ListNode; struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) { //判断两个链表是否是空链表 if(list1 == NULL) { return list2;//直接返回list2 } if(list2 ==NULL) { return list1;//直接返回list1 } //创建空链表:空的首节点 ListNode* newhead, *newtail; newhead = newtail = NULL; //两个指向指针 ListNode* l1 = list1; ListNode* l2 = list2; //遍历原链表 //先将相同长度内的节点进行比较 while(l1 != NULL && l2 != NULL) { if(l1->val < l2->val) { //l1尾插 //第一回尾插,链表为空 if(newhead == NULL) { newhead = newtail = l1;//为头、为尾 } //后续尾插 else { newtail->next = l1; newtail = newtail->next; //尾后移 } l1 = l1->next;//链表1后移 } else { //l2尾插 //第一回尾插,链表为空 if(newhead == NULL) { newhead = newtail = l2;//为头、为尾 } //后续尾插 else { newtail->next = l2; newtail = newtail->next; //尾后移 } l2 = l2->next; } //跳出循环,代表有链表走到了空 if(l1) { newtail->next = l1; } if(l2) { newtail->next = l2; } } return newhead; }
--前面进行的单独判断为空:防止后续解引用空指针。

--最后面的是判断哪个链表走到了空,直接将另一个链表链接到尾节点。
  • 改良:减少代码冗余
--发现上面的两串代码重复性很高,为什么??

        :初始时,设置的头节点、尾节点为空,导致尾插都要判断是否为空——>创建非空链表?——>可以!
typedef struct ListNode ListNode; struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) { //判断两个链表是否是空链表 if(list1 == NULL) { return list2;//直接返回list2 } if(list2 ==NULL) { return list1;//直接返回list1 } //创建非空链表:申请一份节点的空间 ListNode* newhead, *newtail; newhead = newtail = (ListNode*)malloc(sizeof(ListNode)); //两个指向指针 ListNode* l1 = list1; ListNode* l2 = list2; //遍历原链表 //先将相同长度内的节点进行比较 while(l1 != NULL && l2 != NULL) { if(l1->val < l2->val) { //l1尾插 newtail->next = l1; newtail = newtail->next; //尾后移 l1 = l1->next;//链表1后移 } else { //l2尾插 newtail->next = l2; newtail = newtail->next; //尾后移 l2 = l2->next; } //跳出循环,代表有链表走到了空 if(l1) { newtail->next = l1; } if(l2) { newtail->next = l2; } } //手动申请的空间自己要释放 //释放前,保存节点 ListNode* retnewhead = newhead->next; free(newhead); newhead = NULL; return retnewhead; }
改良:申请一份节点大小的空间,让头、尾节点直接指向这个空间(“哨兵节点”),就不需要后面的判断是否为空链表了。



--不是遇到重复代码,可以封装函数吗?

       
:没错,但只是代码的呈现形式改变了而已,实际的逻辑没有变。
--后面虽然结束后,会自动释放,但是还要保持良好的习惯。

--后面新创建的 retnewhead 指向的是 newhead 的下一个节点(不要倒在小错误上!)。

回顾:

《一招吃透链表操作:三指针反转法,面试遇到链表题再也不慌!》

《算法面试“必杀技”:双指针法高效解决数组原地操作》
结语:掌握“快慢指针”这一基础工具,就能优雅地解决“中间结点”问题,而“哨兵节点”的应用更为我们后续攻克“双向链表”等复杂题目打下了坚实基础下一题刷题之道,在于积跬步以致千里。,再见~~

Read more

GitHub Copilot 教程

文章来源:https://vscode.it-docs.cn/docs/copilot/overview.html GitHub Copilot 为 Visual Studio Code 增加了多代理开发功能。规划好你的方法,然后让AI代理在项目中实现并验证代码变更。并行运行多个代理会话:本地、后台或云端。从一个中心视角管理所有角色。内联建议、内联聊天和智能行为会帮助你完成整个编码流程。 代理与代理会话 代理端到端地处理完整的编码任务。给代理一个高级任务,它会将工作拆分成步骤,编辑文件,运行终端命令,调用工具,并在遇到错误或测试失败时自我纠正。每个任务都运行在一个代理会话中,这是一个持续存在的对话,你可以跟踪、暂停、继续或交接给另一个代理。 重要 你们组织可能在VS Code中禁用了代理。请联系你的管理员以启用此功能。 从中央视图管理会话 并行运行多个代理会话,每个会话专注于不同的任务。聊天面板中的会话视图为你提供了一个统一的地方来监控所有活跃会话,无论是本地运行、后台还是云端运行。查看每次会话的状态,切换,查看文件变更,

By Ne0inhk
Matlab Copilot_AI工具箱: 对接DeepSeek/Kimi/GPT/千问/文心一言等多款AI大模型,一站式提升编程效率

Matlab Copilot_AI工具箱: 对接DeepSeek/Kimi/GPT/千问/文心一言等多款AI大模型,一站式提升编程效率

🔥 为什么需要这款工具? * Matlab 2025虽自带Copilot功能,但受地区、许可证的限制,多数用户无法使用; * 在Matlab和ChatGPT、DeepSeek等AI模型之间来回切换操作繁琐,无法实现“所见即所得”的编程体验,且代码报错后的调试繁琐。 这款Matlab Copilot_AI工具箱作为Matlab与多款AI模型的对接载体,支持DeepSeek V3.2(基础/思考版)、Kimi K2、百度文心一言、阿里云通义千问、ChatGPT(百度千帆版)等模型,还支持4种自定义模型配置(可对接百度千帆平台近百种大模型); 工具直接在Matlab内(不限于2025a)运行,无需切换其他软件,支持“一键生成、运行、调试、修复bug、导出”全流程编程辅助,使用成本可控(单模型月均几元即可满足基础使用),且工具箱一次授权终身免费更新。 多款AI模型可选择,还支持四种自定义模型组合。 更新记录 1. 20260123更新至v4.0,更新:

By Ne0inhk

【AIGC】即梦omnihuaman-api调用实现

即梦数字人视频生成(Streamlit Demo) 基于 火山引擎即梦(Jimeng)CV API 的数字人视频生成示例项目。 支持 图片 + 音频驱动 的数字人视频生成流程,集成了主体检测、Mask 选择、Prompt 控制、视频生成与下载等完整功能,适合 内部测试 / 技术演示 / 二次开发。 一、功能概览 ✅ 核心功能 * 🔐 AK / SK 在线填写 * 支持火山引擎 Access Key / Secret Key 在页面中直接输入 * 无需写死在代码中,便于多账号切换 * api key申请地址:https://console.volcengine.com/iam/keymanage * 🖼 图片上传(人物图像) * 支持 JPG / PNG

By Ne0inhk

Stable Diffusion底模对应的VAE推荐:提升生成质量的关键技术解析

Stable Diffusion底模对应的VAE推荐:提升生成质量的关键技术解析 引言:VAE在Stable Diffusion生态系统中的核心作用 变分自编码器(VAE)是Stable Diffusion生成架构中不可或缺的组件,负责将潜在空间表示与像素空间相互转换。尽管常常被忽视,VAE的质量直接影响图像生成的细节表现、色彩准确性和整体视觉效果。本文将深入解析不同Stable Diffusion底模对应的最优VAE配置,从技术原理到实践应用全面剖析VAE的选择策略。 VAE在Stable Diffusion中的核心功能包括: * 编码过程:将输入图像压缩到潜在空间表示(latent representation) * 解码过程:将潜在表示重构为高质量图像 * 正则化作用:确保潜在空间遵循高斯分布,便于扩散过程采样 一、VAE技术原理深度解析 1.1 变分自编码器的数学基础 变分自编码器的目标是学习数据的潜在表示,其数学基础建立在变分推断之上。给定输入数据 x x x,VAE试图最大化证据下界(ELBO): log ⁡ p ( x ) ≥ E q ( z ∣

By Ne0inhk