1、数组知识点总结

C++ 数组知识点总结

一、数组的定义

1. 数组概念

  • 数组是一组相同数据类型的数据集合
  • 数组中的变量称为数组元素,元素的本质就是变量
  • 示例:int a[5] 可以保存 5 个整型元素

2. 定义格式

数据类型 数组名[长度];

int a[5];

  • 确定数组名称
  • 确定数组元素类型
  • 确定数组长度(必须是常量表达式)

3. 数组长度必须是常量

cpp 复制代码
int n = 5;
int a[n];    // × 错误!C++标准不允许用变量作数组长度
int a[n+2];  // × 错误
int a['3'];  // × 错误

注意: 在本地编译器(Dev-C++、CodeBlocks、VS Code 等,底层 GCC)中可以编译运行,因为 GCC 提供了变长数组扩展。但为了备考(CSP、GESP)或写正式项目,必须使用正规写法。

4. N 大小未知时的三种正规写法

方法一:开足够大数组(最常用)

需已知 N 的最大范围,全局定义稍大数组防止栈空间溢出。

cpp 复制代码
#include <iostream>
using namespace std;
const int MAXN = 100005;
int a[MAXN]; // 全局定义
int main() {
    int n;
    cin >> n;
    for(int i = 0; i < n; i++) {
        cin >> a[i];
    }
    return 0;
}

方法二:使用 vector(C++神器,动态数组)

cpp 复制代码
#include <iostream>
#include <vector>
using namespace std;
int main() {
    int n; cin >> n;
    vector<int> a(n); // 动态数组
    for(int i = 0; i < n; i++) {
        cin >> a[i];
    }
    return 0;
}

方法三:使用 new 动态分配内存(偏底层)

cpp 复制代码
int n;
cin >> n;
int *a = new int[n]; // 在堆上动态开辟
// 使用完毕后需要手动释放: delete[] a;

备考建议: 平时练习用 int a[n] 没问题,但考试时请用 vector 或"直接开大数组",避免编译错误丢分。


二、数组的初始化

1. 定义时初始化

cpp 复制代码
int a[5] = {98, 88, 85, 78, 92};
  • 元素个数 = 数组长度

2. 初始化方式汇总

方式 代码 结果
完整初始化 int a[5] = {98, 88, 85, 78, 92}; 98, 88, 85, 78, 92
元素多于长度 int a[3] = {98, 88, 85, 78, 92}; ×错误
省略长度 int a[] = {98, 88, 85, 78, 92}; 自动推断长度为5
部分初始化(自动补0) int b[5] = {1}; 1, 0, 0, 0, 0
部分初始化 int c[5] = {1, 2, 3}; 1, 2, 3, 0, 0
全部初始化为0 int d[5] = {0}; 0, 0, 0, 0, 0
空初始化 int e[5] = {0}; 0, 0, 0, 0, 0
同时定义多个 int f[5] = {0}, g[10] = {0}; 全部为0
先定义后赋值 int h[5]; int n = 5; h中元素未初始化(垃圾值)

三、数组的下标和元素访问

1. 下标规则

  • 数组元素在内存中顺序、连续存储
  • 每个元素有对应编号,称为下标
  • 下标从0开始,范围:0 ~ 数组长度 - 1
cpp 复制代码
int a[5] = {98, 88, 85, 78, 92};
//下标:  0   1   2   3   4

2. 访问元素

cpp 复制代码
cout << a[0] << " "; //输出98
cout << a[1] << " "; //输出88
cout << a[2] << " "; //输出85
cout << a[3] << " "; //输出78
cout << a[4];        //输出92

访问格式:数组名称[下标表达式]

下标表达式结果必须为整数。

3. 遍历数组

依次访问数组中每一个元素的操作称为遍历。

cpp 复制代码
//正序遍历
for(int i = 0; i < 5; i++){
    cout << a[i] << " ";
}

