1 概念
列表 (list) 是一个抽象的数据结构概念,它表示元素的有序集合,支持元素访问、修改、添加、删除和遍历等操作,无须使用者考虑容量限制的问题。列表可以基于链表或数组实现。
- 链表天然可以看作一个列表,其支持元素增删查改操作,并且可以灵活动态扩容。
- 数组也支持元素增删查改,但由于其长度不可变,因此只能看作一个具有长度限制的列表。
当使用数组实现列表时,长度不可变的性质会导致列表的实用性降低。这是因为我们通常无法事先确定需要存储多少数据,从而难以选择合适的列表长度。若长度过小,则很可能无法满足使用需求;若长度过大,则会造成内存空间浪费。
为解决此问题,我们可以使用动态数组 (dynamic array) 来实现列表。它继承了数组的各项优点,并且可以在程序运行过程中进行动态扩容。
实际上,许多编程语言中的标准库提供的列表是基于动态数组实现的,例如 Python 中的 list 、 Java 中的 ArrayList 、 C++ 中的 vector 和 C# 中的 List 等。在接下来的讨论中,我们将把“列表”和“动态数组”视为等同的概念。
2 列表常用操作
2.1 初始化列表
我们通常使用“无初始值”和“有初始值”这两种初始化方法:
1 2 3 4 5
|
nums1: list[int] = []
nums: list[int] = [1, 3, 2, 5, 4]
|
2.2 访问元素
列表本质上是数组,因此可以在 O(1) 时间内访问和更新元素,效率很高。
1 2 3 4 5
| num: int = nums[1]
nums[1] = 0
|
2.3 插入与删除元素
相较于数组,列表可以自由地添加与删除元素。在列表尾部添加元素地时间复杂度为 O(1) ,但插入和删除元素地效率仍然与数组相同,时间复杂度为 O(n) 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| nums.clear()
nums.append(1) nums.append(3) nums.append(2) nums.append(5) nums.append(4)
nums.insert(3, 6)
nums.pop(3)
|
2.4 遍历列表
与数组一样,列表可以根据索引遍历,也可以直接遍历各元素。
1 2 3 4 5 6 7 8
| count = 0 for i in range(len(nums)): count += nums[i]
for num in nums: count += num
|
2.5 拼接列表
给定一个新列表 nums1 ,我们可以将其拼接到原列表地尾部。
1 2 3
| nums1: list[int] = [6, 8, 7, 10, 9] nums += nums1
|
2.6 排序列表
完成列表排序后,我们便可以使用在数组类算法题中经常考查的“二分查找”和“双指针”算法。
3 列表实现
许多编程语言内置了列表,例如 Java 、 C++ 、 Python 等。它们的实现比较复杂,各个参数的设定也非常考究,例如初始容量、扩容倍数等。感兴趣者可查阅源码进行学习。
为了加深对列表工作原理的理解,我们尝试实现一个简易版列表,包括以下三个重点设计。
- 初始容量: 选取一个合理的数组初始容量。在本示例中,我们选择 10 作为初始容量。
- 数量记录: 声明一个变量
size ,用于记录列表当前元素容量,并随着元素插入和删除实时更新。根据此变量,我们可以定位列表尾部,以及判断是否需要扩容。
- 扩容机制: 若插入元素时列表容量已满,则需要进行扩容。先根据扩容倍数创建一个更大的数组,再将当前数组的所有元素依次移动至新数组。在本示例中,我们规定每次将数组扩容至之前的 2 倍。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
| typedef struct { int *arr; int capacity; int size; int extendRatio; } MyList;
MyList *newMyList() { MyList *nums = malloc(sizeof(MyList)); nums->capacity = 10; nums->arr = malloc(sizeof(int) * nums->capacity); nums->size = 0; nums->extendRatio = 2; return nums; }
void delMyList(MyList *nums) { free(nums->arr); free(nums); }
int size(MyList *nums) { return nums->size; }
int capacity(MyList *nums) { return nums->capacity; }
int get(MyList *nums, int index) { assert(index >= 0 && index < nums->size); return nums->arr[index]; }
void set(MyList *nums, int index, int num) { assert(index >= 0 && index < nums->size); nums->arr[index] = num; }
void add(MyList *nums, int num) { if (size(nums) == capacity(nums)) { extendCapacity(nums); } nums->arr[size(nums)] = num; nums->size++; }
void insert(MyList *nums, int index, int num) { assert(index >= 0 && index < size(nums)); if (size(nums) == capacity(nums)) { extendCapacity(nums); } for (int i = size(nums); i > index; --i) { nums->arr[i] = nums->arr[i - 1]; } nums->arr[index] = num; nums->size++; }
int removeItem(MyList *nums, int index) { assert(index >= 0 && index < size(nums)); int num = nums->arr[index]; for (int i = index; i < size(nums) - 1; i++) { nums->arr[i] = nums->arr[i + 1]; } nums->size--; return num; }
void extendCapacity(MyList *nums) { int newCapacity = capacity(nums) * nums->extendRatio; int *extend = (int *)malloc(sizeof(int) * newCapacity); int *temp = nums->arr;
for (int i = 0; i < size(nums); i++) extend[i] = nums->arr[i];
free(temp);
nums->arr = extend; nums->capacity = newCapacity; }
int *toArray(MyList *nums) { return nums->arr; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
| class MyList: """列表类"""
def __init__(self): """构造方法""" self._capacity: int = 10 self._arr: list[int] = [0] * self._capacity self._size: int = 0 self._extend_ratio: int = 2
def size(self) -> int: """获取列表长度(当前元素数量)""" return self._size
def capacity(self) -> int: """获取列表容量""" return self._capacity
def get(self, index: int) -> int: """访问元素""" if index < 0 or index >= self._size: raise IndexError("索引越界") return self._arr[index]
def set(self, num: int, index: int): """更新元素""" if index < 0 or index >= self._size: raise IndexError("索引越界") self._arr[index] = num
def add(self, num: int): """在尾部添加元素""" if self.size() == self.capacity(): self.extend_capacity() self._arr[self._size] = num self._size += 1
def insert(self, num: int, index: int): """在中间插入元素""" if index < 0 or index >= self._size: raise IndexError("索引越界") if self._size == self.capacity(): self.extend_capacity() for j in range(self._size - 1, index - 1, -1): self._arr[j + 1] = self._arr[j] self._arr[index] = num self._size += 1
def remove(self, index: int) -> int: """删除元素""" if index < 0 or index >= self._size: raise IndexError("索引越界") num = self._arr[index] for j in range(index, self._size - 1): self._arr[j] = self._arr[j + 1] self._size -= 1 return num
def extend_capacity(self): """列表扩容""" self._arr = self._arr + [0] * self.capacity() * (self._extend_ratio - 1) self._capacity = len(self._arr)
def to_array(self) -> list[int]: """返回有效长度的列表""" return self._arr[: self._size]
|