diff --git a/docs/chapter_data_structure/basic_data_types.md b/docs/chapter_data_structure/basic_data_types.md
new file mode 100644
index 000000000..5a1fc0e9b
--- /dev/null
+++ b/docs/chapter_data_structure/basic_data_types.md
@@ -0,0 +1,125 @@
+# 基本数据类型
+
+谈及计算机中的数据,我们会想到文本、图片、视频、语音、3D 模型等各种形式。尽管这些数据的组织形式各异,但它们都由各种基本数据类型构成。
+
+**基本数据类型是 CPU 可以直接进行运算的类型,在算法中直接被使用**。它包括:
+
+- 整数类型 `byte` , `short` , `int` , `long` ;
+- 浮点数类型 `float` , `double` ,用于表示小数;
+- 字符类型 `char` ,用于表示各种语言的字母、标点符号、甚至表情符号等;
+- 布尔类型 `bool` ,用于表示“是”与“否”判断;
+
+**所有基本数据类型都以二进制的形式存储在计算机中**。在计算机中,我们将 $1$ 个二进制位称为 $1$ 比特,并规定 $1$ 字节(byte)由 $8$ 比特(bits)组成。基本数据类型的取值范围取决于其占用的空间大小,例如:
+
+- 整数类型 `byte` 占用 $1$ byte = $8$ bits ,可以表示 $2^{8}$ 个不同的数字;
+- 整数类型 `int` 占用 $4$ bytes = $32$ bits ,可以表示 $2^{32}$ 个数字;
+
+下表列举了各种基本数据类型的占用空间、取值范围和默认值。此表格无需硬背,大致理解即可,需要时可以通过查表来回忆。
+
+
+
+| 类型 | 符号 | 占用空间 | 最小值 | 最大值 | 默认值 |
+| ------ | -------- | ---------------- | ------------------------ | ----------------------- | -------------- |
+| 整数 | `byte` | 1 byte | $-2^7$ ($-128$) | $2^7 - 1$ ($127$) | $0$ |
+| | `short` | 2 bytes | $-2^{15}$ | $2^{15} - 1$ | $0$ |
+| | `int` | 4 bytes | $-2^{31}$ | $2^{31} - 1$ | $0$ |
+| | `long` | 8 bytes | $-2^{63}$ | $2^{63} - 1$ | $0$ |
+| 浮点数 | `float` | 4 bytes | $1.175 \times 10^{-38}$ | $3.403 \times 10^{38}$ | $0.0 f$ |
+| | `double` | 8 bytes | $2.225 \times 10^{-308}$ | $1.798 \times 10^{308}$ | $0.0$ |
+| 字符 | `char` | 2 bytes / 1 byte | $0$ | $2^{16} - 1$ | $0$ |
+| 布尔 | `bool` | 1 byte / 1 bit | $\text{false}$ | $\text{true}$ | $\text{false}$ |
+
+
+
+那么,基本数据类型与数据结构之间有什么联系与区别呢?我们知道,数据结构是在计算机中组织与存储数据的方式。它的主语是“结构”,而非“数据”。如果想要表示“一排数字”,我们自然会想到使用数组。这是因为数组的线性结构可以表示数字的相邻关系和顺序关系,但至于存储的内容是整数 `int` 、小数 `float` 、还是字符 `char` ,则与“数据结构”无关。
+
+换句话说,**基本数据类型提供了数据的“内容类型”,而数据结构提供了数据的“组织方式”**。如以下代码所示,我们可以使用不同基本数据类型来初始化数组。
+
+=== "Java"
+
+ ```java title=""
+ /* 使用多种「基本数据类型」来初始化「数组」 */
+ int[] numbers = new int[5];
+ float[] decimals = new float[5];
+ char[] characters = new char[5];
+ boolean[] booleans = new boolean[5];
+ ```
+
+=== "C++"
+
+ ```cpp title=""
+ /* 使用多种「基本数据类型」来初始化「数组」 */
+ int numbers[5];
+ float decimals[5];
+ char characters[5];
+ bool booleans[5];
+ ```
+
+=== "Python"
+
+ ```python title=""
+ # Python 的 list 可以自由存储各种基本数据类型和对象
+ list = [0, 0.0, 'a', False]
+ ```
+
+=== "Go"
+
+ ```go title=""
+ // 使用多种「基本数据类型」来初始化「数组」
+ var numbers = [5]int{}
+ var decimals = [5]float64{}
+ var characters = [5]byte{}
+ var booleans = [5]bool{}
+ ```
+
+=== "JavaScript"
+
+ ```javascript title=""
+ /* JavaScript 的数组可以自由存储各种基本数据类型和对象 */
+ const array = [0, 0.0, 'a', false];
+ ```
+
+=== "TypeScript"
+
+ ```typescript title=""
+ /* 使用多种「基本数据类型」来初始化「数组」 */
+ const numbers: number[] = [];
+ const characters: string[] = [];
+ const booleans: boolean[] = [];
+ ```
+
+=== "C"
+
+ ```c title=""
+ /* 使用多种「基本数据类型」来初始化「数组」 */
+ int numbers[10];
+ float decimals[10];
+ char characters[10];
+ bool booleans[10];
+ ```
+
+=== "C#"
+
+ ```csharp title=""
+ /* 使用多种「基本数据类型」来初始化「数组」 */
+ int[] numbers = new int[5];
+ float[] decimals = new float[5];
+ char[] characters = new char[5];
+ bool[] booleans = new bool[5];
+ ```
+
+=== "Swift"
+
+ ```swift title=""
+ /* 使用多种「基本数据类型」来初始化「数组」 */
+ let numbers = Array(repeating: Int(), count: 5)
+ let decimals = Array(repeating: Double(), count: 5)
+ let characters = Array(repeating: Character("a"), count: 5)
+ let booleans = Array(repeating: Bool(), count: 5)
+ ```
+
+=== "Zig"
+
+ ```zig title=""
+
+ ```
diff --git a/docs/chapter_data_structure/character_encoding.assets/ascii_table.png b/docs/chapter_data_structure/character_encoding.assets/ascii_table.png
new file mode 100644
index 000000000..01dd642f3
Binary files /dev/null and b/docs/chapter_data_structure/character_encoding.assets/ascii_table.png differ
diff --git a/docs/chapter_data_structure/character_encoding.assets/unicode_hello_algo.png b/docs/chapter_data_structure/character_encoding.assets/unicode_hello_algo.png
new file mode 100644
index 000000000..4eb791abe
Binary files /dev/null and b/docs/chapter_data_structure/character_encoding.assets/unicode_hello_algo.png differ
diff --git a/docs/chapter_data_structure/character_encoding.assets/utf-8_hello_algo.png b/docs/chapter_data_structure/character_encoding.assets/utf-8_hello_algo.png
new file mode 100644
index 000000000..efbec2b80
Binary files /dev/null and b/docs/chapter_data_structure/character_encoding.assets/utf-8_hello_algo.png differ
diff --git a/docs/chapter_data_structure/character_encoding.md b/docs/chapter_data_structure/character_encoding.md
new file mode 100644
index 000000000..bb8600a38
--- /dev/null
+++ b/docs/chapter_data_structure/character_encoding.md
@@ -0,0 +1,65 @@
+# 字符集与编码
+
+在计算机中,所有数据都是以二进制数的形式存储的,字符 `char` 也不例外。为了表示字符,我们需要建立一套「字符集」,规定每个字符和二进制数之间的一一对应关系。有了字符集之后,计算机就可以通过查表完成二进制数到字符的转换。
+
+## ASCII 字符集
+
+「ASCII 码」是最早出现的字符集,全称为“美国标准信息交换代码”。它使用 7 位二进制数(即一个字节的前 7 位)表示一个字符,最多能够表示 128 个不同的字符。这包括英文字母的大小写、数字 0-9 、一些标点符号,以及一些控制字符(如换行符和制表符)。
+
+![ASCII 码](character_encoding.assets/ascii_table.png)
+
+然而,ASCII 码仅局限于表示英文。随着计算机的全球化,一种能够表示更多语言的字符集「EASCII」应运而生。它在 ASCII 的 7 位基础上扩展到 8 位,能够表示 256 个不同的字符。世界陆续诞生了一批适用于不同地区的 EASCII 字符集。这些字符集的前 128 个字符统一为 ASCII 码,后 128 个字符定义了不同语言的字符。
+
+## GBK 字符集
+
+EASCII 码仍然无法满足许多语言的字符数量要求。例如,汉字大约有近十万个,光日常使用的就有几千个。为此,中国国家标准总局于 1980 年发布了「GB2312」字符集,其收录了 6763 个汉字,基本满足了汉字的计算机处理需要。
+
+然而,GB2312 无法处理部分的罕见字和繁体字。之后在 GB2312 的基础上,扩展得到了「GBK」字符集,它共收录了 21886 个汉字。在 GBK 编码方案中,ASCII 字符使用一个字节表示,汉字使用两个字节表示。
+
+## Unicode 字符集
+
+随着计算机的蓬勃发展,在世界范围内诞生了许多字符集与编码标准,而这带来了许多问题。一方面,这些字符集一般只定义了某种特定语言的字符,无法实现跨语言解析;另一方面,同一种语言也存在多种字符集,如果两台电脑安装的是不同的编码标准,则在信息传递时就会出现乱码。
+
+那个时代的人们会想:如果推出一个足够完整的字符集,将世界范围内的所有语言和符号都纳入其中,不就可以解决跨语言环境和乱码问题了吗?在这种思考下,一个庞大的字符集 Unicode 应运而生。
+
+「Unicode」的全称为“统一字符编码”,理论上能容纳一百多万个字符。它致力于将全球范围内的字符纳入到统一的字符集之中,提供一种通用的字符集来处理和显示各种语言文字,减少因为编码标准不同而产生的乱码问题。
+
+自 1991 年发布以来,Unicode 不断扩充新的语言与字符。截止 2022 年 9 月,Unicode 已经包含 149186 个字符,包括各种语言的字符、符号、甚至是表情符号等。在庞大的 Unicode 字符集中,常用的字符占用 2 字节,有些生僻的字符占 3 字节甚至 4 字节。
+
+Unicode 是一种字符集标准,本质上是给每个字符分配一个编号(称为“码点”),**但它并没有规定在计算机中如何存储这些字符码点**。我们不禁会问:当多种长度的 Unicode 码点同时出现在同一个文本中时,系统如何解析字符?例如,给定一个长度为 2 字节的编码,系统如何确认它是一个 2 字节的字符还是两个 1 字节的字符?
+
+**最直接的解决方案是将所有字符存储为等长的编码**。如下图所示,“Hello”中的每个字符占用 1 字节,“算法”中的每个字符占用 2 字节。我们可以通过高位填 0 ,将“Hello 算法”中的所有字符都编码为 2 字节长度。这样系统就可以每隔 2 字节解析一个字符,恢复出这个短语的内容了。
+
+![Unicode 编码示例](character_encoding.assets/unicode_hello_algo.png)
+
+然而,ASCII 码已经向我们证明,编码英文只需要 1 字节。若采用上述方案,英文文本占用空间的大小将会是 ASCII 编码下大小的 2 倍,非常浪费内存空间。因此,我们需要一种更加高效的 Unicode 编码方法。
+
+## UTF-8 编码
+
+随着互联网的发展,UTF-8 成为国际上使用最广泛的 Unicode 编码方法。**它是一种可变长的编码**,使用 1 到 4 个字节来表示一个字符,根据字符的复杂性而变。ASCII 字符只需要 1 个字节,拉丁字母和希腊字母需要 2 个字节,常用的中文字符需要 3 个字节,其他的一些生僻字符需要 4 个字节。UTF-8 的编码规则是:
+
+- 对于长度为 1 字节的字符,将最高位设置为 0 、其余 7 位设置为 Unicode 码点。值得注意的是,ASCII 字符在 Unicode 字符集中占据了前 128 个码点。也就是说,**UTF-8 编码可以向下兼容 ASCII 码**。这意味着我们可以使用 UTF-8 来解析年代久远的 ASCII 码文本。
+- 对于长度为 $n$ 字节的字符(其中 $n > 1$),将首个字节的高 $n$ 位都设置为 $1$ 、第 $n + 1$ 位设置为 $0$ ;从第二个字节开始,将每个字节的高 2 位都设置为 $10$ ;其余所有位用于填充字符的 Unicode 码点。
+
+下图展示了“Hello算法”对应的 UTF-8 编码。将最高 $n$ 位设置为 $1$ 比较容易理解,可以向系统指出字符的长度为 $n$ 。那么,为什么要将其余所有字节的高 2 位都设置为 $10$ 呢?实际上,这个 $10$ 能够起到校验符的作用,因为在 UTF-8 编码规则下,不可能有字符的最高两位是 $10$ 。这是因为长度为 1 字节的字符的最高一位是 $0$ 。假设系统从一个错误的字节开始解析文本,字节头部的 $10$ 能够帮助系统快速的判断出异常。
+
+![UTF-8 编码示例](character_encoding.assets/utf-8_hello_algo.png)
+
+除了 UTF-8 之外,常见的编码方式还包括 UTF-16 和 UTF-32 。它们为 Unicode 字符集提供了不同的编码方法。
+
+- **UTF-16 编码**:使用 2 或 4 个字节来表示一个字符。所有的 ASCII 字符和很多常用的中文字符,都用 2 个字节表示。少数字符需要用到 4 个字节表示。
+- **UTF-32 编码**:每个字符都使用 4 个字节。这意味着 UTF-32 会比 UTF-8 和 UTF-16 更占用空间,特别是对于主要使用 ASCII 字符的文本。
+
+从存储空间的角度看,使用 UTF-8 表示英文字符非常高效,因为它仅需 1 个字节;使用 UTF-16 编码某些非英文字符(例如中文)会更加高效,因为它只需要 2 个字节,而 UTF-8 可能需要 3 个字节。从兼容性的角度看,UTF-8 的通用性最佳,许多工具和库都优先支持 UTF-8 。
+
+如下表所示,为各个编程语言的字符串默认编码方式。由于 UTF-16 和 UTF-32 属于等长的编码方法,因此编程语言可以直接计算字符串的长度,也可以快速地访问字符串中的任意字符。而如果使用 UTF-8 这种变长的编码方法,编程语言往往需要额外维护一个字符数组,才能实现高效的随机访问。
+
+
+
+| 编码 | 编程语言 |
+| ------ | -------------------------------- |
+| UTF-8 | Python, Go, Rust, Swift |
+| UTF-16 | Java, C#, JavaScript, TypeScript |
+| UTF-32 | / |
+
+
diff --git a/docs/chapter_data_structure/data_and_memory.assets/computer_memory_location.png b/docs/chapter_data_structure/classification_of_data_structure.assets/computer_memory_location.png
similarity index 100%
rename from docs/chapter_data_structure/data_and_memory.assets/computer_memory_location.png
rename to docs/chapter_data_structure/classification_of_data_structure.assets/computer_memory_location.png
diff --git a/docs/chapter_data_structure/classification_of_data_structure.md b/docs/chapter_data_structure/classification_of_data_structure.md
index 079cde1f1..7f3f3dd0e 100644
--- a/docs/chapter_data_structure/classification_of_data_structure.md
+++ b/docs/chapter_data_structure/classification_of_data_structure.md
@@ -15,11 +15,17 @@
## 物理结构:连续与离散
-!!! note
+在计算机中,内存和硬盘是两种主要的存储硬件设备。「硬盘」主要用于长期存储数据,容量较大(通常可达到 TB 级别)、速度较慢。「内存」用于运行程序时暂存数据,速度较快,但容量较小(通常为 GB 级别)。
- 如若阅读起来有困难,建议先阅读下一章“数组与链表”,然后再回头理解物理结构的含义。
+**在算法运行过程中,相关数据都存储在内存中**。下图展示了一个计算机内存条,其中每个黑色方块都包含一块内存空间。我们可以将内存想象成一个巨大的 Excel 表格,其中每个单元格都可以存储 1 byte 的数据,在算法运行时,所有数据都被存储在这些单元格中。
-**「物理结构」体现了数据在计算机内存中的存储方式**,可以分为数组的连续空间存储和链表的离散空间存储。物理结构从底层决定了数据的访问、更新、增删等操作方法,同时在时间效率和空间效率方面呈现出互补的特点。
+**系统通过「内存地址 Memory Location」来访问目标内存位置的数据**。计算机根据特定规则为表格中的每个单元格分配编号,确保每个内存空间都有唯一的内存地址。有了这些地址,程序便可以访问内存中的数据。
+
+![内存条、内存空间、内存地址](classification_of_data_structure.assets/computer_memory_location.png)
+
+内存是所有程序的共享资源,当内存被某个程序占用时,其他程序无法同时使用。**因此,在数据结构与算法的设计中,内存资源是一个重要的考虑因素**。例如,算法所占用的内存峰值不应超过系统剩余空闲内存;如果运行的程序很多并且缺少大量连续的内存空间,那么所选用的数据结构必须能够存储在离散的内存空间内。
+
+**「物理结构」反映了数据在计算机内存中的存储方式**,可分为数组的连续空间存储和链表的离散空间存储。物理结构从底层决定了数据的访问、更新、增删等操作方法,同时在时间效率和空间效率方面呈现出互补的特点。
![连续空间存储与离散空间存储](classification_of_data_structure.assets/classification_phisical_structure.png)
@@ -32,4 +38,4 @@
!!! tip
- 数组与链表是其他所有数据结构的“底层积木”,建议读者投入更多时间深入了解这两种基本数据结构。
+ 如若感觉理解物理结构有困难,建议先阅读下一章“数组与链表”,然后再回头理解物理结构的含义。数组与链表是其他所有数据结构的基石,建议你投入更多时间深入了解这两种基本数据结构。
diff --git a/docs/chapter_data_structure/data_and_memory.assets/ieee_754_float.png b/docs/chapter_data_structure/data_and_memory.assets/ieee_754_float.png
deleted file mode 100644
index 897c8b7e8..000000000
Binary files a/docs/chapter_data_structure/data_and_memory.assets/ieee_754_float.png and /dev/null differ
diff --git a/docs/chapter_data_structure/data_and_memory.md b/docs/chapter_data_structure/data_and_memory.md
deleted file mode 100644
index 82f0a89a7..000000000
--- a/docs/chapter_data_structure/data_and_memory.md
+++ /dev/null
@@ -1,211 +0,0 @@
-# 数据与内存
-
-## 基本数据类型
-
-谈及计算机中的数据,我们会想到文本、图片、视频、语音、3D 模型等各种形式。尽管这些数据的组织形式各异,但它们都由各种基本数据类型构成。
-
-**「基本数据类型」是 CPU 可以直接进行运算的类型,在算法中直接被使用**。
-
-- 「整数」按照不同的长度分为 byte, short, int, long 。在满足取值范围的前提下,我们应该尽量选取较短的整数类型,以减小内存空间占用;
-- 「浮点数」表示小数,按长度分为 float, double ,选用规则与整数相同。
-- 「字符」在计算机中以字符集形式保存,char 的值实际上是数字,代表字符集中的编号,计算机通过字符集查表完成编号到字符的转换。
-- 「布尔」代表逻辑中的“是”与“否”,其占用空间需根据编程语言确定。
-
-
-
-| 类别 | 符号 | 占用空间 | 取值范围 | 默认值 |
-| ------ | ----------- | ----------------- | ---------------------------------------------- | -------------- |
-| 整数 | byte | 1 byte | $-2^7$ ~ $2^7 - 1$ ( $-128$ ~ $127$ ) | $0$ |
-| | short | 2 bytes | $-2^{15}$ ~ $2^{15} - 1$ | $0$ |
-| | **int** | 4 bytes | $-2^{31}$ ~ $2^{31} - 1$ | $0$ |
-| | long | 8 bytes | $-2^{63}$ ~ $2^{63} - 1$ | $0$ |
-| 浮点数 | **float** | 4 bytes | $-3.4 \times 10^{38}$ ~ $3.4 \times 10^{38}$ | $0.0$ f |
-| | double | 8 bytes | $-1.7 \times 10^{308}$ ~ $1.7 \times 10^{308}$ | $0.0$ |
-| 字符 | **char** | 2 bytes / 1 byte | $0$ ~ $2^{16} - 1$ | $0$ |
-| 布尔 | **bool** | 1 byte / 1 bit | $\text{true}$ 或 $\text{false}$ | $\text{false}$ |
-
-
-
-以上表格中,加粗项在算法题中最为常用。此表格无需硬背,大致理解即可,需要时可以通过查表来回忆。
-
-### 整数表示方式
-
-整数的取值范围取决于变量使用的内存长度,即字节(或比特)数。在计算机中,1 字节 (byte) = 8 比特 (bit),1 比特即 1 个二进制位。以 int 类型为例:
-
-1. 整数类型 int 占用 4 bytes = 32 bits ,可以表示 $2^{32}$ 个不同的数字;
-2. 将最高位视为符号位,$0$ 代表正数,$1$ 代表负数,一共可表示 $2^{31}$ 个正数和 $2^{31}$ 个负数;
-3. 当所有 bits 为 0 时代表数字 $0$ ,从零开始增大,可得最大正数为 $2^{31} - 1$ ;
-4. 剩余 $2^{31}$ 个数字全部用来表示负数,因此最小负数为 $-2^{31}$ ;具体细节涉及“源码、反码、补码”的相关知识,有兴趣的同学可以查阅学习;
-
-其他整数类型 byte, short, long 的取值范围的计算方法与 int 类似,在此不再赘述。
-
-### 浮点数表示方式 *
-
-!!! note
-
- 本书中,标题后的 * 符号代表选读章节。如果你觉得理解困难,建议先跳过,等学完必读章节后再单独攻克。
-
-细心的你可能会发现:int 和 float 长度相同,都是 4 bytes,但为什么 float 的取值范围远大于 int ?按理说 float 需要表示小数,取值范围应该变小才对。
-
-实际上,这是因为浮点数 float 采用了不同的表示方式。根据 IEEE 754 标准,32-bit 长度的 float 由以下部分构成:
-
-- 符号位 $\mathrm{S}$ :占 1 bit ;
-- 指数位 $\mathrm{E}$ :占 8 bits ;
-- 分数位 $\mathrm{N}$ :占 24 bits ,其中 23 位显式存储;
-
-设 32-bit 二进制数的第 $i$ 位为 $b_i$ ,则 float 值的计算方法定义为:
-
-$$
-\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
-$$
-
-转化到十进制下的计算公式为
-
-$$
-\text { val }=(-1)^{\mathrm{S}} \times 2^{\mathrm{E} -127} \times (1 + \mathrm{N})
-$$
-
-其中各项的取值范围为
-
-$$
-\begin{aligned}
-\mathrm{S} \in & \{ 0, 1\} , \quad \mathrm{E} \in \{ 1, 2, \dots, 254 \} \newline
-(1 + \mathrm{N}) = & (1 + \sum_{i=1}^{23} b_{23-i} 2^{-i}) \subset [1, 2 - 2^{-23}]
-\end{aligned}
-$$
-
-![IEEE 754 标准下的 float 表示方式](data_and_memory.assets/ieee_754_float.png)
-
-以上图为例,$\mathrm{S} = 0$ , $\mathrm{E} = 124$ ,$\mathrm{N} = 2^{-2} + 2^{-3} = 0.375$ ,易得
-
-$$
-\text { val } = (-1)^0 \times 2^{124 - 127} \times (1 + 0.375) = 0.171875
-$$
-
-现在我们可以回答最初的问题:**float 的表示方式包含指数位,导致其取值范围远大于 int** 。根据以上计算,float 可表示的最大正数为 $2^{254 - 127} \times (2 - 2^{-23}) \approx 3.4 \times 10^{38}$ ,切换符号位便可得到最小负数。
-
-**尽管浮点数 float 扩展了取值范围,但其副作用是牺牲了精度**。整数类型 int 将全部 32 位用于表示数字,数字是均匀分布的;而由于指数位的存在,浮点数 float 的数值越大,相邻两个数字之间的差值就会趋向越大。
-
-进一步地,指数位 $E = 0$ 和 $E = 255$ 具有特殊含义,**用于表示零、无穷大、$\mathrm{NaN}$ 等**。
-
-
-
-| 指数位 E | 分数位 $\mathrm{N} = 0$ | 分数位 $\mathrm{N} \ne 0$ | 计算公式 |
-| ------------------ | ----------------------- | ---------------------------- | ------------------------------------------------------------ |
-| $0$ | $\pm 0$ | 次正规数 | $(-1)^{\mathrm{S}} \times 2^{-126} \times (0.\mathrm{N})$ |
-| $1, 2, \dots, 254$ | 正规数 | 正规数 | $(-1)^{\mathrm{S}} \times 2^{(\mathrm{E} -127)} \times (1.\mathrm{N})$ |
-| $255$ | $\pm \infty$ | $\mathrm{NaN}$ | |
-
-
-
-特别地,次正规数显著提升了浮点数的精度,这是因为:
-
-- 最小正正规数为 $2^{-126} \approx 1.18 \times 10^{-38}$ ;
-- 最小正次正规数为 $2^{-126} \times 2^{-23} \approx 1.4 \times 10^{-45}$ ;
-
-双精度 double 也采用类似 float 的表示方法,此处不再详述。
-
-### 基本数据类型与数据结构的关系
-
-我们知道,**数据结构是在计算机中组织与存储数据的方式**,它的核心是“结构”,而非“数据”。如果想要表示“一排数字”,我们自然会想到使用「数组」数据结构。数组的存储方式可以表示数字的相邻关系、顺序关系,但至于具体存储的是整数 int 、小数 float 、还是字符 char ,则与“数据结构”无关。换句话说,基本数据类型提供了数据的“内容类型”,而数据结构提供了数据的“组织方式”。
-
-=== "Java"
-
- ```java title=""
- /* 使用多种「基本数据类型」来初始化「数组」 */
- int[] numbers = new int[5];
- float[] decimals = new float[5];
- char[] characters = new char[5];
- boolean[] booleans = new boolean[5];
- ```
-
-=== "C++"
-
- ```cpp title=""
- /* 使用多种「基本数据类型」来初始化「数组」 */
- int numbers[5];
- float decimals[5];
- char characters[5];
- bool booleans[5];
- ```
-
-=== "Python"
-
- ```python title=""
- # Python 的 list 可以自由存储各种基本数据类型和对象
- list = [0, 0.0, 'a', False]
- ```
-
-=== "Go"
-
- ```go title=""
- // 使用多种「基本数据类型」来初始化「数组」
- var numbers = [5]int{}
- var decimals = [5]float64{}
- var characters = [5]byte{}
- var booleans = [5]bool{}
- ```
-
-=== "JavaScript"
-
- ```javascript title=""
- /* JavaScript 的数组可以自由存储各种基本数据类型和对象 */
- const array = [0, 0.0, 'a', false];
- ```
-
-=== "TypeScript"
-
- ```typescript title=""
- /* 使用多种「基本数据类型」来初始化「数组」 */
- const numbers: number[] = [];
- const characters: string[] = [];
- const booleans: boolean[] = [];
- ```
-
-=== "C"
-
- ```c title=""
- /* 使用多种「基本数据类型」来初始化「数组」 */
- int numbers[10];
- float decimals[10];
- char characters[10];
- bool booleans[10];
- ```
-
-=== "C#"
-
- ```csharp title=""
- /* 使用多种「基本数据类型」来初始化「数组」 */
- int[] numbers = new int[5];
- float[] decimals = new float[5];
- char[] characters = new char[5];
- bool[] booleans = new bool[5];
- ```
-
-=== "Swift"
-
- ```swift title=""
- /* 使用多种「基本数据类型」来初始化「数组」 */
- let numbers = Array(repeating: Int(), count: 5)
- let decimals = Array(repeating: Double(), count: 5)
- let characters = Array(repeating: Character("a"), count: 5)
- let booleans = Array(repeating: Bool(), count: 5)
- ```
-
-=== "Zig"
-
- ```zig title=""
-
- ```
-
-## 计算机内存
-
-在计算机中,内存和硬盘是两种主要的存储硬件设备。「硬盘」主要用于长期存储数据,容量较大(通常可达到 TB 级别)、速度较慢。「内存」用于运行程序时暂存数据,速度较快,但容量较小(通常为 GB 级别)。
-
-**在算法运行过程中,相关数据都存储在内存中**。下图展示了一个计算机内存条,其中每个黑色方块都包含一块内存空间。我们可以将内存想象成一个巨大的 Excel 表格,其中每个单元格都可以存储 1 byte 的数据,在算法运行时,所有数据都被存储在这些单元格中。
-
-**系统通过「内存地址 Memory Location」来访问目标内存位置的数据**。计算机根据特定规则为表格中的每个单元格分配编号,确保每个内存空间都有唯一的内存地址。有了这些地址,程序便可以访问内存中的数据。
-
-![内存条、内存空间、内存地址](data_and_memory.assets/computer_memory_location.png)
-
-**在数据结构与算法的设计中,内存资源是一个重要的考虑因素**。内存是所有程序的共享资源,当内存被某个程序占用时,其他程序无法同时使用。我们需要根据剩余内存资源的实际情况来设计算法。例如,算法所占用的内存峰值不应超过系统剩余空闲内存;如果运行的程序很多并且缺少大量连续的内存空间,那么所选用的数据结构必须能够存储在离散的内存空间内。
diff --git a/docs/chapter_data_structure/number_encoding.assets/1s_2s_complement.png b/docs/chapter_data_structure/number_encoding.assets/1s_2s_complement.png
new file mode 100644
index 000000000..ccc568632
Binary files /dev/null and b/docs/chapter_data_structure/number_encoding.assets/1s_2s_complement.png differ
diff --git a/docs/chapter_data_structure/number_encoding.assets/ieee_754_float.png b/docs/chapter_data_structure/number_encoding.assets/ieee_754_float.png
new file mode 100644
index 000000000..e14069d9e
Binary files /dev/null and b/docs/chapter_data_structure/number_encoding.assets/ieee_754_float.png differ
diff --git a/docs/chapter_data_structure/number_encoding.md b/docs/chapter_data_structure/number_encoding.md
new file mode 100644
index 000000000..e54c309f2
--- /dev/null
+++ b/docs/chapter_data_structure/number_encoding.md
@@ -0,0 +1,145 @@
+# 数字编码 *
+
+!!! note
+
+ 在本书中,标题带有的 * 符号的是选读章节。如果你时间有限或感到理解困难,建议先跳过,等学完必读章节后再单独攻克。
+
+## 原码、反码和补码
+
+从上一节的表格中我们发现,所有整数类型能够表示的负数都比正数多一个。例如,`byte` 的取值范围是 $[-128, 127]$ 。这个现象比较反直觉,它的内在原因涉及到原码、反码、补码的相关知识。在展开分析之前,我们首先给出三者的定义:
+
+- **原码**:我们将数字的二进制表示的最高位视为符号位,其中 $0$ 表示正数,$1$ 表示负数,其余位表示数字的值。
+- **反码**:正数的反码与其原码相同,负数的反码是对其原码除符号位外的所有位取反。
+- **补码**:正数的补码与其原码相同,负数的补码是在其反码的基础上加 $1$ 。
+
+![原码、反码与补码之间的相互转换](number_encoding.assets/1s_2s_complement.png)
+
+显然,「原码」最为直观,**然而数字却是以「补码」的形式存储在计算机中的**。这是因为原码存在一些局限性。
+
+一方面,**负数的原码不能直接用于运算**。例如,我们在原码下计算 $1 + (-2)$ ,得到的结果是 $-3$ ,这显然是不对的。
+
+$$
+\begin{aligned}
+& 1 + (-2) \newline
+& = 0000 \space 0001 + 1000 \space 0010 \newline
+& = 1000 \space 0011 \newline
+& = -3
+\end{aligned}
+$$
+
+为了解决此问题,计算机引入了「反码」。例如,我们先将原码转换为反码,并在反码下计算 $1 + (-2)$ ,并将结果从反码转化回原码,则可得到正确结果 $-1$ 。
+
+$$
+\begin{aligned}
+& 1 + (-2) \newline
+& = 0000 \space 0001 \space \text{(原码)} + 1000 \space 0010 \space \text{(原码)} \newline
+& = 0000 \space 0001 \space \text{(反码)} + 1111 \space 1101 \space \text{(反码)} \newline
+& = 1111 \space 1110 \space \text{(反码)} \newline
+& = 1000 \space 0001 \space \text{(原码)} \newline
+& = -1
+\end{aligned}
+$$
+
+另一方面,**数字零的原码有 $+0$ 和 $-0$ 两种表示方式**。这意味着数字零对应着两个不同的二进制编码,而这可能会带来歧义问题。例如,在条件判断中,如果没有区分正零和负零,可能会导致错误的判断结果。如果我们想要处理正零和负零歧义,则需要引入额外的判断操作,其可能会降低计算机的运算效率。
+
+$$
++0 = 0000 \space 0000 \newline
+-0 = 1000 \space 0000
+$$
+
+与原码一样,反码也存在正负零歧义问题。为此,计算机进一步引入了「补码」。那么,补码有什么作用呢?我们先来分析一下负零的补码的计算过程:
+
+$$
+\begin{aligned}
+-0 = \space & 1000 \space 0000 \space \text{(原码)} \newline
+= \space & 1111 \space 1111 \space \text{(反码)} \newline
+= 1 \space & 0000 \space 0000 \space \text{(补码)} \newline
+\end{aligned}
+$$
+
+在负零的反码基础上加 $1$ 会产生进位,而由于 byte 的长度只有 8 位,因此溢出到第 9 位的 $1$ 会被舍弃。**从而得到负零的补码为 $0000 \space 0000$ ,与正零的补码相同**。这意味着在补码表示中只存在一个零,从而解决了正负零歧义问题。
+
+还剩余最后一个疑惑:byte 的取值范围是 $[-128, 127]$ ,多出来的一个负数 $-128$ 是如何得到的呢?我们注意到,区间 $[-127, +127]$ 内的所有整数都有对应的原码、反码和补码,并且原码和补码之间是可以互相转换的。
+
+然而,**补码 $1000 \space 0000$ 是一个例外,它并没有对应的原码**。根据转换方法,我们得到该补码的原码为 $0000 \space 0000$ 。这显然是矛盾的,因为该原码表示数字 $0$ ,它的补码应该是自身。计算机规定这个特殊的补码 $1000 \space 0000$ 代表 $-128$ 。实际上,$(-1) + (-127)$ 在补码下的计算结果就是 $-128$ 。
+
+$$
+\begin{aligned}
+& (-127) + (-1) \newline
+& = 1111 \space 1111 \space \text{(原码)} + 1000 \space 0001 \space \text{(原码)} \newline
+& = 1000 \space 0000 \space \text{(反码)} + 1111 \space 1110 \space \text{(反码)} \newline
+& = 1000 \space 0001 \space \text{(补码)} + 1111 \space 1111 \space \text{(补码)} \newline
+& = 1000 \space 0000 \space \text{(补码)} \newline
+& = -128
+\end{aligned}
+$$
+
+你可能已经发现,上述的所有计算都是加法运算。这暗示着一个重要事实:**计算机内部的硬件电路主要是基于加法运算设计的**。这是因为加法运算相对于其他运算(比如乘法、除法和减法)来说,硬件实现起来更简单,更容易进行并行化处理,从而提高运算速度。
+
+然而,这并不意味着计算机只能做加法。**通过将加法与一些基本逻辑运算结合,计算机能够实现各种其他的数学运算**。例如,计算减法 $a - b$ 可以转换为计算加法 $a + (-b)$ ;计算乘法和除法可以转换为计算多次加法或减法。
+
+现在,我们可以总结出计算机使用补码的原因:基于补码表示,计算机可以用同样的电路和操作来处理正数和负数的加法,不需要设计特殊的硬件电路来处理减法,并且无需特别处理正负零的歧义问题。这大大简化了硬件设计,并提高了运算效率。
+
+补码的设计非常精妙,由于篇幅关系我们先介绍到这里。建议有兴趣的读者进一步深度了解。
+
+## 浮点数编码
+
+细心的你可能会发现:`int` 和 `float` 长度相同,都是 4 bytes,但为什么 `float` 的取值范围远大于 `int` ?这非常反直觉,因为按理说 `float` 需要表示小数,取值范围应该变小才对。
+
+实际上,这是因为浮点数 `float` 采用了不同的表示方式。根据 IEEE 754 标准,32-bit 长度的 `float` 由以下部分构成:
+
+- 符号位 $\mathrm{S}$ :占 1 bit ;
+- 指数位 $\mathrm{E}$ :占 8 bits ;
+- 分数位 $\mathrm{N}$ :占 24 bits ,其中 23 位显式存储;
+
+设 32-bit 二进制数的第 $i$ 位为 $b_i$ ,则 `float` 值的计算方法定义为:
+
+$$
+\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
+$$
+
+转化到十进制下的计算公式为
+
+$$
+\text { val }=(-1)^{\mathrm{S}} \times 2^{\mathrm{E} -127} \times (1 + \mathrm{N})
+$$
+
+其中各项的取值范围为
+
+$$
+\begin{aligned}
+\mathrm{S} \in & \{ 0, 1\} , \quad \mathrm{E} \in \{ 1, 2, \dots, 254 \} \newline
+(1 + \mathrm{N}) = & (1 + \sum_{i=1}^{23} b_{23-i} 2^{-i}) \subset [1, 2 - 2^{-23}]
+\end{aligned}
+$$
+
+![IEEE 754 标准下的 float 表示方式](number_encoding.assets/ieee_754_float.png)
+
+以上图为例,$\mathrm{S} = 0$ , $\mathrm{E} = 124$ ,$\mathrm{N} = 2^{-2} + 2^{-3} = 0.375$ ,易得
+
+$$
+\text { val } = (-1)^0 \times 2^{124 - 127} \times (1 + 0.375) = 0.171875
+$$
+
+现在我们可以回答最初的问题:**`float` 的表示方式包含指数位,导致其取值范围远大于 `int`** `。根据以上计算,float` 可表示的最大正数为 $2^{254 - 127} \times (2 - 2^{-23}) \approx 3.4 \times 10^{38}$ ,切换符号位便可得到最小负数。
+
+**尽管浮点数 `float` 扩展了取值范围,但其副作用是牺牲了精度**。整数类型 `int` 将全部 32 位用于表示数字,数字是均匀分布的;而由于指数位的存在,浮点数 `float` 的数值越大,相邻两个数字之间的差值就会趋向越大。
+
+进一步地,指数位 $E = 0$ 和 $E = 255$ 具有特殊含义,**用于表示零、无穷大、$\mathrm{NaN}$ 等**。
+
+
+
+| 指数位 E | 分数位 $\mathrm{N} = 0$ | 分数位 $\mathrm{N} \ne 0$ | 计算公式 |
+| ------------------ | ----------------------- | ------------------------- | ---------------------------------------------------------------------- |
+| $0$ | $\pm 0$ | 次正规数 | $(-1)^{\mathrm{S}} \times 2^{-126} \times (0.\mathrm{N})$ |
+| $1, 2, \dots, 254$ | 正规数 | 正规数 | $(-1)^{\mathrm{S}} \times 2^{(\mathrm{E} -127)} \times (1.\mathrm{N})$ |
+| $255$ | $\pm \infty$ | $\mathrm{NaN}$ | |
+
+
+
+特别地,次正规数显著提升了浮点数的精度,这是因为:
+
+- 最小正正规数为 $2^{-126} \approx 1.18 \times 10^{-38}$ ;
+- 最小正次正规数为 $2^{-126} \times 2^{-23} \approx 1.4 \times 10^{-45}$ ;
+
+双精度 `double` 也采用类似 `float` 的表示方法,此处不再详述。
diff --git a/docs/chapter_data_structure/summary.md b/docs/chapter_data_structure/summary.md
index ca005bbc9..5a29901d5 100644
--- a/docs/chapter_data_structure/summary.md
+++ b/docs/chapter_data_structure/summary.md
@@ -1,7 +1,14 @@
# 小结
-- 计算机中的基本数据类型包括整数 byte, short, int, long 、浮点数 float, double 、字符 char 和布尔 boolean ,它们的取值范围取决于占用空间大小和表示方式。
-- 当程序运行时,数据被存储在计算机内存中。每个内存空间都拥有对应的内存地址,程序通过这些内存地址访问数据。
- 数据结构可以从逻辑结构和物理结构两个角度进行分类。逻辑结构描述了数据元素之间的逻辑关系,而物理结构描述了数据在计算机内存中的存储方式。
- 常见的逻辑结构包括线性、树状和网状等。通常我们根据逻辑结构将数据结构分为线性(数组、链表、栈、队列)和非线性(树、图、堆)两种。哈希表的实现可能同时包含线性和非线性结构。
+- 当程序运行时,数据被存储在计算机内存中。每个内存空间都拥有对应的内存地址,程序通过这些内存地址访问数据。
- 物理结构主要分为连续空间存储(数组)和离散空间存储(链表)。所有数据结构都是由数组、链表或两者的组合实现的。
+- 计算机中的基本数据类型包括整数 byte, short, int, long 、浮点数 float, double 、字符 char 和布尔 boolean 。它们的取值范围取决于占用空间大小和表示方式。
+- 整数的原码的最高位是符号位,其余位是数字的值。数字的原码、反码和补码是可以相互转换的。
+- 整数在计算机中是以补码的形式存储的。在补码表示下,计算机可以对正数和负数的加法一视同仁,不需要为减法操作单独设计特殊的硬件电路,并且不存在正负零歧义的问题。
+- 浮点数的编码由 1 位符号位、8 位指数位和 23 位分数为构成。由于存在指数位,浮点数的取值范围远大于整数。
+- ASCII 码是最早出现的英文字符集,长度为 1 字节,共收录 127 个字符。GBK 字符集是常用的中文字符集,共收录两万多个汉字。
+- Unicode 致力于提供一个统一的字符集标准,其包含世界范围内的各种字符,从而解决由于字符编码不同导致的乱码问题。
+- UTF-8 是最受欢迎的 Unicode 编码方法,通用性非常好。它是一种变长的编码方法,具有很好的扩展性,有效提升了存储空间的使用效率。
+- UTF-16 和 UTF-32 是等长的编码方法。在编码中文时,UTF-16 比 UTF-8 的占用空间更小。Java, C# 等编程语言默认使用 UTF-16 编码。
diff --git a/mkdocs.yml b/mkdocs.yml
index 56017ca4e..dcb3143d6 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -137,10 +137,12 @@ nav:
- 2.2. 时间复杂度: chapter_computational_complexity/time_complexity.md
- 2.3. 空间复杂度: chapter_computational_complexity/space_complexity.md
- 2.4. 小结: chapter_computational_complexity/summary.md
- - 3. 数据结构简介:
- - 3.1. 数据与内存: chapter_data_structure/data_and_memory.md
- - 3.2. 数据结构分类: chapter_data_structure/classification_of_data_structure.md
- - 3.3. 小结: chapter_data_structure/summary.md
+ - 3. 数据结构与数据:
+ - 3.1. 数据结构分类: chapter_data_structure/classification_of_data_structure.md
+ - 3.2. 基本数据类型: chapter_data_structure/basic_data_types.md
+ - 3.3. 数字编码 *: chapter_data_structure/number_encoding.md
+ - 3.4. 字符编码 *: chapter_data_structure/character_encoding.md
+ - 3.5. 小结: chapter_data_structure/summary.md
- 4. 数组与链表:
- 4.1. 数组: chapter_array_and_linkedlist/array.md
- 4.2. 链表: chapter_array_and_linkedlist/linked_list.md