Skip to content

数组

约 4580 字大约 15 分钟

2025-06-25

数组与数组下标

数组

在C++中,数组是存储相同类型元素的连续内存空间,通过下标访问元素,是信奥赛中处理批量数据的核心工具(如存储数列、矩阵、图的邻接表等)。

一、数组的基本概念

基本概念

1. 定义与初始化

数组定义的基本语法:

c++
数据类型 数组名[元素个数];

初始化方式

  • 完全初始化:指定所有元素的值
c++
int arr[5] = {1, 2, 3, 4, 5};  // 5个元素分别为1~5
  • 部分初始化:未指定的元素自动为0(数值类型)或空(字符类型)
c++
int arr[5] = {1, 2};  // 元素为1,2,0,0,0
  • 省略长度初始化:编译器根据初始值数量自动确定长度
c++
int arr[] = {1, 2, 3};  // 长度为3的数组

2. 数组的特性

  • 同类型元素:数组中所有元素必须是同一数据类型(如intdouble)。
  • 连续内存:元素在内存中连续存储,可通过地址偏移快速访问。
  • 固定长度:数组长度在定义时确定,且不能动态修改(信奥赛中需提前预估最大需求)。

二、数组下标(索引)

下标

1. 下标的作用与语法

数组通过下标(索引) 访问元素,语法:

c++
数组名[下标]

示例

c++
int arr[5] = {10, 20, 30, 40, 50};
cout << arr[0];  // 输出第1个元素10
arr[2] = 300;    // 修改第3个元素为300

2. 下标规则(信奥赛核心考点)

  • 从0开始:数组第1个元素的下标为0,第n个元素的下标为n-1
    长度为len的数组,下标范围是 0 ~ len-1
  • 越界风险:访问下标 <0≥len 的元素会导致数组越界,可能读取垃圾值或修改其他内存数据,导致程序崩溃(信奥赛高频错误)。
    错误示例:
    c++
    int arr[5] = {1,2,3,4,5};
    cout << arr[5];  // 越界访问(下标5≥长度5),结果未知

三、数组的遍历(信奥赛必会技能)

遍历

遍历数组即依次访问所有元素,通常用for循环结合下标实现:

1. 基本遍历

c++
int arr[5] = {1,2,3,4,5};
for (int i = 0; i < 5; i++) {  // i从0到4(下标范围)
    cout << arr[i] << " ";
}
// 输出:1 2 3 4 5

2. 用常量定义长度(推荐)

为避免硬编码长度导致越界,用const常量定义长度:

c++
const int N = 5;  // 定义常量表示长度
int arr[N] = {1,2,3,4,5};
for (int i = 0; i < N; i++) {  // 用N控制循环范围,修改方便
    cout << arr[i] << " ";
}

四、多维数组(以二维数组为例)

二维数组

信奥赛中常用二维数组表示矩阵、表格等,本质是“数组的数组”。

1. 定义与初始化

c++
// 定义3行4列的二维数组
int matrix[3][4] = {
    {1, 2, 3, 4},   // 第0行
    {5, 6, 7, 8},   // 第1行
    {9, 10, 11, 12} // 第2行
};

2. 二维数组的下标

二维数组通过行下标列下标访问元素,均从0开始:

c++
cout << matrix[1][2];  // 访问第1行第2列的元素7
matrix[0][3] = 100;    // 修改第0行第3列的元素为100

3. 二维数组的遍历

用嵌套for循环遍历,外层控制行,内层控制列:

c++
const int ROW = 3, COL = 4;
int matrix[ROW][COL] = {{1,2,3,4}, {5,6,7,8}, {9,10,11,12}};

for (int i = 0; i < ROW; i++) {       // 行下标i
    for (int j = 0; j < COL; j++) {   // 列下标j
        cout << matrix[i][j] << " ";
    }
    cout << endl;  // 每行结束换行
}

五、信奥赛常见应用

应用

  1. 存储批量数据:如统计多个学生的成绩、多个点的坐标等。

    c++
    const int STUDENTS = 50;
    int scores[STUDENTS];  // 存储50个学生的成绩
  2. 频率计数:用数组下标表示数值,元素值表示出现次数(哈希思想)。

    c++
    int freq[10] = {0};  // 统计0~9的出现次数
    int nums[] = {1,3,5,3,3,7};
    for (int x : nums) {
        freq[x]++;  // 对应数字的计数+1
    }
    // freq[3]的值为3(数字3出现3次)
  3. 滑动窗口:在数组中通过下标范围截取子数组(如求连续子数组的最大和)。

