<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[光和尘's RSS Feed]]></title><description><![CDATA[光和尘的个人站点]]></description><link>https://me.guanghechen.com</link><generator>GatsbyJS</generator><lastBuildDate>Sun, 04 Jun 2023 15:10:23 GMT</lastBuildDate><item><title><![CDATA[Javascript 踩坑记——继承和原型链]]></title><description><![CDATA[<section class="yozora-markdown"><main><h2 class="yozora-heading"><span class="yozora-text">前言</span></h2><p class="yozora-paragraph"><span class="yozora-text">和其它面向对象的语言不同——继承只存在于两个不同的类之间——，Javascript 没有真正的类的概念。它采用一种原型链的机制，通过原型对象的连接关系来表达继承：通过某个属性（</span><pre class="yozora-inline-code"><code>__proto__</code></pre><span class="yozora-text">）将原型对象连接成树形结构，则所谓的继承即为该树中节点与其祖先节点的血缘关系。在访问某个对象的属性时，会顺着原型对象树往上寻找目标属性，并返回第一个含有此属性的节点的对应属性值。这种继承策略带来的副产品是，可以轻易地通过修改原型对象上的属性使得所有继承它的对象都拥有此新增属性</span></p></main><footer><div class="yozora-footnote-definitions"><div class="yozora-footnote-definitions__title">footnote-definitions</div><ul class="yozora-footnote-definitions__main"></ul></div></footer></section>]]></description><link>https://me.guanghechen.com/post/web/javascript/inherit/</link><guid isPermaLink="false">https://me.guanghechen.com/post/web/javascript/inherit/</guid><content:encoded>&lt;section class=&quot;yozora-markdown&quot;&gt;&lt;main&gt;&lt;h2 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;前言&lt;/span&gt;&lt;/h2&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;和其它面向对象的语言不同——继承只存在于两个不同的类之间——，Javascript 没有真正的类的概念。它采用一种原型链的机制，通过原型对象的连接关系来表达继承：通过某个属性（&lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;__proto__&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt;）将原型对象连接成树形结构，则所谓的继承即为该树中节点与其祖先节点的血缘关系。在访问某个对象的属性时，会顺着原型对象树往上寻找目标属性，并返回第一个含有此属性的节点的对应属性值。这种继承策略带来的副产品是，可以轻易地通过修改原型对象上的属性使得所有继承它的对象都拥有此新增属性&lt;/span&gt;&lt;sup id=&quot;reference-footnote-1&quot; class=&quot;yozora-footnote-reference&quot;&gt;&lt;a href=&quot;#footnote-1&quot; title=&quot;1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;&lt;span class=&quot;yozora-text&quot;&gt;。&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-admonition yozora-admonition--warning&quot;&gt;&lt;h5 class=&quot;yozora-admonition__heading&quot;&gt;&lt;span class=&quot;yozora-admonition__heading-icon&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; height=&quot;24px&quot; viewBox=&quot;0 0 24 24&quot; width=&quot;24px&quot;&gt;&lt;path d=&quot;M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z&quot; /&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;yozora-admonition__heading-title&quot;&gt;CAUTION&lt;/span&gt;&lt;/h5&gt;&lt;div class=&quot;yozora-admonition__body&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;__proto__&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 虽然被纳入了 ECMA 标准中，但目前它是不被推荐使用的，请使用
&lt;/span&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/getPrototypeOf&quot; title=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/getPrototypeOf&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;Reflect.getPrototypeOf()&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt; 和 &lt;/span&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/setPrototypeOf&quot; title=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/setPrototypeOf&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;Reflect.setPrototypeOf()&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;
代替。&lt;/span&gt;&lt;/p&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;出于方便叙述考虑，本文仍采用 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;__proto__&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 进行演示。&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;h2 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;原型链&lt;/span&gt;&lt;/h2&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;在 ES6 标准里，所有的对象都有两个内置属性 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;constructor&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 和 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;__proto__&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt;，其中：&lt;/span&gt;&lt;/p&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;constructor&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt;: 指向创建它的构造函数。&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;__proto__&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt;: 指向创建它的构造函数的原型对象。&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;构造函数还有一个内置属性 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;prototype&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 指向它的原型对象；而构造函数的原型对象的
&lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;constructor&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 又指向此构造函数。有点绕，可以看下面的代码示例&lt;/span&gt;&lt;sup id=&quot;reference-footnote-2&quot; class=&quot;yozora-footnote-reference&quot;&gt;&lt;a href=&quot;#footnote-2&quot; title=&quot;2&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt;&lt;span class=&quot;yozora-text&quot;&gt;：&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; name
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; alice &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Alice&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

alice&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;constructor &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; Person &lt;span class=&quot;token comment&quot;&gt;// =&gt; true&lt;/span&gt;
alice&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;__proto__ &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prototype &lt;span class=&quot;token comment&quot;&gt;// =&gt; true&lt;/span&gt;

&lt;span class=&quot;token class-name&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;constructor &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; Function &lt;span class=&quot;token comment&quot;&gt;// =&gt; true&lt;/span&gt;
Person&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;__proto__ &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prototype &lt;span class=&quot;token comment&quot;&gt;// =&gt; true&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prototype&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;constructor &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; Person &lt;span class=&quot;token comment&quot;&gt;// =&gt; true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;继承&lt;/span&gt;&lt;/h2&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;前面提到过，Javascript 通过原型对象的连接关系来表达继承，因此我们可以修改连接关系来实现继承。比如下面的代码中，想让 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;Student&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 继承自 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;Person&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt;，只需要将
&lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;Student&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 的原型对象的 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;__proto__&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 指向 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;Person&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 的原型对象就好了（仅用于演示，请不要投入到生产中）：&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; name &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prototype&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;great&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Hello, &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Student&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;grade&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;grade &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; grade &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;Student&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prototype&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;showGrade&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Grade: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;grade&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token class-name&quot;&gt;Student&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prototype&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;__proto__ &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prototype

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; alice &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Student&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
alice &lt;span class=&quot;token keyword&quot;&gt;instanceof&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Student&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// =&gt; true&lt;/span&gt;
alice &lt;span class=&quot;token keyword&quot;&gt;instanceof&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Person&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// =&gt; true&lt;/span&gt;

alice&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;great&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// =&gt; &apos;Hello, undefined!&apos;&lt;/span&gt;
alice&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Alice&apos;&lt;/span&gt;
alice&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;showGrade&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// =&gt; &apos;Grade: 2&apos;&lt;/span&gt;
alice&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;great&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// =&gt; &apos;Hello, Alice!&apos;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;yozora-admonition yozora-admonition--warning&quot;&gt;&lt;h5 class=&quot;yozora-admonition__heading&quot;&gt;&lt;span class=&quot;yozora-admonition__heading-icon&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; height=&quot;24px&quot; viewBox=&quot;0 0 24 24&quot; width=&quot;24px&quot;&gt;&lt;path d=&quot;M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z&quot; /&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;yozora-admonition__heading-title&quot;&gt;CAUTION&lt;/span&gt;&lt;/h5&gt;&lt;div class=&quot;yozora-admonition__body&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;上面的修改看起来很直观，但直接操作 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;__proto__&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 是不被推荐的，你可以使用
&lt;/span&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/setPrototypeOf&quot; title=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/setPrototypeOf&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;Reflect.setPrototypeOf()&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt; 来做修改。&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;hr class=&quot;yozora-thematic-break&quot; /&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;此外，原型对象之间的连接还可以通过 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;constructor&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 和 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;constructor.prototype&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 来表达，（前文提到过，对象的 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;constructor&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 指向其构造函数）如最开始的例子中：&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;alice&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;__proto__ &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Student&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prototype &lt;span class=&quot;token comment&quot;&gt;// =&gt; true&lt;/span&gt;
Student&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;__proto__ &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prototype &lt;span class=&quot;token comment&quot;&gt;// =&gt; true&lt;/span&gt;
Person&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;__proto__ &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prototype &lt;span class=&quot;token comment&quot;&gt;// =&gt; true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;等价于：&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;alice&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;constructor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prototype &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prototype &lt;span class=&quot;token comment&quot;&gt;// =&gt; true&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;constructor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prototype &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prototype &lt;span class=&quot;token comment&quot;&gt;// =&gt; true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;因此我们可以利用 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;Object.create&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 来创建一个以 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;Person.prototype&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 为原型对象的对象，然后将 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;Student.prototype&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 指向此对象：&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;inherit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;Child&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Parent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; prototype &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Parent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prototype&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; key &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getOwnPropertyNames&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Child&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prototype&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;prototype&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;hasOwnProperty&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;continue&lt;/span&gt;
    prototype&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Child&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prototype&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  prototype&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;constructor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Child
  &lt;span class=&quot;token class-name&quot;&gt;Child&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prototype &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; prototype
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;yozora-admonition yozora-admonition--success&quot;&gt;&lt;h5 class=&quot;yozora-admonition__heading&quot;&gt;&lt;span class=&quot;yozora-admonition__heading-icon&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; height=&quot;24px&quot; viewBox=&quot;0 0 24 24&quot; width=&quot;24px&quot;&gt;&lt;path d=&quot;M9 21c0 .55.45 1 1 1h4c.55 0 1-.45 1-1v-1H9v1zm3-19C8.14 2 5 5.14 5 9c0 2.38 1.19 4.47 3 5.74V17c0 .55.45 1 1 1h6c.55 0 1-.45 1-1v-2.26c1.81-1.27 3-3.36 3-5.74 0-3.86-3.14-7-7-7zm2.85 11.1l-.85.6V16h-4v-2.3l-.85-.6C7.8 12.16 7 10.63 7 9c0-2.76 2.24-5 5-5s5 2.24 5 5c0 1.63-.8 3.16-2.15 4.1z&quot; /&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;yozora-admonition__heading-title&quot;&gt;SUCCESS&lt;/span&gt;&lt;/h5&gt;&lt;div class=&quot;yozora-admonition__body&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;注意上面代码高亮部分，将 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;Child.prototype&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 的值复制到了新的原型对象中，否则调用此继承函数后，原先定义在 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;Child.prototype&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 上的属性会丢失。&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;h2 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;创建对象&lt;/span&gt;&lt;/h2&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;在 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;Javascript&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 中创建对象有多种方式：&lt;/span&gt;&lt;/p&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;new&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt;: &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;new Person()&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 创建并返回一个以 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;Person.constructor&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 作为原型对象的对象，因此返回的对象继承自 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;Person&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;Object.create&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt;: &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;Object.create(__proto__)&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt;: 接受一个对象 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;__proto__&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt;，创建并返回一个以此对象作为原型对象的对象，因此使用 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;Object.create(null)&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 可以创建一个不继承自任何对象的对象。&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;const alice = { name: &apos;alice&apos; }&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt;: 等价于 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;const alice = new Object({ name: &apos;alice&apos; })&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;Related&lt;/span&gt;&lt;/h2&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/getPrototypeOf&quot; title=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/getPrototypeOf&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;Reflect.getPrototypeOf() | MDN&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/setPrototypeOf&quot; title=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/setPrototypeOf&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;Reflect.setPrototypeOf() | MDN&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Inheritance&quot; title=&quot;https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Inheritance&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;Inheritance in JavaScript | MDN&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://stackoverflow.com/questions/12201082/prototypal-inheritance-concept-in-javascript-as-a-prototype-based-language&quot; title=&quot;https://stackoverflow.com/questions/12201082/prototypal-inheritance-concept-in-javascript-as-a-prototype-based-language&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;prototypal inheritance concept in javascript as a prototype based language | Stack Overflow&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://stackoverflow.com/questions/4166616/understanding-the-difference-between-object-create-and-new-somefunction&quot; title=&quot;https://stackoverflow.com/questions/4166616/understanding-the-difference-between-object-create-and-new-somefunction&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;Understanding the difference between Object.create() and new SomeFunction() | Stack Overflow&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div className=&quot;yozora-footnote-definition&quot;&gt;&lt;p className=&quot;yozora-footnote-definition__title yozora-paragraph&quot;&gt;&lt;a href=&quot;#reference-footnote-1&quot;&gt;&amp;uarr;&lt;/a&gt;&lt;span&gt;&amp;nbsp;[1]:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;div className=&quot;yozora-footnote-definition__content&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;其它面向对象的语言不得不修改类的实现才能获得此能力。&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div className=&quot;yozora-footnote-definition&quot;&gt;&lt;p className=&quot;yozora-footnote-definition__title yozora-paragraph&quot;&gt;&lt;a href=&quot;#reference-footnote-2&quot;&gt;&amp;uarr;&lt;/a&gt;&lt;span&gt;&amp;nbsp;[2]:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;div className=&quot;yozora-footnote-definition__content&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;需要注意的是，构造函数如果返回一个对象，则此对象的 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;__proto__&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 将指向
&lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;Object.prototype&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt;，如：&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; name
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; name &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; alice &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;alice&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
alice&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;constructor &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; Object &lt;span class=&quot;token comment&quot;&gt;// =&gt; true&lt;/span&gt;
alice&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;__proto__ &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prototype &lt;span class=&quot;token comment&quot;&gt;// =&gt; true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div className=&quot;yozora-footnote-definition&quot;&gt;&lt;p className=&quot;yozora-footnote-definition__title yozora-paragraph&quot;&gt;&lt;a href=&quot;#reference-footnote-3&quot;&gt;&amp;uarr;&lt;/a&gt;&lt;span&gt;&amp;nbsp;[3]:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;div className=&quot;yozora-footnote-definition__content&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;Javascript 访问属性时，会沿着继承链往上找，所以即便是一个空对象，还是能够访问其祖先节点上定义的属性的。&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/main&gt;&lt;footer&gt;&lt;div class=&quot;yozora-footnote-definitions&quot;&gt;&lt;div class=&quot;yozora-footnote-definitions__title&quot;&gt;footnote-definitions&lt;/div&gt;&lt;ul class=&quot;yozora-footnote-definitions__main&quot;&gt;&lt;div className=&quot;yozora-footnote-definition&quot;&gt;&lt;p className=&quot;yozora-footnote-definition__title yozora-paragraph&quot;&gt;&lt;a href=&quot;#reference-footnote-1&quot;&gt;&amp;uarr;&lt;/a&gt;&lt;span&gt;&amp;nbsp;[1]:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;div className=&quot;yozora-footnote-definition__content&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;其它面向对象的语言不得不修改类的实现才能获得此能力。&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div className=&quot;yozora-footnote-definition&quot;&gt;&lt;p className=&quot;yozora-footnote-definition__title yozora-paragraph&quot;&gt;&lt;a href=&quot;#reference-footnote-2&quot;&gt;&amp;uarr;&lt;/a&gt;&lt;span&gt;&amp;nbsp;[2]:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;div className=&quot;yozora-footnote-definition__content&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;需要注意的是，构造函数如果返回一个对象，则此对象的 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;__proto__&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 将指向
&lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;Object.prototype&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt;，如：&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; name
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; name &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; alice &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;alice&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
alice&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;constructor &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; Object &lt;span class=&quot;token comment&quot;&gt;// =&gt; true&lt;/span&gt;
alice&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;__proto__ &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prototype &lt;span class=&quot;token comment&quot;&gt;// =&gt; true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div className=&quot;yozora-footnote-definition&quot;&gt;&lt;p className=&quot;yozora-footnote-definition__title yozora-paragraph&quot;&gt;&lt;a href=&quot;#reference-footnote-3&quot;&gt;&amp;uarr;&lt;/a&gt;&lt;span&gt;&amp;nbsp;[3]:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;div className=&quot;yozora-footnote-definition__content&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;Javascript 访问属性时，会沿着继承链往上找，所以即便是一个空对象，还是能够访问其祖先节点上定义的属性的。&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/footer&gt;&lt;/section&gt;</content:encoded></item><item><title><![CDATA[防抖和节流]]></title><description><![CDATA[<section class="yozora-markdown"><main><h2 class="yozora-heading"><span class="yozora-text">前言</span></h2><p class="yozora-paragraph"><span class="yozora-text">防抖（debounce）和节流（throttle）在前端开发中十分常见，它们都是针对一个事件被连续触发时限制执行次数的算法，不同的是 debounce 只处理最后一次事件触发，而 throttle
则以一个固定的频率处理事件触发。你可以在</span><span class="yozora-text">直观地观察到它们之间的区别。</span></p><p class="yozora-paragraph"><span class="yozora-text">在开始正文之前，先看一下 Typescript 的两个工具类型 </span></p></main><footer><div class="yozora-footnote-definitions"><div class="yozora-footnote-definitions__title">footnote-definitions</div><ul class="yozora-footnote-definitions__main"></ul></div></footer></section>]]></description><link>https://me.guanghechen.com/post/algorithm/debounce-and-throttle/</link><guid isPermaLink="false">https://me.guanghechen.com/post/algorithm/debounce-and-throttle/</guid><content:encoded>&lt;section class=&quot;yozora-markdown&quot;&gt;&lt;main&gt;&lt;h2 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;前言&lt;/span&gt;&lt;/h2&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;防抖（debounce）和节流（throttle）在前端开发中十分常见，它们都是针对一个事件被连续触发时限制执行次数的算法，不同的是 debounce 只处理最后一次事件触发，而 throttle
则以一个固定的频率处理事件触发。你可以在&lt;/span&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;http://demo.nimius.net/debounce_throttle/&quot; title=&quot;http://demo.nimius.net/debounce_throttle/&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;这里&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;直观地观察到它们之间的区别。&lt;/span&gt;&lt;/p&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;在开始正文之前，先看一下 Typescript 的两个工具类型 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;Parameters&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 和 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;ReturnType&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt;，引入它们的目的是因为 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;debounce&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 和 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;throttle&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 的实现都以高阶函数的方式展现，本着&lt;/span&gt;&lt;del class=&quot;yozora-delete&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;作死无极限&lt;/span&gt;&lt;/del&gt;&lt;span class=&quot;yozora-text&quot;&gt;精益求精的态度，在实现时自然要考虑返回的函数和原函数应具有相同的参数类型的调用体验啦。&lt;/span&gt;&lt;/p&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;Parameters&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt;: &lt;/span&gt;&lt;em class=&quot;yozora-emphasis&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;Typescript@3.1&lt;/span&gt;&lt;/em&gt;&lt;span class=&quot;yozora-text&quot;&gt; 引入的工具类型，用于获取一个函数的参数类型&lt;/span&gt;&lt;sup id=&quot;reference-footnote-1&quot; class=&quot;yozora-footnote-reference&quot;&gt;&lt;a href=&quot;#footnote-1&quot; title=&quot;1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;&lt;span class=&quot;yozora-text&quot;&gt;：&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;type Parameters any&amp;gt; = 
  T extends (...args: infer P) =&amp;gt; any ? P : never

type A = Parameters&amp;lt;(x: number, y: number) =&amp;gt; void&amp;gt;
// A 的类型为 [x: number, y: number]
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;ReturnType&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt;: &lt;/span&gt;&lt;em class=&quot;yozora-emphasis&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;Typescript@2.8&lt;/span&gt;&lt;/em&gt;&lt;span class=&quot;yozora-text&quot;&gt; 引入的工具类型，用于获取一个函数的返回类型：&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;type ReturnType any&amp;gt; =
  T extends (...args: any) =&amp;gt; infer R ? R : any

