<?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[Gatsby Starter Blog RSS Feed]]></title><description><![CDATA[Never put off till tomorrow what you can do today]]></description><link>https://sunyl.ink</link><generator>GatsbyJS</generator><lastBuildDate>Mon, 13 Feb 2023 14:11:48 GMT</lastBuildDate><item><title><![CDATA[归并排序 - Merge Sort]]></title><description><![CDATA[『归并排序』基于分支算法实现，时间复杂度为：O(nlogn)。一般有两种实现方式，一种是自顶向下的递归实现，但递归调用需要使用额外的栈空间，空间复杂度为 O(logn)；另一种是自底向上的时间方式，空间复杂度为常数级的 O(1)。 nlogn 比 n^…]]></description><link>https://sunyl.ink/merge-sort/</link><guid isPermaLink="false">https://sunyl.ink/merge-sort/</guid><pubDate>Thu, 22 Sep 2022 10:56:03 GMT</pubDate><content:encoded>&lt;p&gt;『归并排序』基于分支算法实现，&lt;strong&gt;时间复杂度为：O(nlogn)&lt;/strong&gt;。一般有两种实现方式，一种是自顶向下的递归实现，但递归调用需要使用额外的栈空间，空间复杂度为 O(logn)；另一种是自底向上的时间方式，空间复杂度为常数级的 O(1)。&lt;/p&gt;
&lt;h2&gt;nlogn 比 n^2 快多少？&lt;/h2&gt;
&lt;p&gt;下面的表格中简单比较了在 10 ～ 10^5 数据量下，nlogn 和 n^2 分别耗费的时间：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;n^2&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;nlogn&lt;/th&gt;
&lt;th&gt;faster&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;n = 10&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;100&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;33&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;n = 100&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;1000&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;664&lt;/td&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;n = 10^3&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;10^6&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;9966&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;n = 10^4&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;10^8&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;132877&lt;/td&gt;
&lt;td&gt;753&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;n = 10^5&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;10^10&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;1660964&lt;/td&gt;
&lt;td&gt;6020&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;最后一列 faster 表示了 nlogn 比 n^2 快了多少倍。在 n = 10 时，nlogn 只比 n^2 快了 3 倍，在现代的计算机上，可能只是 1ms 和 3ms 的差别，我们可能根本感知不到。&lt;/p&gt;
&lt;p&gt;但随着 n 的数据量逐渐增大，nlogn 比 n^2 的优势会越来越大，当 n = 10^5 时，这个数据量还没那么大的时候，nlogn 已经比 n^2 快了近 6000 倍。这是什么概念呢？如果我们处理一个 10^5 左右数据量的数据集，用 nlogn 的算法处理需要花费一天，那么用 n^2 的算法就需要花费六千天。&lt;/p&gt;
&lt;h2&gt;实现思路 - 自顶向下&lt;/h2&gt;
&lt;p&gt;现有一待排序数组 [8,6,2,3,1,5,7,4]。归并排序要做的事就是将该数组分成两半，然后分别将左边数组排序，再将右边数组排序，之后再将两部分归并起来：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/27a713d59abd6ca7c929513c62440fca/MergeSort-a.svg&quot; alt=&quot;MergeSort-a&quot;&gt;&lt;/p&gt;
&lt;p&gt;对左边和右边数组排序的过程，就是再将左边的数组和右边的数组分成两半，然后对每个部分排序：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/4bdfea0299b03b342e7a6d012d2bda21/MergeSort-2.svg&quot; alt=&quot;MergeSort-2&quot;&gt;&lt;/p&gt;
&lt;p&gt;对于上面数组的每个部分进行的排序操作，依然是先将其分半，之后再将其归并。等到数组不能再分的时候，也就是下图中的这种情况，每个元素就是单独的一半时，每个元素默认就是有序的了：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/333ee1471ec57590f3bc66fd8da9540c/MergeSort-3.svg&quot; alt=&quot;MergeSort-3&quot;&gt;&lt;/p&gt;
&lt;p&gt;上述待排序的数组共 8 个元素，每次将每个整块元素分为两块，共分了 3 次，也就是计算 8 除以 2 除几次等于 1，换算成数学公式就是以 2 为底的 8 的对数 等于 3:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/194b27c463ca2ae883c6566dcaa246d9/MergeSort-4.svg&quot; alt=&quot;MergeSort-4&quot;&gt;&lt;/p&gt;
&lt;p&gt;如果共有 N 个元素的话，就会分成 logN 个层级，如果 N 为奇数的话，向上取整就好了。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;===》 归并的过程：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;虽然每层都将元素分成了不同的部分，但每层要处理的元素个数是相同的。如果每层都能以 O(N) 的时间复杂度解决的话，那么就设计出了 Nlog(N) 级别的算法。&lt;/p&gt;
&lt;p&gt;实际上，这也是 Nlog(N) 时间复杂度算法的主要来源。通常都是上述这种二分法达到了 log(N) 的层级，之后每个层级用 O(N) 级别的算法做事情。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;大概流程如下：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/72d545696dbfbb9da1050580167397d0/Merge-a.svg&quot; alt=&quot;Merge-a&quot;&gt;&lt;/p&gt;
&lt;p&gt;对第三层中的每一半进行两两归并，结果如下↯↯&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/e33b35bfbd90b31e3f472f739aa07993/Merge-b.svg&quot; alt=&quot;Merge-b&quot;&gt;&lt;/p&gt;
&lt;p&gt;对第二层中的每一半进行两两归并，结果如下↯↯&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/20bdb93028bf137b5f7b28bdce73151a/Merge-c.svg&quot; alt=&quot;Merge-c&quot;&gt;&lt;/p&gt;
&lt;p&gt;🤔 &lt;span style=&quot;color:#ff6600&quot;&gt;现在需要解决的问题是，我们能不能将数组划分成两部分，之后将这两部分都分别排好序后，使用 O(N) 级别的算法，将它们归并到一起，形成一个新的数组呢？&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;答案是可以的。&lt;span style=&apos;color:#339933&apos;&gt;但是在原数组上操作比较困难，需要开辟一块新的空间。这也是归并排序与其他排序不同的地方。归并排序虽然能将算法的时间复杂度提升到 O(nlogn) 级别，但是它比其他的算法要多使用了额外的空间。&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;归并的过程简单描述如下：&lt;/p&gt;
&lt;p&gt;使用索引 k 指向原数组，索引 i 指向辅助数组的左半部分的第一个元素，索引 j 指向辅助数组的右半部分的第一个元素：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/7a27e7664eb995e1984737ff57c603f6/merge-1.svg&quot; alt=&quot;merge-1&quot;&gt;&lt;/p&gt;
&lt;p&gt;比较 索引i 和 索引j 指向的元素的值，如果 nums[i] &gt; nums[j]，就将 nums[j] 的值赋给 索引k 指向的值，并将 索引j 和 索引k 移动到下一个元素：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/2e0f3690d89c0804397878554827e86c/merge-2.svg&quot; alt=&quot;merge-2&quot;&gt;&lt;/p&gt;
&lt;p&gt;重复上述操作：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/2dd15b596f0111f8e2fcb0a4ee86b096/merge-3.svg&quot; alt=&quot;merge-3&quot;&gt;&lt;/p&gt;
&lt;p&gt;为了避免 i 和 j 越界，需要设置 l、r 和 mid 指针分别指向如下位置：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/35e6eb245bf4d9be2d7c7bb65d4629ec/merge-4.svg&quot; alt=&quot;merge-4&quot;&gt;&lt;/p&gt;
&lt;h3&gt;码一下&lt;/h3&gt;
&lt;h4&gt;Java 版&lt;/h4&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MergeSort&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sort&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&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; nums&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;mergeSort&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;nums&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; nums&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&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 comment&quot;&gt;/**
     * 递归使用归并排序，对 arr[l...r] 的范围进行排序
     *
     * @param nums
     * @param l
     * @param r
     */&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;mergeSort&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&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; nums&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; l&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; r&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;// 处理递归到底的情况&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;l &lt;span class=&quot;token operator&quot;&gt;&gt;=&lt;/span&gt; r&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 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;int&lt;/span&gt; mid &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; l &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;r &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; l&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 number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;mergeSort&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;nums&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; l&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; mid&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;mergeSort&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;nums&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; mid &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; r&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;merge&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;nums&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; l&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; mid&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; r&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 comment&quot;&gt;/**
     * 对 arr[l...mid] 和 arr[mid+1...r] 两部分进行合并
     *
     * @param nums
     * @param l
     * @param mid
     * @param r
     */&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;merge&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&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; nums&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; l&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; mid&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; r&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;// 辅助的空间，aux -&gt; auxiliary&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;int&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; aux &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 keyword&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;r &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; l &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&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;// 初始化辅助空间的值，aux 数组从索引 0 开始赋值，但 nums 数组从 l 开始遍历，所以赋值时需要设置 i-l 的偏移量&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;int&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; l&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;&amp;lt;=&lt;/span&gt; r&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&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;
            aux&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; l&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; nums&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&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 comment&quot;&gt;// 索引 i 和 j 分别指向 mid 指针左右两侧的首个元素&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; l&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; j &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; mid &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&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;int&lt;/span&gt; k &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; l&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; k &lt;span class=&quot;token operator&quot;&gt;&amp;lt;=&lt;/span&gt; r&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; k&lt;span class=&quot;token operator&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;i &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; mid&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                nums&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;k&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; aux&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;j &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; l&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                j&lt;span class=&quot;token operator&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;else&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;j &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; r&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                nums&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;k&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; aux&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; l&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                i&lt;span class=&quot;token operator&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;else&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;aux&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; l&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; aux&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;j &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; l&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;
                nums&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;k&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; aux&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; l&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                i&lt;span class=&quot;token operator&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;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                nums&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;k&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; aux&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;j &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; l&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                j&lt;span class=&quot;token operator&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 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;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;GoLang 版&lt;/h4&gt;