六、避坑指南

注意

  1. 数组越界:始终确保下标在 0 ~ len-1 范围内,尤其循环中注意终止条件(如i < len而非i <= len)。

  2. 未初始化的数组:局部数组(如函数内定义的数组)若未初始化,元素值为随机垃圾值,需手动初始化(如int arr[5] = {0};)。

  3. 二维数组的长度:C++中二维数组的列数必须显式指定,行数可省略(如int arr[][4] = {{1,2}, {3,4}};)。

  4. 大数组的定义位置:过大的数组(如int arr[1000000])应定义在函数外(全局变量),避免栈内存溢出。

七、实战:数组求最值

数组求最值

题目:输入5个整数,找出其中的最大值和最小值。

c++

要点

数组是信奥赛中处理数据的基础结构,熟练掌握下标操作和遍历技巧是解决批量数据问题的关键。尤其要注意下标越界和初始化问题,这是新手最易犯错的点。

数组的读入与输出

数组的读入与输出

在C++中,数组的读入与输出是信奥赛的基础操作,通常结合循环结构实现批量数据的处理。

一、一维数组的读入与输出

一维数组的读入与输出

一维数组的读写核心是通过下标循环访问每个元素,配合cin(读入)和cout(输出)完成操作。

1. 基本方法

c++

2. 常见变体

  • 输出格式控制:根据需求调整分隔符(如逗号、换行):

    c++
    // 每行输出2个元素
    for (int i = 0; i < N; i++) {
        cout << arr[i];
        if ((i + 1) % 2 == 0) {  // 每2个元素换行
            cout << endl;
        } else {
            cout << " ";         // 否则用空格分隔
        }
    }
  • 从文件读入/输出到文件(信奥赛常用,需包含<fstream>):

    c++

二、二维数组的读入与输出

二维数组的读入与输出

二维数组的读写需用嵌套循环,外层循环控制行,内层循环控制列。

1. 基本方法

c++

2. 注意事项

  • 输入顺序:通常按行输入(先输入第0行所有元素,再输入第1行,以此类推)。
  • 列数固定:C++中二维数组的列数必须显式指定,行数可省略(如int matrix[][4] = ...)。

三、信奥赛高频与技巧

高频与技巧

1. 未知长度的数组处理

若输入数据量未知(但不超过预设最大值),可先读入元素总数,再动态控制循环:

c++

2. 字符数组的读写

字符数组(字符串)可直接用cin/cout整行读写(无需循环):

c++

3. 数组输出的效率优化

当数组元素过多(如1e5个),用cout输出可能较慢,可改用printf(需包含<cstdio>):

c++
#include <cstdio>
// ...
for (int i = 0; i < N; i++) {
    printf("%d ", arr[i]);  // 比cout快,适合大数据量
}

四、避坑指南

避坑指南

  1. 下标越界:循环条件必须严格控制在 0 ~ len-1,例如:

    c++
  2. 输入缓冲区残留:连续读写不同类型数据时(如先读整数再读字符串),需用cin.ignore()清除残留的换行符,否则可能读入空内容。

  3. 大数组的定义:容量超过1e5的数组应定义为全局变量(在main函数外),避免栈内存溢出:

    c++

五、实战:统计数组中偶数的个数并输出

统计数组中偶数的个数并输出

c++

要点

数组的读入与输出是信奥赛中处理批量数据的基础,核心是通过循环控制下标,确保访问范围合法。熟练掌握不同维度数组的读写技巧,以及输入输出的效率优化方法,能为解决复杂问题打下坚实基础。

二维数组与多维数组

二维数组与多维数组

在C++中,二维数组和多维数组是处理结构化数据(如矩阵、表格、三维模型坐标等)的重要工具,在信奥赛中广泛应用于动态规划、图论、矩阵运算等场景。

一、二维数组(核心重点)

二维数组

二维数组可理解为“数组的数组”,本质是按行优先顺序存储的连续内存块,适合表示矩阵、表格等二维结构。

1. 定义与初始化

基本语法

