mirror of
https://github.com/krahets/hello-algo.git
synced 2026-04-04 03:00:06 +08:00
deploy
This commit is contained in:
@@ -3397,7 +3397,7 @@
|
||||
<p>在计算机中,所有数据都是以二进制数的形式存储的,字符 <code>char</code> 也不例外。为了表示字符,我们需要建立一套“字符集”,规定每个字符和二进制数之间的一一对应关系。有了字符集之后,计算机就可以通过查表完成二进制数到字符的转换。</p>
|
||||
<h2 id="341-ascii">3.4.1 ASCII 字符集<a class="headerlink" href="#341-ascii" title="Permanent link">¶</a></h2>
|
||||
<p>「ASCII 码」是最早出现的字符集,全称为“美国标准信息交换代码”。它使用 7 位二进制数(即一个字节的低 7 位)表示一个字符,最多能够表示 128 个不同的字符。如图 3-6 所示,ASCII 码包括英文字母的大小写、数字 0 ~ 9、一些标点符号,以及一些控制字符(如换行符和制表符)。</p>
|
||||
<p><a class="glightbox" href="../character_encoding.assets/ascii_table.png" data-type="image" data-width="100%" data-height="auto" data-desc-position="bottom"><img alt="ASCII 码" src="../character_encoding.assets/ascii_table.png" /></a></p>
|
||||
<p><a class="glightbox" href="../character_encoding.assets/ascii_table.png" data-type="image" data-width="100%" data-height="auto" data-desc-position="bottom"><img alt="ASCII 码" class="animation-figure" src="../character_encoding.assets/ascii_table.png" /></a></p>
|
||||
<p align="center"> 图 3-6 ASCII 码 </p>
|
||||
|
||||
<p>然而,<strong>ASCII 码仅能够表示英文</strong>。随着计算机的全球化,诞生了一种能够表示更多语言的字符集「EASCII」。它在 ASCII 的 7 位基础上扩展到 8 位,能够表示 256 个不同的字符。</p>
|
||||
@@ -3412,7 +3412,7 @@
|
||||
<p>自 1991 年发布以来,Unicode 不断扩充新的语言与字符。截止 2022 年 9 月,Unicode 已经包含 149186 个字符,包括各种语言的字符、符号、甚至是表情符号等。在庞大的 Unicode 字符集中,常用的字符占用 2 字节,有些生僻的字符占 3 字节甚至 4 字节。</p>
|
||||
<p>Unicode 是一种字符集标准,本质上是给每个字符分配一个编号(称为“码点”),<strong>但它并没有规定在计算机中如何存储这些字符码点</strong>。我们不禁会问:当多种长度的 Unicode 码点同时出现在同一个文本中时,系统如何解析字符?例如给定一个长度为 2 字节的编码,系统如何确认它是一个 2 字节的字符还是两个 1 字节的字符?</p>
|
||||
<p>对于以上问题,<strong>一种直接的解决方案是将所有字符存储为等长的编码</strong>。如图 3-7 所示,“Hello”中的每个字符占用 1 字节,“算法”中的每个字符占用 2 字节。我们可以通过高位填 0 ,将“Hello 算法”中的所有字符都编码为 2 字节长度。这样系统就可以每隔 2 字节解析一个字符,恢复出这个短语的内容了。</p>
|
||||
<p><a class="glightbox" href="../character_encoding.assets/unicode_hello_algo.png" data-type="image" data-width="100%" data-height="auto" data-desc-position="bottom"><img alt="Unicode 编码示例" src="../character_encoding.assets/unicode_hello_algo.png" /></a></p>
|
||||
<p><a class="glightbox" href="../character_encoding.assets/unicode_hello_algo.png" data-type="image" data-width="100%" data-height="auto" data-desc-position="bottom"><img alt="Unicode 编码示例" class="animation-figure" src="../character_encoding.assets/unicode_hello_algo.png" /></a></p>
|
||||
<p align="center"> 图 3-7 Unicode 编码示例 </p>
|
||||
|
||||
<p>然而 ASCII 码已经向我们证明,编码英文只需要 1 字节。若采用上述方案,英文文本占用空间的大小将会是 ASCII 编码下大小的两倍,非常浪费内存空间。因此,我们需要一种更加高效的 Unicode 编码方法。</p>
|
||||
@@ -3426,7 +3426,7 @@
|
||||
<p>图 3-8 展示了“Hello算法”对应的 UTF-8 编码。观察发现,由于最高 <span class="arithmatex">\(n\)</span> 位都被设置为 <span class="arithmatex">\(1\)</span> ,因此系统可以通过读取最高位 <span class="arithmatex">\(1\)</span> 的个数来解析出字符的长度为 <span class="arithmatex">\(n\)</span> 。</p>
|
||||
<p>但为什么要将其余所有字节的高 2 位都设置为 <span class="arithmatex">\(10\)</span> 呢?实际上,这个 <span class="arithmatex">\(10\)</span> 能够起到校验符的作用。假设系统从一个错误的字节开始解析文本,字节头部的 <span class="arithmatex">\(10\)</span> 能够帮助系统快速的判断出异常。</p>
|
||||
<p>之所以将 <span class="arithmatex">\(10\)</span> 当作校验符,是因为在 UTF-8 编码规则下,不可能有字符的最高两位是 <span class="arithmatex">\(10\)</span> 。这个结论可以用反证法来证明:假设一个字符的最高两位是 <span class="arithmatex">\(10\)</span> ,说明该字符的长度为 <span class="arithmatex">\(1\)</span> ,对应 ASCII 码。而 ASCII 码的最高位应该是 <span class="arithmatex">\(0\)</span> ,与假设矛盾。</p>
|
||||
<p><a class="glightbox" href="../character_encoding.assets/utf-8_hello_algo.png" data-type="image" data-width="100%" data-height="auto" data-desc-position="bottom"><img alt="UTF-8 编码示例" src="../character_encoding.assets/utf-8_hello_algo.png" /></a></p>
|
||||
<p><a class="glightbox" href="../character_encoding.assets/utf-8_hello_algo.png" data-type="image" data-width="100%" data-height="auto" data-desc-position="bottom"><img alt="UTF-8 编码示例" class="animation-figure" src="../character_encoding.assets/utf-8_hello_algo.png" /></a></p>
|
||||
<p align="center"> 图 3-8 UTF-8 编码示例 </p>
|
||||
|
||||
<p>除了 UTF-8 之外,常见的编码方式还包括以下两种。</p>
|
||||
|
||||
@@ -3360,7 +3360,7 @@
|
||||
<li><strong>线性数据结构</strong>:数组、链表、栈、队列、哈希表。</li>
|
||||
<li><strong>非线性数据结构</strong>:树、堆、图、哈希表。</li>
|
||||
</ul>
|
||||
<p><a class="glightbox" href="../classification_of_data_structure.assets/classification_logic_structure.png" data-type="image" data-width="100%" data-height="auto" data-desc-position="bottom"><img alt="线性与非线性数据结构" src="../classification_of_data_structure.assets/classification_logic_structure.png" /></a></p>
|
||||
<p><a class="glightbox" href="../classification_of_data_structure.assets/classification_logic_structure.png" data-type="image" data-width="100%" data-height="auto" data-desc-position="bottom"><img alt="线性与非线性数据结构" class="animation-figure" src="../classification_of_data_structure.assets/classification_logic_structure.png" /></a></p>
|
||||
<p align="center"> 图 3-1 线性与非线性数据结构 </p>
|
||||
|
||||
<p>非线性数据结构可以进一步被划分为树形结构和网状结构。</p>
|
||||
@@ -3373,12 +3373,12 @@
|
||||
<p>在计算机中,内存和硬盘是两种主要的存储硬件设备。硬盘主要用于长期存储数据,容量较大(通常可达到 TB 级别)、速度较慢。内存用于运行程序时暂存数据,速度较快,但容量较小(通常为 GB 级别)。</p>
|
||||
<p><strong>在算法运行过程中,相关数据都存储在内存中</strong>。图 3-2 展示了一个计算机内存条,其中每个黑色方块都包含一块内存空间。我们可以将内存想象成一个巨大的 Excel 表格,其中每个单元格都可以存储一定大小的数据,在算法运行时,所有数据都被存储在这些单元格中。</p>
|
||||
<p><strong>系统通过内存地址来访问目标位置的数据</strong>。如图 3-2 所示,计算机根据特定规则为表格中的每个单元格分配编号,确保每个内存空间都有唯一的内存地址。有了这些地址,程序便可以访问内存中的数据。</p>
|
||||
<p><a class="glightbox" href="../classification_of_data_structure.assets/computer_memory_location.png" data-type="image" data-width="100%" data-height="auto" data-desc-position="bottom"><img alt="内存条、内存空间、内存地址" src="../classification_of_data_structure.assets/computer_memory_location.png" /></a></p>
|
||||
<p><a class="glightbox" href="../classification_of_data_structure.assets/computer_memory_location.png" data-type="image" data-width="100%" data-height="auto" data-desc-position="bottom"><img alt="内存条、内存空间、内存地址" class="animation-figure" src="../classification_of_data_structure.assets/computer_memory_location.png" /></a></p>
|
||||
<p align="center"> 图 3-2 内存条、内存空间、内存地址 </p>
|
||||
|
||||
<p>内存是所有程序的共享资源,当某块内存被某个程序占用时,则无法被其他程序同时使用了。<strong>因此在数据结构与算法的设计中,内存资源是一个重要的考虑因素</strong>。比如,算法所占用的内存峰值不应超过系统剩余空闲内存;如果缺少连续大块的内存空间,那么所选用的数据结构必须能够存储在分散的内存空间内。</p>
|
||||
<p>如图 3-3 所示,<strong>物理结构反映了数据在计算机内存中的存储方式</strong>,可分为连续空间存储(数组)和分散空间存储(链表)。物理结构从底层决定了数据的访问、更新、增删等操作方法,同时在时间效率和空间效率方面呈现出互补的特点。</p>
|
||||
<p><a class="glightbox" href="../classification_of_data_structure.assets/classification_phisical_structure.png" data-type="image" data-width="100%" data-height="auto" data-desc-position="bottom"><img alt="连续空间存储与分散空间存储" src="../classification_of_data_structure.assets/classification_phisical_structure.png" /></a></p>
|
||||
<p><a class="glightbox" href="../classification_of_data_structure.assets/classification_phisical_structure.png" data-type="image" data-width="100%" data-height="auto" data-desc-position="bottom"><img alt="连续空间存储与分散空间存储" class="animation-figure" src="../classification_of_data_structure.assets/classification_phisical_structure.png" /></a></p>
|
||||
<p align="center"> 图 3-3 连续空间存储与分散空间存储 </p>
|
||||
|
||||
<p>值得说明的是,<strong>所有数据结构都是基于数组、链表或二者的组合实现的</strong>。例如,栈和队列既可以使用数组实现,也可以使用链表实现;而哈希表的实现可能同时包含数组和链表。</p>
|
||||
|
||||
@@ -3292,7 +3292,7 @@
|
||||
<!-- Page content -->
|
||||
<h1 id="3">第 3 章 数据结构<a class="headerlink" href="#3" title="Permanent link">¶</a></h1>
|
||||
<div class="center-table">
|
||||
<p><a class="glightbox" href="../assets/covers/chapter_data_structure.jpg" data-type="image" data-width="100%" data-height="auto" data-desc-position="bottom"><img alt="数据结构" src="../assets/covers/chapter_data_structure.jpg" width="600" /></a></p>
|
||||
<p><a class="glightbox" href="../assets/covers/chapter_data_structure.jpg" data-type="image" data-width="100%" data-height="auto" data-desc-position="bottom"><img alt="数据结构" class="cover-image" src="../assets/covers/chapter_data_structure.jpg" /></a></p>
|
||||
</div>
|
||||
<div class="admonition abstract">
|
||||
<p class="admonition-title">Abstract</p>
|
||||
|
||||
@@ -3365,7 +3365,7 @@
|
||||
<li><strong>补码</strong>:正数的补码与其原码相同,负数的补码是在其反码的基础上加 <span class="arithmatex">\(1\)</span> 。</li>
|
||||
</ul>
|
||||
<p>图 3-4 展示了原码、反码和补码之间的转换方法。</p>
|
||||
<p><a class="glightbox" href="../number_encoding.assets/1s_2s_complement.png" data-type="image" data-width="100%" data-height="auto" data-desc-position="bottom"><img alt="原码、反码与补码之间的相互转换" src="../number_encoding.assets/1s_2s_complement.png" /></a></p>
|
||||
<p><a class="glightbox" href="../number_encoding.assets/1s_2s_complement.png" data-type="image" data-width="100%" data-height="auto" data-desc-position="bottom"><img alt="原码、反码与补码之间的相互转换" class="animation-figure" src="../number_encoding.assets/1s_2s_complement.png" /></a></p>
|
||||
<p align="center"> 图 3-4 原码、反码与补码之间的相互转换 </p>
|
||||
|
||||
<p>「原码 true form」虽然最直观,但存在一些局限性。一方面,<strong>负数的原码不能直接用于运算</strong>。例如在原码下计算 <span class="arithmatex">\(1 + (-2)\)</span> ,得到的结果是 <span class="arithmatex">\(-3\)</span> ,这显然是不对的。</p>
|
||||
@@ -3447,7 +3447,7 @@ b_{31} b_{30} b_{29} \ldots b_2 b_1 b_0
|
||||
(1 + \mathrm{N}) = & (1 + \sum_{i=1}^{23} b_{23-i} 2^{-i}) \subset [1, 2 - 2^{-23}]
|
||||
\end{aligned}
|
||||
\]</div>
|
||||
<p><a class="glightbox" href="../number_encoding.assets/ieee_754_float.png" data-type="image" data-width="100%" data-height="auto" data-desc-position="bottom"><img alt="IEEE 754 标准下的 float 的计算示例" src="../number_encoding.assets/ieee_754_float.png" /></a></p>
|
||||
<p><a class="glightbox" href="../number_encoding.assets/ieee_754_float.png" data-type="image" data-width="100%" data-height="auto" data-desc-position="bottom"><img alt="IEEE 754 标准下的 float 的计算示例" class="animation-figure" src="../number_encoding.assets/ieee_754_float.png" /></a></p>
|
||||
<p align="center"> 图 3-5 IEEE 754 标准下的 float 的计算示例 </p>
|
||||
|
||||
<p>观察图 3-5 ,给定一个示例数据 <span class="arithmatex">\(\mathrm{S} = 0\)</span> , <span class="arithmatex">\(\mathrm{E} = 124\)</span> ,<span class="arithmatex">\(\mathrm{N} = 2^{-2} + 2^{-3} = 0.375\)</span> ,则有:</p>
|
||||
|
||||
Reference in New Issue
Block a user