<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title>豆浆小屋</title><link>https://soybeanmilk.netlify.app/</link><description>记录日常 · 漫画 · 一点小折腾</description><generator>Hugo -- gohugo.io</generator><language>zh-CN</language><copyright>This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.</copyright><lastBuildDate>Tue, 04 Aug 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://soybeanmilk.netlify.app/index.xml" rel="self" type="application/rss+xml"/><item><title>C++ 学习与面试资料导航</title><link>https://soybeanmilk.netlify.app/cpp%E5%AD%A6%E4%B9%A0%E8%B5%84%E6%96%99%E5%AF%BC%E8%88%AA/</link><pubDate>Fri, 09 Oct 2026 00:00:00 +0000</pubDate><author><name>作者</name></author><guid>https://soybeanmilk.netlify.app/cpp%E5%AD%A6%E4%B9%A0%E8%B5%84%E6%96%99%E5%AF%BC%E8%88%AA/</guid><description><![CDATA[<p>这份导航收集了一批公开可访问的 C++ 学习资料。它们各有侧重：有的适合建立知识地图，有的适合按题复习，有的则能直接跟着源码理解工程实现。</p>
<p>如果刚开始整理知识体系，建议先选一个综合站点作为主线，再用题库查漏补缺。项目源码更适合在掌握基础语法、CMake 和调试工具后动手。</p>
<h2 id="一系统知识库用来建立主线" class="headerLink">
    <a href="#%e4%b8%80%e7%b3%bb%e7%bb%9f%e7%9f%a5%e8%af%86%e5%ba%93%e7%94%a8%e6%9d%a5%e5%bb%ba%e7%ab%8b%e4%b8%bb%e7%ba%bf" class="header-mark"></a>1 一、系统知识库：用来建立主线</h2><h3 id="编程指北c-面试题库httpscsguidecncppintrohtml" class="headerLink">
    <a href="#%e7%bc%96%e7%a8%8b%e6%8c%87%e5%8c%97c-%e9%9d%a2%e8%af%95%e9%a2%98%e5%ba%93httpscsguidecncppintrohtml" class="header-mark"></a>1.1 <a href="https://csguide.cn/cpp/intro.html" target="_blank" rel="noopener noreferrer">编程指北：C++ 面试题库</a></h3><p>覆盖 C++ 基础、面向对象、内存管理和现代 C++ 等模块。分类清楚，适合先快速浏览目录，建立“还需要学什么”的整体地图。</p>
<h3 id="huihutcc-技术面试知识库httpsinterviewhuihutcom" class="headerLink">
    <a href="#huihutcc-%e6%8a%80%e6%9c%af%e9%9d%a2%e8%af%95%e7%9f%a5%e8%af%86%e5%ba%93httpsinterviewhuihutcom" class="header-mark"></a>1.2 <a href="https://interview.huihut.com/" target="_blank" rel="noopener noreferrer">huihut：C/C++ 技术面试知识库</a></h3><p>除了 C/C++，还包含 STL、数据结构、操作系统、网络与链接装载等内容。适合把语言知识和计算机基础串起来。</p>
<ul>
<li><a href="https://github.com/huihut/interview" target="_blank" rel="noopener noreferrer">对应的 GitHub 开源仓库</a></li>
</ul>
<h3 id="卡码笔记面试知识库httpsnoteskamacodercom" class="headerLink">
    <a href="#%e5%8d%a1%e7%a0%81%e7%ac%94%e8%ae%b0%e9%9d%a2%e8%af%95%e7%9f%a5%e8%af%86%e5%ba%93httpsnoteskamacodercom" class="header-mark"></a>1.3 <a href="https://notes.kamacoder.com/" target="_blank" rel="noopener noreferrer">卡码笔记：面试知识库</a></h3><p>覆盖语言、操作系统、网络和并发等常见方向，适合按模块复习，并顺着题目继续追问底层机制。</p>
<h3 id="小林-codingc-面试题httpsxiaolincodingcominterviewcpphtml" class="headerLink">
    <a href="#%e5%b0%8f%e6%9e%97-codingc-%e9%9d%a2%e8%af%95%e9%a2%98httpsxiaolincodingcominterviewcpphtml" class="header-mark"></a>1.4 <a href="https://xiaolincoding.com/interview/cpp.html" target="_blank" rel="noopener noreferrer">小林 coding：C++ 面试题</a></h3><p>以问题为入口，配有较多图解。遇到内存管理、STL 或语言特性等问题时，可以用来辅助理解。</p>
<h2 id="二逐题练习用来查漏补缺" class="headerLink">
    <a href="#%e4%ba%8c%e9%80%90%e9%a2%98%e7%bb%83%e4%b9%a0%e7%94%a8%e6%9d%a5%e6%9f%a5%e6%bc%8f%e8%a1%a5%e7%bc%ba" class="header-mark"></a>2 二、逐题练习：用来查漏补缺</h2><h3 id="面试鸭c-基础面试题库httpswwwmianshiyacombank1810642495797854209" class="headerLink">
    <a href="#%e9%9d%a2%e8%af%95%e9%b8%adc-%e5%9f%ba%e7%a1%80%e9%9d%a2%e8%af%95%e9%a2%98%e5%ba%93httpswwwmianshiyacombank1810642495797854209" class="header-mark"></a>2.1 <a href="https://www.mianshiya.com/bank/1810642495797854209" target="_blank" rel="noopener noreferrer">面试鸭：C++ 基础面试题库</a></h3><p>适合快速检查基础题覆盖情况。使用时不要只背结论，可以继续追问：为什么这样设计、底层如何实现、边界在哪里。</p>
<h3 id="拾光小舟c-基础面试集httpsblogtttxiaozcnpostsinterviewc-e59fbae7a180" class="headerLink">
    <a href="#%e6%8b%be%e5%85%89%e5%b0%8f%e8%88%9fc-%e5%9f%ba%e7%a1%80%e9%9d%a2%e8%af%95%e9%9b%86httpsblogtttxiaozcnpostsinterviewc-e59fbae7a180" class="header-mark"></a>2.2 <a href="https://blog.tttxiaoz.cn/posts/interview/c-%E5%9F%BA%E7%A1%80/" target="_blank" rel="noopener noreferrer">拾光小舟：C++ 基础面试集</a></h3><p>按“回答重点”和扩展内容组织，适合把零散知识整理成能在面试中表达的答案。</p>
<h3 id="程序厨c-面试题库httpswwwchengxuchucom" class="headerLink">
    <a href="#%e7%a8%8b%e5%ba%8f%e5%8e%a8c-%e9%9d%a2%e8%af%95%e9%a2%98%e5%ba%93httpswwwchengxuchucom" class="header-mark"></a>2.3 <a href="https://www.chengxuchu.com/" target="_blank" rel="noopener noreferrer">程序厨：C++ 面试题库</a></h3><p>题目覆盖面较广，可以当作复习清单使用。遇到没有完整展开的题目，再回到系统知识库补原理。</p>
<h3 id="guaguaupupc-后台开发面经httpsgithubcomguaguaupupcpp_interview" class="headerLink">
    <a href="#guaguaupupc-%e5%90%8e%e5%8f%b0%e5%bc%80%e5%8f%91%e9%9d%a2%e7%bb%8fhttpsgithubcomguaguaupupcpp_interview" class="header-mark"></a>2.4 <a href="https://github.com/guaguaupup/cpp_interview" target="_blank" rel="noopener noreferrer">guaguaupup：C++ 后台开发面经</a></h3><p>内容覆盖 C++、Linux、操作系统、网络、数据库和设计模式等方向，适合准备后端岗位时补齐关联知识。</p>
<h2 id="三作者公开资料用来交叉验证" class="headerLink">
    <a href="#%e4%b8%89%e4%bd%9c%e8%80%85%e5%85%ac%e5%bc%80%e8%b5%84%e6%96%99%e7%94%a8%e6%9d%a5%e4%ba%a4%e5%8f%89%e9%aa%8c%e8%af%81" class="header-mark"></a>3 三、作者公开资料：用来交叉验证</h2><p>下面几处资料来自同一作者或同一资料体系，适合互相对照，不必重复通读。</p>
<ul>
<li><a href="https://aigccoding.feishu.cn/wiki/PQhfwgLOoi4cqgkyDTecelDDnNe" target="_blank" rel="noopener noreferrer">程序喵大人的公开知识库入口</a></li>
<li><a href="https://chengxumiaodaren.com/docs/interview/advance/" target="_blank" rel="noopener noreferrer">C++ 高级面试题答案合集</a></li>
<li><a href="https://www.mianshiya.com/user/1807760817992474626/answer?current=1" target="_blank" rel="noopener noreferrer">面试鸭公开答题记录</a></li>
<li><a href="https://github.com/chengxumiaodaren/cpp-learning" target="_blank" rel="noopener noreferrer">cpp-learning 公开资料导航</a></li>
</ul>
<p>部分入口可能要求登录。公开仓库与网站内容可以作为补充，但不代表其他配套内容也全部开放。</p>
<h2 id="四开源项目从会答走到会做" class="headerLink">
    <a href="#%e5%9b%9b%e5%bc%80%e6%ba%90%e9%a1%b9%e7%9b%ae%e4%bb%8e%e4%bc%9a%e7%ad%94%e8%b5%b0%e5%88%b0%e4%bc%9a%e5%81%9a" class="header-mark"></a>4 四、开源项目：从“会答”走到“会做”</h2><h3 id="buriedpoint埋点上报-sdkhttpsgithubcomcpptraininghubburiedpoint" class="headerLink">
    <a href="#buriedpoint%e5%9f%8b%e7%82%b9%e4%b8%8a%e6%8a%a5-sdkhttpsgithubcomcpptraininghubburiedpoint" class="header-mark"></a>4.1 <a href="https://github.com/CppTrainingHub/BuriedPoint" target="_blank" rel="noopener noreferrer">BuriedPoint：埋点上报 SDK</a></h3><p>仓库包含库实现、服务端、示例、测试和 CMake 配置，适合观察一个小型 C++ SDK 如何组织。阅读时可以沿着“初始化 → 启动 → 上报 → 持久化或发送”的调用链推进。</p>