c++
数据类型 数组名[行数][列数];

初始化方式

  • 完全初始化(按行分组):
c++
// 3行4列的二维数组,每行元素用{}包裹
int matrix[3][4] = {
    {1, 2, 3, 4},   // 第0行
    {5, 6, 7, 8},   // 第1行
    {9, 10, 11, 12} // 第2行
};
  • 部分初始化(未指定元素默认为0):
c++
int matrix[3][4] = {{1}, {5,6}, {9}};
// 结果:
// 第0行:1, 0, 0, 0
// 第1行:5, 6, 0, 0
// 第2行:9, 0, 0, 0
  • 省略行数(列数必须指定):
c++
int matrix[][4] = {{1,2}, {3,4,5}, {6}};  // 自动推断行数为3

2. 元素访问与下标

二维数组通过行下标列下标访问元素,两者均从0开始:

c++
int val = matrix[1][2];  // 访问第1行第2列元素(值为7)
matrix[0][3] = 100;      // 修改第0行第3列元素为100

关键规则

  • 行数范围:0 ~ 行数-1
  • 列数范围:0 ~ 列数-1
  • 越界风险:访问超出范围的下标会导致内存错误(信奥赛高频坑点)。

3. 遍历与操作

需用嵌套循环遍历,外层控制行,内层控制列:

c++

二、多维数组(以三维为例)

三维数组

三维及以上数组是二维数组的扩展,用于表示更高维度的数据(如立体坐标、时间序列矩阵等),信奥赛中较少直接使用,但原理相通。

1. 定义与初始化

基本语法

c++
数据类型 数组名[维度1][维度2][维度3];

示例(三维数组,可理解为“多个二维矩阵”):

c++
// 2个3行4列的三维数组(如2个矩阵)
int cube[2][3][4] = {
    { {1,2,3,4}, {5,6,7,8}, {9,10,11,12} },  // 第0个矩阵
    { {13,14,15,16}, {17,18,19,20}, {21,22,23,24} }  // 第1个矩阵
};

2. 元素访问与遍历

通过三重下标访问,遍历需三重循环:

c++

三、信奥赛核心应用

核心应用

1. 矩阵运算(如加法、乘法)

c++

2. 动态规划(DP表)

二维数组常用于存储中间状态(如最长公共子序列、背包问题):

c++
// 二维DP表,dp[i][j]表示前i个物品、容量j时的最大价值
int dp[101][1001] = {0};  // 假设100个物品,容量1000

3. 图论(邻接矩阵)

用二维数组表示图中顶点间的连接关系:

c++
const int MAX_V = 100;
int graph[MAX_V][MAX_V];  // graph[i][j]表示i到j的边权

四、内存存储与性能特性

内存存储与性能特性

  • 行优先存储:二维数组在内存中按行连续存储(先存第0行所有元素,再存第1行,以此类推)。访问时按行遍历效率更高(缓存友好)。
  • 内存占用int a[m][n] 占用 m * n * sizeof(int) 字节,大数组需注意内存限制(如int a[1000][1000] 约4MB,可接受;a[10000][10000] 约400MB,需谨慎)。

五、避坑指南

避坑指南

  1. 列数必须显式指定:定义二维数组时,列数不能省略(行数可省略):
c++
int a[3][4];  // 正确
int a[][4];   // 正确(行数可推断)
int a[3][];   // 错误(列数必须指定)
  1. 避免过大的局部二维数组:函数内定义的大数组(如int a[10000][10000])可能导致栈溢出,应定义为全局变量或使用动态分配。

  2. 多维数组的越界检查:嵌套循环的终止条件需严格控制(如i < ROW而非i <= ROW),避免访问非法内存。

  3. 初始化不完全的陷阱:局部二维数组若未初始化,元素值为随机垃圾值,需手动初始化(如int a[3][4] = {0}; 可将所有元素置0)。

六、实战:二维数组找最大值及位置

二维数组找最大值及位置

题目:在3x4的矩阵中找到最大值,并输出其值及行、列下标。

c++

要点

二维数组是信奥赛中处理结构化数据的核心工具,多维数组则是其扩展。掌握其定义、初始化、访问及遍历技巧,能有效解决矩阵运算、动态规划、图论等领域的问题。需特别注意下标范围和内存限制,避免常见错误。