第 2 课:数组基础
学习目标
完成本课学习后,你将能够:
- 理解数组的定义和基本特性。
- 掌握数组的声明、初始化和访问元素的方法。
- 熟练使用循环(如
for循环)遍历数组。 - 理解索引(Index)的概念,并能正确使用索引操作数组。
- 了解数组在内存中的存储方式及其优缺点。
核心概念
什么是数组?
想象你有一个书架,上面有一排连续的格子,每个格子都可以存放一本书。数组就像这样一个书架:它是一个线性数据结构,用于存储相同类型的固定数量的元素,这些元素在内存中连续存放。
数组中的每个元素都有一个唯一的编号,叫做索引(Index),通过索引我们可以快速访问(读取或修改)对应的元素。大多数编程语言中,数组的索引是从 0 开始的。
数组的内存模型
当你在程序中声明一个数组时,计算机会在内存中分配一块连续的空间来存储这些元素。假设一个整型数组 arr 有 5 个元素,每个整数占 4 个字节,那么它的内存布局如下:
索引: 0 1 2 3 4
内存: [4B] [4B] [4B] [4B] [4B] <- 连续的内存块
值: ? ? ? ? ? <- 初始化前的值(可能是随机值)
关键点:因为内存是连续的,所以通过计算 起始地址 + 索引 * 每个元素大小,可以在 O(1) 时间内直接定位到任意元素,这是数组最大的优点。
数组的声明与初始化(以 Python 为例)
在 Python 中,我们通常使用 列表(List) 来表示数组。列表在功能上类似于其他语言的数组,但更灵活(如长度可变、可存放不同类型元素)。为了学习基础数据结构,我们将 Python 列表当作数组来使用。
# 方法1:直接赋值初始化
scores = [90, 85, 78, 92, 88] # 创建一个包含5个整数的“数组”
# 方法2:创建一个空列表,后续添加元素
names = []
# 方法3:使用列表推导式(模拟固定大小初始化)
zeros = [0 for _ in range(10)] # 创建一个包含10个0的数组
代码示例
示例1:数组的创建、访问与遍历
# 创建一个存储学生成绩的数组
grades = [85, 92, 78, 95, 88]
# 1. 访问数组元素(通过索引)
print("第一个学生的成绩:", grades[0]) # 输出: 85 (索引从0开始)
print("第三个学生的成绩:", grades[2]) # 输出: 78
print("最后一个学生的成绩:", grades[4]) # 输出: 88
# 也可以使用负索引从末尾访问
print("最后一个学生的成绩(负索引):", grades[-1]) # 输出: 88
# 2. 修改数组元素
grades[0] = 88 # 将第一个学生的成绩从85改为88
print("修改后的成绩数组:", grades) # 输出: [88, 92, 78, 95, 88]
# 3. 获取数组长度
print("学生总数:", len(grades)) # 输出: 5
# 4. 遍历数组(使用for循环)
print("\n所有学生的成绩:")
for i in range(len(grades)): # 方法1:通过索引遍历
print(f"学生 {i+1} 的成绩: {grades[i]}")
print("\n使用enumerate遍历:")
for index, grade in enumerate(grades): # 方法2:更Pythonic的方式
print(f"学生 {index+1} 的成绩: {grade}")
示例2:一个简单的数组应用——计算平均分
def calculate_average(arr):
"""计算数组的平均值"""
if len(arr) == 0: # 处理空数组的情况
return 0
total = 0
for score in arr:
total += score # 累加每个元素
return total / len(arr) # 总和除以元素个数
# 使用示例
scores = [78, 85, 92, 67, 95]
average = calculate_average(scores)
print(f"成绩数组: {scores}")
print(f"平均分: {average:.2f}") # 格式化为两位小数
输出:
成绩数组: [78, 85, 92, 67, 95]
平均分: 83.40
实践练习
练习1:基础操作 - 数组元素统计
编写一个函数,接收一个数字数组,返回该数组中的最大值、最小值和总和。
def array_stats(numbers):
# 在这里编写你的代码
# 提示:先处理空数组的情况
# 遍历数组,比较更新最大值和最小值,同时累加总和
pass
# 测试用例
test_array = [3, 7, 1, 9, 4]
result = array_stats(test_array)
print(f"数组: {test_array}")
print(f"最大值: {result['max']}") # 预期: 9
print(f"最小值: {result['min']}") # 预期: 1
print(f"总和: {result['sum']}") # 预期: 24
练习2:进阶操作 - 数组反转
编写一个函数,将输入的数组原地反转(不使用额外的数组空间)。
def reverse_array(arr):
# 在这里编写你的代码
# 提示:使用两个指针,一个在开头,一个在末尾,交换它们指向的元素,然后逐步向中间移动
pass
# 测试用例
original = [1, 2, 3, 4, 5]
print(f"反转前: {original}")
reverse_array(original)
print(f"反转后: {original}") # 预期: [5, 4, 3, 2, 1]
练习3:挑战 - 二维数组遍历
以下是一个 3x4 的二维数组(矩阵),编写代码按行优先的顺序遍历并打印所有元素。
matrix = [
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]
]
# 你的遍历代码应该输出:
# 1 2 3 4
# 5 6 7 8
# 9 10 11 12
常见错误
-
索引越界(Index Out of Bounds)
arr = [1, 2, 3] print(arr[3]) # 错误!有效索引是0,1,2解决方案:始终记得数组长度是
n,有效索引是0到n-1。 -
混淆索引和元素值
arr = [10, 20, 30] # 错误:试图用元素值作为索引 index = 20 # 这是一个值,不是索引 print(arr[index]) # 可能导致意外结果或错误 -
忽略数组的连续性假设 在 Python 列表中,虽然元素在逻辑上是连续的,但实际存储的是对象的引用。对于真正的数组(如 C 语言的数组),必须存储相同大小的元素。
-
忘记数组长度固定(在某些语言中) 在 C、Java 等语言中,数组创建后长度不可变。Python 列表可以动态增长,但理解基础数组的固定长度特性对学习很重要。
小结
本课我们学习了数组的基础知识:
- 数组是一种存储相同类型元素的线性数据结构,元素在内存中连续存放。
- 通过索引(从0开始)可以在 O(1) 时间内访问任意元素。
- 数组的声明、初始化和遍历是所有操作的基础。
- 遍历数组通常使用
for循环,可以是索引遍历或元素遍历。 - 数组的主要优点是快速随机访问,主要缺点是固定大小(在非动态数组中)和插入/删除效率低(需要移动元素)。
掌握了数组,你就拥有了处理大量同类型数据的基本工具。下一课,我们将学习链表,了解另一种线性数据结构,它与数组有互补的特性。