C语言学习(2)--数组

数组:
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 2
2 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
1012
19
21*3
1118
25***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;
}
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值