This commit is contained in:
krahets
2025-07-10 07:14:51 +08:00
parent ea4ae128df
commit 4045471c84
349 changed files with 15161 additions and 15092 deletions

View File

@ -62,17 +62,15 @@
<link href="../../assets/stylesheets/glightbox.min.css" rel="stylesheet"/><style>
html.glightbox-open { overflow: initial; height: 100%; }
.gslide-title { margin-top: 0px; user-select: text; }
.gslide-desc { color: #666; user-select: text; }
.gslide-image img { background: white; }
.gscrollbar-fixer { padding-right: 15px; }
.gdesc-inner { font-size: 0.75rem; }
body[data-md-color-scheme="slate"] .gdesc-inner { background: var(--md-default-bg-color);}
body[data-md-color-scheme="slate"] .gslide-title { color: var(--md-default-fg-color);}
body[data-md-color-scheme="slate"] .gslide-desc { color: var(--md-default-fg-color);}
</style> <script src="../../assets/javascripts/glightbox.min.js"></script></head>
html.glightbox-open { overflow: initial; height: 100%; }
.gslide-title { margin-top: 0px; user-select: text; }
.gslide-desc { color: #666; user-select: text; }
.gslide-image img { background: white; }
.gscrollbar-fixer { padding-right: 15px; }
.gdesc-inner { font-size: 0.75rem; }
body[data-md-color-scheme="slate"] .gdesc-inner { background: var(--md-default-bg-color);}
body[data-md-color-scheme="slate"] .gslide-title { color: var(--md-default-fg-color);}
body[data-md-color-scheme="slate"] .gslide-desc { color: var(--md-default-fg-color);}</style> <script src="../../assets/javascripts/glightbox.min.js"></script></head>
@ -3653,7 +3651,7 @@
<ul>
<li>冒泡排序通过交换相邻元素来实现排序。通过添加一个标志位来实现提前返回,我们可以将冒泡排序的最佳时间复杂度优化到 <span class="arithmatex">\(O(n)\)</span></li>
<li>插入排序每轮将未排序区间内的元素插入到已排序区间的正确位置,从而完成排序。虽然插入排序的时间复杂度为 <span class="arithmatex">\(O(n^2)\)</span> ,但由于单元操作相对较少,因此在小数据量的排序任务中非常受欢迎。</li>
<li>快速排序基于哨兵划分操作实现排序。在哨兵划分中,有可能每次都选取到最差的基准数,导致时间复杂度劣化至 <span class="arithmatex">\(O(n^2)\)</span> 。引入中位数基准数或随机基准数可以降低这种劣化的概率。尾递归方法可以有效地减少递归深度,将空间复杂度优化到 <span class="arithmatex">\(O(\log n)\)</span></li>
<li>快速排序基于哨兵划分操作实现排序。在哨兵划分中,有可能每次都选取到最差的基准数,导致时间复杂度劣化至 <span class="arithmatex">\(O(n^2)\)</span> 。引入中位数基准数或随机基准数可以降低这种劣化的概率。通过优先递归较短子区间,可有效减小递归深度,将空间复杂度优化到 <span class="arithmatex">\(O(\log n)\)</span></li>
<li>归并排序包括划分和合并两个阶段,典型地体现了分治策略。在归并排序中,排序数组需要创建辅助数组,空间复杂度为 <span class="arithmatex">\(O(n)\)</span> ;然而排序链表的空间复杂度可以优化至 <span class="arithmatex">\(O(1)\)</span></li>
<li>桶排序包含三个步骤:数据分桶、桶内排序和合并结果。它同样体现了分治策略,适用于数据体量很大的情况。桶排序的关键在于对数据进行平均分配。</li>
<li>计数排序是桶排序的一个特例,它通过统计数据出现的次数来实现排序。计数排序适用于数据量大但数据范围有限的情况,并且要求数据能够转换为正整数。</li>
@ -3673,9 +3671,9 @@
<p>哨兵划分 <code>partition()</code> 的最后一步是交换 <code>nums[left]</code><code>nums[i]</code> 。完成交换后,基准数左边的元素都 <code>&lt;=</code> 基准数,<strong>这就要求最后一步交换前 <code>nums[left] &gt;= nums[i]</code> 必须成立</strong>。假设我们先“从左往右查找”,那么如果找不到比基准数更大的元素,<strong>则会在 <code>i == j</code> 时跳出循环,此时可能 <code>nums[j] == nums[i] &gt; nums[left]</code></strong>。也就是说,此时最后一步交换操作会把一个比基准数更大的元素交换至数组最左端,导致哨兵划分失败。</p>
<p>举个例子,给定数组 <code>[0, 0, 0, 0, 1]</code> ,如果先“从左向右查找”,哨兵划分后数组为 <code>[1, 0, 0, 0, 0]</code> ,这个结果是不正确的。</p>
<p>再深入思考一下,如果我们选择 <code>nums[right]</code> 为基准数,那么正好反过来,必须先“从左往右查找”。</p>
<p><strong>Q</strong>:关于尾递归优化,为什么选短的数组能保证递归深度不超过 <span class="arithmatex">\(\log n\)</span> </p>
<p>递归深度就是当前未返回的递归方法的数量。每轮哨兵划分我们将原数组划分为两个子数组。在递归优化后,向下递归的子数组长度最大为原数组长度的一半。假设最差情况,一直为一半长度,那么最终的递归深度就是 <span class="arithmatex">\(\log n\)</span></p>
<p>回顾原始的快速排序,我们有可能会连续地递归长度较大的数组,最差情况下为 <span class="arithmatex">\(n\)</span><span class="arithmatex">\(n - 1\)</span><span class="arithmatex">\(\dots\)</span><span class="arithmatex">\(2\)</span><span class="arithmatex">\(1\)</span> ,递归深度为 <span class="arithmatex">\(n\)</span>递归优化可以避免这种情况出现。</p>
<p><strong>Q</strong>:关于快速排序的递归深度优化,为什么选短的数组能保证递归深度不超过 <span class="arithmatex">\(\log n\)</span> </p>
<p>递归深度就是当前未返回的递归方法的数量。每轮哨兵划分我们将原数组划分为两个子数组。在递归深度优化后,向下递归的子数组长度最大为原数组长度的一半。假设最差情况,一直为一半长度,那么最终的递归深度就是 <span class="arithmatex">\(\log n\)</span></p>
<p>回顾原始的快速排序,我们有可能会连续地递归长度较大的数组,最差情况下为 <span class="arithmatex">\(n\)</span><span class="arithmatex">\(n - 1\)</span><span class="arithmatex">\(\dots\)</span><span class="arithmatex">\(2\)</span><span class="arithmatex">\(1\)</span> ,递归深度为 <span class="arithmatex">\(n\)</span> 。递归深度优化可以避免这种情况出现。</p>
<p><strong>Q</strong>:当数组中所有元素都相等时,快速排序的时间复杂度是 <span class="arithmatex">\(O(n^2)\)</span> 吗?该如何处理这种退化情况?</p>
<p>是的。对于这种情况,可以考虑通过哨兵划分将数组划分为三个部分:小于、等于、大于基准数。仅向下递归小于和大于的两部分。在该方法下,输入元素全部相等的数组,仅一轮哨兵划分即可完成排序。</p>
<p><strong>Q</strong>:桶排序的最差时间复杂度为什么是 <span class="arithmatex">\(O(n^2)\)</span> </p>
@ -3930,5 +3928,7 @@ aria-label="页脚"
<script src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/3.2.2/es5/tex-mml-chtml.min.js"></script>
<script>document$.subscribe(() => {const lightbox = GLightbox({"touchNavigation": true, "loop": false, "zoomable": true, "draggable": false, "openEffect": "zoom", "closeEffect": "zoom", "slideEffect": "none"});})</script></body>
<script id="init-glightbox">const lightbox = GLightbox({"touchNavigation": true, "loop": false, "zoomable": true, "draggable": false, "openEffect": "zoom", "closeEffect": "zoom", "slideEffect": "none"});
document$.subscribe(() => { lightbox.reload() });
</script></body>
</html>