&lt;h3&gt;优化&lt;/h3&gt;
&lt;h4&gt;优化一&lt;/h4&gt;
&lt;p&gt;在合并两部分数组之前，就已经保证了这两部分数组，从 l ~ mid，mid+1 ~ r 都是有序的，那么如果 nums[mid] &amp;#x3C;= nums[mid+1]，就无需在执行合并两部分数组的操作了，因为这两部分已经是有序的了。所以只有当 nums[mid] &gt; nums[mid+1] 时，才需要执行 merge 操作。修改如下：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MergeSortOptimizeOne&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sort&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&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; nums&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;mergeSort&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;nums&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; nums&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&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;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;mergeSort&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&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; nums&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; l&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; r&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;l &lt;span class=&quot;token operator&quot;&gt;&gt;=&lt;/span&gt; r&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 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;int&lt;/span&gt; mid &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; l &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;r &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; l&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 number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;mergeSort&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;nums&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; l&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; mid&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;mergeSort&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;nums&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; mid &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; r&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;nums&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;mid&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; nums&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;mid &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&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 function&quot;&gt;merge&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;nums&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; l&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; mid&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; r&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 punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;merge&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&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; nums&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; l&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; mid&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; r&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;int&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; aux &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 keyword&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;r &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; l &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&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;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; l&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;&amp;lt;=&lt;/span&gt; r&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&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;
            aux&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; l&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; nums&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&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;int&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; l&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; j &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; mid &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&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;int&lt;/span&gt; k &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; l&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; k &lt;span class=&quot;token operator&quot;&gt;&amp;lt;=&lt;/span&gt; r&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; k&lt;span class=&quot;token operator&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;i &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; mid&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                nums&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;k&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; aux&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;j &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; l&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                j&lt;span class=&quot;token operator&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;else&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;j &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; r&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                nums&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;k&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; aux&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; l&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                i&lt;span class=&quot;token operator&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;else&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;aux&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; l&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; aux&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;j &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; l&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;
                nums&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;k&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; aux&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; l&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                i&lt;span class=&quot;token operator&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;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                nums&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;k&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; aux&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;j &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; l&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                j&lt;span class=&quot;token operator&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 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;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;优化二&lt;/h4&gt;
