Python和C++数据结构整理
Python和C++数据结构整理
引言
数据结构是软件开发的基石,但许多开发者对数据结构的分类和选型仍存在认知盲区。本文将系统梳理数据结构的逻辑分类,深入剖析C++和Python中常用数据结构的底层实现与应用场景,通过实际代码示例帮助开发者建立完整的知识体系。
第一部分:数据结构的逻辑分类
逻辑结构的本质
数据结构的分类应当从逻辑结构角度理解,而非物理存储方式。逻辑结构描述数据元素之间的关系,这种关系独立于具体编程语言和底层实现。从逻辑层面看,数据结构分为线性结构和非线性结构两大类。
线性结构
线性结构指数据元素之间存在一对一的顺序关系。除首尾元素外,每个元素都有唯一的前驱和后继。典型的线性结构包括数组、链表、栈、队列和哈希表。哈希表虽然在物理存储上通过散列函数分散存储元素,但从逻辑关系看,每个键值对是独立的一一对应关系,因此属于线性结构。
非线性结构
非线性结构表现为数据元素之间存在一对多或多对多的关系。树结构是典型的一对多关系,每个父节点可以拥有多个子节点,形成层次化结构。图结构代表多对多关系,图中节点可以与任意数量的其他节点建立连接,这种灵活性使得图成为表达复杂关系网络的理想选择。
第二部分:核心数据结构详解
Python的四大内置结构
1. list:动态数组
底层实现:Python的list是动态数组实现,基于动态数组(Dynamic Array)实现,使用连续内存空间存储元素引用。在底层,Python维护一个数组指针和当前大小,当容量不足时自动分配更大内存空间并复制现有元素,采用过度分配(Over-Allocation)机制减少频繁的内存重新分配。
特性:
- 有序、可变、允许重复元素
- O(1)时间复杂度的索引访问
- O(1)均摊时间复杂度的末尾追加操作
- O(n)时间复杂度的中间插入/删除操作
基本操作:
lst = [1, 2, 3]
# 末尾添加
lst.append(4) # [1, 2, 3, 4] | O(1)均摊
# 指定位置插入
lst.insert(0, 0) # [0, 1, 2, 3, 4] | O(n)
# 索引访问
element = lst[2] # 3 | O(1)
# 末尾删除
lst.pop() # 4 | O(1)
# 指定位置删除
lst.pop(0) # 0 | O(n)
# 切片操作
sub_list = lst[1:3] # [1, 2] | 创建新列表
lst[1:3] = [10, 20] # [0, 10, 20, 4] | 替换子序列
# 列表推导式
squares = [x**2 for x in range(10)] # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
# 遍历
for item in lst:
print(item)
高级操作:
# 合并列表
combined = lst + [5, 6] # [0, 10, 20, 4, 5, 6]
# 复制列表
copy = lst[:] # [0, 10, 20, 4]
# 排序
lst.sort() # [0, 4, 10, 20] | 原地排序
sorted_lst = sorted(lst) # [0, 4, 10, 20] | 返回新列表
# 去重
unique = list(set(lst)) # [0, 4, 10, 20]
最佳实践:
- 频繁在末尾添加元素时优先使用
append - 频繁在头部插入/删除时考虑使用
collections.deque - 避免在循环中频繁修改列表(如
insert(0, x))
2. dict:哈希表
底层实现:Python的dict基于哈希表实现,采用开放地址法(Open Addressing)解决哈希冲突。自Python 3.7起,dict保证维护插入顺序,这使得字典在大多数情况下是有序的。
特性:
- 键值对集合,键唯一
- O(1)平均时间复杂度的查找、插入和删除
- 键必须是可哈希的不可变对象(字符串、数字、元组等)
- 值可以是任意类型
基本操作:
d = {'name': 'Alice', 'age': 30}
# 插入/更新
d['city'] = 'Beijing' # O(1)
# 安全访问
value = d.get('name') # 'Alice' | O(1)
value = d.get('gender', 'N/A') # 'N/A' | 提供默认值
# 删除
del d['age'] # O(1)
value = d.pop('city') # 'Beijing' | 返回值
# 检查键存在
if 'name' in d:
print(d['name']) # 'Alice'
# 遍历
for key, value in d.items():
print(f"{key}: {value}")
# 字典推导式
squared = {x: x**2 for x in range(5)} # {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
# 合并字典 (Python 3.9+)
d1 = {'a': 1, 'b': 2}
d2 = {'b': 3, 'c': 4}
merged = d1 | d2 # {'a': 1, 'b': 3, 'c': 4}
高级操作:
# 获取所有键/值
keys = list(d.keys()) # ['name', 'age']
values = list(d.values()) # ['Alice', 30]
# 从键值对创建字典
d = dict([('name', 'Alice'), ('age', 30)])
# 初始化默认值
from collections import defaultdict
d = defaultdict(int)
d['a'] += 1 # 1
最佳实践:
- 避免在循环中频繁修改字典
- 使用
get()代替直接访问避免KeyError - 使用字典推导式简化数据转换
- 对于大量数据,考虑使用
collections.Counter统计频率
3. set:集合
底层实现:Python的set基于哈希表实现,用于存储唯一元素,提供O(1)的成员检查、添加和删除操作。
特性:
- 无序、不重复元素集合
- O(1)平均时间复杂度的成员检查、添加和删除
- 适用于快速判断元素是否存在、去重和集合运算
基本操作:
s = {1, 2, 3}
# 添加元素
s.add(4) # {1, 2, 3, 4} | O(1)
# 删除元素
s.remove(2) # {1, 3, 4} | 不存在会报错
s.discard(2) # {1, 3, 4} | 不存在不报错
# 成员检查
exists = 3 in s # True | O(1)
# 集合运算
s1 = {1, 2, 3, 4}
s2 = {3, 4, 5, 6}
union = s1 | s2 # {1, 2, 3, 4, 5, 6} | 并集
intersection = s1 & s2 # {3, 4} | 交集
difference = s1 - s2 # {1, 2} | 差集
sym_diff = s1 ^ s2 # {1, 2, 5, 6} | 对称差
# 去重
lst = [1, 2, 2, 3, 3, 3]
unique = list(set(lst)) # [1, 2, 3]
# frozenset (不可变集合)
fs = frozenset([1, 2, 3])
# fs可以作为dict的键或另一个set的元素
最佳实践:
- 使用set进行快速成员检查
- 用set进行数据去重
- 用集合运算进行数据处理
- 当需要不可变集合时使用frozenset
4. tuple:不可变序列
底层实现:tuple是Python的不可变序列类型,创建后无法修改。底层内存布局比list更紧凑,创建速度更快。
特性:
- 不可变序列
- 可以作为dict的键
- 多线程环境中天然线程安全
- 适合表示固定的数据组合
基本操作:
# 创建tuple
t = (1, 2, 3)
t = 1, 2, 3 # 括号可省略
single = (1,) # 单元素tuple需要逗号
empty = ()
# 访问元素
first = t[0] # 1 | O(1) 索引访问
sub = t[1:3] # (2, 3) | 切片返回新tuple
# 解包
a, b, c = t
x, *rest = (1, 2, 3, 4) # x=1, rest=[2,3,4]
# 作为字典的键
point_data = {(0, 0): 'origin', (1, 1): 'diagonal'}
# 函数返回多值
def get_coordinates():
return 10, 20
x, y = get_coordinates()
# 命名元组 (更好的可读性)
from collections import namedtuple
Point = namedtuple('Point', ['x', 'y'])
p = Point(10, 20)
print(p.x, p.y) # 10 20
最佳实践:
- 用tuple表示固定数据组合(如坐标、日期)
- 用tuple作为dict的键
- 用tuple返回多值函数结果
- 用命名元组提高代码可读性
C++的核心容器体系
1. vector:动态数组
底层实现:vector在连续内存空间中存储元素,提供O(1)的随机访问和O(1)均摊的末尾插入。采用容量翻倍的扩容策略以减少重新分配的频率。
特性:
- 连续内存,随机访问O(1)
- 末尾插入/删除O(1)均摊
- 中间插入/删除O(n)
- 有容量管理方法
基本操作:
#include <vector>
#include <iostream>
// 基本操作
std::vector<int> vec = {1, 2, 3};
vec.push_back(4); // O(1) 末尾添加
vec.pop_back(); // O(1) 末尾删除
int elem = vec[0]; // O(1) 访问,无边界检查
int safe = vec.at(0); // O(1) 访问,有边界检查
// 插入删除
vec.insert(vec.begin(), 0); // O(n) 头部插入
vec.erase(vec.begin()); // O(n) 头部删除
// 容量管理
vec.reserve(1000); // 预分配容量
vec.shrink_to_fit(); // 释放多余容量
size_t sz = vec.size(); // 当前元素数量
size_t cap = vec.capacity(); // 当前容量
// 遍历
for (int x : vec) {
std::cout << x << " ";
}
// 使用迭代器
for (auto it = vec.begin(); it != vec.end(); ++it) {
std::cout << *it << " ";
}
// 算法配合
#include <algorithm>
std::sort(vec.begin(), vec.end()); //先对vector进行排序`
auto it = std::find(vec.begin(), vec.end(), 3); //在排序后的vector中查找元素3`
高级操作:
// emplace_back (与push_back相同,末尾添加,但避免了拷贝,添加更加高效)
vec.emplace_back(5); // 直接在末尾构造元素
// 范围初始化
std::vector<int> vec2(vec.begin() + 1, vec.end() - 1); // 范围即为索引,begin()为索引0——第一个元素,vec.end()表示遍历结束的信号,为索引size+1,vec.end()-1表示不输出最后一个,这里会输出[2,3]
// 与数组互转
int arr[] = {1, 2, 3};
// 方式1: 使用数组指针
std::vector<int> vec1(arr, arr + 3); // {1, 2, 3}
// 方式2: 使用std::begin/std::end
std::vector<int> vec2(std::begin(arr), std::end(arr));
最佳实践:
- 优先使用vector而非原生数组
- 使用emplace_back替代push_back避免拷贝
- 预先reserve()避免频繁扩容
- 用范围for循环遍历
2. unordered_map:哈希表
底层实现:unordered_map基于哈希表实现,使用哈希函数将键映射到桶中,采用链地址法解决冲突。提供O(1)的平均查找时间。
特性:
- 键值对集合,键唯一
- O(1)平均时间复杂度的查找、插入、删除
- 无序(不保证元素顺序)
- 需要提供哈希函数
基本操作:
#include <unordered_map>
#include <string>
// 基本操作
std::unordered_map<int, std::string> map;
map[1] = "one"; // O(1) 插入/更新
map.insert({2, "two"}); // O(1) 插入
std::string val = map[1]; // O(1) 访问
// 安全访问
if (map.find(3) != map.end()) {
std::cout << map[3];
}
// 检查键存在
if (map.count(1) > 0) {
// 键存在
}
// 删除
map.erase(1); // O(1) 删除
// 遍历
for (const auto& [key, value] : map) {
std::cout << key << ": " << value << "\n";
}
// 预分配
map.reserve(1000);
// 自定义类型需要提供哈希函数
struct Point {
int x, y;
};
struct PointHash {
size_t operator()(const Point& p) const {
return std::hash<int>()(p.x) ^ (std::hash<int>()(p.y) << 1);
}
};
struct PointEqual {
bool operator()(const Point& a, const Point& b) const {
return a.x == b.x && a.y == b.y;
}
};
std::unordered_map<Point, std::string, PointHash, PointEqual> point_map;
最佳实践:
- 优先使用emplace()代替insert()
- 使用find()代替operator[]进行查找
- 预先reserve()避免频繁rehash
- 为自定义类型提供合适的哈希函数
3. map:有序映射
底层实现:map基于红黑树实现,提供O(log n)的查找时间但保证元素按键有序。
特性:
- 键值对集合,键唯一
- O(log n)时间复杂度的查找、插入、删除
- 元素按键有序(升序)
- 支持范围查询
基本操作:
#include <map>
// 基本操作
std::map<int, std::string> map;
map[1] = "one";
map[2] = "two";
map[3] = "three";
// 有序遍历
for (const auto& [key, value] : map) {
std::cout << key << ": " << value << "\n";
// 输出顺序: 1, 2, 3
}
// 范围查询
auto lower = map.lower_bound(2); // 第一个 >= 2 的元素
auto upper = map.upper_bound(2); // 第一个 > 2 的元素
// 区间遍历
for (auto it = lower; it != upper; ++it) {
std::cout << it->first << ": " << it->second << "\n";
}
// 查找
auto it = map.find(2);
if (it != map.end()) {
std::cout << "Found: " << it->second;
}
最佳实践:
- 当需要有序遍历或范围查询时使用map
- 使用lower_bound/upper_bound进行范围查询
- 用insert()代替operator[]避免意外插入
4. unordered_set与set
底层实现:
- unordered_set:基于哈希表实现,提供O(1)的平均成员检查
- set:基于红黑树实现,提供O(log n)的成员检查但保证元素有序
基本操作:
#include <unordered_set>
#include <set>
// unordered_set 基本操作
std::unordered_set<int> uset = {1, 2, 3};
uset.insert(4); // O(1) 插入
uset.erase(2); // O(1) 删除
bool exists = uset.count(3); // O(1) 检查存在
// set 基本操作
std::set<int> oset = {3, 1, 4, 2};
oset.insert(5);
// 有序遍历
for (int x : oset) {
std::cout << x << " "; // 输出: 1 2 3 4 5
}
// 范围查询
auto lower = oset.lower_bound(2);
auto upper = oset.upper_bound(4);
// 集合运算
std::set<int> s1 = {1, 2, 3, 4};
std::set<int> s2 = {3, 4, 5, 6};
std::set<int> result;
std::set_intersection(s1.begin(), s1.end(),
s2.begin(), s2.end(),
std::inserter(result, result.begin()));
// result = {3, 4}
最佳实践:
- 无需维护顺序时使用unordered_set
- 需要有序遍历或范围查询时使用set
- 使用set_intersection等算法进行集合运算
5. array:固定大小数组
底层实现:array是C++11引入的固定大小数组容器,在栈上分配内存,提供编译时大小检查。
特性:
- 固定大小,编译时确定
- 栈上分配,性能高
- 提供STL容器的接口
- 与原生数组兼容
基本操作:
#include <array>
#include <iostream>
// 创建固定大小数组
std::array<int, 5> arr = {1, 2, 3, 4, 5};
// 访问元素
int first = arr[0];
int safe = arr.at(0);
// 大小
size_t sz = arr.size(); // 编译时常量
// 遍历
for (int x : arr) {
std::cout << x << " ";
}
// 与STL算法配合
std::sort(arr.begin(), arr.end());
// 多维数组
std::array<std::array<int, 3>, 3> matrix = {{
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
}};
最佳实践:
- 表示固定维度的数学向量
- 小型查找表
- 配置常量数组
- 需要栈上分配且大小固定的场景
总结对比
| 特性 | Python list | Python dict | Python set | Python tuple | C++ vector | C++ unordered_map | C++ map | C++ array |
|---|---|---|---|---|---|---|---|---|
| 底层 | 动态数组 | 哈希表 | 哈希表 | 不可变序列 | 连续数组 | 哈希表 | 红黑树 | 固定大小数组 |
| 访问 | O(1)索引 | O(1)平均 | O(1)平均 | O(1)索引 | O(1)随机 | O(1)平均 | O(log n) | O(1)随机 |
| 插入 | O(1)均摊 | O(1)平均 | O(1)平均 | 无 | O(1)均摊 | O(1)平均 | O(log n) | 无 |
| 删除 | O(n) | O(1)平均 | O(1)平均 | 无 | O(n) | O(1)平均 | O(log n) | 无 |
| 排序 | 有序 | 有序(3.7+) | 无序 | 有序 | 有序 | 无序 | 有序 | 有序 |
| 键/索引 | 索引 | 键 | 无 | 索引 | 索引 | 键 | 键 | 索引 |
| 可变性 | 可变 | 可变 | 可变 | 不可变 | 可变 | 可变 | 可变 | 不可变 |
| 典型用途 | 序列数据 | 数据索引 | 唯一元素 | 固定组合 | 动态数组 | 快速查找 | 有序查找 | 固定大小数组 |
选择建议:
- Python:需要动态序列?用list;需要键值对?用dict;需要唯一元素?用set;需要固定组合?用tuple
- C++:需要动态数组?用vector;需要快速查找?用unordered_map;需要有序查找?用map;需要固定大小数组?用array
第三部分:次要结构速览
栈与队列
栈遵循后进先出(LIFO)原则,队列遵循先进先出(FIFO)原则。这两种结构在算法实现中广泛应用,但通常不需要专门的栈或队列类型。
# Python - 栈操作
stack = []
stack.append(10) # 入栈
stack.append(20)
top = stack[-1] # 查看栈顶
stack.pop() # 出栈
# Python - 队列操作
from collections import deque
queue = deque()
queue.append(10) # 入队
queue.append(20)
front = queue[0] # 查看队首
queue.popleft() # 出队
// C++ - 栈
#include <stack>
std::stack<int> s;
s.push(10); // 入栈
s.push(20);
int top = s.top(); // 查看栈顶
s.pop(); // 出栈
// C++ - 队列
#include <queue>
std::queue<int> q;
q.push(10); // 入队
q.push(20);
int front = q.front();// 查看队首
q.pop(); // 出队
栈的典型应用包括函数调用栈模拟、括号匹配、表达式求值、深度优先搜索的迭代实现。队列的主要应用在广度优先搜索、任务调度、消息传递、缓冲区管理等场景。
deque:双端队列
deque支持在两端高效插入和删除。Python的collections.deque和C++的std::deque都提供O(1)的两端操作。
# Python deque
from collections import deque
dq = deque([1, 2, 3])
dq.append(4) # 右端添加
dq.appendleft(0) # 左端添加
dq.pop() # 右端删除
dq.popleft() # 左端删除
dq.rotate(1) # 循环右移
// C++ deque
#include <deque>
std::deque<int> dq = {1, 2, 3};
dq.push_back(4); // 右端添加
dq.push_front(0); // 左端添加
dq.pop_back(); // 右端删除
dq.pop_front(); // 左端删除
int elem = dq[1]; // O(1) 随机访问
deque的典型应用包括实现固定大小的滑动窗口、维护最近访问记录、实现双端队列算法等。deque作为std::stack和std::queue的默认底层容器,因为它比vector在头部操作更高效,比list的随机访问更快。
堆:优先级队列
堆是特殊的完全二叉树结构,满足堆性质。堆能够在O(log n)时间内插入元素和取出最大或最小元素,这使得它成为实现优先级队列的标准数据结构。
# Python heapq (最小堆)
import heapq
heap = []
heapq.heappush(heap, 3)
heapq.heappush(heap, 1)
heapq.heappush(heap, 2)
min_elem = heapq.heappop(heap) # 1
# 将列表转换为堆
lst = [3, 1, 4, 1, 5]
heapq.heapify(lst)
# Top K问题
numbers = [5, 2, 8, 1, 9, 3]
top3 = heapq.nlargest(3, numbers) # [9, 8, 5]
// C++ priority_queue (最大堆)
#include <queue>
std::priority_queue<int> pq;
pq.push(3);
pq.push(1);
pq.push(2);
int max_elem = pq.top(); // 3
pq.pop();
// 最小堆
std::priority_queue<int, std::vector<int>, std::greater<int>> min_pq;
min_pq.push(3);
min_pq.push(1);
int min_elem = min_pq.top(); // 1
堆的应用场景包括Top-K问题、任务调度、Dijkstra最短路径算法、合并K个有序数组等。
树与图
树和图是表达层次关系和网络关系的非线性数据结构,在Python和C++中都没有内置的标准实现,需要根据具体问题自行设计。
# Python - 树节点定义
class TreeNode:
def __init__(self, val):
self.val = val
self.left = None
self.right = None
# 二叉树遍历
def inorder(root):
if root:
inorder(root.left)
print(root.val)
inorder(root.right)
# Python - 图的邻接表表示
graph = {
'A': ['B', 'C'],
'B': ['A', 'D', 'E'],
'C': ['A', 'F'],
'D': ['B'],
'E': ['B', 'F'],
'F': ['C', 'E']
}
# BFS遍历
from collections import deque
def bfs(graph, start):
visited = set()
queue = deque([start])
visited.add(start)
while queue:
node = queue.popleft()
print(node)
for neighbor in graph[node]:
if neighbor not in visited:
visited.add(neighbor)
queue.append(neighbor)
// C++ - 树节点定义
struct TreeNode {
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};
// C++ - 图的邻接表表示
#include <unordered_map>
#include <vector>
std::unordered_map<int, std::vector<int>> graph;
graph[1] = {2, 3};
graph[2] = {1, 4};
graph[3] = {1, 4};
graph[4] = {2, 3};
// DFS遍历
void dfs(int node, std::unordered_set<int>& visited) {
visited.insert(node);
std::cout << node << " ";
for (int neighbor : graph[node]) {
if (visited.find(neighbor) == visited.end()) {
dfs(neighbor, visited);
}
}
}
树结构应用于文件系统、语法分析树、决策树、B树索引等。C++的std::map和std::set底层就是红黑树实现。图结构用于表达任意的多对多关系,应用于网络路由、社交网络分析、依赖关系解析等领域。
第四部分:选型实战指南
核心三件套的应用场景
在实际开发中,大约90%的数据结构需求可以由数组、字典和集合满足。这三种结构分别对应三种最基本的数据组织需求:有序集合、键值映射、无重复集合。
# Python实战示例:推荐系统中的数据结构选择
# 1. 用户历史行为 - 使用list
user_history = [101, 205, 303, 410] # 按时间顺序的物品ID
# 2. 物品特征映射 - 使用dict
item_features = {
101: {'category': 'electronics', 'price': 999},
205: {'category': 'books', 'price': 29}
}
# 3. 已推荐物品 - 使用set
recommended = {101, 205, 303}
# 过滤已推荐物品
candidates = [101, 205, 401, 502]
new_candidates = [item for item in candidates if item not in recommended]
# 计算用户共同兴趣
user1_items = {101, 205, 303}
user2_items = {205, 303, 401}
common_interests = user1_items & user2_items # {205, 303}
// C++实战示例:高性能计算中的数据结构选择
#include <vector>
#include <unordered_map>
#include <unordered_set>
// 1. 大规模数值计算 - 使用vector
std::vector<double> data(1000000);
for (size_t i = 0; i < data.size(); ++i) {
data[i] = compute(i);
}
// 2. 特征索引 - 使用unordered_map
std::unordered_map<std::string, int> feature_index;
feature_index["age"] = 0;
feature_index["income"] = 1;
// 3. 去重与快速查找 - 使用unordered_set
std::unordered_set<int> seen;
for (int value : data) {
if (seen.count(value) == 0) {
process(value);
seen.insert(value);
}
}
特定场景的选型决策
// 场景1:需要维护有序数据并进行范围查询 - 使用map
std::map<int, std::string> timestamp_events;
timestamp_events[1000] = "event1";
timestamp_events[2000] = "event2";
timestamp_events[3000] = "event3";
// 查询时间区间[1500, 2500]内的所有事件
auto start = timestamp_events.lower_bound(1500);
auto end = timestamp_events.upper_bound(2500);
for (auto it = start; it != end; ++it) {
std::cout << it->second << "\n";
}
# 场景2:频繁在序列两端操作 - 使用deque
from collections import deque
# 实现固定大小的滑动窗口
window = deque(maxlen=5)
for value in data_stream:
window.append(value)
if len(window) == 5:
avg = sum(window) / 5
print(f"Moving average: {avg}")
# 场景3:优先级调度 - 使用堆
import heapq
# 任务调度系统
tasks = []
heapq.heappush(tasks, (1, "high priority task"))
heapq.heappush(tasks, (5, "low priority task"))
heapq.heappush(tasks, (3, "medium priority task"))
# 按优先级处理任务
while tasks:
priority, task = heapq.heappop(tasks)
process(task)
性能优化要点
// 优化1:预分配空间避免频繁扩容
std::vector<int> vec;
vec.reserve(1000000); // 预分配容量
for (int i = 0; i < 1000000; ++i) {
vec.push_back(i); // 不会触发扩容
}
// 优化2:使用emplace避免不必要的拷贝
std::vector<std::string> strings;
strings.emplace_back("hello"); // 就地构造,无拷贝
// 优化3:使用const引用传递容器
void process(const std::vector<int>& data) {
// 避免拷贝整个容器
}
# 优化1:使用生成器减少内存占用
def process_large_file(filename):
with open(filename) as f:
for line in f: # 逐行处理,不加载整个文件
yield process(line)
# 优化2:使用集合判断成员资格而非列表
# 错误方式 - O(n)
if item in large_list:
pass
# 正确方式 - O(1)
large_set = set(large_list)
if item in large_set:
pass
浙公网安备 33010602011771号