Files
Cloud-book/Python/Python基础/Python数据结构.md
2025-08-27 17:10:05 +08:00

796 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Python 基本数据结构
# 列表List
列表是一个有序的可变集合,可以存储不同类型的元素。列表相比于字符串,不仅可以储存不同的数据类型,而且可以储存大量数据。而且列表是有序的,有索引值,可切片,方便取值。
## 定义列表
```python
fruits = ["apple", "banana", "cherry"]
print(fruits)
# Output:
['apple', 'banana', 'cherry']
```
## 增加元素
```python
# 1. 按照索引的位置去增加
fruits.insert(1, "kiwi")
# 2. 在最末尾添加
fruits.append("orange")
# 3. 用迭代的方式去添加
fruits.extend(['a','b','c'])
# 可以立即为再传递一个List进去然后依次添加到末尾,而append会把括号里的当成一个整体添加到末尾
# Output:
['apple', 'kiwi', 'banana', 'cherry', 'orange', 'a', 'b', 'c']
```
## 删除元素
```python
# 1. 删除制定的具体元素
fruits.remove("cherry")
# 2. 按照索引位置去删除
fruits.pop() # 默认是删除最后一个,可以加上索引。并且有返回值,返回值为删除的元素
fruits.pop(1)
# 3. 使用切片范围删除
del fruits[1:3] # 没有返回值
# 4. 清空列表
fruits.clear()
# 5. 删除列表
del fruits
# clear是清空列表的内容变成一个空列表而del是删除列表本身后续无法再次调用...
# Output:
['apple', 'banana', 'cherry']
['apple', 'kiwi', 'banana', 'cherry', 'orange', 'a', 'b', 'c']
[]
```
## 修改元素
```python
# 1. 按照索引修改某个元素的值
fruits = ["apple", "banana", "cherry"]
fruits[1] = "orange"
print(fruits)
# Output:
['apple', 'orange', 'cherry']
# 2. 按照切片范围修改
fruits[0:2] = ['kiwi','orange']
print(fruits)
# Output:
['kiwi', 'orange', 'cherry']
```
## 查找元素
```python
# 1. index(element):返回元素的索引
fruits = ["apple", "banana", "cherry"]
index_of_apple = fruits.index("apple")
print(index_of_apple)
# Output:
0
# 2. count(element):返回元素出现的次数
count_of_cherry = fruits.count("cherry")
print(count_of_cherry)
# Output:
1
```
## 列表的切片
切片是按照一定的索引规律分割列表从而组成一个新的列表,类似与我们字符串中讲到的切片
```python
li = [1,2,4,5,4,2,4]
sub_li = li[2:5]
print(sub_li)
# Output:
[4, 5, 4]
```
## 其他操作
```python
li = [1,2,4,5,4,2,4]
# 1. 统计某个元素出现的次数
print (li.count(4))
# 2. 从列表中找出某个值第一个匹配项的索引位置
print (li.index(2))
# 3. 对列表进行排序
li.sort()
print(li)
# 4. 将列表中的元素反向存放
li.reverse()
print (li)
# Output:
3
1
[1, 2, 2, 4, 4, 4, 5]
[5, 4, 4, 4, 2, 2, 1]
```
# 元组Tuple
有序的不可变集合,也被被称为只读列表,即数据可以被查询,但不能被修改。
## 定义元组
```python
tuple = (1,2,3,'a','b','c')
print(tuple)
print(tuple[1])
# 可以删除元素吗?
tuple.pop()
# Output:
AttributeError: 'tuple' object has no attribute 'pop'
```
## 可变元组
tuple 其实不可变的是地址空间,如果地址空间里存的是可变的数据类型的话,比如列表就是可变的
```python
tuple = (1, 2, 3, [1, 4, 7])
print(tuple)
tuple[3][2] = 100
print(tuple)
# Output:
(1, 2, 3, [1, 4, 7])
(1, 2, 3, [1, 4, 100])
```
在元组 tuple 中,包含一个 list 类型的数据,由于 list 是可以变的,所以我们可以更改元组里 list 的值,但是元组本身的地址空间是不变的。
<img src="Python数据结构/可变元组.png" alt="image-20240904095734746" style="zoom:80%;" />
# 字典Dict
字典是 python 中唯一的映射类型采用键值对key-value的形式存储数据。python 对 key 进行哈希函数运算,根据计算的结果决定 value 的存储地址所以字典是无序存储的且key必须是可哈希的。可哈希表示 key 必须是不可变类型,如:数字、字符串、元组。
不过在 Python 3.6 版本以后,字典会保留插入顺序,变成有序数据结构。
而且键是具有唯一性的,每个键只能出现一次,但是值没有限制,在一个字典中可以出现多个相同的值。
## 定义字典
```python
dic = {'name':'nls','age':18,'job':'teacher'}
print(dic)
print(dic['name'])
print(dic['age'])
print(dic['job'])
# Output:
{'name': 'nls', 'age': 18, 'job': 'teacher'}
nls
18
teacher
```
## 增加键值
```python
dic = {'name':'nls','age':18,'job':'teacher'}
# 1. 直接通过键值对来增加
dic['hobby'] = 'study' # 如果键已经存在,那么就会替换原来的值
print(dic)
# Output: {'name': 'nls', 'age': 18, 'job': 'teacher', 'hobby': 'study'}
# 2. 在字典中添加键值对时,如果指定的键已经存在则不做任何操作,如果原字典中不存在指定的键值对,则会添加。
dic.setdefault('name','牛老师')
dic.setdefault('gender','男')
print(dic)
# Output:
{'name': 'nls', 'age': 18, 'job': 'teacher', 'hobby': 'study', 'gender': '男'}
```
## 删除键值
```python
dic = {'name':'nls','age':18,'job':'teacher'}
# 1. 删除指定的键,并且返回对应的值
name = dic.pop('job')
hobby = dic.pop('hobby','查无此项') # 可以在后面加上一个异常处理如果key不存在就输出后面的内容
print(dic)
print(name)
print(hobby)
# Output:
{'name': 'nls', 'age': 18}
teacher
查无此项
# 2. 使用del关键字删除指定的键值对
del dic['name']
# 3. 删除最后插入的键值对
dic_pop = dic.popitem()
print(dic_pop)
# 4. 清空字典
dic.clear()
print(dic)
# Output: {}
```
## 修改键值
```python
dic = {'name':'nls','age':18,'job':'teacher'}
dic['age'] = 25
print(dic)
# Output:
{'name': 'nls', 'age': 25, 'job': 'teacher'}
```
## 查找键值
```python
dic = {'name':'nls','age':18,'job':'teacher'}
# 1. 直接通过键名获取,如果不存在会报错
value = dic['name']
print(value)
# 2. 使用get方法获取键值,若不存在则返回 None可以自定义异常返回值
value = dic.get('job','查无此项')
print(value)
# Output:
nls
teacher
# 3. IN关键字存在返回True反之False
exists = "name" in dic
print(exists)
# Output:
True
```
## 其他操作
```python
dic = {'name':'nls','age':18,"phone":['1888888888','0511-10101010']}
# 字典里面的value也可以是容器类型的数据比如列表字典等等...但是key必须是不可变的
print(dic)
# 1. 对键和值进行迭代操作
for i in dic.items():
# 将键和值作为元祖列出
print(i)
for i in dic:
# 只迭代键
print(i)
# Output:
('name', 'nls')
('age', 18)
('phone', ['1888888888', '0511-10101010'])
name
age
phone
# 2. 使用keys()和values()方法获取键值
keys = dic.keys()
print(keys,type(keys))
value = dic.values()
print(value,type(value))
# Output:
dict_keys(['name', 'age', 'phone']) <class 'dict_keys'>
dict_values(['nls', 18, ['1888888888', '0511-10101010']]) <class 'dict_values'>
```
# 集合Set
集合是**无序**的,**不重复****确定性**的数据集合,它里面的元素是可哈希的(不可变类型),但是集合本身是不可哈希(所以集合做不了字典的键)的。以下是集合最重要的两点:
- 去重,把一个列表变成集合,就自动去重了。
- 关系测试,测试两组数据之前的交集、差集、并集等关系。
## 定义集合
```python
set1 = {1,2,3,'a','b','c'} # 推荐方法
set2 = set({1,2,3})
print(set1, set2)
# Output:
{1, 2, 3, 'b', 'c', 'a'} {1, 2, 3}
```
## 增加元素
```python
set1 = {1,2,3,'a','b','c'}
# 1. 使用add()方法为集合增加元素
set1.add('d')
print(set1)
# Output:
{'a', 1, 2, 3, 'c', 'd', 'b'}
# 2. 使用update()方法迭代的去增加
set1.update('e','f')
# update接收的参数应该是可迭代的数据类型比如字符串、元组、列表、集合、字典。这些都可以向集合中添加元素但是整型、浮点型不可以
set1.update([4,5,6])
print(set1)
# Output:
{1, 2, 3, 4, 5, 6, 'a', 'd', 'b', 'c', 'f', 'e'}
```
## 删除元素
```python
set1 = {1,2,3,'a','b','c'}
# 1. 使用remove()方法删除元素
set1.remove('a')
print(set1)
# 2. 随机删除某个元素
set1.pop()
print(set1)
# Output:
{1, 2, 3, 'c', 'b'}
{2, 3, 'c', 'b'}
# 3. 删除集合本身
del set1
```
## 查找元素
```python
set1 = {1,2,3,'a','b','c'}
exists = "a" in set1 # True
print(exists)
```
## 关系测试
### 交集
取出两个集合共有的元素
```python
set1 = {1,2,3,4,5}
set2 = {3,4,5,6,7}
print(set1 & set2)
print(set1.intersection(set2))
# Output:
{3, 4, 5}
{3, 4, 5}
```
### 反交集
```python
set1 = {1,2,3,4,5}
set2 = {3,4,5,6,7}
print(set1 ^ set2)
print(set1.symmetric_difference(set2))
# Output:
{1, 2, 6, 7}
{1, 2, 6, 7}
```
### 并集
合并两个集合的所有元素
```python
set1 = {1,2,3,4,5}
set2 = {3,4,5,6,7}
print(set1 | set2)
print(set2.union(set1))
# Output:
{1, 2, 3, 4, 5, 6, 7}
{1, 2, 3, 4, 5, 6, 7}
```
### 差集
第一个集合去除二者共有的元素
```python
set1 = {1,2,3,4,5}
set2 = {3,4,5,6,7}
print(set1 - set2)
print(set1.difference(set2))
# Output:
{1, 2}
{1, 2}
```
### 子集与超集
当一共集合的所有元素都在另一个集合里,则称这个集合是另一个集合的子集,另一个集合是这个集合的超集
```python
set1 = {1,2,3}
set2 = {1,2,3,4,5,6}
print(set1 < set2)
print(set1.issubset(set2)) # 这两个相同都是说明set1是set2子集。
print(set2 > set1)
print(set2.issuperset(set1)) # 这两个相同都是说明set2是set1超集
```
## 不可变集合
```python
set1 = {1,2,3,4,5,6}
set2 = frozenset(set1)
print(set2,type(set2))
set2.add(7) # 不可以修改,会报错
# Output:
AttributeError: 'frozenset' object has no attribute 'add'
```
# 数据结构的总结
| 数据结构 | 描述 | 特性 | 常见操作 | 适用场景 | 优点 | 缺点 |
| :-------------------- | :--------------- | :--------------------- | :--------------------- | :--------------------------------------- | :-------------------------- | :------------------------------------ |
| **列表** | 有序的可变集合 | 有序、可变、支持重复 | 添加、删除、修改、查找 | 需要维护元素顺序的场景,如队列、栈的实现 | 灵活性高,支持多种操作 | 查询复杂度为 O(n),插入和删除性能较差 |
| **元组** | 有序的不可变集合 | 有序、不可变、支持重复 | 查找 | 元素不需要修改的场景,如函数参数、字典键 | 更节省内存,速度较快 | 不支持修改 |
| **集合** | 无序的可变集合 | 无序、可变、不支持重复 | 添加、删除、查找 | 需要去重和快速查找的场景,如集合运算 | 快速查找和去重 | 不支持索引,元素无序 |
| **字典** | 有序的键值对集合 | 有序、可变、键唯一 | 添加、删除、修改、查找 | 需要快速查找和存储键值对的场景,如缓存 | 快速查找O(1) 平均复杂度) | 键必须是不可变类型,内存开销较大 |
| **字符串** | 字符的序列 | 不可变 | 查找、切片、替换 | 文本处理 | 易于使用,内置丰富的方法 | 不可修改,性能较低 |
# 流程控制
## 判断语句
**语法:**
```python
if 条件:
满足条件后要执行的代码
```
<img src="Python数据结构/流程控制.png" alt="img-流程控制" style="zoom:80%;" />
### 单分支判断
```python
# 提示用户输入年龄
age = int(input("请输入你的年龄:"))
# 单分支判断
if age >= 18:
print("已经成年,可以去网吧上网了")
print("欢迎来到Eagles网吧")
print("你还没成年,要好好学习")
```
### 双分支判断
```python
"""
if 条件:
满足条件执行代码
else:
if条件不满足就走这段
"""
# 提示用户输入年龄
age = int(input("请输入你的年龄:"))
# 单分支判断
if age >= 18:
print("已经成年,可以去网吧上网了")
print("欢迎来到Eagles网吧")
else:
print("你还没成年,要好好学习")
```
### 多分支判断
```python
if 条件:
满足条件执行代码
elif 条件:
上面的条件不满足就走这个
elif 条件:
上面的条件不满足就走这个
elif 条件:
上面的条件不满足就走这个
else:
上面所有的条件不满足就走这段
# 成绩判断程序
# 提示用户输入成绩
score = int(input("请输入成绩0-100"))
# 多分支判断成绩
if score >= 90 and score <= 100:
print("优秀")
elif score >= 60 and score < 90:
print("良好")
elif score >= 0 and score < 60:
print("不及格")
else:
print("输入错误请输入0到100之间的成绩。")
```
## 循环语句 - while
**语法:**
```python
while 条件:
循环体
# 循环条件可以直接是True/False或者1/0也可以是某个语句...
```
如果条件为真,那么循环体则执行
如果条件为假,那么循环体不执行
**示例:猜数字小游戏**
```python
print('猜数字游戏开始')
num = 54
while True:
guess = int(input("您猜数字是什么?(输入0-100的数字):"))
if guess < num:
print("您猜小了")
continue
elif guess > num:
print("您猜大了")
continue
break
print("您猜对了!")
# Output:
猜数字游戏开始
您猜数字是什么(输入0-100的数字):10
您猜小了
您猜数字是什么(输入0-100的数字):50
您猜小了
您猜数字是什么(输入0-100的数字):60
您猜大了
您猜数字是什么(输入0-100的数字):54
您猜对了
```
### 循环终止语句
#### break
用于完全结束一个循环,跳出循环体执行循环后面的语句
#### continue
和 break 有点类似,区别在于 continue 只是终止本次循环接着还执行后面的循环break 则完全终止循环
#### while …… else
while 后面的 else 作用是指,当 while 循环正常执行完,中间没有被 break 中止的话,就会执行 else 后面的语句
- **语法:**
```python
while condition:
# 循环体
if some_condition:
break
else:
# 循环正常结束时执行的代码
```
- **示例:**
```python
# 寻找素数的示例
num = 10
i = 2
while i < num:
if num % i == 0:
print(f"{num} 不是素数,因为它可以被 {i} 整除。")
break
i += 1
else:
print(f"{num} 是一个素数!")
```
## 循环语句 - for
for 循环:用户按照顺序循环可迭代对象的内容
**语法:**
```python
for variable in iterable:
# 循环体
# 执行的代码
```
### 遍历列表
```python
fruits = ["apple", "banana", "cherry"]
for fruit in fruits:
print(fruit)
# Output:
apple
banana
cherry
```
### 遍历字符串
```python
word = "helloworld"
for letter in word:
print(letter)
# Output:
h
e
l
l
o
w
o
r
l
d
```
## enumerate
enumerate枚举对于一个可迭代的iterable/可遍历的对象如列表、字符串enumerate 将其组成一个索引序列,利用它可以同时获得索引和值。
```python
li = ['甲','乙','丙','丁']
for i in enumerate(li):
print(i)
for index,value in enumerate(li):
print(index,value)
for index,value in enumerate(li,100): #从哪个数字开始索引
print(index,value)
# Output:
(0, '甲')
(1, '乙')
(2, '丙')
(3, '丁')
0
1
2
3
100
101
102
103
```
## range
指定范围,生成指定数字
```bash
for i in range(1,10):
print(i)
for i in range(1,10,2): # 步长
print(i)
for i in range(10,1,-2): # 反向步长
print(i)
```
### 小游戏案例
石头简单布
```bash
import random
# random产生随机值或者从给定值中随机选择
# 可选择的选项
options = ["石头", "剪子", "布"]
print("欢迎来到石头剪子布游戏!")
print("请从以下选项中选择:")
for i, option in enumerate(options):
print(f"{i + 1}. {option}")
# 玩家选择
player_choice = int(input("请输入你的选择1-3")) - 1
# 计算机随机选择
computer_choice = random.randint(0, 2)
print(f"\n你选择了{options[player_choice]}")
print(f"计算机选择了:{options[computer_choice]}")
# 判断胜负
if player_choice == computer_choice:
print("平局!")
elif (player_choice == 0 and computer_choice == 1) or \
(player_choice == 1 and computer_choice == 2) or \
(player_choice == 2 and computer_choice == 0):
print("你赢了!🎉")
else:
print("你输了!😢")
```
# 课后作业
## 完善猜数字小游戏
1. 生成一个随机数
2. 猜错3次了以后程序自动退出...
## 完善石头剪刀布游戏
1. 更新游戏规则,实现三局两胜制
2. 胜利条件:
- **2:1**
- **3:0**
# 扩展阅读
**内置类型**https://docs.python.org/zh-cn/3.9/library/stdtypes.html#