<p>几个值得优先看的入口：</p>
<ul>
<li><a href="https://github.com/CppTrainingHub/BuriedPoint/blob/main/src/buried_core.cc" target="_blank" rel="noopener noreferrer">核心初始化与启动逻辑：buried_core.cc</a></li>
<li><a href="https://github.com/CppTrainingHub/BuriedPoint/blob/main/src/report/buried_report.cc" target="_blank" rel="noopener noreferrer">上报处理逻辑：buried_report.cc</a></li>
<li><a href="https://github.com/CppTrainingHub/BuriedPoint/blob/main/src/CMakeLists.txt" target="_blank" rel="noopener noreferrer">构建与依赖配置：src/CMakeLists.txt</a></li>
</ul>
<p>这个项目偏向 Windows/MSVC 环境。阅读源码时，可以重点检查对象生命周期、线程安全、失败重试，以及接口被错误调用时的边界处理。</p>
<h2 id="五推荐使用顺序" class="headerLink">
    <a href="#%e4%ba%94%e6%8e%a8%e8%8d%90%e4%bd%bf%e7%94%a8%e9%a1%ba%e5%ba%8f" class="header-mark"></a>5 五、推荐使用顺序</h2><ol>
<li>从“编程指北”或“huihut”选一个作为主线，先搭知识地图。</li>
<li>用面试鸭、拾光小舟和程序厨检查遗漏，但不要同时刷多个重复题库。</li>
<li>每遇到一道高频题，整理成“结论 → 机制 → 边界与取舍”三层回答。</li>
<li>最后选一个小项目跟调用链、编译运行，再脱离源码重写最小版本。</li>
</ol>
<p>资料会变化或失效；如果某个入口打不开，可以优先检查它对应的 GitHub 仓库或站点首页。</p>
]]></description></item><item><title>自学编程一年，我总结的几条血泪经验</title><link>https://soybeanmilk.netlify.app/%E8%87%AA%E5%AD%A6%E7%BC%96%E7%A8%8B%E4%B8%80%E5%B9%B4%E7%9A%84%E8%A1%80%E6%B3%AA%E7%BB%8F%E9%AA%8C/</link><pubDate>Thu, 13 Aug 2026 00:00:00 +0000</pubDate><author><name>作者</name></author><guid>https://soybeanmilk.netlify.app/%E8%87%AA%E5%AD%A6%E7%BC%96%E7%A8%8B%E4%B8%80%E5%B9%B4%E7%9A%84%E8%A1%80%E6%B3%AA%E7%BB%8F%E9%AA%8C/</guid><description><![CDATA[<blockquote>
<p>不是什么高大上的方法论，就是自己踩坑踩出来的几条，一条一条列给你看。</p></blockquote>
<h2 id="先说说心态" class="headerLink">
    <a href="#%e5%85%88%e8%af%b4%e8%af%b4%e5%bf%83%e6%80%81" class="header-mark"></a>1 先说说心态</h2><ul>
<li>遇到问题，<strong>多去搜索</strong>，别硬憋。</li>
<li><strong>非阻塞的问题不要陷进去</strong>，卡住半天出不来最亏。</li>
<li>什么东西都学点，得到一个程序员的思维，就很好了。</li>
</ul>
<h2 id="学习路上最大的阻碍" class="headerLink">
    <a href="#%e5%ad%a6%e4%b9%a0%e8%b7%af%e4%b8%8a%e6%9c%80%e5%a4%a7%e7%9a%84%e9%98%bb%e7%a2%8d" class="header-mark"></a>2 学习路上最大的阻碍</h2><ul>
<li>知识枯燥，容易<strong>陷入细节</strong>出不来。</li>
<li>知识其实分<strong>场景、原理、细节</strong>三层。</li>
<li>基础薄弱的时候，重点看<strong>场景和原理</strong>，之后再回归细节——顺序很重要。</li>
</ul>
<h2 id="怎么对抗打断" class="headerLink">
    <a href="#%e6%80%8e%e4%b9%88%e5%af%b9%e6%8a%97%e6%89%93%e6%96%ad" class="header-mark"></a>3 怎么对抗打断</h2><ul>
<li>自驱力有限？那就别硬扛。<strong>手机放一边</strong>，网络关掉。</li>
<li>现实打断多？<strong>去图书馆</strong>，物理隔离最有效。</li>
<li>给自己<strong>及时的奖励</strong>，用番茄钟，学 25 分钟歇 5 分钟。</li>
<li>找个<strong>队友</strong>，加个群，有人一起走才走得远。</li>
<li>目标要明确：<strong>可衡量、具体、可实现、有时限</strong>。</li>
</ul>
<h2 id="学习方法" class="headerLink">
    <a href="#%e5%ad%a6%e4%b9%a0%e6%96%b9%e6%b3%95" class="header-mark"></a>4 学习方法</h2><ul>
<li>不了解的模块直接<strong>上手</strong>，先跑起来再说。</li>
<li>学一个东西先问四件事：<strong>是什么？解决什么问题？有哪些重要特性？面试重点是什么？</strong></li>
<li>先有<strong>整体思路</strong>，再深入某个点，别一上来就钻。</li>
</ul>
<h2 id="刷题技巧" class="headerLink">
    <a href="#%e5%88%b7%e9%a2%98%e6%8a%80%e5%b7%a7" class="header-mark"></a>5 刷题技巧</h2><ul>
<li>核心就一个字：<strong>坚持</strong>。</li>
<li>前期像乌龟，慢慢来；中期像兔子，速度起来；后期龟兔切换，稳中带快。</li>
</ul>
<h2 id="两个不要" class="headerLink">
    <a href="#%e4%b8%a4%e4%b8%aa%e4%b8%8d%e8%a6%81" class="header-mark"></a>6 两个&quot;不要&quot;</h2><ul>
<li>&ldquo;抓大放小&quot;不是很好——<strong>进度 &gt; 细节，原理 &gt; 源码</strong>，但不是啥都不看。</li>
<li>学完就忘？很正常，所有人都是。</li>
</ul>
<h2 id="忘光了怎么办" class="headerLink">
    <a href="#%e5%bf%98%e5%85%89%e4%ba%86%e6%80%8e%e4%b9%88%e5%8a%9e" class="header-mark"></a>7 忘光了怎么办</h2><ul>
<li><strong>三遍学习法</strong>：第一遍鸟瞰式（看全貌），第二遍牛耕式（慢慢啃），第三遍消化式（输出内化）。</li>
<li><strong>费曼学习法</strong>：把学到的讲给别人听，讲不明白的地方就是你没懂的地方。</li>
</ul>
<h2 id="最后" class="headerLink">
    <a href="#%e6%9c%80%e5%90%8e" class="header-mark"></a>8 最后</h2><ul>
<li>上面这些不一定全对，但都是我自己试过的。</li>
<li>学习是场长跑，别急着一天跑完，<strong>跑得久比跑得快重要</strong>。</li>
</ul>
]]></description></item><item><title>自学方法论六条（我的思考）</title><link>https://soybeanmilk.netlify.app/%E8%87%AA%E5%AD%A6%E6%96%B9%E6%B3%95%E8%AE%BA%E5%85%AD%E6%9D%A1/</link><pubDate>Thu, 13 Aug 2026 00:00:00 +0000</pubDate><author><name>作者</name></author><guid>https://soybeanmilk.netlify.app/%E8%87%AA%E5%AD%A6%E6%96%B9%E6%B3%95%E8%AE%BA%E5%85%AD%E6%9D%A1/</guid><description><![CDATA[<blockquote>
<p>针对&quot;AI 时代自学新知识&quot;的场景，逐条整理我的六条自学经验。</p></blockquote>
<hr>
<h2 id="一先搞明白学的东西是什么" class="headerLink">
    <a href="#%e4%b8%80%e5%85%88%e6%90%9e%e6%98%8e%e7%99%bd%e5%ad%a6%e7%9a%84%e4%b8%9c%e8%a5%bf%e6%98%af%e4%bb%80%e4%b9%88" class="header-mark"></a>1 一、先搞明白&quot;学的东西是什么&quot;</h2><blockquote>
<p>学东西之前，先搞明白它<strong>底层到底是干什么的</strong>，再接触知识会更轻松。</p></blockquote>
<p>以 AI 产品为例，每发布一个产品：</p>
<ul>
<li>去看它的<strong>官网</strong>（定位、宣传口径）</li>
<li>看它的 <strong>GitHub</strong>（代码、star、issues）</li>
<li>看它的 <strong>HuggingFace</strong>（模型、榜单、评测）</li>
<li>看它的<strong>发布说明/文档</strong>（release notes、用法）</li>
</ul>
<p>把&quot;蒸馏、开源、性能&quot;这类关键词搞明白——即<strong>它为了解决什么问题而生</strong>——再学具体知识，效率完全不同。</p>
<h2 id="二选方向要功利扣细节要纯粹" class="headerLink">
    <a href="#%e4%ba%8c%e9%80%89%e6%96%b9%e5%90%91%e8%a6%81%e5%8a%9f%e5%88%a9%e6%89%a3%e7%bb%86%e8%8a%82%e8%a6%81%e7%ba%af%e7%b2%b9" class="header-mark"></a>2 二、选方向要功利，扣细节要纯粹</h2><blockquote>