type A = ReturnType&amp;lt;(x: number, y: number) =&amp;gt; string&amp;gt;
// A 的类型为 string
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;防抖 (debounce)&lt;/span&gt;&lt;/h2&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;防抖是指事件连续触发时，只在最后一次事件触发后再执行响应动作。比如，事件被触发的
&lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 秒后才执行回调，若 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 秒内此事件被再次触发，则重新计时。&lt;/span&gt;&lt;/p&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;适用场景：&lt;/span&gt;&lt;/p&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;提交表单时，快速点击多次，但只执行一次提交。&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;搜索框中输入内容实时展示关联条目，只在输入停顿时才发起查询请求（和 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;throttle&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt;
中列出的场景有所不同）。&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h3 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;简化版&lt;/span&gt;&lt;/h3&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;在了解了 debounce 要解决的问题后，不难想到可以利用 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;setTimeout&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 来倒计时，当函数被顺利执行时，则重置定时器，表示当前处于空闲状态；否则，有一个新的事件在倒计时未结束时触发，则同样将计时器重置，但只开始计时，不执行任务。由此不难实现一个简单的版本：&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;export function debounce any&amp;gt;(
  fn: T,
  wait?: number,
): (...args: Parameters) =&amp;gt; void {
  // 浏览器中 setTimeout 返回的是 number
  // 而 NodeJs 中返回的是 NodeJs.Timeout
  let timeout: ReturnType
  return debounced

  function debounced(...args: Parameters): void {
    if (timeout) clearTimeout(timeout)
    const context = this
    timeout = setTimeout(() =&amp;gt; fn.apply(context, args), wait)
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;注意上面代码的高亮部分，将 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;debounced&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 的 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;this&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 指针绑定到 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;fn&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 中再执行，这是
Javascript 老生常谈的问题了：当 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;fn&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 中通过 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;this&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 引用变量时，&lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;this&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 指针将默认指向 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;Window&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; &lt;/span&gt;&lt;sup id=&quot;reference-footnote-2&quot; class=&quot;yozora-footnote-reference&quot;&gt;&lt;a href=&quot;#footnote-2&quot; title=&quot;2&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt;&lt;span class=&quot;yozora-text&quot;&gt;，而在有些情况下，执行 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;fn&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 时会显式地绑定一个 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;this&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 指针，如 DOM 事件的回调函数中，会把触发事件的元素作为 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;this&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 绑定到 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;fn&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 中，于是我们可以通过 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;this&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 指针去访问当前触发事件的元素：&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;document.body.addEventListener(&apos;click&apos;, debounce(function () {
  console.log(this.innerText)
}))
&lt;/code&gt;&lt;/pre&gt;&lt;h3 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;带返回值&lt;/span&gt;&lt;/h3&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;上面的实现版本中，&lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;debounced&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 返回的是 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;void&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt;，对于大多数场景已经够用了，但如果要返回值的话，应该如何考虑呢？&lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;debounced&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 在事件连续多次触发时只会执行一次，我们可以记录下最后一次执行时的结果，然后在每次非实际执行时，返回上一次的结果：&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;export function debounce any&amp;gt;(
  fn: T,
  wait?: number,
): (...args: Parameters) =&amp;gt; ReturnType | void {
  let timeout: ReturnType
  let lastResult: ReturnType | undefined
  return debounced

  function debounced(...args: Parameters): ReturnType | void {
    if (timeout) clearTimeout(timeout)

    const context = this
    timeout = setTimeout(function () {
      lastResult = fn.apply(context, args)
    }, wait)
    return lastResult
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;h2 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;节流 (throttle)&lt;/span&gt;&lt;/h2&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;节流是指事件连续触发时，在一个固定的时间间隔内只执行一次响应动作。比如事件被触发的 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 秒后再次执行回调，若 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 秒内此事件被再次触发，直接无视，并不重新计时；而对于 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 秒后第一次触发的事件会执行响应，并重新计时。&lt;/span&gt;&lt;/p&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;适用场景：&lt;/span&gt;&lt;/p&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;UI 的拖拽事件、鼠标点击事件、滚动事件等，无需为每一次事件触发进行响应，但需要在一个时间范围内至少作出一次响应，否则会影响用户体验（掉帧）。&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;搜索框中输入内容实时展示关联条目，每隔一个固定的时间间隔（如 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 秒）发起一次查询请求（和 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;debounce&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 中列出的场景有所不同）。&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h3 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;简化版&lt;/span&gt;&lt;/h3&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;既然节流只需保证一个固定周期内只执行一次函数，则可以在空闲状态时接收事件触发，并在事件触发时启动一个倒计时的定时器，在倒计时未结束前，忽略所有的其它事件触发，等到上一次处理操作完成后，再将计时器重置，表明重新进入空闲状态。由此可以实现一个简化版的节流函数：&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;export function throttle any&amp;gt;(
  fn: T,
  wait?: number,
): (...args: Parameters) =&amp;gt; void {
  let timeout: ReturnType
  return throttled

  function throttled(...args: Parameters): void {
    if (timeout) return

    const context = this
    timeout = setTimeout(function () {
      fn.apply(context, args)
      timeout = null
    }, wait)
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;上面的代码中仍然考虑了 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;this&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 指针的指向问题，此处不再赘述。&lt;/span&gt;&lt;/p&gt;&lt;h3 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;带返回值&lt;/span&gt;&lt;/h3&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;同样地，我们可以记录下上一次 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;throttled&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 返回值，在未实际执行的事件触发中，简单地返回一次记录的返回值。&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;export function throttle any&amp;gt;(
  fn: T,
  wait?: number,
): (...args: Parameters) =&amp;gt; ReturnType | void {
  let timeout: ReturnType
  let lastResult: ReturnType | undefined
  return throttled

  function throttled(...args: Parameters): ReturnType | void {
    if (timeout) return lastResult

    const context = this
    timeout = setTimeout(function () {
      lastResult = fn.apply(context, args)
      timeout = null
    }, wait)
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;h2 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;Related&lt;/span&gt;&lt;/h2&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://stackoverflow.com/questions/25991367/difference-between-throttling-and-debouncing-a-function&quot; title=&quot;https://stackoverflow.com/questions/25991367/difference-between-throttling-and-debouncing-a-function&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;Difference Between throttling and debouncing a function | Stack Overflow&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div className=&quot;yozora-footnote-definition&quot;&gt;&lt;p className=&quot;yozora-footnote-definition__title yozora-paragraph&quot;&gt;&lt;a href=&quot;#reference-footnote-1&quot;&gt;&amp;uarr;&lt;/a&gt;&lt;span&gt;&amp;nbsp;[1]:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;div className=&quot;yozora-footnote-definition__content&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;infer&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 关键字是在 &lt;/span&gt;&lt;em class=&quot;yozora-emphasis&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;Typescript@2.9&lt;/span&gt;&lt;/em&gt;&lt;span class=&quot;yozora-text&quot;&gt; 引入的，它用于在条件类型下的类型推断，比如我们在想要获得一个 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;Promise&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 对象的返回值类型：&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;type DataType = T extends Promise ? R : T

type A = DataType          // =&amp;gt; number[]
type B = DataType&amp;gt;   // =&amp;gt; string
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div className=&quot;yozora-footnote-definition&quot;&gt;&lt;p className=&quot;yozora-footnote-definition__title yozora-paragraph&quot;&gt;&lt;a href=&quot;#reference-footnote-2&quot;&gt;&amp;uarr;&lt;/a&gt;&lt;span&gt;&amp;nbsp;[2]:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;div className=&quot;yozora-footnote-definition__content&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;若开启了 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;use strict;&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 选项，则 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;this&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 指针默认指向 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;undefined&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/main&gt;&lt;footer&gt;&lt;div class=&quot;yozora-footnote-definitions&quot;&gt;&lt;div class=&quot;yozora-footnote-definitions__title&quot;&gt;footnote-definitions&lt;/div&gt;&lt;ul class=&quot;yozora-footnote-definitions__main&quot;&gt;&lt;div className=&quot;yozora-footnote-definition&quot;&gt;&lt;p className=&quot;yozora-footnote-definition__title yozora-paragraph&quot;&gt;&lt;a href=&quot;#reference-footnote-1&quot;&gt;&amp;uarr;&lt;/a&gt;&lt;span&gt;&amp;nbsp;[1]:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;div className=&quot;yozora-footnote-definition__content&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;infer&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 关键字是在 &lt;/span&gt;&lt;em class=&quot;yozora-emphasis&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;Typescript@2.9&lt;/span&gt;&lt;/em&gt;&lt;span class=&quot;yozora-text&quot;&gt; 引入的，它用于在条件类型下的类型推断，比如我们在想要获得一个 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;Promise&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 对象的返回值类型：&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;type DataType = T extends Promise ? R : T

type A = DataType          // =&amp;gt; number[]
type B = DataType&amp;gt;   // =&amp;gt; string
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div className=&quot;yozora-footnote-definition&quot;&gt;&lt;p className=&quot;yozora-footnote-definition__title yozora-paragraph&quot;&gt;&lt;a href=&quot;#reference-footnote-2&quot;&gt;&amp;uarr;&lt;/a&gt;&lt;span&gt;&amp;nbsp;[2]:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;div className=&quot;yozora-footnote-definition__content&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;若开启了 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;use strict;&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 选项，则 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;this&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 指针默认指向 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;undefined&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/footer&gt;&lt;/section&gt;</content:encoded></item><item><title><![CDATA[XSS 与 CSRF 的攻防]]></title><description><![CDATA[<section class="yozora-markdown"><main><h2 class="yozora-heading"><span class="yozora-text">前言</span></h2><p class="yozora-paragraph"><span class="yozora-text">本文主要参考了美团技术团队的系列文章：</span></p><ul class="yozora-list"><li class="yozora-list-item"></li><li class="yozora-list-item"></li></ul><h2 class="yozora-heading"><span class="yozora-text">XSS 攻击</span></h2><p class="yozora-paragraph"><span class="yozora-text">XSS</span><sup id="reference-footnote-1" class="yozora-footnote-reference"><a href="#footnote-1" title="1">[1]</a></sup><span class="yozora-text"> (Cross-site Scripting) 跨站脚本攻击，是一种代码注入攻击。攻击者通过在
web 页面中插入浏览器上可执行的恶意代码，在用户浏览网页时恶意代码会被浏览器执行，从而完成攻击。</span></p></main><footer><div class="yozora-footnote-definitions"><div class="yozora-footnote-definitions__title">footnote-definitions</div><ul class="yozora-footnote-definitions__main"></ul></div></footer></section>]]></description><link>https://me.guanghechen.com/post/web/security/xss-csrf/</link><guid isPermaLink="false">https://me.guanghechen.com/post/web/security/xss-csrf/</guid><content:encoded>&lt;section class=&quot;yozora-markdown&quot;&gt;&lt;main&gt;&lt;h2 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;前言&lt;/span&gt;&lt;/h2&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;本文主要参考了美团技术团队的系列文章：&lt;/span&gt;&lt;/p&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://tech.meituan.com/2018/09/27/fe-security.html&quot; title=&quot;https://tech.meituan.com/2018/09/27/fe-security.html&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;前端安全系列（一）：如何防止XSS攻击？&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://tech.meituan.com/2018/10/11/fe-security-csrf.html&quot; title=&quot;https://tech.meituan.com/2018/10/11/fe-security-csrf.html&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;前端安全系列（二）：如何防止CSRF攻击？&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;XSS 攻击&lt;/span&gt;&lt;/h2&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;XSS&lt;/span&gt;&lt;sup id=&quot;reference-footnote-1&quot; class=&quot;yozora-footnote-reference&quot;&gt;&lt;a href=&quot;#footnote-1&quot; title=&quot;1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;&lt;span class=&quot;yozora-text&quot;&gt; (Cross-site Scripting) 跨站脚本攻击，是一种代码注入攻击。攻击者通过在
web 页面中插入浏览器上可执行的恶意代码，在用户浏览网页时恶意代码会被浏览器执行，从而完成攻击。&lt;/span&gt;&lt;/p&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;根据攻击的来源，XSS 攻击可分为存储型、反射型和 DOM 型三种：&lt;/span&gt;&lt;/p&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;存储型 XSS 攻击步骤&lt;/span&gt;&lt;/p&gt;&lt;ol class=&quot;yozora-list&quot; start=&quot;1&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;攻击者将恶意代码提交到目标网站的数据库中（如在输入框中输入
&lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;&quot;&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt;）；&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;用户访问目标网站，&lt;/span&gt;&lt;strong class=&quot;yozora-strong&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;服务端&lt;/span&gt;&lt;/strong&gt;&lt;span class=&quot;yozora-text&quot;&gt;从数据库中取出包含恶意代码的数据，未经正确转义就通过模板引擎渲染成 HTML 返回；&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;用户浏览器渲染接收到的 HTML，混在其中的恶意代码得到执行。&lt;/span&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;反射型 XSS 攻击步骤&lt;/span&gt;&lt;/p&gt;&lt;ol class=&quot;yozora-list&quot; start=&quot;1&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;攻击者构造出特殊的 url，其中包含恶意代码（如
&lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;https://example.com/?keyword=%3Cscript%3Ealert(&apos;xss&apos;)%3C/script%3E&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt;）；&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;用户访问携带恶意代码的 URL，&lt;/span&gt;&lt;strong class=&quot;yozora-strong&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;服务端&lt;/span&gt;&lt;/strong&gt;&lt;span class=&quot;yozora-text&quot;&gt;从 URL 中取出含恶意代码的参数，未经正确转义就通过模板引擎渲染成 HTML 返回；&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;用户浏览器渲染接收到的 HTML，混在其中的恶意代码得到执行。&lt;/span&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;DOM 型 XSS 攻击步骤&lt;/span&gt;&lt;/p&gt;&lt;ol class=&quot;yozora-list&quot; start=&quot;1&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;攻击者构造出特殊的 url，其中包含恶意代码（如
&lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;https://example.com/?to=javascript%3Aalert(&apos;xss&apos;)&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt;）；&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;用户访问携带恶意代码的 URL，&lt;/span&gt;&lt;strong class=&quot;yozora-strong&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;前端&lt;/span&gt;&lt;/strong&gt;&lt;span class=&quot;yozora-text&quot;&gt;从 URL 中取出含恶意代码的参数，传入特殊的 HTML 属性中（如 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;link&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt;），在接下来的事件触发中（如点击前一个括号中的链接）恶意代码得到执行。&lt;/span&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;在 web 页面中，脚本注入有很多方法，常见的有：&lt;/span&gt;&lt;/p&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;构造特殊的 url：有些网站会从 url 中拉取参数直接进行字符串拼接；&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;特殊的 HTML 属性：如在 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;href&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt;、&lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;src&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 属性中，包含 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;javascript:&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 等可执行代码；&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;事件回调函数（如 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;onload&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt;、&lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;onclick&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt;）中注入恶意代码；&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;在标签属性中包含 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;&quot;&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 或 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt;，提前关闭属性甚至合法的标签，从而注入额外的属性甚至 HTML 标签。&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class=&quot;yozora-admonition yozora-admonition--note&quot;&gt;&lt;h5 class=&quot;yozora-admonition__heading&quot;&gt;&lt;span class=&quot;yozora-admonition__heading-icon&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; height=&quot;24px&quot; viewBox=&quot;0 0 24 24&quot; width=&quot;24px&quot;&gt;&lt;path d=&quot;M11 7h2v2h-2zm0 4h2v6h-2zm1-9C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z&quot; /&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;yozora-admonition__heading-title&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;XSS 攻击举例 节选自 &lt;/span&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://tech.meituan.com/2018/09/27/fe-security.html&quot; title=&quot;https://tech.meituan.com/2018/09/27/fe-security.html&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;前端安全系列（一）：如何防止XSS攻击？&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/h5&gt;&lt;div class=&quot;yozora-admonition__body&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;在早期前后端未分离时，很流行使用 JSP 等模板引擎生成 HTML，很容易忘记对用户输入的数据进行转义，直接通过模板引擎进行字符串拼接，从而产生 XSS 漏洞：&lt;/span&gt;&lt;/p&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;script&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 标签&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;keyword: &amp;lt;%= getParameter(&quot;keyword&quot;) %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;如上是一段 JSP 代码，如果攻击者分享了一个 url，其内容为
&lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;https://example.com/?keyword=%3Cscript%3Ealert(&apos;xss&apos;)%3C/script%3E&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt;，则渲染结果为：&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;keyword: &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;span class=&quot;token language-javascript&quot;&gt;&lt;span class=&quot;token function&quot;&gt;alert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;xss&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;url 中包含的 js 代码得到执行。&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;内联脚本 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;javascript:&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt;（这段字符浏览器不区分大小写）&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;&quot;&amp;gt;跳转...
&lt;/code&gt;&lt;/pre&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;同样地，对于面面这段 JSP 代码，若 url 为
&lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;https://example.com/?to=javascript:alert(&apos;xss&apos;)&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt;，则渲染结果为：&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;javascript:alert(&apos;xss&apos;)&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;跳转...&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;() =&amp;gt; (
  跳转...`
    }} 
  /&amp;gt;
)
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/div&gt;&lt;h2 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;XSS 攻击的预防&lt;/span&gt;&lt;/h2&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;XSS 攻击有如下特点：&lt;/span&gt;&lt;/p&gt;&lt;ol class=&quot;yozora-list&quot; start=&quot;1&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;攻击者构造并提交恶意代码&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;浏览器执行恶意代码&lt;/span&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;我们可以针对这些特点进行防御。&lt;/span&gt;&lt;/p&gt;&lt;h3 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;输入过滤&lt;/span&gt;&lt;/h3&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;由于网络服务中所有请求都可以绕过前端直接向服务端发起，因此仅在浏览器端中做输入过滤是不能起到防范xss的效果的。但是后端做输入过滤同样存在问题，原因是不同的客户端及其渲染框架对于要接受的数据的转义规则存在差别，而服务端要做过滤只能选择某一种转义规则，众口难调。贸然进行转义可能会丢失原意。&lt;/span&gt;&lt;/p&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;比如一个合法用户提交的内容为 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;a &amp;lt; b&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt;，后端在写入数据前，将其转义成 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;a &amp;lt; b&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt;，这在纯 HTML 中渲染是有效的，但如果它是作为一段 url 内容则这个转义会破坏了原意。此外，对于 React / Vue 等现代前端框架，在渲染内容时需要的是原始的字符串内容，转义后的内容无法正确展示。&lt;/span&gt;&lt;/p&gt;&lt;h3 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;纯前端渲染&lt;/span&gt;&lt;/h3&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;采用前后端分离的开发模式，前端使用 React / Vue 等现代前端框架进行开发，以 React
为例：&lt;/span&gt;&lt;/p&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;在不使用 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;dangerouslySetInnerHTML&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 属性渲染数据时，其内部逻辑天然地对 HTML
进行了严格的转义。&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;在代码规范中严禁内联 javascript 的事件注册方式，统一通过 React / jsx 提供的
Event Handlers 进行定义（在原始的 html / js 项目中，可以用 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;.addEventListener&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt;
方法进行事件注册） 函数来注册事件回调函数。&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h3 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;其它防御策略&lt;/span&gt;&lt;/h3&gt;&lt;div class=&quot;yozora-admonition yozora-admonition--note&quot;&gt;&lt;h5 class=&quot;yozora-admonition__heading&quot;&gt;&lt;span class=&quot;yozora-admonition__heading-icon&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; height=&quot;24px&quot; viewBox=&quot;0 0 24 24&quot; width=&quot;24px&quot;&gt;&lt;path d=&quot;M11 7h2v2h-2zm0 4h2v6h-2zm1-9C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z&quot; /&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;yozora-admonition__heading-title&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;节选自 &lt;/span&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://tech.meituan.com/2018/09/27/fe-security.html&quot; title=&quot;https://tech.meituan.com/2018/09/27/fe-security.html&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;前端安全系列（一）：如何防止XSS攻击？&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/h5&gt;&lt;div class=&quot;yozora-admonition__body&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;Content Security Policy
严格的 CSP 在 XSS 的防范中可以起到以下的作用：&lt;/span&gt;&lt;/p&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;禁止加载外域代码，防止复杂的攻击逻辑。&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;禁止外域提交，网站被攻击后，用户的数据不会泄露到外域。&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;禁止内联脚本执行（规则较严格，目前发现 GitHub 使用）。&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;禁止未授权的脚本执行（新特性，Google Map 移动版在使用）。&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;合理使用上报可以及时发现 XSS，利于尽快修复问题。&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/div&gt;&lt;h2 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;CSRF 攻击&lt;/span&gt;&lt;/h2&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;CSRF (Cross-site Request Forgery) 跨站请求伪造，是通过诱导受害者访问第三方网站，并在第三方网站中盗用用户在目标网站上的身份（Cookie）发送跨站请求进行攻击的。&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-admonition yozora-admonition--success&quot;&gt;&lt;h5 class=&quot;yozora-admonition__heading&quot;&gt;&lt;span class=&quot;yozora-admonition__heading-icon&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; height=&quot;24px&quot; viewBox=&quot;0 0 24 24&quot; width=&quot;24px&quot;&gt;&lt;path d=&quot;M9 21c0 .55.45 1 1 1h4c.55 0 1-.45 1-1v-1H9v1zm3-19C8.14 2 5 5.14 5 9c0 2.38 1.19 4.47 3 5.74V17c0 .55.45 1 1 1h6c.55 0 1-.45 1-1v-2.26c1.81-1.27 3-3.36 3-5.74 0-3.86-3.14-7-7-7zm2.85 11.1l-.85.6V16h-4v-2.3l-.85-.6C7.8 12.16 7 10.63 7 9c0-2.76 2.24-5 5-5s5 2.24 5 5c0 1.63-.8 3.16-2.15 4.1z&quot; /&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;yozora-admonition__heading-title&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;CSRF 攻击举例&lt;/span&gt;&lt;/span&gt;&lt;/h5&gt;&lt;div class=&quot;yozora-admonition__body&quot;&gt;&lt;ol class=&quot;yozora-list&quot; start=&quot;1&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;受害者登录 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;a.com&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;攻击者引诱受害者访问 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;danger.com&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt;（攻击者可以利用或控制的站点）&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;在 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;danger.com&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 中，向 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;a.com&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 发起一个请求（浏览器默认会携带 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;a.com&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 的 Cookie）&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;a.com&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 接收到请求，对请求进行验证（校验 Cookie），确认是受害者的凭证后执行请求&lt;/span&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;&lt;/div&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;发送跨站请求有多种方式：&lt;/span&gt;&lt;/p&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;图片地址：浏览器在渲染页面时会自动访问 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;img&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 元素中指定的 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;src&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 地址来加载图片，它本质上是一个 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;GET&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 请求，如将此地址设置成 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt;，则在浏览器尝试加载此“图片”时，一次恶意的跨站请求就完成了。&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;链接地址：需要诱导用户点击才会触发，如 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;提交表单：此攻击方式比较严格，通常需要黑客完全控制第三方站点，可以在其上执行自己的 javascript 脚本，如：&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;form&lt;/span&gt; 
  &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;hack-form&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://bank.com/withdraw&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;POST&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;hidden&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;amount&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;1000&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;hidden&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;to&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;hacker&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;form&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;span class=&quot;token language-javascript&quot;&gt;document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;hack-form&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;submit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;CSRF 攻击的防护&lt;/span&gt;&lt;/h2&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;CSRF 攻击有如下特点：&lt;/span&gt;&lt;/p&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;攻击通常发生在第三方网站。&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;攻击通过冒用受害者的登陆凭证发起恶意请求完成，整个阶段中不能获得用户的登录凭证。&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;跨站请求十分容易达成，图片、超链接、表单提交，在一些可以发图、链接的第三方平台中可以直接嵌入恶意请求（如在一些支持 Markdown 语法的评论框中内嵌一个恶意的请求地址作为 url 的图片）。&lt;/span&gt;&lt;/p&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;如果被攻击的目标站点中有上述容易被利用的功能，则攻击可以直接在本域下进行，此时攻击可以绕过同源策略的防御机制。&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;我们可以针对这些特点进行防御。&lt;/span&gt;&lt;/p&gt;&lt;h3 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;同源检测&lt;/span&gt;&lt;/h3&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;CSRF 攻击大多是利用第三方站点发起恶意请求，因此直接拒绝来自第三方站点的请求进行规避（此方法对于本站下发起的 CSRF 攻击无效）。&lt;/span&gt;&lt;/p&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;如何判断请求是否来自外域：&lt;/span&gt;&lt;/p&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;检查 HTTP Header 中的 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;Origin&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 字段&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-admonition yozora-admonition--warning&quot;&gt;&lt;h5 class=&quot;yozora-admonition__heading&quot;&gt;&lt;span class=&quot;yozora-admonition__heading-icon&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; height=&quot;24px&quot; viewBox=&quot;0 0 24 24&quot; width=&quot;24px&quot;&gt;&lt;path d=&quot;M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z&quot; /&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;yozora-admonition__heading-title&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;节选自 &lt;/span&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://tech.meituan.com/2018/10/11/fe-security-csrf.html&quot; title=&quot;https://tech.meituan.com/2018/10/11/fe-security-csrf.html&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;前端安全系列（二）：如何防止CSRF攻击？&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/h5&gt;&lt;div class=&quot;yozora-admonition__body&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;Origin 在以下两种情况下不存在：&lt;/span&gt;&lt;/p&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;IE11 同源策略：IE11 不会在 CORS 请求上添加 Origin 字段。参见
&lt;/span&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy#IE_Exceptions&quot; title=&quot;https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy#IE_Exceptions&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy#IE_Exceptions&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;302 重定向：在 302 重定向之后的请求中，请求头上不包含 Origin 字段。&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/div&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;检查 HTTP Header 中的 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;Referer&lt;/code&gt;&lt;/pre&gt;&lt;sup id=&quot;reference-footnote-2&quot; class=&quot;yozora-footnote-reference&quot;&gt;&lt;a href=&quot;#footnote-2&quot; title=&quot;2&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt;&lt;span class=&quot;yozora-text&quot;&gt; 字段&lt;/span&gt;&lt;/p&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;Referer 字段记录了请求的来源地址：&lt;/span&gt;&lt;/p&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;对于 ajax 请求以及图片、脚本等资源请求，Referer 为发起请求的页面地址&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;对于页面跳转，Referer 为页面历史记录中的上一个页面地址&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class=&quot;yozora-admonition yozora-admonition--info&quot;&gt;&lt;h5 class=&quot;yozora-admonition__heading&quot;&gt;&lt;span class=&quot;yozora-admonition__heading-icon&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; height=&quot;24px&quot; viewBox=&quot;0 0 24 24&quot; width=&quot;24px&quot;&gt;&lt;path d=&quot;M11 15h2v2h-2v-2zm0-8h2v6h-2V7zm.99-5C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z&quot; /&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;yozora-admonition__heading-title&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;节选自 &lt;/span&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Referrer-Policy&quot; title=&quot;https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Referrer-Policy&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;Referrer-Policy | MDN&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/h5&gt;&lt;div class=&quot;yozora-admonition__body&quot;&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;Referrer-Policy: no-referrer&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt;:
整个 Referer 首部会被移除。访问来源信息不随着请求一起发送。&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;Referrer-Policy: no-referrer-when-downgrade&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt;: （默认值）在没有指定任何策略的情况下用户代理的默认行为。在同等安全级别的情况下，引用页面的地址会被发送(HTTPS-&amp;gt;HTTPS)，但是在降级的情况下不会被发送 (HTTPS-&amp;gt;HTTP)。&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;Referrer-Policy: origin&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt;:
在任何情况下，仅发送文件的源作为引用地址。例如 https&lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;://example.com/page.html&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt;
会将 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;https://example.com/&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 作为引用地址。&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;Referrer-Policy: origin-when-cross-origin&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt;:
对于同源的请求，会发送完整的 url 作为引用地址，但是对于非同源请求仅发送文件的源。&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;Referrer-Policy: same-origin&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt;:
对于同源的请求会发送引用地址，但是对于非同源请求则不发送引用地址信息。&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;Referrer-Policy: strict-origin&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt;:
在同等安全级别的情况下，发送文件的源作为引用地址(HTTPS-&amp;gt;HTTPS)，但是在降级的情况下不会发送 (HTTPS-&amp;gt;HTTP)。&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;Referrer-Policy: strict-origin-when-cross-origin&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt;:
对于同源的请求，会发送完整的URL作为引用地址；在同等安全级别的情况下，发送文件的源作为引用地址(HTTPS-&amp;gt;HTTPS)；在降级的情况下不发送此首部 (HTTPS-&amp;gt;HTTP)。&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;Referrer-Policy: unsafe-url&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt;:
无论是同源请求还是非同源请求，都发送完整的 URL（移除参数信息之后）作为引用地址。&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-admonition yozora-admonition--warning&quot;&gt;&lt;h5 class=&quot;yozora-admonition__heading&quot;&gt;&lt;span class=&quot;yozora-admonition__heading-icon&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; height=&quot;24px&quot; viewBox=&quot;0 0 24 24&quot; width=&quot;24px&quot;&gt;&lt;path d=&quot;M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z&quot; /&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;yozora-admonition__heading-title&quot;&gt;CAUTION&lt;/span&gt;&lt;/h5&gt;&lt;div class=&quot;yozora-admonition__body&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;这项设置会将受 TLS 安全协议保护的资源的源和路径信息泄露给非安全的源服务器。进行此项设置的时候要慎重考虑。&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/div&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h3 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;CSRF Token&lt;/span&gt;&lt;/h3&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;前文中提到 CSRF 攻击是利用了浏览器在发送请求时默认携带对应站点的 Cookie 的机制完成的，如果服务器要求所有的请求都需要携带一个 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;token&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt;，且该 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;token&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 不存在服务器上，则校验此 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;token&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 就可以完全防御 CSRF 攻击了。&lt;/span&gt;&lt;/p&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;使用此方法需要在所有的请求中携带一个固定的 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;token&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt;，可以将其写入在 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;html&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 的某个 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;meta&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 元素中，然后在发起请求时通过 js 查询此 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;token&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt;；或者在服务端渲染页面时将此 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;token&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 注入到链接（站点链接，非用户填入的链接）和表单中。此外，还可以将
&lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;token&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 写入 HTTP Header 中，如使用 &lt;/span&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://github.com/axios/axios&quot; title=&quot;https://github.com/axios/axios&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;axios&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt; 请求库时，可以设置默认的 HTTP Headers。&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-admonition yozora-admonition--success&quot;&gt;&lt;h5 class=&quot;yozora-admonition__heading&quot;&gt;&lt;span class=&quot;yozora-admonition__heading-icon&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; height=&quot;24px&quot; viewBox=&quot;0 0 24 24&quot; width=&quot;24px&quot;&gt;&lt;path d=&quot;M9 21c0 .55.45 1 1 1h4c.55 0 1-.45 1-1v-1H9v1zm3-19C8.14 2 5 5.14 5 9c0 2.38 1.19 4.47 3 5.74V17c0 .55.45 1 1 1h6c.55 0 1-.45 1-1v-2.26c1.81-1.27 3-3.36 3-5.74 0-3.86-3.14-7-7-7zm2.85 11.1l-.85.6V16h-4v-2.3l-.85-.6C7.8 12.16 7 10.63 7 9c0-2.76 2.24-5 5-5s5 2.24 5 5c0 1.63-.8 3.16-2.15 4.1z&quot; /&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;yozora-admonition__heading-title&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html&quot; title=&quot;https://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;jwt&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/h5&gt;&lt;div class=&quot;yozora-admonition__body&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;由于此 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;token&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 是为了防止 CSRF 攻击存在的，仅用来校验请求是否从可信源中发出，而无需在服务器端废止它（仅设置一个过期时间让它自然失效即可），故可以使用 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;jwt&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 来实现，将用户名写进 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;jwt&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 中即可防止攻击者伪造 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;token&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt;。这样服务器端就无需存储额外的信息了，仅需通过算法进行校验就可以完成验证了。&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;h3 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;其它防御策略&lt;/span&gt;&lt;/h3&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;双重 Cookie: 在 Cookie 中写入一个随机数（作为 CSRF Token），之后所有的请求中手动在请求参数中携带此随机数，服务器端通过检查 Cookie 中的随机数和请求中的随机数是否一致进行检验。&lt;/span&gt;&lt;/p&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;此方法和 CSRF Token 差不多，换汤不换药&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;Samesite Cookie: 服务器在 http 请求头上写入 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;Set-Cookie: xxxxx; Samesite=Strict&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt;
表示此 Cookie 仅限同站下使用，从根源上杜绝了 CSRF 攻击。&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;Related&lt;/span&gt;&lt;/h2&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://tech.meituan.com/2018/09/27/fe-security.html&quot; title=&quot;https://tech.meituan.com/2018/09/27/fe-security.html&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;前端安全系列（一）：如何防止XSS攻击？&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://tech.meituan.com/2018/10/11/fe-security-csrf.html&quot; title=&quot;https://tech.meituan.com/2018/10/11/fe-security-csrf.html&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;前端安全系列（二）：如何防止CSRF攻击？&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html&quot; title=&quot;https://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;JSON Web Token 入门教程&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Referrer-Policy&quot; title=&quot;https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Referrer-Policy&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;Referrer-Policy | MDN&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div className=&quot;yozora-footnote-definition&quot;&gt;&lt;p className=&quot;yozora-footnote-definition__title yozora-paragraph&quot;&gt;&lt;a href=&quot;#reference-footnote-1&quot;&gt;&amp;uarr;&lt;/a&gt;&lt;span&gt;&amp;nbsp;[1]:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;div className=&quot;yozora-footnote-definition__content&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;为不和层叠样式表（Cascading Styles Sheets, CSS）的缩写混淆，故将跨站脚本攻击缩写写为 XSS。可参见
&lt;/span&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://zh.wikipedia.org/wiki/%E8%B7%A8%E7%B6%B2%E7%AB%99%E6%8C%87%E4%BB%A4%E7%A2%BC&quot; title=&quot;https://zh.wikipedia.org/wiki/%E8%B7%A8%E7%B6%B2%E7%AB%99%E6%8C%87%E4%BB%A4%E7%A2%BC&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;https://zh.wikipedia.org/wiki/%E8%B7%A8%E7%B6%B2%E7%AB%99%E6%8C%87%E4%BB%A4%E7%A2%BC&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div className=&quot;yozora-footnote-definition&quot;&gt;&lt;p className=&quot;yozora-footnote-definition__title yozora-paragraph&quot;&gt;&lt;a href=&quot;#reference-footnote-2&quot;&gt;&amp;uarr;&lt;/a&gt;&lt;span&gt;&amp;nbsp;[2]:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;div className=&quot;yozora-footnote-definition__content&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;有趣的是，&lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;Referer&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 实际上是 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;Referrer&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 的错误拼写，难以想象这样低级的错误在制定规范的过程发生并保留了下来，可参见
&lt;/span&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://zh.wikipedia.org/wiki/HTTP%E5%8F%83%E7%85%A7%E4%BD%8D%E5%9D%80&quot; title=&quot;https://zh.wikipedia.org/wiki/HTTP%E5%8F%83%E7%85%A7%E4%BD%8D%E5%9D%80&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;https://zh.wikipedia.org/wiki/HTTP%E5%8F%83%E7%85%A7%E4%BD%8D%E5%9D%80&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/main&gt;&lt;footer&gt;&lt;div class=&quot;yozora-footnote-definitions&quot;&gt;&lt;div class=&quot;yozora-footnote-definitions__title&quot;&gt;footnote-definitions&lt;/div&gt;&lt;ul class=&quot;yozora-footnote-definitions__main&quot;&gt;&lt;div className=&quot;yozora-footnote-definition&quot;&gt;&lt;p className=&quot;yozora-footnote-definition__title yozora-paragraph&quot;&gt;&lt;a href=&quot;#reference-footnote-1&quot;&gt;&amp;uarr;&lt;/a&gt;&lt;span&gt;&amp;nbsp;[1]:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;div className=&quot;yozora-footnote-definition__content&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;为不和层叠样式表（Cascading Styles Sheets, CSS）的缩写混淆，故将跨站脚本攻击缩写写为 XSS。可参见
&lt;/span&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://zh.wikipedia.org/wiki/%E8%B7%A8%E7%B6%B2%E7%AB%99%E6%8C%87%E4%BB%A4%E7%A2%BC&quot; title=&quot;https://zh.wikipedia.org/wiki/%E8%B7%A8%E7%B6%B2%E7%AB%99%E6%8C%87%E4%BB%A4%E7%A2%BC&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;https://zh.wikipedia.org/wiki/%E8%B7%A8%E7%B6%B2%E7%AB%99%E6%8C%87%E4%BB%A4%E7%A2%BC&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div className=&quot;yozora-footnote-definition&quot;&gt;&lt;p className=&quot;yozora-footnote-definition__title yozora-paragraph&quot;&gt;&lt;a href=&quot;#reference-footnote-2&quot;&gt;&amp;uarr;&lt;/a&gt;&lt;span&gt;&amp;nbsp;[2]:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;div className=&quot;yozora-footnote-definition__content&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;有趣的是，&lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;Referer&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 实际上是 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;Referrer&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 的错误拼写，难以想象这样低级的错误在制定规范的过程发生并保留了下来，可参见
&lt;/span&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://zh.wikipedia.org/wiki/HTTP%E5%8F%83%E7%85%A7%E4%BD%8D%E5%9D%80&quot; title=&quot;https://zh.wikipedia.org/wiki/HTTP%E5%8F%83%E7%85%A7%E4%BD%8D%E5%9D%80&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;https://zh.wikipedia.org/wiki/HTTP%E5%8F%83%E7%85%A7%E4%BD%8D%E5%9D%80&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/footer&gt;&lt;/section&gt;</content:encoded></item><item><title><![CDATA[自然对数底数e]]></title><description><![CDATA[<section class="yozora-markdown"><main><h2 class="yozora-heading"><span class="yozora-text">前言</span></h2><p class="yozora-paragraph"><span class="yozora-inline-math">e</span><span class="yozora-text"> 是一个很神奇的常数，长期以来我只知道它是一个很重要的对数底数。以它为底的对数被称为自然对数，它有一个很重要的性质：对数函数 </span><span class="yozora-inline-math">\log_a(x)</span><span class="yozora-text"> 的导数为
</span><span class="yozora-inline-math">\displaystyle \frac{1}{x\ln a}</span><span class="yozora-text">，幂函数 </span><span class="yozora-inline-math">y=a^x</span><span class="yozora-text"> 的导数为 </span><span class="yozora-inline-math">a^x\ln a</span><span class="yozora-text">。</span><sup id="reference-footnote-1" class="yozora-footnote-reference"><a href="#footnote-1" title="1">[1]</a></sup><span class="yozora-text">
也就是说所有对数函数和幂函数的导数都与 </span></p></main><footer><div class="yozora-footnote-definitions"><div class="yozora-footnote-definitions__title">footnote-definitions</div><ul class="yozora-footnote-definitions__main"></ul></div></footer></section>]]></description><link>https://me.guanghechen.com/post/math/calculus/自然对数底数e/</link><guid isPermaLink="false">https://me.guanghechen.com/post/math/calculus/自然对数底数e/</guid><content:encoded>&lt;section class=&quot;yozora-markdown&quot;&gt;&lt;main&gt;&lt;h2 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;前言&lt;/span&gt;&lt;/h2&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 是一个很神奇的常数，长期以来我只知道它是一个很重要的对数底数。以它为底的对数被称为自然对数，它有一个很重要的性质：对数函数 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\log_a(x)&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 的导数为
&lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\displaystyle \frac{1}{x\ln a}&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，幂函数 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;y=a^x&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 的导数为 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;a^x\ln a&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;。&lt;/span&gt;&lt;sup id=&quot;reference-footnote-1&quot; class=&quot;yozora-footnote-reference&quot;&gt;&lt;a href=&quot;#footnote-1&quot; title=&quot;1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;&lt;span class=&quot;yozora-text&quot;&gt;
也就是说所有对数函数和幂函数的导数都与 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 有关。&lt;/span&gt;&lt;/p&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;为了理解它为什么被称为自然对数，翻阅了一些网上的资料，发现不少都拿银行的复利来举例；此外 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 还与对数螺旋线有关。如果你只是对 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 为什么被称为自然对数感兴趣，推荐直接阅读下面两篇文章，本文更多的是记录一些和 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 相关的数学式子和证明：&lt;/span&gt;&lt;/p&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://zhuanlan.zhihu.com/p/48391829&quot; title=&quot;https://zhuanlan.zhihu.com/p/48391829&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;飞蛾真的是因为趋光，所以扑火？ | 知乎专栏&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://zhuanlan.zhihu.com/p/48391055&quot; title=&quot;https://zhuanlan.zhihu.com/p/48391055&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;自然底数e怎么就“自然”了？ | 知乎专栏&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 的由来&lt;/span&gt;&lt;/h2&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 有两种表示法：&lt;/span&gt;&lt;/p&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\displaystyle e = \lim_{x \rightarrow +\infty} \left(1 + \frac{1}{n} \right)^n&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;.&lt;/span&gt;&lt;/p&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;这种表示方式正是 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 的定义&lt;/span&gt;&lt;sup id=&quot;reference-footnote-2&quot; class=&quot;yozora-footnote-reference&quot;&gt;&lt;a href=&quot;#footnote-2&quot; title=&quot;2&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\displaystyle e = \sum_{i=0}^{+\infty} \frac{1}{i!} = 1 + \frac{1}{1!} + \frac{1}{2!} + \cdots&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;.&lt;/span&gt;&lt;/p&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;这是 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;e^x&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 的麦克劳林级数在 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;x=1&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 时的值。&lt;/span&gt;&lt;sup id=&quot;reference-footnote-3&quot; class=&quot;yozora-footnote-reference&quot;&gt;&lt;a href=&quot;#footnote-3&quot; title=&quot;3&quot;&gt;[3]&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;hr class=&quot;yozora-thematic-break&quot; /&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 的第一种表示方法与复利率模型有关。在介绍复利率之前，我们先看一下指数增长模型。&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-admonition yozora-admonition--note&quot;&gt;&lt;h5 class=&quot;yozora-admonition__heading&quot;&gt;&lt;span class=&quot;yozora-admonition__heading-icon&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; height=&quot;24px&quot; viewBox=&quot;0 0 24 24&quot; width=&quot;24px&quot;&gt;&lt;path d=&quot;M11 7h2v2h-2zm0 4h2v6h-2zm1-9C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z&quot; /&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;yozora-admonition__heading-title&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;指数增长模型&lt;/span&gt;&lt;/span&gt;&lt;/h5&gt;&lt;div class=&quot;yozora-admonition__body&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;假设某种细菌每天分裂一次，那么 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 天后，一个细菌将会繁殖总数为（假设这些天里没有细菌死亡）&lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;2^x&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 的菌落。这是一个经典的指数增长模型，无论初始时有多少细菌，在 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 天后的总数量是初始时的 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;2^x&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 倍，它的数学表达为：&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-math&quot;&gt;Q = 2^x = (1 + 1)^x = (1 + 100\%)^x
&lt;/div&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;上式中隐含的是增长率为 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;100\%&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 时，&lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 天后的总数量是初始时的 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;Q&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 倍。更宽泛地，记增长率为 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，则有：&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-math&quot;&gt;Q = (1 + r)^x
&lt;/div&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;表示在一个增长周期内的增长率为 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; （增加了 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 倍），则在增长了 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 个周期后，总数量将为初始时的 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;Q&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 倍。&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;复利率法是一种计算利息的方法，按照指的是某段时间后利息也能产生利息。它和上文提到的细菌分裂有些类似，只不过复利率可以是小数：&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-admonition yozora-admonition--note&quot;&gt;&lt;h5 class=&quot;yozora-admonition__heading&quot;&gt;&lt;span class=&quot;yozora-admonition__heading-icon&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; height=&quot;24px&quot; viewBox=&quot;0 0 24 24&quot; width=&quot;24px&quot;&gt;&lt;path d=&quot;M11 7h2v2h-2zm0 4h2v6h-2zm1-9C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z&quot; /&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;yozora-admonition__heading-title&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;复利率模型&lt;/span&gt;&lt;/span&gt;&lt;/h5&gt;&lt;div class=&quot;yozora-admonition__body&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;现在在一家年利率为 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;100\%&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 的银行存入了一元钱，银行每季度支付一次利息，这样一年可取四次利息，总计能获得一元的利息，手上的钱变成了两元。聪明的你在每次获得利息后转手又存入银行，则一年后手上的钱变为：&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-math&quot;&gt;Q = \left( 1 + \frac{100\%}{4} \right)^4 = 2.4414
&lt;/div&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;虽然年利率没有改变，但因为结算的周期变短了，使得最后拿到手的钱变多了。贪心的你开始思考如果银行交付的周期变成无限小，那拿到手的钱会不会变成无穷多呢？不妨记银行一年支付利息 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 次，则每次的利率为 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\displaystyle \frac{100\%}{x}&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，当 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 趋于无穷时，结合 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 的第一种表示法可知，一年后到手的钱为：&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-math&quot;&gt;Q = \lim_{x \rightarrow +\infty} \left( 1 + \frac{100\%}{x} \right)^x = e
&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;h2 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 是一个无理数&lt;/span&gt;&lt;/h2&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;可以用反证法来证明。若 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 是一个有理数，不妨记它为 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\displaystyle e = \frac{a}{b}&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，其中 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 和 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 都是正整数。我们可以取一个正整数 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，并在等式两次同乘以 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;b \cdot n!&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;：&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-math&quot;&gt;e \cdot b \cdot n!= \frac{a}{b} \cdot b \cdot n! = a \cdot n!
&lt;/div&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;显然，等式右侧是一个正整数。现在观察等式的左侧，根据前文 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 的第二个表式法&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-math&quot;&gt;e = \sum_{i=0}^{+\infty} \frac{1}{i!} = 1 + \frac{1}{1!} + \frac{1}{2!} + \cdots
&lt;/div&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;有：&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-math&quot;&gt;\begin{aligned}
  b \cdot n! \cdot e 
    &amp;amp;= b \cdot n! \cdot \left( \sum_{i=0}^{+\infty} \frac{1}{i!} \right) \\
    &amp;amp;= b \cdot n! \cdot \left(1 + \frac{1}{1!} + \cdots + \frac{1}{n!} \right) \\
    &amp;amp;\quad + b \cdot \left(\frac{1}{n+1} + \frac{1}{(n+1) \cdot (n+2)} + \cdots \right) \\
\end{aligned} \label{eq-2} \tag{2}
&lt;/div&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;等式右侧的第一项显然是个整数，现在继续观察第二项，令&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-math&quot;&gt;\begin{aligned}
  0 &amp;lt; \epsilon &amp;amp;= b \cdot \left( \frac{1}{n+1} + \frac{1}{(n+1) \cdot (n+2)} + \frac{1}{(n+1) \cdot (n+2) \cdot (n+3)} + \cdots \right) \\
        &amp;amp;&amp;lt; b \cdot \left( \frac{1}{n+1} + \frac{1}{(n+1)^2} + \frac{1}{(n+1)^3} + \cdots \right) \\
        &amp;amp;= b \cdot \left( \frac{1}{n+1} \cdot \frac{1 - \left(\frac{1}{n+1}\right)^{+\infty}}{1 - \frac{1}{n+1}} \right) \\
        &amp;amp;= b \cdot \left( \frac{1}{n+1} \cdot \frac{1}{1 - \frac{1}{n+1}} \right)
         = b \cdot \frac{1}{n} \\
\end{aligned}
&lt;/div&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;则当 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 取一个大于 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 的整数时，有： &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;0 &amp;lt; \epsilon &amp;lt; 1&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，即第二项不为整数，即式 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\eqref{eq-2}&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 不成立，故原假设矛盾，&lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 不可能为有理数，所以 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 是一个无理数。&lt;/span&gt;&lt;/p&gt;&lt;h2 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;欧拉方程&lt;/span&gt;&lt;/h2&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;谈到自然对数 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，就不得不提起大名鼎鼎的欧拉方程&lt;/span&gt;&lt;sup id=&quot;reference-footnote-4&quot; class=&quot;yozora-footnote-reference&quot;&gt;&lt;a href=&quot;#footnote-4&quot; title=&quot;4&quot;&gt;[4]&lt;/a&gt;&lt;/sup&gt;&lt;span class=&quot;yozora-text&quot;&gt;了：&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-math&quot;&gt;e^{ix} = \cos x + \sin x
&lt;/div&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;特别地，当取 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;x=\pi&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 时，有：&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-math&quot;&gt;e^{i\pi} + 1= 0
&lt;/div&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;初次见到它还是在大一时的一门选修课上，由于它实在是太优美了，以至于自初见起便一直念念不忘。它包含了数学上最奇妙的几个常数：&lt;/span&gt;&lt;/p&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;: 自然对数的底数&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;: 复数的单位，凭借一己之力表达了整个复数域（以实数作为系数）&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\pi&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;: 圆周率（&lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\pi&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 是我学到的第一个无理数）&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;: 零是数学上极为重要的数字，它拥有许多性质：&lt;/span&gt;&lt;/p&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 是唯一可以作为无穷小量的常数&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 是唯一既非正也非负的实数，&lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 的相反数和绝对值都是其本身&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 是唯一找不到复数 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 使得 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;e^w = z&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 的复数 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;z&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 在概率上代表不可能事件&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 是多个重要数列的项，如&lt;/span&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://zh.wikipedia.org/wiki/%E4%BD%A9%E5%B0%94%E6%95%B0&quot; title=&quot;https://zh.wikipedia.org/wiki/%E4%BD%A9%E5%B0%94%E6%95%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;佩尔数&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;、 &lt;/span&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://zh.wikipedia.org/wiki/%E6%96%90%E6%B3%A2%E9%82%A3%E5%A5%91%E6%95%B0%E5%88%97&quot; title=&quot;https://zh.wikipedia.org/wiki/%E6%96%90%E6%B3%A2%E9%82%A3%E5%A5%91%E6%95%B0%E5%88%97&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;斐波那契数&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;、&lt;/span&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://zh.wikipedia.org/wiki/%E9%AB%98%E6%96%AF%E6%95%B4%E6%95%B8&quot; title=&quot;https://zh.wikipedia.org/wiki/%E9%AB%98%E6%96%AF%E6%95%B4%E6%95%B8&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;高斯整数&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;等&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 是加减法运算的零元：任何数字和零做加减法运算都得到它本身&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 乘任何数都得到 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;0&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 是任何数的倍数，&lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 除任何数都得到 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;0&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 不能作为除数，也不能作为对数的底，它没有倒数&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;...&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;: 一也是一个重要的数字，它同样拥有很多性质：&lt;/span&gt;&lt;/p&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 是第一个&lt;/span&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://zh.wikipedia.org/wiki/%E5%B9%B8%E8%BF%90%E6%95%B0&quot; title=&quot;https://zh.wikipedia.org/wiki/%E5%B9%B8%E8%BF%90%E6%95%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;幸运数&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 是第一个&lt;/span&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://zh.wikipedia.org/wiki/%E5%BF%AB%E6%A8%82%E6%95%B8&quot; title=&quot;https://zh.wikipedia.org/wiki/%E5%BF%AB%E6%A8%82%E6%95%B8&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;快乐数&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 在概率上代表必然事件&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 是乘除法运算的零元：任何数字和一做乘除法运算都得到它本身&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 是唯一不能作为对数的底的正数&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;...&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;几个重要的极限和无穷量&lt;/span&gt;&lt;/h2&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\displaystyle \lim_{x \rightarrow 0} \left( 1+\frac{1}{x} \right)^x = e&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\displaystyle \lim_{x \rightarrow 0} \ln(1+x) = \lim_{x \rightarrow 0} x&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; （墨卡托级数）&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\displaystyle \lim_{x \rightarrow 0} \left( a^x - 1 \right) = \lim_{x \rightarrow 0} x\ln a&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-admonition yozora-admonition--note&quot;&gt;&lt;h5 class=&quot;yozora-admonition__heading&quot;&gt;&lt;span class=&quot;yozora-admonition__heading-icon&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; height=&quot;24px&quot; viewBox=&quot;0 0 24 24&quot; width=&quot;24px&quot;&gt;&lt;path d=&quot;M11 7h2v2h-2zm0 4h2v6h-2zm1-9C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z&quot; /&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;yozora-admonition__heading-title&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;节选自湖南大学《大学数学1》第三版 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\;P_{70}&lt;/span&gt;&lt;/span&gt;&lt;/h5&gt;&lt;div class=&quot;yozora-admonition__body&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;令 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;y=a^x - 1&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，则 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;x \rightarrow 0&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 时，&lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;y \rightarrow 0&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，且
&lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\displaystyle x = \log_a (1+y) = \frac{\ln (1 + y)}{\ln a}&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，故：&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-math&quot;&gt;\lim_{x \rightarrow 0} \frac{a^x - 1}{x\ln a}
  = \lim_{y \rightarrow 0} \frac{y}{\ln (1 + y)}
  = \lim_{y \rightarrow 0} \frac{1}{\ln (1 + y)^{\frac{1}{y}}}
  = \frac{1}{\ln e}
  = 1
&lt;/div&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;即&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-math&quot;&gt;\lim_{x \rightarrow 0} \left( a^x - 1 \right) = \lim_{x \rightarrow 0} x\ln a
&lt;/div&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;【证毕】&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;Related&lt;/span&gt;&lt;/h2&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://zhuanlan.zhihu.com/p/48391829&quot; title=&quot;https://zhuanlan.zhihu.com/p/48391829&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;飞蛾真的是因为趋光，所以扑火？ | 知乎专栏&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://zhuanlan.zhihu.com/p/48391055&quot; title=&quot;https://zhuanlan.zhihu.com/p/48391055&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;自然底数e怎么就“自然”了？ | 知乎专栏&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://zh.wikipedia.org/wiki/%E8%87%AA%E7%84%B6%E5%B0%8D%E6%95%B8&quot; title=&quot;https://zh.wikipedia.org/wiki/%E8%87%AA%E7%84%B6%E5%B0%8D%E6%95%B8&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;自然对数 | 维基百科&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://zh.wikipedia.org/wiki/%E6%B3%B0%E5%8B%92%E7%BA%A7%E6%95%B0&quot; title=&quot;https://zh.wikipedia.org/wiki/%E6%B3%B0%E5%8B%92%E7%BA%A7%E6%95%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;泰勒级数 | 维基百科&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://zh.wikipedia.org/wiki/0&quot; title=&quot;https://zh.wikipedia.org/wiki/0&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; | 维基百科&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://zh.wikipedia.org/wiki/1&quot; title=&quot;https://zh.wikipedia.org/wiki/1&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; | 维基百科&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://zh.wikipedia.org/wiki/%E4%BD%A9%E5%B0%94%E6%95%B0&quot; title=&quot;https://zh.wikipedia.org/wiki/%E4%BD%A9%E5%B0%94%E6%95%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;佩尔数 | 维基百科&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://zh.wikipedia.org/wiki/%E6%96%90%E6%B3%A2%E9%82%A3%E5%A5%91%E6%95%B0%E5%88%97&quot; title=&quot;https://zh.wikipedia.org/wiki/%E6%96%90%E6%B3%A2%E9%82%A3%E5%A5%91%E6%95%B0%E5%88%97&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;斐波那契数 | 维基百科&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://zh.wikipedia.org/wiki/%E9%AB%98%E6%96%AF%E6%95%B4%E6%95%B8&quot; title=&quot;https://zh.wikipedia.org/wiki/%E9%AB%98%E6%96%AF%E6%95%B4%E6%95%B8&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;高斯整数 | 维基百科&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://zh.wikipedia.org/wiki/%E5%B9%B8%E8%BF%90%E6%95%B0&quot; title=&quot;https://zh.wikipedia.org/wiki/%E5%B9%B8%E8%BF%90%E6%95%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;幸运数 | 维基百科&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://zh.wikipedia.org/wiki/%E5%BF%AB%E6%A8%82%E6%95%B8&quot; title=&quot;https://zh.wikipedia.org/wiki/%E5%BF%AB%E6%A8%82%E6%95%B8&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;快乐数 | 维基百科&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://zh.wikipedia.org/wiki/%E6%AC%A7%E6%8B%89%E5%85%AC%E5%BC%8F&quot; title=&quot;https://zh.wikipedia.org/wiki/%E6%AC%A7%E6%8B%89%E5%85%AC%E5%BC%8F&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;欧拉公式 | 维基百科&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;湖南大学《大学数学1》第三版&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div className=&quot;yozora-footnote-definition&quot;&gt;&lt;p className=&quot;yozora-footnote-definition__title yozora-paragraph&quot;&gt;&lt;a href=&quot;#reference-footnote-1&quot;&gt;&amp;uarr;&lt;/a&gt;&lt;span&gt;&amp;nbsp;[1]:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;div className=&quot;yozora-footnote-definition__content&quot;&gt;&lt;div class=&quot;yozora-admonition yozora-admonition--note&quot;&gt;&lt;h5 class=&quot;yozora-admonition__heading&quot;&gt;&lt;span class=&quot;yozora-admonition__heading-icon&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; height=&quot;24px&quot; viewBox=&quot;0 0 24 24&quot; width=&quot;24px&quot;&gt;&lt;path d=&quot;M11 7h2v2h-2zm0 4h2v6h-2zm1-9C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z&quot; /&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;yozora-admonition__heading-title&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;节选自湖南大学《大学数学1》第三版 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\;P_{96}&lt;/span&gt;&lt;/span&gt;&lt;/h5&gt;&lt;div class=&quot;yozora-admonition__body&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;对于函数 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;y = \log_a x \, (a &amp;gt; 0, a \neq 1)&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，增量
&lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\Delta y=\log_a(x + \Delta x) - \log_a x&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，则有：&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-math&quot;&gt;\frac{\Delta y}{\Delta x}
= \frac{1}{\Delta x} \log_a \left( 1 + \frac{\Delta x}{x} \right)
= \frac{1}{x} \log_a \left( 1 + \frac{\Delta x}{x} \right)^{\frac{x}{\Delta x}}.
&lt;/div&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;取极限，有&lt;/span&gt;&lt;sup id=&quot;reference-footnote-5&quot; class=&quot;yozora-footnote-reference&quot;&gt;&lt;a href=&quot;#footnote-5&quot; title=&quot;5&quot;&gt;[5]&lt;/a&gt;&lt;/sup&gt;&lt;span class=&quot;yozora-text&quot;&gt;：&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-math&quot;&gt;\begin{aligned}
  \lim_{\Delta x \rightarrow 0} \frac{\Delta y}{\Delta x}
  &amp;amp;= \frac{1}{x} \log_a \left[ \lim_{\Delta x \rightarrow 0} \left( 1 + \frac{\Delta x}{x} \right)^{\frac{x}{\Delta x}} \right] \\
  &amp;amp;= \frac{1}{x} \log_a e = \frac{1}{x \ln a}.
\end{aligned}
&lt;/div&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;即：&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-math&quot;&gt;(\log_a x)&apos; = \frac{1}{x\ln a}
&lt;/div&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;【证毕】&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div className=&quot;yozora-footnote-definition&quot;&gt;&lt;p className=&quot;yozora-footnote-definition__title yozora-paragraph&quot;&gt;&lt;a href=&quot;#reference-footnote-2&quot;&gt;&amp;uarr;&lt;/a&gt;&lt;span&gt;&amp;nbsp;[2]:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;div className=&quot;yozora-footnote-definition__content&quot;&gt;&lt;div class=&quot;yozora-admonition yozora-admonition--note&quot;&gt;&lt;h5 class=&quot;yozora-admonition__heading&quot;&gt;&lt;span class=&quot;yozora-admonition__heading-icon&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; height=&quot;24px&quot; viewBox=&quot;0 0 24 24&quot; width=&quot;24px&quot;&gt;&lt;path d=&quot;M11 7h2v2h-2zm0 4h2v6h-2zm1-9C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z&quot; /&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;yozora-admonition__heading-title&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;节选自湖南大学《大学数学1》第三版 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\;P_{38}&lt;/span&gt;&lt;/span&gt;&lt;/h5&gt;&lt;div class=&quot;yozora-admonition__body&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;当 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;a &amp;gt; b &amp;gt; 0&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 时，有&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-math&quot;&gt;\begin{aligned}
 a^{n+1} - b^{n+1} &amp;amp;= (a - b) \cdot \left(\sum_{k=0}^{n} a^k \cdot b^{n-k} \right)\\
                   &amp;amp;&amp;lt; (n + 1) \cdot (a - b) \cdot a^n\\
\end{aligned}
&lt;/div&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;即&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-math&quot;&gt;a^n [(n+1)b - na] &amp;lt; b^{n+1} \label{eq-1}\tag{1}
&lt;/div&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;取 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\displaystyle a=1 + \frac{1}{2n}&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，&lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;b=1&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，代入 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\eqref{eq-1}&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 式，得&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-math&quot;&gt;\left( 1 + \frac{1}{2n} \right)^n &amp;lt; 2
&lt;/div&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;从而&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-math&quot;&gt;\left( 1 + \frac{1}{2n} \right)^{2n} &amp;lt; 4, \qquad n=1,2,\cdots
&lt;/div&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;再取 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\displaystyle a = 1 + \frac{1}{n}&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，&lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\displaystyle b = 1 + \frac{1}{n+1}&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，代入 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\eqref{eq-1}&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 式，得&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-math&quot;&gt;\left( 1 + \frac{1}{n} \right)^n &amp;lt; \left( 1 + \frac{1}{n+1} \right)^{n+1}
&lt;/div&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;从而数列 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\displaystyle \left\{ \left( 1 + \frac{1}{n} \right)^n \right\}&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 是严格单调递增的，故有&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-math&quot;&gt;\left( 1 + \frac{1}{n} \right)^n &amp;lt; \left( 1 + \frac{1}{2n} \right)^{2n} &amp;lt; 4, \qquad n=1,2,\cdots
&lt;/div&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;即 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\displaystyle \left\{ \left( 1 + \frac{1}{n} \right)^n \right\}&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 有上界，又由于它是单调递增的，故它是一个收敛的数列。通常我们将这个数列的极限值记为 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;.&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div className=&quot;yozora-footnote-definition&quot;&gt;&lt;p className=&quot;yozora-footnote-definition__title yozora-paragraph&quot;&gt;&lt;a href=&quot;#reference-footnote-3&quot;&gt;&amp;uarr;&lt;/a&gt;&lt;span&gt;&amp;nbsp;[3]:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;div className=&quot;yozora-footnote-definition__content&quot;&gt;&lt;div class=&quot;yozora-admonition yozora-admonition--note&quot;&gt;&lt;h5 class=&quot;yozora-admonition__heading&quot;&gt;&lt;span class=&quot;yozora-admonition__heading-icon&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; height=&quot;24px&quot; viewBox=&quot;0 0 24 24&quot; width=&quot;24px&quot;&gt;&lt;path d=&quot;M11 7h2v2h-2zm0 4h2v6h-2zm1-9C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z&quot; /&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;yozora-admonition__heading-title&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;节选自湖南大学《大学数学1》第三版 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\;P_{134}&lt;/span&gt;&lt;/span&gt;&lt;/h5&gt;&lt;div class=&quot;yozora-admonition__body&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;麦克劳林（Maclaurin）公式如下（其中，&lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\xi&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 在 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 和 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 之间）：&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-math&quot;&gt;  f(x) = f(0) + f&apos;(0)x + \frac{f&apos;&apos;(0)}{2!} + \cdots + 
         \frac{f^{(n)}(0)}{n!}x + \frac{f^{(n+1)}(\xi)}{(n+1)!} x^{n+1}
&lt;/div&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;求 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;e^x&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 的麦克劳林级数时应用到了 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 的一个重要性质： &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;(e^x)&apos; = e^x&lt;/span&gt;&lt;/p&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;由于 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;f&apos;(x) = (e^x)&apos; = e^x&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\cdots&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;f^{(n)}(x)=e^x&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;f^{(n + 1)}(x)=e^x&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;， &lt;/span&gt;&lt;br class=&quot;yozora-break&quot; /&gt;&lt;span class=&quot;yozora-text&quot;&gt;
得 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;f(0)=1&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;f&apos;(0)=1&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\cdots&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;f^{(n)}(0)=1&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;f^{(n+1)}(\xi)=e^x&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;. 故有：&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-math&quot;&gt;e^x = 1 + x + \frac{x^2}{2!} + \cdots + \frac{x^n}{n!} + \frac{e^\xi}{(n+1)!} x^{n+1}
&lt;/div&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;当 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;x \rightarrow 0&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 时，有：&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-math&quot;&gt;\lim_{x \rightarrow 0} e^x = 1 + x + \frac{x^2}{2!} + \cdots + \frac{x^2}{n!}
&lt;/div&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;【证毕】&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div className=&quot;yozora-footnote-definition&quot;&gt;&lt;p className=&quot;yozora-footnote-definition__title yozora-paragraph&quot;&gt;&lt;a href=&quot;#reference-footnote-4&quot;&gt;&amp;uarr;&lt;/a&gt;&lt;span&gt;&amp;nbsp;[4]:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;div className=&quot;yozora-footnote-definition__content&quot;&gt;&lt;div class=&quot;yozora-admonition yozora-admonition--note&quot;&gt;&lt;h5 class=&quot;yozora-admonition__heading&quot;&gt;&lt;span class=&quot;yozora-admonition__heading-icon&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; height=&quot;24px&quot; viewBox=&quot;0 0 24 24&quot; width=&quot;24px&quot;&gt;&lt;path d=&quot;M11 7h2v2h-2zm0 4h2v6h-2zm1-9C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z&quot; /&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;yozora-admonition__heading-title&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;节选自 &lt;/span&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://zh.wikipedia.org/wiki/%E6%AC%A7%E6%8B%89%E5%85%AC%E5%BC%8F&quot; title=&quot;https://zh.wikipedia.org/wiki/%E6%AC%A7%E6%8B%89%E5%85%AC%E5%BC%8F&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;欧拉公式 | 维基百科&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/h5&gt;&lt;div class=&quot;yozora-admonition__body&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;首先，在复数域上对 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;e^x&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 进行定义：&lt;/span&gt;&lt;/p&gt;&lt;blockquote class=&quot;yozora-blockquote&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;对于 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;a,b \in \mathbb{R}, c=a+ib \in \mathbb{C}&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，规定
&lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\displaystyle e^{\mathbb C}=\lim_{n \rightarrow \infty} \left(1 + \frac{c}{n} \right)^n&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;.&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;对&lt;/span&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://zh.wikipedia.org/wiki/%E5%A4%8D%E6%95%B0_(%E6%95%B0%E5%AD%A6)#%E6%9E%81%E5%9D%90%E6%A0%87%E5%BD%A2%E5%BC%8F&quot; title=&quot;https://zh.wikipedia.org/wiki/%E5%A4%8D%E6%95%B0_(%E6%95%B0%E5%AD%A6)#%E6%9E%81%E5%9D%90%E6%A0%87%E5%BD%A2%E5%BC%8F&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;复数的极坐标表示&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt; &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;w= \mu + i\nu = r(\cos\theta + i\sin\theta)&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，从而有：&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-math&quot;&gt;r = \sqrt{\mu^2 + \nu^2} \in \mathbb{R}, \; \theta=\arctan \left( \frac{\nu}{\mu} \right) \in \mathbb{R}
&lt;/div&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;根据&lt;/span&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://zh.wikipedia.org/wiki/%E6%A3%A3%E8%8E%AB%E5%BC%97%E5%85%AC%E5%BC%8F&quot; title=&quot;https://zh.wikipedia.org/wiki/%E6%A3%A3%E8%8E%AB%E5%BC%97%E5%85%AC%E5%BC%8F&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;棣莫弗公式&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt; &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;w^n=(\mu + i\nu)^n = r^n(\cos{n\theta} + i\sin{n\theta})&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，有：&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-math&quot;&gt;\left( 1 + \frac{a + bi}{n} \right)^n 
= \left[ \left( 1 + \frac{a}{n} \right) + i \cdot \frac{b}{n} \right]^n 
= r_n(\cos\theta_n + i\sin\theta_n)
&lt;/div&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;假设 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;n &amp;gt; \lvert a \rvert&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，则：&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-math&quot;&gt;r_n = \left[ \left( 1 + \frac{a}{n} \right)^2 + \left( \frac{b}{n} \right)^2 \right]^{\frac{n}{2}}, \quad 
\theta_n=n \arctan \frac{\frac{b}{n}}{1 + \frac{a}{n}}
&lt;/div&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;从而有&lt;/span&gt;&lt;sup id=&quot;reference-footnote-6&quot; class=&quot;yozora-footnote-reference&quot;&gt;&lt;a href=&quot;#footnote-6&quot; title=&quot;6&quot;&gt;[6]&lt;/a&gt;&lt;/sup&gt;&lt;span class=&quot;yozora-text&quot;&gt;：&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-math&quot;&gt;\begin{aligned}
  \lim_{n \rightarrow \infty} \ln r_n 
    &amp;amp;= \lim_{n \rightarrow \infty} \left[ \frac{n}{2} \cdot \ln \left( 1 + \frac{2a}{n} + \frac{a^2 + b^2}{n^2} \right) \right] \\
    &amp;amp;= \lim_{n \rightarrow \infty} \left[ \frac{n}{2} \cdot \left( \frac{2a}{n} + \frac{a^2 + b^2}{n^2} \right) \right] \\
    &amp;amp;= a \\
\end{aligned}
&lt;/div&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;又因为：&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-math&quot;&gt;\begin{aligned}
  \lim_{n \rightarrow \infty} \ln \theta_n
    &amp;amp;= \lim_{n \rightarrow \infty} \left( n \cdot \arctan \frac{\frac{b}{n}}{1 + \frac{a}{n}} \right) \\
    &amp;amp;= \lim_{n \rightarrow \infty} \left( n \cdot \frac{\frac{b}{n}}{1 + \frac{a}{n}} \right) \\
    &amp;amp;= b \\
\end{aligned}
&lt;/div&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;从而有：&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-math&quot;&gt;\begin{aligned}
  \lim_{n \rightarrow \infty} \left( 1 + \frac{a+bi}{n} \right)^n
    &amp;amp;= \lim_{n \rightarrow \infty} r_n(\cos\theta_n + i\sin\theta_n) \\
    &amp;amp;= \lim_{n \rightarrow \infty} r_n \cdot \lim_{n \rightarrow \infty} (\cos\theta_n + i\sin\theta_n) \\
    &amp;amp;= e^{\lim_{n \rightarrow \infty} \ln r_n} \cdot \lim_{n \rightarrow \infty} (\cos\theta_n + i\sin\theta_n) \\
    &amp;amp;= e^{\lim_{n \rightarrow \infty} \ln r_n} \cdot \left( \lim_{n \rightarrow \infty} \cos\theta_n + \lim_{n \rightarrow \infty} i\sin\theta_n \right) \\
    &amp;amp;= e^a \cdot (\cos b + i\sin b)
\end{aligned}
&lt;/div&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;即 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;e^{a+ib} = e^a(\cos b + i\sin b)&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，令 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;a=0&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，则有&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-math&quot;&gt;e^{ix} = \cos x + i\sin x
&lt;/div&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;【证毕】&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div className=&quot;yozora-footnote-definition&quot;&gt;&lt;p className=&quot;yozora-footnote-definition__title yozora-paragraph&quot;&gt;&lt;a href=&quot;#reference-footnote-5&quot;&gt;&amp;uarr;&lt;/a&gt;&lt;span&gt;&amp;nbsp;[5]:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;div className=&quot;yozora-footnote-definition__content&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;用到了无穷小量 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\displaystyle \lim_{x \rightarrow 0} \left( 1+\frac{1}{x} \right)^x = e&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div className=&quot;yozora-footnote-definition&quot;&gt;&lt;p className=&quot;yozora-footnote-definition__title yozora-paragraph&quot;&gt;&lt;a href=&quot;#reference-footnote-6&quot;&gt;&amp;uarr;&lt;/a&gt;&lt;span&gt;&amp;nbsp;[6]:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;div className=&quot;yozora-footnote-definition__content&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;用到了墨卡托级数： &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\ln (1+x) \sim x&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/main&gt;&lt;footer&gt;&lt;div class=&quot;yozora-footnote-definitions&quot;&gt;&lt;div class=&quot;yozora-footnote-definitions__title&quot;&gt;footnote-definitions&lt;/div&gt;&lt;ul class=&quot;yozora-footnote-definitions__main&quot;&gt;&lt;div className=&quot;yozora-footnote-definition&quot;&gt;&lt;p className=&quot;yozora-footnote-definition__title yozora-paragraph&quot;&gt;&lt;a href=&quot;#reference-footnote-1&quot;&gt;&amp;uarr;&lt;/a&gt;&lt;span&gt;&amp;nbsp;[1]:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;div className=&quot;yozora-footnote-definition__content&quot;&gt;&lt;div class=&quot;yozora-admonition yozora-admonition--note&quot;&gt;&lt;h5 class=&quot;yozora-admonition__heading&quot;&gt;&lt;span class=&quot;yozora-admonition__heading-icon&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; height=&quot;24px&quot; viewBox=&quot;0 0 24 24&quot; width=&quot;24px&quot;&gt;&lt;path d=&quot;M11 7h2v2h-2zm0 4h2v6h-2zm1-9C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z&quot; /&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;yozora-admonition__heading-title&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;节选自湖南大学《大学数学1》第三版 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\;P_{96}&lt;/span&gt;&lt;/span&gt;&lt;/h5&gt;&lt;div class=&quot;yozora-admonition__body&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;对于函数 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;y = \log_a x \, (a &amp;gt; 0, a \neq 1)&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，增量
&lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\Delta y=\log_a(x + \Delta x) - \log_a x&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，则有：&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-math&quot;&gt;\frac{\Delta y}{\Delta x}
= \frac{1}{\Delta x} \log_a \left( 1 + \frac{\Delta x}{x} \right)
= \frac{1}{x} \log_a \left( 1 + \frac{\Delta x}{x} \right)^{\frac{x}{\Delta x}}.
&lt;/div&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;取极限，有&lt;/span&gt;&lt;sup id=&quot;reference-footnote-5&quot; class=&quot;yozora-footnote-reference&quot;&gt;&lt;a href=&quot;#footnote-5&quot; title=&quot;5&quot;&gt;[5]&lt;/a&gt;&lt;/sup&gt;&lt;span class=&quot;yozora-text&quot;&gt;：&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-math&quot;&gt;\begin{aligned}
  \lim_{\Delta x \rightarrow 0} \frac{\Delta y}{\Delta x}
  &amp;amp;= \frac{1}{x} \log_a \left[ \lim_{\Delta x \rightarrow 0} \left( 1 + \frac{\Delta x}{x} \right)^{\frac{x}{\Delta x}} \right] \\
  &amp;amp;= \frac{1}{x} \log_a e = \frac{1}{x \ln a}.
\end{aligned}
&lt;/div&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;即：&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-math&quot;&gt;(\log_a x)&apos; = \frac{1}{x\ln a}
&lt;/div&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;【证毕】&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div className=&quot;yozora-footnote-definition&quot;&gt;&lt;p className=&quot;yozora-footnote-definition__title yozora-paragraph&quot;&gt;&lt;a href=&quot;#reference-footnote-2&quot;&gt;&amp;uarr;&lt;/a&gt;&lt;span&gt;&amp;nbsp;[2]:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;div className=&quot;yozora-footnote-definition__content&quot;&gt;&lt;div class=&quot;yozora-admonition yozora-admonition--note&quot;&gt;&lt;h5 class=&quot;yozora-admonition__heading&quot;&gt;&lt;span class=&quot;yozora-admonition__heading-icon&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; height=&quot;24px&quot; viewBox=&quot;0 0 24 24&quot; width=&quot;24px&quot;&gt;&lt;path d=&quot;M11 7h2v2h-2zm0 4h2v6h-2zm1-9C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z&quot; /&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;yozora-admonition__heading-title&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;节选自湖南大学《大学数学1》第三版 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\;P_{38}&lt;/span&gt;&lt;/span&gt;&lt;/h5&gt;&lt;div class=&quot;yozora-admonition__body&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;当 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;a &amp;gt; b &amp;gt; 0&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 时，有&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-math&quot;&gt;\begin{aligned}
 a^{n+1} - b^{n+1} &amp;amp;= (a - b) \cdot \left(\sum_{k=0}^{n} a^k \cdot b^{n-k} \right)\\
                   &amp;amp;&amp;lt; (n + 1) \cdot (a - b) \cdot a^n\\
\end{aligned}
&lt;/div&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;即&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-math&quot;&gt;a^n [(n+1)b - na] &amp;lt; b^{n+1} \label{eq-1}\tag{1}
&lt;/div&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;取 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\displaystyle a=1 + \frac{1}{2n}&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，&lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;b=1&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，代入 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\eqref{eq-1}&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 式，得&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-math&quot;&gt;\left( 1 + \frac{1}{2n} \right)^n &amp;lt; 2
&lt;/div&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;从而&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-math&quot;&gt;\left( 1 + \frac{1}{2n} \right)^{2n} &amp;lt; 4, \qquad n=1,2,\cdots
&lt;/div&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;再取 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\displaystyle a = 1 + \frac{1}{n}&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，&lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\displaystyle b = 1 + \frac{1}{n+1}&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，代入 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\eqref{eq-1}&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 式，得&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-math&quot;&gt;\left( 1 + \frac{1}{n} \right)^n &amp;lt; \left( 1 + \frac{1}{n+1} \right)^{n+1}
&lt;/div&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;从而数列 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\displaystyle \left\{ \left( 1 + \frac{1}{n} \right)^n \right\}&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 是严格单调递增的，故有&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-math&quot;&gt;\left( 1 + \frac{1}{n} \right)^n &amp;lt; \left( 1 + \frac{1}{2n} \right)^{2n} &amp;lt; 4, \qquad n=1,2,\cdots
&lt;/div&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;即 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\displaystyle \left\{ \left( 1 + \frac{1}{n} \right)^n \right\}&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 有上界，又由于它是单调递增的，故它是一个收敛的数列。通常我们将这个数列的极限值记为 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;.&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div className=&quot;yozora-footnote-definition&quot;&gt;&lt;p className=&quot;yozora-footnote-definition__title yozora-paragraph&quot;&gt;&lt;a href=&quot;#reference-footnote-3&quot;&gt;&amp;uarr;&lt;/a&gt;&lt;span&gt;&amp;nbsp;[3]:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;div className=&quot;yozora-footnote-definition__content&quot;&gt;&lt;div class=&quot;yozora-admonition yozora-admonition--note&quot;&gt;&lt;h5 class=&quot;yozora-admonition__heading&quot;&gt;&lt;span class=&quot;yozora-admonition__heading-icon&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; height=&quot;24px&quot; viewBox=&quot;0 0 24 24&quot; width=&quot;24px&quot;&gt;&lt;path d=&quot;M11 7h2v2h-2zm0 4h2v6h-2zm1-9C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z&quot; /&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;yozora-admonition__heading-title&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;节选自湖南大学《大学数学1》第三版 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\;P_{134}&lt;/span&gt;&lt;/span&gt;&lt;/h5&gt;&lt;div class=&quot;yozora-admonition__body&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;麦克劳林（Maclaurin）公式如下（其中，&lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\xi&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 在 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 和 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 之间）：&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-math&quot;&gt;  f(x) = f(0) + f&apos;(0)x + \frac{f&apos;&apos;(0)}{2!} + \cdots + 
         \frac{f^{(n)}(0)}{n!}x + \frac{f^{(n+1)}(\xi)}{(n+1)!} x^{n+1}
&lt;/div&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;求 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;e^x&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 的麦克劳林级数时应用到了 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 的一个重要性质： &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;(e^x)&apos; = e^x&lt;/span&gt;&lt;/p&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;由于 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;f&apos;(x) = (e^x)&apos; = e^x&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\cdots&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;f^{(n)}(x)=e^x&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;f^{(n + 1)}(x)=e^x&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;， &lt;/span&gt;&lt;br class=&quot;yozora-break&quot; /&gt;&lt;span class=&quot;yozora-text&quot;&gt;
得 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;f(0)=1&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;f&apos;(0)=1&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\cdots&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;f^{(n)}(0)=1&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;f^{(n+1)}(\xi)=e^x&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;. 故有：&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-math&quot;&gt;e^x = 1 + x + \frac{x^2}{2!} + \cdots + \frac{x^n}{n!} + \frac{e^\xi}{(n+1)!} x^{n+1}
&lt;/div&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;当 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;x \rightarrow 0&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 时，有：&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-math&quot;&gt;\lim_{x \rightarrow 0} e^x = 1 + x + \frac{x^2}{2!} + \cdots + \frac{x^2}{n!}
&lt;/div&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;【证毕】&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div className=&quot;yozora-footnote-definition&quot;&gt;&lt;p className=&quot;yozora-footnote-definition__title yozora-paragraph&quot;&gt;&lt;a href=&quot;#reference-footnote-4&quot;&gt;&amp;uarr;&lt;/a&gt;&lt;span&gt;&amp;nbsp;[4]:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;div className=&quot;yozora-footnote-definition__content&quot;&gt;&lt;div class=&quot;yozora-admonition yozora-admonition--note&quot;&gt;&lt;h5 class=&quot;yozora-admonition__heading&quot;&gt;&lt;span class=&quot;yozora-admonition__heading-icon&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; height=&quot;24px&quot; viewBox=&quot;0 0 24 24&quot; width=&quot;24px&quot;&gt;&lt;path d=&quot;M11 7h2v2h-2zm0 4h2v6h-2zm1-9C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z&quot; /&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;yozora-admonition__heading-title&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;节选自 &lt;/span&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://zh.wikipedia.org/wiki/%E6%AC%A7%E6%8B%89%E5%85%AC%E5%BC%8F&quot; title=&quot;https://zh.wikipedia.org/wiki/%E6%AC%A7%E6%8B%89%E5%85%AC%E5%BC%8F&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;欧拉公式 | 维基百科&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/h5&gt;&lt;div class=&quot;yozora-admonition__body&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;首先，在复数域上对 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;e^x&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 进行定义：&lt;/span&gt;&lt;/p&gt;&lt;blockquote class=&quot;yozora-blockquote&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;对于 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;a,b \in \mathbb{R}, c=a+ib \in \mathbb{C}&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，规定
&lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\displaystyle e^{\mathbb C}=\lim_{n \rightarrow \infty} \left(1 + \frac{c}{n} \right)^n&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;.&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;对&lt;/span&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://zh.wikipedia.org/wiki/%E5%A4%8D%E6%95%B0_(%E6%95%B0%E5%AD%A6)#%E6%9E%81%E5%9D%90%E6%A0%87%E5%BD%A2%E5%BC%8F&quot; title=&quot;https://zh.wikipedia.org/wiki/%E5%A4%8D%E6%95%B0_(%E6%95%B0%E5%AD%A6)#%E6%9E%81%E5%9D%90%E6%A0%87%E5%BD%A2%E5%BC%8F&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;复数的极坐标表示&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt; &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;w= \mu + i\nu = r(\cos\theta + i\sin\theta)&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，从而有：&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-math&quot;&gt;r = \sqrt{\mu^2 + \nu^2} \in \mathbb{R}, \; \theta=\arctan \left( \frac{\nu}{\mu} \right) \in \mathbb{R}
&lt;/div&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;根据&lt;/span&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://zh.wikipedia.org/wiki/%E6%A3%A3%E8%8E%AB%E5%BC%97%E5%85%AC%E5%BC%8F&quot; title=&quot;https://zh.wikipedia.org/wiki/%E6%A3%A3%E8%8E%AB%E5%BC%97%E5%85%AC%E5%BC%8F&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;棣莫弗公式&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt; &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;w^n=(\mu + i\nu)^n = r^n(\cos{n\theta} + i\sin{n\theta})&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，有：&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-math&quot;&gt;\left( 1 + \frac{a + bi}{n} \right)^n 
= \left[ \left( 1 + \frac{a}{n} \right) + i \cdot \frac{b}{n} \right]^n 
= r_n(\cos\theta_n + i\sin\theta_n)
&lt;/div&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;假设 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;n &amp;gt; \lvert a \rvert&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，则：&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-math&quot;&gt;r_n = \left[ \left( 1 + \frac{a}{n} \right)^2 + \left( \frac{b}{n} \right)^2 \right]^{\frac{n}{2}}, \quad 
\theta_n=n \arctan \frac{\frac{b}{n}}{1 + \frac{a}{n}}
&lt;/div&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;从而有&lt;/span&gt;&lt;sup id=&quot;reference-footnote-6&quot; class=&quot;yozora-footnote-reference&quot;&gt;&lt;a href=&quot;#footnote-6&quot; title=&quot;6&quot;&gt;[6]&lt;/a&gt;&lt;/sup&gt;&lt;span class=&quot;yozora-text&quot;&gt;：&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-math&quot;&gt;\begin{aligned}
  \lim_{n \rightarrow \infty} \ln r_n 
    &amp;amp;= \lim_{n \rightarrow \infty} \left[ \frac{n}{2} \cdot \ln \left( 1 + \frac{2a}{n} + \frac{a^2 + b^2}{n^2} \right) \right] \\
    &amp;amp;= \lim_{n \rightarrow \infty} \left[ \frac{n}{2} \cdot \left( \frac{2a}{n} + \frac{a^2 + b^2}{n^2} \right) \right] \\
    &amp;amp;= a \\
\end{aligned}
&lt;/div&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;又因为：&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-math&quot;&gt;\begin{aligned}
  \lim_{n \rightarrow \infty} \ln \theta_n
    &amp;amp;= \lim_{n \rightarrow \infty} \left( n \cdot \arctan \frac{\frac{b}{n}}{1 + \frac{a}{n}} \right) \\
    &amp;amp;= \lim_{n \rightarrow \infty} \left( n \cdot \frac{\frac{b}{n}}{1 + \frac{a}{n}} \right) \\
    &amp;amp;= b \\
\end{aligned}
&lt;/div&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;从而有：&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-math&quot;&gt;\begin{aligned}
  \lim_{n \rightarrow \infty} \left( 1 + \frac{a+bi}{n} \right)^n
    &amp;amp;= \lim_{n \rightarrow \infty} r_n(\cos\theta_n + i\sin\theta_n) \\
    &amp;amp;= \lim_{n \rightarrow \infty} r_n \cdot \lim_{n \rightarrow \infty} (\cos\theta_n + i\sin\theta_n) \\
    &amp;amp;= e^{\lim_{n \rightarrow \infty} \ln r_n} \cdot \lim_{n \rightarrow \infty} (\cos\theta_n + i\sin\theta_n) \\
    &amp;amp;= e^{\lim_{n \rightarrow \infty} \ln r_n} \cdot \left( \lim_{n \rightarrow \infty} \cos\theta_n + \lim_{n \rightarrow \infty} i\sin\theta_n \right) \\
    &amp;amp;= e^a \cdot (\cos b + i\sin b)
\end{aligned}
&lt;/div&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;即 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;e^{a+ib} = e^a(\cos b + i\sin b)&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，令 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;a=0&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，则有&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-math&quot;&gt;e^{ix} = \cos x + i\sin x
&lt;/div&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;【证毕】&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div className=&quot;yozora-footnote-definition&quot;&gt;&lt;p className=&quot;yozora-footnote-definition__title yozora-paragraph&quot;&gt;&lt;a href=&quot;#reference-footnote-5&quot;&gt;&amp;uarr;&lt;/a&gt;&lt;span&gt;&amp;nbsp;[5]:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;div className=&quot;yozora-footnote-definition__content&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;用到了无穷小量 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\displaystyle \lim_{x \rightarrow 0} \left( 1+\frac{1}{x} \right)^x = e&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div className=&quot;yozora-footnote-definition&quot;&gt;&lt;p className=&quot;yozora-footnote-definition__title yozora-paragraph&quot;&gt;&lt;a href=&quot;#reference-footnote-6&quot;&gt;&amp;uarr;&lt;/a&gt;&lt;span&gt;&amp;nbsp;[6]:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;div className=&quot;yozora-footnote-definition__content&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;用到了墨卡托级数： &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\ln (1+x) \sim x&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/footer&gt;&lt;/section&gt;</content:encoded></item><item><title><![CDATA[当你想来一把数独]]></title><description><![CDATA[<section class="yozora-markdown"><main><h2 class="yozora-heading"><span class="yozora-text">前言</span></h2><p class="yozora-paragraph"><span class="yozora-text">前一阵子想要整理一下</span><span class="yozora-text">，为了验证对算法理解的准确性写了一道数独的题目。想起大学时用 </span><pre class="yozora-inline-code"><code>C++</code></pre><span class="yozora-text"> 写过一个回溯版的，当时还兴致冲冲地拿它去求解手机上的数独游戏。想到这里时还特意在电脑上翻了好久也没能找到当时的代码；想起那时在 codevs.cn 上做过提交，本来还想去嫌弃下自己当年写的代码的，结果发现
codevs.cn 好像死掉了。</span></p></main><footer><div class="yozora-footnote-definitions"><div class="yozora-footnote-definitions__title">footnote-definitions</div><ul class="yozora-footnote-definitions__main"></ul></div></footer></section>]]></description><link>https://me.guanghechen.com/post/game/sudoku/</link><guid isPermaLink="false">https://me.guanghechen.com/post/game/sudoku/</guid><content:encoded>&lt;section class=&quot;yozora-markdown&quot;&gt;&lt;main&gt;&lt;h2 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;前言&lt;/span&gt;&lt;/h2&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;前一阵子想要整理一下&lt;/span&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;/post/algorithm/dlx/&quot; title=&quot;/post/algorithm/dlx/&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;精确覆盖问题和 DLX 算法&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;，为了验证对算法理解的准确性写了一道数独的题目。想起大学时用 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;C++&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 写过一个回溯版的，当时还兴致冲冲地拿它去求解手机上的数独游戏。想到这里时还特意在电脑上翻了好久也没能找到当时的代码；想起那时在 codevs.cn 上做过提交，本来还想去嫌弃下自己当年写的代码的，结果发现
codevs.cn 好像死掉了。&lt;/span&gt;&lt;/p&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;时间过得可真快，转眼间又是几个春秋。而我仿佛总是在迟到，好几件事情都没能在最希望完成的时候做到，却又在过后耿耿于怀，不甘心地追逐着过去的时空里所发生的期待。不是在原地踏步，可还是开始动摇，想必继续往前的地方是没有尽头的。&lt;/span&gt;&lt;/p&gt;&lt;h2 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;什么是数独&lt;/span&gt;&lt;/h2&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;一个经典的数独游戏由 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;x^2&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\times x^2&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 的网格构成，游戏的规则就是在网格上填数直到满足下述四个约束：&lt;/span&gt;&lt;/p&gt;&lt;ol class=&quot;yozora-list&quot; start=&quot;1&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;网格中所有的格子都恰好填上一个数字&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;每一行中需要出现 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;[1, x^2]&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 之间的所有整数&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;每一列中需要出现 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;[1, x^2]&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 之间的所有整数&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;每一个子方阵中需要出现 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;[1, x^2]&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 之间的所有整数&lt;/span&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;如下所示是一个经典的 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;9 \times 9&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 的数独面板，其中粗线围成了 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;x^2=9&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 个子方阵
&lt;/span&gt;&lt;sup id=&quot;reference-footnote-1&quot; class=&quot;yozora-footnote-reference&quot;&gt;&lt;a href=&quot;#footnote-1&quot; title=&quot;1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;&lt;span class=&quot;yozora-text&quot;&gt;。&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;() =&amp;gt; ()
&lt;/code&gt;&lt;/pre&gt;&lt;h2 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;生成一个数独谜题&lt;/span&gt;&lt;/h2&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;要写一个离线游戏首先要解决数据的问题，对于数独要考虑的问题有：&lt;/span&gt;&lt;/p&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;如何确保存在解&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;如何确保唯一解&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;如何区分难度&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;如何获得更好的随机性&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h3 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;如何确保存在解&lt;/span&gt;&lt;/h3&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;先生成一个填满的数独面板 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;G(r, c)&lt;/span&gt;&lt;/p&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;在数独面板上选取若干个位置，无冲突地填入值，然后跑求解数独的算法，若无解，则从之前选取填入的位置中选取一个，将其上面的值擦除，再运行求解数独的算法，不断重复此过程，必然能得到一个填满的数独&lt;/span&gt;&lt;sup id=&quot;reference-footnote-2&quot; class=&quot;yozora-footnote-reference&quot;&gt;&lt;a href=&quot;#footnote-2&quot; title=&quot;2&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt;&lt;span class=&quot;yozora-text&quot;&gt;。&lt;/span&gt;&lt;/p&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;如何求解数独可参见 &lt;/span&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;/post/algorithm/dlx/&quot; title=&quot;/post/algorithm/dlx/&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;精确覆盖问题和 DLX 算法&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;在一个填满的数独面板中选取若干个位置，依次枚举这些位置 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;(r, c)&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，尝试将上面的值 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;v=G(r, c)&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 擦除，则得到一个必定存在解的数独谜题。&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h3 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;如何确保唯一解&lt;/span&gt;&lt;/h3&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;在上一步选取待擦除位置的操作中增加一个校验的逻辑：尝试擦除位置 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;(r, c)&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 上的值
&lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;v = G(r, c)&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 时，枚举 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;[1, x^2]&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 之间除 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 外的所有整数 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;v&apos;&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，并将格子
&lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;(r, c)&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 的值设置为 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;v&apos;&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，对于每次枚举都分别跑一次求解数独的算法，若存在解，说明此位置上的值不能擦除&lt;/span&gt;&lt;sup id=&quot;reference-footnote-3&quot; class=&quot;yozora-footnote-reference&quot;&gt;&lt;a href=&quot;#footnote-3&quot; title=&quot;3&quot;&gt;[3]&lt;/a&gt;&lt;/sup&gt;&lt;span class=&quot;yozora-text&quot;&gt;；否则，擦除此格子上的值。&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h3 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;如何区分难度&lt;/span&gt;&lt;/h3&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;一个直观的印象是：数独面板中缺失的格子越多，想要解决的困难度越大。所以难度可以映射为在生成数独谜题时尽可能多地尝试擦除格子。&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;下面是一个示例，拖动滑块以切换难度。&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;() =&amp;gt; ()
&lt;/code&gt;&lt;/pre&gt;&lt;h3 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;如何获得更好的随机性&lt;/span&gt;&lt;/h3&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;在初始填充的数独面板时：&lt;/span&gt;&lt;/p&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;随机选取填充的位置&lt;/span&gt;&lt;/p&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;将所有的格子排成一排，为方便叙述不妨将其记为 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，应用 &lt;/span&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;/post/algorithm/shuffle/&quot; title=&quot;/post/algorithm/shuffle/&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;Knuth Shuffle&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;
算法将其顺序打乱。则在随机选取格子时直接遍历打乱顺序后的 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 就可以了。一方面 &lt;/span&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;/post/algorithm/shuffle/&quot; title=&quot;/post/algorithm/shuffle/&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;Knuth Shuffle&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt; 保证了每个格子以相同的概率排在任一位置上；另一方面直接遍历 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 相比随机枚举格子，不存在重复枚举的可能性。&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;按随机顺序枚举某个位置上可填入的值&lt;/span&gt;&lt;/p&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;枚举 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;v&apos;&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 时可以先求出一个候选项列表&lt;/span&gt;&lt;sup id=&quot;reference-footnote-4&quot; class=&quot;yozora-footnote-reference&quot;&gt;&lt;a href=&quot;#footnote-4&quot; title=&quot;4&quot;&gt;[4]&lt;/a&gt;&lt;/sup&gt;&lt;span class=&quot;yozora-text&quot;&gt;，同样地，将候选项列表打乱顺序后进行遍历。&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;在尝试擦除格子时：&lt;/span&gt;&lt;/p&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;随机选取待擦除的位置&lt;/span&gt;&lt;/p&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;类似地，在尝试擦除格子时，可以通过遍历 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 达到随机枚举格子的效果。检查擦除此位置时是否存在多解时，直接按顺序遍历即可。&lt;/span&gt;&lt;/p&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;为了体现难度，可以只对前 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\displaystyle \left\lceil difficulty \times \big\lvert T \big\rvert \right\rceil&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;
个格子进行擦除尝试。&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;交互设计&lt;/span&gt;&lt;/h2&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;除了数据外还需要考虑几个交互问题：&lt;/span&gt;&lt;/p&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;input checked=&quot;&quot; disabled=&quot;&quot; type=&quot;checkbox&quot; /&gt; &lt;span class=&quot;yozora-text&quot;&gt;切换难度和数独面板的大小&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;input checked=&quot;&quot; disabled=&quot;&quot; type=&quot;checkbox&quot; /&gt; &lt;span class=&quot;yozora-text&quot;&gt;暂停（暂停时用模糊滤镜遮住谜题）、继续游戏、重新开始游戏&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;input checked=&quot;&quot; disabled=&quot;&quot; type=&quot;checkbox&quot; /&gt; &lt;span class=&quot;yozora-text&quot;&gt;计时器：支持暂停、恢复、重置&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;input checked=&quot;&quot; disabled=&quot;&quot; type=&quot;checkbox&quot; /&gt; &lt;span class=&quot;yozora-text&quot;&gt;区分谜题预填充的格子和玩家填充的格子&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;input checked=&quot;&quot; disabled=&quot;&quot; type=&quot;checkbox&quot; /&gt; &lt;span class=&quot;yozora-text&quot;&gt;格子的输入&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;input checked=&quot;&quot; disabled=&quot;&quot; type=&quot;checkbox&quot; /&gt; &lt;span class=&quot;yozora-text&quot;&gt;输入冲突的值时高亮提醒&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;input checked=&quot;&quot; disabled=&quot;&quot; type=&quot;checkbox&quot; /&gt; &lt;span class=&quot;yozora-text&quot;&gt;选中非空格子时高亮与之值相同的格子&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;input checked=&quot;&quot; disabled=&quot;&quot; type=&quot;checkbox&quot; /&gt; &lt;span class=&quot;yozora-text&quot;&gt;完成游戏时的简易提示&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;input checked=&quot;&quot; disabled=&quot;&quot; type=&quot;checkbox&quot; /&gt; &lt;span class=&quot;yozora-text&quot;&gt;简易的提示和小抄&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;input disabled=&quot;&quot; type=&quot;checkbox&quot; /&gt; &lt;span class=&quot;yozora-text&quot;&gt;完成某行、列或子方阵时的提示动画&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;附录&lt;/span&gt;&lt;/h2&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;下面我实现的一个简易数独，求解数独以及生成数独所需的数据的算法我封装到了
&lt;/span&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://github.com/guanghechen/algorithm.ts/tree/main/packages/sudoku&quot; title=&quot;https://github.com/guanghechen/algorithm.ts/tree/main/packages/sudoku&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;@algorithm.ts/sudoku&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt; 中，有兴趣的朋友可以自取。ui 组件打算之后做好整理再开源。&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;() =&amp;gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;h2 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;Related&lt;/span&gt;&lt;/h2&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;/post/algorithm/dlx/&quot; title=&quot;/post/algorithm/dlx/&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;精确覆盖问题和 DLX 算法 | 光和尘&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;/post/algorithm/shuffle/&quot; title=&quot;/post/algorithm/shuffle/&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;洗牌问题和 Knuth-Shuffle 算法 | 光和尘&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;/game/sudoku&quot; title=&quot;/game/sudoku&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;Sudoku Game | 光和尘&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://github.com/guanghechen/algorithm.ts/tree/main/packages/sudoku&quot; title=&quot;https://github.com/guanghechen/algorithm.ts/tree/main/packages/sudoku&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;@algorithm.ts/sudoku&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div className=&quot;yozora-footnote-definition&quot;&gt;&lt;p className=&quot;yozora-footnote-definition__title yozora-paragraph&quot;&gt;&lt;a href=&quot;#reference-footnote-1&quot;&gt;&amp;uarr;&lt;/a&gt;&lt;span&gt;&amp;nbsp;[1]:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;div className=&quot;yozora-footnote-definition__content&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;即上述约束中第四个约束提到的“子方阵”&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div className=&quot;yozora-footnote-definition&quot;&gt;&lt;p className=&quot;yozora-footnote-definition__title yozora-paragraph&quot;&gt;&lt;a href=&quot;#reference-footnote-2&quot;&gt;&amp;uarr;&lt;/a&gt;&lt;span&gt;&amp;nbsp;[2]:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;div className=&quot;yozora-footnote-definition__content&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;因为空的面板必然存在解，最坏的情况是擦除所有的格子上的值（当然，不可能等到所有格子都被擦除才找到解）&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div className=&quot;yozora-footnote-definition&quot;&gt;&lt;p className=&quot;yozora-footnote-definition__title yozora-paragraph&quot;&gt;&lt;a href=&quot;#reference-footnote-3&quot;&gt;&amp;uarr;&lt;/a&gt;&lt;span&gt;&amp;nbsp;[3]:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;div className=&quot;yozora-footnote-definition__content&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;否则将存在多个解，因为此时格子 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;(r, c)&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 至少可以有 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;
和 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;v&apos;&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 两种选择&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div className=&quot;yozora-footnote-definition&quot;&gt;&lt;p className=&quot;yozora-footnote-definition__title yozora-paragraph&quot;&gt;&lt;a href=&quot;#reference-footnote-4&quot;&gt;&amp;uarr;&lt;/a&gt;&lt;span&gt;&amp;nbsp;[4]:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;div className=&quot;yozora-footnote-definition__content&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;因为可以简单检查格子所处的行、列、子方阵上的值构成的集合，即可在 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;O(x^2)&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 的复杂度内排除部分答案，这个预处理的开销远远小于运行求解数独算法的开销，且事先排除掉不可能的情况有利于消除边缘数据&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/main&gt;&lt;footer&gt;&lt;div class=&quot;yozora-footnote-definitions&quot;&gt;&lt;div class=&quot;yozora-footnote-definitions__title&quot;&gt;footnote-definitions&lt;/div&gt;&lt;ul class=&quot;yozora-footnote-definitions__main&quot;&gt;&lt;div className=&quot;yozora-footnote-definition&quot;&gt;&lt;p className=&quot;yozora-footnote-definition__title yozora-paragraph&quot;&gt;&lt;a href=&quot;#reference-footnote-1&quot;&gt;&amp;uarr;&lt;/a&gt;&lt;span&gt;&amp;nbsp;[1]:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;div className=&quot;yozora-footnote-definition__content&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;即上述约束中第四个约束提到的“子方阵”&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div className=&quot;yozora-footnote-definition&quot;&gt;&lt;p className=&quot;yozora-footnote-definition__title yozora-paragraph&quot;&gt;&lt;a href=&quot;#reference-footnote-2&quot;&gt;&amp;uarr;&lt;/a&gt;&lt;span&gt;&amp;nbsp;[2]:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;div className=&quot;yozora-footnote-definition__content&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;因为空的面板必然存在解，最坏的情况是擦除所有的格子上的值（当然，不可能等到所有格子都被擦除才找到解）&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div className=&quot;yozora-footnote-definition&quot;&gt;&lt;p className=&quot;yozora-footnote-definition__title yozora-paragraph&quot;&gt;&lt;a href=&quot;#reference-footnote-3&quot;&gt;&amp;uarr;&lt;/a&gt;&lt;span&gt;&amp;nbsp;[3]:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;div className=&quot;yozora-footnote-definition__content&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;否则将存在多个解，因为此时格子 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;(r, c)&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 至少可以有 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;
和 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;v&apos;&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 两种选择&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div className=&quot;yozora-footnote-definition&quot;&gt;&lt;p className=&quot;yozora-footnote-definition__title yozora-paragraph&quot;&gt;&lt;a href=&quot;#reference-footnote-4&quot;&gt;&amp;uarr;&lt;/a&gt;&lt;span&gt;&amp;nbsp;[4]:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;div className=&quot;yozora-footnote-definition__content&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;因为可以简单检查格子所处的行、列、子方阵上的值构成的集合，即可在 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;O(x^2)&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 的复杂度内排除部分答案，这个预处理的开销远远小于运行求解数独算法的开销，且事先排除掉不可能的情况有利于消除边缘数据&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/footer&gt;&lt;/section&gt;</content:encoded></item><item><title><![CDATA[精确覆盖问题和 DLX 算法]]></title><description><![CDATA[<section class="yozora-markdown"><main><h2 class="yozora-heading"><span class="yozora-text">前言</span></h2><p class="yozora-paragraph"><span class="yozora-text">很早就想要补一下舞蹈链和精确覆盖算法，却一直各种拖延，趁着最近有空，又重新翻开了刘汝佳的书。大学的时候看过几次，甚至照着书里的思路手敲了一遍并通过了例题，但对于算法的原理一直有些不求甚解。很早以前，一位同学告诉我说“现在看不懂的东西不用勉强，以后慢慢就会懂了”，后来也真的在不断印证这句话；但我很担心随着年纪的增长，记忆力和学习能力不断退化之后，恐怕这个 flag 会逐渐倒下。所以趁着眼下尚能理解进去，尽量用自己的语言做一下记录。</span></p></main><footer><div class="yozora-footnote-definitions"><div class="yozora-footnote-definitions__title">footnote-definitions</div><ul class="yozora-footnote-definitions__main"></ul></div></footer></section>]]></description><link>https://me.guanghechen.com/post/algorithm/dlx/</link><guid isPermaLink="false">https://me.guanghechen.com/post/algorithm/dlx/</guid><content:encoded>&lt;section class=&quot;yozora-markdown&quot;&gt;&lt;main&gt;&lt;h2 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;前言&lt;/span&gt;&lt;/h2&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;很早就想要补一下舞蹈链和精确覆盖算法，却一直各种拖延，趁着最近有空，又重新翻开了刘汝佳的书。大学的时候看过几次，甚至照着书里的思路手敲了一遍并通过了例题，但对于算法的原理一直有些不求甚解。很早以前，一位同学告诉我说“现在看不懂的东西不用勉强，以后慢慢就会懂了”，后来也真的在不断印证这句话；但我很担心随着年纪的增长，记忆力和学习能力不断退化之后，恐怕这个 flag 会逐渐倒下。所以趁着眼下尚能理解进去，尽量用自己的语言做一下记录。&lt;/span&gt;&lt;/p&gt;&lt;h2 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;精确覆盖问题&lt;/span&gt;&lt;/h2&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;有一些由整数 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;1 \sim n&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 中的数字组成的集合 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;S_1, S_2, \cdots, S_m&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，要求选择若干个集合 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;S_i&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，使得 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;1 \sim n&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 中每个整数都在选出的集合中的某个出现且恰好仅出现一次。举个栗子：&lt;/span&gt;&lt;/p&gt;&lt;blockquote class=&quot;yozora-blockquote&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;不妨假设 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;n=7, m=6&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，集合为：&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-math&quot;&gt;\begin{aligned}
  S_1&amp;amp;=\lbrace 1, 4, 7 \rbrace\\
  S_2&amp;amp;=\lbrace 1, 4 \rbrace\\
  S_3&amp;amp;=\lbrace 4,5,7 \rbrace\\
  S_4&amp;amp;=\lbrace 3, 5, 6 \rbrace\\
  S_5&amp;amp;=\lbrace 2, 3, 6, 7 \rbrace\\
  S_6&amp;amp;=\lbrace 2, 7 \rbrace\\
\end{aligned}
&lt;/div&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;则一个精确覆盖为 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\lbrace S_2, S_4, S_6 \rbrace&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，因为 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\lbrace 1, 4 \rbrace&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;,
&lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\lbrace 3, 5, 6 \rbrace&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\lbrace 2, 7 \rbrace&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 无重复、无遗漏地包含了 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;1 \sim 7&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;
中的所有整数。&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;我们可以用一个 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;m \times n&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 的 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;01&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 矩阵来表示集合，其中，&lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 表示不包含，&lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 表示包含。比如第 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;(i, j)&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 个位置若为 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，则说明 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;S_i&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 中不包含 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;。上文中的栗子用矩阵表示如下所示：&lt;/span&gt;&lt;/p&gt;&lt;table class=&quot;yozora-table&quot;&gt;&lt;thead class=&quot;yozora-table__thead&quot;&gt;&lt;tr class=&quot;yozora-table-row&quot;&gt;&lt;th class=&quot;yozora-table-cell&quot; align=&quot;center&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt; &lt;/span&gt;&lt;/th&gt;&lt;th class=&quot;yozora-table-cell&quot; align=&quot;center&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;1&lt;/span&gt;&lt;/th&gt;&lt;th class=&quot;yozora-table-cell&quot; align=&quot;center&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;2&lt;/span&gt;&lt;/th&gt;&lt;th class=&quot;yozora-table-cell&quot; align=&quot;center&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;3&lt;/span&gt;&lt;/th&gt;&lt;th class=&quot;yozora-table-cell&quot; align=&quot;center&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;4&lt;/span&gt;&lt;/th&gt;&lt;th class=&quot;yozora-table-cell&quot; align=&quot;center&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;5&lt;/span&gt;&lt;/th&gt;&lt;th class=&quot;yozora-table-cell&quot; align=&quot;center&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;6&lt;/span&gt;&lt;/th&gt;&lt;th class=&quot;yozora-table-cell&quot; align=&quot;center&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;7&lt;/span&gt;&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody class=&quot;yozora-table__tbody&quot;&gt;&lt;tr class=&quot;yozora-table-row&quot;&gt;&lt;td class=&quot;yozora-table-cell&quot; align=&quot;center&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;S_1&lt;/span&gt;&lt;/td&gt;&lt;td class=&quot;yozora-table-cell&quot; align=&quot;center&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;1&lt;/span&gt;&lt;/td&gt;&lt;td class=&quot;yozora-table-cell&quot; align=&quot;center&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;0&lt;/span&gt;&lt;/td&gt;&lt;td class=&quot;yozora-table-cell&quot; align=&quot;center&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;0&lt;/span&gt;&lt;/td&gt;&lt;td class=&quot;yozora-table-cell&quot; align=&quot;center&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;1&lt;/span&gt;&lt;/td&gt;&lt;td class=&quot;yozora-table-cell&quot; align=&quot;center&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;0&lt;/span&gt;&lt;/td&gt;&lt;td class=&quot;yozora-table-cell&quot; align=&quot;center&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;0&lt;/span&gt;&lt;/td&gt;&lt;td class=&quot;yozora-table-cell&quot; align=&quot;center&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;1&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr class=&quot;yozora-table-row&quot;&gt;&lt;td class=&quot;yozora-table-cell&quot; align=&quot;center&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;S_2&lt;/span&gt;&lt;/td&gt;&lt;td class=&quot;yozora-table-cell&quot; align=&quot;center&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;1&lt;/span&gt;&lt;/td&gt;&lt;td class=&quot;yozora-table-cell&quot; align=&quot;center&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;0&lt;/span&gt;&lt;/td&gt;&lt;td class=&quot;yozora-table-cell&quot; align=&quot;center&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;0&lt;/span&gt;&lt;/td&gt;&lt;td class=&quot;yozora-table-cell&quot; align=&quot;center&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;1&lt;/span&gt;&lt;/td&gt;&lt;td class=&quot;yozora-table-cell&quot; align=&quot;center&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;0&lt;/span&gt;&lt;/td&gt;&lt;td class=&quot;yozora-table-cell&quot; align=&quot;center&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;0&lt;/span&gt;&lt;/td&gt;&lt;td class=&quot;yozora-table-cell&quot; align=&quot;center&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;0&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr class=&quot;yozora-table-row&quot;&gt;&lt;td class=&quot;yozora-table-cell&quot; align=&quot;center&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;S_3&lt;/span&gt;&lt;/td&gt;&lt;td class=&quot;yozora-table-cell&quot; align=&quot;center&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;0&lt;/span&gt;&lt;/td&gt;&lt;td class=&quot;yozora-table-cell&quot; align=&quot;center&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;0&lt;/span&gt;&lt;/td&gt;&lt;td class=&quot;yozora-table-cell&quot; align=&quot;center&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;0&lt;/span&gt;&lt;/td&gt;&lt;td class=&quot;yozora-table-cell&quot; align=&quot;center&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;1&lt;/span&gt;&lt;/td&gt;&lt;td class=&quot;yozora-table-cell&quot; align=&quot;center&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;1&lt;/span&gt;&lt;/td&gt;&lt;td class=&quot;yozora-table-cell&quot; align=&quot;center&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;0&lt;/span&gt;&lt;/td&gt;&lt;td class=&quot;yozora-table-cell&quot; align=&quot;center&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;1&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr class=&quot;yozora-table-row&quot;&gt;&lt;td class=&quot;yozora-table-cell&quot; align=&quot;center&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;S_4&lt;/span&gt;&lt;/td&gt;&lt;td class=&quot;yozora-table-cell&quot; align=&quot;center&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;0&lt;/span&gt;&lt;/td&gt;&lt;td class=&quot;yozora-table-cell&quot; align=&quot;center&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;0&lt;/span&gt;&lt;/td&gt;&lt;td class=&quot;yozora-table-cell&quot; align=&quot;center&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;1&lt;/span&gt;&lt;/td&gt;&lt;td class=&quot;yozora-table-cell&quot; align=&quot;center&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;0&lt;/span&gt;&lt;/td&gt;&lt;td class=&quot;yozora-table-cell&quot; align=&quot;center&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;1&lt;/span&gt;&lt;/td&gt;&lt;td class=&quot;yozora-table-cell&quot; align=&quot;center&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;1&lt;/span&gt;&lt;/td&gt;&lt;td class=&quot;yozora-table-cell&quot; align=&quot;center&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;0&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr class=&quot;yozora-table-row&quot;&gt;&lt;td class=&quot;yozora-table-cell&quot; align=&quot;center&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;S_5&lt;/span&gt;&lt;/td&gt;&lt;td class=&quot;yozora-table-cell&quot; align=&quot;center&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;0&lt;/span&gt;&lt;/td&gt;&lt;td class=&quot;yozora-table-cell&quot; align=&quot;center&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;1&lt;/span&gt;&lt;/td&gt;&lt;td class=&quot;yozora-table-cell&quot; align=&quot;center&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;1&lt;/span&gt;&lt;/td&gt;&lt;td class=&quot;yozora-table-cell&quot; align=&quot;center&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;0&lt;/span&gt;&lt;/td&gt;&lt;td class=&quot;yozora-table-cell&quot; align=&quot;center&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;0&lt;/span&gt;&lt;/td&gt;&lt;td class=&quot;yozora-table-cell&quot; align=&quot;center&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;1&lt;/span&gt;&lt;/td&gt;&lt;td class=&quot;yozora-table-cell&quot; align=&quot;center&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;1&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr class=&quot;yozora-table-row&quot;&gt;&lt;td class=&quot;yozora-table-cell&quot; align=&quot;center&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;S_6&lt;/span&gt;&lt;/td&gt;&lt;td class=&quot;yozora-table-cell&quot; align=&quot;center&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;0&lt;/span&gt;&lt;/td&gt;&lt;td class=&quot;yozora-table-cell&quot; align=&quot;center&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;1&lt;/span&gt;&lt;/td&gt;&lt;td class=&quot;yozora-table-cell&quot; align=&quot;center&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;0&lt;/span&gt;&lt;/td&gt;&lt;td class=&quot;yozora-table-cell&quot; align=&quot;center&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;0&lt;/span&gt;&lt;/td&gt;&lt;td class=&quot;yozora-table-cell&quot; align=&quot;center&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;0&lt;/span&gt;&lt;/td&gt;&lt;td class=&quot;yozora-table-cell&quot; align=&quot;center&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;0&lt;/span&gt;&lt;/td&gt;&lt;td class=&quot;yozora-table-cell&quot; align=&quot;center&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;1&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;则精确覆盖问题可重新表述为：在一个 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;m \times n&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 的 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;01&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 矩阵中，选择若干行，对这些行做向量加法，得到的结果为 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;(1, 1, \cdots, 1)&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;。即：&lt;/span&gt;&lt;/p&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;选出的行里，不存在某列同时在两行中值均为 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;；&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;所有选出的行叠加在一起的结果覆盖所有列（每一列的值都不为 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;）。&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;算法 X&lt;/span&gt;&lt;/h2&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;算法 X（Algorithm X），其实就是回溯，可能是专门针对覆盖问题提出的算法吧。算法描述如下：&lt;/span&gt;&lt;/p&gt;&lt;blockquote class=&quot;yozora-blockquote&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;每次选择一个没有被删除列，然后枚举该列为 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 的所有行，尝试删除这些行，递归搜索后再恢复这些行。尝试删除行时，还要将该行中所有值为 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 的列也一并删除&lt;/span&gt;&lt;sup id=&quot;reference-footnote-1&quot; class=&quot;yozora-footnote-reference&quot;&gt;&lt;a href=&quot;#footnote-1&quot; title=&quot;1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;&lt;span class=&quot;yozora-text&quot;&gt;，恢复时也同样。&lt;/span&gt;&lt;/p&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;若没有可以选择的列了（即所有列都被删除了），说明已经找到一个精确覆盖的解了&lt;/span&gt;&lt;sup id=&quot;reference-footnote-2&quot; class=&quot;yozora-footnote-reference&quot;&gt;&lt;a href=&quot;#footnote-2&quot; title=&quot;2&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt;&lt;span class=&quot;yozora-text&quot;&gt;；若还有列但是没有行了&lt;/span&gt;&lt;sup id=&quot;reference-footnote-3&quot; class=&quot;yozora-footnote-reference&quot;&gt;&lt;a href=&quot;#footnote-3&quot; title=&quot;3&quot;&gt;[3]&lt;/a&gt;&lt;/sup&gt;&lt;span class=&quot;yozora-text&quot;&gt;说明原问题无解。&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;h2 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;舞蹈链&lt;/span&gt;&lt;/h2&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;舞蹈链（Dancing Link），一种支持快速删除、恢复列和行的数据结构。&lt;/span&gt;&lt;/p&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;舞蹈链是一个十字型双向链表结构，链表中的每个节点对应上述 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;01&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 矩阵中的一个 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;。另外还有 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;n+1&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 个虚拟节点，其中每列最上方一个虚拟节点作为该列链表的头指针，而所有虚拟节点的最前方有一个虚拟节点，作为虚拟节点的头指针，它也是舞蹈链的头指针。如下图所示（图片来源于网络）：&lt;/span&gt;&lt;/p&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;img class=&quot;yozora-image&quot; alt=&quot;dancing-link.png&quot; src=&quot;/static/906debbf150b5fffd06cfd7dee50449b/0227d/dancing-link.png&quot; title=&quot;dancing-link.png&quot; /&gt;&lt;/p&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;使用四个数组 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;R&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;D&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 分别表示舞蹈链中节点的左、右、上、下四个方向的指针，下标为节点的编号；同时列编号作为该列的虚拟节点，&lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 表示舞蹈链的头指针对应的虚拟节点。&lt;/span&gt;&lt;/p&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;删除行：只需要修改将该行中所有节点的上下行的下、上指针互指就好了。&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;// 设要删除的某行中某个节点编号为 R[i]
for (let j = R[i]; j !== i; j = R[j]) {
  U[D[j]] = U[j]
  D[U[j]] = D[j]
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;恢复行：只需要将该行中所有节点的上下行的下、上指针分别指向该该节点就好了。&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;// 设要删除的某行中某个节点编号为 R[i]
for (let j = R[i]; j !== i; j = R[j]) {
  U[D[j]] = j
  D[U[j]] = j
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;删除列和恢复列也类似，此处略去。&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;DLX 算法&lt;/span&gt;&lt;/h2&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;使用了舞蹈链的算法 X 通常被称为 DLX 算法。此处给出 DLX 算法的 Typescript 实现&lt;/span&gt;&lt;sup id=&quot;reference-footnote-4&quot; class=&quot;yozora-footnote-reference&quot;&gt;&lt;a href=&quot;#footnote-4&quot; title=&quot;4&quot;&gt;[4]&lt;/a&gt;&lt;/sup&gt;&lt;span class=&quot;yozora-text&quot;&gt;。&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;/**
 * The algorithm X that applied the dancing-link, it is also called as &quot;DLX&quot;.
 * It is used to solve the exact-cover problem.
 *
 * Dancing-link: A cross doubly linked list, each column has a virtual node as
 * the head pointer, and at the top of all virtual nodes there is an additional
 * virtual node as the head pointer of the virtual node, which is also the head
 * pointer of the entire dancing-link. In the implementation of using an array
 * to simulate a linked list, the virtual node is represented by a column
 * number, and the head pointer of the dancing-link can be represented by 0.
 *
 * @see https://me.guanghechen.com/post/algorithm/dlx/
 */
export interface DLX {
  /**
   * Initialize the dancing-link.
   * @param totalColumns   number of columns
   */
  init(totalColumns: number): void

  /**
   * Release memory variables.
   */
  destroy(): void

  /**
   * Add a row to the dancing-link.
   *
   * It should be noted that after solving the exact-cover problem, the
   * result is a list of selected row numbers, so the row number should be
   * specified as a value that can carry information.
   *
   * @param r         the row number
   * @param columns   columns on the row
   */
  addRow(rowNo: number, columns: ReadonlyArray): void

  /**
   * Try to find a precise coverage.
   *
   * When a solution is found, return the row numbers of all selected rows,
   * otherwise return null.
   */
  solve(): number[] | null
}

/**
 * Generate an object that encapsulates the DLX algorithm.
 *
 * @param MAX_N   maximum number of nodes in the dancing-link
 * @returns
 */
export function createDLX(MAX_N: number): DLX {
  // The number of nodes in the dancing-link (including the virtual nodes on
  // the column).
  let sz: number

  // the number of columns in the dancing-link.
  let totalColumns: number

  const selectedRowNos: number[] = new Array(MAX_N) // list of row numbers of selected rows
  let countOfSelectedRows: number // the number of selected rows

  const count: number[] = new Array(MAX_N) // lhe number of nodes of a column in the dancing-link
  const row: number[] = new Array(MAX_N) // the row number of a node in the dancing-link
  const col: number[] = new Array(MAX_N) // the column number of a node in the dancing-link
  const L: number[] = new Array(MAX_N) // left pointer of cross-link list
  const R: number[] = new Array(MAX_N) // right pointer of cross-link list
  const U: number[] = new Array(MAX_N) // up pointer of cross-link list
  const D: number[] = new Array(MAX_N) // down pointer of cross-link list
  return { init, destroy, addRow, solve }

  /**
   * @see DLX#init
   * @public
   */
  function init(_totalColumns: number): void {
    totalColumns = _totalColumns
    sz = _totalColumns + 1

    // Resize arrays.
    if (selectedRowNos.length &amp;lt; sz) {
      selectedRowNos.length = sz
      count.length = sz
      row.length = sz
      col.length = sz
      L.length = sz
      R.length = sz
      U.length = sz
      D.length = sz
    }

    for (let i = 0; i &amp;lt; sz; ++i) {
      L[i] = i - 1
      R[i] = i + 1
      U[i] = i
      D[i] = i
    }
    R[_totalColumns] = 0
    L[0] = _totalColumns

    count.fill(0, 0, sz)
  }

  /**
   * @see DLX#destroy
   * @public
   */
  function destroy(): void {
    selectedRowNos.length = 0
    count.length = 0
    row.length = 0
    col.length = 0
    L.length = 0
    R.length = 0
    U.length = 0
    D.length = 0
  }

  /**
   * @see DLX#addRow
   * @public
   */
  function addRow(r: number, columns: ReadonlyArray): void {
    const first = sz
    for (let i = 0; i &amp;lt; columns.length; ++i, ++sz) {
      const c = columns[i]
      row[sz] = r
      col[sz] = c
      count[c] += 1

      // Connect left and right nodes
      L[sz] = sz - 1
      R[sz] = sz + 1

      // Connect top and bottom nodes,
      // c is the virtual node on the c-th column, and is also the head pointer
      // of the linked list of the column, so at this time U[c] is the last
      // element of the column
      D[sz] = c
      D[U[c]] = sz
      U[sz] = U[c]
      U[c] = sz
    }

    // Since this is a circular linked list, the first and last columns of the
    // current row are connected to each other.
    R[sz - 1] = first
    L[first] = sz - 1
  }

  /**
   * @see DLX#solve
   * @public
   */
  function solve(): number[] | null {
    if (!algorithmX(0)) return null
    return selectedRowNos.slice(0, countOfSelectedRows)
  }

  /**
   * Remove a column from the dancing-link.
   * @param c   column number
   * @private
   */
  function removeColumn(c: number): void {
    L[R[c]] = L[c]
    R[L[c]] = R[c]
    for (let i = D[c]; i !== c; i = D[i]) {
      for (let j = R[i]; j !== i; j = R[j]) {
        U[D[j]] = U[j]
        D[U[j]] = D[j]
        count[col[j]] -= 1
      }
    }
  }

  /**
   * Restore a previously deleted column
   * @param c   column number
   * @private
   */
  function restoreColumn(c: number): void {
    for (let i = U[c]; i !== c; i = U[i]) {
      for (let j = L[i]; j !== i; j = L[j]) {
        count[col[j]] += 1
        U[D[j]] = j
        D[U[j]] = j
      }
    }
    L[R[c]] = c
    R[L[c]] = c
  }

  /**
   * Algorithm X.
   *
   * Recursively solve the problem of precise coverage, enumerate which rows are
   * selected in the recursive process, remove the selected rows and all the
   * columns on the rows, and restore these rows and columns during the
   * backtrack.
   *
   * @param dep   recursion depth
   * @private
   */
  function algorithmX(dep: number): boolean {
    // Find a solution when the dancing-link is empty.
    if (R[0] === 0) {
      // Record the length of the solution.
      countOfSelectedRows = dep
      return true
    }

    /**
     * Optimization: Find the column with the least number of nodes, and try to
     * cover from this column.
     */
    let c = R[0]
    for (let i = R[0]; i !== 0; i = R[i]) {
      if (count[i] &amp;lt; count[c]) c = i
    }

    // Remove this column.
    removeColumn(c)
    for (let i = D[c]; i !== c; i = D[i]) {
      selectedRowNos[dep] = row[i]
      for (let j = R[i]; j !== i; j = R[j]) removeColumn(col[j])

      // Recursively processing.
      if (algorithmX(dep + 1)) return true

      // Backtrack.
      for (let j = L[i]; j !== i; j = L[j]) restoreColumn(col[j])
    }
    // Backtrack.
    restoreColumn(c)

    return false
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;我把它封装在了 &lt;/span&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://github.com/guanghechen/algorithm.ts/blob/main/packages/dlx/README.md&quot; title=&quot;https://github.com/guanghechen/algorithm.ts/blob/main/packages/dlx/README.md&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;@algorithm.ts/dlx&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt; 中，你可以通过 npm 包导入它：&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;import { createDLX } from &apos;algorithm.ts/dlx&apos;

// 创建一个基于至多有 1000 个节点的舞蹈链的 dlx 算法
const dlx = createDLX(1000)

// 初始化 dlx 算法，总共有 1000 列
dlx.init(1000)

// 添加行，此处为伪代码
dlx.addRow(...)

// 尝试找到一个精确覆盖
dlx.solve()
&lt;/code&gt;&lt;/pre&gt;&lt;h2 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;求解数独问题&lt;/span&gt;&lt;/h2&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;数独是精确覆盖问题的一个特例。为了套用 DLX 算法框架，首先需要弄清楚如何构建 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;01&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;
矩阵。一般来说，可以将列对应成约束，而将行对应到策略，即选择某个策略时能够满足哪些约束。比如考虑经典 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;x^2 \times x^2&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 数独游戏，其有如下类型的约束：&lt;/span&gt;&lt;/p&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;Slot(a,b)&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;: 第 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 行第 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 列格子要有数字；&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;Row(a,b)&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;: 第 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 行要有数字 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;；&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;Col(a,b)&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;: 第 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 列要有数字 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;；&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;Sub(a,b)&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;: 第 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 个子方阵中要有方阵 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;；&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;一共有 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;x^2 \times x^2 \times 4&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 中约束。再考虑可选择的策略，即在第 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 行第 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;
列填入数字 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，且可以满足约束 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;Slot(r,c)&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;、&lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;Row(r,v)&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;、&lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;Col(c,v)&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;、
&lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\displaystyle Sub\left(\left\lfloor \frac{r}{x} \right\rfloor \times x + \left\lfloor \frac{c}{x} \right\rfloor,v\right)&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;。&lt;/span&gt;&lt;/p&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;代码如下：&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;import { createDLX } from &apos;./dlx&apos;

/**
 * @param SUDOKU_SIZE_SQRT 数独子方阵大小（即数独行数的平方根)
 */
export function createSudokuSolver(SUDOKU_SIZE_SQRT: number): SudokuSolver {
  const ebs = 1e-6
  const SUDOKU_SIZE = SUDOKU_SIZE_SQRT * SUDOKU_SIZE_SQRT
  const SUDOKU_SIZE_SQUARE = SUDOKU_SIZE * SUDOKU_SIZE
  const MAX_NODES = SUDOKU_SIZE_SQUARE * 4

  let codeA: number, codeB: number, codeC: number
  const columns: number[] = new Array(4)
  const solver = createDLX(MAX_NODES)
  return { solve }

  function solve(puzzle: number[][]): boolean {
    solver.init(MAX_NODES)
    for (let r = 0; r &amp;lt; SUDOKU_SIZE; ++r) {
      for (let c = 0; c &amp;lt; SUDOKU_SIZE; ++c) {
        const w = puzzle[r][c]

        // (r,c) 所属的子方阵编号
        const s =
          Math.floor(r / SUDOKU_SIZE_SQRT + ebs) * SUDOKU_SIZE_SQRT +
          Math.floor(c / SUDOKU_SIZE_SQRT + ebs)
        for (let v = 0; v &amp;lt; SUDOKU_SIZE; ++v) {
          if (w === -1 || w === v) {
            columns[0] = encode(SudokuConstraint.SLOT, r, c)
            columns[1] = encode(SudokuConstraint.ROW, r, v)
            columns[2] = encode(SudokuConstraint.COL, c, v)
            columns[3] = encode(SudokuConstraint.SUB, s, v)
            solver.addRow(encode(r, c, v), columns)
          }
        }
      }
    }

    const answer: number[] | null = solver.solve()
    if (answer === null) return false

    for (const code of answer) {
      decode(code)
      // eslint-disable-next-line no-param-reassign
      puzzle[codeA][codeB] = codeC
    }

    return true
  }

  function encode(a: number, b: number, c: number): number {
    return a * SUDOKU_SIZE_SQUARE + b * SUDOKU_SIZE + c + 1
  }

  function decode(code: number): void {
    let c = code - 1
    codeC = c % SUDOKU_SIZE

    c = Math.floor(c / SUDOKU_SIZE + ebs)
    codeB = c % SUDOKU_SIZE

    c = Math.floor(c / SUDOKU_SIZE + ebs)
    codeA = c
  }
}

/**
 * Sudoku constraints.
 */
export enum SudokuConstraint {
  SLOT = 0, // Slot(a, b) 表示第 a 行 b 列个格子上要有数字
  ROW = 1, // Row(a, b) 表示第 a 行要有数字 b
  COL = 2, // Col(a, b) 表示第 a 列要有数字 b
  SUB = 3, // Sub(a, b) 表示第 a 个子方阵要有数字 b
}

export interface SudokuSolver {
  /**
   * 数独的谜题格子，从 0 开始填，若某个格子未被填，则将其置为 -1
   * @param puzzle
   */
  solve(puzzle: number[][]): boolean
}
&lt;/code&gt;&lt;/pre&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;我把求解数独的算法封装在了 &lt;/span&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://github.com/guanghechen/algorithm.ts/blob/main/packages/sudoku/README-zh.md&quot; title=&quot;https://github.com/guanghechen/algorithm.ts/blob/main/packages/sudoku/README-zh.md&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;@algorithm.ts/sudoku&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt; 中，下面是求解 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;3^2 \times 3^2&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;
数独谜题的示例&lt;/span&gt;&lt;sup id=&quot;reference-footnote-5&quot; class=&quot;yozora-footnote-reference&quot;&gt;&lt;a href=&quot;#footnote-5&quot; title=&quot;5&quot;&gt;[5]&lt;/a&gt;&lt;/sup&gt;&lt;span class=&quot;yozora-text&quot;&gt;：&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;// import { SudokuSolver, createSudokuBoardData } from &apos;@algorithm.ts/sudoku&apos;
const solver = new SudokuSolver({ childMatrixWidth: 3 })

render()

function SudokuLiveSolver() {
  const [puzzle, setPuzzle] = React.useState(() =&amp;gt; ([
     5,  0,  6,  7,  8, -1, -1,  3,  2,
     1,  4,  2,  3, -1, -1,  8,  7,  6,
     7,  3, -1, -1, -1, -1,  4,  0, -1,
     8,  7,  3,  0,  6, -1, -1,  4,  1,
     2, -1,  5,  1,  4,  3,  0,  8,  7,
    -1,  1, -1,  8, -1, -1, -1, -1, -1,
    -1,  8,  0, -1, -1, -1,  5,  1,  4,
    -1,  5,  1,  4,  0,  6, -1,  2,  8,
    -1, -1,  7,  5,  1,  8, -1,  6, -1
  ]))

  // Resolve sudoku puzzle.
  const solution = React.useMemo(() =&amp;gt; {
    // Solve a sudoku puzzle and write the result into the `solution` array.
    const solution = createSudokuBoardData(solver.size)
    solver.solve(puzzle, solution)
    return solution
  }, [puzzle])

  return (
    
  )
}
&lt;/code&gt;&lt;/pre&gt;&lt;h2 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;Related&lt;/span&gt;&lt;/h2&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;刘汝佳《算法竞赛入门经典──训练指南》 P406 6.3.3 精确覆盖问题和 DLX 算法&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div className=&quot;yozora-footnote-definition&quot;&gt;&lt;p className=&quot;yozora-footnote-definition__title yozora-paragraph&quot;&gt;&lt;a href=&quot;#reference-footnote-1&quot;&gt;&amp;uarr;&lt;/a&gt;&lt;span&gt;&amp;nbsp;[1]:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;div className=&quot;yozora-footnote-definition__content&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;需要注意的是，此处并没有构成递归删除，因为只有被选择的行才需要删除行上的其余列，而删除这些列时所删除的其它行并未被选择（被选择行的此时均已被删除），也就不会进一步删除它们的列了。&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div className=&quot;yozora-footnote-definition&quot;&gt;&lt;p className=&quot;yozora-footnote-definition__title yozora-paragraph&quot;&gt;&lt;a href=&quot;#reference-footnote-2&quot;&gt;&amp;uarr;&lt;/a&gt;&lt;span&gt;&amp;nbsp;[2]:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;div className=&quot;yozora-footnote-definition__content&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;每次删除时都是选择一个未被覆盖的列，然后枚举覆盖此列的行，之后删除该行上的其它列。即每次选取了一个集合 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，将目标集合中所有在 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 中出现的数字都删去，同时删除所有与 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 有非空交集的那些集合，也就是每次选择集合都保证了和之前已选择的集合间没有交集。而当目标集合为空时，说明所有数字都在已选择的集合中出现过了，也就是已选择的集合中无重叠、无遗漏的覆盖了目标集合中的所有数字。&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div className=&quot;yozora-footnote-definition&quot;&gt;&lt;p className=&quot;yozora-footnote-definition__title yozora-paragraph&quot;&gt;&lt;a href=&quot;#reference-footnote-3&quot;&gt;&amp;uarr;&lt;/a&gt;&lt;span&gt;&amp;nbsp;[3]:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;div className=&quot;yozora-footnote-definition__content&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;这里指的是虚拟列，即相当于目标集合中的未被覆盖的元素，对应于下文中将提到的舞蹈链中中的列虚拟节点，所以即便所有行都被删除，列虚拟节点构成的链表仍可能不为空。&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div className=&quot;yozora-footnote-definition&quot;&gt;&lt;p className=&quot;yozora-footnote-definition__title yozora-paragraph&quot;&gt;&lt;a href=&quot;#reference-footnote-4&quot;&gt;&amp;uarr;&lt;/a&gt;&lt;span&gt;&amp;nbsp;[4]:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;div className=&quot;yozora-footnote-definition__content&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;在使用数组模拟链表的实现中，虚拟节点用列号表示就行了。而舞蹈链的头指针有
&lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 表示即可。需要注意的是，舞蹈链中所有列号均为正整数。&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div className=&quot;yozora-footnote-definition&quot;&gt;&lt;p className=&quot;yozora-footnote-definition__title yozora-paragraph&quot;&gt;&lt;a href=&quot;#reference-footnote-5&quot;&gt;&amp;uarr;&lt;/a&gt;&lt;span&gt;&amp;nbsp;[5]:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;div className=&quot;yozora-footnote-definition__content&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;需要注意的是，&lt;/span&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://github.com/guanghechen/algorithm.ts/blob/main/packages/sudoku/README-zh.md&quot; title=&quot;https://github.com/guanghechen/algorithm.ts/blob/main/packages/sudoku/README-zh.md&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;@algorithm.ts/sudoku&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt; 中对于一个 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;x^2 \times x^2&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;
的数独，使用 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;[-1, x^2)&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 之间的整数作为数独格子的值，其中 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;-1&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 代表对应格子未预置值&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/main&gt;&lt;footer&gt;&lt;div class=&quot;yozora-footnote-definitions&quot;&gt;&lt;div class=&quot;yozora-footnote-definitions__title&quot;&gt;footnote-definitions&lt;/div&gt;&lt;ul class=&quot;yozora-footnote-definitions__main&quot;&gt;&lt;div className=&quot;yozora-footnote-definition&quot;&gt;&lt;p className=&quot;yozora-footnote-definition__title yozora-paragraph&quot;&gt;&lt;a href=&quot;#reference-footnote-1&quot;&gt;&amp;uarr;&lt;/a&gt;&lt;span&gt;&amp;nbsp;[1]:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;div className=&quot;yozora-footnote-definition__content&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;需要注意的是，此处并没有构成递归删除，因为只有被选择的行才需要删除行上的其余列，而删除这些列时所删除的其它行并未被选择（被选择行的此时均已被删除），也就不会进一步删除它们的列了。&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div className=&quot;yozora-footnote-definition&quot;&gt;&lt;p className=&quot;yozora-footnote-definition__title yozora-paragraph&quot;&gt;&lt;a href=&quot;#reference-footnote-2&quot;&gt;&amp;uarr;&lt;/a&gt;&lt;span&gt;&amp;nbsp;[2]:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;div className=&quot;yozora-footnote-definition__content&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;每次删除时都是选择一个未被覆盖的列，然后枚举覆盖此列的行，之后删除该行上的其它列。即每次选取了一个集合 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，将目标集合中所有在 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 中出现的数字都删去，同时删除所有与 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 有非空交集的那些集合，也就是每次选择集合都保证了和之前已选择的集合间没有交集。而当目标集合为空时，说明所有数字都在已选择的集合中出现过了，也就是已选择的集合中无重叠、无遗漏的覆盖了目标集合中的所有数字。&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div className=&quot;yozora-footnote-definition&quot;&gt;&lt;p className=&quot;yozora-footnote-definition__title yozora-paragraph&quot;&gt;&lt;a href=&quot;#reference-footnote-3&quot;&gt;&amp;uarr;&lt;/a&gt;&lt;span&gt;&amp;nbsp;[3]:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;div className=&quot;yozora-footnote-definition__content&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;这里指的是虚拟列，即相当于目标集合中的未被覆盖的元素，对应于下文中将提到的舞蹈链中中的列虚拟节点，所以即便所有行都被删除，列虚拟节点构成的链表仍可能不为空。&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div className=&quot;yozora-footnote-definition&quot;&gt;&lt;p className=&quot;yozora-footnote-definition__title yozora-paragraph&quot;&gt;&lt;a href=&quot;#reference-footnote-4&quot;&gt;&amp;uarr;&lt;/a&gt;&lt;span&gt;&amp;nbsp;[4]:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;div className=&quot;yozora-footnote-definition__content&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;在使用数组模拟链表的实现中，虚拟节点用列号表示就行了。而舞蹈链的头指针有
&lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 表示即可。需要注意的是，舞蹈链中所有列号均为正整数。&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div className=&quot;yozora-footnote-definition&quot;&gt;&lt;p className=&quot;yozora-footnote-definition__title yozora-paragraph&quot;&gt;&lt;a href=&quot;#reference-footnote-5&quot;&gt;&amp;uarr;&lt;/a&gt;&lt;span&gt;&amp;nbsp;[5]:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;div className=&quot;yozora-footnote-definition__content&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;需要注意的是，&lt;/span&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://github.com/guanghechen/algorithm.ts/blob/main/packages/sudoku/README-zh.md&quot; title=&quot;https://github.com/guanghechen/algorithm.ts/blob/main/packages/sudoku/README-zh.md&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;@algorithm.ts/sudoku&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt; 中对于一个 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;x^2 \times x^2&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;
的数独，使用 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;[-1, x^2)&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 之间的整数作为数独格子的值，其中 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;-1&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 代表对应格子未预置值&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/footer&gt;&lt;/section&gt;</content:encoded></item><item><title><![CDATA[洗牌问题和 knuth-shuffle 算法]]></title><description><![CDATA[<section class="yozora-markdown"><main><p class="yozora-paragraph"><span class="yozora-text">对于给定的 </span><span class="yozora-inline-math">N</span><span class="yozora-text"> 个数，将其公平地随机打乱，使得每个位置上每个数出现的概率相等。</span></p><h2 class="yozora-heading"><span class="yozora-text">约瑟夫环</span></h2><p class="yozora-paragraph"><span class="yozora-text">遍历位置 </span><span class="yozora-inline-math">i</span><span class="yozora-text">，每次从剩余的数中随机取一个放到该位置上。由此可以将问题归约到
</span><span class="yozora-text">，即：</span><span class="yozora-inline-math">N</span><span class="yozora-text"> 个人围坐一圈，从 </span><span class="yozora-inline-math">1</span><span class="yozora-text"> 开始报数，每次随机一个数 </span><span class="yozora-inline-math">x</span><span class="yozora-text">，报到 </span><span class="yozora-inline-math">x</span><span class="yozora-text"> 的人从圈中离开，然后进入下一轮游戏。因为需要求离开的顺序，所以不能使用递推法，只能用树状数组+二分，复杂度为：</span></p></main><footer><div class="yozora-footnote-definitions"><div class="yozora-footnote-definitions__title">footnote-definitions</div><ul class="yozora-footnote-definitions__main"></ul></div></footer></section>]]></description><link>https://me.guanghechen.com/post/algorithm/shuffle/</link><guid isPermaLink="false">https://me.guanghechen.com/post/algorithm/shuffle/</guid><content:encoded>&lt;section class=&quot;yozora-markdown&quot;&gt;&lt;main&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;对于给定的 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 个数，将其公平地随机打乱，使得每个位置上每个数出现的概率相等。&lt;/span&gt;&lt;/p&gt;&lt;h2 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;约瑟夫环&lt;/span&gt;&lt;/h2&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;遍历位置 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，每次从剩余的数中随机取一个放到该位置上。由此可以将问题归约到
&lt;/span&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;/post/quiz/classical/Josephus-ring/#heading-%E4%B8%8D%E5%AE%9A%E6%AD%A5%E9%95%BF%E7%9A%84%E7%BA%A6%E7%91%9F%E5%A4%AB%E9%97%AE%E9%A2%98&quot; title=&quot;/post/quiz/classical/Josephus-ring/#heading-%E4%B8%8D%E5%AE%9A%E6%AD%A5%E9%95%BF%E7%9A%84%E7%BA%A6%E7%91%9F%E5%A4%AB%E9%97%AE%E9%A2%98&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;不定步长约瑟夫环问题&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;，即：&lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 个人围坐一圈，从 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 开始报数，每次随机一个数 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，报到 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 的人从圈中离开，然后进入下一轮游戏。因为需要求离开的顺序，所以不能使用递推法，只能用树状数组+二分，复杂度为：&lt;/span&gt;&lt;/p&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;时间复杂度： &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;O(N \cdot \log^2 N)&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;额外空间复杂度： &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;O(N)&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;knuth-shuffle&lt;/span&gt;&lt;/h2&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;进一步考虑，在 shuffle 问题中，我们并不需要保证每个“人”的相对位置不变，也不必每次从上一个被踢的位置开始继续报数，而是只需要保证剩下的“人”被选出来的概率相同即可；如果能让剩下的人始终紧密地聚集到一起，那么利用数组的索引特性，每次 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;O(1)&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 即可选出该轮应该被踢出的“人”。&lt;/span&gt;&lt;/p&gt;&lt;h3 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;算法描述&lt;/span&gt;&lt;/h3&gt;&lt;ol class=&quot;yozora-list&quot; start=&quot;1&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;从第 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 个位置（最右侧）开始向左遍历位置 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;i&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;每次从最左侧到当前位置 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 上的所有数中随机选择一个数和当前位置的数进行交换&lt;/span&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;应用上述算法，每个数字出现的概率为：&lt;/span&gt;&lt;/p&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;第 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 个位置上，每个数出现的概率为：&lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\displaystyle \frac{1}{N}&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;第 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;N-1&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 个位置上，每个数出现的概率为：&lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\displaystyle \frac{N-1}{N} \times \frac{1}{N-1}=\frac{1}{N}&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;...&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;第 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 个位置上，每个数出现的概率为：&lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\displaystyle \frac{N-1}{N} \times \frac{N-2}{N-1} \times \cdots \times \frac{1}{N-x}=\frac{1}{N}&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;...&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h3 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;程序实现&lt;/span&gt;&lt;/h3&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;时间复杂度： &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;O(N)&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;额外空间复杂度： &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;O(1)&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;export function knuthShuffle(nodes: T[]): void {
  const N = nodes.length
  for (let i = N - 1, j: number, x: T; i &amp;gt; 0; --i) {
    j = Math.floor(Math.random() * (i + 1))
    x = nodes[i]
    nodes[i] = nodes[j]
    nodes[j] = x
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/main&gt;&lt;footer&gt;&lt;div class=&quot;yozora-footnote-definitions&quot;&gt;&lt;div class=&quot;yozora-footnote-definitions__title&quot;&gt;footnote-definitions&lt;/div&gt;&lt;ul class=&quot;yozora-footnote-definitions__main&quot;&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/footer&gt;&lt;/section&gt;</content:encoded></item><item><title><![CDATA[统计区间内的线段]]></title><description><![CDATA[<section class="yozora-markdown"><main><h2 class="yozora-heading"><span class="yozora-text">一维区间</span></h2><h3 class="yozora-heading"><span class="yozora-text">问题简述</span></h3><p class="yozora-paragraph"><span class="yozora-text">坐标轴上存在 </span><span class="yozora-inline-math">N</span><span class="yozora-text"> 条线段，有 </span><span class="yozora-inline-math">Q</span><span class="yozora-text"> 次查询，每次查询询问与给定区间 </span><span class="yozora-inline-math">[a_q, b_q]</span><span class="yozora-text"> 相交的线段数量。（相交指存在重叠的区域，包括</span><strong class="yozora-strong"><span class="yozora-text">相互包含</span></strong><span class="yozora-text">的情况）</span></p><h3 class="yozora-heading"><span class="yozora-text">问题简析</span></h3><p class="yozora-paragraph"><span class="yozora-text">先对线段进行排序：左端点小的排在前面，左端点相同时，右端点小的排在前面。</span></p><p class="yozora-paragraph"><span class="yozora-text">记 </span><span class="yozora-inline-math">f(x)</span><span class="yozora-text"> 为左端点在点 </span><span class="yozora-inline-math">x</span><span class="yozora-text"> 上的线段条数，</span></p></main><footer><div class="yozora-footnote-definitions"><div class="yozora-footnote-definitions__title">footnote-definitions</div><ul class="yozora-footnote-definitions__main"></ul></div></footer></section>]]></description><link>https://me.guanghechen.com/post/quiz/scanning-line/segments/</link><guid isPermaLink="false">https://me.guanghechen.com/post/quiz/scanning-line/segments/</guid><content:encoded>&lt;section class=&quot;yozora-markdown&quot;&gt;&lt;main&gt;&lt;h2 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;一维区间&lt;/span&gt;&lt;/h2&gt;&lt;h3 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;问题简述&lt;/span&gt;&lt;/h3&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;坐标轴上存在 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 条线段，有 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;Q&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 次查询，每次查询询问与给定区间 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;[a_q, b_q]&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 相交的线段数量。（相交指存在重叠的区域，包括&lt;/span&gt;&lt;strong class=&quot;yozora-strong&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;相互包含&lt;/span&gt;&lt;/strong&gt;&lt;span class=&quot;yozora-text&quot;&gt;的情况）&lt;/span&gt;&lt;/p&gt;&lt;h3 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;问题简析&lt;/span&gt;&lt;/h3&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;先对线段进行排序：左端点小的排在前面，左端点相同时，右端点小的排在前面。&lt;/span&gt;&lt;/p&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;记 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;f(x)&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 为左端点在点 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 上的线段条数，&lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;g(x)&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 为右端点在 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 上的线段条数。计算 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;f(x)&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 和 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;g(x)&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 可以通过扫描排好序的线段，不妨记当前扫描到的线段为 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;[x1, x2]&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，并执行下述操作：&lt;/span&gt;&lt;/p&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;f(x1) = f(x1) + 1&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;g(x2) = g(x2) + 1&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;在扫描的过程中，不难有：&lt;/span&gt;&lt;/p&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;区间左侧 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;[-\infty, a_q-1]&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 与当前区间不相交的数量为线段右端点个数，即
&lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\displaystyle \sum_{x=-\infty}^{a_q - 1} f(x)&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;；&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;区间右侧 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;[b_q + 1, +\infty]&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 与当前区间不相交的数量为线段左端点个数，即
&lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\displaystyle \sum_{x=b_q + 1}^{+\infty} g(x)&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;；&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;由容斥定理可知，与区间 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;[a_q, b_q]&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 相交的线段数为&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-math&quot;&gt;N - \left ( \sum_{x=-\infty}^{a_q - 1} f(x) + \sum_{x=b_q + 1}^{+\infty} g(x)\right )
&lt;/div&gt;&lt;h3 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;小结&lt;/span&gt;&lt;/h3&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;对区间进行排序（或标号），再维护两个前缀和就可以了。&lt;/span&gt;&lt;/p&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;时间复杂度： &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;O(N \log N + N)&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;空间复杂度： &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;O(N)&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;二维区间（离线查询）&lt;/span&gt;&lt;/h2&gt;&lt;h3 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;问题描述&lt;/span&gt;&lt;/h3&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;二维坐标系中存在 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 条与坐标轴垂直或平行的线段；有 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;Q&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 次查询，每次查询询问与给定矩形窗口 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;W_q = \big\lbrace A_q(x1_q, y1_q), B_q(x2_q, y2_q) \big\rbrace&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 相交的线段数量。其中，矩形窗口的边与坐标轴平行或垂直，且 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;A_q&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 为矩形的左下角顶点，
&lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;B_q&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 为矩形的右上角对顶点。（相交指存在重叠的区域，包括&lt;/span&gt;&lt;strong class=&quot;yozora-strong&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;相互包含&lt;/span&gt;&lt;/strong&gt;&lt;span class=&quot;yozora-text&quot;&gt;的情况）&lt;/span&gt;&lt;/p&gt;&lt;h3 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;问题简析&lt;/span&gt;&lt;/h3&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;先仅考虑与 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 轴平行的线段与 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;W_q&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 相交情况。假设与 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 轴平行的线段有 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;N_y&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 条。记：&lt;/span&gt;&lt;/p&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;f(y)&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 为上端点在点 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 上的线段条数；&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;g(y)&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 为下端点在 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 上的线段条数，初始时 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;f(y)=g(y)=0&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;；&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;h1(q)&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 表示窗口 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 的左边界之左（不包括左边界）与区间 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;[y1_q,y2_q]&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 相交的线段数，&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;h2(q)&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 表示窗口 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 的右边界之左（包括右边界）与区间 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;[y1_q,y2_q]&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 相交的线段数；&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;不难发现 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;h2(q) - h1(q)&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 即为与 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 轴平行的线段中与 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;W_q&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 相交的数量。&lt;/span&gt;&lt;/p&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;将所有的窗口按照左边界 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 坐标从小到大排序，并维护一个单调队列 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;S&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;；遍历排好序的窗口 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;W_i = \big\lbrace A_i(x1_i, y1_i), B_i(x2_i, y2_i) \big\rbrace&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;。&lt;/span&gt;&lt;/p&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;若 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;S&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 中存在窗口（必在队首） &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;W_k = \big\lbrace A_k(x1_k, y1_k), B_k(x2_k, y2_k) \big\rbrace&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;
满足 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;x2_k &amp;lt; x1_i&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，执行下述操作，直到队列中没有这样的 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;W_k&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;：&lt;/span&gt;&lt;/p&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;取出队首元素 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;W_k&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;；&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;等待所有未操作过的且 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 坐标小于等于 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;x2_k&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 的线段计入到 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;f, g&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 中；&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;计算此时 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 坐标在 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;x2_k&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 左侧的线段中，与区间 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;[y1_k, y2_k]&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 相交的垂直线段条数：&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-math&quot;&gt;h_2(k) = N_y - \left( \sum_{y=-\infty}^{y1_k - 1} f(y) + \sum_{y=y2_k + 1}^{+\infty} g(x) \right)
&lt;/div&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;则此时 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;h_2(k) - h_1(k)&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 即为与 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 轴平行的线段中与 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;W_k&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 相交的数量。&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;将 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;W_i&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 丢进 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;S&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 队尾，并执行下述操作：&lt;/span&gt;&lt;/p&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;等待所有未操作过的且 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 坐标小于 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;x1_i&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 的线段计入到 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;f, g&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 中；&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;计算此时 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 坐标在 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;x1_i&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 左侧的线段中，与区间 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;[y1_i, y2_i]&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 相交的垂直线段条数：&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-math&quot;&gt;h_1(i) = N_y - \left( \sum_{y=-\infty}^{y1_i - 1} f(y) + \sum_{y=y2_i + 1}^{+\infty} g(x) \right)
&lt;/div&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;继续遍历排好序的窗口 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;W_i&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;；&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;类似地，可得到与窗口 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 相交的水平线段的数量，做一次相加即可得到答案。&lt;/span&gt;&lt;/p&gt;&lt;h3 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;小结&lt;/span&gt;&lt;/h3&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;以统计窗口与平行于 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 轴方向上的线段相交的数量为例，算法的思路是将当前需要考虑的线段限定在目标边界以左，则此时问题归约为垂直方向上统计某个区间内的线段数量，而这个区间内的线段有可能在目标窗口的左边界以左，但只使用一种类似前缀和的思想（参见前文中关于 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;h_1&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 和 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;h_2&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 的定义）将这部分线段再减去即可。&lt;/span&gt;&lt;/p&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;和一维问题的区别在于多了一个动态计入 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;f,g&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 的步骤，因此可归约到【单点更新，区间求和】问题，维护一棵线段树或树状数组即可。&lt;/span&gt;&lt;/p&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;时间复杂度： &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;O(N \log N)&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;空间复杂度： &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;O(N)&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;二维区间（在线查询）&lt;/span&gt;&lt;/h2&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;和上一个问题类似，不同的是，每次查询需要立刻给出答案，即我们无法再对窗口进行排序了。&lt;/span&gt;&lt;/p&gt;&lt;h3 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;问题简析&lt;/span&gt;&lt;/h3&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;在离线查询的算法中，因为将查询窗口进行排序，所以每次计算窗口的左右边界时，天然地保证了当前考虑过的线段全在边界以左。如果我们增加一个额外的维度维护 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 轴的信息，即能够快速地查询在某个 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 轴左侧的线段的分布情况，则可解决本问题。&lt;/span&gt;&lt;/p&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;不难想到可以建立一棵主席树（可持久化线段树），每个版本为只包含某个特定的 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 轴以左的线段下 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 和 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 的值，则查询时可以先查询到版本，然后再查询垂直方向上的线段相交情况。此外再建立线段树时，仅需考虑所有与 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 轴平行的线段的 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 值即可，之后给定了窗口后，可通过二分确定主席树中的版本号。&lt;/span&gt;&lt;/p&gt;&lt;h3 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;小结&lt;/span&gt;&lt;/h3&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;和离线查询的版本相比，把线段树换成了主席树，因此无论时间复杂度还是空间复杂度都需要再乘一个 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\log N&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;。此外，因为确定窗口边界所对应的主席树所对应的版本号需要进行一次二分，因此时间复杂度还需要再乘一个 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\log N&lt;/span&gt;&lt;/p&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;时间复杂度： &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;O(N \log^3 N)&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;空间复杂度： &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;O(N \log N)&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/main&gt;&lt;footer&gt;&lt;div class=&quot;yozora-footnote-definitions&quot;&gt;&lt;div class=&quot;yozora-footnote-definitions__title&quot;&gt;footnote-definitions&lt;/div&gt;&lt;ul class=&quot;yozora-footnote-definitions__main&quot;&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/footer&gt;&lt;/section&gt;</content:encoded></item><item><title><![CDATA[约瑟夫环问题]]></title><description><![CDATA[<section class="yozora-markdown"><main><h2 class="yozora-heading"><span class="yozora-text">经典约瑟夫环问题</span></h2><p class="yozora-paragraph"><span class="yozora-text">有 </span><span class="yozora-inline-math">N</span><span class="yozora-text"> 个人围坐成一圈，任选某个人将其编号为 </span><span class="yozora-inline-math">0</span><span class="yozora-text">，其右手边的人编号为 </span><span class="yozora-inline-math">1</span><span class="yozora-text">，以此类推，将全部 </span><span class="yozora-inline-math">N</span><span class="yozora-text"> 个人进行唯一编号。由编号为 </span><span class="yozora-inline-math">0</span><span class="yozora-text"> 的人从 </span><span class="yozora-inline-math">1</span><span class="yozora-text"> 开始报数，其右手边的人报下一个数，以此类推。报到 </span><span class="yozora-inline-math">M</span><span class="yozora-text"> 的人起身离开，TA 右手边的人又从 </span><span class="yozora-inline-math">1</span><span class="yozora-text"> 开始报数，直到所有人起身离开，求最后一个起身离开的人的编号。</span></p></main><footer><div class="yozora-footnote-definitions"><div class="yozora-footnote-definitions__title">footnote-definitions</div><ul class="yozora-footnote-definitions__main"></ul></div></footer></section>]]></description><link>https://me.guanghechen.com/post/quiz/classical/Josephus-ring/</link><guid isPermaLink="false">https://me.guanghechen.com/post/quiz/classical/Josephus-ring/</guid><content:encoded>&lt;section class=&quot;yozora-markdown&quot;&gt;&lt;main&gt;&lt;h2 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;经典约瑟夫环问题&lt;/span&gt;&lt;/h2&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;有 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 个人围坐成一圈，任选某个人将其编号为 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，其右手边的人编号为 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，以此类推，将全部 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 个人进行唯一编号。由编号为 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 的人从 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 开始报数，其右手边的人报下一个数，以此类推。报到 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;M&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 的人起身离开，TA 右手边的人又从 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 开始报数，直到所有人起身离开，求最后一个起身离开的人的编号。&lt;/span&gt;&lt;/p&gt;&lt;h3 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;暴力法&lt;/span&gt;&lt;/h3&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;使用链表进行模拟，起身离开的复杂度为 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;O(1)&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，报数的复杂度为 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;O(M)&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，故总复杂度为：&lt;/span&gt;&lt;/p&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;时间复杂度： &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;O(M \cdot N)&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;额外空间复杂度： &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;O(N)&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h3 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;树状数组&lt;/span&gt;&lt;/h3&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;用一个数组 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 表示这 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 个人当前的游戏状态：&lt;/span&gt;&lt;/p&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;A[i] = 1&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;: 编号为 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 的人还坐在那里&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;A[i] = 0&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;: 编号为 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 的人已经起身离开&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;则可以使用树状数组维护前缀和&lt;/span&gt;&lt;sup id=&quot;reference-footnote-1&quot; class=&quot;yozora-footnote-reference&quot;&gt;&lt;a href=&quot;#footnote-1&quot; title=&quot;1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;&lt;span class=&quot;yozora-text&quot;&gt;，报数相当于快速找到某个指定位置开始的区间 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;[l,r]&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，使得
&lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\displaystyle \sum_{i=l}^r A[i] = M&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;.&lt;/span&gt;&lt;/p&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;在前期 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 数组中 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 的密度浓的时候，单次报数的复杂度接近于 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;O(\log N)&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; （即少数几次树状数组的查询）；但在游戏后期，&lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 数组中密度低的时候，单次报数的复杂度可能退化为 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;O(M \log N)&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; ，故总复杂度为：&lt;/span&gt;&lt;/p&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;时间复杂度（宽松上界）： &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;O(M \cdot \min\{N, M\} \cdot \log N)&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;额外空间复杂度： &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;O(N)&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;import { BinarySearchTree, createBinarySearchTree } from &apos;./bit&apos;

export function lastRemaining(N: number, M: number): number {
  if (N &amp;lt;= 0) return -1
  if (N === 1) return 1

  const bit: BinarySearchTree = createBinarySearchTree(N)
  for (let i = 1; i &amp;lt;= N; ++i) bit.add(i, 1)

  let p = 0
  for (let n = N; n &amp;gt; 0; --n) {
    let total = M % n
    for (total = total === 0 ? M : total; total &amp;gt; 0; ) {
      const _end = Math.min(N, p + total)
      total -= bit.sum(_end) - bit.sum(p)
      p = _end === N ? 0 : _end
    }
    bit.add(p === 0 ? N : p, -1)
  }
  return (p + N - 1) % N
}
&lt;/code&gt;&lt;/pre&gt;&lt;h3 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;树状数组 + 二分&lt;/span&gt;&lt;/h3&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;为了快速跳过中间的 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，我们可以通过二分的方式快速确定下一次树状数组需要查询的位置，则单次报数的复杂度为 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;O(\log^2 N)&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，总复杂度为：&lt;/span&gt;&lt;/p&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;时间复杂度： &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;O(N \cdot \log^2 N)&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;额外空间复杂度： &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;O(N)&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;下面的代码中假定当三分之一的人起身离开后，&lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 数组中 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 密度足够多，即阈值为
&lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\frac{2N}{3}&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，（可以自己调试阈值）。当人数少于阈值后，开始使用二分确定下一步查询的位置。实践证明优化后的代码比优化前快了一倍多。&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;import { BinarySearchTree, createBinarySearchTree } from &apos;./bit&apos;

export function lastRemaining(N: number, M: number): number {
  if (N &amp;lt;= 0) return -1
  if (N === 1) return 1

  const bit: BinarySearchTree = createBinarySearchTree(N)
  for (let i = 1; i &amp;lt;= N; ++i) bit.add(i, 1)

  let p = 0
  for (let n = N, threshold = Math.round((N * 2) / 3); n &amp;gt; 0; --n) {
    let total = M % n
    for (total = total === 0 ? M : total; total &amp;gt; 0; ) {
      let _end = p + total
      const target = total + bit.sum(p)

      if (n &amp;lt; threshold) {
        if (_end &amp;lt; N) {
          let lft = _end
          let rht = N + 1
          while (lft &amp;lt; rht) {
            const mid = (lft + rht) &amp;gt;&amp;gt; 1
            if (bit.sum(mid) &amp;lt; target) lft = mid + 1
            rht = mid
          }
          _end = lft &amp;lt; N ? lft : N
        }
      }

      if (_end &amp;gt; N) _end = N
      total = target - bit.sum(_end)
      p = _end === N ? 0 : _end
    }
    bit.add(p === 0 ? N : p, -1)
  }
  return (p + N - 1) % N
}
&lt;/code&gt;&lt;/pre&gt;&lt;h3 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;递推&lt;/span&gt;&lt;/h3&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;考虑这样一种操作：每次有人起身离开时，将TA右手边的人重新编号为 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，右手边的右手边的人重新编号为 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，……，以此类推。&lt;/span&gt;&lt;/p&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;为方便表述，不妨记圈中剩余 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 个人时，即将起身离开的人在此轮中的编号为 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;h(n)&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，在上一轮的编号为 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;h&apos;(n)&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;。根据游戏规则，不难有：当某轮游戏中只剩下 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 个人时，即将起身离开的人在这一轮中的编号为 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;h(n) = M - 1 \mod n&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;；下一轮进行重新编号时，将此时编号为 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;h(n) + k \mod n&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; （其中 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;1 \leqslant k &amp;lt; n&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;）的人重新编号为 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;k-1&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;。特别地，当 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;k=M&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 时，根据映射关系有：&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-math&quot;&gt;h(n) + M \mod n \equiv (M - 1) \mod (n - 1) \quad\rightarrow\quad h(n-1)
&lt;/div&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;即 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;h&apos;(n-1) \equiv h(n) + M \mod n&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，也就是：游戏进行到剩余 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;n-1&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 个人时，即将离开的人其在上一轮的编号为当前编号 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;+M&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 再对 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 取模。而当游戏仅剩下一个人时，此时离开的人即为原问题中最后一个离开的人，也就是我们只要求出 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;h(1)&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 在游戏初始时的编号就可以了。&lt;/span&gt;&lt;/p&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;不妨设 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;f(n)&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 表示原问题中最后一个起身离开的人在剩下 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 个人时那一轮游戏中的编号，则所求答案为 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;f(N)&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;。根据上述分析不难得到递推方程：&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-math&quot;&gt;  f(n) = \left\lbrace \begin{aligned}
    &amp;amp;0, &amp;amp;n = 1\\ 
    &amp;amp;f(n - 1) + M \mod n, &amp;amp;n &amp;gt; 1\\
  \end{aligned}\right.
&lt;/div&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;由 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 的定义可以看出来，递推法求解约瑟夫环问题可以在 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;O(N)&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 的复杂度下求出最后一个离开的人的编号，但不能求出整个游戏中的离场顺序。若要求出倒数第二个人离场的人的编号，只要改一下递推方程的初始条件就好了：&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-math&quot;&gt;  f(n) = \left\lbrace \begin{aligned}
    &amp;amp;M + 1 \mod n, &amp;amp;n = 2\\ 
    &amp;amp;f(n - 1) + M \mod n, &amp;amp;n &amp;gt; 1\\
  \end{aligned}\right.
&lt;/div&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;递推写法&lt;/span&gt;&lt;/p&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;时间复杂度： &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;O(N)&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;额外空间复杂度： &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;O(1)&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;export function lastRemaining(N: number, M: number): number {
  if (N &amp;lt;= 0) return -1
  let ans = 0
  for (let n = 2; n &amp;lt;= N; ++n) ans = (ans + M) % n
  return ans
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;递归写法&lt;/span&gt;&lt;/p&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;时间复杂度： &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;O(N)&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;额外空间复杂度： &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;O(N)&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;export function lastRemaining(N: number, M: number): number {
  if (N &amp;lt;= 0) return -1
  return round(N)

  function round(n: number): number {
    if (n === 1) return 0
    const y = round(n - 1)
    return (y + M) % n
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;不定步长的约瑟夫问题&lt;/span&gt;&lt;/h2&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;在经典约瑟夫问题的基础上，每次起身离开的人报的的数不同。其中第 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 个起身的人需要报到的数为 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;M_i&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;。&lt;/span&gt;&lt;/p&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;不难发现，前面提到的 &lt;/span&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;#heading-%E6%9A%B4%E5%8A%9B%E6%B3%95&quot; title=&quot;#heading-%E6%9A%B4%E5%8A%9B%E6%B3%95&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;暴力法&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;、&lt;/span&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;#heading-%E6%A0%91%E7%8A%B6%E6%95%B0%E7%BB%84&quot; title=&quot;#heading-%E6%A0%91%E7%8A%B6%E6%95%B0%E7%BB%84&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;前缀和-树状数组&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt; 算法仍然适用。这里仅就 &lt;/span&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;#heading-%E9%80%92%E6%8E%A8&quot; title=&quot;#heading-%E9%80%92%E6%8E%A8&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;递推法&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt; 进行讨论。&lt;/span&gt;&lt;/p&gt;&lt;h3 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;递推&lt;/span&gt;&lt;/h3&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;对比游戏规则，不难想到上文中提到的映射关系&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-math&quot;&gt;h(n) + M \mod n \equiv (M - 1) \mod (n - 1) \quad\rightarrow\quad h(n-1)
&lt;/div&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;需要更新为&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-math&quot;&gt;h(n) + M_{N-n} \mod n \equiv (M_{N-n} - 1) \mod (n - 1) \quad\rightarrow\quad h(n-1)
&lt;/div&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;则不难得到新的递推方程：&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-math&quot;&gt;  f(n) = \left\lbrace \begin{aligned}
    &amp;amp;0, &amp;amp;n = 1\\ 
    &amp;amp;f(n - 1) + M_{N-n} \mod n, &amp;amp;n &amp;gt; 1\\
  \end{aligned}\right.
&lt;/div&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;递推写法&lt;/span&gt;&lt;/p&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;时间复杂度： &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;O(N)&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;额外空间复杂度： &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;O(1)&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;export function lastRemaining(N: number, M: number[]): number {
  if (N &amp;lt;= 0) return -1
  let ans = 0
  for (let n = 2; n &amp;lt;= N; ++n) ans = (ans + M[N - n]) % n
  return ans
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;递归写法&lt;/span&gt;&lt;/p&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;时间复杂度： &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;O(N)&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;额外空间复杂度： &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;O(N)&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;export function lastRemaining(N: number, M: number[]): number {
  if (N &amp;lt;= 0) return -1
  return round(N)

  function round(n: number): number {
    if (n === 1) return 0
    const y = round(n - 1)
    return (y + M[N - n]) % n
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div className=&quot;yozora-footnote-definition&quot;&gt;&lt;p className=&quot;yozora-footnote-definition__title yozora-paragraph&quot;&gt;&lt;a href=&quot;#reference-footnote-1&quot;&gt;&amp;uarr;&lt;/a&gt;&lt;span&gt;&amp;nbsp;[1]:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;div className=&quot;yozora-footnote-definition__content&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;为了方便树状数组操作，处理时将所有人的编号 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;+1&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，在计算答案时再 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;-1&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/main&gt;&lt;footer&gt;&lt;div class=&quot;yozora-footnote-definitions&quot;&gt;&lt;div class=&quot;yozora-footnote-definitions__title&quot;&gt;footnote-definitions&lt;/div&gt;&lt;ul class=&quot;yozora-footnote-definitions__main&quot;&gt;&lt;div className=&quot;yozora-footnote-definition&quot;&gt;&lt;p className=&quot;yozora-footnote-definition__title yozora-paragraph&quot;&gt;&lt;a href=&quot;#reference-footnote-1&quot;&gt;&amp;uarr;&lt;/a&gt;&lt;span&gt;&amp;nbsp;[1]:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;div className=&quot;yozora-footnote-definition__content&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;为了方便树状数组操作，处理时将所有人的编号 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;+1&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，在计算答案时再 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;-1&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/footer&gt;&lt;/section&gt;</content:encoded></item><item><title><![CDATA[剑指offer 解题报告]]></title><description><![CDATA[<section class="yozora-markdown"><main><h2 class="yozora-heading"><span class="yozora-text">前言</span></h2><p class="yozora-paragraph"><span class="yozora-text">这两天奶奶做手术，我在医院里陪护，奶奶不会说普通话所以只好一直陪在旁边。实在有些闲得慌，正好在刷知乎的时候看到了《剑指offer》的推荐，搜了下发现有 OJ 收录了原书中的题目，于是开始尝试手机写代码23333（没有带电脑）。 强迫症晚期，一上手就非要做完不可，还好题目量少。使用低效率的方式做一件并不紧要的事情实在是太浪费生命了。（不过也有人说，生命就是用来浪费的2333</span></p></main><footer><div class="yozora-footnote-definitions"><div class="yozora-footnote-definitions__title">footnote-definitions</div><ul class="yozora-footnote-definitions__main"></ul></div></footer></section>]]></description><link>https://me.guanghechen.com/post/acm/oj/nowcoder/jz-offer/</link><guid isPermaLink="false">https://me.guanghechen.com/post/acm/oj/nowcoder/jz-offer/</guid><content:encoded>&lt;section class=&quot;yozora-markdown&quot;&gt;&lt;main&gt;&lt;h2 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;前言&lt;/span&gt;&lt;/h2&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;这两天奶奶做手术，我在医院里陪护，奶奶不会说普通话所以只好一直陪在旁边。实在有些闲得慌，正好在刷知乎的时候看到了《剑指offer》的推荐，搜了下发现有 OJ 收录了原书中的题目，于是开始尝试手机写代码23333（没有带电脑）。 强迫症晚期，一上手就非要做完不可，还好题目量少。使用低效率的方式做一件并不紧要的事情实在是太浪费生命了。（不过也有人说，生命就是用来浪费的2333&lt;/span&gt;&lt;/p&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;为了&lt;/span&gt;&lt;del class=&quot;yozora-delete&quot;&gt;&lt;em class=&quot;yozora-emphasis&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;偷懒&lt;/span&gt;&lt;/em&gt;&lt;/del&gt;&lt;span class=&quot;yozora-text&quot;&gt;节约篇幅，将过于基础的题放在了 &lt;/span&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;#heading-%E9%99%84%E5%BD%95&quot; title=&quot;#heading-%E9%99%84%E5%BD%95&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;附录&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt; 中。&lt;/span&gt;&lt;/p&gt;&lt;h2 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;01 二维数组的查找&lt;/span&gt;&lt;/h2&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.nowcoder.com/practice/abc3fe2ce8e146608e868a70efebf62e&quot; title=&quot;https://www.nowcoder.com/practice/abc3fe2ce8e146608e868a70efebf62e&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;题目链接&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;h3 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;题意简述&lt;/span&gt;&lt;/h3&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;有一个 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;R \times C&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 的矩阵，每行从左至右数值递增，每列从上至下数值递增，求矩阵中是否包含给定的数值。&lt;/span&gt;&lt;/p&gt;&lt;h3 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;题目简析&lt;/span&gt;&lt;/h3&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;注意到矩阵中任意一个子矩阵仍然满足此特性，则可以二分子矩阵，直到子矩阵的大小为
&lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;1 \times 1&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;. 具体地，若当前所检查的子矩阵为 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;(l_1, r_1, l_2, r_2)&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，取位置
&lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\displaystyle \left(x = \left\lfloor\frac{l_1+l_2}{2}\right\rfloor, y = \left\lfloor\frac{r_1+r_2}{2}\right\rfloor\right)&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，则：&lt;/span&gt;&lt;/p&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;若目标值恰好等于此位置上的值，则直接返回 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;true&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt;；&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;否则，若目标值小于此位置上的值，则其只可能存在于子矩阵 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;(l_1, x, l_2, y)&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 中；&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;否则，其只可能存在于子矩阵 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;(x + 1, r_1, l_2, r_2)&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 或子矩阵 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;(l_1, r_1, y + 1, r_2)&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 中&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h3 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;程序实现&lt;/span&gt;&lt;/h3&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;时间复杂度： &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;O(\log R \times \log C)&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;额外空间复杂度： &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;O(\log R \times \log C)&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;export function Find(target: number, array: number[][]): boolean {
  if (array.length &amp;lt;= 0) return false
  if (array[0].length &amp;lt;= 0) return false

  const R = array.length
  const C = array[0].length
  return search(0, R, 0, C)

  function search(lft1: number, rht1: number, lft2: number, rht2: number): boolean {
    if (lft1 &amp;gt;= R || lft2 &amp;gt;= C) return false
    if (lft1 &amp;gt; rht1 || lft2 &amp;gt; rht2) return false
    if (lft1 === rht1 &amp;amp;&amp;amp; lft2 === rht2) return false

    const x = (lft1 + rht1) &amp;gt;&amp;gt; 1
    const y = (lft2 + rht2) &amp;gt;&amp;gt; 1
    const z = array[x][y]

    if (target === z) return true
    if (target &amp;lt; z) return search(lft1, x, lft2, y)
    return search(x + 1, rht1, lft2, rht2) || search(lft1, rht1, y + 1, rht2)
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;h2 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;04 重建二叉树&lt;/span&gt;&lt;/h2&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.nowcoder.com/practice/8a19cbe657394eeaac2f6ea9b0f6fcf6&quot; title=&quot;https://www.nowcoder.com/practice/8a19cbe657394eeaac2f6ea9b0f6fcf6&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;题目链接&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;h3 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;题目简述&lt;/span&gt;&lt;/h3&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;给定一棵 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 个节点的二叉树的先序遍历和中序遍历结果，重建二叉树。&lt;/span&gt;&lt;/p&gt;&lt;h3 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;题目简析&lt;/span&gt;&lt;/h3&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;先序遍历的第一个节点为根节点，不妨记为 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，找到中序遍历中 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 的位置，不妨记为
&lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，则中序遍历序列中：&lt;/span&gt;&lt;/p&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 之前所有的节点都为 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 的左子树中的节点；&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;而 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 之后的所有节点都为 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 的右子树的节点；&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;又因为无论先序遍历还是后序遍历都是先访问完左子树再访问右子树的，所以先序遍历序列中前 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 个元素同样是 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 的左子树中的节点，之后递归处理即可。&lt;/span&gt;&lt;/p&gt;&lt;h3 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;程序实现&lt;/span&gt;&lt;/h3&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;时间复杂度： &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;O(N)&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;额外空间复杂度： &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;O(N)&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;interface TreeNode {
  val: number
  left: TreeNode | null
  right: TreeNode | null
}

export function reConstructBinaryTree(pre: number[], vin: number[]): TreeNode {
  if (pre.length === 0 || pre.length !== vin.length) return null
  const tree = build(0, pre.length - 1, 0, vin.length - 1)
  return tree

  function build(lft1: number, rht1: number, lft2: number, rht2: number): TreeNode {
    const x = pre[lft1]

    // 找到
    let p = lft2
    for (; p &amp;lt;= rht2; ++p) if (vin[p] === x) break

    const o: TreeNode = {
      val: x,
      left: lft2 &amp;lt; p ? build(lft1 + 1, lft1 + p - lft2, lft2, p - 1) : null,
      right: rht2 &amp;gt; p ? build(lft1 + p - lft2 + 1, rht1, p + 1, rht2) : null,
    }
    return o
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;h2 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;06 旋转数组的最小数字&lt;/span&gt;&lt;/h2&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.nowcoder.com/practice/9f3231a991af4f55b95579b44b7a01ba&quot; title=&quot;https://www.nowcoder.com/practice/9f3231a991af4f55b95579b44b7a01ba&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;题目链接&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;h3 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;题目简述&lt;/span&gt;&lt;/h3&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;有一个长度为 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 的非严格递增序列 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;A=\lbrace a_1,a_2,\cdots,a_N \rbrace&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，将其前面 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;K&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 个（&lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;0 \leqslant K &amp;lt; N&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;）个元素取出并按顺序放在原序列的末尾，得到新序列&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-math&quot;&gt;B=\lbrace b_1, b_2, \cdots, b_N\rbrace = \lbrace a_{K+1},a_{K+2},\cdots,a_N, a_1, a_2, \cdots, a_K \rbrace
&lt;/div&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;求 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 中的最小值。&lt;/span&gt;&lt;/p&gt;&lt;h3 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;题目简析&lt;/span&gt;&lt;/h3&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;先考虑简化版问题。若原数列严格递增，则存在两种情况：&lt;/span&gt;&lt;/p&gt;&lt;ol class=&quot;yozora-list&quot; start=&quot;1&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;b_1 &amp;lt; b_N&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，即 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 单调递增，此时 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;b_1&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 即为所求答案&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;b_1 &amp;gt; b_N&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，即 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 先上升后突降再上升，取 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;x = \left\lfloor\frac{1 + N}{2}\right\rfloor&lt;/span&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;若 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;b_x &amp;lt; b_N&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，则答案落在 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\lbrace b_1, b_2, \cdots b_x\rbrace&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 中&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;否则，即 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;b_x &amp;gt; b_N&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，则答案落在 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\lbrace b_{x+1}, b_{x+2}, \cdots, c_N\rbrace&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 中&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;原问题在简化版问题基础上的第二种情况种多了一种可能： &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;b_1 = b_N&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;。此时取 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;x=N-1&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 即可。&lt;/span&gt;&lt;/p&gt;&lt;h3 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;程序实现&lt;/span&gt;&lt;/h3&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;时间复杂度： &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;O(\log N)&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; （极端情况下可退化为 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;O(N)&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;）&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;额外空间复杂度： &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;O(1)&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;export function minNumberInRotateArray(rotateArray: number[]): number {
  const A: number[] = rotateArray
  if (A.length &amp;lt;= 0) return 0

  let lft = 0
  let rht = A.length
  while (lft &amp;lt; rht) {
    const last: number = A[rht - 1]
    if (lft + 1 === rht || last &amp;gt; A[lft]) return A[lft]

    const mid: number = (lft + rht) &amp;gt;&amp;gt; 1
    const x: number = A[mid]
    if (mid + 1 === rht || x &amp;lt; last) {
      // 因为前面已经判断过区间首元素和尾元素，所以此时
      // mid 不可能和 lft 相等
      if (x &amp;lt; A[mid - 1]) return x

      rht = mid
    } else if (x === last) rht -= 1
    // 复杂度退化的情况
    else lft = mid + 1
  }
  return A[rht - 1]
}
&lt;/code&gt;&lt;/pre&gt;&lt;h2 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;23 二叉搜索树的后序遍历序列&lt;/span&gt;&lt;/h2&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.nowcoder.com/practice/a861533d45854474ac791d90e447bafd&quot; title=&quot;https://www.nowcoder.com/practice/a861533d45854474ac791d90e447bafd&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;题目链接&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;h3 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;题目描述&lt;/span&gt;&lt;/h3&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;判断给定的数组 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;A=\lbrace a_1, a_2, \cdots, a_N \rbrace&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 是否为一棵二叉搜索树的后序遍历结果。&lt;/span&gt;&lt;/p&gt;&lt;h3 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;题目简析&lt;/span&gt;&lt;/h3&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;后续遍历中，最后一个元素为根节点，这启发我们逆序遍历 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 进行判断。又因为原数组是一棵二叉搜索的遍历结果，即右子树的最小值总比左子树及根节点值都要大，事实上我们仅检验这一点就可以判断给定数组是否为某棵二叉搜索树的后序遍历结果了。&lt;/span&gt;&lt;/p&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;算法的核心思想是沿着树中最右侧的链遍历（访问过的节点可标记为删除）的方式去遍历整棵树，则每次要进入一个左子树时，里面元素的值不得超过当前遍最右侧链的最大值，否则其不为一个有效的二叉搜索树后序遍历结果。树上的最右侧链可以使用一个栈进行维护，不难发现其是一个严格递增的单调栈。每当树上某个节点的右子树访问完后，将其在栈中对应的元素从栈中移除，不难发现，移除操作总是弹出栈顶元素。&lt;/span&gt;&lt;/p&gt;&lt;h3 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;程序实现&lt;/span&gt;&lt;/h3&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;时间复杂度： &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;O(N)&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;额外空间复杂度： &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;O(N)&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;export function VerifySquenceOfBST(sequence: number[]): boolean {
  if (sequence.length &amp;lt;= 0) return false

  const stack: number[] = []
  let maxOfTree: number = Number.MAX_SAFE_INTEGER
  for (let i = sequence.length - 1; i &amp;gt; -1; --i) {
    const x = sequence[i]
    if (x &amp;gt;= maxOfTree) return false

    // 栈顶元素为树中最大的节点，当遇到一个比栈顶元素小的节点时，
    // 说明此后不应该再遇到比栈顶元素大的元素(因为是后续遍历的逆序，
    // 所以我们会先访问完右子树再访问左子树)，同时可以将栈中所以大于
    // 它的元素一并删除(所以这还是个单调栈)。
    while (stack.length &amp;gt; 0 &amp;amp;&amp;amp; x &amp;lt; stack[stack.length - 1]) {
      maxOfTree = stack.pop()
    }

    stack.push(x)
  }
  return true
}
&lt;/code&gt;&lt;/pre&gt;&lt;h2 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;附录&lt;/span&gt;&lt;/h2&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.nowcoder.com/practice/0e26e5551f2b489b9f58bc83aa4b6c68&quot; title=&quot;https://www.nowcoder.com/practice/0e26e5551f2b489b9f58bc83aa4b6c68&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;#02 替换空格&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;： 字符串替换&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;export function replaceSpace(text: string): string {
  return text.replace(/[ ]/, &apos;%20&apos;)
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.nowcoder.com/practice/d0267f7f55b3412ba93bd35cfa8e8035&quot; title=&quot;https://www.nowcoder.com/practice/d0267f7f55b3412ba93bd35cfa8e8035&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;#03 从尾到头打印链表&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;： 栈的应用&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;interface ListNode {
  val: number
  next: ListNode | null
}

const stack: number[] = []

export function printListFromTailToHead(head: ListNode): number[] {
  let tot = 0
  for (let o: ListNode = head; o; o = o.next) stack[tot++] = o.val

  const result: number[] = new Array(tot)
  for (let i = 0; tot--; ++i) result[i] = stack[tot]
  return result
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.nowcoder.com/practice/54275ddae22f475981afa2244dd448c6&quot; title=&quot;https://www.nowcoder.com/practice/54275ddae22f475981afa2244dd448c6&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;#05 用两个栈实现队列&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;： 栈和队列的应用&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;const s1: number[] = []
const s2: number[] = []

export function push(node: number) {
  s1.push(node)
}

export function pop(): number | undefined {
  // 如果 s2 为空，则将栈 s1 中的内容弹出并压入栈 s2 中
  if (s2.length &amp;lt;= 0) {
    while (s1.length &amp;gt; 0) s2.push(s1.pop())
  }

  // 从 s2 中弹栈的值遵循原序列的先入先出规则
  return s2.pop()
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.nowcoder.com/practice/c6c7742f5ba7442aada113136ddea0c3&quot; title=&quot;https://www.nowcoder.com/practice/c6c7742f5ba7442aada113136ddea0c3&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;#07 斐波那契数列&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;： 递推&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-math&quot;&gt;  f(n) = \left\lbrace\begin{aligned}
    &amp;amp;0, &amp;amp;n \leqslant 0\\
    &amp;amp;1, &amp;amp;n = 1\\
    &amp;amp;1, &amp;amp;n = 2\\
    &amp;amp;f(n-1) + f(n-2), &amp;amp;n \geqslant 3\\
  \end{aligned}\right.
&lt;/div&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;export function Fibonacci(n: number): number {
  if (n &amp;lt;= 1) return n

  let a = 0
  let b = 1
  let c = 1
  for (let i = 3; i &amp;lt;= n; ++i) {
    a = b
    b = c
    c = a + b
  }
  return c
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.nowcoder.com/practice/8c82a5b80378478f9484d87d1c5f12a4&quot; title=&quot;https://www.nowcoder.com/practice/8c82a5b80378478f9484d87d1c5f12a4&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;#08 跳台阶&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;： 递推&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-math&quot;&gt;  f(n) = \left\lbrace\begin{aligned}
    &amp;amp;0, &amp;amp;n \leqslant 0\\
    &amp;amp;1, &amp;amp;n = 1\\
    &amp;amp;2, &amp;amp;n = 2\\
    &amp;amp;f(n-1) + f(n-2), &amp;amp;n \geqslant 3\\
  \end{aligned}\right.
&lt;/div&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;export function jumpFloor(n: number): number {
  if (n &amp;lt;= 0) return 0
  if (n &amp;lt;= 2) return n

  let a = 1
  let b = 2
  let c = 3
  for (let i = 3; i &amp;lt; n; ++i) {
    a = b
    b = c
    c = a + b
  }
  return c
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.nowcoder.com/practice/22243d016f6b47f2a6928b4313c85387&quot; title=&quot;https://www.nowcoder.com/practice/22243d016f6b47f2a6928b4313c85387&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;#09 跳台阶扩展问题&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;： 递推&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-math&quot;&gt;  f(n) = \left\lbrace\begin{aligned}
    &amp;amp;0, &amp;amp;n \leqslant 0\\
    &amp;amp;2^{n-1}, &amp;amp;n &amp;gt; 0\\
  \end{aligned}\right.
&lt;/div&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;export function jumpFloorII(n: number): number {
  if (n &amp;lt;= 0) return 0
  return 1 &amp;lt;&amp;lt; (n - 1)
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.nowcoder.com/practice/72a5a919508a4251859fb2cfb987a0e6&quot; title=&quot;https://www.nowcoder.com/practice/72a5a919508a4251859fb2cfb987a0e6&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;#10 跳台阶扩展问题&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;： 递推&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;yozora-math&quot;&gt;  f(n) = \left\lbrace\begin{aligned}
    &amp;amp;0, &amp;amp;n \leqslant 0\\
    &amp;amp;1, &amp;amp;n = 1\\
    &amp;amp;2, &amp;amp;n = 2\\
    &amp;amp;f(n-1) + f(n-2), &amp;amp;n \geqslant 3\\
  \end{aligned}\right.
&lt;/div&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;export function rectCover(n: number): number {
  if (n &amp;lt;= 0) return 0
  if (n &amp;lt;= 2) return n

  let a = 1
  let b = 2
  let c = 3
  for (let i = 3; i &amp;lt; n; ++i) {
    a = b
    b = c
    c = a + b
  }
  return c
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.nowcoder.com/practice/8ee967e43c2c4ec193b040ea7fbb10b8&quot; title=&quot;https://www.nowcoder.com/practice/8ee967e43c2c4ec193b040ea7fbb10b8&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;#11 二进制中 1 的个数&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;： 数学，二进制&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;// 循环判断
export function NumberOf1(n: number): number {
  let result = 0
  for (n; n; n &amp;gt;&amp;gt;&amp;gt;= 1) {
    if (n &amp;amp; 1) result += 1
  }
  return result
}

// 使用 lowbit
export function NumberOf1_2(n: number): number {
  let result = 0
  for (n; n; n ^= n &amp;amp; -n) result += 1
  return result
}

// 使用偏移累加
export function NumberOf1_3(n: number): number {
  let x = (n &amp;amp; 0x55555555) + ((n &amp;gt;&amp;gt; 1) &amp;amp; 0x55555555)
  x = (x &amp;amp; 0x33333333) + ((x &amp;gt;&amp;gt; 2) &amp;amp; 0x33333333)
  x = (x &amp;amp; 0x0f0f0f0f) + ((x &amp;gt;&amp;gt; 4) &amp;amp; 0x0f0f0f0f)
  x = (x &amp;amp; 0x00ff00ff) + ((x &amp;gt;&amp;gt; 8) &amp;amp; 0x00ff00ff)
  x = (x &amp;amp; 0x0000ffff) + ((x &amp;gt;&amp;gt; 16) &amp;amp; 0x0000ffff)
  return x
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.nowcoder.com/practice/1a834e5e3e1a4b7ba251417554e07c00&quot; title=&quot;https://www.nowcoder.com/practice/1a834e5e3e1a4b7ba251417554e07c00&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;#12 数值的整数次方&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;： 数学，快速幂&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;export function Power(base: number, exponent: number): number {
  if (base === 0) return 0

  let n = Math.abs(exponent)
  let result = 1
  for (let x = base; n &amp;gt; 0; n &amp;gt;&amp;gt;= 1, x = x * x) {
    if (n &amp;amp; 1) result *= x
  }
  return exponent &amp;lt; 0 ? 1 / result : result
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.nowcoder.com/practice/ef1f53ef31ca408cada5093c8780f44b&quot; title=&quot;https://www.nowcoder.com/practice/ef1f53ef31ca408cada5093c8780f44b&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;#13 调整数组顺序使奇数位于偶数前面&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;： 数学，二进制&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;export function reOrderArray(nums: number[]): number[] {
  const A: number[] = []
  const B: number[] = []
  for (const x of nums) {
    if (x &amp;amp; 1) A.push(x)
    else B.push(x)
  }
  return A.concat(B)
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.nowcoder.com/practice/886370fe658f41b498d40fb34ae76ff9&quot; title=&quot;https://www.nowcoder.com/practice/886370fe658f41b498d40fb34ae76ff9&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;#14 链表中倒数最后K个节点&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;： 链表，双指针&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;interface ListNode {
  val: number
  next: ListNode | null
}

export function FindKthToTail(pHead: ListNode, k: number) {
  let faster = pHead
  let slower = pHead

  // 快指针先走 k 步
  for (let step = 0; step &amp;lt; k; ++step) {
    if (faster == null) return null
    faster = faster.next
  }

  // 两个指针一起走
  while (faster != null) {
    faster = faster.next
    slower = slower.next
  }
  return slower
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.nowcoder.com/practice/75e878df47f24fdc9dc3e400ec6058ca&quot; title=&quot;https://www.nowcoder.com/practice/75e878df47f24fdc9dc3e400ec6058ca&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;#15 反转链表&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;： 链表&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;interface ListNode {
  val: number
  next: ListNode | null
}

export function ReverseList(pHead: ListNode): ListNode {
  if (pHead == null) return null

  let x: ListNode | null = pHead
  let y: ListNode | null = pHead.next
  while (y != null) {
    const z = y.next
    y.next = x
    x = y
    y = z
  }
  pHead.next = null
  return x
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.nowcoder.com/practice/d8b6b4358f774294a89de2a6ac4d9337&quot; title=&quot;https://www.nowcoder.com/practice/d8b6b4358f774294a89de2a6ac4d9337&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;#16 合并两个排序的链表&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;： 归并排序&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;interface ListNode {
  val: number
  next: ListNode | null
}

export function Merge(pHead1: ListNode, pHead2: ListNode): ListNode {
  const fakeRoot: ListNode = {
    val: -1,
    next: null,
  }

  let x: ListNode = pHead1
  let y: ListNode = pHead2
  let z: ListNode = fakeRoot
  for (; x != null &amp;amp;&amp;amp; y != null; z = z.next) {
    if (x.val &amp;lt; y.val) {
      z.next = x
      x = x.next
    } else {
      z.next = y
      y = y.next
    }
  }

  if (x != null) z.next = x
  if (y != null) z.next = y
  return fakeRoot.next
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.nowcoder.com/practice/6e196c44c7004d15b1610b9afca8bd88&quot; title=&quot;https://www.nowcoder.com/practice/6e196c44c7004d15b1610b9afca8bd88&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;#17 树的子结构&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;： 二叉树，递归&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;interface TreeNode {
  val: number
  left: TreeNode | null
  right: TreeNode | null
}

export function HasSubtree(pRoot1: TreeNode, pRoot2: TreeNode): boolean {
  if (pRoot1 == null || pRoot2 == null) return false
  if (isSubTree(pRoot1, pRoot2)) return true
  if (pRoot1.left != null &amp;amp;&amp;amp; HasSubtree(pRoot1.left, pRoot2)) return true
  if (pRoot1.right != null &amp;amp;&amp;amp; HasSubtree(pRoot1.right, pRoot2)) return true
  return false
}

// 检查 v 是否为 u 的子树，且它们根节点相同
function isSubTree(u: TreeNode, v: TreeNode): boolean {
  if (u.val !== v.val) return false
  if (v.left != null) {
    if (u.left == null || !isSubTree(u.left, v.left)) return false
  }
  if (v.right != null) {
    if (u.right == null || !isSubTree(u.right, v.right)) return false
  }
  return true
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.nowcoder.com/practice/a9d0ecbacef9410ca97463e4a5c83be7&quot; title=&quot;https://www.nowcoder.com/practice/a9d0ecbacef9410ca97463e4a5c83be7&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;#18 二叉树的镜像&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;： 二叉树，递归&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;interface TreeNode {
  val: number
  left: TreeNode | null
  right: TreeNode | null
}

export function Mirror(pRoot: TreeNode): TreeNode {
  if (pRoot == null) return null
  const x = pRoot.left
  pRoot.left = pRoot.right
  pRoot.right = x
  Mirror(pRoot.left)
  Mirror(pRoot.right)
  return pRoot
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.nowcoder.com/practice/9b4c81a02cd34f76be2659fa0d54342a&quot; title=&quot;https://www.nowcoder.com/practice/9b4c81a02cd34f76be2659fa0d54342a&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;#19 顺时针打印矩阵&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;： 模拟&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;export function printMatrix(matrix: number[][]): number[] {
  if (matrix.length &amp;lt;= 0) return []
  if (matrix[0].length &amp;lt;= 0) return []

  const R = matrix.length
  const C = matrix[0].length

  const result: number[] = []
  const totalRound = Math.min(R, C) &amp;gt;&amp;gt; 1
  for (let round = 0; round &amp;lt; totalRound; ++round) {
    right(round, round, C - round)
    down(C - round - 1, round + 1, R - round)
    left(R - round - 1, C - round - 2, round - 1)
    up(round, R - round - 2, round)
  }

  if (Math.min(R, C) &amp;amp; 1) {
    if (C &amp;gt; R) {
      right(totalRound, totalRound, C - totalRound)
    } else {
      down(totalRound, totalRound, R - totalRound)
    }
  }
  return result

  function right(row: number, x: number, y: number): void {
    for (let i = x; i &amp;lt; y; ++i) {
      result.push(matrix[row][i])
    }
  }

  function down(col: number, x: number, y: number): void {
    for (let i = x; i &amp;lt; y; ++i) {
      result.push(matrix[i][col])
    }
  }

  function left(row: number, x: number, y: number): void {
    for (let i = x; i &amp;gt; y; --i) {
      result.push(matrix[row][i])
    }
  }

  function up(col: number, x: number, y: number): void {
    for (let i = x; i &amp;gt; y; --i) {
      result.push(matrix[i][col])
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.nowcoder.com/practice/4c776177d2c04c2494f2555c9fcc1e49&quot; title=&quot;https://www.nowcoder.com/practice/4c776177d2c04c2494f2555c9fcc1e49&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;#20 包含min函数的栈&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;： 单调栈&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;const nums: number[] = [] // 普通栈
const stack: number[] = [] // 单调栈

export function push(node: number): void {
  nums.push(node)

  // 允许放入和栈顶元素相等的元素是为了避免在弹出元素时做特判
  if (stack.length &amp;lt;= 0 || stack[stack.length - 1] &amp;gt;= node) {
    stack.push(node)
  }
}

export function pop(): void {
  const x = nums.pop()

  // 单调栈中栈顶元素最小，如果 nums 弹出的元素和单调栈中的栈顶元素相同，
  // 则单调栈中也同时弹出元素
  if (x != null &amp;amp;&amp;amp; x === min()) stack.pop()
}

export function top(): number | undefined {
  return nums.length &amp;gt; 0 ? nums[nums.length - 1] : undefined
}

export function min(): number {
  return stack[stack.length - 1]
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.nowcoder.com/practice/d77d11405cc7470d82554cb392585106&quot; title=&quot;https://www.nowcoder.com/practice/d77d11405cc7470d82554cb392585106&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;#21 栈的压入、弹出序列&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;： 栈，模拟&lt;/span&gt;&lt;/p&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;根据 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;push&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 序列按顺序进行压栈，每次压完栈后根据 &lt;/span&gt;&lt;pre class=&quot;yozora-inline-code&quot;&gt;&lt;code&gt;pop&lt;/code&gt;&lt;/pre&gt;&lt;span class=&quot;yozora-text&quot;&gt; 序列检查是否要弹栈&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;export function IsPopOrder(pushV: number[], popV: number[]): boolean {
  const stack: number[] = []
  let i = 0
  let j = 0
  for (const x of pushV) {
    const y = popV[j]
    if (x === y) j += 1
    else stack[i++] = x

    while (i &amp;gt; 0) {
      if (stack[i - 1] !== popV[j]) break
      i -= 1
      j += 1
    }
  }
  return i === 0
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.nowcoder.com/practice/7fe2212963db4790b57431d9ed259701&quot; title=&quot;https://www.nowcoder.com/practice/7fe2212963db4790b57431d9ed259701&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;#22 从上往下打印二叉树&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;： 二叉树，BFS&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;interface TreeNode {
  val: number
  left: TreeNode | null
  right: TreeNode | null
}

export function PrintFromTopToBottom(root: TreeNode): number[] {
  if (root == null) return []
  const Q: TreeNode[] = []
  const result: number[] = []
  Q.push(root)
  while (Q.length &amp;gt; 0) {
    const x = Q.shift()
    result.push(x.val)
    if (x.left) Q.push(x.left)
    if (x.right) Q.push(x.right)
  }
  return result
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.nowcoder.com/practice/b736e784e3e34731af99065031301bca&quot; title=&quot;https://www.nowcoder.com/practice/b736e784e3e34731af99065031301bca&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;#24 二叉树中和为某一值的路径&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;： 二叉树，DFS&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;interface TreeNode {
  val: number
  left: TreeNode | null
  right: TreeNode | null
}

export function FindPath(root: TreeNode, target: number): number[][] {
  if (root == null) return []
  const results: number[][] = []
  const tmp: number[] = []
  let total = 0
  f(root, 0)
  return results

  function f(o: TreeNode, cur: number): void {
    tmp[cur] = o.val
    total += o.val
    if (o.left == null &amp;amp;&amp;amp; o.right == null) {
      if (total === target) {
        results.push(tmp.slice(0, cur + 1))
      }
    } else {
      if (o.left != null) f(o.left, cur + 1)
      if (o.right != null) f(o.right, cur + 1)
    }
    total -= o.val
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.nowcoder.com/practice/f836b2c43afc4b35ad6adc41ec941dba&quot; title=&quot;https://www.nowcoder.com/practice/f836b2c43afc4b35ad6adc41ec941dba&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;#25 复杂链表的复制&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;： 链表，哈希表、Map&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;interface RandomListNode {
  label: number
  next: RandomListNode | null
  random: RandomListNode | null
}

export function Clone(o: RandomListNode): RandomListNode {
  if (o == null) return null
  const map = new Map()
  const root = { ...o }
  for (let u = root, v = o; v != null; u = u.next, v = v.next) {
    map.set(v, u)
    if (v.next != null) {
      let w = map.get(v.next)
      if (w != null) {
        u.next = w
        break
      }
      u.next = { ...v.next }
    }
  }
  for (let u = root; u != null; u = u.next) {
    const r = map.get(u.random)
    u.random = r === undefined ? null : r
  }
  return root
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.nowcoder.com/practice/947f6eb80d944a84850b0538bf0ec3a5&quot; title=&quot;https://www.nowcoder.com/practice/947f6eb80d944a84850b0538bf0ec3a5&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;#26 二叉搜索树与双向链表&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;： 二叉树，中序遍历&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;interface TreeNode {
  val: number
  left: TreeNode | null
  right: TreeNode | null
}

export function Convert(pRootOfTree: TreeNode): TreeNode {
  if (pRootOfTree == null) return null

  f(null, pRootOfTree)
  let root = pRootOfTree
  while (root.left != null) root = root.left
  return root
}

function f(p: TreeNode, o: TreeNode): TreeNode {
  if (o.left) p = f(p, o.left)

  if (p != null) p.right = o
  o.left = p
  p = o

  if (o.right) p = f(p, o.right)
  return p
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.nowcoder.com/practice/fe6b651b66ae47d7acce78ffdd9a96c7&quot; title=&quot;https://www.nowcoder.com/practice/fe6b651b66ae47d7acce78ffdd9a96c7&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;#27 字符串的排列&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;： 全排列&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;export function Permutation(str: string): string[] {
  if (str == null || str.length &amp;lt;= 0) return []
  const letters: string[] = str.split(&apos;&apos;).sort()
  const answers: string[] = []
  const _end = letters.length - 1

  while (true) {
    answers.push(letters.join(&apos;&apos;))

    // 从右往左扫描找到第一个相邻且逆序的元素对 (i - 1, i)
    // 将第 i-1 个元素和最后一个元素交换，然后将 [i, N] 中的元素反转顺序

    let i = _end
    for (; i &amp;gt; 0; --i) {
      if (letters[i] &amp;gt; letters[i - 1]) break
    }

    // 若不存在这样的 i，则结束循环
    if (i &amp;lt;= 0) break

    let x = letters[i - 1]
    let k = _end
    for (; k &amp;gt;= i; --k) if (letters[k] &amp;gt; x) break
    letters[i - 1] = letters[k]
    letters[k] = x
    const L = i + _end
    const mid = L &amp;gt;&amp;gt; 1
    for (let j = i; j &amp;lt;= mid; ++j) {
      x = letters[j]
      letters[j] = letters[L - j]
      letters[L - j] = x
    }
  }
  return answers
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.nowcoder.com/practice/e8a1b01a2df14cb2b228b30ee6a92163&quot; title=&quot;https://www.nowcoder.com/practice/e8a1b01a2df14cb2b228b30ee6a92163&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;#28 数组中出现次数超过一半的数字&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;： 中位数，随机测试，找第 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 大数&lt;/span&gt;&lt;/p&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;排序&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;/**
 * 排序并返回中位数
 *
 * 时间复杂度： O(N \log N)
 * 额外空间复杂度： O(N)
 */
export function MoreThanHalfNum_Solution(nums: number[]): number {
  nums.sort((x, y) =&amp;gt; x - y)
  return nums[nums.length &amp;gt;&amp;gt; 1]
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;随机测试&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;/**
 * 随机测试
 *
 * 时间复杂度： O(N \log N)
 * 额外空间复杂度： O(1)
 */
export function MoreThanHalfNum_Solution_2(nums: number[]): number {
  let last: number = Number.NaN

  while (true) {
    const current = nums[Math.floor(Math.random() * nums.length)]

    if (last === current) continue
    last = current

    let cnt = 0
    for (let x of nums) if (x === current) cnt += 1
    if (cnt * 2 &amp;gt;= nums.length) return current
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;找第 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 大的数&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;/**
 * 快排思想找数组中第 k 大元素
 *
 * 时间复杂度： O(N)
 * 额外空间复杂度： O(log N)
 */
export function MoreThanHalfNum_Solution_3(nums: number[]): number {
  return kth(0, nums.length - 1, (nums.length + 1) &amp;gt;&amp;gt; 1)

  function kth(lft: number, rht: number, k: number): number {
    if (lft === rht) return nums[lft]

    // 选取一个随机值防止极端数据下的复杂度退化
    const x = lft + 7 &amp;lt;= rht ? lft + Math.floor((rht - lft) * Math.random()) : (lft + rht) &amp;gt;&amp;gt; 1
    const pivot = nums[x]
    nums[x] = nums[lft]

    let i = lft
    let j = rht + 1
    while (i &amp;lt; j) {
      for (j -= 1; i &amp;lt; j &amp;amp;&amp;amp; nums[j] &amp;gt;= pivot; ) --j
      if (i === j) break
      nums[i] = nums[j]

      for (i += 1; i &amp;lt; j &amp;amp;&amp;amp; nums[i] &amp;lt;= pivot; ) ++i
      if (i === j) break
      nums[j] = nums[i]
    }
    nums[i] = pivot

    // 优化，中间元素的边界尽可能延申，否则复杂度可能退化为 O(N^2)
    for (i -= 1; i &amp;gt;= lft; --i) if (nums[i] !== pivot) break
    for (j += 1; j &amp;lt;= rht; ++j) if (nums[j] !== pivot) break

    // 先检查左边的元素个数是否大于或等于 k 个
    let k2 = k - (i + 1 - lft)
    if (k2 &amp;lt;= 0) return kth(lft, i, k)

    // 再判断左边的元素个数+中间元素个数是否大于等于 k 个
    k2 -= j - i - 1
    if (k2 &amp;lt;= 0) return pivot

    // 否则，目标值再右边的元素列表中
    return kth(j, rht, k2)
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.nowcoder.com/practice/6a296eb82cf844ca8539b57c23e6e9bf&quot; title=&quot;https://www.nowcoder.com/practice/6a296eb82cf844ca8539b57c23e6e9bf&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;#29 最小的K个数&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;： 排序，小根堆，找第 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 的数&lt;/span&gt;&lt;/p&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;小根堆&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;/**
 * 大根堆
 *
 * 时间复杂度： O(N \log N)
 * 额外空间复杂度： O(N)
 */
export function GetLeastNumbers_Solution(nums: number[], _k: number): number[] {
  if (_k &amp;lt;= 0) return []
  if (_k &amp;gt;= nums.length) return nums

  const Q: PriorityQueue = createPriorityQueue((x, y) =&amp;gt; y - x)
  for (let i = 0; i &amp;lt; _k; ++i) Q.enqueue(nums[i])
  for (let i = _k; i &amp;lt; nums.length; ++i) {
    const x = nums[i]
    if (Q.top() &amp;lt;= x) continue
    Q.dequeue()
    Q.enqueue(x)
  }

  return Q.collect()
}

interface PriorityQueue {
  enqueue: (val: T) =&amp;gt; void
  dequeue: () =&amp;gt; T | undefined
  collect: () =&amp;gt; T[]
  top: () =&amp;gt; T | undefined
  size: () =&amp;gt; number
  isEmpty: () =&amp;gt; boolean
}

// 优先队列
function createPriorityQueue(cmp: (x: T, y: T) =&amp;gt; -1 | 0 | 1 | number): PriorityQueue {
  const _tree: T[] = [null]
  let _size: number = 0

  function enqueue(val: T): void {
    _tree[++_size] = val
    _up(_size)
  }

  function dequeue(): T | undefined {
    if (_size &amp;lt; 1) return undefined

    const target = _tree[1]
    _tree[1] = _tree[_size--]
    _down(1)

    return target
  }

  function top(): T | undefined {
    return _size &amp;gt; 0 ? _tree[1] : undefined
  }

  function collect(): T[] {
    return _tree.slice(1)
  }

  function _down(index: number): void {
    for (let i = index; i &amp;lt;= _size; ) {
      const lft = i &amp;lt;&amp;lt; 1
      const rht = lft | 1
      if (lft &amp;gt; _size) break

      let q = lft
      if (rht &amp;lt;= _size &amp;amp;&amp;amp; cmp(_tree[rht], _tree[q]) &amp;lt; 0) q += 1

      const x = _tree[q]
      if (cmp(_tree[i], x) &amp;lt;= 0) break

      _tree[q] = _tree[i]
      _tree[i] = x
      i = q
    }
  }

  function _up(index: number): void {
    for (let i = index; i &amp;gt; 1; ) {
      const q = i &amp;gt;&amp;gt; 1
      const x = _tree[q]
      if (cmp(x, _tree[i]) &amp;lt;= 0) break

      _tree[q] = _tree[i]
      _tree[i] = x
      i = q
    }
  }

  return {
    enqueue,
    dequeue,
    top,
    collect,
    size: () =&amp;gt; _size,
    isEmpty: () =&amp;gt; _size &amp;lt; 1,
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;找第 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 大的数&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;/**
 * 快排思想找数组中第 k 大元素
 *
 * 时间复杂度： O(N)
 * 额外空间复杂度： O(log N)
 */
export function GetLeastNumbers_Solution(nums: number[], _k: number): number[] {
  if (_k &amp;lt;= 0) return []
  if (_k &amp;gt;= nums.length) return nums

  kth(0, nums.length - 1, _k)
  return nums.slice(0, _k)

  function kth(lft: number, rht: number, k: number): number {
    if (lft === rht) return nums[lft]

    // 选取一个随机值防止极端数据下的复杂度退化
    const x = lft + 7 &amp;lt;= rht ? lft + Math.floor((rht - lft) * Math.random()) : (lft + rht) &amp;gt;&amp;gt; 1
    const pivot = nums[x]
    nums[x] = nums[lft]

    let i = lft
    let j = rht + 1
    while (i &amp;lt; j) {
      for (j -= 1; i &amp;lt; j &amp;amp;&amp;amp; nums[j] &amp;gt;= pivot; ) --j
      if (i === j) break
      nums[i] = nums[j]

      for (i += 1; i &amp;lt; j &amp;amp;&amp;amp; nums[i] &amp;lt;= pivot; ) ++i
      if (i === j) break
      nums[j] = nums[i]
    }
    nums[i] = pivot

    // 优化，中间元素的边界尽可能延申，否则复杂度可能退化为 O(N^2)
    for (i -= 1; i &amp;gt;= lft; --i) if (nums[i] !== pivot) break
    for (j += 1; j &amp;lt;= rht; ++j) if (nums[j] !== pivot) break

    // 先检查左边的元素个数是否大于或等于 k 个
    let k2 = k - (i + 1 - lft)
    if (k2 &amp;lt;= 0) return kth(lft, i, k)

    // 再判断左边的元素个数+中间元素个数是否大于等于 k 个
    k2 -= j - i - 1
    if (k2 &amp;lt;= 0) return pivot

    // 否则，目标值再右边的元素列表中
    return kth(j, rht, k2)
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.nowcoder.com/practice/459bd355da1549fa8a49e350bf3df484&quot; title=&quot;https://www.nowcoder.com/practice/459bd355da1549fa8a49e350bf3df484&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;#30 连续子数组的最大和&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;： 前缀和&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;export function FindGreatestSumOfSubArray(nums: number[]): number {
  if (nums.length &amp;lt;= 0) return 0

  let answer = nums[0]
  let x = 0
  let y = nums[0]
  for (let i = 1; i &amp;lt; nums.length; ++i) {
    x = Math.min(x, y)
    y += nums[i]
    if (y - x &amp;gt; answer) answer = y - x
  }
  return answer
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.nowcoder.com/practice/bd7f978302044eee894445e244c7eee6&quot; title=&quot;https://www.nowcoder.com/practice/bd7f978302044eee894445e244c7eee6&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;#31 整数中 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 出现的次数（从 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 到 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 整数中1出现的次数）&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;： 数学，排列组合&lt;/span&gt;&lt;/p&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;因为十进制数位中每个位置上 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 的贡献度是彼此独立的，所以依次枚举每个位置，计算该位置为 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 所有可能情况数，累计求和即可。&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;const tenth: number[] = []
for (let i = 0, t = 1; i &amp;lt; 18; ++i, t *= 10) {
  tenth.push(t)
}

export function NumberOf1Between1AndN_Solution(n: number): number {
  const nums: number[] = String(n)
    .split(&apos;&apos;)
    .map(x =&amp;gt; Number(x))

  let result = 0
  for (let i = 0; i &amp;lt; nums.length; ++i) {
    result += toNum(0, i) * tenth[nums.length - i - 1]
    const x = nums[i]
    if (x &amp;lt; 1) continue
    else if (x &amp;gt; 1) result += tenth[nums.length - i - 1]
    else if (x === 1) result += toNum(i + 1, nums.length) + 1
  }
  return result

  function toNum(lft: number, rht: number): number {
    let result = 0
    for (let i = lft; i &amp;lt; rht; ++i) {
      result = result * 10 + nums[i]
    }
    return result
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.nowcoder.com/practice/8fecd3f8ba334add803bf2a06af1b993&quot; title=&quot;https://www.nowcoder.com/practice/8fecd3f8ba334add803bf2a06af1b993&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;#32 把数组排成最小的数&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;： 排序，贪心&lt;/span&gt;&lt;/p&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;方便起见，将原数组中所有的数字转为字符串。考虑每个顺序下两个相邻的数字 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 和
&lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，若组成的字符串 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;xy&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 字典序大于字符串 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;yx&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，则交换它们的位置可以得到一个更优的结果，且不会影响完整字符串中其它部分的大小。&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;export function PrintMinNumber(A: number[]): string {
  const result = A.map(x =&amp;gt; String(x))
    .sort((x, y) =&amp;gt; {
      if (x === y) return 0
      return x + y &amp;lt; y + x ? -1 : 1
    })
    .join(&apos;&apos;)
  return result
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.nowcoder.com/practice/6aa9e04fc3794f68acf8778237ba065b&quot; title=&quot;https://www.nowcoder.com/practice/6aa9e04fc3794f68acf8778237ba065b&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;#33 丑数&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;： 素因子，小根堆&lt;/span&gt;&lt;/p&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;每次取出小根堆中的根元素，然后放入其 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\times 2, \times 3, \times 5&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 的数值，直到取出了 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 个数为止。&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;export function GetUglyNumber_Solution(N: number): number {
  for (let i = answers.length; i &amp;lt;= N; ++i) {
    const c = answers[i - 1]
    while (!Q.isEmpty()) {
      const x = Q.dequeue()
      if (x &amp;lt;= c) continue
      answers.push(x)
      Q.enqueue(x * 2)
      Q.enqueue(x * 3)
      Q.enqueue(x * 5)
      break
    }
  }
  return answers[N]
}

const answers: number[] = [0]
const Q: PriorityQueue = createPriorityQueue((x, y) =&amp;gt; x - y)
Q.enqueue(1)

interface PriorityQueue {
  enqueue: (val: T) =&amp;gt; void
  dequeue: () =&amp;gt; T | undefined
  collect: () =&amp;gt; T[]
  top: () =&amp;gt; T | undefined
  size: () =&amp;gt; number
  isEmpty: () =&amp;gt; boolean
}

// 优先队列
function createPriorityQueue(cmp: (x: T, y: T) =&amp;gt; -1 | 0 | 1 | number): PriorityQueue {
  const _tree: T[] = [null]
  let _size: number = 0

  function enqueue(val: T): void {
    _tree[++_size] = val
    _up(_size)
  }

  function dequeue(): T | undefined {
    if (_size &amp;lt; 1) return undefined

    const target = _tree[1]
    _tree[1] = _tree[_size--]
    _down(1)

    return target
  }

  function top(): T | undefined {
    return _size &amp;gt; 0 ? _tree[1] : undefined
  }

  function collect(): T[] {
    return _tree.slice(1)
  }

  function _down(index: number): void {
    for (let i = index; i &amp;lt;= _size; ) {
      const lft = i &amp;lt;&amp;lt; 1
      const rht = lft | 1
      if (lft &amp;gt; _size) break

      let q = lft
      if (rht &amp;lt;= _size &amp;amp;&amp;amp; cmp(_tree[rht], _tree[q]) &amp;lt; 0) q += 1

      const x = _tree[q]
      if (cmp(_tree[i], x) &amp;lt;= 0) break

      _tree[q] = _tree[i]
      _tree[i] = x
      i = q
    }
  }

  function _up(index: number): void {
    for (let i = index; i &amp;gt; 1; ) {
      const q = i &amp;gt;&amp;gt; 1
      const x = _tree[q]
      if (cmp(x, _tree[i]) &amp;lt;= 0) break

      _tree[q] = _tree[i]
      _tree[i] = x
      i = q
    }
  }

  return {
    enqueue,
    dequeue,
    top,
    collect,
    size: () =&amp;gt; _size,
    isEmpty: () =&amp;gt; _size &amp;lt; 1,
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.nowcoder.com/practice/1c82e8cf713b4bbeb2a5b31cf5b0417c&quot; title=&quot;https://www.nowcoder.com/practice/1c82e8cf713b4bbeb2a5b31cf5b0417c&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;#34 第一个只出现一次的字符&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;： 哈希表，Map&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;export function FirstNotRepeatingChar(str: string): number {
  const duplicated: Record = {}
  for (let i = 0; i &amp;lt; str.length; ++i) {
    const c = str[i]
    duplicated[c] = duplicated[c] === undefined ? false : true
  }

  for (let i = 0; i &amp;lt; str.length; ++i) {
    if (duplicated[str[i]]) continue
    return i
  }
  return -1
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.nowcoder.com/practice/96bd6684e04a44eb80e6a68efc0ec6c5&quot; title=&quot;https://www.nowcoder.com/practice/96bd6684e04a44eb80e6a68efc0ec6c5&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;#35 数组中的逆序对&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;： 逆序对，树状数组，映射&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;export function InversePairs(data: number[]): number {
  const _size = data.length

  const idx: number[] = new Array(_size)
  for (let i = 0; i &amp;lt; _size; ++i) idx[i] = i
  idx.sort((i, j) =&amp;gt; data[i] - data[j])

  // 映射
  const rnk: number[] = []
  for (let i = 0; i &amp;lt; _size; ++i) rnk[idx[i]] = i + 1

  let result = 0
  const bit = createBIT(_size)
  for (const x of rnk) {
    result += bit.sum(_size) - bit.sum(x)
    bit.add(x, 1)
  }
  return result % (1e9 + 7)
}

function createBIT(n: number) {
  const lowbit = (x: number): number =&amp;gt; x &amp;amp; -x

  const sumv: number[] = new Array(n + 1).fill(0)

  const add = (x: number, v: number): void =&amp;gt; {
    for (let i = x; i &amp;lt;= n; i += lowbit(i)) sumv[i] += v
  }

  const sum = (x: number): number =&amp;gt; {
    let answer = 0
    for (let i = x; i &amp;gt; 0; i -= lowbit(i)) answer += sumv[i]
    return answer
  }

  return { add, sum }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.nowcoder.com/practice/6ab1d9a29e88450685099d45c9e31e46&quot; title=&quot;https://www.nowcoder.com/practice/6ab1d9a29e88450685099d45c9e31e46&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;#36 两个链表的第一个公共节点&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;： 双指针&lt;/span&gt;&lt;/p&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;先分别从两个链表的表达走到末尾，得到所需步数分别为 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;k_1&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 和 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;k_2&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，不妨设
&lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;k_1 &amp;gt; k_2&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，即第一个链表比第二个长，先让快指针从第一个链表出发先走 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;k_1 - k_2&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;
步，再让慢指针从第二个链表出发和快指针一起前进，当慢指针和快指针相等时即到达第一个公共节点&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;interface ListNode {
  val: number
  next: ListNode | null
}

export function FindFirstCommonNode(pHead1: ListNode, pHead2: ListNode): ListNode {
  let k1 = 0
  let k2 = 0

  for (let o = pHead1; o != null; o = o.next) k1 += 1
  for (let o = pHead2; o != null; o = o.next) k2 += 1

  const delta: number = Math.abs(k1 - k2)
  let faster: ListNode | null = k1 &amp;gt; k2 ? pHead1 : pHead2
  let slower: ListNode | null = k1 &amp;lt; k2 ? pHead1 : pHead2

  for (let i = 0; i &amp;lt; delta; ++i) faster = faster.next
  while (faster !== slower) {
    faster = faster.next
    slower = slower.next
  }
  return faster
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.nowcoder.com/practice/70610bf967994b22bb1c26f9ae901fa2&quot; title=&quot;https://www.nowcoder.com/practice/70610bf967994b22bb1c26f9ae901fa2&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;#37 数字在升序数组中出现的次数&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;： 二分&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;export function GetNumberOfK(nums: number[], k: number): number {
  return lowerBound(k + 1) - lowerBound(k)

  function lowerBound(x: number): number {
    let lft = 0
    let rht = nums.length
    while (lft &amp;lt; rht) {
      const mid = (lft + rht) &amp;gt;&amp;gt; 1
      if (nums[mid] &amp;lt; x) lft = mid + 1
      else rht = mid
    }
    return lft
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.nowcoder.com/practice/435fb86331474282a3499955f0a41e8b&quot; title=&quot;https://www.nowcoder.com/practice/435fb86331474282a3499955f0a41e8b&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;#38 二叉树的深度&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;： 二叉树，DFS&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;interface TreeNode {
  val: number
  left: TreeNode | null
  right: TreeNode | null
}

export function TreeDepth(o: TreeNode | null): number {
  if (o == null) return 0
  return Math.max(TreeDepth(o.left), TreeDepth(o.right)) + 1
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.nowcoder.com/practice/8b3b95850edb4115918ecebdf1b4d222&quot; title=&quot;https://www.nowcoder.com/practice/8b3b95850edb4115918ecebdf1b4d222&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;#39 平衡二叉树&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;： 二叉树，DFS&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;interface TreeNode {
  val: number
  left: TreeNode | null
  right: TreeNode | null
}

export function IsBalanced_Solution(pRoot: TreeNode): boolean {
  return dfs(pRoot) !== -1

  function dfs(o: TreeNode): 0 | -1 | number {
    if (o == null) return 0

    const h1 = dfs(o.left)
    if (h1 === -1) return -1

    const h2 = dfs(o.right)
    if (h2 === -1) return -1

    const delta = Math.abs(h1 - h2)
    return delta &amp;lt;= 1 ? Math.max(h1, h2) + 1 : -1
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.nowcoder.com/practice/389fc1c3d3be4479a154f63f495abff8&quot; title=&quot;https://www.nowcoder.com/practice/389fc1c3d3be4479a154f63f495abff8&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;#40 数组中只出现一次的两个数字&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;： 异或，分类&lt;/span&gt;&lt;/p&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;不妨记只出现一次的两个数字分别为 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;x_1&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 和 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;x_2&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;。先求出所有数字的异或结果 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，不难发现 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;s = x_1 \oplus x_2&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;。又因为 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;x_1 \neq x_2&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，所以 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;s \neq 0&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;。取出
&lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 二进制位中任意一位不为 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 的位 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，即 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;s \oplus 2^{b-1} = s - 2^{b-1}&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，将原数组中所有的数字划分为第 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 为 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 和第 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 位为 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 两大类，不难发现，&lt;/span&gt;&lt;/p&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;相同的数字会被分配到同一类中&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;x_1&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 和 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;x_2&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 会被分配到不同的类别中&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;则问题转换为【一个数组中，除一个元素只出现一次外其它元素均恰好出现两次，求这个只出现一次的元素】，这个问题直接异或就可以了。&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;export function FindNumsAppearOnce(nums: number[]): [number, number] {
  let s = 0
  for (const x of nums) s ^= x
  const b = s &amp;amp; -s

  let x1 = 0
  let x2 = 0
  for (const x of nums) {
    if (x &amp;amp; b) x1 ^= x
    else x2 ^= x
  }

  return [Math.min(x1, x2), Math.max(x1, x2)]
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.nowcoder.com/practice/c451a3fd84b64cb19485dad758a55ebe&quot; title=&quot;https://www.nowcoder.com/practice/c451a3fd84b64cb19485dad758a55ebe&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;#41 和为 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;S&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 的连续正数序列&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;： 前缀和&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;export function FindContinuousSequence(sum: number): number[][] {
  if (sum &amp;lt;= 0) return []

  const results: number[][] = []
  let lft = 1
  let total = 1
  for (let i = 2, _end = ((sum + 1) &amp;gt;&amp;gt; 1) + 1; i &amp;lt; _end; ++i) {
    total += i
    for (; total &amp;gt; sum; ++lft) total -= lft
    if (total === sum) {
      const answer: number[] = []
      for (let x = lft; x &amp;lt;= i; ++x) answer.push(x)
      results.push(answer)
    }
  }
  return results
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.nowcoder.com/practice/390da4f7a00f44bea7c2f3d19491311b&quot; title=&quot;https://www.nowcoder.com/practice/390da4f7a00f44bea7c2f3d19491311b&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;#42 和为 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;S&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 的两个数字&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;： 二分&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;export function FindNumbersWithSum(nums: number[], sum: number): number[] {
  const _size = nums.length
  if (_size &amp;lt;= 1) return []

  let i = lowerBound(1, _size, sum - nums[0])
  if (i === _size) return []

  for (; i &amp;gt; 0; --i) {
    const x = nums[i]
    const y = sum - x
    const j = lowerBound(0, i, y)
    if (j &amp;lt; i &amp;amp;&amp;amp; nums[j] === y) return [y, x]
  }
  return []

  function lowerBound(lft: number, rht: number, x: number): number {
    while (lft &amp;lt; rht) {
      const mid = (lft + rht) &amp;gt;&amp;gt; 1
      if (nums[mid] &amp;lt; x) lft = mid + 1
      else rht = mid
    }
    return lft
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.nowcoder.com/practice/12d959b108cb42b1ab72cef4d36af5ec&quot; title=&quot;https://www.nowcoder.com/practice/12d959b108cb42b1ab72cef4d36af5ec&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;#43 左旋转字符串&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;： 数组切片&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;export function LeftRotateString(str: string, n: number): string {
  if (str == null || str.length &amp;lt;= 0) return &apos;&apos;
  const _size = str.length
  const k = ((n % _size) + _size) % _size
  if (k === 0) return str
  return str.slice(k) + str.slice(0, k)
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.nowcoder.com/practice/3194a4f4cf814f63919d0790578d51f3&quot; title=&quot;https://www.nowcoder.com/practice/3194a4f4cf814f63919d0790578d51f3&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;#44 翻转单词序列&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;： 字符串&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;export function ReverseSentence(s: string): string {
  return s.split(/\s+/g).reverse().join(&apos; &apos;)
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.nowcoder.com/practice/762836f4d43d43ca9deb273b3de8e1f4&quot; title=&quot;https://www.nowcoder.com/practice/762836f4d43d43ca9deb273b3de8e1f4&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;#45 扑克牌顺子&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;： 模拟，枚举&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;export function IsContinuous(numbers: number[]): boolean {
  const nums = numbers.slice().sort((x, y) =&amp;gt; x - y)
  let i = 0
  for (; i &amp;lt; nums.length; ++i) if (nums[i]) break
  if (i + 1 &amp;gt;= nums.length) return true

  let x = nums[i]
  for (let j = i + 1; j &amp;lt; nums.length; ++j) {
    const y = nums[j]
    if (x === y) return false
    i -= y - x - 1
    if (i &amp;lt; 0) return false
    x = y
  }
  return true
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.nowcoder.com/practice/f78a359491e64a50bce2d89cff857eb6&quot; title=&quot;https://www.nowcoder.com/practice/f78a359491e64a50bce2d89cff857eb6&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;#46 孩子们的游戏(圆圈中最后剩下的数)&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;： 数学，&lt;/span&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;/post/quiz/classical/Josephus-ring/&quot; title=&quot;/post/quiz/classical/Josephus-ring/&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;约瑟夫环&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;export function LastRemaining_Solution(n: number, m: number): number {
  if (n === 0) return -1
  if (n === 1) return 0
  const x = LastRemaining_Solution(n - 1, m)

  // 以上一个位置为零号位置重新编号
  return (m + x) % n
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.nowcoder.com/practice/7a0da8fc483247ff8800059e12d7caf1&quot; title=&quot;https://www.nowcoder.com/practice/7a0da8fc483247ff8800059e12d7caf1&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;#47 求 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;1 + 2 + \cdots + n&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;： 逻辑运算符，递归&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;export function Sum_Solution(n: number): number {
  return n &amp;gt; 0 &amp;amp;&amp;amp; Sum_Solution(n - 1) + n
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.nowcoder.com/practice/59ac416b4b944300b617d4f7f111b215&quot; title=&quot;https://www.nowcoder.com/practice/59ac416b4b944300b617d4f7f111b215&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;#48 不用加减乘除做加法&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;： 位运算，模拟&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;export function Add(num1: number, num2: number): number {
  let result = 0
  for (; num1 &amp;gt; 0 &amp;amp;&amp;amp; num2 &amp;gt; 0; ) {
    let a = num1 &amp;amp; -num1
    let b = num2 &amp;amp; -num2
    if (a &amp;lt;= b) {
      num1 ^= a
      for (; a &amp;amp; result; a &amp;lt;&amp;lt;= 1) result ^= a
      result ^= a
    } else {
      num2 ^= b
      for (; b &amp;amp; result; b &amp;lt;&amp;lt;= 1) result ^= b
      result ^= b
    }
  }

  if (num2 &amp;gt; 0) num1 = num2
  for (; num1 &amp;gt; 0; ) {
    let a = num1 &amp;amp; -num1
    num1 ^= a
    for (; a &amp;amp; result; a &amp;lt;&amp;lt;= 1) result ^= a
    result ^= a
  }
  return result
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.nowcoder.com/practice/1277c681251b4372bdef344468e4f26e&quot; title=&quot;https://www.nowcoder.com/practice/1277c681251b4372bdef344468e4f26e&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;#49 把字符串转换成整数&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;： 十进制，模拟&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;export function StrToInt(str: string): number {
  if (str == null || str.length &amp;lt;= 0) return 0

  let flag: boolean = false
  let result = 0
  let i = 0

  if (str[0] === &apos;+&apos;) i += 1
  else if (str[0] === &apos;-&apos;) {
    flag = true
    i += 1
  }

  if (i &amp;gt;= str.length) return 0
  const zero = &apos;0&apos;.codePointAt(0)
  for (; i &amp;lt; str.length; ++i) {
    const x = str.codePointAt(i) - zero
    if (x &amp;lt; 0 || x &amp;gt; 9) return 0
    result = result * 10 + x
  }
  return flag &amp;amp;&amp;amp; result ? -result : result
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.nowcoder.com/practice/6fe361ede7e54db1b84adc81d09d8524&quot; title=&quot;https://www.nowcoder.com/practice/6fe361ede7e54db1b84adc81d09d8524&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;#50 数组中重复的数字&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;： 判重&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;export function duplicate(nums: number[]): number {
  const _size: number = nums.length
  const set: Set = new Set()

  // 校验输入数据
  for (const x of nums) {
    if (x &amp;lt; 0 || x &amp;gt;= _size) return -1
  }

  for (const x of nums) {
    if (set.has(x)) return x
    set.add(x)
  }
  return -1
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.nowcoder.com/practice/94a4d381a68b47b7a8bed86f2975db46&quot; title=&quot;https://www.nowcoder.com/practice/94a4d381a68b47b7a8bed86f2975db46&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;#51 构建乘积数组&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;： 前缀积、后缀积&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;export function multiply(A: number[]): number[] {
  const _size = A.length
  const s1: number[] = [A[0]]
  const s2: number[] = []
  s2[_size - 1] = A[_size - 1]

  for (let i = 1; i &amp;lt; _size; ++i) s1[i] = s1[i - 1] * A[i]
  for (let i = _size - 2; i &amp;gt;= 0; --i) s2[i] = s2[i + 1] * A[i]

  const B: number[] = [s2[1]]
  for (let i = 1; i + 1 &amp;lt; _size; ++i) B[i] = s1[i - 1] * s2[i + 1]
  B[_size - 1] = s1[_size - 2]
  return B
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.nowcoder.com/practice/28970c15befb4ff3a264189087b99ad4&quot; title=&quot;https://www.nowcoder.com/practice/28970c15befb4ff3a264189087b99ad4&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;#52 正则表达式匹配&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;： 字符串，匹配，递归&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;export function match(str: string, pattern: string): boolean {
  return f(0, 0)

  function f(i: number, j: number): boolean {
    if (i === str.length) {
      if ((pattern.length - j) &amp;amp; 1) return false
      for (j += 1; j &amp;lt; pattern.length; j += 2) {
        if (pattern[j] !== &apos;*&apos;) return false
      }
      return true
    }
    if (j === pattern.length) return false

    const x = pattern[j]
    switch (x) {
      case &apos;*&apos;: {
        return f(i, j + 1)
      }
      case &apos;.&apos;: {
        if (j + 1 &amp;lt; pattern.length &amp;amp;&amp;amp; pattern[j + 1] === &apos;*&apos;) {
          for (; i &amp;lt;= str.length; ++i) {
            if (f(i, j + 2)) return true
          }
          return false
        }
        return f(i + 1, j + 1)
      }
      default: {
        if (j + 1 &amp;lt; pattern.length &amp;amp;&amp;amp; pattern[j + 1] === &apos;*&apos;) {
          if (f(i, j + 2)) return true
          for (; i &amp;lt; str.length &amp;amp;&amp;amp; str[i] === x; ++i) if (f(i + 1, j + 2)) return true
          return false
        }
        return str[i] === x &amp;amp;&amp;amp; f(i + 1, j + 1)
      }
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.nowcoder.com/practice/e69148f8528c4039ad89bb2546fd4ff8&quot; title=&quot;https://www.nowcoder.com/practice/e69148f8528c4039ad89bb2546fd4ff8&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;#53 表示数值的字符串&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;： 字符串，匹配，正则表达式&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;export function isNumeric(str) {
  return /^[+-]?(\.\d+|\d+(\.\d+)?)([eE][+-]?\d+)?$/.test(str)
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.nowcoder.com/practice/00de97733b8e4f97a3fb5c680ee10720&quot; title=&quot;https://www.nowcoder.com/practice/00de97733b8e4f97a3fb5c680ee10720&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;#54 字符流中第一个不重复的字符&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;： Set&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;const queue: string[] = []
const set1: Set = new Set()
const set2: Set = new Set()

export function Init(): void {
  queue.length = 0
  set1.clear()
  set2.clear()
}

export function Insert(ch: string): void {
  if (set1.has(ch)) return void set2.add(ch)
  set1.add(ch)
  queue.push(ch)
}

export function FirstAppearingOnce(): string {
  while (queue.length &amp;gt; 0) {
    const x = queue[0]
    if (set2.has(x)) queue.shift()
    else return x
  }
  return &apos;#&apos;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.nowcoder.com/practice/253d2c59ec3e4bc68da16833f79a38e4&quot; title=&quot;https://www.nowcoder.com/practice/253d2c59ec3e4bc68da16833f79a38e4&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;#55 链表中环的入口结点&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;： 链表，双指针&lt;/span&gt;&lt;/p&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;参见 &lt;/span&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;/post/quiz/partition/find-duplicate-number/#heading-%E8%BF%BD%E5%87%BB%E6%B3%95&quot; title=&quot;/post/quiz/partition/find-duplicate-number/#heading-%E8%BF%BD%E5%87%BB%E6%B3%95&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;不修改数组找出重复的数字#追击法&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;interface ListNode {
  val: number
  next: ListNode | null
}

export function EntryNodeOfLoop(pHead: ListNode): ListNode | null {
  if (pHead == null) return null

  let slower: ListNode | null = pHead
  let faster: ListNode | null = pHead
  do {
    faster = faster.next
    if (faster == null) return null

    faster = faster.next
    if (faster == null) return null

    slower = slower.next
  } while (slower !== faster)

  slower = pHead
  while (slower !== faster) {
    faster = faster.next
    slower = slower.next
  }
  return slower
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.nowcoder.com/practice/fc533c45b73a41b0b44ccba763f866ef&quot; title=&quot;https://www.nowcoder.com/practice/fc533c45b73a41b0b44ccba763f866ef&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;#56 删除链表中重复的节点&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;： 链表，递归&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;interface ListNode {
  val: number
  next: ListNode | null
}

export function deleteDuplication(o: ListNode | null): ListNode | null {
  if (o == null) return null

  let v = o.next
  if (v == null) return o

  o.next = null
  if (o.val === v.val) {
    for (v = v.next; v != null; v = v.next) if (o.val != v.val) break
    return deleteDuplication(v)
  }
  o.next = deleteDuplication(v)
  return o
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.nowcoder.com/practice/9023a0c988684a53960365b889ceaf5e&quot; title=&quot;https://www.nowcoder.com/practice/9023a0c988684a53960365b889ceaf5e&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;#57 二叉树的下一个结点&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;： 二叉树，中序遍历&lt;/span&gt;&lt;/p&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;中序遍历的下一个节点要么是第一个还没有遍历右子节点的祖先节点，要么是右子树中的最左叶子节点。&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;interface TreeLinkNode {
  val: number
  left: TreeLinkNode | null
  right: TreeLinkNode | null
  next: TreeLinkNode | null
}

export function GetNext(o: TreeLinkNode): TreeLinkNode | null {
  if (o.right) {
    let v = o.right
    while (v.left) v = v.left
    return v
  }

  let v = o.next
  while (v &amp;amp;&amp;amp; v.right === o) {
    o = v
    v = v.next
  }
  return v
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.nowcoder.com/practice/ff05d44dfdb04e1d83bdbdab320efbcb&quot; title=&quot;https://www.nowcoder.com/practice/ff05d44dfdb04e1d83bdbdab320efbcb&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;#58 对称的二叉树&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;： 二叉树，BFS&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;interface TreeNode {
  val: number
  left: TreeNode | null
  right: TreeNode | null
}

interface Item {
  node: TreeNode
  step: number
}

export function isSymmetrical(pRoot: TreeNode): boolean {
  if (pRoot == null) return true
  const Q: Item[] = []
  const nums: number[] = []
  let tot = 0

  let currentStep = 0
  Q.push({ node: pRoot, step: 0 })
  while (Q.length &amp;gt; 0) {
    const { node, step } = Q.shift()

    if (currentStep &amp;lt; step) {
      currentStep = step
      for (let i = 0, j = tot - 1; i &amp;lt; j; ++i, --j) {
        if (nums[i] !== nums[j]) return false
      }
      tot = 0
    }

    if (node == null) nums[tot++] = -Number.MAX_SAFE_INTEGER
    else {
      nums[tot++] = node.val
      Q.push({ node: node.left, step: step + 1 })
      Q.push({ node: node.right, step: step + 1 })
    }
  }

  for (let i = 0, j = tot - 1; i &amp;lt; j; ++i, --j) {
    if (nums[i] !== nums[j]) return false
  }
  return true
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.nowcoder.com/practice/91b69814117f4e8097390d107d2efbe0&quot; title=&quot;https://www.nowcoder.com/practice/91b69814117f4e8097390d107d2efbe0&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;#59 按之字形顺序打印二叉树&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;： 二叉树，BFS&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;interface TreeNode {
  val: number
  left: TreeNode | null
  right: TreeNode | null
}

interface Item {
  node: TreeNode
  step: number
}

export function Print(o: TreeNode): number[][] {
  if (o == null) return []
  const Q: Item[] = []
  const results: number[][] = []
  const tmp: number[] = []
  let currentStep = 1
  let tot = 0
  Q.push({ node: o, step: 1 })

  while (Q.length &amp;gt; 0) {
    const { node, step } = Q.shift()
    if (currentStep &amp;lt; step) {
      const result: number[] = tmp.slice(0, tot)
      if (step &amp;amp; 1) result.reverse()
      results.push(result)
      tot = 0
      currentStep = step
    }
    tmp[tot++] = node.val
    if (node.left) Q.push({ node: node.left, step: step + 1 })
    if (node.right) Q.push({ node: node.right, step: step + 1 })
  }
  const result: number[] = tmp.slice(0, tot)
  if ((currentStep + 1) &amp;amp; 1) result.reverse()
  results.push(result)
  return results
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.nowcoder.com/practice/445c44d982d04483b04a54f298796288&quot; title=&quot;https://www.nowcoder.com/practice/445c44d982d04483b04a54f298796288&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;#60 把二叉树打印成多行&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;： 二叉树，BFS&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;interface TreeNode {
  val: number
  left: TreeNode | null
  right: TreeNode | null
}

interface Item {
  node: TreeNode
  step: number
}

export function Print(o: TreeNode): number[][] {
  if (o == null) return []

  const Q: Item[] = []
  const results: number[][] = []
  const tmp: number[] = []

  let currentStep = 1
  let tot = 0
  Q.push({ node: o, step: 1 })

  while (Q.length &amp;gt; 0) {
    const { node, step } = Q.shift()
    if (currentStep &amp;lt; step) {
      const result = tmp.slice(0, tot)
      results.push(result)
      tot = 0
      currentStep = step
    }
    tmp[tot++] = node.val
    if (node.left) Q.push({ node: node.left, step: step + 1 })
    if (node.right) Q.push({ node: node.right, step: step + 1 })
  }
  const result = tmp.slice(0, tot)
  results.push(result)
  return results
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.nowcoder.com/practice/cf7e25aa97c04cc1a68c8f040e71fb84&quot; title=&quot;https://www.nowcoder.com/practice/cf7e25aa97c04cc1a68c8f040e71fb84&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;#61 序列化二叉树&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;： 二叉树，重建二叉树&lt;/span&gt;&lt;/p&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;序列化：求出先序遍历和后序遍历，并将其转为字符串&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;反序列化：通过先序遍历和后序遍历结果重建树&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;interface TreeNode {
  val: number
  left: TreeNode | null
  right: TreeNode | null
}

export function Serialize(pRoot: TreeNode): string {
  const pre: number[] = []
  const vin: number[] = []

  if (pRoot != null) {
    preTraverse(pRoot)
    vinTraverse(pRoot)
  }

  return pre.join(&apos;,&apos;) + &apos;#&apos; + vin.join(&apos;,&apos;)

  function preTraverse(o) {
    if (o == null) return
    pre.push(o.val)
    preTraverse(o.left)
    preTraverse(o.right)
  }

  function vinTraverse(o) {
    if (o == null) return
    vinTraverse(o.left)
    vin.push(o.val)
    vinTraverse(o.right)
  }
}

export function Deserialize(s: string): TreeNode {
  if (s === &apos;#&apos;) return null
  const [x, y] = s.split(&apos;#&apos;)
  const pre: number[] = x.split(&apos;,&apos;).map(n =&amp;gt; Number(n))
  const vin: number[] = y.split(&apos;,&apos;).map(n =&amp;gt; Number(n))

  if (pre.length &amp;lt;= 0 || pre.length !== vin.length) return null
  const tree = f(0, pre.length - 1, 0, vin.length - 1)
  return tree

  function f(lft1: number, rht1: number, lft2: number, rht2: number): TreeNode {
    const t = pre[lft1]
    let x = lft2
    for (; x &amp;lt;= rht2; ++x) if (vin[x] === t) break

    const o = {
      val: t,
      left: lft2 &amp;lt; x ? f(lft1 + 1, lft1 + x - lft2, lft2, x - 1) : null,
      right: rht2 &amp;gt; x ? f(lft1 + x - lft2 + 1, rht1, x + 1, rht2) : null,
    }
    return o
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.nowcoder.com/practice/ef068f602dde4d28aab2b210e859150a&quot; title=&quot;https://www.nowcoder.com/practice/ef068f602dde4d28aab2b210e859150a&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;#62 二叉搜索树的第k个结点&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;： 二叉树，二叉搜索树，中序遍历&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;interface TreeNode {
  val: number
  left: TreeNode | null
  right: TreeNode | null
}

export function KthNode(pRoot: TreeNode, k: number): TreeNode | null {
  let result = null
  if (k &amp;lt;= 0) return result

  f(pRoot)
  return result

  function f(o: TreeNode): void {
    if (o == null) return

    f(o.left)
    if (result != null) return

    k -= 1
    if (k &amp;lt;= 0) {
      result = o
      return
    }

    f(o.right)
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.nowcoder.com/practice/9be0172896bd43948f8a32fb954e1be1&quot; title=&quot;https://www.nowcoder.com/practice/9be0172896bd43948f8a32fb954e1be1&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;#63 数据流中的中位数&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;： 堆，中位数&lt;/span&gt;&lt;/p&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;维护一个大根堆和小根堆：&lt;/span&gt;&lt;/p&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;大根堆中存放当前数列中前 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\left\lceil\frac{N}{2}\right\rceil&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 小的元素；&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;小根堆中存放当前数列中后 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;\left\lfloor\frac{N}{2}\right\rfloor&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 大的元素；&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;则每次查询时，若元素个数为奇数，直接返回大根堆的根顶元素；否则，即元素个数为偶数，返回小根堆和大根堆的堆顶元素的平均值。&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;import { PriorityQueue } from &apos;@algorithm.ts/queue&apos;

const lowerQ = new PriorityQueue({ compare: (x, y) =&amp;gt; y - x })
const upperQ = new PriorityQueue({ compare: (x, y) =&amp;gt; x - y })

export function Insert(num: number): void {
  if (lowerQ.size === upperQ.size) {
    upperQ.enqueue(num)
    lowerQ.enqueue(upperQ.dequeue()!)
  } else {
    lowerQ.enqueue(num)
    upperQ.enqueue(lowerQ.dequeue()!)
  }
}

export function GetMedian(): number {
  return lowerQ.size === upperQ.size ? (lowerQ.front()! + upperQ.front()!) / 2 : lowerQ.front()!
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.nowcoder.com/practice/1624bc35a45c42c0bc17d17fa0cba788&quot; title=&quot;https://www.nowcoder.com/practice/1624bc35a45c42c0bc17d17fa0cba788&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;#64 滑动窗口的最大值&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;： 滑动窗口，单调栈&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;export function maxInWindows(nums: number[], size: number): number[] {
  if (nums.length &amp;lt; size || size &amp;lt;= 0) return []
  const stack: number[] = []
  for (let i = 0, _end = size - 1; i &amp;lt; _end; ++i) {
    const x = nums[i]
    while (stack.length &amp;gt; 0 &amp;amp;&amp;amp; x &amp;gt;= nums[stack[stack.length - 1]]) stack.pop()
    stack.push(i)
  }

  const results: number[] = []
  for (let i = size - 1; i &amp;lt; nums.length; ++i) {
    const x = nums[i]
    if (stack.length &amp;gt; 0 &amp;amp;&amp;amp; stack[0] + size === i) stack.shift()
    while (stack.length &amp;gt; 0 &amp;amp;&amp;amp; x &amp;gt;= nums[stack[stack.length - 1]]) stack.pop()
    stack.push(i)
    results.push(nums[stack[0]])
  }
  return results
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.nowcoder.com/practice/2a49359695a544b8939c77358d29b7e6&quot; title=&quot;https://www.nowcoder.com/practice/2a49359695a544b8939c77358d29b7e6&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;#65 矩阵中的路径&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;： 模拟，矩阵&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;export function hasPath(matrix: string[][], word: string): boolean {
  if (word == null || word.length &amp;lt;= 0) return true

  const R = matrix.length
  if (R &amp;lt;= 0) return false

  const C = matrix[0].length
  if (C &amp;lt;= 0) return false

  const vis: boolean[][] = []
  for (let x = 0; x &amp;lt; R; ++x) vis[x] = new Array(C).fill(false)

  for (let x = 0; x &amp;lt; R; ++x) {
    for (let y = 0; y &amp;lt; C; ++y) {
      if (f(x, y, 0)) return true
    }
  }
  return false

  function f(x: number, y: number, cur: number): boolean {
    if (x &amp;lt; 0 || x &amp;gt;= R || y &amp;lt; 0 || y &amp;gt;= C) return false
    if (vis[x][y] || matrix[x][y] !== word[cur]) return false

    cur += 1
    if (cur === word.length) return true

    vis[x][y] = true
    const flag = f(x - 1, y, cur) || f(x + 1, y, cur) || f(x, y - 1, cur) || f(x, y + 1, cur)
    vis[x][y] = false
    return flag
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.nowcoder.com/practice/6e5207314b5241fb83f2329e89fdecc8&quot; title=&quot;https://www.nowcoder.com/practice/6e5207314b5241fb83f2329e89fdecc8&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;#66 机器人的运动范围&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;： BFS&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;export function movingCount(threshold: number, rows: number, cols: number): number {
  const vis: boolean[][] = []
  for (let i = 0; i &amp;lt; rows; ++i) {
    vis[i] = new Array(cols).fill(false)
  }

  let result = 0
  f(0, 0)
  return result

  function f(x: number, y: number): void {
    if (x &amp;lt; 0 || x &amp;gt;= rows || y &amp;lt; 0 || y &amp;gt;= cols) return
    if (vis[x][y]) return
    vis[x][y] = true
    const total = weight(x) + weight(y)
    if (total &amp;gt; threshold) return

    result += 1
    f(x - 1, y)
    f(x + 1, y)
    f(x, y - 1)
    f(x, y + 1)
  }

  function weight(x: number): number {
    let result = 0
    for (let i = x; i &amp;gt; 0; i = Math.round((i - (i % 10)) / 10)) result += i % 10
    return result
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://www.nowcoder.com/practice/57d85990ba5b440ab888fc72b0751bf8&quot; title=&quot;https://www.nowcoder.com/practice/57d85990ba5b440ab888fc72b0751bf8&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;#67 剪绳子&lt;/span&gt;&lt;/a&gt;&lt;span class=&quot;yozora-text&quot;&gt;： 数学&lt;/span&gt;&lt;/p&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;考虑剪下来的某段长为 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 的绳子，继续减它不会对其它段造成影响。&lt;/span&gt;&lt;/p&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;若 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;x &amp;gt; 4&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;，则必然有 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;(x - 3) \times 3 &amp;gt; x&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt;；&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;当 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;x \leqslant 4&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 时，有 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;x &amp;gt; (x-1) \times 1 \geqslant (x-2) \times 2&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;yozora-paragraph&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;这启发我们尽可能将绳子剪成长度为 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 的段，而当某一段长度为 &lt;/span&gt;&lt;span class=&quot;yozora-inline-math&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;yozora-text&quot;&gt; 时，则不再剪它。&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;yozora-code&quot;&gt;&lt;code&gt;export function cutRope(n: number): number {
  if (n &amp;lt;= 0) return 0
  if (n &amp;lt;= 4) return n

  let result = 1
  if (n % 3 === 1) {
    result = 4
    n -= 4
  } else if (n % 3 === 2) {
    result = 2
    n -= 2
  }
  for (; n &amp;gt; 0; n -= 3) {
    result *= 3
  }
  return result
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 class=&quot;yozora-heading&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;Related&lt;/span&gt;&lt;/h2&gt;&lt;ul class=&quot;yozora-list&quot;&gt;&lt;li class=&quot;yozora-list-item&quot;&gt;&lt;a class=&quot;yozora-link&quot; href=&quot;https://github.com/guanghechen/algorithm.ts/tree/main/packages/queue&quot; title=&quot;https://github.com/guanghechen/algorithm.ts/tree/main/packages/queue&quot; target=&quot;_blank&quot; rel=&quot;noopener,noreferrer&quot;&gt;&lt;span class=&quot;yozora-text&quot;&gt;@algorithm.ts/queue&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/main&gt;&lt;footer&gt;&lt;div class=&quot;yozora-footnote-definitions&quot;&gt;&lt;div class=&quot;yozora-footnote-definitions__title&quot;&gt;footnote-definitions&lt;/div&gt;&lt;ul class=&quot;yozora-footnote-definitions__main&quot;&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/footer&gt;&lt;/section&gt;</content:encoded></item></channel></rss>