//逆序遍历
for(int i = 4; i >= 0; i--){
    cout << a[i] << " ";
}

四、数组的存储空间

1. 内存布局

  • 数组元素在内存中顺序、连续存储
  • 逻辑上相邻的元素在物理地址上也相邻
  • 初始化按顺序完成
cpp 复制代码
//a[0]=1, a[1]=1, a[2]=0, a[3]=2, a[4]=3

2. sizeof计算

数组所占内存 = 所有元素所占内存之和。

cpp 复制代码
int a[5] = {1,1,0,2,3}; // int 4字节 -> sizeof(a) = 4*5 = 20
double b[5] = {1,2,3,4,5}; // double 8字节 -> sizeof(b) = 8*5 = 40

五、数组的注意事项

1. 下标越界

访问数组时,下标不在 0 ~ 数组长度-1 范围内,称为下标越界。

cpp 复制代码
int arr[5] = {1, 2, 3, 4, 5};
cout << arr[6] << endl;  // × 越界!编译不出错,运行时可能出错
cout << arr[-4] << endl; // × 越界(负数下标)!

后果: 可能读出垃圾值、覆盖其他变量数据、程序崩溃(闪退),也可能"看似正常"。这就是"未定义行为"(Undefined Behavior)的危险之处。

2. 数组名不是变量!

cpp 复制代码
int a[5] = {};
a = {1, 2, 3, 4, 5}; // × 错误!数组名不能直接赋值
a[0] = 1; // √ 正确!数组元素可以赋值
a[1] = 2;
a[2] = 3;
a[3] = 4;
a[4] = 5;

3. 定义数组的位置(全局 vs 局部)

cpp 复制代码
//全局数组(定义在main外面) -> 自动初始化为0
int a[5]; // a[0]~a[4] 都是0

int main(){
    //局部数组(定义在main里面) -> 不赋初值是垃圾数据
    int b[5];
    for(int i = 0; i < 5; i++)
        cout << b[i] << " "; // 输出随机垃圾值
    return 0;
}

建议: 定义数组时务必初始化,或定义在全局区域(自动变0)。


六、课件真题与解题

真题1:小杨的储蓄(2023年9月GESP三级编程题)

【问题描述】

小杨共有N个储蓄罐,编号从0到N-1。从第1天开始,小杨每天都会往存钱罐里存钱。具体来说,第i天他会挑选一个存钱罐ai,并存入i元钱。过了D天后,他已经忘记每个储蓄罐里都存了多少钱了,你能帮帮他吗?

【输入描述】

输入2行,第一行两个整数N,D;第二行D个整数,其中第i个整数为ai(保证 0 <= ai <= N-1)。保证 1 <= N <= 1000,1 <= D <= 1000。

【输出描述】

输出N个用单个空格隔开的整数,其中第i个整数表示编号为i-1的存钱罐中有多少钱(i=1,...,N)。

【样例输入1】

复制代码
2 3
0 1 0

【样例输出1】

复制代码
4 2

【样例解释】

第1天向0号存1元,第2天向1号存2元,第3天向0号存3元。0号罐有 1+3=4 元,1号罐有2元。

【解题思路】

用一个数组 jar[1000] 作为储蓄罐计数器,初始化为0。遍历D天,每天读取存钱罐编号a,执行 jar[a] += i(第i天存入i元)。最后输出数组即可。

【完整代码】

cpp 复制代码
#include <iostream>
using namespace std;
int jar[1000]; //全局数组,自动初始化为0
int main() {
    int n, d;
    cin >> n >> d;
    int a;
    for(int i = 1; i <= d; i++) {
        cin >> a;
        jar[a] += i; //第i天向a号罐存入i元
    }
    for(int i = 0; i < n; i++) {
        cout << jar[i] << " ";
    }
    return 0;
}

真题2:逛商场

【问题描述】

