20260227 120004 Cpp 入门第五课

20260227_120004_CPP_入门第五课.md

第五章:批量存储数据——数组

你好!欢迎来到第五章!在前面的章节,我们学会了用变量存储单个数据,比如一个年龄、一个成绩。但如果要存储全班50个人的成绩,难道要定义50个变量吗?那太麻烦了!这时候就需要数组——它可以一次性定义多个相同类型的变量,就像一排带编号的柜子,每个柜子里可以放一个数据。这一章我们就来学习如何使用数组。


5.1 一维数组

5.1.1 数组程序范例

先看一个简单的例子:定义一个能装5个整数的数组,然后给它们赋值并输出。

#include <iostream>
using namespace std;

int main() {
    int a[5];          // 定义一个数组,名字叫a,里面可以放5个整数

    // 给数组的每个元素赋值
    a[0] = 10;         // 第一个格子(下标0)放10
    a[1] = 20;         // 第二个格子(下标1)放20
    a[2] = 30;
    a[3] = 40;
    a[4] = 50;

    // 输出数组的每个元素
    cout << a[0] << " " << a[1] << " " << a[2] << " " << a[3] << " " << a[4] << endl;

    return 0;
}

运行结果

10 20 30 40 50

5.1.2 数组的用法

什么是数组?

数组就是一组相同类型的数据的集合,它们在内存中连续存放。每个数据叫做数组元素,通过下标(也叫索引)来访问。下标从0开始编号。

  • 定义格式:数据类型 数组名[元素个数];
    例如:int score[5]; 定义了一个名为score的数组,可以存5个整数。

  • 访问元素:数组名[下标],下标范围是0到元素个数-1。
    例如:score[0] = 98; 给第一个元素赋值。

数组的初始化

可以在定义时直接赋值:

int a[5] = {10, 20, 30, 40, 50};   // 全部初始化
int b[] = {1, 2, 3, 4, 5};          // 不写个数,编译器自动计算为5
int c[5] = {1, 2};                   // 只给前两个赋值,后面3个默认为0
使用循环遍历数组

数组通常和循环一起使用,因为可以用循环变量作为下标。

int a[5] = {10, 20, 30, 40, 50};
for (int i = 0; i < 5; i++) {
    cout << a[i] << " ";   // 依次输出每个元素
}
数组的注意事项
  • 下标不能越界,比如定义 int a[5];,只能使用 a[0]a[4],使用 a[5] 会导致未定义行为(可能程序崩溃)。
  • 数组一旦定义,大小不能改变。

5.1.3 编程实例讲解

实例1:从键盘输入5个整数,求它们的和与平均值
#include <iostream>
using namespace std;

int main() {
    int a[5];
    int sum = 0;

    cout << "请输入5个整数:";
    for (int i = 0; i < 5; i++) {
        cin >> a[i];          // 输入存到数组
        sum += a[i];          // 累加
    }

    double avg = (double)sum / 5;   // 平均值
    cout << "和:" << sum << ",平均值:" << avg << endl;

    return 0;
}
实例2:找出数组中的最大值和最小值
#include <iostream>
using namespace std;

int main() {
    int a[5] = {34, 67, 12, 89, 45};
    int max = a[0];   // 假设第一个是最大值
    int min = a[0];   // 假设第一个是最小值

    for (int i = 1; i < 5; i++) {   // 从第二个开始比较
        if (a[i] > max) {
            max = a[i];
        }
        if (a[i] < min) {
            min = a[i];
        }
    }

    cout << "最大值:" << max << endl;
    cout << "最小值:" << min << endl;

    return 0;
}
实例3:数组元素逆序(把数组反过来)
#include <iostream>
using namespace std;

int main() {
    int a[5] = {1, 2, 3, 4, 5};
    int temp;

    // 交换对称位置的元素
    for (int i = 0; i < 5 / 2; i++) {
        temp = a[i];
        a[i] = a[4 - i];   // 4是最后一个元素的下标
        a[4 - i] = temp;
    }

    cout << "逆序后的数组:";
    for (int i = 0; i < 5; i++) {
        cout << a[i] << " ";
    }
    cout << endl;

    return 0;
}

运行结果:5 4 3 2 1

5.1.4 阶段性编程练习

  1. 练习1:定义一个包含10个整数的数组,用循环给数组赋值为1到10,然后输出。
  2. 练习2:输入8个整数,存入数组,然后输出所有大于平均数的数。
  3. 练习3:输入10个整数,查找某个数(由用户输入)是否在数组中,如果在,输出它的位置(下标),否则输出“未找到”。
  4. 练习4:输入一个正整数n(≤20),再输入n个整数,将它们从小到大输出(可以先不管排序,直接输出,排序下一节学)。但这里可以先练习用两个循环选择最小输出,或者简单用冒泡排序提前体验。