<p>选方向跟着主流走（功利），但深入了解时对细节要纯粹。</p></blockquote>
<ul>
<li><strong>选方向功利</strong>：现在主流在搞 Agent，就选 Agent 方向，别逆着趋势。</li>
<li><strong>扣细节纯粹</strong>：自己去了解它的时候，把细节钻研透彻，不掺功利心。</li>
<li>例子：你是搞 Java 的，但学习计算机底层的时候，要像纯粹研究它一样去学，别想&quot;这对我 Java 有没有用&quot;。</li>
</ul>
<h2 id="三战略上藐视战术上重视" class="headerLink">
    <a href="#%e4%b8%89%e6%88%98%e7%95%a5%e4%b8%8a%e8%97%90%e8%a7%86%e6%88%98%e6%9c%af%e4%b8%8a%e9%87%8d%e8%a7%86" class="header-mark"></a>3 三、战略上藐视，战术上重视</h2><blockquote>
<p>第一次认识一个技术时，战略上把它看成一个<strong>很小</strong>的东西，把它的&quot;壳&quot;去掉；但不要走极端——它发展过程中一定有自己特色，战术上还是要重视。</p></blockquote>
<p>反过来说：把技术看得太高上大，人会畏难不敢碰。</p>
<p><strong>我的一个思考</strong>：有没有可能通过看一个产品的<strong>迭代过程</strong>（它最开始是为了实现什么目的，后来加了什么）来了解一个东西是干什么的？——用&quot;演进史&quot;当认知路径。</p>
<h2 id="四自下而上--自上而下两条路交替走" class="headerLink">
    <a href="#%e5%9b%9b%e8%87%aa%e4%b8%8b%e8%80%8c%e4%b8%8a--%e8%87%aa%e4%b8%8a%e8%80%8c%e4%b8%8b%e4%b8%a4%e6%9d%a1%e8%b7%af%e4%ba%a4%e6%9b%bf%e8%b5%b0" class="header-mark"></a>4 四、自下而上 × 自上而下，两条路交替走</h2><blockquote>
<p>学东西有两个方向，<strong>上下交替走，路才通</strong>。</p></blockquote>
<ul>
<li><strong>自下而上</strong>：从原理/底层往上，疏通这个东西发展的逻辑。</li>
<li><strong>自上而下</strong>：从产品/应用往下，补充它的细节。</li>
</ul>
<p>⚠️ 陷阱：<strong>一直自下而上会很快遇到边际效益</strong>——投入大量时间但回报很少。这时应该<strong>直接接触它的产品</strong>，看它具体在干什么，快速获取细节上的认识。</p>
<blockquote>
<p>一开始随便选一条路就行，不必纠结起点。</p></blockquote>
<h2 id="五尝试把别人教会费曼学习法" class="headerLink">
    <a href="#%e4%ba%94%e5%b0%9d%e8%af%95%e6%8a%8a%e5%88%ab%e4%ba%ba%e6%95%99%e4%bc%9a%e8%b4%b9%e6%9b%bc%e5%ad%a6%e4%b9%a0%e6%b3%95" class="header-mark"></a>5 五、尝试把别人教会（费曼学习法）</h2><blockquote>
<p>教别人是最好的教程。给别人讲不清楚的地方，就是自己没搞明白的地方。</p></blockquote>
<ul>
<li>讲解过程会暴露<strong>逻辑断层</strong>：讲着讲着卡住 = 没真正理解。</li>
<li>过程中还能遇到很多<strong>同频的人</strong>（讲的人、听的人互相筛选）。</li>
</ul>
<h2 id="六不要太急功近利绕弯路有时就是捷径" class="headerLink">
    <a href="#%e5%85%ad%e4%b8%8d%e8%a6%81%e5%a4%aa%e6%80%a5%e5%8a%9f%e8%bf%91%e5%88%a9%e7%bb%95%e5%bc%af%e8%b7%af%e6%9c%89%e6%97%b6%e5%b0%b1%e6%98%af%e6%8d%b7%e5%be%84" class="header-mark"></a>6 六、不要太急功近利：绕弯路有时就是捷径</h2><blockquote>
<p>把时间线拉长，不计较当下得失。在当下埋下种子，等待它发芽。</p></blockquote>
<ul>
<li>有些弯路其实是在<strong>积累关联知识</strong>，将来会连成网。</li>
<li>但战线<strong>不要拉得太远</strong>——种子可以埋，不要种到看不见希望的地方。</li>
</ul>
<hr>
<h2 id="自测清单" class="headerLink">
    <a href="#%e8%87%aa%e6%b5%8b%e6%b8%85%e5%8d%95" class="header-mark"></a>7 自测清单</h2><ul>
<li><i class="far fa-square fa-fw"></i> 学一个新东西前，能说出&quot;它底层是干什么的&quot;</li>
<li><i class="far fa-square fa-fw"></i> 能区分&quot;选方向&quot;和&quot;扣细节&quot;两种心态</li>
<li><i class="far fa-square fa-fw"></i> 出现边际效益时，知道切换方向去接触产品</li>
<li><i class="far fa-square fa-fw"></i> 能主动给同频的人讲一遍所学</li>
</ul>
]]></description></item><item><title>LLM Prompt 缓存机制</title><link>https://soybeanmilk.netlify.app/llm-prompt%E7%BC%93%E5%AD%98%E6%9C%BA%E5%88%B6/</link><pubDate>Sat, 08 Aug 2026 00:00:00 +0000</pubDate><author><name>作者</name></author><guid>https://soybeanmilk.netlify.app/llm-prompt%E7%BC%93%E5%AD%98%E6%9C%BA%E5%88%B6/</guid><description><![CDATA[<h2 id="简要回答" class="headerLink">
    <a href="#%e7%ae%80%e8%a6%81%e5%9b%9e%e7%ad%94" class="header-mark"></a>1 简要回答</h2><p>LLM API 对请求的 <strong>前缀（prefix）</strong> 做自动缓存。两次请求的 prompt 开头部分完全相同时，相同部分的 token 不再重新计算，只付缓存价（通常低 100 倍以上）。</p>
<h2 id="详细回答" class="headerLink">
    <a href="#%e8%af%a6%e7%bb%86%e5%9b%9e%e7%ad%94" class="header-mark"></a>2 详细回答</h2><h3 id="命中条件" class="headerLink">
    <a href="#%e5%91%bd%e4%b8%ad%e6%9d%a1%e4%bb%b6" class="header-mark"></a>2.1 命中条件</h3><ol>
<li><strong>必须是前缀</strong>：缓存只认从 prompt 第 1 个 token 开始的连续相同段</li>
<li><strong>完全匹配</strong>：字符级一模一样才命中，多一个空格就失效</li>
<li><strong>至少 N 个 token</strong>：大部分模型要求缓存段 ≥ 1024 token</li>
<li><strong>TTL 内</strong>：缓存有效期通常 5-10 分钟，超时重新计算</li>
</ol>
<h3 id="什么最容易命中" class="headerLink">
    <a href="#%e4%bb%80%e4%b9%88%e6%9c%80%e5%ae%b9%e6%98%93%e5%91%bd%e4%b8%ad" class="header-mark"></a>2.2 什么最容易命中</h3><ul>
<li><strong>System prompt / 系统指令</strong>：每轮对话都一样 → 必中</li>
<li><strong>多轮对话（重点）</strong>：同一对话框里持续聊天，每轮请求都会把&quot;全部历史&quot;重发给 API。历史部分和上一轮请求完全相同 → 前缀越来越大，缓存命中越来越多</li>
<li><strong>反复引用的长文档/代码</strong>：同一个文件的 content 放前面</li>
<li><strong>AGENTS.md</strong>：每次都是 system prompt 开头 → 必中</li>
</ul>
<h4 id="多轮对话为什么自动命中" class="headerLink">
    <a href="#%e5%a4%9a%e8%bd%ae%e5%af%b9%e8%af%9d%e4%b8%ba%e4%bb%80%e4%b9%88%e8%87%aa%e5%8a%a8%e5%91%bd%e4%b8%ad" class="header-mark"></a>2.2.1 多轮对话为什么自动命中</h4><p>关键认知：<strong>每次发消息，API 收到的是&quot;全部聊天记录 + 你的新消息&quot;，不是只发新的一句。</strong></p>
<table>
  <thead>
      <tr>
          <th>轮次</th>
          <th>发送内容</th>
          <th>缓存情况</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>第1轮</td>
          <td><code>[系统指令] + &quot;问题A&quot;</code></td>
          <td>全部走输入价</td>
      </tr>
      <tr>
          <td>第2轮</td>
          <td><code>[系统指令] + &quot;问题A&quot; + [AI回答1] + &quot;问题B&quot;</code></td>
          <td>和第一轮完全相同的前缀 → 缓存价；新增部分 → 输入价</td>
      </tr>
      <tr>
          <td>第3轮</td>
          <td><code>[系统指令] + [历史1-2全部] + &quot;问题C&quot;</code></td>
          <td>更长前缀 → 更多缓存命中；新增部分 → 输入价</td>
      </tr>
      <tr>
          <td>&hellip;</td>
          <td>&hellip;</td>
          <td>聊越久，缓存 hit 越多</td>
      </tr>
  </tbody>