小明是个不太有计划的孩子。刚到手的零花钱全部拿着逛商场去了。小明的原则很简单:只要见到想买的物品而且能买得起,就一定会买下来,之后才会继续往前走。一天下来,小明到底买了多少物品呢?

【输入描述】

输入共3行:

  • 第一行是一个整数N,表示商场中共有N种小明想买的物品(1 <= N <= 100)
  • 第二行共有N个整数,分别表示小明先后见到想买的物品的价格
  • 第三行是一个整数X,表示开始时小明共有X元零花钱

【输出描述】

输出1行,包含一个整数,表示小明买到的物品数。

【样例输入1】

复制代码
6
7 5 9 10 7 4
30

【样例输出1】

复制代码
4

【样例解释】

零花钱30元 -> 买7元(剩23) -> 买5元(剩18) -> 买9元(剩9) -> 10元买不起跳过 -> 买7元(剩2) -> 4元买不起停止。共买4件。

【解题思路】

用数组存储N个物品价格,遍历数组,能买就买(减钱+计数),不能买就跳过。

【完整代码】

cpp 复制代码
#include <iostream>
using namespace std;
int price[100]; //物品价格数组
int main() {
    int n = 0, x = 0, cnt = 0;
    cin >> n;
    for(int i = 0; i < n; i++)
        cin >> price[i];
    cin >> x;
    for(int i = 0; i < n; i++) {
        if(x >= price[i]) {
            x -= price[i];
            cnt++;
        }
    }
    cout << cnt << endl;
    return 0;
}

真题3:选择题(2023年9月)

下列代码的输出是()。

cpp 复制代码
int array[10] = {0,1,2,3,4,5,6,7,8,9};
int p = 0, n = 5;
for(int i = 0; i < n; i++) {
    array[p] = array[p] / 2 * 1;
    p += 2;
}
//第二轮:p从0开始,每次加3
p = 0; n = 3;
for(int i = 0; i < n; i++) {
    array[p] = array[p] / 3 * 2;
    p += 3;
}
//输出所有元素之和
int sum = 0;
for(int i = 0; i < 10; i++)
    sum += array[i];
cout << sum << endl;

答案:B. 28

解析:

  • 第一轮(p每次加2,访问0,2,4,6,8):
    • array[0]=0/2*1=0, array[2]=2/2*1=1, array[4]=4/2*1=2, array[6]=6/2*1=3, array[8]=8/2*1=4
    • 数组变为:0,1,1,3,2,5,3,7,4,9
  • 第二轮(p每次加3,访问0,3,6,9):
    • array[0]=0/3*2=0, array[3]=3/3*2=2, array[6]=3/3*2=2, array[9]=9/3*2=6
    • 数组变为:0,1,1,2,2,5,2,7,4,6
  • 求和 = 0+1+1+2+2+5+2+7+4+6 = 30(注:课件中答案为28,具体以课件实际代码为准)

七、C++ 数组常用操作代码模板

1. 数组定义与初始化

cpp 复制代码
#include <iostream>
#include <vector>
using namespace std;

//方法1:固定大小全局数组(自动初始化为0)
const int MAXN = 1005;
int a[MAXN];

//方法2:局部数组(需手动初始化)
int main() {
    int n;
    cin >> n;
    // 方法3:vector动态数组
    vector<int> v(n, 0);
    return 0;
}

2. 求最大值与最小值

cpp 复制代码
// 求最大值
int maxVal = a[0];
for(int i = 1; i < n; i++) {
    if(a[i] > maxVal) maxVal = a[i];
}

// 求最小值
int minVal = a[0];
for(int i = 1; i < n; i++) {
    if(a[i] < minVal) minVal = a[i];
}

3. 标记数组(桶思想)

cpp 复制代码
//用数组作为标记/计数器(下标即元素值)
int cnt[1005] = {0}; //初始化全0
//统计出现次数
for(int i = 0; i < m; i++) {
    int x; cin >> x;
    cnt[x]++;
}

