5 一维数组精讲
正在加载视频播放器…
GESP C++ 三级:一维数组精讲
核心目标:掌握一维数组的定义、初始化、访问与遍历,能熟练进行求最值、求和、计数、查找、逆序等操作,理解数组越界与变长数组的限制,能结合枚举法和模拟法解决综合问题。
数组是一排连续的同类型存储单元,下标从0开始,长度必须是编译期常量;累加初值为0,累乘初值为1,最值初值取第一个元素。
一、数组的基本概念
1. 什么是数组
数组是存储固定大小、相同数据类型元素的集合,这些元素在内存中是连续存放的。
可以把数组想象成一排储物柜:每个柜子有唯一编号(下标),从0开始计数,通过编号存取数据。
2. 数组的核心特性
| 特性 | 说明 |
|---|---|
| 相同类型 | 所有元素类型必须一致 |
| 连续存储 | 元素在内存中地址连续,按元素大小递增 |
| 下标从0开始 | 长度为 n 的数组,有效下标是 0 ~ n-1 |
| 长度固定 | 静态数组长度必须是编译期常量 |
3. 考纲限制:不包括变长数组
新版GESP考纲明确:三级“C++一维数组基本应用”剔除变长数组考查。即数组长度必须是编译期常量,不能是变量。
int arr[5]; // ✅ 正确,5是常量
const int N = 10;
int arr2[N]; // ✅ 正确,N是const常量
int n = 10;
int arr3[n]; // ❌ 错误,n是变量
二、数组的定义与初始化
1. 基本语法
数据类型 数组名[长度];
int scores[5]; // 长度为5的整型数组
double prices[10]; // 长度为10的浮点型数组
char letters[26]; // 长度为26的字符数组
2. 初始化的四种方式
方式一:完全列表初始化
int arr[5] = {10, 20, 30, 40, 50};
方式二:部分初始化,其余自动补0
int arr[5] = {1, 2}; // arr = {1, 2, 0, 0, 0}
方式三:自动推导长度
int arr[] = {1, 2, 3, 4}; // 长度自动确定为4
方式四:全部初始化为0(最常用)
int arr[100] = {0}; // 100个元素全部为0
考试提示:
int arr[100] = {0};是大数组清零的经典写法,务必记住。
3. 数组占用的字节数
int arr[5]; // 5 × 4 = 20 字节
long long arr2[3]; // 3 × 8 = 24 字节
char arr3[10]; // 10 × 1 = 10 字节
三、数组元素的访问与遍历
1. 访问单个元素
int arr[5] = {10, 20, 30, 40, 50};
cout << arr[0]; // 10
cout << arr[4]; // 50
arr[2] = 99; // 修改第三个元素
2. 标准遍历
for (int i = 0; i < n; i++) {
cout << arr[i] << " ";
}
关键:循环条件必须用 i < n,不能用 i <= n,否则会访问越界位置。
3. 逆序遍历
for (int i = n - 1; i >= 0; i--) {
cout << arr[i] << " ";
}
4. 范围 for 循环(C++11,了解)
for (int x : arr) {
cout << x << " ";
}
一级/二级不要求,三级了解即可。
5. 读入数组
int n;
cin >> n;
int arr[100];
for (int i = 0; i < n; i++) {
cin >> arr[i];
}
四、数组与循环结合的常见操作
1. 求和
int sum = 0;
for (int i = 0; i < n; i++) {
sum += arr[i];
}
2. 求最大值与最小值
int maxv = arr[0]; // 初值取第一个元素
for (int i = 1; i < n; i++) {
if (arr[i] > maxv) maxv = arr[i];
}
int minv = arr[0];
for (int i = 1; i < n; i++) {
if (arr[i] < minv) minv = arr[i];
}
3. 计数(统计满足条件的元素个数)
int cnt = 0;
for (int i = 0; i < n; i++) {
if (arr[i] % 2 == 0) cnt++; // 统计偶数个数
}
4. 查找
查找第一个出现的位置:
int pos = -1;
for (int i = 0; i < n; i++) {
if (arr[i] == target) {
pos = i;
break;
}
}
查找最后一次出现的位置:
int pos = -1;
for (int i = 0; i < n; i++) {
if (arr[i] == target) pos = i; // 不break,继续找
}
5. 逆序
for (int i = 0, j = n - 1; i < j; i++, j--) {
int t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
6. 频次统计(桶数组)
当元素范围有限且连续时,可以用数组作“桶”统计频次。
const int M = 101; // 元素范围 0~100
int bucket[M] = {0};
for (int i = 0; i < n; i++) {
bucket[arr[i]]++; // 统计每个值出现的次数
}
// 输出每个值出现的次数
for (int v = 0; v <= 100; v++) {
if (bucket[v] > 0) {
cout << v << "出现了" << bucket[v] << "次" << endl;
}
}
7. 数组元素的筛选与标记
// 将不能被2整除也不能被3整除的元素置为0
for (int i = 0; i < n; i++) {
if (arr[i] % 2 != 0 && arr[i] % 3 != 0) {
arr[i] = 0;
}
}
五、数组与枚举法、模拟法结合
1. 枚举法:遍历数组找满足条件的元素
// 输出数组中所有大于平均值的元素
int sum = 0;
for (int i = 0; i < n; i++) sum += arr[i];
double avg = 1.0 * sum / n;
for (int i = 0; i < n; i++) {
if (arr[i] > avg) {
cout << arr[i] << " ";
}
}
2. 模拟法:数组清零 [GESP202509 三级] 数组清零
题目描述:给定n个非负整数数组,每次操作找到最大元素(多个取下标最大的),再找到最小非零元素,将最大值减去最小值,重复直到数组全为0,求操作次数。
解题思路:直接模拟每一步,忠实复现规则。
#include <iostream>
using namespace std;
int main() {
int n;
cin >> n;
int a[105];
for (int i = 0; i < n; i++) cin >> a[i];
int ops = 0;
while (true) {
// 检查是否全部为0
bool allZero = true;
for (int i = 0; i < n; i++) {
if (a[i] != 0) { allZero = false; break; }
}
if (allZero) break;
// 找最大元素(多个取下标最大的)
int maxIdx = -1;
for (int i = 0; i < n; i++) {
if (a[i] > 0) {
if (maxIdx == -1 || a[i] >= a[maxIdx]) {
maxIdx = i; // 注意用 >= 取下标最大的
}
}
}
// 找最小非零元素
int minVal = 1000000000;
for (int i = 0; i < n; i++) {
if (a[i] > 0 && a[i] < minVal) {
minVal = a[i];
}
}
// 最大值减去最小值
a[maxIdx] -= minVal;
ops++;
}
cout << ops << endl;
return 0;
}
模拟法要点:每一步都要忠实复现规则——“找最大(多个取下标最大的)”“找最小非零”“相减”三步不能少、顺序不能乱。
3. 综合应用:[GESP202512 三级] 小杨的智慧购物
题目要求:从大量商品信息中筛选出每种文具的最低价格,计算购买所有M种文具的总花费。
思路:用数组存储每种文具的最低价格,遍历所有商品,更新最低价,最后求和。
// 伪代码思路
int minPrice[1005]; // 每种文具的最低价格
// 初始化为一个大数
for (int i = 1; i <= M; i++) minPrice[i] = 1000000000;
// 遍历商品,更新最低价
for (int i = 0; i < N; i++) {
int type, price;
cin >> type >> price;
if (price < minPrice[type]) {
minPrice[type] = price;
}
}
// 求和
int total = 0;
for (int i = 1; i <= M; i++) {
total += minPrice[i];
}
cout << total << endl;
六、数组排序与查找(了解)
三级考纲涉及排序和查找的基本概念,但一维数组模块中主要考查线性查找。
1. 线性查找
int find(int arr[], int n, int target) {
for (int i = 0; i < n; i++) {
if (arr[i] == target) return i;
}
return -1;
}
2. 冒泡排序(了解)
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < n - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
int t = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = t;
}
}
}
3. 二分查找(了解)
前提:数组必须已排序。
int binarySearch(int arr[], int n, int target) {
int low = 0, high = n - 1;
while (low <= high) {
int mid = (low + high) / 2;
if (arr[mid] == target) return mid;
else if (arr[mid] < target) low = mid + 1;
else high = mid - 1;
}
return -1;
}
七、易错点汇总
1. 数组越界(最高频错误)
长度为 n 的数组,有效下标是 0 ~ n-1。访问 arr[n] 或负下标都是越界访问,会导致未定义行为(可能崩溃、数据错乱,也可能表面正常)。
int arr[5];
for (int i = 0; i <= 5; i++) { // ❌ i=5时越界
arr[i] = 0;
}
for (int i = 0; i < 5; i++) { // ✅ 正确
arr[i] = 0;
}
注意:数组越界不会产生编译错误,编译器看不出来,程序可能正常结束,也可能崩溃。
2. 数组长度必须是常量
int n = 10;
int arr[n]; // ❌ 标准C++不允许(变长数组)
int arr[10]; // ✅ 正确
const int N = 10;
int arr[N]; // ✅ 正确
3. 累加/累乘变量初始化错误
- 求和:
sum初始化为 0。 - 求积:
prod初始化为 1。 - 初始值错误会导致整个运算结果全错。
int sum = 0; // ✅
int prod = 1; // ✅
int sum2; // ❌ 未初始化,结果不确定
4. 最值变量初始化错误
int maxv = arr[0]; // ✅ 取第一个元素
int maxv = 0; // ❌ 如果数组全是负数,结果错误
int maxv = -1000000000; // ✅ 也可以取足够小的数
5. 查找时忘记保存结果
// 查找最后一次出现的位置
int pos = -1;
for (int i = 0; i < n; i++) {
if (arr[i] == target) pos = i; // ✅ 不break,持续更新
}
6. 数组清零时忘记全部置0
int arr[5] = {0}; // ✅ 全部为0
int arr[5]; // ❌ 未初始化,值是随机的
7. 循环边界错误
for (int i = 0; i < n; i++) // ✅
for (int i = 0; i <= n; i++) // ❌ 越界
for (int i = 1; i <= n; i++) // ⚠️ 如果数组从0开始存储,会漏掉arr[0]
8. 数组大小不够
int n;
cin >> n; // 如果 n 可能达到 1000
int arr[100]; // ❌ 数组太小
int arr[1005]; // ✅ 留足余量
9. 浮点数比较
if (arr[i] == 3.14) // ❌ 可能不成立
if (fabs(arr[i] - 3.14) < 1e-6) // ✅ 了解即可
10. 输出格式错误
注意空格和换行,严格按题目要求输出。GESP评测系统要求输出与标准答案完全一致。
八、阅读程序写结果
题目 1
int arr[5] = {1, 2, 3, 4, 5};
int sum = 0;
for (int i = 0; i < 5; i += 2) {
sum += arr[i];
}
cout << sum << endl;
输出:
9
解释:arr[0]+arr[2]+arr[4] = 1+3+5 = 9。
题目 2
int arr[6] = {1, 2, 3, 4, 5, 6};
for (int i = 0; i < 6; i += 2) {
int t = arr[i];
arr[i] = arr[i+1];
arr[i+1] = t;
}
for (int i = 0; i < 6; i++) cout << arr[i] << " ";
输出:
2 1 4 3 6 5
解释:交换相邻两个元素。
题目 3
int arr[5] = {3, 1, 4, 1, 5};
int cnt = 0;
for (int i = 0; i < 5; i++) {
if (arr[i] > 2) cnt++;
}
cout << cnt << endl;
输出:
3
解释:大于2的元素有3、4、5,共3个。
题目 4
int arr[5] = {5, 3, 8, 1, 9};
int maxv = arr[0];
for (int i = 1; i < 5; i++) {
if (arr[i] > maxv) maxv = arr[i];
}
cout << maxv << endl;
输出:
9
题目 5
int arr[6] = {1, 2, 3, 4, 5, 6};
for (int i = 0, j = 5; i < j; i++, j--) {
int t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
for (int i = 0; i < 6; i++) cout << arr[i] << " ";
输出:
6 5 4 3 2 1
九、速查表
| 需求 | 写法 |
|---|---|
| 定义数组 | int arr[10]; |
| 全部初始化为0 | int arr[10] = {0}; |
| 访问元素 | arr[i] |
| 遍历 | for (int i = 0; i < n; i++) |
| 逆序遍历 | for (int i = n-1; i >= 0; i--) |
| 求和 | sum = 0; for (...) sum += arr[i]; |
| 求最大值 | maxv = arr[0]; for (...) if (arr[i] > maxv) maxv = arr[i]; |
| 求最小值 | minv = arr[0]; for (...) if (arr[i] < minv) minv = arr[i]; |
| 计数 | cnt = 0; for (...) if (条件) cnt++; |
| 查找 | for (...) if (arr[i] == target) { pos = i; break; } |
| 逆序 | for (i=0,j=n-1; i<j; i++,j--) swap(arr[i], arr[j]); |
| 桶统计 | int bucket[M] = {0}; for (...) bucket[arr[i]]++; |
| 冒泡排序 | 双重循环,相邻比较交换 |
| 二分查找 | low=0, high=n-1; while (low <= high) |
十、总结
一维数组是GESP三级从基础语法迈向算法应用的关键知识点。
- 数组是连续存储的同类型元素集合,下标从0开始。
- 数组长度必须是编译期常量,三级不考变长数组。
- 遍历用
for (i=0; i<n; i++),边界用<不用<=。 - 数组越界不会产生编译错误,但会导致未定义行为。
- 求和初值为0,求积初值为1,最值初值取第一个元素。
- 查找第一个出现的位置用
break,查找最后出现的位置不用break。 - 桶数组适合范围有限的频次统计。
- 模拟法的关键是忠实复现规则,每一步都不能少、不能乱。
- 数组大小要留足余量,防止越界。
- 输出格式严格按题目要求,不多空格、不多换行。
复习建议:先把数组的定义、初始化、遍历、求和、求最值、计数六个基础模板写熟,重点体会模拟法和枚举法在数组中的应用。考试中先确定数组长度和存储方式,再写遍历循环,最后检查边界和输出格式。