</table>
<p>所以同一个框连聊 20 轮，从第 2 轮起前面绝大部分都是缓存价，真正按输入价算的只有最新一句。</p>
<h3 id="为什么便宜百倍" class="headerLink">
    <a href="#%e4%b8%ba%e4%bb%80%e4%b9%88%e4%be%bf%e5%ae%9c%e7%99%be%e5%80%8d" class="header-mark"></a>2.3 为什么便宜百倍</h3><p>Transformer 推理最贵的步骤是 <strong>self-attention 的 KV 矩阵计算</strong>。如果 prefix 相同，上一轮算好的 KV cache 直接复用，跳过计算，只剩后面新增 token 的增量计算。服务商 GPU 成本大头被消除，所以定价极低。</p>
<h3 id="实际影响" class="headerLink">
    <a href="#%e5%ae%9e%e9%99%85%e5%bd%b1%e5%93%8d" class="header-mark"></a>2.4 实际影响</h3><ul>
<li>第一次请求：按输入价全额计费</li>
<li>后续请求（前缀相同）：前缀部分按缓存价，新增部分按输入价</li>
<li>AGENTS.md / 项目 context 只有第一次会话贵，之后近乎免费</li>
</ul>
<h2 id="知识扩展" class="headerLink">
    <a href="#%e7%9f%a5%e8%af%86%e6%89%a9%e5%b1%95" class="header-mark"></a>3 知识扩展</h2><p>不同模型的缓存定价差异很大：</p>
<ul>
<li>DeepSeek V4 Pro：缓存 ¥0.02 → 是输入价的 1/147</li>
<li>Qwen3.7 Plus：缓存 ¥0.27 → 是输入价的 1/10</li>
<li>缓存折扣越大，说明该模型在长对话/大 context 场景越划算</li>
</ul>
]]></description></item><item><title>从零理解 Attention 和 Transformer（学习笔记）</title><link>https://soybeanmilk.netlify.app/transformer%E5%92%8Cattention%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/</link><pubDate>Sat, 08 Aug 2026 00:00:00 +0000</pubDate><author><name>作者</name></author><guid>https://soybeanmilk.netlify.app/transformer%E5%92%8Cattention%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/</guid><description><![CDATA[<h2 id="我的想法--卡点" class="headerLink">
    <a href="#%e6%88%91%e7%9a%84%e6%83%b3%e6%b3%95--%e5%8d%a1%e7%82%b9" class="header-mark"></a>1 我的想法 / 卡点</h2><p>之前对 AI 的底层原理理解很模糊，一堆术语堆在一起分不清。今天从对话里一步步拆明白了。</p>
<p>主要卡点：</p>
<ol>
<li>模型的&quot;权重&quot;到底是静态的还是动态的 → 两种都有</li>
<li>Decoder-only 为什么不需要 Encoder → Decoder 自己就能看自己</li>
<li>Attention 里的 Q、K、V 到底是什么 → 每个 token 乘三个矩阵算出来的</li>
</ol>
<h2 id="最终理解" class="headerLink">
    <a href="#%e6%9c%80%e7%bb%88%e7%90%86%e8%a7%a3" class="header-mark"></a>2 最终理解</h2><h3 id="两种权重别搞混" class="headerLink">
    <a href="#%e4%b8%a4%e7%a7%8d%e6%9d%83%e9%87%8d%e5%88%ab%e6%90%9e%e6%b7%b7" class="header-mark"></a>2.1 两种权重别搞混</h3><table>
  <thead>
      <tr>
          <th>类型</th>
          <th>什么时候变</th>
          <th>作用</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>模型静态权重</strong></td>
          <td>训完就固定</td>
          <td>模型的&quot;知识&quot;，比如知道&quot;的&quot;后面常跟&quot;名&quot;</td>
      </tr>
      <tr>
          <td><strong>注意力权重</strong></td>
          <td>每次输入动态算</td>
          <td>这句话里哪个词跟当前输出最相关</td>
      </tr>
  </tbody>
</table>
<h3 id="attention-三步用今天天气很好生成好为例" class="headerLink">
    <a href="#attention-%e4%b8%89%e6%ad%a5%e7%94%a8%e4%bb%8a%e5%a4%a9%e5%a4%a9%e6%b0%94%e5%be%88%e5%a5%bd%e7%94%9f%e6%88%90%e5%a5%bd%e4%b8%ba%e4%be%8b" class="header-mark"></a>2.2 Attention 三步（用&quot;今天天气很好&quot;生成&quot;好&quot;为例）</h3><p>每个 token 变成向量后，乘三个矩阵（W_q、W_k、W_v），得到三样东西：</p>
<ul>
<li><strong>Q（查询）</strong>：我要找什么信息</li>
<li><strong>K（键值）</strong>：我身上有什么标签</li>
<li><strong>V（价值）</strong>：我身上有什么信息</li>
</ul>
<p>然后：当前位置的 Q × 每个位置的 K → 得出得分 → softmax 归一化 → 用归一化后的分数加权取 V → 最终输出。</p>
<p>关键是自己在注意力计算时<strong>能看自己</strong>，不是只看前面的词。</p>
<h3 id="transformer--注意力--前馈叠-n-层" class="headerLink">
    <a href="#transformer--%e6%b3%a8%e6%84%8f%e5%8a%9b--%e5%89%8d%e9%a6%88%e5%8f%a0-n-%e5%b1%82" class="header-mark"></a>2.3 Transformer = 注意力 + 前馈，叠 N 层</h3><div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">输入文字
</span></span><span class="line"><span class="cl">   → Token Embedding + 位置编码
</span></span><span class="line"><span class="cl">   → [Self-Attention（看全局）→ 前馈网络（消化信息）] × N 层
</span></span><span class="line"><span class="cl">   → 线性层 → 预测下一个字
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="为什么现在的大模型都是-decoder-only" class="headerLink">
    <a href="#%e4%b8%ba%e4%bb%80%e4%b9%88%e7%8e%b0%e5%9c%a8%e7%9a%84%e5%a4%a7%e6%a8%a1%e5%9e%8b%e9%83%bd%e6%98%af-decoder-only" class="header-mark"></a>2.4 为什么现在的大模型都是 Decoder-only</h3><p>原始 Transformer 有 Encoder + Decoder，但聊天场景下输入和输出天然在一条序列里，Decoder 的 self-attention 自己就能回看所有 token，不需要 Encoder 额外&quot;翻译&quot;。</p>
<h2 id="联系之前的问题" class="headerLink">
    <a href="#%e8%81%94%e7%b3%bb%e4%b9%8b%e5%89%8d%e7%9a%84%e9%97%ae%e9%a2%98" class="header-mark"></a>3 联系之前的问题</h2><ul>
<li>缓存为什么便宜：每个 token 的 K、V 上轮已经算好了，直接复用不用重算</li>
<li>多模态为什么多一个编码器：图像被打包成视觉 token 送进同一个 Transformer</li>
</ul>
]]></description></item><item><title>多模态模型原理：图像/视频/PDF 怎么被读</title><link>https://soybeanmilk.netlify.app/%E5%A4%9A%E6%A8%A1%E6%80%81%E6%A8%A1%E5%9E%8B%E5%8E%9F%E7%90%86/</link><pubDate>Sat, 08 Aug 2026 00:00:00 +0000</pubDate><author><name>作者</name></author><guid>https://soybeanmilk.netlify.app/%E5%A4%9A%E6%A8%A1%E6%80%81%E6%A8%A1%E5%9E%8B%E5%8E%9F%E7%90%86/</guid><description><![CDATA[<h2 id="简要回答" class="headerLink">
    <a href="#%e7%ae%80%e8%a6%81%e5%9b%9e%e7%ad%94" class="header-mark"></a>1 简要回答</h2><p>文本模型只认识 token（词元）。多模态模型在语言模型外面加了一个<strong>视觉编码器（Vision Encoder）</strong>，把图像切块（patch）变成&quot;视觉 token&quot;，再投影到和文字 token 相同的向量空间，喂进同一个 Transformer。所以图像对它来说也是一串&quot;符号&quot;，只是这个符号来自像素而不是文字。</p>
<h2 id="详细回答" class="headerLink">
    <a href="#%e8%af%a6%e7%bb%86%e5%9b%9e%e7%ad%94" class="header-mark"></a>2 详细回答</h2><h3 id="文本模型怎么读文字基线" class="headerLink">
    <a href="#%e6%96%87%e6%9c%ac%e6%a8%a1%e5%9e%8b%e6%80%8e%e4%b9%88%e8%af%bb%e6%96%87%e5%ad%97%e5%9f%ba%e7%ba%bf" class="header-mark"></a>2.1 文本模型怎么读文字（基线）</h3><div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">&#34;你好&#34; → 分词器 → [token1, token2] → 嵌入表查表 → 向量 → Transformer
</span></span></code></pre></td></tr></table>
</div>
</div><p>文字本来就有天然的分词方法（中文按字/词，英文按子词），每个 token 对应嵌入表里的一行向量。</p>
<h3 id="多模态模型怎么读图像" class="headerLink">
    <a href="#%e5%a4%9a%e6%a8%a1%e6%80%81%e6%a8%a1%e5%9e%8b%e6%80%8e%e4%b9%88%e8%af%bb%e5%9b%be%e5%83%8f" class="header-mark"></a>2.2 多模态模型怎么读图像</h3><div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">1024x1024 图片 → 视觉编码器(ViT/CLIP) → 切成 16x16 的 patch → 每个 patch 一个&#34;视觉 token&#34;
