课程章节

实验2.数据结构综合实验

数据结构综合实验

本实验把列表、元组、字符串、集合、字典串起来,做一个班级花名册:既能增删查改,也能按标签筛选、按成绩排序、导出摘要。

实验目标

  1. 会用列表保存有序记录,会用 append、insert、remove、sort 和列表生成式。
  2. 会用元组表示“不可拆改”的字段组合(如坐标式的一条固定信息)。
  3. 会用字符串方法清洗姓名、拼接输出、格式化对齐。
  4. 会用集合做去重和交并差,筛选“同时选修多门课”的学生。
  5. 会用字典按学号快速查找,并用字典生成式做统计。

数据约定

每名学生是一个字典,结构如下:

{
    'id': 'S001',           # 学号,字符串
    'name': '张三',         # 姓名
    'scores': [90, 85, 78], # 三门课成绩,列表
    'tags': {'数学', '篮球'}  # 兴趣/课程标签,集合
}

roster 是保存多条学生记录的列表。

参考实现

"""
班级花名册
覆盖:列表、元组、字符串、集合、字典
"""

# 1)准备数据:列表 + 字典 + 集合
roster = [
    {
        'id': 'S001',
        'name': '  张三  ',
        'scores': [90, 85, 78],
        'tags': {'数学', '篮球'},
    },
    {
        'id': 'S002',
        'name': 'Li Si',
        'scores': [72, 68, 91],
        'tags': {'英语', '篮球'},
    },
    {
        'id': 'S003',
        'name': '王五',
        'scores': [55, 60, 58],
        'tags': {'数学', '英语'},
    },
    {
        'id': 'S004',
        'name': '赵六',
        'scores': [88, 92, 95],
        'tags': {'数学'},
    },
]

# 2)字符串清洗:去掉姓名两侧空白,英文统一首字母大写
for student in roster:
    raw_name = student['name']
    cleaned = raw_name.strip()
    if cleaned.isascii():
        cleaned = cleaned.title()
    student['name'] = cleaned

# 3)列表方法:新增、查找、删除
new_student = {
    'id': 'S005',
    'name': '钱七',
    'scores': [80, 80, 80],
    'tags': {'篮球'},
}
roster.append(new_student)

# 按学号查找(字典键访问)
target_id = 'S002'
found = None
for student in roster:
    if student['id'] == target_id:
        found = student
        break
print(f'查找 {target_id}:', found['name'] if found else '未找到')

# 删除指定学号的学生
remove_id = 'S003'
roster[:] = [s for s in roster if s['id'] != remove_id]
print(f'已删除 {remove_id},剩余 {len(roster)} 人')

# 4)平均分与排序:用列表生成式提取平均分,再按平均分降序
for student in roster:
    student['average'] = sum(student['scores']) / len(student['scores'])

# 先算再排;用 sorted 生成新列表,不直接打乱原列表顺序
ranked = sorted(roster, key=lambda s: s['average'], reverse=True)
print('按平均分排名:')
for rank, student in enumerate(ranked, start=1):
    # 字符串格式化与对齐
    line = f"{rank}. {student['id']} {student['name']:<8} 平均分 {student['average']:.1f}"
    print(line)

# 5)集合运算:同时有“数学”和“篮球”的学生
math_students = {s['id'] for s in roster if '数学' in s['tags']}
basketball_students = {s['id'] for s in roster if '篮球' in s['tags']}
both = math_students & basketball_students
print('同时选数学和篮球:', sorted(both))
print('只选数学:', sorted(math_students - basketball_students))
print('选了数学或篮球:', sorted(math_students | basketball_students))

# 6)字典生成式:学号 -> 平均分
avg_map = {s['id']: round(s['average'], 1) for s in roster}
print('学号平均分表:', avg_map)

# 7)元组:用不可变三元组导出“学号、姓名、平均分”
summary = tuple((s['id'], s['name'], round(s['average'], 1)) for s in ranked)
print('摘要(元组):', summary)

# 8)按分数段统计人数(字典 + 列表)
level_count = {'优秀': 0, '良好': 0, '及格': 0, '不及格': 0}
for student in roster:
    avg = student['average']
    if avg >= 90:
        level_count['优秀'] += 1
    elif avg >= 80:
        level_count['良好'] += 1
    elif avg >= 60:
        level_count['及格'] += 1
    else:
        level_count['不及格'] += 1
print('分数段人数:', level_count)

预期结果要点

清洗后姓名应为:张三、Li Si、王五、赵六、钱七(王五 在删除后不存在)。删除 S003 后剩 4 人。S002 的平均分约为 77.0。同时含“数学”和“篮球”的学号应只有 S001。

扩展挑战

  1. 用 zip 把课程名列表 ['语文', '数学', '英语'] 和 scores 压成“课程-分数”字典。
  2. 用 str.split / str.join 把标签集合导出成 数学|篮球 形式。
  3. 增加“按姓名模糊查找”:对 name 做 lower() 后再用成员运算 in 判断。

知识点总结

  1. 列表:有序、可变;适合保存花名册本体和每人多门成绩;append 追加,列表生成式做过滤,sorted(..., key=lambda) 排序而不必原地打乱。
  2. 元组:有序、不可变;适合把“一条摘要记录”冻结成 (id, name, avg),避免后续被误改。
  3. 字符串:strip 去空白,title / isascii 处理英文名,{:<8} 左对齐,f-string 做展示;字符串是序列,也可切片和 in 判断。
  4. 集合:无序、元素唯一;用 {...} 字面量或集合生成式创建;&、|、- 做交并差,非常适合“标签筛选”。
  5. 字典:键值对,按键 $O(1)$ 查找;学生记录用字典表达字段;{k: v for ...} 做映射统计;遍历常用 items()。
  6. 嵌套结构:列表套字典、字典里的值再是列表/集合,是真实业务数据的常见形态;修改时要先定位到具体那条字典再改字段。
  7. 易错点:对元组用下标赋值会 TypeError;集合不能存列表等可变对象当元素;字典键不存在时用 [] 会 KeyError(本实验用“先查找再使用”避免);sorted 默认按字典对象比较会报错,必须写 key。
  8. 学习重点:能根据“有序、可变、是否按键查找、是否需要去重”为每段数据选对容器,并完成一次跨容器的小型数据整理流程。