mirror of
https://github.com/krahets/hello-algo.git
synced 2025-07-28 21:02:56 +08:00
deploy
This commit is contained in:
@ -1795,13 +1795,13 @@
|
||||
|
||||
<h1 id="31">3.1. 数据与内存<a class="headerlink" href="#31" title="Permanent link">¶</a></h1>
|
||||
<h2 id="311">3.1.1. 基本数据类型<a class="headerlink" href="#311" title="Permanent link">¶</a></h2>
|
||||
<p>谈到计算机中的数据,我们能够想到文本、图片、视频、语音、3D 模型等等,这些数据虽然组织形式不同,但都是由各种基本数据类型构成的。</p>
|
||||
<p>谈及计算机中的数据,我们会想到文本、图片、视频、语音、3D 模型等各种形式。尽管这些数据的组织形式各异,但它们都由各种基本数据类型构成。</p>
|
||||
<p><strong>「基本数据类型」是 CPU 可以直接进行运算的类型,在算法中直接被使用</strong>。</p>
|
||||
<ul>
|
||||
<li>「整数」根据不同的长度分为 byte, short, int, long ,根据算法需求选用,即在满足取值范围的情况下尽量减小内存空间占用;</li>
|
||||
<li>「浮点数」代表小数,根据长度分为 float, double ,同样根据算法的实际需求选用;</li>
|
||||
<li>「字符」在计算机中是以字符集的形式保存的,char 的值实际上是数字,代表字符集中的编号,计算机通过字符集查表来完成编号到字符的转换。占用空间通常为 2 bytes 或 1 byte ;</li>
|
||||
<li>「布尔」代表逻辑中的“是”与“否”,其占用空间需根据编程语言确定,通常为 1 byte 或 1 bit ;</li>
|
||||
<li>「整数」按照不同的长度分为 byte, short, int, long 。在满足取值范围的前提下,我们应该尽量选取较短的整数类型,以减小内存空间占用;</li>
|
||||
<li>「浮点数」表示小数,按长度分为 float, double ,选用规则与整数相同。</li>
|
||||
<li>「字符」在计算机中以字符集形式保存,char 的值实际上是数字,代表字符集中的编号,计算机通过字符集查表完成编号到字符的转换。</li>
|
||||
<li>「布尔」代表逻辑中的“是”与“否”,其占用空间需根据编程语言确定。</li>
|
||||
</ul>
|
||||
<div class="center-table">
|
||||
<table>
|
||||
@ -1874,32 +1874,29 @@
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="admonition tip">
|
||||
<p class="admonition-title">Tip</p>
|
||||
<p>以上表格中,加粗项在「算法题」中最为常用。此表格无需硬背,大致理解即可,需要时可以通过查表来回忆。</p>
|
||||
</div>
|
||||
<p>以上表格中,加粗项在算法题中最为常用。此表格无需硬背,大致理解即可,需要时可以通过查表来回忆。</p>
|
||||
<h3 id="_1">整数表示方式<a class="headerlink" href="#_1" title="Permanent link">¶</a></h3>
|
||||
<p>整数的取值范围取决于变量使用的内存长度,即字节(或比特)数。在计算机中, 1 字节 (byte) = 8 比特 (bit) , 1 比特即 1 个二进制位。以 int 类型为例:</p>
|
||||
<p>整数的取值范围取决于变量使用的内存长度,即字节(或比特)数。在计算机中,1 字节 (byte) = 8 比特 (bit),1 比特即 1 个二进制位。以 int 类型为例:</p>
|
||||
<ol>
|
||||
<li>整数类型 int 占用 4 bytes = 32 bits ,因此可以表示 <span class="arithmatex">\(2^{32}\)</span> 个不同的数字;</li>
|
||||
<li>将最高位看作符号位,<span class="arithmatex">\(0\)</span> 代表正数,<span class="arithmatex">\(1\)</span> 代表负数,从而可以表示 <span class="arithmatex">\(2^{31}\)</span> 个正数和 <span class="arithmatex">\(2^{31}\)</span> 个负数;</li>
|
||||
<li>当所有 bits 为 0 时代表数字 <span class="arithmatex">\(0\)</span> ,从零开始增大,可得最大正数为 <span class="arithmatex">\(2^{31} - 1\)</span> ;</li>
|
||||
<li>剩余 <span class="arithmatex">\(2^{31}\)</span> 个数字全部用来表示负数,因此最小负数为 <span class="arithmatex">\(-2^{31}\)</span> ;具体细节涉及到到“源码、反码、补码”知识,有兴趣的同学可以查阅学习; </li>
|
||||
<li>整数类型 int 占用 4 bytes = 32 bits ,可以表示 <span class="arithmatex">\(2^{32}\)</span> 个不同的数字;</li>
|
||||
<li>将最高位视为符号位,<span class="arithmatex">\(0\)</span> 代表正数,<span class="arithmatex">\(1\)</span> 代表负数,一共可表示 <span class="arithmatex">\(2^{31}\)</span> 个正数和 <span class="arithmatex">\(2^{31}\)</span> 个负数;</li>
|
||||
<li>当所有 bits 为 0 时代表数字 <span class="arithmatex">\(0\)</span> ,从零开始增大,可得最大正数为 <span class="arithmatex">\(2^{31} - 1\)</span>;</li>
|
||||
<li>剩余 <span class="arithmatex">\(2^{31}\)</span> 个数字全部用来表示负数,因此最小负数为 <span class="arithmatex">\(-2^{31}\)</span> ;具体细节涉及“源码、反码、补码”的相关知识,有兴趣的同学可以查阅学习;</li>
|
||||
</ol>
|
||||
<p>其它整数类型 byte, short, long 取值范围的计算方法与 int 类似,在此不再赘述。</p>
|
||||
<p>其它整数类型 byte, short, long 的取值范围的计算方法与 int 类似,在此不再赘述。</p>
|
||||
<h3 id="_2">浮点数表示方式 *<a class="headerlink" href="#_2" title="Permanent link">¶</a></h3>
|
||||
<div class="admonition note">
|
||||
<p class="admonition-title">Note</p>
|
||||
<p>在本书中,标题后的 <code>*</code> 符号代表选读章节,如果你觉得理解困难,建议先跳过,等学完必读章节后续再单独攻克。 </p>
|
||||
<p>本书中,标题后的 * 符号代表选读章节。如果你觉得理解困难,建议先跳过,等学完必读章节后再单独攻克。</p>
|
||||
</div>
|
||||
<p>细心的你可能会疑惑: int 和 float 长度相同,都是 4 bytes ,<strong>但为什么 float 的取值范围远大于 int</strong> ?按说 float 需要表示小数,取值范围应该变小才对。</p>
|
||||
<p>其实,这是因为浮点数 float 采用了不同的表示方式。IEEE 754 标准规定,32-bit 长度的 float 由以下部分构成:</p>
|
||||
<p>细心的你可能会发现:int 和 float 长度相同,都是 4 bytes,但为什么 float 的取值范围远大于 int ?按理说 float 需要表示小数,取值范围应该变小才对。</p>
|
||||
<p>实际上,这是因为浮点数 float 采用了不同的表示方式。根据 IEEE 754 标准,32-bit 长度的 float 由以下部分构成:</p>
|
||||
<ul>
|
||||
<li>符号位 <span class="arithmatex">\(\mathrm{S}\)</span> :占 1 bit ;</li>
|
||||
<li>指数位 <span class="arithmatex">\(\mathrm{E}\)</span> :占 8 bits ;</li>
|
||||
<li>分数位 <span class="arithmatex">\(\mathrm{N}\)</span> :占 24 bits ,其中 23 位显式存储;</li>
|
||||
</ul>
|
||||
<p>设 32-bit 二进制数的第 <span class="arithmatex">\(i\)</span> 位为 <span class="arithmatex">\(b_i\)</span> ,则 float 值的计算方法定义为</p>
|
||||
<p>设 32-bit 二进制数的第 <span class="arithmatex">\(i\)</span> 位为 <span class="arithmatex">\(b_i\)</span>,则 float 值的计算方法定义为:</p>
|
||||
<div class="arithmatex">\[
|
||||
\text { val } = (-1)^{b_{31}} \times 2^{\left(b_{30} b_{29} \ldots b_{23}\right)_2-127} \times\left(1 . b_{22} b_{21} \ldots b_0\right)_2
|
||||
\]</div>
|
||||
@ -1921,8 +1918,8 @@
|
||||
<div class="arithmatex">\[
|
||||
\text { val } = (-1)^0 \times 2^{124 - 127} \times (1 + 0.375) = 0.171875
|
||||
\]</div>
|
||||
<p>现在我们可以回答开始的问题:<strong>float 的表示方式包含指数位,导致其取值范围远大于 int</strong> 。根据以上计算, float 可表示的最大正数为 <span class="arithmatex">\(2^{254 - 127} \times (2 - 2^{-23}) \approx 3.4 \times 10^{38}\)</span> ,切换符号位便可得到最小负数。</p>
|
||||
<p><strong>浮点数 float 虽然拓展了取值范围,但副作用是牺牲了精度</strong>。整数类型 int 将全部 32 位用于表示数字,数字是均匀分布的;而由于指数位的存在,浮点数 float 的数值越大,相邻两个数字之间的差值就会趋向越大。</p>
|
||||
<p>现在我们可以回答最初的问题:<strong>float 的表示方式包含指数位,导致其取值范围远大于 int</strong> 。根据以上计算,float 可表示的最大正数为 <span class="arithmatex">\(2^{254 - 127} \times (2 - 2^{-23}) \approx 3.4 \times 10^{38}\)</span> ,切换符号位便可得到最小负数。</p>
|
||||
<p><strong>尽管浮点数 float 扩展了取值范围,但其副作用是牺牲了精度</strong>。整数类型 int 将全部 32 位用于表示数字,数字是均匀分布的;而由于指数位的存在,浮点数 float 的数值越大,相邻两个数字之间的差值就会趋向越大。</p>
|
||||
<p>进一步地,指数位 <span class="arithmatex">\(E = 0\)</span> 和 <span class="arithmatex">\(E = 255\)</span> 具有特殊含义,<strong>用于表示零、无穷大、<span class="arithmatex">\(\mathrm{NaN}\)</span> 等</strong>。</p>
|
||||
<div class="center-table">
|
||||
<table>
|
||||
@ -1956,15 +1953,14 @@
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<p>特别地,次正规数显著提升了小数精度:</p>
|
||||
<p>特别地,次正规数显著提升了浮点数的精度,这是因为:</p>
|
||||
<ul>
|
||||
<li>最小正正规数为 <span class="arithmatex">\(2^{-126} \approx 1.18 \times 10^{-38}\)</span> ;</li>
|
||||
<li>最小正次正规数为 <span class="arithmatex">\(2^{-126} \times 2^{-23} \approx 1.4 \times 10^{-45}\)</span> ;</li>
|
||||
</ul>
|
||||
<p>双精度 double 也采用类似 float 的表示方法,在此不再赘述。</p>
|
||||
<p>双精度 double 也采用类似 float 的表示方法,此处不再详述。</p>
|
||||
<h3 id="_3">基本数据类型与数据结构的关系<a class="headerlink" href="#_3" title="Permanent link">¶</a></h3>
|
||||
<p>我们知道,<strong>数据结构是在计算机中组织与存储数据的方式</strong>,它的主语是“结构”,而不是“数据”。如果我们想要表示“一排数字”,自然想到使用「数组」数据结构。数组的存储方式可以表示数字的相邻关系、顺序关系,但至于其中存储的是整数 int ,还是小数 float ,或是字符 char ,<strong>则与所谓的数据的结构无关了</strong>。</p>
|
||||
<p>换言之,基本数据类型提供了数据的“内容类型”,而数据结构提供数据的“组织方式”。</p>
|
||||
<p>我们知道,<strong>数据结构是在计算机中组织与存储数据的方式</strong>,它的核心是“结构”,而非“数据”。如果想要表示“一排数字”,我们自然会想到使用「数组」数据结构。数组的存储方式可以表示数字的相邻关系、顺序关系,但至于具体存储的是整数 int 、小数 float 、还是字符 char ,则与“数据结构”无关。换句话说,基本数据类型提供了数据的“内容类型”,而数据结构提供了数据的“组织方式”。</p>
|
||||
<div class="tabbed-set tabbed-alternate" data-tabs="1:10"><input checked="checked" id="__tabbed_1_1" name="__tabbed_1" type="radio" /><input id="__tabbed_1_2" name="__tabbed_1" type="radio" /><input id="__tabbed_1_3" name="__tabbed_1" type="radio" /><input id="__tabbed_1_4" name="__tabbed_1" type="radio" /><input id="__tabbed_1_5" name="__tabbed_1" type="radio" /><input id="__tabbed_1_6" name="__tabbed_1" type="radio" /><input id="__tabbed_1_7" name="__tabbed_1" type="radio" /><input id="__tabbed_1_8" name="__tabbed_1" type="radio" /><input id="__tabbed_1_9" name="__tabbed_1" type="radio" /><input id="__tabbed_1_10" name="__tabbed_1" type="radio" /><div class="tabbed-labels"><label for="__tabbed_1_1">Java</label><label for="__tabbed_1_2">C++</label><label for="__tabbed_1_3">Python</label><label for="__tabbed_1_4">Go</label><label for="__tabbed_1_5">JavaScript</label><label for="__tabbed_1_6">TypeScript</label><label for="__tabbed_1_7">C</label><label for="__tabbed_1_8">C#</label><label for="__tabbed_1_9">Swift</label><label for="__tabbed_1_10">Zig</label></div>
|
||||
<div class="tabbed-content">
|
||||
<div class="tabbed-block">
|
||||
@ -2040,12 +2036,12 @@
|
||||
</div>
|
||||
<h2 id="312">3.1.2. 计算机内存<a class="headerlink" href="#312" title="Permanent link">¶</a></h2>
|
||||
<p>在计算机中,内存和硬盘是两种主要的存储硬件设备。「硬盘」主要用于长期存储数据,容量较大(通常可达到 TB 级别)、速度较慢。「内存」用于运行程序时暂存数据,速度较快,但容量较小(通常为 GB 级别)。</p>
|
||||
<p><strong>算法运行中,相关数据都被存储在内存中</strong>。下图展示了一个计算机内存条,其中每个黑色方块都包含一块内存空间。我们可以将内存想象成一个巨大的 Excel 表格,其中每个单元格都可以存储 1 byte 的数据,在算法运行时,所有数据都被存储在这些单元格中。</p>
|
||||
<p><strong>系统通过「内存地址 Memory Location」来访问目标内存位置的数据</strong>。计算机根据特定规则给表格中每个单元格编号,保证每块内存空间都有独立的内存地址。自此,程序便通过这些地址,访问内存中的数据。</p>
|
||||
<p><strong>在算法运行过程中,相关数据都存储在内存中</strong>。下图展示了一个计算机内存条,其中每个黑色方块都包含一块内存空间。我们可以将内存想象成一个巨大的 Excel 表格,其中每个单元格都可以存储 1 byte 的数据,在算法运行时,所有数据都被存储在这些单元格中。</p>
|
||||
<p><strong>系统通过「内存地址 Memory Location」来访问目标内存位置的数据</strong>。计算机根据特定规则为表格中的每个单元格分配编号,确保每个内存空间都有唯一的内存地址。有了这些地址,程序便可以访问内存中的数据。</p>
|
||||
<p><img alt="内存条、内存空间、内存地址" src="../data_and_memory.assets/computer_memory_location.png" /></p>
|
||||
<p align="center"> Fig. 内存条、内存空间、内存地址 </p>
|
||||
|
||||
<p><strong>内存资源是设计数据结构与算法的重要考虑因素</strong>。内存是所有程序的公共资源,当内存被某程序占用时,不能被其它程序同时使用。我们需要根据剩余内存资源的情况来设计算法。例如,若剩余内存空间有限,则要求算法占用的峰值内存不能超过系统剩余内存;若运行的程序很多、缺少大块连续的内存空间,则要求选取的数据结构必须能够存储在离散的内存空间内。</p>
|
||||
<p><strong>在数据结构与算法的设计中,内存资源是一个重要的考虑因素</strong>。内存是所有程序的共享资源,当内存被某个程序占用时,其他程序无法同时使用。我们需要根据剩余内存资源的实际情况来设计算法。例如,算法所占用的内存峰值不应超过系统剩余空闲内存;如果运行的程序很多并且缺少大量连续的内存空间,那么所选用的数据结构必须能够存储在离散的内存空间内。</p>
|
||||
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user