</span></span><span class="line"><span class="cl">→ 线性投影层（Projection Layer）→ 和文字 token 同一向量空间 → 同一个 Transformer
</span></span></code></pre></td></tr></table>
</div>
</div><p>关键点：</p>
<ol>
<li><strong>图像不是&quot;直接看懂&quot;，而是被转换成视觉 token 序列</strong>。一张 1024x1024 的图 ≈ 256~576 个视觉 token</li>
<li><strong>视觉编码器和 LLM 是两个网络</strong>，中间靠一层投影矩阵对齐</li>
<li>Transformer 对 token 一视同仁——不管是文字 token 还是视觉 token，都走 attention</li>
</ol>
<h3 id="和训练方式的区别多模态要分阶段训" class="headerLink">
    <a href="#%e5%92%8c%e8%ae%ad%e7%bb%83%e6%96%b9%e5%bc%8f%e7%9a%84%e5%8c%ba%e5%88%ab%e5%a4%9a%e6%a8%a1%e6%80%81%e8%a6%81%e5%88%86%e9%98%b6%e6%ae%b5%e8%ae%ad" class="header-mark"></a>2.3 和训练方式的区别（多模态要分阶段训）</h3><table>
  <thead>
      <tr>
          <th>阶段</th>
          <th>内容</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>1. 预训练</td>
          <td>大规模&quot;图片-文字对&quot;（CLIP 对比学习，图片配描述），让视觉编码器学会图像特征</td>
      </tr>
      <tr>
          <td>2. 对齐训练</td>
          <td><strong>只训投影层</strong>，把视觉 token 的向量空间对齐到 LLM 的向量空间（LLM 冻住）</td>
      </tr>
      <tr>
          <td>3. 联合预训练</td>
          <td>图文交错数据一起训，LLM 学会理解视觉 token</td>
      </tr>
      <tr>
          <td>4. SFT</td>
          <td>多模态指令数据微调（看图回答问题、OCR、图表理解）</td>
      </tr>
  </tbody>
</table>
<p>所以多模态不是&quot;文本模型加了读图功能&quot;，而是<strong>从一开始就用图文数据训练</strong>，训练数据和计算量都大得多。</p>
<h3 id="视频怎么读" class="headerLink">
    <a href="#%e8%a7%86%e9%a2%91%e6%80%8e%e4%b9%88%e8%af%bb" class="header-mark"></a>2.4 视频怎么读</h3><p>视频 = 连续帧。处理方式：</p>
<ol>
<li>抽帧（通常 1~2 fps，即每秒抽 1-2 帧）</li>
<li>每帧走视觉编码器变成视觉 token</li>
<li>按时间顺序拼成超长 token 序列喂给模型</li>
<li>部分模型（如 Gemini、Qwen2.5-VL）还有<strong>音频编码器</strong>，把声音轨也变成 token 一起处理</li>
</ol>
<p>这就是为什么视频贵：1 分钟视频 = 60 帧 × 每帧 256<del>1100 token ≈ 1.5万</del>6.6万 token。</p>
<h3 id="pdf-怎么读" class="headerLink">
    <a href="#pdf-%e6%80%8e%e4%b9%88%e8%af%bb" class="header-mark"></a>2.5 PDF 怎么读</h3><p>PDF 本身不算独立模态：</p>
<ul>
<li><strong>有文字层的 PDF</strong> → 直接抽文字（像文本一样）</li>
<li><strong>扫描件/图文混排</strong> → 每页渲染成图像，走视觉编码器读</li>
<li>大多数模型实际是&quot;按页转图再读&quot;</li>
</ul>
<h2 id="知识扩展" class="headerLink">
    <a href="#%e7%9f%a5%e8%af%86%e6%89%a9%e5%b1%95" class="header-mark"></a>3 知识扩展</h2><ul>
<li>判断一个模型是否多模态，看它有没有视觉编码器，而不是看名字</li>
<li>同一模型的多模态版本（如 GPT-4o vs 纯文本 GPT-4）架构差别主要在编码器部分，LLM 主干基本共享</li>
<li>DeepSeek V4 Flash 是文本模型，所以视频/图片对它 = 不可读；要用多模态功能得换支持视觉的模型</li>
</ul>
]]></description></item><item><title>插件机制的本质：扩展点 + 回调</title><link>https://soybeanmilk.netlify.app/%E6%8F%92%E4%BB%B6%E6%9C%BA%E5%88%B6%E7%9A%84%E6%9C%AC%E8%B4%A8/</link><pubDate>Fri, 07 Aug 2026 00:00:00 +0000</pubDate><author><name>作者</name></author><guid>https://soybeanmilk.netlify.app/%E6%8F%92%E4%BB%B6%E6%9C%BA%E5%88%B6%E7%9A%84%E6%9C%AC%E8%B4%A8/</guid><description><![CDATA[<h2 id="简要回答" class="headerLink">
    <a href="#%e7%ae%80%e8%a6%81%e5%9b%9e%e7%ad%94" class="header-mark"></a>1 简要回答</h2><p>所有插件机制本质相同：<strong>宿主留扩展点（接口），插件注册回调函数，宿主在关键时刻调用回调</strong>。配置文件覆盖、Obsidian 插件、游戏 Mod 全是这个套路。</p>
<h2 id="详细回答" class="headerLink">
    <a href="#%e8%af%a6%e7%bb%86%e5%9b%9e%e7%ad%94" class="header-mark"></a>2 详细回答</h2><h3 id="1-配置覆盖原理" class="headerLink">
    <a href="#1-%e9%85%8d%e7%bd%ae%e8%a6%86%e7%9b%96%e5%8e%9f%e7%90%86" class="header-mark"></a>2.1 1. 配置覆盖原理</h3><p>以 opencode 模型名显示为例：opencode 启动时读取配置并与模型数据合并（数据在 <code>~/.cache/opencode/models.json</code>），选择器渲染时取合并后的 <code>model.name</code>。所以在 <code>opencode.jsonc</code> 里写：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-jsonc" data-lang="jsonc"><span class="line"><span class="cl"><span class="p">{</span> <span class="nt">&#34;provider&#34;</span><span class="p">:</span> <span class="p">{</span> <span class="nt">&#34;opencode-go&#34;</span><span class="p">:</span> <span class="p">{</span> <span class="nt">&#34;models&#34;</span><span class="p">:</span> <span class="p">{</span> <span class="nt">&#34;deepseek-v4-flash&#34;</span><span class="p">:</span> <span class="p">{</span> <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;...价格...&#34;</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>就能覆盖显示名。改配置 → 改数据 → UI 自动变，没碰任何程序代码。</p>
<h3 id="2-插件机制的通用四步" class="headerLink">
    <a href="#2-%e6%8f%92%e4%bb%b6%e6%9c%ba%e5%88%b6%e7%9a%84%e9%80%9a%e7%94%a8%e5%9b%9b%e6%ad%a5" class="header-mark"></a>2.2 2. 插件机制的通用四步</h3><ol>
<li><strong>扩展点</strong>：宿主在关键流程留接口（消息发送前、工具执行后、模型显示时…）</li>
<li><strong>注册</strong>：插件把函数注册到挂钩点（宿主保存回调列表，不写死行为）</li>
<li><strong>调用</strong>：宿主在合适时机遍历回调列表依次调用</li>
<li><strong>公开 API</strong>：插件只能通过宿主开放的 API 交互，拿不到内部实现</li>
</ol>
<h3 id="3-c-类比" class="headerLink">
    <a href="#3-c-%e7%b1%bb%e6%af%94" class="header-mark"></a>2.3 3. C++ 类比</h3><ul>
