数组:
1 数组的引入和基本语法
1)有些问题的解决需要我们保留大量的数据。
案例:班上有10个同学,根据班上同学的平均分调节同学的成绩,如果平均分小于55分,则每个同学都提高10分。
#include <stdio.h>
int main()
{
int score[10] = {60,70,30,50,56,56,60,70,30,50};
// 求出同学们成绩的平均分
int i;
int sum = 0;
for(i=0; i<10; ++i) {
sum += score[i];
}
// 求平均分
int m = sum/10;
printf("%d\n", m);
if(m<55) {
// 每个同学的成绩提高10分
for(i=0; i<10; ++i) {
score[i] += 10;
}
}
// 输出数组中的每个元素
for(i=0; i<10; ++i){
printf("%3d", score[i]);
}
return 0;
}
如果要求从键盘上录入同学的成绩
for(i=0; i<10; ++i)
{
scanf("%d", &score[i]);
}
2)数组定义语法
数组是用来存储一系列数据,但它往往被认为是一系列相同类型的变量。
数组元素在内存中的存放位置是连续的。
3)数组元素的初始化
double balance[5] = {1000.0, 2.0, 3.4, 7.0, 50.0};
double balance[] = {1000.0, 2.0, 3.4, 7.0, 50.0};
4)数组元素的使用
访问和修改
数组元素可以通过数组名称加索引进行访问。元素的索引是放在方括号内,跟在数组名称的后边。
可以读取数组中的元素也可以修改数组中元素的值。
位置下标是从0开始的。 最后一个元素的下标是N-1(假设数组容量是N个元素)
注意访问元素的时候下标不要越界。
int score[10] = {60,70,30,50,56,56,60,70,30,50};
score[0];
5)获得数组的大小(字节数),获得数组中的元素数量
2 使用数组的典型案例
1)存储大量数据并完成针对大量数据的算法,最基础的问题,算法简单(不用数组也可以完成,但是不能保留所有的数据)
找到这些数据中的最大值或者最小值。
#include <stdio.h>
int main()
{
int a[5] = {2,3,7,5,1};
int m = a[0];
for(int i=1; i<5; i++){
if(a[i]>m){
m = a[i];
}
}
printf("max is %d\n", m);
return 0;
}
扩展:在找最大值的同时,要给出最大值所在的位置编号。
#include <stdio.h>
int main()
{
int a[5] = {2,3,7,5,1};
int m = a[0];
int pos = 0;
for(int i=1; i<5; i++){
if(a[i]>m){
m = a[i];
pos = i;
}
}
printf("max is %d at %d\n", m, pos);
return 0;
}
在数据中查找是否存在某个数(或者符合某种条件的某个数),找到返回该数在序列中的位置,找不到返回-1.
#include <stdio.h>
int main()
{
int a[5] = {2,3,7,5,1};
int n = 3;
int pos = -1;
for(int i=0; i<5; i++){
if(a[i]==n){
pos = i;
break;
}
}
if(pos==-1)
printf("not find");
else{
printf("find at %d", pos);
}
return 0;
}
2)用数组保存统计结果。
例题:输入一个整数(可能很长),统计整数中每个数字出现的次数。
如 输入1124511, 输出 0:0, 1:4,2:1,3:0,4:1,5:1,6:0,7:0,8:0,9:0
用一个数组来保存数字的数量,a[0]保存0的数量,a[9]保存9的数量,int a[10]
取得输入的整数中的每一位数字,让后对对应数组的统计元素进行加一操作。
怎样取到整数中的每一位?(整数分离,可以用循环)
怎么处理很长的整数?(所以不能使用整型变量)
#include <stdio.h>
int main()
{
char c;
int a[10] = {0};
c = getchar(); // c='1'
// 在输入的同时完成数字数量的统计
while(c!='\n'){
a[c-'0']++; // '1' - '0'
c = getchar();
}
// 按照题目要求打印输入统计结果(统计数组)
for(int i=0; i<10; i++){
printf("%d:%d\n", i, a[i]);
}
return 0;
}
扩展1,输入字母组成的字符串,统计每个字母出现的次数。(有26个元素的数组来存储字母出现的数量,可能还有考虑大小写字母,字母对应下标 c-‘a’)
扩展2,输入字母和数字组成的字符串,统计每个数字和字母出现的次数。(多个数组,有些存储数字数量,有些存储字母数量)
扩展3,输入字母、数字、空格和其他字符组成的字符串,统计字母(字母的总数)、数字(数字总数)、空格和其他字符的个数。(可以不用数组)
3)存储一系列的数,辅助完成迭代算法。
斐波拉契数列之数组版。
#include <stdio.h>
int main()
{
int a[10] = {0};
a[0] = 1; a[1] = 1;
for(int i=2; i<10; i++){
a[i] = a[i-1] + a[i-2];
printf("%d ", a[i]);
}
return 0;
}
4)存储大量数据,完成针对大量数据的算法,进阶问题及算法。
选择排序
#include <stdio.h>
int main()
{
const int n = 5;
int a[5] = {3, 2, 4, 5, 1};
for(int i=0; i<n-1; i++){
// 在a[i]到a[n-1]范围中找到最小并且记录最小值的位置,并放置在i位置处(和a[i]交换)
int m = a[i], k;
int j;
for(j=i+1, k=i; j<n; j++){
if(a[j]<m){
m = a[j];
k = j;
}
}
// 交换 a[k] 和 a[i]
int t = a[i];
a[i] = a[k];
a[k] = t;
}
// 输出数组
for(int i=0; i<n; i++){
printf("%4d", a[i]);
}
return 0;
}
扩展,1)从大到小。 2)学号数组和成绩数组,一一对应关系。按照成绩的升序输出学号(姓名)。(按照成绩数组排序,同时交换对应的学号)
冒泡排序
#include <stdio.h>
int main()
{
const int n = 5;
int a[5] = {3, 2, 4, 5, 1};
for(int i=0; i<n-1; i++){
for(int j=0; j<n-1-i ;j++){
// 两两比较,大于则交换
if(a[j]>a[j+1]){
int t = a[j];
a[j] = a[j+1];
a[j+1] = t;
}
}
}
// 输出数组
for(int i=0; i<n; i++){
printf("%4d", a[i]);
}
return 0;
}
插入排序
归并排序
…
查找
暴力查找
二分查找(效率更高,但是要求数组中的元素是有序的)
#include <stdio.h>
int main()
{
const int n = 5;
int a[5] = {1, 2, 3, 4, 5};
int s=0, e=n-1;
int x = 8;
int pos = -1;
while(s<=e){
int mid = (s+e)/2;
if(a[mid]==x){
pos = mid;
break;
}else if(x>a[mid]){
s = mid+1;
}else if(x<a[mid]){
e = mid-1;
}
}
if(pos==-1){
printf("not find\n");
}else{
printf("found %d at %d\n", x, pos);
}
return 0;
}
5) 用数组存储好固定的数据,代码中直接提取使用,避免繁琐和反复的计算。
计算某年的某个月有多少天?
可以将每个月的天数提前存入一个表示天数的数组中,然后将月份作为数组的下标进行对应,直接从数组中取出,让代码的逻辑和实现都变得很简单了。
举例,计算日期是该年的第几天?
#include <stdio.h>
int is_leap(int y)
{
return ((y%4==0&&y%100!=0) || y%400==0);
}
int main()
{
int a[13] = {0, 0, 31, 59, 90, 120, 151, 181,
212, 243, 273, 304, 334}; // 每个月之前当年有多少天
int y = 2000;
int m = 3;
int res = a[m];
if (m>=3 && is_leap(y))
res += 1;
printf("%d\n", res);
return 0;
}
6)必须先把大量的数据保存下来,才能进行后续的处理。
入门案例(见上面的例题)
整数分解,正序输出每个数字。详细讲解参考MOOC B。
还有其他更好的解决方案吗?(使用数组)
反序取到每个数字,将他们存入数组中,反序输出数组。
#include <stdio.h>
#include <math.h>
int main()
{
int a=187;
int num[100];
int i=0;
int d;
do{
d = a%10;
num[i] = d; i++;
a /= 10;
}while(a>0);
for(int j=i-1; j>=0; j--){
printf("%4d", num[j]);
}
return 0;
}
例题:本题要求计算 A/B,其中 A 是不超过 1000 位的正整数,B 是 1 位正整数。你需要输出商数 Q 和余数 R,使得 A=B×Q+R 成立。
输入格式:
输入在一行中依次给出 A 和 B,中间以 1 空格分隔。
输出格式:
在一行中依次输出 Q 和 R,中间以 1 空格分隔。
输入样例:
123456789050987654321 7
输出样例:
17636684150141093474 3
核心思想:
用一个字符数组存储数字A的每一位,每一步的计算与以下两项有关:
1、前一位留下的余数yu
2、此位的数字a[j](存于字符数组里为字符)
每一步输出(yu×10+a[j])/B,输出之后,yu变为(yu×10+a[j])%B,为下一步做准备。
另外注意:首位特殊处理,当A只有一位时也要特殊处理。
#include <stdio.h>
int main()
{
int a[1001] = {0};
char c;
int i = 0;
while((c=getchar())!=' ') {
a[i++] = c-'0';
}
int b;
scanf("%d", &b);
int yu = 0;
for(int j=0; j<i; j++) {
int tem = yu*10+a[j];
a[j] = tem/b;
yu = tem%b;
}
int b_first = 1;
for(int j=0; j<i; j++) {
if(b_first){
if(a[j]!=0){
printf("%d", a[j]);
b_first = 0;
}
} else
printf("%d", a[j]);
}
if(b_first)
printf("0");
printf(" %d", yu);
return 0;
}
二维数组
1 二维数组的意义及基本语法
1)意义
有两个维度,可以被认为是行列结构。想象一下二维结构的图像。
2)定义及初始化
int x[3][4];
int a[3][4] = {
{0, 1, 2, 3} , /* 初始化索引号为 0 的行 */
{4, 5, 6, 7} , /* 初始化索引号为 1 的行 */
{8, 9, 10, 11} /* 初始化索引号为 2 的行 */
};
int a[3][4] = {0,1,2,3,4,5,6,7,8,9,10,11};
二维数组的元素在内存空间中是连续存放的,以行的顺序进行展开成一个一维结构。
3)引用及访问修改
a[1][2]
通常会结合二重循环来访问每个元素。
2 二维数组的典型应用
1)存储二维结构数据,对二维数据进行统计
做成绩表,很多个同学,学了多个科目。
二维表格,成绩表,一行代表一个同学,一列代表一门科目。
所有成绩的总分及平均分。 每个科目的平均分。 每个同学的平均分。
2)模拟针对矩阵的操作
矩阵转置
题目内容:
用二维数组作为函数参数,编程计算并输出n×n阶矩阵的转置矩阵。其中,n的值不超过10,n的值由用户从键盘输入。
程序运行结果示例1:
Input n:3↙
Input 33 matrix:
1 2 3↙
4 5 6↙
7 8 9↙
The transposed matrix is:
1 4 7
2 5 8
3 6 9
程序运行结果示例2:
Input n:2↙
Input 22 matrix:
1 2↙
4 5↙
The transposed matrix is:
1 4
2 5
#include <stdio.h>
int main()
{
int a[10][10];
printf("Input n:");
int n;
scanf("%d", &n);
printf("Input %d*%d matrix:\n", n, n);
for(int i=0; i<n; i++){
for(int j=0; j<n; j++){
scanf("%d", &a[i][j]);
}
}
for(int i=0; i<n; i++){
for(int j=0; j<i; j++){
int t = a[i][j];
a[i][j] = a[j][i];
a[j][i] = t;
}
}
printf("The transposed matrix is:\n");
for(int i=0; i<n; i++){
for(int j=0; j<n; j++){
printf("%4d", a[i][j]);
}
printf("\n");
}
return 0;
}
检验并打印幻方矩阵
题目内容:
幻方矩阵是指该矩阵中每一行、每一列、每一对角线上的元素之和都是相等的。从键盘输入一个5×5的矩阵并将其存入一个二维整型数组中,检验其是否为幻方矩阵,并将其按指定格式显示到屏幕上。
输入格式: “%d”
输出格式:
如果是幻方矩阵,输出提示信息: “It is a magic square!\n”
矩阵元素的输出: “%4d”(换行使用"\n")
如果不是幻方矩阵,输出提示信息: “It is not a magic square!\n”
输入样例1:
17_24_1_8_15
23_5_7_14_16
4_6_13_20_22
10_12_19_21_3
11_18_25_2_9
(输人样例中“”代表空格)
输出样例1:
It is a magic square!
17241815
23571416
46132022
10121921*3
111825***2**9
(输出样例中“”代表空格)
输入样例2:
1_0_1_6_1
3_1_1_1_1
1_1_1_1_2
1_1_1_1_1
9_1_7_1_1
(输人样例中“”代表空格)
输出样例2:
It is not a magic square!
先求出矩阵第一行所有元素的和,放入sum中,再求其他行或者列或者对角线的和,其他的和都等于sum,说明是,否则不是。
#include <stdio.h>
#include <math.h>
int main()
{
int a[5][5];
int n = 5;
int i, j;
for(i=0; i<5; i++){
for(j=0; j<5; j++){
scanf("%d", &a[i][j]);
}
}
int flag = 1;
int s = 0;
for(i=0; i<n; i++){
s += a[i][0];
}
int s1 = 0;
for(i=0; i<n; i++){
s1 += a[i][n-1];
}
if(s1!=s)
flag = 0;
s1 = 0;
for(j=0; j<n; j++)
s1 += a[0][j];
if(s1!=s)
flag = 0;
s1 = 0;
for(j=0; j<n; j++)
s1 += a[n-1][j];
if(s1!=s)
flag = 0;
s1 = 0;
for(i=0; i<5; i++){
s1 += a[i][i]; // 对角线上的元素
}
if(s1!=s)
flag = 0;
s1 = 0;
for(i=0; i<5; i++){
s1 += a[i][n-1-i]; // 反对角线上的元素
}
if(s1!=s)
flag = 0;
if(flag==1){
printf("It is a magic square!\n");
for(i=0; i<5; i++){
for(j=0; j<5; j++){
printf("%4d", a[i][j]);
}
printf("\n");
}
}
else{
printf("It is not a magic square!\n");
}
return 0;
}
找鞍点:
一个矩阵元素的“鞍点”是指该位置上的元素值在该行上最大、在该列上最小。
本题要求编写程序,求一个给定的n阶方阵的鞍点。
输入格式:
输入第一行给出一个正整数n(1≤n≤6)。随后n行,每行给出n个整数,其间以空格分隔。
输出格式:
输出在一行中按照“行下标 列下标”(下标从0开始)的格式输出鞍点的位置。如果鞍点不存在,则输出“NONE”。题目保证给出的矩阵至多存在一个鞍点。
输入样例1:
4
1 7 4 1
4 8 3 6
1 6 1 2
0 7 8 9
输出样例1:
2 1
输入样例2:
2
1 7
4 1
输出样例2:
NONE
#include<stdio.h>
int main(void)
{
int a[10][10];
int m, n;
scanf("%d%d", &m, &n);
for(int i=0; i<m; i++){
for(int j=0; j<n; j++){
scanf("%d", &a[i][j]);
}
}
// 提前储出每一行的最大值和每一列的最小值
int maxs[10] = {0};
for(int i=0; i<m; i++){
maxs[i] = a[i][0];
for(int j=1; j<n; j++){
if(a[i][j]>maxs[i]){
maxs[i] = a[i][j];
}
}
}
int mins[10] = {0};
for(int j=0; j<n; j++){
mins[j] = a[0][j];
for(int i=1; i<m; i++){
if(a[i][j]<mins[j]){
mins[j] = a[i][j];
}
}
}
int flag = 0;
for(int i=0; i<m; i++){
for(int j=0; j<n; j++){
if(a[i][j]==maxs[i] && a[i][j]==mins[j]){
printf("Array[%d][%d]=%d\n", i, j, a[i][j]);
flag = 1;
}
}
}
if(!flag){
printf("None\n");
}
return 0;
}
蛇形矩阵填充
#include <stdio.h>
int a[101][101];
void print(int a[][101], int n)
{
for(int i=0; i<n; i++){
for(int j=0; j<n; j++){
printf("%4d", a[i][j]);
}
printf("\n");
}
}
/*
按照对角线的顺序填充,利用对角线的序号奇偶性控制填充的方向,提前规划好每个对角线应该
填充的数量,在边界处进行处理
*/
int main()
{
int n;
printf("Input n:\n");
int n_in = scanf("%d", &n);
if(n_in!=1 || n>100 || n<=0){
printf("Input error!\n");
return 0;
}
int N = n*n;
int line_no = 1, num_in_line = 1;
int m = 1;
int i=0, j=0;
while(m<=N){
// fill a line
for(int k=0; k<num_in_line; k++){
a[i][j] = m++;
if(line_no%2==1){
if(i-1>=0 && j+1<=n-1) i--, j++;
}
else{
if(i+1<=n-1 && j-1>=0) i++, j--;
}
}
// end of line
// locate i, j for next line
if(line_no<n){
if(line_no%2==1 && j<n-1) j+=1;
if(line_no%2==0 && i<n-1) i+=1;
num_in_line++;
}
else{
if(line_no%2==1 && j==n-1) i+=1;
if(line_no%2==0 && i==n-1) j+=1;
num_in_line--;
}
line_no++;
}
print(a, n);
return 0;
}
#include <stdio.h>
int a[101][101];
void print(int a[][101], int n){
for(int i=0; i<n; i++){
for(int j=0; j<n; j++){
printf("%4d", a[i][j]);
}
printf("\n");
}
}
/*
从1开始填充
i_sign = -1, j_sign = +1;
边界及边界变化 j==n-1 --> i+=1; i==n-1 --> j+=1; i==0--> j+=1; j==0 --> i+=1;
边界处同时改变方向 i_sign *= -1; j_sign *= -1;
边界处同时填充下一个位置(新斜线的起点) a[i][j]=m++;
所有情况都移动位置 i += i_sign; j += j_sign;
注意在边界处应该将下一个位置填充后跳过下一个位置,应为边界处的下一个位置不应该被作为边界了
*/
int main()
{
int n;
printf("Input n:\n");
int n_in = scanf("%d", &n);
if(n_in!=1 || n>100 || n<=0){
printf("Input error!\n");
return 0;
}
int N = n*n;
//int line_no = 1, num_in_line = 1;
int m = 1;
//a[0][0] = m++;
int i=0, j=0, i_sign=-1, j_sign=1;
while(m<=N){
a[i][j] = m++;
if(j==n-1){
i+=1; a[i][j]=m++;
i_sign *= -1; j_sign *= -1;
}else if(i==n-1){
j+=1; a[i][j]=m++;
i_sign *= -1; j_sign *= -1;
}else if(i==0){
j+=1; a[i][j]=m++;
i_sign *= -1; j_sign *= -1;
}else if(j==0){
i+=1; a[i][j]=m++;
i_sign *= -1; j_sign *= -1;
}
i += i_sign; j += j_sign;
}
print(a, n);
return 0;
}
#include <stdio.h>
int a[101][101];
void print(int a[][101], int n){
for(int i=0; i<n; i++){
for(int j=0; j<n; j++){
printf("%4d", a[i][j]);
}
printf("\n");
}
}
/*
从1开始填充
i_sign = -1, j_sign = +1;
边界及边界变化 j==n-1 --> i+=1; i==n-1 --> j+=1; i==0--> j+=1; j==0 --> i+=1;
边界处同时改变方向
边界处同时填充下一个位置(新斜线的起点) a[i][j]=m++;
所有情况都移动位置 i += i_sign; j += j_sign;
注意在边界处应该将下一个位置填充后跳过下一个位置,应为边界处的下一个位置不应该被作为边界了
*/
int can_step(int i, int j, int i_sign, int j_sign, int n){
i+=i_sign; j+=j_sign;
if(i<0||i>n-1||j<0||j>n-1) return 0; else return 1;
}
int main()
{
int n;
printf("Input n:\n");
int n_in = scanf("%d", &n);
if(n_in!=1 || n>100 || n<=0){
printf("Input error!\n");
return 0;
}
int N = n*n;
int m = 1;
int i=0, j=0, i_sign=-1, j_sign=1;
while(m<=N){
a[i][j] = m++;
if(!can_step(i,j,i_sign,j_sign,n)){
if(j==n-1){
i+=1;
}else if(i==n-1){
j+=1;
}else if(i==0){
j+=1;
}else if(j==0){
i+=1;
}
i_sign *= -1; j_sign *= -1;
}else{
i += i_sign; j += j_sign;
}
}
print(a, n);
return 0;
}
杨辉三角
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
#include<stdio.h>
int main(void)
{
int a[10][10];
for(int i=0; i<10; i++){
a[i][0] = 1;
a[i][i] = 1;
}
for(int i=2; i<10; i++){
for(int j=1; j<i; j++){
a[i][j] = a[i-1][j] + a[i-1][j-1];
}
}
for(int i=0; i<10; i++){
for(int j=0; j<=i; j++){
if(j==0)
printf("%d", a[i][j]);
else
printf(" %d", a[i][j]);
}
printf("\n");
}
return 0;
}