简单来说,数组就是存放数据的一个组,所有的数据都统一存放在这一个组中,一个数组可以同时存放多个数据。
一维数组
比如现在想保存 12 个月的天数,那么只需要创建一个 int 类型的数组就可以了,它可以保存很多个 int 类型的数据,这些保存在数组中的数据,称为元素:
// 12个月的数据全部保存在了一起
int arr[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
可以看到,数组的定义方式也比较简单:
类型 数组名称[数组大小] = {数据1, 数据2...};
- 后面的数据可以在一开始的时候不赋值
- 数组大小必须是整数
注意数组只能存放指定类型的数据,一旦确定是不能更改的。
因为数组声明后,会在内存中开辟一块连续的区域,来存放这些数据,所以类型和长度必须在一开始就明确。
创建数组的方式有很多种:
// 直接声明int类型数组,容量为10
int a[10];
// 声明后,可以赋值初始值,使用{}囊括,不一定需要让10个位置都有初始值,比如这里仅仅是为前三个设定了初始值
// 注意,跟变量一样,如果不设定初始值,数组内的数据并不一定都是0
int b[10] = {1, 2, 4};
// 也可以通过 [下标] = 的形式来指定某一位的初始值
// 注意下标是从0开始的,第一个元素就是第0个下标位置,比如这里数组容量为10,那么最多到9
int c[10] = {1, 2, [4] = 777, [9] = 666};
// 也可以根据后面的赋值来决定数组长度
int c[] = {1, 2, 3};
基本类型都可以声明数组:
#include <stdio.h>
int main() {
// 多个字符
char str[] = {'A', 'B', 'C'};
// 实际上字符串就是多个字符的数组形式
char str2[] = "ABC";
}
那么数组定义好了,如何去使用它呢?比如现在需要打印 12 个月的天数:
#include <stdio.h>
int main() {
int arr[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
for (int i = 0; i < 12; i++) {
// 直接通过数组 名称[下标] 来访问对应的元素值
int days = arr[i];
printf("2022年 %d 月的天数是:%d 天\n", (i + 1), days);
}
}
2022年 1 月的天数是:31 天
2022年 2 月的天数是:28 天
2022年 3 月的天数是:31 天
2022年 4 月的天数是:30 天
2022年 5 月的天数是:31 天
2022年 6 月的天数是:30 天
2022年 7 月的天数是:31 天
2022年 8 月的天数是:31 天
2022年 9 月的天数是:30 天
2022年 10 月的天数是:31 天
2022年 11 月的天数是:30 天
2022年 12 月的天数是:31 天
当然也可以对数组中的值进行修改:
#include <stdio.h>
int main() {
int arr[] = {666, 777, 888};
// 比如现在想要让第二个元素的值变成999
arr[1] = 999;
// 打印一下看看是不是变成了999
printf("%d", arr[1]);
}
999
和变量一样,如果只是创建数组但是不赋初始值的话,因为是在内存中随机申请的一块空间,有可能之前其他地方使用过,保存了一些数据,所以数组内部的元素值并不一定都是 0:
#include <stdio.h>
int main() {
int arr[10];
for (int i = 0; i < 10; i++) {
printf("%d ", arr[i]);
}
}
10566448 0 -962063554 32758 1 0 10566394 0 10566554 0
不要尝试去访问超出数组长度位置的数据,虽然可以编译通过,但是会给警告,这些数据是毫无意义的:
#include <stdio.h>
int main() {
int arr[] = {111, 222, 333};
// 不能去访问超出数组长度的元素,很明显这里根本就没有第四个元素
printf("%d", arr[3]);
}
42
多维数组
数组不仅仅只可以有一个维度,也可以创建二维甚至多维的数组,简单来说就是,存放数组的数组:
// 可以看到,数组里面存放的是数组
int arr[][2] = {{20, 10}, {18, 9}};
存放的内层数组的长度是需要确定的,存放数组的数组和之前一样,可以根据后面的值决定
比如现在要存放 2020 - 2022 年每个月的天数,那么此时用一维数组就不方便了,可以使用二维数组来处理:
// 2020年是闰年,2月有29天
int arr[3][12] = {{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}};
这样,就通过二维数组将这三年每个月的天数都保存下来了。
那么二维数组又该如何去访问呢?
#include <stdio.h>
int main() {
// 2020年是闰年,2月有29天
int arr[3][12] = {{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}};
// 比如现在想要获取2020年2月的天数,首先第一个是[0]表示存放的第一个数组,第二个[1]表示数组中的第二个元素
printf("%d", arr[0][1]);
}
29
当然除了二维还可以上升到三维、四维:
int arr[2][2][2] = {{{1, 2}, {1, 2}}, {{1, 2}, {1, 2}}};
多维数组比较复杂,使用得也比较少,这里就不深入研究了。
实战:冒泡排序算法
现在有一个 int 数组,但是数组内的数据是打乱的,现在通过 C 语言,实现将数组中的数据按从小到大的顺序进行排列。
这里使用冒泡排序算法来实现,此算法的核心思想是:
- 假设数组长度为 N
- 进行 N 轮循环,每轮循环都选出一个最大的数放到后面
- 每次循环中,从第一个数开始,让其与后面的数两两比较,如果更大,就交换位置,如果更小,就不动
动画演示:https://visualgo.net/zh/sorting?slide=2-2
#include <stdio.h>
// 冒泡排序
int main() {
// 乱序数组
int arr[10] = {16, 5, 7, 32, 59, 10, 64, 1, 38, 41};
// 遍历数组元素
for (int i = 0; i < 10; i++) {
// 是否交换
_Bool flag = 0;
// 循环实现交换
for (int j = 0; j < 10 - i; j++) {
// 比较当前元素和下一个元素
if (arr[j] > arr[j + 1]) {
// 交换两个元素
int temp = arr[j + 1];
arr[j + 1] = arr[j];
arr[j] = temp;
// 发生了交换,设为1
flag = 1;
}
}
// 如果都没有发生交换,说明已经排好序了,跳出循环
if (flag == 0) {
break;
}
}
// 打印排序后数组
for (int i = 0; i < 10; i++) {
printf("%d ", arr[i]);
}
}
实战:斐波那契数列解法其二
学习了数组,再来看看如何利用数组来计算斐波那契数列,这里采用动态规划的思想。
动态规划算法通常用于求解具有某种最优性质的问题。在这类问题中,可能会有许多可行解。每一个解都对应于一个值,我们希望找到具有最优值的解。动态规划算法与分治法类似,其基本思想也是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。
可以在一开始创建一个数组,然后从最开始的条件不断向后推导,从斐波那契数列的规律可以得知:
fib[i] = fib[i - 1] + fib[i - 2]
(这里fib
代表斐波那契数列)
得到这样的一个关系(递推方程)就好办了,要求解数列第i
个位置上的数,只需要知道i - 1
和i - 2
的值即可,这样,一个大问题,就分成了两个小问题,比如现在要求解斐波那契数列的第 5 个元素:
fib[4] = fib[3] + fib[2]
现在我们只需要知道fib[3]
和fib[2]
即可,那么我们接着来看:fib[3] = fib[2] + fib[1]
以及fib[2] = fib[1] + fib[0]
- 由于
fib[0]
和fib[1]
我们已经明确知道是1
了,那么现在问题其实已经有结果了
现在设计一个 C 语言程序,利用动态规划的思想解决斐波那契数列问题。
#include <stdio.h>
// 斐波那契数列解法其二
int main() {
// 斐波那契数列的元素位置
int index = 6;
// 根据位置建立数组
int arr[index];
// 第一个元素固定为1
arr[0] = 1;
// 第二个元素固定为1
arr[1] = 1;
// 遍历数组
for (int i = 2; i < index; i++) {
// 根据斐波那契数列定义,从第三项开始,每一项都等于前两项之和
arr[i] = arr[i - 1] + arr[i -2];
}
// 打印数组元素
printf("%d", arr[index - 1]);
}