<li>接口 + 虚函数：<code>class IPlugin { virtual void onLoad() = 0; }</code>，插件实现接口</li>
<li>Windows：DLL 动态加载（<code>LoadLibrary</code>）</li>
<li>设计模式：观察者模式 / 回调函数指针</li>
</ul>
<h3 id="4-插件的写法" class="headerLink">
    <a href="#4-%e6%8f%92%e4%bb%b6%e7%9a%84%e5%86%99%e6%b3%95" class="header-mark"></a>2.4 4. 插件的写法</h3><div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-ts" data-lang="ts"><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="p">(</span><span class="kr">async</span> <span class="p">({</span> <span class="nx">client</span><span class="p">,</span> <span class="nx">project</span><span class="p">,</span> <span class="nx">$</span> <span class="p">})</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">config</span><span class="o">:</span> <span class="p">(</span><span class="nx">cfg</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="cm">/* 配置加载时 */</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;tool.execute.before&#34;</span><span class="o">:</span> <span class="kr">async</span> <span class="p">(</span><span class="nx">input</span><span class="p">,</span> <span class="nx">output</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="cm">/* 工具执行前 */</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;chat.message&#34;</span><span class="o">:</span> <span class="p">(</span><span class="nx">input</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="cm">/* 每来一条消息 */</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nx">event</span><span class="o">:</span> <span class="p">(</span><span class="nx">input</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="cm">/* 所有事件 */</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">})</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>加载：<code>plugin: [&quot;npm包名&quot;, &quot;./本地.ts&quot;]</code>，或放 <code>.opencode/plugin/</code> 自动发现。宿主启动时调用导出函数拿到 hooks，之后事件发生时调用对应 hook。</p>
<h3 id="5-对照obsidian-插件" class="headerLink">
    <a href="#5-%e5%af%b9%e7%85%a7obsidian-%e6%8f%92%e4%bb%b6" class="header-mark"></a>2.5 5. 对照：Obsidian 插件</h3><p><code>manifest.json</code> 声明 → Obsidian 扫描插件目录 → 实例化 <code>class extends Plugin</code> 并调用 <code>onLoad()</code> → 在 onLoad 里注册 ribbon 图标、状态栏、命令。和 <code>export default function</code> 是同一件事的两种写法：<strong>宿主调用插件注册的回调</strong>。</p>
<h2 id="知识扩展" class="headerLink">
    <a href="#%e7%9f%a5%e8%af%86%e6%89%a9%e5%b1%95" class="header-mark"></a>3 知识扩展</h2><ul>
<li>事件总线：宿主发布事件，插件订阅（<code>event</code> hook 就是这个）</li>
<li>生命周期：发现插件 → 加载模块 → 拿 hooks → 绑定事件 → 卸载时清理</li>
<li>宿主不直接引用插件类型，通过约定的接口/函数签名解耦——插件和插件之间互不感知</li>
</ul>
]]></description></item><item><title>Codex / Cloud Code 接 DeepSeek API：能接吗？效果差多少？</title><link>https://soybeanmilk.netlify.app/codex%E4%B8%8Ecloud-code%E6%8E%A5deepseek/</link><pubDate>Thu, 06 Aug 2026 00:00:00 +0000</pubDate><author><name>作者</name></author><guid>https://soybeanmilk.netlify.app/codex%E4%B8%8Ecloud-code%E6%8E%A5deepseek/</guid><description><![CDATA[<h2 id="简要回答" class="headerLink">
    <a href="#%e7%ae%80%e8%a6%81%e5%9b%9e%e7%ad%94" class="header-mark"></a>1 简要回答</h2><ul>
<li><strong>Codex</strong> 能接 DeepSeek，三行配置的事</li>
<li><strong>Cloud Code</strong>（Google）基本不能接，绑定自家生态</li>
<li>效果差别：外壳（工具本身）一样，<strong>上限由模型决定</strong>；GPT-5-Codex 为 coding agent 特调，DeepSeek 是通用模型，复杂任务差距明显、简单任务够用且便宜</li>
</ul>
<h2 id="我的想法--卡点" class="headerLink">
    <a href="#%e6%88%91%e7%9a%84%e6%83%b3%e6%b3%95--%e5%8d%a1%e7%82%b9" class="header-mark"></a>2 我的想法 / 卡点</h2><p>之前一直以为这些工具只能用自己的模型，聊完才知道：</p>
<ul>
<li>Codex 2026 年 6 月官方宣布支持第三方模型，从&quot;OpenAI 专用客户端&quot;变成&quot;模型中立的编码代理平台&quot;</li>
<li>我的困惑点是&quot;DeepSync 是什么&quot;——其实是语音识别把 DeepSeek 听错了</li>
<li>&ldquo;Cloud Code 源码泄露导致 React/Reason 流行&quot;这个说法也记错了：<strong>泄露的是 Claude Code（Anthropic）</strong>，不是 Google 的 Cloud Code</li>
</ul>
<h2 id="详细回答" class="headerLink">
    <a href="#%e8%af%a6%e7%bb%86%e5%9b%9e%e7%ad%94" class="header-mark"></a>3 详细回答</h2><h3 id="codex-接入-deepseek可行" class="headerLink">
    <a href="#codex-%e6%8e%a5%e5%85%a5-deepseek%e5%8f%af%e8%a1%8c" class="header-mark"></a>3.1 Codex 接入 DeepSeek（可行）</h3><p><code>~/.codex/config.toml</code> 加 provider 定义：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">model_providers</span><span class="p">.</span><span class="nx">deepseek</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="nx">name</span> <span class="p">=</span> <span class="s2">&#34;DeepSeek&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">base_url</span> <span class="p">=</span> <span class="s2">&#34;https://api.deepseek.com/v1&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">env_key</span> <span class="p">=</span> <span class="s2">&#34;DEEPSEEK_API_KEY&#34;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>命令行走 <code>codex --provider deepseek --model deepseek-coder</code> 切换。Kimi、GLM、Qwen、Ollama 同理。</p>
<p><strong>坑</strong>：</p>
<ul>
<li>Codex 2026 年 2 月起默认走 Responses API，第三方接口大多只有 Chat Completions，需设 <code>wire_api = &quot;chat&quot;</code>，否则报流式/格式错误</li>
<li>桌面端接 DeepSeek 需要两层本地代理（模型名映射 + Responses→Chat 转换），社区有现成方案</li>
<li>provider ID 区分大小写，模型名前缀写错会被静默忽略走 fallback</li>
</ul>
<h3 id="cloud-code-接入-deepseek基本不可行" class="headerLink">
    <a href="#cloud-code-%e6%8e%a5%e5%85%a5-deepseek%e5%9f%ba%e6%9c%ac%e4%b8%8d%e5%8f%af%e8%a1%8c" class="header-mark"></a>3.2 Cloud Code 接入 DeepSeek（基本不可行）</h3><ul>
<li>Cloud Code（IDE 插件）绑定 Google 生态，不开放自定义模型</li>
<li>Gemini CLI（已开源）原生只认 Gemini；需&quot;魔改版&quot;或 LiteLLM 代理才能接 DeepSeek</li>
<li>且 Gemini CLI 免费版 <strong>2026-06-18 已停用</strong>，个人用户迁移到 Antigravity CLI</li>
</ul>
<h3 id="效果差别外壳一样上限由模型决定" class="headerLink">
    <a href="#%e6%95%88%e6%9e%9c%e5%b7%ae%e5%88%ab%e5%a4%96%e5%a3%b3%e4%b8%80%e6%a0%b7%e4%b8%8a%e9%99%90%e7%94%b1%e6%a8%a1%e5%9e%8b%e5%86%b3%e5%ae%9a" class="header-mark"></a>3.3 效果差别：外壳一样，上限由模型决定</h3><table>
  <thead>
      <tr>
          <th>场景</th>
          <th>效果</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>简单任务、补全、小修改</td>
          <td>DeepSeek 够用，价格低一个量级，性价比最高</td>
      </tr>
      <tr>
          <td>复杂重构、多文件修改</td>
          <td>GPT-5-Codex 工具调用准确率高，DeepSeek 出错率明显上升</td>
      </tr>
      <tr>
          <td>多步 Agent 长任务</td>
          <td>专用模型规划更稳，DeepSeek 容易跑偏</td>
      </tr>
  </tbody>
</table>
<p><strong>结论</strong>：日常小活用 DeepSeek 省钱，大活切回专用模型。Codex 支持多 provider 随时切，opencode 本身 75+ 模型随便换。</p>
<h3 id="彩蛋源码泄露事件的真相" class="headerLink">
    <a href="#%e5%bd%a9%e8%9b%8b%e6%ba%90%e7%a0%81%e6%b3%84%e9%9c%b2%e4%ba%8b%e4%bb%b6%e7%9a%84%e7%9c%9f%e7%9b%b8" class="header-mark"></a>3.4 彩蛋：源码泄露事件的真相</h3><ul>
<li>2026-03-31，Anthropic 的 Claude Code 因 npm 包遗留 59MB source map（<code>cli.js.map</code>），51.2 万行 TypeScript 全曝光（第二次同原因泄露）</li>
<li>大家因此看到：终端 UI 用 React+Ink、Bun 运行时、40+ 工具模块、权限分层</li>
<li><strong>React（2013 年）/ ReAct（2022 年论文）不是泄露&quot;导致&quot;的</strong>，泄露只是让大家看到了顶级 Agent 的工程实现，引发讨论和借鉴</li>
</ul>
<h2 id="知识扩展" class="headerLink">
    <a href="#%e7%9f%a5%e8%af%86%e6%89%a9%e5%b1%95" class="header-mark"></a>4 知识扩展</h2><ul>