&lt;p&gt;当数组内待排序的元素个数比较小时，整个数组近乎有序的概率就比较大，此时可以将归并排序替换为插入排序，因为在近乎有序的情况下，插入排序更有优势。&lt;/p&gt;
&lt;h2&gt;实现思路 - 自底向上&lt;/h2&gt;
&lt;p&gt;现在有一待排序数组：[8,6,2,3,1,5,7,4]，从左到右将该数组中的元素依次划分成小段，比如两个元素一个小段：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/2374f691ab77eba63318cf1d250c4d2d/ms.svg&quot; alt=&quot;ms-m600&quot;&gt;&lt;/p&gt;
&lt;p&gt;进行归并排序：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/8fbf8d14c04c7e031e111a2159e27685/ms-01.svg&quot; alt=&quot;ms-01-m600&quot;&gt;&lt;/p&gt;
&lt;p&gt;当归并元素排序完成一轮之后，再 4 个元素一个小段的进行归并排序：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/537d86f4c8e4a035a496a5f54ef33b1f/ms-02.svg&quot; alt=&quot;ms-02-m600&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/e5be2e2a4e8057f3123d30442e773939/ms-03.svg&quot; alt=&quot;ms-03-m600&quot;&gt;&lt;/p&gt;
&lt;p&gt;最后再 8 个元素一个小段，在当前示例中也就是整个数组全体，进行归并排序：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/1487a1e2b82b8526babdca1b23ac4dd4/ms-04.svg&quot; alt=&quot;ms-04-m600&quot;&gt;&lt;/p&gt;
&lt;p&gt;最终完成了整个数组的排序过程。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/7dd5d77224402640c78d6f936eb419b0/ms-05.svg&quot; alt=&quot;ms-05-m600&quot;&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[ICMP 与 Ping]]></title><description><![CDATA[🫠 无论是在宿舍还是办公室，或者是运维一个数据中心，总会遇到网络不通的问题。那台机器明明就在那里，你甚至可以通过机器的终端连上去看。它看着好好的，可是就是连不上去，究竟是哪里出了问题呢？ ICMP…]]></description><link>https://sunyl.ink/icmp-and-ping/</link><guid isPermaLink="false">https://sunyl.ink/icmp-and-ping/</guid><pubDate>Tue, 20 Sep 2022 10:12:03 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;🫠 无论是在宿舍还是办公室，或者是运维一个数据中心，总会遇到网络不通的问题。那台机器明明就在那里，你甚至可以通过机器的终端连上去看。它看着好好的，可是就是连不上去，究竟是哪里出了问题呢？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;ICMP 协议&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;😎 一般情况下，你可能会想到 &lt;code class=&quot;language-text&quot;&gt;ping&lt;/code&gt; 一下，那你知道 &lt;code class=&quot;language-text&quot;&gt;ping&lt;/code&gt; 是如何工作的么？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;span style=&apos;color:#0066ff&apos;&gt;&lt;code class=&quot;language-text&quot;&gt;ping&lt;/code&gt; 是基于 ICMP 协议工作的。ICMP 全称为 Internet Control Message Protocol，互联网控制报文协议。&lt;/span&gt;&lt;/strong&gt;这里面的关键词是 “控制”，那具体是怎么 “控制” 的呢？&lt;/p&gt;
&lt;p&gt;网络包在异常复杂的网络环境中传输时，常常会遇到各种各样的问题。当遇到问题的时候，总不能 “死个不明不白”，要传出消息来，报告情况，这样才能调整传输策略。这就相当于我们经常看到的电视剧里，古代行军的时候，为将为帅者需要通过侦查兵、哨探或传令兵等人肉的方式来掌握情况，控制整个战局。&lt;/p&gt;
&lt;p&gt;&lt;span style=&apos;color:#ff6600&apos;&gt;ICMP 报文是封装在 IP 包里面的。因为传输指令的时候肯定需要源地址和目标地址。&lt;/span&gt;它本身非常简单，因为要做侦察兵，要轻装上阵，不能携带大量的物品。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/1cc86cd4522089f349ff67015fc9675a/eda84/abc.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 41.139240506329116%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAIAAAB2/0i6AAAACXBIWXMAAAsTAAALEwEAmpwYAAABQUlEQVQY042QTW+CQBCG9/8fe2nSNKGRgrArKIKa9AtFRSnaNk1pIy0hkp68WFaisMtCY0k89CPpk8lkDvNM8g5o1MTz41Pu6Fg84ZqCnKZpWZYFY8UBxso/AIH/Giz8t4Ufhcv3ZVSwovw3IE3T2Xg6tx13ZD+6d5O+NbOntjlwrNH4pm+b1mQwpIT+LuM4NqCq1ZHK13uq1hJhT9U0CV3p3ct210CqJqFksyGEZF+kux2ltAoEft5jjOV5niRJjLEztA2kyhzfEuWWKEOOhxz/dP9QrQFCiOd5URTt/1QUVc+yDMd4u93Op7e6rLQl1Gk0O6ipSUiHinVx7XvPvvcCgiAQBME0zco8gDFef6yd4diAqlITdajoUIFnvFaHMsc3aoIOFRCGoeu6q9Xqm3yIQAmhlBJCqoESklO6rzz/BB16pTkvlYrQAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;abc-m600&quot;
        title=&quot;abc-m600&quot;
        src=&quot;/static/1cc86cd4522089f349ff67015fc9675a/f058b/abc.png&quot;
        srcset=&quot;/static/1cc86cd4522089f349ff67015fc9675a/c26ae/abc.png 158w,