5.2 数组排序

排序就是把一组数按从小到大(升序)或从大到小(降序)排列。我们这里学习最简单的冒泡排序

5.2.1 排序程序范例

#include <iostream>
using namespace std;

int main() {
    int a[5] = {34, 67, 12, 89, 45};
    int n = 5;

    // 冒泡排序
    for (int i = 0; i < n - 1; i++) {           // 外层循环控制比较的轮数
        for (int j = 0; j < n - 1 - i; j++) {   // 内层循环控制每轮比较的次数
            if (a[j] > a[j + 1]) {               // 如果前一个比后一个大,交换
                int temp = a[j];
                a[j] = a[j + 1];
                a[j + 1] = temp;
            }
        }
    }

    cout << "排序后的数组:";
    for (int i = 0; i < n; i++) {
        cout << a[i] << " ";
    }
    cout << endl;

    return 0;
}

运行结果:12 34 45 67 89

5.2.2 数组排序的用法

冒泡排序的原理

想象有一排泡泡,轻的往上浮,重的往下沉。冒泡排序就是每次比较相邻的两个数,如果顺序不对(比如前大后小,我们要升序),就交换它们。这样每一轮都会把最大的数“沉”到最后。

  • 第一轮:从第一个元素开始,依次比较相邻的两个,如果前>后,交换。第一轮结束后,最大的数就到了最后。
  • 第二轮:再从第一个开始,比较到倒数第二个(因为最后一个已经最大了),把第二大的数放到倒数第二。
  • 重复n-1轮,所有数就排好了。
代码解释
  • 外层循环 i 控制轮数,一共需要 n-1 轮(因为最后剩下一个不用排)。
  • 内层循环 j 控制比较范围,每一轮比较的范围逐渐缩小,因为末尾已经排好的元素不用再比。所以 j 从0到 n-1-i。
  • 如果 a[j] > a[j+1],交换。
其他排序方法(拓展了解)
  • 选择排序:每一轮找到最小(或最大)的元素,放到前面。
  • 插入排序:像打扑克牌,每次把一张牌插入到已排好序的手牌中。

但初学者先掌握冒泡排序就好。

5.2.3 编程实例讲解

实例4:输入n个数,升序输出
#include <iostream>
using namespace std;

int main() {
    int n;
    cout << "请输入数字个数:";
    cin >> n;
    int a[100];   // 假设最多100个数

    cout << "请输入" << n << "个整数:";
    for (int i = 0; i < n; i++) {
        cin >> a[i];
    }

    // 冒泡排序
    for (int i = 0; i < n - 1; i++) {
        for (int j = 0; j < n - 1 - i; j++) {
            if (a[j] > a[j + 1]) {
                int temp = a[j];
                a[j] = a[j + 1];
                a[j + 1] = temp;
            }
        }
    }

    cout << "排序后:";
    for (int i = 0; i < n; i++) {
        cout << a[i] << " ";
    }
    cout << endl;

    return 0;
}
实例5:降序排序(从大到小)

只需要把比较条件 a[j] > a[j+1] 改成 a[j] < a[j+1] 即可。

if (a[j] < a[j + 1]) {   // 如果前一个小于后一个,交换,让大的往前移
    // 交换
}
实例6:对字符串数组排序(了解)

数组也可以是字符串类型,比如 string names[5]; 排序方法和整数类似,直接用 >< 比较字符串(按字典序)。

5.2.4 阶段性编程练习

  1. 练习1:输入10个整数,用冒泡排序将它们从大到小输出。
  2. 练习2:输入n个学生的成绩,排序后输出,并输出最高分和最低分。
  3. 练习3:用选择排序实现升序排列(自学选择排序算法并实现)。
  4. 练习4:输入n个整数,去掉重复的数字后输出(提示:可以先排序,然后输出时跳过相邻相同的)。

5.3 二维数组

5.3.1 二维数组程序范例

二维数组就像一张表格,有行和列。比如定义一个3行4列的二维数组,并输出:

#include <iostream>
using namespace std;

int main() {
    int a[3][4] = {      // 3行4列
        {1, 2, 3, 4},
        {5, 6, 7, 8},
        {9, 10, 11, 12}
    };

    // 用双重循环输出每个元素
    for (int i = 0; i < 3; i++) {        // i控制行
        for (int j = 0; j < 4; j++) {    // j控制列
            cout << a[i][j] << "\t";     // \t 是制表符,对齐
        }
        cout << endl;                     // 每行结束换行
    }

    return 0;
}

运行结果

