This commit is contained in:
krahets 2023-10-23 04:56:26 +08:00
parent 1ab075904f
commit f0d798173f
6 changed files with 23 additions and 6 deletions

View file

@ -3387,7 +3387,7 @@
<p>与许多语言不同的是,在 Python 中数字也被包装为对象,列表中存储的不是数字本身,而是对数字的引用。因此,我们会发现两个数组中的相同数字拥有同一个 id ,并且这些数字的内存地址是无须连续的。</p>
</div>
<div class="admonition question">
<p class="admonition-title">C++ STL 里面的 std::list 已经实现了双向链表,但好像一些算法的书上都不怎么直接用这个,是不是有什么局限性呢?</p>
<p class="admonition-title">C++ STL 里面的 <code>std::list</code> 已经实现了双向链表,但好像一些算法的书上都不怎么直接用这个,是不是有什么局限性呢?</p>
<p>一方面,我们往往更青睐使用数组实现算法,而只有在必要时才使用链表,主要有两个原因。</p>
<ul>
<li>空间开销:由于每个元素需要两个额外的指针(一个用于前一个元素,一个用于后一个元素),所以 <code>std::list</code> 通常比 <code>std::vector</code> 更占用空间。</li>
@ -3395,6 +3395,14 @@
</ul>
<p>另一方面,必要使用链表的情况主要是二叉树和图。栈和队列往往会使用编程语言提供的 <code>stack</code><code>queue</code> ,而非链表。</p>
</div>
<div class="admonition question">
<p class="admonition-title">初始化列表 <code>res = [0] * self.size()</code> 操作,会导致 <code>res</code> 的每个元素引用相同的地址吗?</p>
<p>不会。但二维数组会有这个问题,例如初始化二维列表 <code>res = [[0] * self.size()]</code> ,则多次引用了同一个列表 <code>[0]</code></p>
</div>
<div class="admonition question">
<p class="admonition-title">在删除节点中,需要断开该节点与其后继节点之间的引用指向吗?</p>
<p>从数据结构与算法(做题)的角度看,不断开没有关系,只要保证程序的逻辑是正确的就行。从标准库的角度看,断开更加安全、逻辑更加清晰。如果不断开,假设被删除节点未被正常回收,那么它也会影响后继节点的内存回收。</p>
</div>
<!-- Source file information -->

View file

@ -4316,8 +4316,8 @@
<h3 id="3">3. &nbsp; 两种剪枝对比<a class="headerlink" href="#3" title="Permanent link">&para;</a></h3>
<p>请注意,虽然 <code>selected</code><code>duplicated</code> 都用作剪枝,但两者的目标是不同的。</p>
<ul>
<li><strong>重复选择剪枝</strong>:整个搜索过程中只有一个 <code>selected</code> 。它记录的是当前状态中包含哪些元素,作用是避免某个元素在 <code>state</code> 中重复出现。</li>
<li><strong>相等元素剪枝</strong>:每轮选择(即每个开启<code>backtrack</code> 函数)都包含一个 <code>duplicated</code> 。它记录的是在遍历中哪些元素已被选择过,作用是保证相等元素只被选择一次。</li>
<li><strong>重复选择剪枝</strong>:整个搜索过程中只有一个 <code>selected</code> 。它记录的是当前状态中包含哪些元素,作用是防止 <code>choices</code> 中的任一元素在 <code>state</code> 中重复出现。</li>
<li><strong>相等元素剪枝</strong>:每轮选择(即每个调用<code>backtrack</code> 函数)都包含一个 <code>duplicated</code> 。它记录的是在本轮遍历(即 <code>for</code> 循环)中哪些元素已被选择过,作用是保证相等元素只被选择一次。</li>
</ul>
<p>图 13-9 展示了两个剪枝条件的生效范围。注意,树中的每个节点代表一个选择,从根节点到叶节点的路径上的各个节点构成一个排列。</p>
<p><a class="glightbox" href="../permutations_problem.assets/permutations_ii_pruning_summary.png" data-type="image" data-width="100%" data-height="auto" data-desc-position="bottom"><img alt="两种剪枝条件的作用范围" src="../permutations_problem.assets/permutations_ii_pruning_summary.png" /></a></p>

View file

