mirror of
https://github.com/krahets/hello-algo.git
synced 2025-11-02 21:24:53 +08:00
Update the book based on the revised second edition (#1014)
* Revised the book * Update the book with the second revised edition * Revise base on the manuscript of the first edition
This commit is contained in:
@ -13,8 +13,8 @@
|
||||
|
||||
基本数据类型的取值范围取决于其占用的空间大小。下面以 Java 为例。
|
||||
|
||||
- 整数类型 `byte` 占用 $1$ byte = $8$ bits ,可以表示 $2^{8}$ 个数字。
|
||||
- 整数类型 `int` 占用 $4$ bytes = $32$ bits ,可以表示 $2^{32}$ 个数字。
|
||||
- 整数类型 `byte` 占用 $1$ 字节 = $8$ 比特 ,可以表示 $2^{8}$ 个数字。
|
||||
- 整数类型 `int` 占用 $4$ 字节 = $32$ 比特 ,可以表示 $2^{32}$ 个数字。
|
||||
|
||||
下表列举了 Java 中各种基本数据类型的占用空间、取值范围和默认值。此表格无须死记硬背,大致理解即可,需要时可以通过查表来回忆。
|
||||
|
||||
@ -22,25 +22,25 @@
|
||||
|
||||
| 类型 | 符号 | 占用空间 | 最小值 | 最大值 | 默认值 |
|
||||
| ------ | -------- | -------- | ------------------------ | ----------------------- | -------------- |
|
||||
| 整数 | `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\text{f}$ |
|
||||
| | `double` | 8 bytes | $2.225 \times 10^{-308}$ | $1.798 \times 10^{308}$ | $0.0$ |
|
||||
| 字符 | `char` | 2 bytes | $0$ | $2^{16} - 1$ | $0$ |
|
||||
| 布尔 | `bool` | 1 byte | $\text{false}$ | $\text{true}$ | $\text{false}$ |
|
||||
| 整数 | `byte` | 1 字节 | $-2^7$ ($-128$) | $2^7 - 1$ ($127$) | $0$ |
|
||||
| | `short` | 2 字节 | $-2^{15}$ | $2^{15} - 1$ | $0$ |
|
||||
| | `int` | 4 字节 | $-2^{31}$ | $2^{31} - 1$ | $0$ |
|
||||
| | `long` | 8 字节 | $-2^{63}$ | $2^{63} - 1$ | $0$ |
|
||||
| 浮点数 | `float` | 4 字节 | $1.175 \times 10^{-38}$ | $3.403 \times 10^{38}$ | $0.0\text{f}$ |
|
||||
| | `double` | 8 字节 | $2.225 \times 10^{-308}$ | $1.798 \times 10^{308}$ | $0.0$ |
|
||||
| 字符 | `char` | 2 字节 | $0$ | $2^{16} - 1$ | $0$ |
|
||||
| 布尔 | `bool` | 1 字节 | $\text{false}$ | $\text{true}$ | $\text{false}$ |
|
||||
|
||||
请注意,上表针对的是 Java 的基本数据类型的情况。每种编程语言都有各自的数据类型定义,它们的占用空间、取值范围和默认值可能会有所不同。
|
||||
|
||||
- 在 Python 中,整数类型 `int` 可以是任意大小,只受限于可用内存;浮点数 `float` 是双精度 64 位;没有 `char` 类型,单个字符实际上是长度为 1 的字符串 `str` 。
|
||||
- C 和 C++ 未明确规定基本数据类型大小,而因实现和平台各异。上表遵循 LP64 [数据模型](https://en.cppreference.com/w/cpp/language/types#Properties),其用于包括 Linux 和 macOS 在内的 Unix 64 位操作系统。
|
||||
- C 和 C++ 未明确规定基本数据类型的大小,而因实现和平台各异。上表遵循 LP64 [数据模型](https://en.cppreference.com/w/cpp/language/types#Properties),其用于包括 Linux 和 macOS 在内的 Unix 64 位操作系统。
|
||||
- 字符 `char` 的大小在 C 和 C++ 中为 1 字节,在大多数编程语言中取决于特定的字符编码方法,详见“字符编码”章节。
|
||||
- 即使表示布尔量仅需 1 位($0$ 或 $1$),它在内存中通常存储为 1 字节。这是因为现代计算机 CPU 通常将 1 字节作为最小寻址内存单元。
|
||||
- 即使表示布尔量仅需 1 位($0$ 或 $1$),它在内存中通常也存储为 1 字节。这是因为现代计算机 CPU 通常将 1 字节作为最小寻址内存单元。
|
||||
|
||||
那么,基本数据类型与数据结构之间有什么联系呢?我们知道,数据结构是在计算机中组织与存储数据的方式。这句话的主语是“结构”而非“数据”。
|
||||
|
||||
如果想表示“一排数字”,我们自然会想到使用数组。这是因为数组的线性结构可以表示数字的相邻关系和顺序关系,但至于存储的内容是整数 `int`、小数 `float` 或是字符 `char` ,则与“数据结构”无关。
|
||||
如果想表示“一排数字”,我们自然会想到使用数组。这是因为数组的线性结构可以表示数字的相邻关系和顺序关系,但至于存储的内容是整数 `int`、小数 `float` 还是字符 `char` ,则与“数据结构”无关。
|
||||
|
||||
换句话说,**基本数据类型提供了数据的“内容类型”,而数据结构提供了数据的“组织方式”**。例如以下代码,我们用相同的数据结构(数组)来存储与表示不同的基本数据类型,包括 `int`、`float`、`char`、`bool` 等。
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
|
||||

|
||||
|
||||
然而,**ASCII 码仅能够表示英文**。随着计算机的全球化,诞生了一种能够表示更多语言的字符集「EASCII」。它在 ASCII 的 7 位基础上扩展到 8 位,能够表示 256 个不同的字符。
|
||||
然而,**ASCII 码仅能够表示英文**。随着计算机的全球化,诞生了一种能够表示更多语言的「EASCII」字符集。它在 ASCII 的 7 位基础上扩展到 8 位,能够表示 256 个不同的字符。
|
||||
|
||||
在世界范围内,陆续出现了一批适用于不同地区的 EASCII 字符集。这些字符集的前 128 个字符统一为 ASCII 码,后 128 个字符定义不同,以适应不同语言的需求。
|
||||
|
||||
@ -64,7 +64,7 @@ UTF-8 的编码规则并不复杂,分为以下两种情况。
|
||||
|
||||
## 编程语言的字符编码
|
||||
|
||||
对于以往的大多数编程语言,程序运行中的字符串都采用 UTF-16 或 UTF-32 这类等长的编码。在等长编码下,我们可以将字符串看作数组来处理,这种做法具有以下优点。
|
||||
对于以往的大多数编程语言,程序运行中的字符串都采用 UTF-16 或 UTF-32 这类等长编码。在等长编码下,我们可以将字符串看作数组来处理,这种做法具有以下优点。
|
||||
|
||||
- **随机访问**:UTF-16 编码的字符串可以很容易地进行随机访问。UTF-8 是一种变长编码,要想找到第 $i$ 个字符,我们需要从字符串的开始处遍历到第 $i$ 个字符,这需要 $O(n)$ 的时间。
|
||||
- **字符计数**:与随机访问类似,计算 UTF-16 编码的字符串的长度也是 $O(1)$ 的操作。但是,计算 UTF-8 编码的字符串的长度需要遍历整个字符串。
|
||||
|
||||
@ -42,7 +42,7 @@
|
||||
- **基于数组可实现**:栈、队列、哈希表、树、堆、图、矩阵、张量(维度 $\geq 3$ 的数组)等。
|
||||
- **基于链表可实现**:栈、队列、哈希表、树、堆、图等。
|
||||
|
||||
基于数组实现的数据结构也称“静态数据结构”,这意味着此类数据结构在初始化后长度不可变。相对应地,基于链表实现的数据结构称“动态数据结构”,这类数据结构在初始化后,仍可以在程序运行过程中对其长度进行调整。
|
||||
基于数组实现的数据结构也称“静态数据结构”,这意味着此类数据结构在初始化后长度不可变。相对应地,基于链表实现的数据结构也称“动态数据结构”,这类数据结构在初始化后,仍可以在程序运行过程中对其长度进行调整。
|
||||
|
||||
!!! tip
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
|
||||
在本书中,标题带有 * 符号的是选读章节。如果你时间有限或感到理解困难,可以先跳过,等学完必读章节后再单独攻克。
|
||||
|
||||
## 整数编码
|
||||
## 原码、反码和补码
|
||||
|
||||
在上一节的表格中我们发现,所有整数类型能够表示的负数都比正数多一个,例如 `byte` 的取值范围是 $[-128, 127]$ 。这个现象比较反直觉,它的内在原因涉及原码、反码、补码的相关知识。
|
||||
|
||||
@ -88,9 +88,9 @@ $$
|
||||
|
||||
## 浮点数编码
|
||||
|
||||
细心的你可能会发现:`int` 和 `float` 长度相同,都是 4 bytes ,但为什么 `float` 的取值范围远大于 `int` ?这非常反直觉,因为按理说 `float` 需要表示小数,取值范围应该变小才对。
|
||||
细心的你可能会发现:`int` 和 `float` 长度相同,都是 4 字节 ,但为什么 `float` 的取值范围远大于 `int` ?这非常反直觉,因为按理说 `float` 需要表示小数,取值范围应该变小才对。
|
||||
|
||||
实际上,**这是因为浮点数 `float` 采用了不同的表示方式**。记一个 32-bit 长度的二进制数为:
|
||||
实际上,**这是因为浮点数 `float` 采用了不同的表示方式**。记一个 32 位长度的二进制数为:
|
||||
|
||||
$$
|
||||
b_{31} b_{30} b_{29} \ldots b_2 b_1 b_0
|
||||
@ -98,9 +98,9 @@ $$
|
||||
|
||||
根据 IEEE 754 标准,32-bit 长度的 `float` 由以下三个部分构成。
|
||||
|
||||
- 符号位 $\mathrm{S}$ :占 1 bit ,对应 $b_{31}$ 。
|
||||
- 指数位 $\mathrm{E}$ :占 8 bits ,对应 $b_{30} b_{29} \ldots b_{23}$ 。
|
||||
- 分数位 $\mathrm{N}$ :占 23 bits ,对应 $b_{22} b_{21} \ldots b_0$ 。
|
||||
- 符号位 $\mathrm{S}$ :占 1 位 ,对应 $b_{31}$ 。
|
||||
- 指数位 $\mathrm{E}$ :占 8 位 ,对应 $b_{30} b_{29} \ldots b_{23}$ 。
|
||||
- 分数位 $\mathrm{N}$ :占 23 位 ,对应 $b_{22} b_{21} \ldots b_0$ 。
|
||||
|
||||
二进制数 `float` 对应值的计算方法为:
|
||||
|
||||
|
||||
@ -10,8 +10,8 @@
|
||||
- 原码、反码和补码是在计算机中编码数字的三种方法,它们之间可以相互转换。整数的原码的最高位是符号位,其余位是数字的值。
|
||||
- 整数在计算机中是以补码的形式存储的。在补码表示下,计算机可以对正数和负数的加法一视同仁,不需要为减法操作单独设计特殊的硬件电路,并且不存在正负零歧义的问题。
|
||||
- 浮点数的编码由 1 位符号位、8 位指数位和 23 位分数位构成。由于存在指数位,因此浮点数的取值范围远大于整数,代价是牺牲了精度。
|
||||
- ASCII 码是最早出现的英文字符集,长度为 1 字节,共收录 127 个字符。GBK 字符集是常用的中文字符集,共收录两万多个汉字。Unicode 致力于提供一个完整的字符集标准,收录世界内各种语言的字符,从而解决由于字符编码方法不一致而导致的乱码问题。
|
||||
- UTF-8 是最受欢迎的 Unicode 编码方法,通用性非常好。它是一种变长的编码方法,具有很好的扩展性,有效提升了存储空间的使用效率。UTF-16 和 UTF-32 是等长的编码方法。在编码中文时,UTF-16 比 UTF-8 的占用空间更小。Java 和 C# 等编程语言默认使用 UTF-16 编码。
|
||||
- ASCII 码是最早出现的英文字符集,长度为 1 字节,共收录 127 个字符。GBK 字符集是常用的中文字符集,共收录两万多个汉字。Unicode 致力于提供一个完整的字符集标准,收录世界上各种语言的字符,从而解决由于字符编码方法不一致而导致的乱码问题。
|
||||
- UTF-8 是最受欢迎的 Unicode 编码方法,通用性非常好。它是一种变长的编码方法,具有很好的扩展性,有效提升了存储空间的使用效率。UTF-16 和 UTF-32 是等长的编码方法。在编码中文时,UTF-16 占用的空间比 UTF-8 更小。Java 和 C# 等编程语言默认使用 UTF-16 编码。
|
||||
|
||||
### Q & A
|
||||
|
||||
@ -20,14 +20,14 @@
|
||||
哈希表底层是数组,而为了解决哈希冲突,我们可能会使用“链式地址”(后续“哈希冲突”章节会讲):数组中每个桶指向一个链表,当链表长度超过一定阈值时,又可能被转化为树(通常为红黑树)。
|
||||
从存储的角度来看,哈希表的底层是数组,其中每一个桶槽位可能包含一个值,也可能包含一个链表或一棵树。因此,哈希表可能同时包含线性数据结构(数组、链表)和非线性数据结构(树)。
|
||||
|
||||
!!! question "`char` 类型的长度是 1 byte 吗?"
|
||||
!!! question "`char` 类型的长度是 1 字节吗?"
|
||||
|
||||
`char` 类型的长度由编程语言采用的编码方法决定。例如,Java、JavaScript、TypeScript、C# 都采用 UTF-16 编码(保存 Unicode 码点),因此 char 类型的长度为 2 bytes。
|
||||
`char` 类型的长度由编程语言采用的编码方法决定。例如,Java、JavaScript、TypeScript、C# 都采用 UTF-16 编码(保存 Unicode 码点),因此 `char` 类型的长度为 2 字节。
|
||||
|
||||
!!! question "基于数组实现的数据结构也称“静态数据结构” 是否有歧义?因为栈也可以进行出栈和入栈等操作,这些操作都是“动态”的。"
|
||||
!!! question "基于数组实现的数据结构也称“静态数据结构” 是否有歧义?栈也可以进行出栈和入栈等操作,这些操作都是“动态”的。"
|
||||
|
||||
栈确实可以实现动态的数据操作,但数据结构仍然是“静态”(长度不可变)的。尽管基于数组的数据结构可以动态地添加或删除元素,但它们的容量是固定的。如果数据量超出了预分配的大小,就需要创建一个新的更大的数组,并将旧数组的内容复制到新数组中。
|
||||
|
||||
!!! question "在构建栈(队列)的时候,未指定它的大小,为什么它们是“静态数据结构”呢?"
|
||||
|
||||
在高级编程语言中,我们无须人工指定栈(队列)的初始容量,这个工作由类内部自动完成。例如,Java 的 ArrayList 的初始容量通常为 10。另外,扩容操作也是自动实现的。详见后续的“列表”章节。
|
||||
在高级编程语言中,我们无须人工指定栈(队列)的初始容量,这个工作由类内部自动完成。例如,Java 的 `ArrayList` 的初始容量通常为 10。另外,扩容操作也是自动实现的。详见后续的“列表”章节。
|
||||
|
||||
Reference in New Issue
Block a user