1   2   3   4
5   6   7   8
9   10  11  12

5.3.2 二维数组的用法

定义二维数组

格式:数据类型 数组名[行数][列数];
例如:int score[5][3]; 表示5行3列,可以存放5个学生3门课的成绩。

初始化
  • 按行初始化:
    cpp int a[2][3] = {{1, 2, 3}, {4, 5, 6}};
  • 连续赋值(会自动按行填):
    cpp int a[2][3] = {1, 2, 3, 4, 5, 6}; // 等价于上面
  • 部分初始化:未指定的元素默认为0。
访问元素

使用两个下标:数组名[行下标][列下标],行和列都从0开始。
例如:a[1][2] 表示第2行第3列的元素(在数学中常称为第2行第3列,但在编程中下标从0开始,所以实际是第2行第3个)。

遍历二维数组

几乎总是用嵌套循环:外层循环遍历行,内层循环遍历列。

5.3.3 编程实例讲解

实例7:输入一个3×4矩阵,求所有元素的和
#include <iostream>
using namespace std;

int main() {
    int a[3][4];
    int sum = 0;

    cout << "请输入3行4列的矩阵:" << endl;
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 4; j++) {
            cin >> a[i][j];
            sum += a[i][j];
        }
    }

    cout << "所有元素的和:" << sum << endl;
    return 0;
}
实例8:求矩阵中的最大值及其位置
#include <iostream>
using namespace std;

int main() {
    int a[3][4] = {
        {34, 56, 12, 89},
        {23, 45, 67, 90},
        {11, 22, 33, 44}
    };
    int max = a[0][0];
    int max_i = 0, max_j = 0;   // 记录最大值的位置

    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 4; j++) {
            if (a[i][j] > max) {
                max = a[i][j];
                max_i = i;
                max_j = j;
            }
        }
    }

    cout << "最大值:" << max << ",位于第" << max_i+1 << "行第" << max_j+1 << "列" << endl;
    return 0;
}
实例9:矩阵转置(行列互换)

假设有一个3行2列的矩阵,转置后变成2行3列。

#include <iostream>
using namespace std;

int main() {
    int a[3][2] = {{1, 2}, {3, 4}, {5, 6}};
    int b[2][3];   // 转置后的矩阵

    // 转置:b[j][i] = a[i][j]
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 2; j++) {
            b[j][i] = a[i][j];
        }
    }

    // 输出转置后的矩阵
    cout << "转置后的矩阵:" << endl;
    for (int i = 0; i < 2; i++) {
        for (int j = 0; j < 3; j++) {
            cout << b[i][j] << " ";
        }
        cout << endl;
    }

    return 0;
}

运行结果

转置后的矩阵:
1 3 5
2 4 6

5.3.4 阶段性编程练习

  1. 练习1:定义一个4×4的二维数组,用循环赋值为1到16,然后按矩阵形式输出。
  2. 练习2:输入一个3×3矩阵,计算主对角线(从左上到右下)上元素的和。
  3. 练习3:输入两个2×3矩阵,求它们的和(对应位置相加),输出结果矩阵。
  4. 练习4:输入一个5×5矩阵,判断它是否关于主对角线对称(即转置后等于原矩阵)。

5.4 第5章编程作业

恭喜你学完了数组!现在来挑战几个综合题目,综合运用一维、二维数组和排序。

作业1:成绩统计系统

输入一个班的学生人数n(≤30),再输入每个学生的姓名和5门课的成绩。要求:
- 计算每个学生的总分和平均分
- 按总分从高到低排序,并输出排名、姓名、总分、平均分
- 输出每门课的平均分(全班平均)

提示:可以用多个一维数组,或者结构体(但还没学结构体,可以用平行数组:名字数组、总分数组等,排序时同时交换名字和总分)。或者先简单做,只处理成绩。

作业2:矩阵乘法

输入两个矩阵,第一个是m×n,第二个是n×p,计算它们的乘积并输出。m,n,p都不超过10。

作业3:约瑟夫问题

有n个人围成一圈,从第1个人开始报数,数到m的人出列,然后从下一个人继续报数,直到所有人都出列。输出出列顺序。n和m由用户输入。(提示:可以用数组标记是否出列,用循环模拟)

作业4:统计单词

输入一行英文句子(包含空格),统计其中有多少个单词,并输出每个单词的长度。例如输入 “I love C++ programming”,输出单词个数4,以及每个单词的长度(1 4 3 11)。提示:可以用字符串数组,但还没学,可以用字符数组,用循环判断空格。

作业5:杨辉三角(二维数组版)

用二维数组存储杨辉三角的前n行(n由用户输入),然后输出。杨辉三角每个数等于它上方两数之和。