<li>DeepSeek API 完全兼容 OpenAI 协议，官方文档有 coding agents 接入指南</li>
<li>Codex config.toml 三级配置：用户级（<code>~/.codex/config.toml</code>）→ 项目级（<code>.codex/config.toml</code>）→ 命令行参数，优先级递增</li>
<li>环境变量方式（<code>OPENAI_BASE_URL</code>）只能配一个 provider，config.toml 支持多 provider 并存切换</li>
</ul>
]]></description></item><item><title>Q1314 矩阵区域和</title><link>https://soybeanmilk.netlify.app/q1314-%E7%9F%A9%E9%98%B5%E5%8C%BA%E5%9F%9F%E5%92%8C/</link><pubDate>Tue, 04 Aug 2026 00:00:00 +0000</pubDate><author><name>作者</name></author><guid>https://soybeanmilk.netlify.app/q1314-%E7%9F%A9%E9%98%B5%E5%8C%BA%E5%9F%9F%E5%92%8C/</guid><description><![CDATA[<p>![](Pasted image 20260605194937.png)</p>
<p>题目类型：前缀和</p>
<p>当你发现是在求某个大的集合下一<strong>系列</strong>具体的子集时，你可以考虑下前缀和，它可以把遍历，变成值相减。</p>
<p>我的写法是：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span><span class="lnt">35
</span><span class="lnt">36
</span><span class="lnt">37
</span><span class="lnt">38
</span><span class="lnt">39
</span><span class="lnt">40
</span><span class="lnt">41
</span><span class="lnt">42
</span><span class="lnt">43
</span><span class="lnt">44
</span><span class="lnt">45
</span><span class="lnt">46
</span><span class="lnt">47
</span><span class="lnt">48
</span><span class="lnt">49
</span><span class="lnt">50
</span><span class="lnt">51
</span><span class="lnt">52
</span><span class="lnt">53
</span><span class="lnt">54
</span><span class="lnt">55
</span><span class="lnt">56
</span><span class="lnt">57
</span><span class="lnt">58
</span><span class="lnt">59
</span><span class="lnt">60
</span><span class="lnt">61
</span><span class="lnt">62
</span><span class="lnt">63
</span><span class="lnt">64
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;vector&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;algorithm&gt;</span><span class="cp"> </span><span class="c1">// for std::max, std::min
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">using</span> <span class="k">namespace</span> <span class="n">std</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 二维前缀和类，用于快速计算任意矩形子矩阵的和
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">class</span> <span class="nc">SumMatrix</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="k">public</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// preSum[i][j] 表示原始矩阵中前 i 行、前 j 列（即区域 [0, i) × [0, j)）所有元素的和
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// 维度为 (m+1) x (n+1)，其中 m、n 为原始矩阵的行数和列数
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">vector</span><span class="o">&lt;</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;&gt;</span> <span class="n">preSum</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// 构造函数：根据原始矩阵 matrix 构建前缀和数组
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">SumMatrix</span><span class="p">(</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;&gt;&amp;</span> <span class="n">matrix</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kt">int</span> <span class="n">m</span> <span class="o">=</span> <span class="n">matrix</span><span class="p">.</span><span class="n">size</span><span class="p">();</span>          <span class="c1">// 原始矩阵的行数
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="kt">int</span> <span class="n">n</span> <span class="o">=</span> <span class="n">matrix</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">size</span><span class="p">();</span>       <span class="c1">// 原始矩阵的列数
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="c1">// 初始化前缀和数组，多一行一列用于简化边界处理
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="n">preSum</span><span class="p">.</span><span class="n">resize</span><span class="p">(</span><span class="n">m</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span><span class="p">(</span><span class="n">n</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1">// 计算前缀和：preSum[i][j] = preSum[i-1][j] + preSum[i][j-1] - preSum[i-1][j-1] + matrix[i-1][j-1]
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;=</span> <span class="n">m</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">j</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="n">j</span> <span class="o">&lt;=</span> <span class="n">n</span><span class="p">;</span> <span class="n">j</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="n">preSum</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="n">j</span><span class="p">]</span> <span class="o">=</span> <span class="n">preSum</span><span class="p">[</span><span class="n">i</span> <span class="o">-</span> <span class="mi">1</span><span class="p">][</span><span class="n">j</span><span class="p">]</span> <span class="o">+</span> <span class="n">preSum</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="n">j</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">                               <span class="o">+</span> <span class="n">matrix</span><span class="p">[</span><span class="n">i</span> <span class="o">-</span> <span class="mi">1</span><span class="p">][</span><span class="n">j</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span> <span class="o">-</span> <span class="n">preSum</span><span class="p">[</span><span class="n">i</span> <span class="o">-</span> <span class="mi">1</span><span class="p">][</span><span class="n">j</span> <span class="o">-</span> <span class="mi">1</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// 计算原始矩阵中闭区间 [x1, x2] 行、[y1, y2] 列所围成的子矩阵的和
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// 注意：x1, x2, y1, y2 都是原始矩阵的有效索引（0-based）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">int</span> <span class="nf">Addmatrix</span><span class="p">(</span><span class="kt">int</span> <span class="n">x1</span><span class="p">,</span> <span class="kt">int</span> <span class="n">x2</span><span class="p">,</span> <span class="kt">int</span> <span class="n">y1</span><span class="p">,</span> <span class="kt">int</span> <span class="n">y2</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 利用前缀和公式：sum = preSum[x2+1][y2+1] - preSum[x1][y2+1] - preSum[x2+1][y1] + preSum[x1][y1]
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="c1">// 因为 preSum 的行/列索引比原始矩阵大 1，所以需要 +1 转换
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="k">return</span> <span class="n">preSum</span><span class="p">[</span><span class="n">x2</span> <span class="o">+</span> <span class="mi">1</span><span class="p">][</span><span class="n">y2</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]</span> <span class="o">-</span> <span class="n">preSum</span><span class="p">[</span><span class="n">x1</span><span class="p">][</span><span class="n">y2</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">               <span class="o">-</span> <span class="n">preSum</span><span class="p">[</span><span class="n">x2</span> <span class="o">+</span> <span class="mi">1</span><span class="p">][</span><span class="n">y1</span><span class="p">]</span> <span class="o">+</span> <span class="n">preSum</span><span class="p">[</span><span class="n">x1</span><span class="p">][</span><span class="n">y1</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Solution</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="k">public</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 计算矩阵区域和：对于每个位置 (i, j)，求以该位置为中心、边长为 2k+1 的正方形区域的和
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// （边界超出矩阵时只取有效部分）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">vector</span><span class="o">&lt;</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;&gt;</span> <span class="n">matrixBlockSum</span><span class="p">(</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;&gt;&amp;</span> <span class="n">mat</span><span class="p">,</span> <span class="kt">int</span> <span class="n">k</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kt">int</span> <span class="n">m</span> <span class="o">=</span> <span class="n">mat</span><span class="p">.</span><span class="n">size</span><span class="p">();</span>      <span class="c1">// 矩阵行数
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="kt">int</span> <span class="n">n</span> <span class="o">=</span> <span class="n">mat</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">size</span><span class="p">();</span>   <span class="c1">// 矩阵列数
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="c1">// 结果矩阵，大小与原始矩阵相同
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="n">vector</span><span class="o">&lt;</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;&gt;</span> <span class="n">answer</span><span class="p">(</span><span class="n">m</span><span class="p">,</span> <span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span><span class="p">(</span><span class="n">n</span><span class="p">,</span> <span class="mi">0</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 构建原始矩阵的前缀和对象
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="n">SumMatrix</span> <span class="n">mt</span> <span class="o">=</span> <span class="n">SumMatrix</span><span class="p">(</span><span class="n">mat</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 遍历每个位置 (i, j)
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">m</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">j</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">j</span> <span class="o">&lt;</span> <span class="n">n</span><span class="p">;</span> <span class="n">j</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="c1">// 计算以 (i, j) 为中心、半径为 k 的矩形区域的边界（需限制在矩阵范围内）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                <span class="kt">int</span> <span class="n">x1</span> <span class="o">=</span> <span class="n">max</span><span class="p">(</span><span class="n">i</span> <span class="o">-</span> <span class="n">k</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>        <span class="c1">// 上边界（行最小索引）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                <span class="kt">int</span> <span class="n">x2</span> <span class="o">=</span> <span class="n">min</span><span class="p">(</span><span class="n">i</span> <span class="o">+</span> <span class="n">k</span><span class="p">,</span> <span class="n">m</span> <span class="o">-</span> <span class="mi">1</span><span class="p">);</span>    <span class="c1">// 下边界（行最大索引）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                <span class="kt">int</span> <span class="n">y1</span> <span class="o">=</span> <span class="n">max</span><span class="p">(</span><span class="n">j</span> <span class="o">-</span> <span class="n">k</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>        <span class="c1">// 左边界（列最小索引）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                <span class="kt">int</span> <span class="n">y2</span> <span class="o">=</span> <span class="n">min</span><span class="p">(</span><span class="n">j</span> <span class="o">+</span> <span class="n">k</span><span class="p">,</span> <span class="n">n</span> <span class="o">-</span> <span class="mi">1</span><span class="p">);</span>    <span class="c1">// 右边界（列最大索引）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">                <span class="c1">// 通过前缀和快速得到该区域的和，并填入结果矩阵
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                <span class="n">answer</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="n">j</span><span class="p">]</span> <span class="o">=</span> <span class="n">mt</span><span class="p">.</span><span class="n">Addmatrix</span><span class="p">(</span><span class="n">x1</span><span class="p">,</span> <span class="n">x2</span><span class="p">,</span> <span class="n">y1</span><span class="p">,</span> <span class="n">y2</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">answer</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></td></tr></table>
</div>
</div><hr>
<p>这题说白了就是：给你一个矩阵，对于每个格子 <code>(i,j)</code>，把它周围 k 圈范围内的所有数加起来，填回去。</p>
<p>暴力做法也简单，每个格子都去遍历它的 k 邻域，但那是 $O(m \cdot n \cdot k^2)$，k 一大直接炸。</p>
<p>关键思路就是——<strong>前缀和把遍历变成值相减</strong>。</p>
<p>二维前缀和的构造其实就一句话：<code>preSum[i][j]</code> 表示从 <code>(0,0)</code> 到 <code>(i-1, j-1)</code> 这片矩形区域的和。多开一圈 <code>m+1 × n+1</code> 是为了省掉边界判空。</p>
<p>构造公式拆开看很直观：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">preSum[i][j] = preSum[i-1][j]        // 上面那块
</span></span><span class="line"><span class="cl">             + preSum[i][j-1]        // 左边那块
</span></span><span class="line"><span class="cl">             + matrix[i-1][j-1]      // 当前这个数
</span></span><span class="line"><span class="cl">             - preSum[i-1][j-1]      // 左上角被算了两次，扣掉
</span></span></code></pre></td></tr></table>
</div>
</div><p>画个图就是：<strong>上加左，减重叠，加自己。</strong></p>
<p>查子矩阵和也一个道理。想求 <code>(x1,y1)</code> 到 <code>(x2,y2)</code> 这个矩形（闭区间，0-index）的和：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">sum = preSum[x2+1][y2+1]      // 整块从原点到右下
</span></span><span class="line"><span class="cl">    - preSum[x1][y2+1]        // 砍掉上面
</span></span><span class="line"><span class="cl">    - preSum[x2+1][y1]        // 砍掉左边
</span></span><span class="line"><span class="cl">    + preSum[x1][y1]          // 左上角被砍了两次，补回来
</span></span></code></pre></td></tr></table>
</div>
</div><p>就是<strong>大减小，补重叠</strong>，对称得很。</p>
<p>最后一步，每个格子的 k 邻域怎么定界？用 <code>max</code>/<code>min</code> 把越界的部分夹住就行——左边界不能小于 0，右边界不能超过 <code>m-1</code>，上下同理。这样每个格子 O(1) 就能出结果，总复杂度 $O(m \cdot n)$。</p>
<p>一句话总结：<strong>构造一次前缀和，然后每个格子的答案就是四次查表加减</strong>。前缀和的本质就是预计算，用空间换掉内层遍历。</p>
]]></description></item><item><title>Q142 环形链表II</title><link>https://soybeanmilk.netlify.app/q142-%E7%8E%AF%E5%BD%A2%E9%93%BE%E8%A1%A8ii/</link><pubDate>Tue, 04 Aug 2026 00:00:00 +0000</pubDate><author><name>作者</name></author><guid>https://soybeanmilk.netlify.app/q142-%E7%8E%AF%E5%BD%A2%E9%93%BE%E8%A1%A8ii/</guid><description><![CDATA[<blockquote>
<p><strong>题目</strong>：给定一个链表的头节点 <code>head</code>，返回链表开始入环的第一个节点。如果链表无环，返回 <code>null</code>。</p></blockquote>
<h2 id="我的想法" class="headerLink">
    <a href="#%e6%88%91%e7%9a%84%e6%83%b3%e6%b3%95" class="header-mark"></a>1 我的想法</h2><p>最容易踩的坑：以为快慢指针第一次相遇就是答案。<strong>第一次相遇点不是环入口</strong>——相遇后还要从头再走一遍：让 <code>cur</code>（从 head 出发）和 <code>fast</code>（停在相遇点）同步各走 1 步，两者相遇的位置才是环入口。</p>
<h2 id="简要回答" class="headerLink">
    <a href="#%e7%ae%80%e8%a6%81%e5%9b%9e%e7%ad%94" class="header-mark"></a>2 简要回答</h2><ol>
<li>fast 每次 2 步、slow 每次 1 步，若 <code>fast</code> 或 <code>fast-&gt;next</code> 为空 → 无环，返回 null。</li>
<li>第一次 <code>fast == slow</code> → 有环，记下相遇点。</li>
<li><code>cur = head</code>，<code>cur</code> 与 <code>fast</code> 各走 1 步，直到 <code>fast == cur</code> → 返回该节点（环入口）。</li>
</ol>
<ul>
<li>时间复杂度：$O(n)$</li>
<li>空间复杂度：$O(1)$</li>
</ul>
<h2 id="为什么第一次相遇点不是入口" class="headerLink">
    <a href="#%e4%b8%ba%e4%bb%80%e4%b9%88%e7%ac%ac%e4%b8%80%e6%ac%a1%e7%9b%b8%e9%81%87%e7%82%b9%e4%b8%8d%e6%98%af%e5%85%a5%e5%8f%a3" class="header-mark"></a>3 为什么第一次相遇点不是入口</h2><p>第一次相遇发生在环内，具体是环内哪个点取决于 <code>a</code> 和 <code>b</code> 的比例，<strong>没有任何规律保证它是入口</strong>。所以第二阶段必须有。</p>
<h2 id="为什么第二轮双指针能在入口相遇" class="headerLink">
    <a href="#%e4%b8%ba%e4%bb%80%e4%b9%88%e7%ac%ac%e4%ba%8c%e8%bd%ae%e5%8f%8c%e6%8c%87%e9%92%88%e8%83%bd%e5%9c%a8%e5%85%a5%e5%8f%a3%e7%9b%b8%e9%81%87" class="header-mark"></a>4 为什么第二轮双指针能在入口相遇</h2><p>设 head→环入口距离为 <code>a</code>，环入口→第一次相遇点（沿前进方向）距离为 <code>b</code>，环长为 <code>c</code>。</p>
<p>相遇时（fast 走的路程是 slow 的 2 倍）：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">slow 走：a + b
</span></span><span class="line"><span class="cl">fast 走：2(a + b) = a + b + n·c     （fast 比 slow 多绕了 n 圈）
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">⇒  a + b = n·c
</span></span><span class="line"><span class="cl">⇒  a = n·c - b
</span></span></code></pre></td></tr></table>
</div>
</div><p>含义：</p>
<ul>
<li>从 head 走 <code>a</code> 步 → 到达环入口；</li>
<li>从相遇点走 <code>a</code> 步 → 绕 <code>n</code> 圈后再退 <code>b</code> 步 → 也到达环入口。</li>
</ul>
<p>所以 <code>cur</code>（head 出发）和 <code>fast</code>（相遇点出发）同步各走 1 步，恰好都在走 <code>a</code> 步后到达环入口，相遇。</p>
<h2 id="代码" class="headerLink">
    <a href="#%e4%bb%a3%e7%a0%81" class="header-mark"></a>5 代码</h2><div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Solution</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="k">public</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">ListNode</span> <span class="o">*</span><span class="n">detectCycle</span><span class="p">(</span><span class="n">ListNode</span> <span class="o">*</span><span class="n">head</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">ListNode</span><span class="o">*</span> <span class="n">fast</span> <span class="o">=</span> <span class="n">head</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">ListNode</span><span class="o">*</span> <span class="n">slow</span> <span class="o">=</span> <span class="n">head</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">while</span> <span class="p">(</span><span class="n">fast</span> <span class="o">!=</span> <span class="k">nullptr</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="p">(</span><span class="n">fast</span><span class="o">-&gt;</span><span class="n">next</span> <span class="o">==</span> <span class="k">nullptr</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="k">return</span> <span class="k">nullptr</span><span class="p">;</span>          <span class="c1">// 无环
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="n">fast</span> <span class="o">=</span> <span class="n">fast</span><span class="o">-&gt;</span><span class="n">next</span><span class="o">-&gt;</span><span class="n">next</span><span class="p">;</span>     <span class="c1">// 走 2 步
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="n">slow</span> <span class="o">=</span> <span class="n">slow</span><span class="o">-&gt;</span><span class="n">next</span><span class="p">;</span>           <span class="c1">// 走 1 步
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="k">if</span> <span class="p">(</span><span class="n">fast</span> <span class="o">==</span> <span class="n">slow</span><span class="p">)</span> <span class="p">{</span>          <span class="c1">// 第一次相遇，说明有环
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                <span class="n">ListNode</span><span class="o">*</span> <span class="n">cur</span> <span class="o">=</span> <span class="n">head</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                <span class="k">while</span> <span class="p">(</span><span class="n">fast</span> <span class="o">!=</span> <span class="n">cur</span><span class="p">)</span> <span class="p">{</span>    <span class="c1">// 同步走 1 步
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                    <span class="n">cur</span> <span class="o">=</span> <span class="n">cur</span><span class="o">-&gt;</span><span class="n">next</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                    <span class="n">fast</span> <span class="o">=</span> <span class="n">fast</span><span class="o">-&gt;</span><span class="n">next</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span>
</span></span><span class="line"><span class="cl">                <span class="k">return</span> <span class="n">cur</span><span class="p">;</span>              <span class="c1">// 环入口
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="k">nullptr</span><span class="p">;</span>                  <span class="c1">// fast 走到末尾，无环
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>用 <code>1→2→3→4→5→(3)</code>（3 是环入口，环长 3）验证：第一次相遇点在 5，第二阶段从 head 和 5 同步走，在 3 相遇 → 返回 3 ✓。</p>
<h2 id="无环与空指针" class="headerLink">
    <a href="#%e6%97%a0%e7%8e%af%e4%b8%8e%e7%a9%ba%e6%8c%87%e9%92%88" class="header-mark"></a>6 无环与空指针</h2><ul>
<li>无环：<code>fast</code> 先到末尾，<code>fast == nullptr</code> 退出循环，返回 null。</li>
<li>空链表 / 单节点：<code>fast == nullptr</code> 或 <code>fast-&gt;next == nullptr</code>，返回 null。</li>
<li><code>fast-&gt;next-&gt;next</code> 在 <code>fast-&gt;next == nullptr</code> 已排除的前提下是安全的（最多为 null，下一轮循环判断）。</li>
</ul>
<h2 id="易错点" class="headerLink">
    <a href="#%e6%98%93%e9%94%99%e7%82%b9" class="header-mark"></a>7 易错点</h2><ul>
<li><strong>第一次 <code>fast == slow</code> 直接 return 是错的</strong>：那只是环内某个点，不是入口。</li>
<li>第二阶段不需要判空：已确认有环，<code>cur</code> 和 <code>fast</code> 必然相遇。</li>
<li>无环判断必须覆盖 <code>fast == nullptr</code> 和 <code>fast-&gt;next == nullptr</code> 两种。</li>
<li>第一阶段别把 slow 写成走 2 步——快慢都走 2 步永远追不上。</li>
</ul>
]]></description></item></channel></rss>