@ -3346,12 +3346,21 @@
<h3 id="2-q-a">2. &nbsp; Q &amp; A<a class="headerlink" href="#2-q-a" title="Permanent link">&para;</a></h3>
<div class="admonition question">
<p class="admonition-title">为什么哈希表同时包含线性数据结构和非线性数据结构?</p>
<p>哈希表底层是数组,而为了解决哈希冲突,我们可能会使用“链式地址”(后续哈希表章节会讲)。在拉链法中,数组中每个地址(桶)指向一个链表;当这个链表长度超过一定阈值时,又可能被转化为树(通常为红黑树)。因此,哈希表可能同时包含线性(数组、链表)和非线性(树)数据结构。</p>
<p>哈希表底层是数组,而为了解决哈希冲突,我们可能会使用“链式地址”(后续哈希表章节会讲):数组中每个桶指向一个链表,当链表长度超过一定阈值时,又可能被转化为树(通常为红黑树)。
从存储的角度来看,哈希表的底层是数组,其中每一个桶槽位可能包含一个值,也可能包含一个链表或树。因此,哈希表可能同时包含线性(数组、链表)和非线性(树)数据结构。</p>
</div>
<div class="admonition question">
<p class="admonition-title"><code>char</code> 类型的长度是 1 byte 吗?</p>
<p><code>char</code> 类型的长度由编程语言采用的编码方法决定。例如Java、JS、TS、C# 都采用 UTF-16 编码(保存 Unicode 码点),因此 char 类型的长度为 2 bytes 。</p>
</div>
<div class="admonition question">
<p class="admonition-title">基于数组实现的数据结构也被称为“静态数据结构” 是否有歧义?因为栈也可以进行出栈和入栈等操作,这些操作都是“动态”的。</p>
<p>栈确实可以实现动态的数据操作,但数据结构仍然是“静态”(长度不可变)的。尽管基于数组的数据结构可以动态地添加或删除元素,但它们的容量是固定的。如果数据量超出了预分配的大小,就需要创建一个新的更大的数组,并将老数组的内容复制到新数组中。</p>
</div>
<div class="admonition question">
<p class="admonition-title">在构建栈(队列)的时候,未指定它的大小,为什么它们是“静态数据结构”呢?</p>
<p>在高级编程语言中我们无须人工指定栈队列的初始容量这个工作是由类内部自动完成的。例如Java 的 ArrayList 的初始容量通常为 10 。另外,扩容操作也是自动实现的。详见本书的“列表”章节。</p>
</div>
<!-- Source file information -->

View file

@ -4466,7 +4466,7 @@
<li>为了方便添加与删除顶点,以及简化代码,我们使用列表(动态数组)来代替链表。</li>
<li>使用哈希表来存储邻接表,<code>key</code> 为顶点实例,<code>value</code> 为该顶点的邻接顶点列表(链表)。</li>
</ul>
<p>另外,我们在邻接表中使用 <code>Vertex</code> 类来表示顶点。这是因为如果与邻接矩阵一样用列表索引来区分不同顶点。那么假设想要删除索引为 <span class="arithmatex">\(i\)</span> 的顶点,则需遍历整个邻接表,将所有大于 <span class="arithmatex">\(i\)</span> 的索引全部减 <span class="arithmatex">\(1\)</span> ,效率很低。而如果每个顶点都是唯一的 <code>Vertex</code> 实例,删除某一顶点之后就无须改动其他顶点了。</p>
<p>另外,我们在邻接表中使用 <code>Vertex</code> 类来表示顶点,这样做的原因是:如果与邻接矩阵一样,用列表索引来区分不同顶点,那么假设要删除索引为 <span class="arithmatex">\(i\)</span> 的顶点,则需遍历整个邻接表,将所有大于 <span class="arithmatex">\(i\)</span> 的索引全部减 <span class="arithmatex">\(1\)</span> ,效率很低。而如果每个顶点都是唯一的 <code>Vertex</code> 实例,删除某一顶点之后就无须改动其他顶点了。</p>
<div class="tabbed-set tabbed-alternate" data-tabs="4:12"><input checked="checked" id="__tabbed_4_1" name="__tabbed_4" type="radio" /><input id="__tabbed_4_2" name="__tabbed_4" type="radio" /><input id="__tabbed_4_3" name="__tabbed_4" type="radio" /><input id="__tabbed_4_4" name="__tabbed_4" type="radio" /><input id="__tabbed_4_5" name="__tabbed_4" type="radio" /><input id="__tabbed_4_6" name="__tabbed_4" type="radio" /><input id="__tabbed_4_7" name="__tabbed_4" type="radio" /><input id="__tabbed_4_8" name="__tabbed_4" type="radio" /><input id="__tabbed_4_9" name="__tabbed_4" type="radio" /><input id="__tabbed_4_10" name="__tabbed_4" type="radio" /><input id="__tabbed_4_11" name="__tabbed_4" type="radio" /><input id="__tabbed_4_12" name="__tabbed_4" type="radio" /><div class="tabbed-labels"><label for="__tabbed_4_1">Python</label><label for="__tabbed_4_2">C++</label><label for="__tabbed_4_3">Java</label><label for="__tabbed_4_4">C#</label><label for="__tabbed_4_5">Go</label><label for="__tabbed_4_6">Swift</label><label for="__tabbed_4_7">JS</label><label for="__tabbed_4_8">TS</label><label for="__tabbed_4_9">Dart</label><label for="__tabbed_4_10">Rust</label><label for="__tabbed_4_11">C</label><label for="__tabbed_4_12">Zig</label></div>
<div class="tabbed-content">
<div class="tabbed-block">

File diff suppressed because one or more lines are too long

Binary file not shown.