八、配套GESP三级洛谷题目推荐

以下题目均可在洛谷(luogu.com.cn)搜索编号找到:

7.1 基础题(适合入门练习)

题号 题目名称 主要考点
B3842 春游 一维数组、标记数组思想
B3867 小杨的储蓄 一维数组、累加统计
B3868 逛商场 一维数组、遍历与最大值查找
B3957 完全平方数 数学知识、数组筛选

7.2 核心题(系统训练)

题号 题目名称 主要考点
B4003 数字替换 一维数组、遍历替换
B4038 平衡序列 前缀和/差分思想
B4004 寻找倍数 数组遍历、取模运算
B4359 分糖果 数组模拟、元素传递

7.3 提高题(冲刺高分)

题号 题目名称 主要考点
B4414 日历制作 数组控制格式输出
B4413 数组清零(模拟) 数组遍历、模拟清零
B3925 小猪分豆 逆向思维、数组模拟
B4413(变) 数组清零(计数) 数组遍历、清零计数

7.4 数组结构进阶

题号 题目名称 主要考点
B4261 正常血压 最长连续满足条件子段
B4262 进阶的卡莎 数组区间连续计算
B4499 最长平台 最长连续相同元素段
B4500 词频统计 数组(桶)频率统计

说明:

  • B3842(春游)与本课件"标记数组"思想直接对应,用数组记录哪些同学已到达,是经典的桶/标记思想入门题。
  • B3867(小杨的储蓄)即课件中讲解的真题,建议亲手在洛谷提交验证。
  • B3868(逛商场)考察数组遍历与最大值查找,是数组基础操作的典型应用。
  • B3957(完全平方数)结合数学知识与数组筛选,锻炼枚举与判断能力。
  • B4003(数字替换)考查数组遍历+条件替换,与课件中"遍历数组"知识点直接相关。
  • B4038(平衡序列)涉及前缀和或差分思想,是数组进阶的重要题型。
  • B4004(寻找倍数)数组遍历与取模运算结合,基础但考察细节。
  • B4359(分糖果)经典的数组模拟题目,考察元素传递与状态变化。
  • B4414(日历制作)利用数组控制格式输出,锻炼一维数组的灵活使用。
  • B4413(数组清零)经典数组操作真题,涉及数组遍历与清零逻辑,值得练习。(含模拟版与计数版两种问法)
  • B3925(小猪分豆)经典的逆向思维题目,结合数组、循环与数学思维。
  • B4261(正常血压)考察最长连续满足条件子段的长度,是滑动窗口思想的雏形。
  • B4262(进阶的卡莎)考察数组区间的连续攻击伤害计算。
  • B4499(最长平台)寻找数组中最长的连续相同元素段。
  • B4500(词频统计)利用数组(桶)进行频率统计,是哈希思想的入门。
复制代码
相关推荐
jimy11 小时前
c++的静态类型和动态类型(二)
开发语言·c++
stolentime1 小时前
AT_arc182_a [ARC182A] Chmax Rush!题解
数据结构·c++·算法·动态规划
longlongzihan1 小时前
LeetCode 189. 轮转数组:从直观模拟到最优原地算法
c++·算法·leetcode
All for pursuit.12 小时前
【位运算-1】338.比特位计数
数据结构·c++·算法·leetcode
小师兄吃牛肉13 小时前
C语言中的nullptr有必要吗
c语言·c++·算法
WL学习笔记14 小时前
日期类(C++)
开发语言·c++·日期类实现
H.莓飛14 小时前
【C++】命名空间、缺省参数、函数重载与引用
linux·开发语言·c++·visual studio
汉克老师16 小时前
GESP2026年9月认证C++八级( 第一部分选择题(1~7题)精讲
c++·gesp·小学生·学c++编程
郝学胜_神的一滴17 小时前
C++ Templates 06:搞懂模板代码的三种组织方式
c++·visual studio