mirror of
https://github.com/krahets/hello-algo.git
synced 2025-07-25 11:13:38 +08:00
Number the H1 and H2 headings.
This commit is contained in:
@ -2,7 +2,7 @@
|
||||
comments: true
|
||||
---
|
||||
|
||||
# 数组
|
||||
# 4.1. 数组
|
||||
|
||||
「数组 Array」是一种将 **相同类型元素** 存储在 **连续内存空间** 的数据结构,将元素在数组中的位置称为元素的「索引 Index」。
|
||||
|
||||
@ -89,7 +89,7 @@ comments: true
|
||||
let nums = [1, 3, 2, 5, 4]
|
||||
```
|
||||
|
||||
## 数组优点
|
||||
## 4.1.1. 数组优点
|
||||
|
||||
**在数组中访问元素非常高效**。这是因为在数组中,计算元素的内存地址非常容易。给定数组首个元素的地址、和一个元素的索引,利用以下公式可以直接计算得到该元素的内存地址,从而直接访问此元素。
|
||||
|
||||
@ -217,7 +217,7 @@ elementAddr = firtstElementAddr + elementLength * elementIndex
|
||||
}
|
||||
```
|
||||
|
||||
## 数组缺点
|
||||
## 4.1.2. 数组缺点
|
||||
|
||||
**数组在初始化后长度不可变**。由于系统无法保证数组之后的内存空间是可用的,因此数组长度无法扩展。而若希望扩容数组,则需新建一个数组,然后把原数组元素依次拷贝到新数组,在数组很大的情况下,这是非常耗时的。
|
||||
|
||||
@ -551,7 +551,7 @@ elementAddr = firtstElementAddr + elementLength * elementIndex
|
||||
}
|
||||
```
|
||||
|
||||
## 数组常用操作
|
||||
## 4.1.3. 数组常用操作
|
||||
|
||||
**数组遍历**。以下介绍两种常用的遍历方法。
|
||||
|
||||
@ -809,7 +809,7 @@ elementAddr = firtstElementAddr + elementLength * elementIndex
|
||||
}
|
||||
```
|
||||
|
||||
## 数组典型应用
|
||||
## 4.1.4. 数组典型应用
|
||||
|
||||
**随机访问**。如果我们想要随机抽取一些样本,那么可以用数组存储,并生成一个随机序列,根据索引实现样本的随机抽取。
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
comments: true
|
||||
---
|
||||
|
||||
# 链表
|
||||
# 4.2. 链表
|
||||
|
||||
!!! note "引言"
|
||||
|
||||
@ -277,7 +277,7 @@ comments: true
|
||||
n3.next = n4
|
||||
```
|
||||
|
||||
## 链表优点
|
||||
## 4.2.1. 链表优点
|
||||
|
||||
**在链表中,插入与删除结点的操作效率高**。例如,如果想在链表中间的两个结点 `A` , `B` 之间插入一个新结点 `P` ,我们只需要改变两个结点指针即可,时间复杂度为 $O(1)$ ,相比数组的插入操作高效很多。在链表中删除某个结点也很方便,只需要改变一个结点指针即可。
|
||||
|
||||
@ -465,7 +465,7 @@ comments: true
|
||||
}
|
||||
```
|
||||
|
||||
## 链表缺点
|
||||
## 4.2.2. 链表缺点
|
||||
|
||||
**链表访问结点效率低**。上节提到,数组可以在 $O(1)$ 时间下访问任意元素,但链表无法直接访问任意结点。这是因为计算机需要从头结点出发,一个一个地向后遍历到目标结点。例如,倘若想要访问链表索引为 `index` (即第 `index + 1` 个)的结点,那么需要 `index` 次访问操作。
|
||||
|
||||
@ -593,7 +593,7 @@ comments: true
|
||||
|
||||
**链表的内存占用多**。链表以结点为单位,每个结点除了保存值外,还需额外保存指针(引用)。这意味着同样数据量下,链表比数组需要占用更多内存空间。
|
||||
|
||||
## 链表常用操作
|
||||
## 4.2.3. 链表常用操作
|
||||
|
||||
**遍历链表查找**。遍历链表,查找链表内值为 `target` 的结点,输出结点在链表中的索引。
|
||||
|
||||
@ -736,7 +736,7 @@ comments: true
|
||||
}
|
||||
```
|
||||
|
||||
## 常见链表类型
|
||||
## 4.2.4. 常见链表类型
|
||||
|
||||
**单向链表**。即上述介绍的普通链表。单向链表的结点有「值」和指向下一结点的「指针(引用)」两项数据。我们将首个结点称为头结点,尾结点指向 `null` 。
|
||||
|
||||
|
@ -2,13 +2,13 @@
|
||||
comments: true
|
||||
---
|
||||
|
||||
# 列表
|
||||
# 4.3. 列表
|
||||
|
||||
**由于长度不可变,数组的实用性大大降低**。在很多情况下,我们事先并不知道会输入多少数据,这就为数组长度的选择带来了很大困难。长度选小了,需要在添加数据中频繁地扩容数组;长度选大了,又造成内存空间的浪费。
|
||||
|
||||
为了解决此问题,诞生了一种被称为「列表 List」的数据结构。列表可以被理解为长度可变的数组,因此也常被称为「动态数组 Dynamic Array」。列表基于数组实现,继承了数组的优点,同时还可以在程序运行中实时扩容。在列表中,我们可以自由地添加元素,而不用担心超过容量限制。
|
||||
|
||||
## 列表常用操作
|
||||
## 4.3.1. 列表常用操作
|
||||
|
||||
**初始化列表**。我们通常会使用到“无初始值”和“有初始值”的两种初始化方法。
|
||||
|
||||
@ -630,7 +630,7 @@ comments: true
|
||||
list.sort() // 排序后,列表元素从小到大排列
|
||||
```
|
||||
|
||||
## 列表简易实现 *
|
||||
## 4.3.2. 列表简易实现 *
|
||||
|
||||
为了帮助加深对列表的理解,我们在此提供一个列表的简易版本的实现。需要关注三个核心点:
|
||||
|
||||
|
@ -2,14 +2,14 @@
|
||||
comments: true
|
||||
---
|
||||
|
||||
# 小结
|
||||
# 4.4. 小结
|
||||
|
||||
- 数组和链表是两种基本数据结构,代表了数据在计算机内存中的两种存储方式,即连续空间存储和离散空间存储。两者的优点与缺点呈现出此消彼长的关系。
|
||||
- 数组支持随机访问、内存空间占用小;但插入与删除元素效率低,且初始化后长度不可变。
|
||||
- 链表可通过更改指针实现高效的结点插入与删除,并且可以灵活地修改长度;但结点访问效率低、占用内存多。常见的链表类型有单向链表、循环链表、双向链表。
|
||||
- 列表又称动态数组,是基于数组实现的一种数据结构,其保存了数组的优势,且可以灵活改变长度。列表的出现大大提升了数组的实用性,但副作用是会造成部分内存空间浪费。
|
||||
|
||||
## 数组 VS 链表
|
||||
## 4.4.1. 数组 VS 链表
|
||||
|
||||
<p align="center"> Table. 数组与链表特点对比 </p>
|
||||
|
||||
|
Reference in New Issue
Block a user