<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet href="/rss/feed.xsl" type="text/xsl"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Kalev Yang</title><description>记录技术成长，分享编程乐趣</description><link>https://kalev-ovo.github.io</link><item><title>Vibe Coding是在创新，还是在制造垃圾</title><link>https://kalev-ovo.github.io/post/vibe-codingshi-zai-chuang-xin-huan-shi-zai-zhi-zao-la-ji</link><guid isPermaLink="false">vibe-codingshi-zai-chuang-xin-huan-shi-zai-zhi-zao-la-ji</guid><description>使用AI工具的一点感受，提高产出质量的思考</description><pubDate>Mon, 01 Jun 2026 15:40:02 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;AI 固然好用&lt;/strong&gt;：从过去需要仔细研究一个功能怎么写，到现在直接告诉 AI 大人我的想法。所有人都在说，AI 让人有了实现自己想法的能力。但问题在于，这种实现往往寄托于有限的 token、短暂的上下文、缺乏深度推演的过程。最终，很多想法成了半成品，甚至是垃圾。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;想法到实现&lt;/strong&gt;&lt;br /&gt;
一个想法如何被验证？以前可能靠翻阅资料、动手试错、请教他人。现在，我们更倾向于直接把想法丢给 AI，让它来验证。这种验证太轻了——AI 不会替你去碰撞现实的摩擦力。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;主动打破惰性&lt;/strong&gt;（需要做的事）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;逼自己去想实现的具体步骤&lt;/strong&gt;——哪怕很笨、很慢。把 AI 当参谋，不当替身。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;搜索更多资料去完善想法&lt;/strong&gt;——AI 给你的摘要永远比不上你自己顺藤摸瓜找到的那篇冷门文章、那组真实数据、那个失败案例。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;亲手做一个小到不能再小的原型&lt;/strong&gt;——写两行代码、画一张草图、列一份清单。让想法和真实世界发生一次摩擦，哪怕只是极小的摩擦。AI 不会替你感受“这一步原来会卡住”。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;最后记住：&lt;strong&gt;AI 最好的用法，不是替你走路，而是当你已经走在路上时，递给你一张地图。&lt;/strong&gt; 不把想法交给 AI 就完事，而是把 AI 当成逼自己更清醒的工具。&lt;/p&gt;</content:encoded><category>category:随笔</category><category>tag:vibe coding</category></item><item><title>LeetCode 865 具有所有最深节点的最小子树</title><link>https://kalev-ovo.github.io/post/lc865</link><guid isPermaLink="false">lc865</guid><description>题目描述
给定一个根为 root 的二叉树，每个节点的深度是 该节点到根的最短距离 。
返回包含原始树中所有最深节点的 最小子树 。
如果一个节点在 整个树 的任意节点之间具有最大的深度，则该节点是 最深的 。
一个节点的 子树 是该节点加上它的所有后代的集合。
示例 1


