Add Zig code blocks.

This commit is contained in:
Yudong Jin
2023-02-01 22:03:04 +08:00
parent 6cd6d5589e
commit 7ce7386bab
25 changed files with 599 additions and 0 deletions
@@ -89,6 +89,12 @@ comments: true
let nums = [1, 3, 2, 5, 4]
```
=== "Zig"
```zig title="array.zig"
```
## 4.1.1. 数组优点
**在数组中访问元素非常高效**。这是因为在数组中,计算元素的内存地址非常容易。给定数组首个元素的地址、和一个元素的索引,利用以下公式可以直接计算得到该元素的内存地址,从而直接访问此元素。
@@ -217,6 +223,12 @@ elementAddr = firtstElementAddr + elementLength * elementIndex
}
```
=== "Zig"
```zig title="array.zig"
```
## 4.1.2. 数组缺点
**数组在初始化后长度不可变**。由于系统无法保证数组之后的内存空间是可用的,因此数组长度无法扩展。而若希望扩容数组,则需新建一个数组,然后把原数组元素依次拷贝到新数组,在数组很大的情况下,这是非常耗时的。
@@ -359,6 +371,12 @@ elementAddr = firtstElementAddr + elementLength * elementIndex
}
```
=== "Zig"
```zig title="array.zig"
```
**数组中插入或删除元素效率低下**。假设我们想要在数组中间某位置插入一个元素,由于数组元素在内存中是“紧挨着的”,它们之间没有空间再放任何数据。因此,我们不得不将此索引之后的所有元素都向后移动一位,然后再把元素赋值给该索引。删除元素也是类似,需要把此索引之后的元素都向前移动一位。总体看有以下缺点:
- **时间复杂度高**:数组的插入和删除的平均时间复杂度均为 $O(N)$ ,其中 $N$ 为数组长度。
@@ -551,6 +569,12 @@ elementAddr = firtstElementAddr + elementLength * elementIndex
}
```
=== "Zig"
```zig title="array.zig"
```
## 4.1.3. 数组常用操作
**数组遍历**。以下介绍两种常用的遍历方法。
@@ -693,6 +717,12 @@ elementAddr = firtstElementAddr + elementLength * elementIndex
}
```
=== "Zig"
```zig title="array.zig"
```
**数组查找**。通过遍历数组,查找数组内的指定元素,并输出对应索引。
=== "Java"
@@ -809,6 +839,12 @@ elementAddr = firtstElementAddr + elementLength * elementIndex
}
```
=== "Zig"
```zig title="array.zig"
```
## 4.1.4. 数组典型应用
**随机访问**。如果我们想要随机抽取一些样本,那么可以用数组存储,并生成一个随机序列,根据索引实现样本的随机抽取。
@@ -126,6 +126,12 @@ comments: true
}
```
=== "Zig"
```zig title=""
```
**尾结点指向什么?** 我们一般将链表的最后一个结点称为「尾结点」,其指向的是「空」,在 Java / C++ / Python 中分别记为 `null` / `nullptr` / `None` 。在不引起歧义下,本书都使用 `null` 来表示空。
**链表初始化方法**。建立链表分为两步,第一步是初始化各个结点对象,第二步是构建引用指向关系。完成后,即可以从链表的首个结点(即头结点)出发,访问其余所有的结点。
@@ -277,6 +283,12 @@ comments: true
n3.next = n4
```
=== "Zig"
```zig title="linked_list.zig"
```
## 4.2.1. 链表优点
**在链表中,插入与删除结点的操作效率高**。例如,如果想在链表中间的两个结点 `A` , `B` 之间插入一个新结点 `P` ,我们只需要改变两个结点指针即可,时间复杂度为 $O(1)$ ,相比数组的插入操作高效很多。在链表中删除某个结点也很方便,只需要改变一个结点指针即可。
@@ -465,6 +477,12 @@ comments: true
}
```
=== "Zig"
```zig title="linked_list.zig"
```
## 4.2.2. 链表缺点
**链表访问结点效率低**。上节提到,数组可以在 $O(1)$ 时间下访问任意元素,但链表无法直接访问任意结点。这是因为计算机需要从头结点出发,一个一个地向后遍历到目标结点。例如,倘若想要访问链表索引为 `index` (即第 `index + 1` 个)的结点,那么需要 `index` 次访问操作。
@@ -591,6 +609,12 @@ comments: true
}
```
=== "Zig"
```zig title="linked_list.zig"
```
**链表的内存占用多**。链表以结点为单位,每个结点除了保存值外,还需额外保存指针(引用)。这意味着同样数据量下,链表比数组需要占用更多内存空间。
## 4.2.3. 链表常用操作
@@ -736,6 +760,12 @@ comments: true
}
```
=== "Zig"
```zig title="linked_list.zig"
```
## 4.2.4. 常见链表类型
**单向链表**。即上述介绍的普通链表。单向链表的结点有「值」和指向下一结点的「指针(引用)」两项数据。我们将首个结点称为头结点,尾结点指向 `null` 。
@@ -864,6 +894,12 @@ comments: true
}
```
=== "Zig"
```zig title=""
```
![linkedlist_common_types](linked_list.assets/linkedlist_common_types.png)
<p align="center"> Fig. 常见链表类型 </p>
+42
View File
@@ -101,6 +101,12 @@ comments: true
var list = [1, 3, 2, 5, 4]
```
=== "Zig"
```zig title="list.zig"
```
**访问与更新元素**。列表的底层数据结构是数组,因此可以在 $O(1)$ 时间内访问与更新元素,效率很高。
=== "Java"
@@ -189,6 +195,12 @@ comments: true
list[1] = 0 // 将索引 1 处的元素更新为 0
```
=== "Zig"
```zig title="list.zig"
```
**在列表中添加、插入、删除元素**。相对于数组,列表可以自由地添加与删除元素。在列表尾部添加元素的时间复杂度为 $O(1)$ ,但是插入与删除元素的效率仍与数组一样低,时间复杂度为 $O(N)$ 。
=== "Java"
@@ -357,6 +369,12 @@ comments: true
list.remove(at: 3) // 删除索引 3 处的元素
```
=== "Zig"
```zig title="list.zig"
```
**遍历列表**。与数组一样,列表可以使用索引遍历,也可以使用 `for-each` 直接遍历。
=== "Java"
@@ -493,6 +511,12 @@ comments: true
}
```
=== "Zig"
```zig title="list.zig"
```
**拼接两个列表**。再创建一个新列表 `list1` ,我们可以将其中一个列表拼接到另一个的尾部。
=== "Java"
@@ -566,6 +590,12 @@ comments: true
list.append(contentsOf: list1) // 将列表 list1 拼接到 list 之后
```
=== "Zig"
```zig title="list.zig"
```
**排序列表**。排序也是常用的方法之一,完成列表排序后,我们就可以使用在数组类算法题中经常考察的「二分查找」和「双指针」算法了。
=== "Java"
@@ -630,6 +660,12 @@ comments: true
list.sort() // 排序后,列表元素从小到大排列
```
=== "Zig"
```zig title="list.zig"
```
## 4.3.2. 列表简易实现 *
为了帮助加深对列表的理解,我们在此提供一个列表的简易版本的实现。需要关注三个核心点:
@@ -1399,3 +1435,9 @@ comments: true
}
}
```
=== "Zig"
```zig title="my_list.zig"
```