一、引言:为什么数据结构如此重要?
在编程中,数据结构是指相互之间存在一种或多种特定关系的数据元素的集合。它是组织和存储数据的方式,直接影响程序的效率、可读性和可维护性。
选择合适的数据结构可以:
- ✅ 提高程序效率:更快地存取、插入、删除数据
- ✅ 简化代码逻辑:更清晰地表达算法意图
- ✅ 增强代码可维护性:易于修改、扩展和调试
- ✅ 节省内存空间:合理利用资源
Python 提供了丰富的内置数据结构(如列表、字典、集合等),同时支持自定义复杂的数据结构(如链表、栈、队列等)。
💡 数据结构 + 算法 = 程序
二、核心数据结构概览
类别 |
数据结构 |
特点 |
序列类型 |
列表(List)、元组(Tuple)、字符串(String)、范围(Range) |
有序的元素集合,支持索引访问 |
映射类型 |
字典(Dictionary) |
键值对存储,支持快速查找 |
集合类型 |
集合(Set)、冻结集合(Frozen Set) |
无序唯一元素,适合去重和成员测试 |
📌 本文将重点讲解最常用的数据结构及其操作。
三、列表(List)—— 动态数组
1. 定义与基本特性
列表是有序、可变的元素集合,可以存储不同类型的数据。
fruits = ["苹果", "香蕉", "橙子"]
numbers = [1, 2, 3]
mixed = ["Alice", 25, True]
✅ 支持异构数据类型,非常灵活。
2. 常用操作
2.1 索引与切片
print(fruits[0]) # "苹果"
print(fruits[-1]) # "橙子" (负索引:从末尾开始)
print(fruits[1:3]) # ["香蕉", "橙子"] (切片 [start:end),左闭右开)
print(fruits[:2]) # ["苹果", "香蕉"] (从开头到索引2之前)
print(fruits[1:]) # ["香蕉", "橙子"] (从索引1到最后)
📌 切片是 Python 中非常强大的特性,支持步长:fruits[::2]
表示每隔一个取一个。
2.2 修改元素
python
fruits[1] = "葡萄"
print(fruits) # ["苹果", "葡萄", "橙子"]
✅ 列表是可变对象,可以直接通过索引修改。
2.3 添加与删除元素
# 添加
fruits.append("草莓") # 在末尾添加
fruits.insert(1, "芒果") # 在索引1处插入
# 删除
fruits.remove("苹果") # 移除第一个匹配项
del fruits[0] # 根据索引删除
popped = fruits.pop() # 弹出并返回最后一个元素
popped_first = fruits.pop(0) # 弹出第一个元素
⚠️ remove()
只删除第一个匹配项;pop()
返回被删除的值。
2.4 排序与反转
numbers = [3, 1, 4, 1, 5]
numbers.sort() # 原地升序排序 → [1, 1, 3, 4, 5]
numbers.reverse() # 原地反转 → [5, 4, 3, 1, 1]
# 不改变原列表的排序
sorted_numbers = sorted(numbers, reverse=True) # 返回降序新列表
reversed_list = list(reversed(numbers)) # 返回反转迭代器
✅ 推荐使用 sorted()
和 reversed()
以避免副作用。
2.5 列表推导式(List Comprehension)
一种简洁创建列表的方式,语法清晰、性能优越。
# 生成平方数
squares = [x**2 for x in range(1, 6)] # [1, 4, 9, 16, 25]
# 过滤偶数
evens = [x for x in range(10) if x % 2 == 0] # [0, 2, 4, 6, 8]
# 多重循环
pairs = [(x, y) for x in [1,2] for y in ['a','b']]
# [(1,'a'), (1,'b'), (2,'a'), (2,'b')]
✅ 比传统 for
循环更简洁高效。
四、元组(Tuple)—— 不可变序列
1. 定义与基本特性
元组与列表类似,但不可变,适合用于不希望被修改的数据(如坐标、配置项)。
point = (10, 20)
person = ("Alice", 25, "北京")
single = (42,) # 单元素元组必须加逗号
✅ 不可变性带来安全性,可用于字典的键或集合的元素。
2. 常用操作
2.1 访问元素
print(point[0]) # 10
print(person[1]) # 25
📌 支持索引和切片,但不能修改。
2.2 解包(Unpacking)
一种优雅获取多个返回值的方式。
name, age, city = person
print(name, age, city) # Alice 25 北京
# 忽略某些值
_, age, _ = person
# 扩展解包(Python 3+)
first, *rest = [1, 2, 3, 4]
print(first) # 1
print(rest) # [2, 3, 4]
✅ 广泛应用于函数返回值、循环遍历等场景。
五、字典(Dictionary)—— 键值映射
1. 定义与基本特性
字典是键值对的集合,键必须是唯一的且不可变(如字符串、数字、元组),值可以是任意类型。
student = {
"name": "Bob",
"age": 20,
"courses": ["数学", "英语"]
}
✅ 基于哈希表实现,查找时间复杂度接近 O(1)。
2. 常用操作
2.1 访问与修改
rint(student["name"]) # Bob
student["age"] = 21 # 修改现有键
student["city"] = "上海" # 新增键值对
⚠️ 访问不存在的键会抛出 KeyError
。
2.2 安全访问:get()
方法
# 使用 get 安全访问,避免 KeyError
print(student.get("phone", "未知")) # 如果没有 'phone' 键,则返回 "未知"
✅ 推荐在不确定键是否存在时使用 get()
。
2.3 删除键值对
del student["courses"] # 删除指定键
popped_value = student.pop("age") # 弹出并返回值
student.clear() # 清空所有键值对
📌 pop()
也可设置默认值:student.pop("grade", "未评级")
2.4 获取所有键、值、键值对
print(student.keys()) # dict_keys(['name', 'age'])
print(student.values()) # dict_values(['Bob', 21])
print(student.items()) # dict_items([('name', 'Bob'), ('age', 21)])
✅ 可用于遍历:
for key, value in student.items():
print(f"{key}: {value}")
2.5 字典推导式
# 平方映射
squares = {x: x**2 for x in range(1, 6)}
# {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
# 过滤长度大于3的键
filtered = {k: v for k, v in student.items() if len(k) > 3}
✅ 简洁高效地构建字典。
六、集合(Set)—— 无序唯一集合
1. 定义与基本特性
集合是无序、唯一元素的集合,常用于去重和成员测试。
unique_numbers = {1, 2, 3, 3, 4} # {1, 2, 3, 4}
empty_set = set() # 空集合(不能用 {},那是空字典)
✅ 基于哈希表实现,成员测试非常快(O(1))。
2. 常用操作
2.1 成员测试
print(2 in unique_numbers) # True
print(5 not in unique_numbers) # True
✅ 比列表快得多,尤其适合大数据量。
2.2 集合运算:并集、交集、差集、对称差
set1 = {1, 2, 3}
set2 = {3, 4, 5}
union_set = set1 | set2 # {1, 2, 3, 4, 5}
intersection_set = set1 & set2 # {3}
difference_set = set1 - set2 # {1, 2}
symmetric_difference_set = set1 ^ set2 # {1, 2, 4, 5}
# 也可以使用方法
set1.union(set2)
set1.intersection(set2)
✅ 数学集合运算的完美体现。
2.3 添加与删除
set1.add(4)
set1.update([5, 6]) # 批量添加
set1.remove(1) # 删除,不存在则报错
set1.discard(10) # 删除,不存在也不报错
popped = set1.pop() # 随机弹出一个元素
✅ discard()
比 remove()
更安全。
七、冻结集合(Frozen Set)
frozenset
是不可变的集合,可以作为字典的键或集合的元素。
fs = frozenset([1, 2, 3])
d = {fs: "value"} # 合法,因为 frozenset 是不可变的
✅ 适用于需要哈希的场景。
八、总结:数据结构核心要点
数据结构 |
特性 |
使用场景 |
列表 (List) |
有序、可变、支持重复 |
动态数组、堆栈、队列、需要频繁修改的场景 |
元组 (Tuple) |
有序、不可变、轻量 |
固定结构数据(如坐标)、函数返回多个值、作为字典键 |
字典 (Dict) |
键值对、无序(Python 3.7+保持插入顺序)、快速查找 |
映射关系、缓存、配置管理、JSON 解析 |
集合 (Set) |
无序、唯一、快速成员测试 |
去重、集合运算、黑名单/白名单 |
九、最佳实践与建议
建议 |
说明 |
✅ 优先使用内置数据结构 |
经过高度优化,性能好 |
✅ 根据场景选择合适结构 |
不要“万能列表” |
✅ 使用推导式提升效率 |
简洁且性能优于循环 |
✅ 注意可变性带来的副作用 |
如函数参数传递可变对象 |
✅ 避免在循环中进行 O(n) 查找 |
用 set/dict 替代 list |
十、动手练习
1. 创建一个字典,存储学生的姓名、年龄、课程,然后打印出所有课程名称
students = [
{"name": "张三", "age": 20, "courses": ["数学", "物理"]},
{"name": "李四", "age": 21, "courses": ["化学", "生物"]}
]
for student in students:
print(f"{student['name']} 的课程: {', '.join(student['courses'])}")
2. 编写一个函数,接收一个列表作为参数,返回一个新的仅包含偶数的列表
def filter_even(numbers):
return [num for num in numbers if num % 2 == 0]
numbers = [1, 2, 3, 4, 5, 6]
even_numbers = filter_even(numbers)
print(even_numbers) # [2, 4, 6]
3. 使用集合去除重复的用户名单
usernames = ["alice", "bob", "alice", "charlie", "bob"]
unique_usernames = list(set(usernames))
print(unique_usernames) # 顺序可能不同
# 若需保持顺序:
unique_ordered = list(dict.fromkeys(usernames))
print(unique_ordered) # ['alice', 'bob', 'charlie']
🚀 学习建议
- 多实践:通过实际编写代码加深理解。
- 结合应用场景:思考每种数据结构最适合解决的问题类型。
- 深入学习高级应用:
-
- 生成器表达式(节省内存)
collections
模块(defaultdict
,Counter
,deque
)itertools
模块(高效迭代工具)
- 阅读源码:学习标准库和优秀开源项目中的数据结构使用方式。