输入：root =</description><pubDate>Thu, 08 Jan 2026 08:45:50 GMT</pubDate><content:encoded>&lt;h2&gt;题目描述&lt;a href=&quot;#题目描述&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;给定一个根为 root 的二叉树，每个节点的深度是 &lt;strong&gt;该节点到根的最短距离&lt;/strong&gt; 。&lt;/p&gt;
&lt;p&gt;返回包含原始树中所有&lt;strong&gt;最深节点&lt;/strong&gt;的 &lt;strong&gt;最小子树&lt;/strong&gt; 。&lt;/p&gt;
&lt;p&gt;如果一个节点在 &lt;strong&gt;整个树&lt;/strong&gt; 的任意节点之间具有最大的深度，则该节点是 &lt;strong&gt;最深的&lt;/strong&gt; 。&lt;/p&gt;
&lt;p&gt;一个节点的 &lt;strong&gt;子树&lt;/strong&gt; 是该节点加上它的所有后代的集合。&lt;/p&gt;
&lt;h3&gt;示例 1&lt;a href=&quot;#示例-1&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;figure&gt;&lt;img src=&quot;https://s3-lc-upload.s3.amazonaws.com/uploads/2018/07/01/sketch1.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; /&gt;&lt;/figure&gt;
&lt;ul&gt;
&lt;li&gt;输入：root = [3,5,1,6,2,0,8,null,null,7,4]&lt;/li&gt;
&lt;li&gt;输出：[2,7,4]&lt;/li&gt;
&lt;li&gt;解释：
我们返回值为 2 的节点，在图中用黄色标记。
在图中用蓝色标记的是树的最深的节点。
注意，节点 5、3 和 2 包含树中最深的节点，但节点 2 的子树最小，因此我们返回它。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;思路&lt;a href=&quot;#思路&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;考虑到最深的节点可能有多个&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;首先考虑找到最深的叶子节点，可以用&lt;strong&gt;层序遍历&lt;/strong&gt;找到所有最深的叶子节点&lt;/li&gt;
&lt;li&gt;然后从这些最深的叶子节点开始，向上遍历，找到第一个公共祖先，这个公共祖先就是我们要找的最小子树的根节点&lt;/li&gt;
&lt;li&gt;最后返回这个公共祖先作为结果&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;代码实现&lt;a href=&quot;#代码实现&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;/**&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt; * Definition for a binary tree node.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt; * struct TreeNode {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt; *     int val;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt; *     TreeNode *left;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt; *     TreeNode *right;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt; *     TreeNode() : val(0), left(nullptr), right(nullptr) {}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt; *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt; *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt; * };&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt; */&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;class Solution {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;public:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    TreeNode&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; subtreeWithAllDeepest&lt;/span&gt;&lt;span&gt;(TreeNode&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; root) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        vector&lt;/span&gt;&lt;span&gt;&amp;lt;int&amp;gt;&lt;/span&gt;&lt;span&gt; fa&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;501&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;), dleves;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        vector&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;TreeNode&lt;/span&gt;&lt;span&gt;*&amp;gt;&lt;/span&gt;&lt;span&gt; mp&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;501&lt;/span&gt;&lt;span&gt;, nullptr);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        queue&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;TreeNode&lt;/span&gt;&lt;span&gt;*&amp;gt;&lt;/span&gt;&lt;span&gt; q, tmp;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        q.&lt;/span&gt;&lt;span&gt;push&lt;/span&gt;&lt;span&gt;(root);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        while&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;q.&lt;/span&gt;&lt;span&gt;empty&lt;/span&gt;&lt;span&gt;()) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            int&lt;/span&gt;&lt;span&gt; sz &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; q.&lt;/span&gt;&lt;span&gt;size&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            dleves.&lt;/span&gt;&lt;span&gt;clear&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            while&lt;/span&gt;&lt;span&gt; (sz&lt;/span&gt;&lt;span&gt;--&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                TreeNode&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; p &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; q.&lt;/span&gt;&lt;span&gt;front&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                q.&lt;/span&gt;&lt;span&gt;pop&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                dleves.&lt;/span&gt;&lt;span&gt;push_back&lt;/span&gt;&lt;span&gt;(p-&amp;gt;val);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                mp&lt;/span&gt;&lt;span&gt;[p-&amp;gt;val] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; p;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                if&lt;/span&gt;&lt;span&gt; (p-&amp;gt;left){&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                    q.&lt;/span&gt;&lt;span&gt;push&lt;/span&gt;&lt;span&gt;(p-&amp;gt;left);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                    fa&lt;/span&gt;&lt;span&gt;[p-&amp;gt;left-&amp;gt;val] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; p-&amp;gt;val;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                if&lt;/span&gt;&lt;span&gt; (p-&amp;gt;right) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                    q.&lt;/span&gt;&lt;span&gt;push&lt;/span&gt;&lt;span&gt;(p-&amp;gt;right); &lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                    fa&lt;/span&gt;&lt;span&gt;[p-&amp;gt;right-&amp;gt;val] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; p-&amp;gt;val;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        while&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            int&lt;/span&gt;&lt;span&gt; anc &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; dleves&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;];&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            bool&lt;/span&gt;&lt;span&gt; ok &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; true&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            for&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; i &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;span&gt;; i &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt; dleves.&lt;/span&gt;&lt;span&gt;size&lt;/span&gt;&lt;span&gt;(); &lt;/span&gt;&lt;span&gt;++&lt;/span&gt;&lt;span&gt;i) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;dleves&lt;/span&gt;&lt;span&gt;[i] &lt;/span&gt;&lt;span&gt;!=&lt;/span&gt;&lt;span&gt; anc) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                    ok &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; false&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                    break&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            if&lt;/span&gt;&lt;span&gt; (ok) &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; mp&lt;/span&gt;&lt;span&gt;[anc];&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            for&lt;/span&gt;&lt;span&gt; (auto&lt;/span&gt;&lt;span&gt;&amp;amp;&lt;/span&gt;&lt;span&gt; v : dleves) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                v &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; fa&lt;/span&gt;&lt;span&gt;[v];&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        return&lt;/span&gt;&lt;span&gt; root;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;复杂度分析&lt;a href=&quot;#复杂度分析&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;时间复杂度：&lt;span&gt;&lt;span&gt;O(n)O(n)&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;O&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;n&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;，其中 &lt;span&gt;&lt;span&gt;nn&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; 是二叉树的节点数。
需要进行一次层序遍历&lt;span&gt;&lt;span&gt;O(n)O(n)&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;O&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;n&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;，以及一次向上跳跃寻找公共祖先的过程&lt;span&gt;&lt;span&gt;O(logn)O(logn)&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;O&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;l&lt;/span&gt;&lt;span&gt;o&lt;/span&gt;&lt;span&gt;g&lt;/span&gt;&lt;span&gt;n&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;。&lt;/li&gt;
&lt;li&gt;空间复杂度：&lt;span&gt;&lt;span&gt;O(n)O(n)&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;O&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;n&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;，其中 &lt;span&gt;&lt;span&gt;nn&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; 是二叉树的节点数。
需要存储二叉树每个节点的父亲节点，以及层序遍历的队列等等。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;优化&lt;a href=&quot;#优化&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;这样虽然是很朴素的想法，但实际上不够直接，仔细思考发现：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;最深子树的根节点意味着其左右子树的深度相同&lt;/li&gt;
&lt;li&gt;答案一定在左子树或右子树中&lt;/li&gt;
&lt;li&gt;可以用&lt;strong&gt;DFS&lt;/strong&gt;来解决&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;代码实现&lt;a href=&quot;#代码实现-1&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;/**&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt; * Definition for a binary tree node.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt; * struct TreeNode {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt; *     int val;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt; *     TreeNode *left;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt; *     TreeNode *right;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt; *     TreeNode() : val(0), left(nullptr), right(nullptr) {}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt; *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt; *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt; * };&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt; */&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; Solution&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;public:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    pair&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;TreeNode&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt;&amp;gt; &lt;/span&gt;&lt;span&gt;dfs&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;TreeNode&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; p&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;p) &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;span&gt;nullptr&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        auto&lt;/span&gt;&lt;span&gt; [l, ln] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; dfs&lt;/span&gt;&lt;span&gt;(p-&amp;gt;left);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        auto&lt;/span&gt;&lt;span&gt; [r, rn] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; dfs&lt;/span&gt;&lt;span&gt;(p-&amp;gt;right);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        if&lt;/span&gt;&lt;span&gt; (ln &lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; rn) &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; {l, ln&lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        if&lt;/span&gt;&lt;span&gt; (rn &lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; ln) &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; {r, rn&lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        return&lt;/span&gt;&lt;span&gt; {p, ln&lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;}; &lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    TreeNode&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; subtreeWithAllDeepest&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;TreeNode&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; root&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        return&lt;/span&gt;&lt;span&gt; dfs&lt;/span&gt;&lt;span&gt;(root).first;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;LeetCode 个人主页&lt;a href=&quot;#leetcode-个人主页&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;div&gt;
  &lt;a href=&quot;https://leetcode.cn/u/kalev/&quot; target=&quot;_blank&quot;&gt;
    &lt;div&gt;
      &lt;div&gt;
        &lt;div&gt;
          &lt;img src=&quot;https://leetcode.cn/favicon.ico&quot; alt=&quot;&quot; loading=&quot;lazy&quot; /&gt;
          &lt;span&gt;leetcode.cn&lt;/span&gt;
        &lt;/div&gt;
        &lt;h3&gt;kalev - 力扣（LeetCode）&lt;/h3&gt;
        &lt;p&gt;View kalev’s profile on LeetCode, the world’s largest programming community.&lt;/p&gt;
        &lt;div&gt;
          &lt;span&gt;https://leetcode.cn/u/kalev/&lt;/span&gt;
           
        &lt;/div&gt;
      &lt;/div&gt;
      &lt;div&gt;&lt;img src=&quot;https://leetcode.com/static/images/LeetCode_Sharing.png&quot; alt=&quot;kalev - 力扣（LeetCode）&quot; loading=&quot;lazy&quot; /&gt;&lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;</content:encoded><category>category:笔记</category><category>tag:LeetCode</category><category>tag:DFS</category></item><item><title>神经网络实践：Spaceship Titanic预测模型优化笔记</title><link>https://kalev-ovo.github.io/post/spaceship-titanic-nn</link><guid isPermaLink="false">spaceship-titanic-nn</guid><description>在这篇博客中，我将分享在 Kaggle 的 Spaceship Titanic 竞赛中构建预测模型的完整流程，重点介绍如何通过余弦退火学习率调度器提升模型性能。
