单链表操作实战:指针移动、增删反转与高频面试题全解析
单链表这东西我在面试别人和带新人时见过太多翻车现场。明明课本上都背过一上手写就丢链、死循环、空指针三连。这篇我不想按教科书顺序讲而是把链表操作里最容易出问题的地方、指针到底怎么动、代码怎么写才不崩结合图文拆开揉碎了说清楚。不管你是刚学数据结构的在校生还是准备面试的求职者或者工作中偶尔要手写数据结构的开发者这篇都能给你省下不少折腾时间。我会从最基础的结构定义开始一步一步带你写出健壮的链表操作代码。1. 为什么现在还要写单链表先搞清楚它到底解决什么问题1.1 数组和链表的核心差异很多人一开始学链表都有个疑问数组用起来好好的下标访问多方便为什么要搞一个只能顺着往后找的结构这个问题的答案藏在内存分配方式里。数组在内存里是一块连续空间你声明int arr[100]编译器就给你划出 400 字节连在一起的区域。访问arr[5]时底层直接算起始地址 5 * 4就拿到了。这是数组最大的优势也是它最大的软肋必须预先知道最大容量扩容时往往要整体搬家。单链表则完全相反。它的每个节点可以散落在内存任何角落节点之间靠指针串联。每个节点包含两部分一个存数据一个存下一个节点的地址。这种结构带来两个直接好处插入和删除不需要移动其他元素只要改指针指向就行容量天然是动态的用多少就分配多少但代价也很明确按位置访问只能从头开始遍历平均时间复杂度是 O(n)。这不叫缺陷而是设计取舍——你用随机访问能力换来了更灵活的内存管理和 O(1) 的插入删除。1.2 单链表到底被用在哪些真实场景里别看单链表在教科书里已经老掉牙真实工程里它依然是很多系统的地基。最常见的是 LRU 缓存淘汰算法。我用数组实现过一版 LRU每次访问都要把数组整体挪一遍数据量一上去就肉眼可见地卡顿。换成链表加哈希表的组合后最近访问的节点移动到头部最久未用的从尾部摘除全部操作都是 O(1) 级别。另一个典型场景是内存池管理。很多嵌入式系统和游戏引擎里空闲内存块就用一个空闲链表串起来分配时从头部取一个节点释放时挂回链表。这里链表天然适合因为块大小不固定连续数组根本没法灵活切分。还有哈希表处理冲突的链地址法本质就是数组 链表的组合结构。每个桶后面挂一条链表冲突的键值对依次往后排。Java 的 HashMap 在链表过长时还会把链表转成红黑树但底层仍然是链表思想。1.3 图文拆解单链表的存储结构长什么样我们先看单链表在内存里到底是什么样。假设有三个节点分别存着数字 1、2、3内存地址 数据区 指针区 0x100 1 0x200 0x200 2 0x300 0x300 3 0x000NULL从逻辑视角看就清晰得多head | v [1 | *] -- [2 | *] -- [3 | NULL]每个方括号代表一个节点左边是数据域右边是指针域。最后一个节点的指针指向 NULL这是链表终止的标记NULL 就是那条无处不在的线路终点。而 head 变量本身不存数据它只是一个指向第一个节点的指针变量。这里有个容易混淆的点head和第一个节点是两回事。head只是一个存放地址的变量而你操作链表时经常要声明临时指针cur、prev来辅助遍历和修改。搞不清这个区别后面写插入、删除时十有八九会写出野指针操作。2. 手写节点定义和基础操作一步一步构建你的链表2.1 C 语言和 Python 的节点定义与差异分析先看最经典的 C 语言定义typedef struct Node { int data; // 数据域这里以整数为例 struct Node *next; // 指针域指向下一个同类型节点 } Node; // 创建一个新节点 Node* create_node(int value) { Node* node (Node*)malloc(sizeof(Node)); if (node NULL) { printf(内存分配失败\n); exit(1); } node-data value; node-next NULL; return node; } // 创建带头节点的空链表 Node* create_list() { Node* head (Node*)malloc(sizeof(Node)); if (head NULL) { printf(内存分配失败\n); exit(1); } head-data 0; // 头节点数据一般不使用 head-next NULL; return head; }为什么我在 C 版本里用了头节点而不是直接让head指向第一个数据节点这是经验之谈。带头节点后链表的第一个真正数据节点就不需要特殊处理了插入和删除的逻辑可以统一起来。如果不带头节点你在头部插入或删除时就必须考虑当前操作的是不是第一个节点这件事每次都要把head本身作为可被修改的对象传入流程会复杂得多。Python 的版本则简洁一些class Node: def __init__(self, value): self.data value self.next None class LinkedList: def __init__(self): self.head Node(0) # 同样用一个带头节点简化边界处理 self.length 0Python 里没有指针的概念每个变量本质上都是对象的引用所以self.next存的其实是对另一个节点的引用。这个差异让 Python 代码看着比 C 简单但它隐藏了一个陷阱Python 赋值的本质是把引用赋值给变量你写cur cur.next时cur现在指向上一个节点的下一个节点但上一个节点本身的next不会因为你移动了cur而改变。理解了这一点用 Python 写链表才不会晕。2.2 遍历和查找的实现写对循环条件是关键链表操作里最基础、也最能看出功底的是遍历。很多人写遍历时死循环问题几乎都出在到底判断cur还是cur.next上。// C 语言遍历并打印所有数据节点 void traverse(Node* head) { Node* cur head-next; // 跳过带头节点 while (cur ! NULL) { printf(%d , cur-data); cur cur-next; } printf(\n); }注意这里的循环条件是cur ! NULL这意味着循环体里能够安全地访问cur-data。当你让cur cur-next时如果这是最后一个节点cur-next就是 NULL此时循环条件不满足自然退出。查找某个值是否存在也很直白int find(Node* head, int target) { Node* cur head-next; int index 0; while (cur ! NULL) { if (cur-data target) { return index; } cur cur-next; index; } return -1; }这里返回的是目标值在链表中的位置从 0 开始找不到就返回 -1。如果链表很长这种 O(n) 的遍历就是必要代价这也是为什么真实场景里链表往往配合哈希表一起用而不是单链表硬扛查询需求。2.3 尾部插入与头部插入两个方向两种写法尾部插入要处理的问题很纯粹找到链表末尾把新节点挂上去。void insert_at_tail(Node* head, int value) { Node* new_node create_node(value); Node* cur head; // 走到最后一个节点 while (cur-next ! NULL) { cur cur-next; } // 此时 cur 是尾节点 cur-next new_node; }这里注意循环条件变成了cur-next ! NULL。因为我要找的是尾节点本身而不是越过尾节点走到 NULL。如果我也写成cur ! NULL循环结束时cur已经是 NULL 了你根本没法再给 NULL 挂 next这就是经典的丢指针问题。头部插入因为用了带头节点逻辑变得非常统一void insert_at_head(Node* head, int value) { Node* new_node create_node(value); new_node-next head-next; head-next new_node; }这一步做了两件事先把新节点指向原来真正的第一个数据节点再让头节点指向新节点。顺序绝对不能反。如果先执行head-next new_node原链表就丢了因为你再也没有办法找到原来的第二个节点了。这个顺序问题就是链表操作最核心的心法先接好新节点的后路再用前一个节点指向新节点。2.4 按位置插入边界情况必须逐个排查按位置插入是初学者最大的坎因为要同时考虑三种情况插在头部、插在中间、插在尾部或者位置非法。int insert_at_index(Node* head, int value, int index) { // 头节点是第0个位置第一个数据节点是第1个位置 if (index 1) { return 0; // 非法位置 } Node* new_node create_node(value); Node* cur head; // 找到第 index-1 个节点也就是插入点的前驱 for (int i 1; i index cur ! NULL; i) { cur cur-next; } if (cur NULL) { // index 超出链表长度 free(new_node); return 0; } new_node-next cur-next; cur-next new_node; return 1; }这段代码的精髓在for循环的终止条件上i index cur ! NULL。一旦cur变成 NULL说明 index 超出了链表实际长度直接返回失败。我第一次写这个函数时没加cur ! NULL这个条件程序直接访问了 NULL 指针导致段错误排查了整整一下午。3. 图文拆解增删查的指针变动全过程3.1 插入操作图解新节点是怎么挤进链表的我们用一个具体例子演示中间插入。原链表是1 - 3 - 4现在要在第 2 个位置插入2也就是在节点1和节点3之间插入。第一步新建节点2此时它的 next 还是 NULLhead | v [1 | *] -- [3 | *] -- [4 | NULL] [2 | NULL] 新节点暂时孤立第二步让新节点的 next 指向3head | v [1 | *] -- [3 | *] -- [4 | NULL] ^ | [2 | *] -----此时新节点已经接上了后续链表所以链表不会断。哪怕后面操作出问题从1 - 3这条原路径还是能走通的。第三步让节点1的 next 指向新节点2head | v [1 | *] -- [2 | *] -- [3 | *] -- [4 | NULL]三步走完插入成功。我说过这句话现在再说一次核心口诀就是先接后路再改前路。两步的顺序不可交换是因为如果先把前一个节点的 next 改掉原链表后半段就找不到了。3.2 按值删除和按位置删除两步操作细节不同按值删除要遍历链表找到值匹配的节点把它从链上摘下来。这里必须记录被删除节点的前一个节点prev否则删除节点后没法把前后两段接起来。int delete_by_value(Node* head, int value) { Node* cur head-next; Node* prev head; while (cur ! NULL) { if (cur-data value) { prev-next cur-next; // 跳过当前节点 free(cur); // 释放内存 return 1; } prev cur; cur cur-next; } return 0; // 没找到 }图解这个删除过程假设链表是1 - 2 - 3要删除2head | v [1 | *] -- [2 | *] -- [3 | NULL] ^ ^ prev cur执行prev-next cur-next之后head | v [1 | *] ---------------- [3 | NULL] [2 | *] 准备 free图中能明显看到节点2已经不在主链表路径上了只是还被cur临时指着等free(cur)后它才真正从内存里消失。按位置删除和按值删除逻辑几乎一样只是找到目标节点的条件从节点值等于 value变成来到第 index 个位置。这里就不再重复写代码了但记住一个教训删除时要同时持有前驱prev和当前节点cur少一个都不行。3.3 反转链表三个指针翻转方向的完整演示反转链表是面试高频题也是检验链表操作功力的试金石。它的思路其实不复杂用三个指针prev、cur、next遍历链表逐个把cur-next指向prev。void reverse(Node* head) { Node* prev NULL; // 原链表第一个数据节点的前驱 Node* cur head-next; // 从第一个数据节点开始 Node* next NULL; while (cur ! NULL) { next cur-next; // 先保存当前节点的下一节点 cur-next prev; // 把当前节点的指针翻转 prev cur; // prev 前移 cur next; // cur 前移 } // 遍历结束prev 就是新的第一个数据节点 head-next prev; }我用图示演示1 - 2 - 3的反转过程。初始状态NULL - [1] [2] [3] ^ ^ prev cur next 2 的 next也就是 节点3第一次循环后NULL - [1] - 之后 [2] ^ cur-next 已经指向 prev1注意此时 节点2 指向1链表暂时分成两段 [3] 保持不动由 next 保存第一次循环结束后prev 指向节点 1cur 指向节点 2中间断了。重复这个流程直到 cur 为 NULL最后head-next prev整个链表就翻转过来了。这个过程最反直觉的地方在于链表在反转过程中会暂时断裂这是正常的。只要next指针还保存着后半段链表的入口就可以放心翻转方向。我见过很多人反转写到一半发现链表抓不住就慌了其实都是next没有保存好。3.4 链表长度计算与判断空链表不能忽略的基础功int get_length(Node* head) { int length 0; Node* cur head-next; while (cur ! NULL) { length; cur cur-next; } return length; } int is_empty(Node* head) { return head-next NULL; }计算长度看起来简单但如果链表有环某个节点的 next 指向前面的节点这段代码就会死循环。所以更严密的做法是配合快慢指针先判环再去求长度。小技巧我放在后面的排查章节详细说。4. 高频面试题与实战场景单链表还能玩出什么花样4.1 找倒数第 K 个节点快慢指针的经典用法这道题在很多大厂笔试和面试中反复出现。常规思路是先遍历一遍算出长度再走一遍长度 - K个节点。但这个方案要遍历两遍面试官往往不满意。更巧妙的做法是快慢指针。让快指针先走 K 步然后慢指针和快指针同步走当快指针走到 NULL 时慢指针指向的恰好就是倒数第 K 个节点。Node* find_kth_from_end(Node* head, int k) { Node* fast head-next; Node* slow head-next; // 快指针先走 k 步 for (int i 0; i k; i) { if (fast NULL) { return NULL; // K 超过链表长度 } fast fast-next; } // 两个指针同时移动 while (fast ! NULL) { fast fast-next; slow slow-next; } return slow; }这个方案的核心思想是让快指针和慢指针之间始终保持 K 个节点的距离差。当快指针到达链表末尾时慢指针停下来时自然就停在倒数第 K 位。整个逻辑只看一遍链表时间复杂度 O(n)空间复杂度 O(1)。4.2 判断链表是否有环快慢指针的不同玩法判环问题也是面试常客。思路是用两个指针一个每次走一步一个每次走两步。如果链表有环快指针最终会追上慢指针两者在环里相遇如果没有环快指针会先走到 NULL。int has_cycle(Node* head) { Node* slow head-next; Node* fast head-next; while (fast ! NULL fast-next ! NULL) { slow slow-next; // 走一步 fast fast-next-next; // 走两步 if (slow fast) { return 1; // 有环 } } return 0; // 无环 }为什么快指针每次走两步而不是三步、四步因为两步能保证快指针不会跳过慢指针。如果间隔是奇数快指针三步走时可能直接跨过慢指针而错过相遇两步则每次相对距离缩小 1必然相遇。发现环之后如果要找到环的入口节点就需要一点数学推导。设链表起点到环入口的距离为 a环入口到相遇点的距离为 b环长度为 c。慢指针走了 ab 步快指针走了 2(ab) 步。快指针比慢指针多走了一圈所以得到2(ab) - (ab) c即ab c。于是从起点和相遇点同时出发每次各走一步再次相遇时就是环入口。Node* detect_cycle_start(Node* head) { Node* slow head-next; Node* fast head-next; // 先找到相遇点 while (fast ! NULL fast-next ! NULL) { slow slow-next; fast fast-next-next; if (slow fast) { break; } } if (fast NULL || fast-next NULL) { return NULL; // 无环 } // 一个从起点出发一个从相遇点出发 slow head-next; while (slow ! fast) { slow slow-next; fast fast-next; } return slow; }4.3 合并两个有序链表递归与迭代两种思路对照合并两个升序链表把结果依旧保持升序。这里有两类写法迭代和递归。迭代法更符合人的直觉递归法代码更短但需要理解递归栈。迭代版Node* merge_two_lists(Node* l1, Node* l2) { // 假设这里已经解决带头节点的问题直接用两个链表的第一个数据节点 Node dummy; // 一个在栈上的临时头节点 Node* cur dummy; while (l1 ! NULL l2 ! NULL) { if (l1-data l2-data) { cur-next l1; l1 l1-next; } else { cur-next l2; l2 l2-next; } cur cur-next; } if (l1 ! NULL) { cur-next l1; } if (l2 ! NULL) { cur-next l2; } return dummy.next; }这里用了一个栈上的dummy节点作为结果链表的临时头避免判断结果链表的第一个节点到底来自哪边这个麻烦问题。这种用一个临时哨兵节点来统一处理头部插入的技巧在实际工程里也非常常用。4.4 实战场景还原LRU 缓存用链表怎么实现LRU 缓存的实现可以当作链表项目的第一次实战。基本结构是哈希表 双向链表这里为了体现单链表内容我简化成单向版本但扩展思路是相通的。访问一个 key 时从哈希表找到链表节点把它从当前位置摘除并移动到链表头新增一个 key 时先检查容量超了就把链表尾部的节点摘除并删除哈希表条目然后插入新节点到头部单向链表做移动操作要额外处理前驱所以真实 LRU 用双向链表更方便。但用单向链表实现一遍你就会深刻理解为什么需要双向链表——因为你不保存前驱的话删除当前节点还得重新从头遍历。这正是你亲手把数组 vs 链表单向 vs 双向这些对比体会一遍的好机会。5. 常见报错排查与调试技巧写链表不崩的独家心得5.1 空指针访问到底是怎么发生的段错误Segmentation Fault是链表新手最容易遇到的问题但在排查之前你得先理解它的成因。空指针访问通常发生在你尝试访问NULL-data或NULL-next的时候。最常见的错误示范// 错误写法循环条件判断错了 void bad_traverse(Node* head) { Node* cur head; while (cur ! NULL) { printf(%d , cur-data); cur cur-next; } }表面上看起来没问题但如果我传入的链表带头节点第一次进入循环时head-data可能没意义打印出来是垃圾值。更关键的问题是如果头节点根本没有初始化next字段cur-next可能是野指针这种错误极难排查。另一个经典错误是在删除时没有正确处理前驱导致删除后链断裂// 错误写法中途丢失链表指针 void bad_delete(Node* head, int value) { Node* cur head-next; while (cur ! NULL) { if (cur-data value) { // 没有保存 prev直接释放 cur free(cur); // 链表在这里断了 break; } cur cur-next; } }这种代码运行后剩余链表根本无法继续遍历而且释放后的节点内存可能被系统回收之后再访问就是未定义行为。排查提示如果你在 FreeBSD 或 Linux 下用 gdb 调试遇到段错误后用bt命令查看调用栈定位到崩溃的那一行基本就能看见是不是在访问空指针或野指针。5.2 死循环的排查技巧是不是链表成环了另一个高频 bug 是程序陷入死循环。最典型的原因是遍历条件写成了while (cur-next ! NULL)但忘记初始化新节点的 next 为 NULL// 错误示范新节点 next 没有初始化 Node* create_node(int value) { Node* node (Node*)malloc(sizeof(Node)); node-data value; // 忘记写 node-next NULL; return node; }这样最后一个节点的 next 是未知的垃圾值遍历时永远到不了 NULL程序自然死循环。排查方法有三个用 gdb 打断点看当前遍历到哪一步检查cur的地址是否出现重复打印链表长度如果超过预期就应该怀疑成环用快慢指针思路写一个临时脚本去判环能定位环是否存在5.3 图文总结链表操作三步自查清单我把所有经验浓缩成一份自查清单每次写完链表代码都可以过一遍操作类型关键检查点典型错误插入节点新节点 next 是否正确指向下一个节点先改前驱 next导致原链表丢失删除节点前驱节点 next 是否跳过被删节点忘了保存 prev释放后链断裂遍历链表循环终止条件是否依赖 NULL用cur-next判断终止导致尾节点漏处理反转链表是否保存 next 指针反转后丢失后半段链表判环算法快指针是否先走到 NULL快指针空指针访问每个链表操作写完在纸上画一遍指针变化图比追着代码干瞪眼效率高得多。5.4 调试链表的神器可视化打印函数程序员之间口口相传的一个技巧是写一个打印函数专门用来调试。不是简单的printf(%d , data)而是把节点的地址、值、next 地址都打出来void debug_print(Node* head) { Node* cur head; int count 0; while (cur ! NULL count 50) { printf([%p] %d - %p\n, (void*)cur, cur-data, (void*)cur-next); cur cur-next; count; } if (count 50) { printf(警告疑似链表成环打印终止\n); } }这个函数能帮你一眼看出链表结构是否完整比如某节点的 next 地址和预期不符或者出现重复地址就说明成环了。每次写完一个操作函数用这个函数验证一遍能帮你省下大量排查时间。尤其是反转链表后、删除节点后这些关键节点打一遍确认结构对不对。根据我个人这么多年的经验链表操作的核心就是不慌遵循先接线再改线的原则配合可视化打印逐步确认基本没有写不出来的操作。实际工作中这些代码可能不会直接出现在项目里但理解链表的数据流动方式对你理解指针、理解内存、理解更复杂的数据结构都有不可替代的助力。

相关新闻

2026远程桌面选型指南:绕过NAT、低延迟、免客户端的实战方案

2026远程桌面选型指南:绕过NAT、低延迟、免客户端的实战方案

1. 这不是选软件,是选“第二台电脑”的使用权“远程桌面软件哪个好?”——这句话背后藏着的,根本不是技术参数对比,而是一个更现实的问题:我能不能把家里的主力电脑,当成随身携带的笔记本用?我试…

2026/10/12 2:43:33 阅读更多 →
Spring Boot + Vue 全栈项目部署实战:从服务器初始化到 HTTPS 上线全攻略

Spring Boot + Vue 全栈项目部署实战:从服务器初始化到 HTTPS 上线全攻略

1. 部署前的整体规划与技术选型1.1 这个考试网站到底是什么,部署要解决哪些问题先交代一下背景。我手头这个项目是一个面向个人的考试练习网站,核心功能包括在线答题、错题记录、成绩统计、题目导入导出,还有一个简单的后台管理模块。整套东西…

2026/10/12 2:43:33 阅读更多 →
ChatGPT引入广告,开发者如何构建内容过滤与广告识别方案

ChatGPT引入广告,开发者如何构建内容过滤与广告识别方案

这次我们不看本地部署,换一个产品动态:OpenAI 官方宣布,正在 ChatGPT 中逐步引入广告。消息本身不长,但影响面不小。如果你只是用 ChatGPT 写文案,可能只会在界面上多看到几个推广位;如果你是做 GPT 应用开…

2026/10/12 2:43:33 阅读更多 →

最新新闻

Docker运行Redis生产实践:从单节点到集群的高可用落地

Docker运行Redis生产实践:从单节点到集群的高可用落地

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 4:59:19 阅读更多 →
线性回归正规方程:闭式解原理、实现与工程权衡

线性回归正规方程:闭式解原理、实现与工程权衡

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 4:59:19 阅读更多 →
Llama 3架构精读与FP8量化实战:128K长上下文压测

Llama 3架构精读与FP8量化实战:128K长上下文压测

Llama 3 发布那几天,整个模型圈都在刷分。我花了两天时间把技术报告和权重包全部过了一遍,顺手在现有的推理环境里做了 FP8 量化,又把长上下文的压力测试跑了几轮。今天这篇文章就是把“精读笔记 量化实操 长上下文实测”三个部分完整复盘一…

2026/10/13 4:59:19 阅读更多 →
软件工程开发阶段实战:从编码到可交付的工程化指南

软件工程开发阶段实战:从编码到可交付的工程化指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 4:59:19 阅读更多 →
SSM框架实战:求职招聘信息管理系统设计与实现

SSM框架实战:求职招聘信息管理系统设计与实现

1. 项目概述与需求拆解做Java Web开发这几年,SSM框架几乎是我接手毕业设计和企业小项目时最常碰到的组合。这个"求职招聘信息管理系统"表面上看着是个典型的管理系统,实际上把SSM框架的三大组件(Spring、SpringMVC、MyBatis&#x…

2026/10/13 4:59:19 阅读更多 →
实习考勤不内耗:习讯云打卡全流程避坑指南

实习考勤不内耗:习讯云打卡全流程避坑指南

实习第一个月,我差点被习讯云实习考勤打卡逼到内耗。每天到公司第一件事不是换工牌,而是掏出手机打开习讯云,看定位转圈、看按钮变灰、看页面弹“不在考勤范围”,一套流程下来,本来就不多的精气神先耗掉三分之一。后来…

2026/10/13 4:58:19 阅读更多 →

日新闻

物联网系统集成避坑指南:从选型到验收的关键要点

物联网系统集成避坑指南:从选型到验收的关键要点

做了这么多年物联网系统集成的项目,我最大的感受是,大部分坑不是出在技术前沿,而是出在一些看起来很平常的地方。设备选了便宜的、协议没问清楚、现场没摸透、验收标准写得含糊——这些细枝末节,最后都会变成延期、超支和扯皮的大…

2026/10/13 0:00:57 阅读更多 →
ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

如果你玩ESP32有一阵子了,很难不碰到舵机控制这道坎。做智能小车要转方向,做机械臂要抬关节,做云台要偏航俯仰,背后都是同一个东西在干活:舵机。而我见过太多人卡在“代码能编译、舵机就是不听话”这个阶段&#xff0c…

2026/10/13 0:00:57 阅读更多 →
CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

1. 为什么CAN总线至今仍是工业现场的“硬通货”?——从汽车底盘到智能温室的真实需求你有没有拆过一辆老款燃油车的中控台?或者调试过某高校实验室里那台跑着Modbus协议却频频丢帧的PLC控制柜?又或者,在某个农业物联网项目里&…

2026/10/13 0:00:57 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/13 2:54:56 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/12 0:16:38 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 21:05:09 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 14:48:49 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 18:11:17 阅读更多 →