第 6 章 测用数组处理批量数据
在之前的章节中,我们主要使用了基本数据类型的变量,如整型、字符型和浮点型。虽然这些简单数据类型可以解决基础问题,但在处理更复杂的数据时,它们的局限性就会显现出来。为了有效地处理大量同类数据,C 语言引入了 数组 这一数据结构。通过数组,程序可以方便地组织和处理批量数据。
6.1 怎样定义和引用一维数组
一维数组 是最简单的数组类型,其每个元素通过数组名和一个下标唯一确定。比如,假设一个班级有 30 个学生,他们的成绩可以用一个一维数组来表示。
定义一维数组:
int a[10];
这里定义了一个包含 10 个整型元素的数组,数组名为 a
。注意:
- 数组名: 命名规则与变量相同,遵循标识符命名规则。
- 元素个数: 在定义数组时需要指定数组的长度(元素的数量)。例如
a[10]
表示数组a
有 10 个元素。 - 下标范围: 数组下标从 0 开始,所以在
a[10]
中,元素编号从a[0]
到a[9]
,并不存在a[10]
这个下标。 - 常量表达式: 数组的长度应该是常量或符号常量(如
int a[3+5];
是合法的)。但不能包含变量(如int a[n];
是不合法的),C 语言中数组的大小在编译时固定,不依赖于程序运行期间的变量值。
当数组 a
被定义后,内存中会为它分配一块空间,存储长度为 10 的整型数据。以 int a[10]
为例,如果每个整型数据在编译器中占用 4 字节,总共将占用 40 字节的内存。
图示:
+------+------+------+------+------+------+------+------+------+------+
| a[0] | a[1] | a[2] | a[3] | a[4] | a[5] | a[6] | a[7] | a[8] | a[9] |
+------+------+------+------+------+------+------+------+------+------+
数组 a
中每个元素的内存地址是连续的,通过数组名和下标可以直接访问每个元素的数据。
6.1.2 怎样引用一维数组元素
概念
在定义数组后,可以通过索引(下标)来引用数组中的单个元素。这一点与完整地引用整个数组不同;在 C 语言中,不能直接操作整个数组作为一个单独的实体,而是需要逐一引用其元素。
引用方法
数组元素的引用格式为:
数组名[下标]
例如,a[0]
表示数组 a
中的第一个元素。下标可以是常量、变量或整型表达式。
示例说明
引用数组元素时,下标可以是任何合法的整型表达式。例如,下面的表达式展示了对数组元素的引用和操作:
a[0] = a[5] + a[7] - a[2];
这里 a[0]
、a[5]
、a[7]
和 a[2]
均引用了数组 a
中的特定元素。
【例 6.1】数组的赋值和逆序输出
问题描述: 定义一个包含 10 个整数的数组,并将数组元素依次赋值为 0 到 9,然后按逆序输出这些元素。
解题思路:
- 首先定义一个长度为 10 的整型数组。
- 使用循环结构将 0 到 9 的值赋给数组的每个元素。
- 再用一个循环按逆序输出这些元素。
程序实现:
#include <stdio.h>
int main() {
int i, a[10];
// 赋值循环
for (i = 0; i < 10; i++) {
a[i] = i;
}
// 逆序输出循环
for (i = 9; i >= 0; i--) {
printf("%d ", a[i]);
}
printf("\n");
return 0;
}
运行结果:
9 8 7 6 5 4 3 2 1 0
程序分析:
- 第一个
for
循环通过a[i] = i;
为数组a
的每个元素从a[0]
到a[9]
赋值。 - 第二个
for
循环从a[9]
开始逆序输出数组元素到a[0]
。
注意事项
- 数组的下标在 C 语言中从 0 开始,最后一个元素的下标是数组长度减 1。因此,对于长度为 10 的数组
a[10]
,有效的下标范围是从 0 到 9。 - 在上述示例中的错误使用示范,
for (i = 1; i <= 10; i++)
会尝试访问a[10]
,这是未定义的行为,因为它越界了数组的有效下标。
通过这种方式,我们能够理解和掌握一维数组元素的定义、赋值和引用,为处理更复杂的数据结构打下坚实的基础。
6.1.3 一维数组的初始化
在 C 语言中,数组的初始化可以通过在定义数组的同时为其元素分配初始值。此方法称为 "初始化列表"。下面介绍几种不同的初始化方法:
-
全部元素赋初值:
- 在数组定义时给每个元素提供初值,所有初值放在一对花括号
{}
内,用逗号分隔。
int a[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
上述代码使得
a[0]
到a[9]
分别被赋值为0
到9
。 - 在数组定义时给每个元素提供初值,所有初值放在一对花括号
-
部分元素赋初值:
- 初始化列表中只给部分元素赋值,未指定的元素会自动初始化为
0
。
int a[10] = {0, 1, 2, 3, 4}; // a[0] 到 a[4] 被赋值,其余元素默认初始化为 0
- 初始化列表中只给部分元素赋值,未指定的元素会自动初始化为
-
全零初始化:
- 如果希望数组的所有元素都为
0
,可以直接写{0}
。
int a[10] = {0}; // 所有元素都被初始化为 0
- 如果希望数组的所有元素都为
-
自动确定长度:
- 不在方括号中指定长度时,数组长度将由初始化列表中的数据个数确定。
int a[] = {1, 2, 3, 4, 5}; // a 的长度自动确定为 5
6.1.4 一维数组程序举例
【例 6.2】 Fibonacci 数列
问题描述: 利用数组处理 Fibonacci 数列的问题,计算并输出该数列的前 20 个数。
解题思路:
- 使用一维数组保存 Fibonacci 数列的每一个值。数组元素分别代表数列中的各个数。
- 首先为
f[0]
和f[1]
赋初值1
,然后依次计算f[2]
到f[19]
,计算方法是f[i] = f[i - 2] + f[i - 1]
。
程序实现:
#include <stdio.h>
int main() {
int i;
int f[20] = {1, 1}; // 初值为前两个 Fibonacci 数
// 从 f[2] 开始,计算并填充数组
for (i = 2; i < 20; i++) {
f[i] = f[i - 2] + f[i - 1];
}
// 逐个输出数组中的 Fibonacci 数
for (i = 0; i < 20; i++) {
if (i % 5 == 0 && i != 0) {
printf("\n"); // 每输出 5 个数换行
}
printf("%12d ", f[i]);
}
printf("\n");
return 0;
}
运行结果:
1 1 2 3 5
8 13 21 34 55
89 144 233 377 610
987 1597 2584 4181 6765
程序分析:
- 初始化时,只为前两个元素
f[0]
和f[1]
赋值。 - 通过
for
循环计算数组中从f[2]
开始的 Fibonacci 数列值,公式为f[i] = f[i - 2] + f[i - 1]
。 - 使用
if
语句控制换行,每输出 5 个数换一次行。
这个程序示例展示了如何利用数组来处理批量数据并提高代码的简洁性和效率。
【例 6.3】起泡排序示例
问题描述: 给出 10 个地区的面积,将它们按从小到大的顺序进行排列。
解题思路:
- 这个问题称为数的排序(sort),其中排序的方式有两种:升序(从小到大)和降序(从大到小)。
- 本例采用升序排序的方式,实现时选择“起泡法”排序。它的基本思路是:通过多次遍历,每次都将相邻的两个元素比较并交换位置,使较大的数逐渐沉到底部(冒泡),每次遍历都能确定当前未排序部分的最大值,最终实现排序。
起泡法的步骤:
- 第 1 趟比较:从第一个数开始,将相邻的两个数比较并交换,最终使得最大数移动到最后。
- 第 2 趟比较:从第一个数开始,对前 n-1 个数进行比较并交换,将次大数移动到倒数第二位。
- 依次类推,每趟比较中所需比较的次数逐渐减少,直到整个数组按照从小到大的顺序排列。
流程图(图 6.5):
- 输入 10 个数,存储到数组
a
中。 - 使用两层循环控制多趟比较。
- 在每趟中,通过内层循环将相邻的两个数比较,如果前面的数比后面的数大,则交换位置。
- 每趟结束后,将当前未排序部分的最大数确定到正确位置。
- 最终输出排好序的数组。
程序实现:
#include <stdio.h>
int main() {
int a[10];
int i, j, t;
// 输入 10 个数
printf("Input 10 numbers:\n");
for (i = 0; i < 10; i++) {
scanf("%d", &a[i]);
}
// 起泡排序
for (j = 0; j < 9; j++) {
for (i = 0; i < 9 - j; i++) {
if (a[i] > a[i + 1]) {
// 交换 a[i] 和 a[i + 1]
t = a[i];
a[i] = a[i + 1];
a[i + 1] = t;
}
}
}
// 输出排好序的数组
printf("The sorted numbers:\n");
for (i = 0; i < 10; i++) {
printf("%d ", a[i]);
}
printf("\n");
return 0;
}
运行结果:
Input 10 numbers:
12 5 8 3 9 15 4 10 6 2
The sorted numbers:
2 3 4 5 6 8 9 10 12 15
程序分析:
- 程序中,第 10~13 行是实现起泡排序的核心部分。
- 外层循环控制整个排序的趟数(共 9 趟)。
- 内层循环控制当前趟的比较次数。随着外层循环的进行,未排序部分逐渐减少,因此内层循环的次数也随之减少。
- 使用
if
语句比较相邻两个元素,将较大的数“沉”到正确的位置。
总结:
- 通过该例,主要学习了起泡排序算法的实现方法。起泡排序虽然算法简单,但在数据量较大的情况下效率较低。其他常用的排序方法还有选择排序、希尔排序等。在实际应用中,可以根据具体需求选择合适的排序算法。