竞赛概述
Spaceship Titanic 是 Kaggle</description><pubDate>Mon, 24 Nov 2025 16:41:20 GMT</pubDate><content:encoded>&lt;p&gt;在这篇博客中，我将分享在 Kaggle 的 Spaceship Titanic 竞赛中构建预测模型的完整流程，重点介绍如何通过余弦退火学习率调度器提升模型性能。&lt;/p&gt;
&lt;h2&gt;竞赛概述&lt;a href=&quot;#竞赛概述&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Spaceship Titanic 是 Kaggle 上的一个经典二分类问题，目标是预测乘客是否在宇宙飞船事故中被运输到另一个维度。这是一个典型的结构化数据分类问题，适合初学者入门机器学习。&lt;/p&gt;
&lt;h2&gt;模型架构与优化策略&lt;a href=&quot;#模型架构与优化策略&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;核心模型结构&lt;a href=&quot;#核心模型结构&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;我构建了一个简单的全连接神经网络作为基础模型：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; SpaceshipNN&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;nn&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Module&lt;/span&gt;&lt;span&gt;):&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    def&lt;/span&gt;&lt;span&gt; __init__&lt;/span&gt;&lt;span&gt;(self, input_size):&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        super&lt;/span&gt;&lt;span&gt;(SpaceshipNN, &lt;/span&gt;&lt;span&gt;self&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;__init__&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        self&lt;/span&gt;&lt;span&gt;.fc1 &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; nn.Linear(input_size, &lt;/span&gt;&lt;span&gt;128&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        self&lt;/span&gt;&lt;span&gt;.fc2 &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; nn.Linear(&lt;/span&gt;&lt;span&gt;128&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;64&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        self&lt;/span&gt;&lt;span&gt;.fc3 &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; nn.Linear(&lt;/span&gt;&lt;span&gt;64&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;32&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        self&lt;/span&gt;&lt;span&gt;.fc4 &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; nn.Linear(&lt;/span&gt;&lt;span&gt;32&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        self&lt;/span&gt;&lt;span&gt;.dropout &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; nn.Dropout(&lt;/span&gt;&lt;span&gt;0.3&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        self&lt;/span&gt;&lt;span&gt;.relu &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; nn.ReLU()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        self&lt;/span&gt;&lt;span&gt;.sigmoid &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; nn.Sigmoid()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    def&lt;/span&gt;&lt;span&gt; forward&lt;/span&gt;&lt;span&gt;(self, x):&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        x &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; self&lt;/span&gt;&lt;span&gt;.relu(&lt;/span&gt;&lt;span&gt;self&lt;/span&gt;&lt;span&gt;.fc1(x))&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        x &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; self&lt;/span&gt;&lt;span&gt;.dropout(x)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        x &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; self&lt;/span&gt;&lt;span&gt;.relu(&lt;/span&gt;&lt;span&gt;self&lt;/span&gt;&lt;span&gt;.fc2(x))&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        x &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; self&lt;/span&gt;&lt;span&gt;.dropout(x)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        x &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; self&lt;/span&gt;&lt;span&gt;.relu(&lt;/span&gt;&lt;span&gt;self&lt;/span&gt;&lt;span&gt;.fc3(x))&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        x &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; self&lt;/span&gt;&lt;span&gt;.sigmoid(&lt;/span&gt;&lt;span&gt;self&lt;/span&gt;&lt;span&gt;.fc4(x))&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        return&lt;/span&gt;&lt;span&gt; x&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;关键优化：余弦退火学习率调度&lt;a href=&quot;#关键优化余弦退火学习率调度&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;为什么选择余弦退火？
传统的学习率调度器如 ReduceLROnPlateau 在验证损失停止改善时降低学习率，但这种方法可能导致训练过早收敛。余弦退火则提供了一种更平滑、周期性的学习率调整方式：&lt;/p&gt;
&lt;h3&gt;替换传统的 ReduceLROnPlateau 为 CosineAnnealingLR&lt;a href=&quot;#替换传统的-reducelronplateau-为-cosineannealinglr&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; torch.optim.lr_scheduler &lt;/span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; CosineAnnealingLR&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# 初始化优化器&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;optimizer &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; torch.optim.Adam(model.parameters(), &lt;/span&gt;&lt;span&gt;lr&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;1e-3&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# 初始化余弦退火学习率调度器&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;scheduler &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; CosineAnnealingLR(optimizer, &lt;/span&gt;&lt;span&gt;T_max&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;50&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;eta_min&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;1e-6&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;实现细节&lt;a href=&quot;#实现细节&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;T_max=50：半个周期的 epoch 数&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;eta_min=1e-6：最小学习率&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;每个 epoch 后调用 scheduler.step()更新学习率&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;训练流程优化&lt;a href=&quot;#训练流程优化&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; train_with_cosine_annealing&lt;/span&gt;&lt;span&gt;(model, train_loader, val_loader, epochs&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;100&lt;/span&gt;&lt;span&gt;):&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    for&lt;/span&gt;&lt;span&gt; epoch &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt; range&lt;/span&gt;&lt;span&gt;(epochs):&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        # 训练阶段&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        model.train()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        for&lt;/span&gt;&lt;span&gt; batch &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt; train_loader:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            # 前向传播、反向传播、参数更新&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            ...&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        # 验证阶段&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        model.eval()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        with&lt;/span&gt;&lt;span&gt; torch.no_grad():&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            ...&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        # 更新学习率（关键步骤）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        scheduler.step()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        # 记录和打印训练信息&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        if&lt;/span&gt;&lt;span&gt; epoch &lt;/span&gt;&lt;span&gt;%&lt;/span&gt;&lt;span&gt; 10&lt;/span&gt;&lt;span&gt; ==&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            print&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;f&lt;/span&gt;&lt;span&gt;&apos;Epoch &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;epoch&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; | Train Loss: &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;train_loss&lt;/span&gt;&lt;span&gt;:.4f&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; | &apos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  f&lt;/span&gt;&lt;span&gt;&apos;Val Loss: &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;val_loss&lt;/span&gt;&lt;span&gt;:.4f&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; | LR: &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;current_lr&lt;/span&gt;&lt;span&gt;:.6f&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;实验结果&lt;a href=&quot;#实验结果&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;通过引入余弦退火学习率调度，模型表现有了显著提升：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;初始得分：0.80360&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;优化后最佳得分：0.80734（提升约 0.47%）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;训练稳定性：明显改善，避免了学习率过早衰减的问题&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;技术要点总结&lt;a href=&quot;#技术要点总结&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;学习率调度的重要性：合适的学习率调度策略可以显著影响模型收敛和最终性能。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;余弦退火的优势：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;平滑的学习率变化减少训练震荡&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;周期性重启帮助跳出局部最优&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;无需手动设置 patience 参数&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;实践建议：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;根据数据集大小调整 T_max 参数&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;监控学习率变化确保合理范围&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;结合早停法防止过拟合&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;进一步优化方向&lt;a href=&quot;#进一步优化方向&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;虽然余弦退火带来了明显改进，但仍有进一步优化的空间：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;带热身的余弦退火：使用 CosineAnnealingWarmRestarts 实现更复杂的调度策略&lt;/li&gt;
&lt;li&gt;模型架构优化：尝试更深的网络或注意力机制&lt;/li&gt;
&lt;li&gt;特征工程：深入挖掘数据特征的相关性&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;结语&lt;a href=&quot;#结语&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;通过这个项目，我深刻体会到学习率调度在深度学习中的重要性。余弦退火作为一种简单而有效的技术，值得在各类分类任务中尝试和应用。机器学习不仅是模型架构的竞赛，更是对训练细节的精心打磨。&lt;/p&gt;
&lt;p&gt;完整代码和详细实现请参考我的 Kaggle 笔记本：&lt;a href=&quot;https://www.kaggle.com/code/kalevyang/notebooke4132da306&quot;&gt;https://www.kaggle.com/code/kalevyang/notebooke4132da306&lt;/a&gt;&lt;/p&gt;</content:encoded><category>category:笔记</category><category>tag:神经网络</category><category>tag:预测模型</category><category>tag:机器学习</category></item><item><title>基于Vue的棋局数据分析网页</title><link>https://kalev-ovo.github.io/post/vue-go-analysis</link><guid isPermaLink="false">vue-go-analysis</guid><description>围棋分析系统
从 25 年 1 月初开始学围棋，基本上在野狐围棋上对局。但是苦于野狐围棋并没有个人的黑棋白棋胜率及更多的分析数据，于是想自己实现一个基于 Vue 的棋局数据分析网页
项目概述
这是一个基于 Vue 3 构建的围棋对局分析系统，用于记录、管理和分析围棋对局。系统支持 SGF</description><pubDate>Tue, 28 Oct 2025 08:25:41 GMT</pubDate><content:encoded>&lt;h1&gt;围棋分析系统&lt;a href=&quot;#围棋分析系统&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;从 25 年 1 月初开始学围棋，基本上在野狐围棋上对局。但是苦于野狐围棋并没有个人的黑棋白棋胜率及更多的分析数据，于是想自己实现一个基于 Vue 的棋局数据分析网页&lt;/p&gt;
&lt;h2&gt;项目概述&lt;a href=&quot;#项目概述&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;这是一个基于 Vue 3 构建的围棋对局分析系统，用于记录、管理和分析围棋对局。系统支持 SGF 文件导入、对局数据存储、胜率统计分析以及棋局详情查看等功能&lt;/p&gt;
&lt;h2&gt;技术栈&lt;a href=&quot;#技术栈&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;前端框架&lt;/strong&gt;: Vue 3 + Vite&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;状态管理&lt;/strong&gt;: Pinia&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;路由管理&lt;/strong&gt;: Vue Router&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;数据可视化&lt;/strong&gt;: Chart.js&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;本地存储&lt;/strong&gt;: IndexedDB (通过 idb 库)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SGF 解析&lt;/strong&gt;: @sabaki/sgf&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;语言&lt;/strong&gt;: TypeScript&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;构建工具&lt;/strong&gt;: Vite&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;项目结构&lt;a href=&quot;#项目结构&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;├── src/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   ├── assets/           # 静态资源&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   ├── components/       # 组件&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   ├── lib/              # 工具函数库&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   ├── router/           # 路由配置&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   ├── stores/           # 状态管理&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   ├── types/            # 类型定义&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   ├── views/            # 页面组件&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   ├── App.vue           # 应用根组件&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   └── main.ts           # 入口文件&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├── public/               # 公共资源&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├── .vscode/              # VSCode配置&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├── tsconfig.json         # TypeScript配置&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├── vite.config.ts        # Vite配置&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;└── package.json          # 项目依赖&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;核心功能模块&lt;a href=&quot;#核心功能模块&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;1. 数据存储模块&lt;a href=&quot;#1-数据存储模块&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;使用 IndexedDB 存储对局数据&lt;/li&gt;
&lt;li&gt;提供完整的增删改查操作&lt;/li&gt;
&lt;li&gt;通过 idb 库简化 IndexedDB 操作&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. SGF 解析模块&lt;a href=&quot;#2-sgf-解析模块&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;解析 SGF 格式的对局记录&lt;/li&gt;
&lt;li&gt;提取对局元数据（日期、对局者、结果等）&lt;/li&gt;
&lt;li&gt;提取手数、让子、贴目等信息&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3. 对局管理模块&lt;a href=&quot;#3-对局管理模块&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;添加对局（支持 SGF 文件导入）&lt;/li&gt;
&lt;li&gt;查看对局列表和详情&lt;/li&gt;
&lt;li&gt;删除对局记录&lt;/li&gt;
&lt;li&gt;筛选和排序功能&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;4. 数据统计模块&lt;a href=&quot;#4-数据统计模块&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;计算并展示总对局数&lt;/li&gt;
&lt;li&gt;统计黑棋和白棋胜率&lt;/li&gt;
&lt;li&gt;展示胜率趋势图表&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;关键文件说明&lt;a href=&quot;#关键文件说明&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;1. 数据存储&lt;a href=&quot;#1-数据存储&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;src/lib/db.ts&lt;/strong&gt;: 数据库初始化和配置&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { openDB } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;idb&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; const&lt;/span&gt;&lt;span&gt; initDB&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; async&lt;/span&gt;&lt;span&gt; () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  return&lt;/span&gt;&lt;span&gt; openDB&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;GoDB&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;, {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    upgrade&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;db&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;db.objectStoreNames.&lt;/span&gt;&lt;span&gt;contains&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;games&apos;&lt;/span&gt;&lt;span&gt;)) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        const&lt;/span&gt;&lt;span&gt; store&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; db.&lt;/span&gt;&lt;span&gt;createObjectStore&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;games&apos;&lt;/span&gt;&lt;span&gt;, { keyPath: &lt;/span&gt;&lt;span&gt;&apos;id&apos;&lt;/span&gt;&lt;span&gt;, autoIncrement: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt; });&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        store.&lt;/span&gt;&lt;span&gt;createIndex&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;date&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;date&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        store.&lt;/span&gt;&lt;span&gt;createIndex&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;color&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;color&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        store.&lt;/span&gt;&lt;span&gt;createIndex&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;result&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;result&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  });&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2. 状态管理&lt;a href=&quot;#2-状态管理&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;src/stores/games.ts&lt;/strong&gt;: 棋局数据的状态管理
&lt;ul&gt;
&lt;li&gt;存储棋局列表&lt;/li&gt;
&lt;li&gt;提供数据计算（胜率、弱点分析等）&lt;/li&gt;
&lt;li&gt;封装数据库操作&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3. SGF 解析&lt;a href=&quot;#3-sgf-解析&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;src/lib/sgf.ts&lt;/strong&gt;: SGF 文件解析工具
&lt;ul&gt;
&lt;li&gt;解析 SGF 字符串&lt;/li&gt;
&lt;li&gt;提取对局信息和手数&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;4. 页面组件&lt;a href=&quot;#4-页面组件&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;GameList.vue&lt;/strong&gt;: 对局列表页面&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GameDetail.vue&lt;/strong&gt;: 对局详情页面&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AddGame.vue&lt;/strong&gt;: 添加对局页面&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Statistics.vue&lt;/strong&gt;: 数据统计页面&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;路由配置&lt;a href=&quot;#路由配置&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { createRouter, createWebHistory } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;vue-router&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; HomeView &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;../views/HomeView.vue&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; router&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; createRouter&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  history: &lt;/span&gt;&lt;span&gt;createWebHistory&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;meta&lt;/span&gt;&lt;span&gt;.env.&lt;/span&gt;&lt;span&gt;BASE_URL&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  routes: [&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      path: &lt;/span&gt;&lt;span&gt;&apos;/&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      name: &lt;/span&gt;&lt;span&gt;&apos;home&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      component: HomeView,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      path: &lt;/span&gt;&lt;span&gt;&apos;/about&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      name: &lt;/span&gt;&lt;span&gt;&apos;about&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      component&lt;/span&gt;&lt;span&gt;: () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; import&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;../views/AboutView.vue&apos;&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      path: &lt;/span&gt;&lt;span&gt;&apos;/game-list&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      name: &lt;/span&gt;&lt;span&gt;&apos;game-list&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      component&lt;/span&gt;&lt;span&gt;: () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; import&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;../views/GameList.vue&apos;&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      path: &lt;/span&gt;&lt;span&gt;&apos;/add-game&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      name: &lt;/span&gt;&lt;span&gt;&apos;add-game&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      component&lt;/span&gt;&lt;span&gt;: () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; import&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;../views/AddGame.vue&apos;&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      path: &lt;/span&gt;&lt;span&gt;&apos;/statistics&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      name: &lt;/span&gt;&lt;span&gt;&apos;statistics&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      component&lt;/span&gt;&lt;span&gt;: () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; import&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;../views/Statistics.vue&apos;&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      path: &lt;/span&gt;&lt;span&gt;&apos;/game/:id&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      name: &lt;/span&gt;&lt;span&gt;&apos;game-detail&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      component&lt;/span&gt;&lt;span&gt;: () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; import&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;../views/GameDetail.vue&apos;&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ],&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;})&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; default&lt;/span&gt;&lt;span&gt; router&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;核心数据类型&lt;a href=&quot;#核心数据类型&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;Game 接口&lt;a href=&quot;#game-接口&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; interface&lt;/span&gt;&lt;span&gt; Game&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  id&lt;/span&gt;&lt;span&gt;?:&lt;/span&gt;&lt;span&gt; number&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  date&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; string&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  color&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &apos;black&apos;&lt;/span&gt;&lt;span&gt; |&lt;/span&gt;&lt;span&gt; &apos;white&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  opponent&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; string&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  result&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &apos;win&apos;&lt;/span&gt;&lt;span&gt; |&lt;/span&gt;&lt;span&gt; &apos;loss&apos;&lt;/span&gt;&lt;span&gt; |&lt;/span&gt;&lt;span&gt; &apos;draw&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  sgf&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; string&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  createdAt&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; string&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  analysis&lt;/span&gt;&lt;span&gt;?:&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    winRate&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; number&lt;/span&gt;&lt;span&gt;[];&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    mistakes&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;move&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; number&lt;/span&gt;&lt;span&gt;; &lt;/span&gt;&lt;span&gt;position&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; string&lt;/span&gt;&lt;span&gt;; &lt;/span&gt;&lt;span&gt;severity&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; number&lt;/span&gt;&lt;span&gt; }[];&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  };&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;使用说明&lt;a href=&quot;#使用说明&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;安装依赖&lt;a href=&quot;#安装依赖&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;npm&lt;/span&gt;&lt;span&gt; install&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;开发模式启动&lt;a href=&quot;#开发模式启动&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;npm&lt;/span&gt;&lt;span&gt; run&lt;/span&gt;&lt;span&gt; dev&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;构建生产版本&lt;a href=&quot;#构建生产版本&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;npm&lt;/span&gt;&lt;span&gt; run&lt;/span&gt;&lt;span&gt; build&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;预览生产版本&lt;a href=&quot;#预览生产版本&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;npm&lt;/span&gt;&lt;span&gt; run&lt;/span&gt;&lt;span&gt; preview&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;功能说明&lt;a href=&quot;#功能说明&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;添加对局&lt;a href=&quot;#添加对局&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;点击”添加对局”导航&lt;/li&gt;
&lt;li&gt;选择 SGF 格式的对局文件&lt;/li&gt;
&lt;li&gt;系统自动解析对局信息&lt;/li&gt;
&lt;li&gt;确认信息无误后保存对局&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;查看对局列表&lt;a href=&quot;#查看对局列表&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;点击”对局列表”导航&lt;/li&gt;
&lt;li&gt;可根据结果和颜色进行筛选&lt;/li&gt;
&lt;li&gt;列表显示对局的关键信息，包括日期、颜色、对手、手数、让子和结果&lt;/li&gt;
&lt;li&gt;点击”查看”按钮进入对局详情&lt;/li&gt;
&lt;li&gt;点击”删除”按钮删除对局&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;查看统计数据&lt;a href=&quot;#查看统计数据&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;点击”统计”导航&lt;/li&gt;
&lt;li&gt;查看总对局数、黑棋胜率和白棋胜率&lt;/li&gt;
&lt;li&gt;查看胜率趋势图表&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;项目特点&lt;a href=&quot;#项目特点&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;本地存储&lt;/strong&gt;: 所有数据存储在浏览器的 IndexedDB 中，无需后端服务&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SGF 支持&lt;/strong&gt;: 支持标准 SGF 格式的围棋对局文件导入和解析&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;数据可视化&lt;/strong&gt;: 使用 Chart.js 提供胜率趋势图表&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;响应式设计&lt;/strong&gt;: 适配不同屏幕尺寸&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;类型安全&lt;/strong&gt;: 使用 TypeScript 确保代码质量和开发体验&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;扩展方向&lt;a href=&quot;#扩展方向&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;增加 AI 分析功能&lt;/li&gt;
&lt;li&gt;添加对局复盘功能&lt;/li&gt;
&lt;li&gt;支持多用户数据同步&lt;/li&gt;
&lt;li&gt;添加更多统计图表和数据洞察&lt;/li&gt;
&lt;li&gt;实现对局评论和笔记功能&lt;/li&gt;
&lt;/ol&gt;</content:encoded><category>category:笔记</category><category>tag:Vue</category><category>tag:棋局数据分析</category><category>tag:前端开发</category></item></channel></rss>