@ -104,7 +104,10 @@ comments: true
=== "Zig"
```zig title="list.zig"
// 初始化列表
var list = std.ArrayList(i32).init(std.heap.page_allocator);
defer list.deinit();
try list.appendSlice(& [_]i32{ 1, 3, 2, 5, 4 });
```
**访问与更新元素**。列表的底层数据结构是数组,因此可以在 $O(1)$ 时间内访问与更新元素,效率很高。
@ -198,7 +201,11 @@ comments: true
=== "Zig"
```zig title="list.zig"
// 访问元素
var num = list.items[1]; // 访问索引 1 处的元素
// 更新元素
list.items[1] = 0; // 将索引 1 处的元素更新为 0
```
**在列表中添加、插入、删除元素**。相对于数组,列表可以自由地添加与删除元素。在列表尾部添加元素的时间复杂度为 $O(1)$ ,但是插入与删除元素的效率仍与数组一样低,时间复杂度为 $O(N)$ 。
@ -372,7 +379,21 @@ comments: true
=== "Zig"
```zig title="list.zig"
// 清空列表
list.clearRetainingCapacity();
// 尾部添加元素
try list.append(1);
try list.append(3);
try list.append(2);
try list.append(5);
try list.append(4);
// 中间插入元素
try list.insert(3, 6); // 在索引 3 处插入数字 6
// 删除元素
_ = list.orderedRemove(3); // 删除索引 3 处的元素
```
**遍历列表**。与数组一样,列表可以使用索引遍历,也可以使用 `for-each` 直接遍历。
@ -514,7 +535,18 @@ comments: true
=== "Zig"
```zig title="list.zig"
// 通过索引遍历列表
var count: i32 = 0;
var i: i32 = 0;
while (i < list.items.len ) : ( i + = 1 ) {
count += 1;
}
// 直接遍历列表元素
count = 0;
for (list.items) |_| {
count += 1;
}
```
**拼接两个列表**。再创建一个新列表 `list1` ,我们可以将其中一个列表拼接到另一个的尾部。
@ -593,7 +625,11 @@ comments: true
=== "Zig"
```zig title="list.zig"
// 拼接两个列表
var list1 = std.ArrayList(i32).init(std.heap.page_allocator);
defer list1.deinit();
try list1.appendSlice(& [_]i32{ 6, 8, 7, 10, 9 });
try list.insertSlice(list.items.len, list1.items); // 将列表 list1 拼接到 list 之后
```
**排序列表**。排序也是常用的方法之一,完成列表排序后,我们就可以使用在数组类算法题中经常考察的「二分查找」和「双指针」算法了。
@ -663,7 +699,8 @@ comments: true
=== "Zig"
```zig title="list.zig"
// 排序列表
std.sort.sort(i32, list.items, {}, comptime std.sort.asc(i32));
```
## 4.3.2. 列表简易实现 *
@ -1439,5 +1476,120 @@ comments: true
=== "Zig"
```zig title="my_list.zig"
// 列表类简易实现
pub fn MyList(comptime T: type) type {
return struct {
const Self = @This ();
nums: []T = undefined, // 数组(存储列表元素)
numsCapacity: usize = 10, // 列表容量
numSize: usize = 0, // 列表长度(即当前元素数量)
extendRatio: usize = 2, // 每次列表扩容的倍数
mem_arena: ?std.heap.ArenaAllocator = null,
mem_allocator: std.mem.Allocator = undefined, // 内存分配器
// 构造函数(分配内存+初始化列表)
pub fn init(self: *Self, allocator: std.mem.Allocator) !void {
if (self.mem_arena == null) {
self.mem_arena = std.heap.ArenaAllocator.init(allocator);
self.mem_allocator = self.mem_arena.?.allocator();
}
self.nums = try self.mem_allocator.alloc(T, self.numsCapacity);
std.mem.set(T, self.nums, @as (T, 0));
}
// 析构函数(释放内存)
pub fn deinit(self: *Self) void {
if (self.mem_arena == null) return;
self.mem_arena.?.deinit();
}
// 获取列表长度(即当前元素数量)
pub fn size(self: *Self) usize {
return self.numSize;
}
// 获取列表容量
pub fn capacity(self: *Self) usize {
return self.numsCapacity;
}
// 访问元素
pub fn get(self: *Self, index: usize) T {
// 索引如果越界则抛出异常,下同
if (index < 0 or index > = self.size()) @panic ("索引越界");
return self.nums[index];
}
// 更新元素
pub fn set(self: *Self, index: usize, num: T) void {
// 索引如果越界则抛出异常,下同
if (index < 0 or index > = self.size()) @panic ("索引越界");
self.nums[index] = num;
}
// 尾部添加元素
pub fn add(self: *Self, num: T) !void {
// 元素数量超出容量时,触发扩容机制
if (self.size() == self.capacity()) try self.extendCapacity();
self.nums[self.size()] = num;
// 更新元素数量
self.numSize += 1;
}
// 中间插入元素
pub fn insert(self: *Self, index: usize, num: T) !void {
if (index < 0 or index > = self.size()) @panic ("索引越界");
// 元素数量超出容量时,触发扩容机制
if (self.size() == self.capacity()) try self.extendCapacity();
// 索引 i 以及之后的元素都向后移动一位
var j = self.size() - 1;
while (j >= index) : (j -= 1) {
self.nums[j + 1] = self.nums[j];
}
self.nums[index] = num;
// 更新元素数量
self.numSize += 1;
}
// 删除元素
pub fn remove(self: *Self, index: usize) T {
if (index < 0 or index > = self.size()) @panic ("索引越界");
var num = self.nums[index];
// 索引 i 之后的元素都向前移动一位
var j = index;
while (j < self.size ( ) - 1 ) : ( j + = 1 ) {
self.nums[j] = self.nums[j + 1];
}
// 更新元素数量
self.numSize -= 1;
// 返回被删除元素
return num;
}
// 列表扩容
pub fn extendCapacity(self: *Self) !void {
// 新建一个长度为 size * extendRatio 的数组,并将原数组拷贝到新数组
var newCapacity = self.capacity() * self.extendRatio;
var extend = try self.mem_allocator.alloc(T, newCapacity);
std.mem.set(T, extend, @as (T, 0));
// 将原数组中的所有元素复制到新数组
std.mem.copy(T, extend, self.nums);
self.nums = extend;
// 更新列表容量
self.numsCapacity = newCapacity;
}
// 将列表转换为数组
pub fn toArray(self: *Self) ![]T {
// 仅转换有效长度范围内的列表元素
var nums = try self.mem_allocator.alloc(T, self.size());
std.mem.set(T, nums, @as (T, 0));
for (nums) |*num, i| {
num.* = self.get(i);
}
return nums;
}
};
}
```