为什么学习数据结构?
数据结构的重要性
在编程的世界里,数据是核心。无论是简单的应用程序还是复杂的企业级系统,处理和管理数据都是必不可少的任务。数据结构就是一种特定的方式或格式来组织、管理和存储数据,以便可以高效地访问和修改这些数据。选择合适的数据结构对于编写高效的程序至关重要
- 提高效率:正确选择的数据结构能够显著提升算法的性能。例如,使用哈希表(字典)查找元素的速度远快于遍历列表
- 简化代码:良好的数据结构可以帮助我们写出更简洁、易读且易于维护的代码。比如,集合可以轻松实现去重功能,而不需要编写复杂的逻辑
- 解决问题的能力:掌握多种数据结构意味着你有更多的工具来解决各种编程问题。不同的数据结构适用于不同类型的问题,了解它们可以让你更好地应对挑战
Python 中的数据结构概述
Python 提供了丰富的内置数据结构,使得开发者能够快速有效地处理各种类型的数据。以下是 Python 中最常用的一些数据结构:
- 列表(List):有序且可变的序列,允许重复元素。非常适合用于需要频繁添加或删除元素的情况
- 元组(Tuple):与列表类似,但不可变。常用于表示固定数量的相关值,如坐标点或数据库记录
- 字典(Dictionary):无序的键值对集合,提供快速查找的能力。广泛应用于关联数组或映射关系中
- 集合(Set):无序且不包含重复元素的集合。常用于数学运算或去除重复项
除了这些基本的数据结构外,Python 还提供了其他高级数据结构,如栈(Stack)、队列(Queue)、双端队列(Deque)和堆(Heap)。这些结构虽然不是 Python 的内置类型,但可以通过标准库模块轻松实现
选择合适的数据结构
理解何时以及如何选择合适的数据结构是一个关键技能。以下是一些考虑因素:
- 操作频率:某些数据结构更适合频繁插入或删除操作,而另一些则擅长快速查找
- 内存占用:不同数据结构对内存的需求各不相同,选择时应考虑到系统的资源限制
- 应用场景:根据具体任务的需求选择最合适的数据结构。例如,在实现缓存机制时,可能需要使用 LRU 缓存(Least Recently Used),这通常由双端队列结合字典来实现
学习目标
通过本课程的学习,你将能够:
- 理解并熟练运用 Python 内置的基本数据结构(列表、元组、字典、集合)
- 掌握如何根据实际需求选择合适的数据结构
- 学会使用 Python 标准库提供的高级数据结构(如
collections
和queue
模块)- 通过实践项目加深对数据结构的理解,并能灵活应用到自己的项目中
列表(List)
列表是 Python 中最常用的数据结构之一,它是一种有序且可变的序列,允许存储不同类型的元素,并支持重复值。列表非常适合用于需要频繁添加、删除或访问元素的情况
创建列表
你可以通过多种方式创建列表:
空列表:使用方括号
[]
或list()
函数
empty_list = []
also_empty_list = list()
- 预填充列表:直接在方括号内列出元素
fruits = ['apple', 'banana', 'cherry']
numbers = [1, 2, 3, 4, 5]
mixed_list = ['apple', 1, True, {'key': 'value'}] # 可以包含不同类型的数据
- 从其他可迭代对象创建列表:使用
list()
函数将字符串、元组等转换为列表
string_to_list = list('hello') # 输出: ['h', 'e', 'l', 'l', 'o']
tuple_to_list = list((1, 2, 3)) # 输出: [1, 2, 3]
访问列表元素
列表中的元素可以通过索引访问,索引从 0 开始。你还可以使用负数索引来从列表末尾开始计数
索引访问
fruits = ['apple', 'banana', 'cherry']
print(fruits[0]) # 输出: apple
print(fruits[-1]) # 输出: cherry
切片操作
切片可以获取子列表,语法为
list[start:end:step]
,其中start
是起始索引(包含),end
是结束索引(不包含),step
是步长(默认为 1)
numbers = [0, 1, 2, 3, 4, 5]
print(numbers[1:4]) # 输出: [1, 2, 3]
print(numbers[:3]) # 输出: [0, 1, 2]
print(numbers[2:]) # 输出: [2, 3, 4, 5]
print(numbers[::2]) # 输出: [0, 2, 4]
print(numbers[::-1]) # 输出: [5, 4, 3, 2, 1, 0]
修改列表
列表是可变的数据结构,这意味着可以在创建后修改其内容
添加元素
append()
:在列表末尾添加一个元素
fruits.append('date')
print(fruits) # 输出: ['apple', 'banana', 'cherry', 'date']
insert()
:在指定位置插入一个元素
fruits.insert(1, 'berry')
print(fruits) # 输出: ['apple', 'berry', 'banana', 'cherry', 'date']
extend()
:将另一个可迭代对象的所有元素添加到列表末尾
fruits.extend(['fig', 'grape'])
print(fruits) # 输出: ['apple', 'berry', 'banana', 'cherry', 'date', 'fig', 'grape']
删除元素
remove()
:移除第一个匹配项。如果元素不存在,会抛出ValueError
fruits.remove('banana')
print(fruits) # 输出: ['apple', 'berry', 'cherry', 'date', 'fig', 'grape']
pop()
:移除并返回指定索引处的元素,默认移除最后一个元素
last_fruit = fruits.pop()
print(last_fruit) # 输出: grape
print(fruits) # 输出: ['apple', 'berry', 'cherry', 'date', 'fig']
del
语句:根据索引或切片删除元素
del fruits[1] # 删除索引为 1 的元素
print(fruits) # 输出: ['apple', 'cherry', 'date', 'fig']
del fruits[1:3] # 删除索引为 1 和 2 的元素
print(fruits) # 输出: ['apple', 'fig']
修改元素
可以直接通过索引赋值来修改列表中的元素
fruits[0] = 'orange'
print(fruits) # 输出: ['orange', 'fig']
遍历列表
遍历列表可以使用
for
循环,这有助于对每个元素进行处理
for fruit in fruits:
print(fruit)
# 使用 enumerate 获取索引和元素
for index, fruit in enumerate(fruits):
print(f"Index {index}: {fruit}")
列表推导式
列表推导式提供了一种简洁的方式来创建新的列表,通常用于基于现有列表生成新列表
squares = [x**2 for x in range(5)]
print(squares) # 输出: [0, 1, 4, 9, 16]
# 带条件的列表推导式
even_squares = [x**2 for x in range(10) if x % 2 == 0]
print(even_squares) # 输出: [0, 4, 16, 36, 64]
列表排序
Python 提供了两种主要的方式对列表进行排序:
sort()
方法和sorted()
函数。
sort()
:就地排序,直接修改原列表
numbers = [3, 1, 4, 1, 5, 9]
numbers.sort()
print(numbers) # 输出: [1, 1, 3, 4, 5, 9]
sorted()
:返回一个新的已排序列表,原列表保持不变
sorted_numbers = sorted(numbers)
print(sorted_numbers) # 输出: [1, 1, 3, 4, 5, 9]
print(numbers) # 输出: [3, 1, 4, 1, 5, 9]
- 自定义排序:可以传递
key
参数来指定排序依据
iterable
:要排序的可迭代对象。key_func
:一个函数,接收一个元素并返回一个用于排序的键
words = ['apple', 'banana', 'cherry', 'date']
sorted_words = sorted(words, key=len)
print(sorted_words) # 输出: ['date', 'apple', 'banana', 'cherry']
按字符串长度排序
假设我们有一个包含单词的列表,想要按照每个单词的长度进行排序
words = ['apple', 'banana', 'cherry', 'date']
# 使用 len 函数作为 key 来按单词长度排序
sorted_words = sorted(words, key=len)
print(sorted_words) # 输出: ['date', 'apple', 'banana', 'cherry']
在这个例子中,
len
函数被用作key
参数。对于每个单词,len
函数计算其字符数,然后根据这些数值对单词进行排序
按对象的属性排序
如果列表中的元素是更复杂的数据结构(如字典或对象),我们可以使用
lambda
表达式或operator.itemgetter
来指定排序依据
按字典的值排序
people = [
{'name': 'Alice', 'age': 30},
{'name': 'Bob', 'age': 25},
{'name': 'Charlie', 'age': 35}
]
# 使用 lambda 表达式按年龄排序
sorted_people_by_age = sorted(people, key=lambda person: person['age'])
print(sorted_people_by_age)
# 输出:
# [{'name': 'Bob', 'age': 25}, {'name': 'Alice', 'age': 30}, {'name': 'Charlie', 'age': 35}]
使用 operator.itemgetter
排序
Python 的
operator
模块提供了itemgetter
函数,可以更简洁地实现同样的功能
from operator import itemgetter
people = [
{'name': 'Alice', 'age': 30},
{'name': 'Bob', 'age': 25},
{'name': 'Charlie', 'age': 35}
]
sorted_people_by_age = sorted(people, key=itemgetter('age'))
print(sorted_people_by_age)
# 输出:
# [{'name': 'Bob', 'age': 25}, {'name': 'Alice', 'age': 30}, {'name': 'Charlie', 'age': 35}]
itemgetter('age')
创建了一个函数,该函数从字典中提取'age'
键的值,作为排序的依据
按多个条件排序
有时我们需要根据多个属性来排序。可以通过传递一个包含多个键的
tuple
给key
参数来实现这一点
students = [
{'name': 'Alice', 'grade': 88, 'age': 22},
{'name': 'Bob', 'grade': 92, 'age': 21},
{'name': 'Charlie', 'grade': 88, 'age': 23}
]
# 先按成绩降序排序,再按年龄升序排序
sorted_students = sorted(students, key=lambda student: (-student['grade'], student['age']))
print(sorted_students)
# 输出:
# [{'name': 'Bob', 'grade': 92, 'age': 21}, {'name': 'Alice', 'grade': 88, 'age': 22}, {'name': 'Charlie', 'grade': 88, 'age': 23}]
在这个例子中,
(-student['grade'], student['age'])
返回一个元组,其中第一个元素是负的成绩(确保成绩高的排在前面),第二个元素是年龄。这样,首先会根据成绩排序,当成绩相同时,再根据年龄排序
按对象方法的结果排序
如果我们有一个类的对象列表,还可以根据对象的方法结果进行排序
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def get_initial(self):
return self.name[0] if self.name else ''
people = [Person('Alice', 30), Person('Bob', 25), Person('Charlie', 35)]
# 按名字首字母排序
sorted_people_by_initial = sorted(people, key=lambda person: person.get_initial())
for person in sorted_people_by_initial:
print(f"{person.name}: {person.age}")
# 输出:
# Alice: 30
# Bob: 25
# Charlie: 35
在这个例子中,
lambda person: person.get_initial()
提取每个Person
对象的名字首字母,并根据这个值进行排序