/static/1cc86cd4522089f349ff67015fc9675a/6bdcf/abc.png 315w,
/static/1cc86cd4522089f349ff67015fc9675a/f058b/abc.png 630w,
/static/1cc86cd4522089f349ff67015fc9675a/40601/abc.png 945w,
/static/1cc86cd4522089f349ff67015fc9675a/78612/abc.png 1260w,
/static/1cc86cd4522089f349ff67015fc9675a/eda84/abc.png 3043w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;ICMP 报文有很多的类型，不同的类型有不同的代码。&lt;strong&gt;&lt;span style=&apos;color:#0066ff&apos;&gt;最常用的类型是主动请求为 8，主动请求的应答为 0&lt;/span&gt;&lt;/strong&gt;。&lt;/p&gt;
&lt;h3&gt;查询报文类型&lt;/h3&gt;
&lt;p&gt;我们经常在电视剧里听到这样的对话：主帅说，来人呐～前方战事如何，快派人去打探，一有情况，立刻通报！&lt;/p&gt;
&lt;p&gt;这种是主帅发起的，主动查看敌情，对应 ICMP 的 &lt;strong&gt;查询报文类型&lt;/strong&gt;。例如，&lt;strong&gt;&lt;span style=&apos;color:#0066ff&apos;&gt;常用的 &lt;code class=&quot;language-text&quot;&gt;ping&lt;/code&gt; 就是查询报文，是一种主动请求，并且获得主动应答的 ICMP 协议。&lt;/span&gt;&lt;/strong&gt;，所以，&lt;code class=&quot;language-text&quot;&gt;ping&lt;/code&gt; 发的包也是符合 ICMP 协议格式的，只不过它在后面增加了自己的格式。&lt;/p&gt;
&lt;h4&gt;ping&lt;/h4&gt;
&lt;p&gt;对 &lt;code class=&quot;language-text&quot;&gt;ping&lt;/code&gt; 的主动请求，进行网络抓包，称为 &lt;strong&gt;ICMP ECHO REQUEST&lt;/strong&gt;。同理主动请求的回复，称为 &lt;strong&gt;ICMP ECHO REPLY&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;比起原生的 ICMP，这里面多了两个字段:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;一个是&lt;span style=&apos;color:#ff6600&apos;&gt;『标识符』&lt;/span&gt;。这个很好理解，你派出去两队侦查兵，一队是侦查战况的，一队是去查找水源的，要有个标识才能区分。&lt;/li&gt;
&lt;li&gt;另一个是&lt;span style=&apos;color:#ff6600&apos;&gt;『序号』&lt;/span&gt;。你派出去的侦查兵，都要有个编号。如果派出去 10 个，就说明前方战况不错；如果派出去 10 个，回来 2 个，就说明情况可能不妙。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在选项数据中，&lt;code class=&quot;language-text&quot;&gt;ping&lt;/code&gt; 还会存放发送请求的时间值，来计算往返时间，说明路程长短。&lt;/p&gt;
&lt;h3&gt;差错报文类型&lt;/h3&gt;
&lt;p&gt;主帅走着走着，突然来了一匹快马，上面的小兵气喘呼呼的：报告主公，大事不好啦～张将军惨遭埋伏，全军覆没啦！这种是异常情况发起的，来报告发生了不好的事情，对应 ICMP 的 &lt;strong&gt;差错报文类型&lt;/strong&gt;。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;下面举几个 ICMP 差错报文的例子。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;span style=&apos;color:#0066ff&apos;&gt;『终点不可达为 3，源抑制为 4，超时为 11，重定向为 5。』&lt;/span&gt;&lt;/strong&gt;这个是什么意思呢，下面来解释下。&lt;/p&gt;
&lt;h4&gt;终点不可达&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;😎 第一种是『终点不可达』。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;小兵：报告主公，您让把粮草送到张将军那里，结果没送到。&lt;/p&gt;
&lt;p&gt;如果你是主公，肯定会问，为啥送不到呀？具体的原因在代码中表示就是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;网络不可达代码为 0&lt;/li&gt;
&lt;li&gt;主机不可达代码为 1&lt;/li&gt;
&lt;li&gt;协议不可达代码为 2&lt;/li&gt;
&lt;li&gt;端口不可达代码为 3&lt;/li&gt;
&lt;li&gt;需要进行分片但设置了不分片代码为 4&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;🌀具体的场景就像这样：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;【网络不可达】主公，找不到地方呀？&lt;/li&gt;
&lt;li&gt;【主机不可达】主公，找到地方没这个人呀？&lt;/li&gt;
&lt;li&gt;【协议不可达】主公，找到地方，找到人，但口号没对上，人家天王盖地虎，我说 12345！&lt;/li&gt;
&lt;li&gt;【端口不可达】主公，找到地方，找到人，对了口号，事儿没对上，我去送粮草，人家说他们在等救兵。&lt;/li&gt;
&lt;li&gt;【需要进行分片但设置了不分片】主公，走到一半，山路狭窄，想换小车，但是您的将令，严禁换小车，就没办法送到了。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;源抑制&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;😎 第二种是『源抑制』。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;『源抑制』就是让源站放慢发送速度。小兵：报告主公，您粮草送的太多了，将士们吃不完呀！&lt;/p&gt;
&lt;h4&gt;时间超时&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;😎 第三种是『时间超时』。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;『时间超时』就是超过网络包的生存时间还是没到。小兵：报告主公，送粮草的人，自己把粮草吃完了，还没找到地方，已经饿死啦。&lt;/p&gt;
&lt;h4&gt;路由重定向&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;😎 第四种是『路由重定向』。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;『路由重定向』就是让下次发给另一个路由。小兵：报告主公，上次送粮草的人本来只要走一站地铁，现在让人家从五环绕，是不是太过分了呀！&lt;/p&gt;
&lt;h4&gt;小结&lt;/h4&gt;
&lt;p&gt;差错报文的结构相对复杂一些。除了前面还是 IP，ICMP 的前 8 个字节不变，&lt;span style=&apos;color:#ff6600&apos;&gt;后面则跟上出错的那个 IP 包的 IP 头和 IP 正文的前 8 个字节。&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;而且这类侦查兵特别恪尽职守，不但自己返回来报信，还把一部分遗物也带回来。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;侦查兵：报告主公，张将军已经战死沙场，这是张将军的印信和佩剑。&lt;/li&gt;
&lt;li&gt;主公：哈？张将军是怎么死的（可以查看 ICMP 的前 8 个字节）？这是张将军的佩剑没错，是他的剑呀（IP 数据包的头及正文的前 8 个字节）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;ping：查询报文类型的使用&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;😎 下面重点来看下 &lt;code class=&quot;language-text&quot;&gt;ping&lt;/code&gt; 的发送和接收过程。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;局域网&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d86d7a792769731384344d3d18b46347/417bd/456.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 62.65822784810127%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAAsTAAALEwEAmpwYAAACZElEQVQ4y31S+0/aUBTuv77oUHmVllLa8pggRN10EHT4SKUVEBQUSmkBdcuSZYlLlm0/ChX8lntEYxbnSb7cc8/5zvNeLhvTkFPSyEgq3olxpIUY0oJMSPISckoSZ7UTJPko3oky8ZLhKBK8BC0sIS3KFJeKxFDIb4KzL7oY9B1cDoaoH5lIBAWkeImgBQVkYyqaZg1qIIJESCQ7S6j4eRRyG7gajuD0bAz7DhrGMbhEOArZz0MNCkSS3vqfEF8OISMpaJhVKIEItJAIeTmEmC8AyReAvBSkIvFABPJKGFvZPLjyVgGl99sobxdxtLsHfacMfXeP9FVJwWpUQfO4RgVZcGnjA/aLpQf+p33iHxR3cVDcIXCV8gGMQx21IwPtkyZquoGqXkG9YiIra7Sf0+MalPkE+4USahUT5qGO88YpFWN8Fm/sHYLTAhEICyuILvoJNK4vgJgvSAkyMXU+Mk/7Y7tkHDa2uLDycC76EXmzhM1UBlyzWofbs2F3umg1T3E1GGLkDNDv9sjOuqjrBj7m1tFqPPgvzlrotNr4cnlNXBbr2n00jCq4tXgC2XgSOTVF3bA72WQNKUFGXk3T67MHYRzmY7tNiXFkZI30vJamb7a9tg7uyh1i1Hdhdyw43R6cjoXOWRv0nSybfD9ubjC0HVwPRrDaF7Ba58R1uz2y210L/Y6Fr9efwWEu0+kUL8nk9hY/v32HNx7jf/I8lru/vwfDbDolB+HuDndzTMZj/Pn1G95kQvfpI57xZ7MZPM8jnaua5lPwS/BY0nmCV3meR41xA9elCo+d/gvme83/HGzkvyIhWrG1JXDkAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;456-m600&quot;
        title=&quot;456-m600&quot;
        src=&quot;/static/d86d7a792769731384344d3d18b46347/f058b/456.png&quot;
        srcset=&quot;/static/d86d7a792769731384344d3d18b46347/c26ae/456.png 158w,
/static/d86d7a792769731384344d3d18b46347/6bdcf/456.png 315w,
/static/d86d7a792769731384344d3d18b46347/f058b/456.png 630w,
/static/d86d7a792769731384344d3d18b46347/40601/456.png 945w,
/static/d86d7a792769731384344d3d18b46347/78612/456.png 1260w,
/static/d86d7a792769731384344d3d18b46347/417bd/456.png 4463w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;🤔 假设主机 A 的 IP 地址为 192.168.1.1，主机 B 的 IP 地址为 192.168.1.2，它们都在同一个子网，那当你在主机 A 上 &lt;code class=&quot;language-text&quot;&gt;ping 192.168.1.2&lt;/code&gt; 后，会发生什么？&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;ping&lt;/code&gt; 命令执行的时候，源主机首先会构建一个 ICMP 请求数据包，ICMP 包内包含多个字短。最重要的两个：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;第一个是 &lt;strong&gt;&lt;span style=&apos;color:#ff6600&apos;&gt;字段类型&lt;/span&gt;&lt;/strong&gt;，&lt;span style=&apos;color:#0066ff&apos;&gt;对于请求数据包而言该字段为 8&lt;/span&gt;；&lt;/li&gt;
&lt;li&gt;另一个是 &lt;strong&gt;&lt;span style=&apos;color:#ff6600&apos;&gt;顺序号&lt;/span&gt;&lt;/strong&gt;，主要用于区分连续 &lt;code class=&quot;language-text&quot;&gt;ping&lt;/code&gt; 的时候发出去的多个数据包。每发出一个请求数据包，顺序号会自动➕1。为了能够计算往返时间 RTT（Round Trip Time），它会在报文的数据部分插入发送时间。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;然后，由 ICMP 协议将这个数据包连同地址 192.168.1.2 一起交给 IP 层。IP 层将以 192.168.1.2 作为目的地址，本机 IP 作为源地址，加上一些其他的控制信息，构建一个 IP 数据包。&lt;/p&gt;
&lt;p&gt;加下来，需要加入 MAC 头。如果在本节 ARP 映射表中查找出 IP地址 192.168.1.2 所对应的 MAC 地址，则可以直接使用；如果没有，则需要发送 ARP 协议查询 MAC 地址，获得 MAC 地址后，由数据链路层构建一个数据帧，目的地址是 IP 层传过来的 MAC 地址，源地址则是本机的 MAC 地址；还要附加上一些控制信息，根据以太网的介质访问规则，将他们发送出去。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;🤨 &lt;span style=&quot;color:#e60000&quot;&gt;“本节 ARP 映射表” 中的 “本节” 指的是什么？&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;🌀 &lt;span style=&quot;color:#2d862d&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;主机 B 收到这个数据帧后，先检查它的目的 MAC 地址，并和本机的 MAC 地址对比，如符合，则接收，否则就丢弃。接收后检查该数据帧，将 IP 数据包从帧中提取出来，交给本机 IP 层。同样，IP 层检查后将有用的信息提取后交给 ICMP 层。&lt;/p&gt;
&lt;p&gt;主机 B 会构建一个 ICMP 应答包，&lt;span style=&apos;color:#0066ff&apos;&gt;应答数据包的类型字段为 0&lt;/span&gt;，顺序号为接收到的请求数据包中的顺序号，然后再发送出去给主机 A。&lt;/p&gt;
&lt;p&gt;在规定时间内，源主机如果没有收到 ICMP 的应答包，则说明目标主机不可达；如果接收了 ICMP 应答包，则说明目标主机可达。此时，源主机会检查，用当前时刻减去该数据包最初从源主机上出发的时刻，就是 ICMP 数据包的时间延迟。&lt;/p&gt;
&lt;h3&gt;跨网段&lt;/h3&gt;
&lt;p&gt;当然这只是最简单的，同一个局域网里面的情况。如果跨网段的话，还会涉及到网关的转发、路由器的转发等等。但是对于 ICMP 的头来讲，是没什么影响的。会影响的是根据目标 IP 地址，选择路由的下一跳，还有经过每一个路由器到达一个新的局域网，需要换 MAC 头里面的 MAC 地址。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;🤨 &lt;span style=&quot;color:#e60000&quot;&gt;「同一局域网内」和「跨网段」的 &lt;code class=&quot;language-text&quot;&gt;ping&lt;/code&gt;，ICMP 数据包除目标 IP 地址和 MAC 地址外，是没什么区别的。对吧？&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;🌀 &lt;span style=&quot;color:#2d862d&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;如果在自己的可控范围内，当遇到网络不通的情况时候，除了直接 &lt;code class=&quot;language-text&quot;&gt;ping&lt;/code&gt; 目标的 IP 地址之外，还应该有一个清晰的网络拓扑图。并且从理论上来讲，应该要清楚的知道一个网络包从「源地址」到「目标地址」都需要经过哪些设备，然后逐个 &lt;code class=&quot;language-text&quot;&gt;ping&lt;/code&gt; 中间的这些设备或者机器。如果可能的话，在这些关键点，通过 &lt;code class=&quot;language-text&quot;&gt;tcpdump -i eth0 icmp&lt;/code&gt; 命令查看包有没有到达某个点，回复的包到达了哪个点，可以更加容易推断出错的位置。&lt;/p&gt;
&lt;p&gt;经常会遇到一个问题，如果不在我们的控制范围内，很多中间设备都是禁止 &lt;code class=&quot;language-text&quot;&gt;ping&lt;/code&gt; 的，但是 &lt;code class=&quot;language-text&quot;&gt;ping&lt;/code&gt; 不通不代表网络不通。这个时候就要使用 &lt;code class=&quot;language-text&quot;&gt;telnet&lt;/code&gt;，通过其它协议来测试网络是否通。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;😋 说了这么多，你应该看出 &lt;code class=&quot;language-text&quot;&gt;ping&lt;/code&gt; 这个程序是使用了 ICMP 里面的  ICMP ECHO REQUEST 和 ICMP ECHO REPLY 类型了吧。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Traceroute：差错报文类型的使用&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;🤔 那其他的类型呢？是不是只有真正遇到错误的时候，才能收到呢？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;那也不是，有一个程序 Traceroute，是个 “大骗子”。他会使用 ICMP 规则，故意制造一些能够产生错误的场景。&lt;/p&gt;
&lt;p&gt;所以，&lt;strong&gt;&lt;span style=&apos;color:#ff6600&apos;&gt;Traceroute 的第一个作用就是故意设置特殊的 TTL（Time To Live），来追踪去往目的地时沿途经过的路由器。&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Traceroute 的参数指向某个目的 IP 地址，它会发送一个 UDP 的数据包。将 TTL 设置成 1，表示一旦遇到一个路由器或者一个关卡，它就 “牺牲” 了。&lt;/p&gt;
&lt;p&gt;如果中间的路由器不止一个，也还是碰到第一个就 “牺牲” 了。“牺牲” 后返回一个 ICMP 包，也就是网络差错包，类型是时间超时。那大军前行就带一顿饭，试一试走多远会被饿死，然后找个哨探回来报告，那我就知道大军前行只带一顿饭能走多远了。&lt;/p&gt;
&lt;p&gt;接下来，将 TTL 设置为 2。第一关过了，第二关 “牺牲” 了，那我就知道第二关有多远。如此反复，直到到达目的主机。这样，Traceroute 就拿到了所有的路由器 IP。当然，有的路由器压根不会回这个 ICMP。这就是 Traceroute 一个公网的地址，看不到中间路由的原因。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;🤔 那怎么知道 UDP 有没有到达目的主机呢？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Traceroute 程序会发送一份 UDP 数据包给目的主机，但他会选择一个不可能的值作为 UDP 端口号（大于 30000）。当数据包到达时，会使目的主机的 UDP 模块产生一份 “端口不可达” 错误 ICMP 报文。如果数据包没有到达，则可能是超时了。&lt;/p&gt;
&lt;p&gt;这就相当于故意派人去西天如来那里去请一本《道德经》，结果人家信佛不信道，消息就会被打出来。被打的消息传回来，你就知道西天是能够到达的。为什么不去取《心经》呢？因为 UDP 是无连接的。也就是说这人一派出去，你就得不到任何音信。你无法区分到底是半路走丢了，还是真的遁入空门了，只有让人家打出来，你才会得到消息。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;span style=&apos;color:#ff6600&apos;&gt;Traceroute 还有一个作用是故意不设置分片，从而确定路径的 MTU（Maximum transmission unit，最大传输单元）。&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;要做的工作首先是发送分组，并设置“不分片”标志。发送的第一个分组的长度正好与出口 MTU 相等。如果中间遇到窄的关口会被卡住，会发送 ICMP 网络差错包，类型为 “需要进行分片但设置了不分片位”。其实，这是人家故意的好吧，每次收到 ICMP“不能分片”差错时就减小分组的长度，直到到达目标主机。&lt;/p&gt;
&lt;h2&gt;总结时刻&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;ICMP 相当于网络世界的侦查兵。一种是主动探查的查询报文，一种是异常报告的差错报文；&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;ping&lt;/code&gt; 使用查询报文；&lt;code class=&quot;language-text&quot;&gt;Traceroute&lt;/code&gt; 使用差错报文。&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item></channel></rss>