目 录CONTENT

文章目录

C语言讲义-数组

smallkun
2023-12-24 / 0 评论 / 0 点赞 / 71 阅读 / 10,754 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2023-12-24,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我删除。

天啊!一大串数正在接近

第1节 逆序输出

思考一个问题:如何从键盘输入5个整数,然后将其逆序输出?比如,输入6、3、5、7、8,则输出8、7、5、3、6。

你可能会说很简单啊,可以这样写:

    #include <stdio.h>
    #include <stdlib.h>
    int main( )
    {
        int a, b, c, d, e;
        scanf("%d%d%d%d%d", &a, &b, &c, &d, &e);
        printf("%d %d %d %d %d", e, d, c, b, a);
        system("pause");
        return 0;
    }

当然,你也可以这样写:

    #include <stdio.h>
    #include <stdlib.h>
    int main( )
    {
        int a, b, c, d, e;
        scanf("%d", &a);
        scanf("%d", &b);
        scanf("%d", &c);
        scanf("%d", &d);
        scanf("%d", &e);
        printf("%d ", e);
        printf("%d ", d);
        printf("%d ", c);
        printf("%d ", b);
        printf("%d ", a);
        system("pause");
        return 0;
    }

不要鄙视我,我没有开玩笑,这样写虽然显得更为复杂,但是在接下来的章节中,你一定会发现这样写的好处。

现在5个数还好办,如果要想读入100个数,然后将这100个数逆序输出该怎么办呢?那岂不是要累死……

第2节 申请100个小房子怎么办

在前面中,我们就已经学习了如何申请一个变量(小房子),很简单:

 int a;

那如果我要申请10个变量呢,你可能会这样写:

  int a, b, c, d, e, f, g, h, i, j;

那如果要申请100个呢,你可能会说没关系啊,慢慢写呗:

int a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15,
a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30,
a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45,
a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60,
a61, a62, a63, a64, a65, a66, a67, a68, a69, a70, a71, a72, a73, a74, a75,
a76, a77, a78, a79, a80, a81, a82, a83, a84, a85, a86, a87, a88, a89, a90,
a91, a92, a93, a94, a95, a96, a97, a98, a99, a100;

那如果要申请10 000个呢?如果这样写下去,估计不吃晚饭也写不完。下面将介绍一种简洁的写法,用一行语句就可以一次性申请10 000个变量。

 int a[10000];

怎么样,是不是很方便,如果只需申请10个,我们可以依葫芦画瓢:

 int a[10];

在上面这行语句中,我们定义了10个整型变量,就如同10个“小房子”并排放在了一起:

img

如何使用这些变量呢?不要着急,马上揭晓。

首先,int a[10];中[ ]里的数字表示需要定义变量的个数,我们这里定义了10个。这10个变量分别用a[0]、a[1]、a[2]、a[3]、a[4]、a[5]、a[6]、a[7]、a[8]、a[9]来表示。

img

你可能有一个疑问,为什么是从a[0]到a[9],而不是从a[1]到a[10]呢?为什么从0开始计数呢?从1开始多好啊!其实一点也不奇怪,只是习惯不同罢了。我们中国人比较喜欢从1开始计数,比如说“楼房”是第一层、第二层……可是在国外,首层是Ground Floor,然后才是First Floor(第一层)、Second Floor(第二层)……

假如我们要将a[0]~a[9]这10个变量分别存储0、1、4、9、16、25、36、49、64、81的话,可以这样写:

    a[0]=0;
    a[1]=1;
    a[2]=4;
    a[3]=9;
    a[4]=16;
    a[5]=25;
    a[6]=36;
    a[7]=49;
    a[8]=64;
    a[9]=81;

当然,你也用for循环来简化上面的代码:

    for(i=0; i<=9; i++)
    {
        a[i]=i*i;
    }

好,我们来看一段完整的代码:

    #include <stdio.h>
    #include <stdlib.h>
    int main( )
    {
        int a[10], i;
        for(i=0; i<=9; i++)
        {
            a[i]=i*i;
        }
        for(i=0; i<=9; i++)
        {
            printf("%d ", a[i]);
        }
        system("pause");
        return 0;
    }

上面这段代码,就是将0、1、4、9、16、25、36、49、64、81这10个数放入a[0]~a[9]中,然后再将a[0]~a[9]中的数打印出来。

第3节 100个数的逆序

好,回到本章的第1个问题——如何逆序输出。我们将利用“数组”来彻底解决这个问题。
很简单,我们先解决输入的问题。根据前面的方法,可以这样写:

    #include <stdio.h>
    #include <stdlib.h>
    int main( )
    {
        int a[5];
        scanf("%d", &a[0]);
        scanf("%d", &a[1]);
        scanf("%d", &a[2]);
        scanf("%d", &a[3]);
        scanf("%d", &a[4]);
        system("pause");
        return 0;
    }

接着可以用for循环来简化上面的代码:

    #include <stdio.h>
    #include <stdlib.h>
    int main( )
    {
        int a[5], i;
        for(i=0; i<=4; i++)
        {
            scanf("%d", &a[i]);
        }
        system("pause");
        return 0;
    }

那逆序输出该怎么办?只需将for(i=0; i<=4; i++)改为for(i=4; i>=0; i–)就可以了。

        for(i=4; i>=0; i--)
        {
            printf("%d ", a[i]);
        }

完整的代码如下:

    #include <stdio.h>
    #include <stdlib.h>
    int main( )
    {
        int a[5], i;
        for(i=0; i<=4; i++)
        {
            scanf("%d", &a[i]);
        }
        for(i=4; i>=0; i--)
        {
            printf("%d ", a[i]);
        }

        system("pause");
        return 0;
    }

第4节 陶陶摘苹果

陶陶摘苹果的问题描述如下:

陶陶家的院子里有一棵苹果树,每到秋天树上就会结出10个苹果。苹果成熟的时候,陶陶就会跑去摘苹果。陶陶有个30cm高的板凳,当她不能直接用手摘到苹果时,就会踩到板凳上再试试。

现在已知10个苹果到地面的高度,以及陶陶把手伸直的时候能够达到的最大高度,请帮陶陶算一下她能够摘到的苹果的数目。假设她碰到苹果,苹果就会掉下来。

img

【输入格式】

输入文件包括两行数据。第1行包含10个100~200之间(包括100和200)的整数(以cm为单位)分别表示10个苹果到地面的高度,两个相邻的整数之间用1个空格隔开。第2行只包括1个100~120之间(包含100和120)的整数(以cm为单位),表示陶陶把手伸直时能够达到的最大高度。

【输出格式】

只包括一行,这一行只包含一个整数,表示陶陶能够摘到的苹果的数目。

【样例输入】

    100 200 150 140 129 134 167 198 200 111
    110

【样例输出】

    5

这个题目很简单,题目的输入数据中已经给出了每个苹果的高度和陶陶的身高。我们只需依次来判断“每个苹果的高度”是否小于等于“陶陶的身高加板凳的高度”。

陶陶的身高是一个整数,我们可以用一个整型变量h来存储。10个苹果的高度,我们可以用一个大小为10的整型数组a[10]来存储。代码如下:

    int h, a[10];

解决了存储的问题,接下来我们来解决读入的问题。题目在给出数据时是先给出10个苹果的高度,再给出陶陶的身高。那我们要注意读入的顺序。

    for(i=0; i<=9; i++)
          scanf("%d", &a[i]);
    scanf("%d", &h);

上面的代码中,我们利用for循环来读入10个苹果的高度并存入数组a中。要注意的是,我们在定义数组a的时候,写的是int a[10],虽然申请了10个空间,但是数组是从0开始计数的,所以是a[0]~a[9]。当然你也可以写int a[11],就可以用a[1]~a[10]了,只是浪费了a[0]这个空间。其实我更倾向于第2种写法,因为我们中国人更喜欢从1开始计数。

在解决了输入问题后,我们需要统计陶陶可以摘到多少苹果了。我们仍然要使用for循环来依次判断陶陶能否摘到每个苹果。如果苹果的高度<=陶陶的身高+板凳的高度,那么这个苹果陶陶就可以摘到。板凳的高度是固定的,为30cm。

    sum=0;
    for(i=0; i<=9; i++)
    {
          if( a[i] <= h+30 )
                sum++;
    }
    printf("%d", sum);

上面的代码中,整型变量sum是用来计数的,所以一定不要忘记sum的初始值为0,当然在使用sum这个变量前别忘了定义int sum;,最后只需输出sum的值就可以了。完整的代码如下:

    #include <stdio.h>
    #include <stdlib.h>
    int main( )
    {
          int h, a[10], i, sum;
          for(i=0; i<=9; i++)
                scanf("%d", &a[i]);
          scanf("%d", &h);
          sum=0;
          for(i=0; i<=9; i++)
          {
                if( a[i] <= h+30 )
                      sum++;
          }
          printf("%d", sum);
          system("pause");
          return 0;
    }

《陶陶摘苹果》题目来源于第十一届全国青少年奥林匹克信息学联赛复赛普及组试题(NOIP 2005)。

第5节 一个萝卜一个坑

这里有一个有趣的问题:从键盘输入5个0~9的数,然后输出0~9中那些没有出现过的数。例如,输入2 5 2 1 8时,输出0 3 4 6 7 9。

想一想,有没有什么好办法?

我们这里借助一个数组就可以解决这个问题。

首先我们需要申请一个大小为10的数组int a[10];。好了,现在你已经有了10个小房间,编号为a[0]~a[9]。

刚开始的时候,我们将a[0]~a[9]都初始化为0。

img

然后用a[0]来表示数字0是否会出现,用a[1]来表示数字1是否会出现……用a[9]来表示数字9是否会出现。

下面就好办了,一会儿哪个数字出现,我们就把相应的小房间的值从0改为1。例如,第一个出现的数是2,我们就把a[2]这个小房间中的值从0变为1。

img

下一个出现的数是5,我们就把a[5]这个小房间中的值从0变为1。

img

注意啦,接下来出现的数又是2,此时a[2]这个小房间中的值已经是1,所以值还是1。

img

接下来出现的数是1,我们就把a[1]这个小房间中的值从0变为1。

img

最后出现的数是8,我们就把a[8]这个小房间中的值从0变为1。

img

看一下最后a[0]~a[9]这10个小房间中的数,你会惊奇地发现:出现过的数,它们所对应的小房间中的值都为1;没有出现过的数所对应的小房间中的值都为0。接下来,只需把小房间中值为0的小房间的编号输出就可以啦。

    #include <stdio.h>
    #include <stdlib.h>
    int main()
    {
        int a[10], i, t;
        for(i=0; i<=9; i++)
          a[i]=0; //初始化每个小房间为0

        for(i=1; i<=5; i++)
        {
            scanf("%d", &t); //依次读入5个数
            a[t]=1;         //把对应的小房间改为1
        }
        for(i=0; i<=9; i++)
            if(a[i]==0)     //输出没有出现过的数
                  printf("%d ", i);
        system("pause");
        return 0;
    }

好了,大功告成了!其实这个方法就是“一个萝卜一个坑”。我们将0~9中的每个数都用单独1个房间来表示,每出现一个数,就将所对应的房间中的值改为1,最后只要看看哪些房间里面的值仍然是0就好了。

就好比原来有10个萝卜,从0~9编号:

img

然后安排人去拔萝卜,第1个人去拔2号萝卜。第2个人去拔5号萝卜,第3个人再去拔2号萝卜(其实此时2号萝卜已经被拔走了),第4个人去拔1号萝卜,第5个人去拔8号萝卜,最后剩下的萝卜的就是答案了。是不是很简单呢?

img

下一个问题:如果现在需要将输入的5个数(范围是0~9)从小到大排序,该怎么办?例如,输入2 5 2 1 8,则输出1 2 2 5 8。

也很简单,只需将上面的代码稍加改动就可以了。

首先我们仍然需要申请一个大小为10的数组int a[10],编号为a[0]~a[9],并初始化为0。

img

在之前的程序中,哪个数字出现了,我们就将相应的小房间的值从0变为1。而现在我们只需将“小房间的值从0变为1”改为“小房间的值加1”就可以了。例如,2出现了,就将a[2]中的值加1。

img

接下来的数是5,就将a[5]中的值加1。

img

到目前为止,貌似和之前的程序没什么不同。下面,关键的一步来了。下一个出现的数又是2,我们再将a[2]中的值加1。

img

注意到没有,此时a[2]中的值为2。

接下来的数是1,就将a[1]中的值加1。

img

最后一个数是8,将a[8]中的值加1。

img

发现没有,其实a[0]~a[9]中所记录的数值就是0~9每个数所出现的次数。其中1出现1次,2出现2次,5出现1次,8出现1次。

接下来,我们只需将出现过的数,按照出现的次数打印出来就可以了。具体如下:

a[0]为0,表示0没有出现过,不打印。

a[1]为1,表示1出现过1次,打印1次。屏幕上显示“1”

a[2]为2,表示2出现过2次,打印2次。屏幕上显示“1 2 2”

a[3]为0,表示3没有出现过,不打印。

a[4]为0,表示4没有出现过,不打印。

a[5]为0,表示5出现过1次,打印1次。屏幕上显示“1 2 2 5”

a[6]为0,表示6没有出现过,不打印。

a[7]为0,表示7没有出现过,不打印。

a[8]为1,表示8出现过1次,打印1次。屏幕上显示“1 2 2 5 8”

a[9]为9,表示9没有出现过,不打印。

    #include <stdio.h>
    #include <stdlib.h>
    int main()
    {
        int a[10], i, j, t;
        for(i=0; i<=9; i++)
          a[i]=0; // 初始化为0

        for(i=1; i<=5; i++)  // 循环读入5个数
        {
            scanf("%d", &t); // 把每一个数读到变量t中
            a[t]++;          // t所对应小房子中的值增加1
        }

       for(i=0; i<=9; i++)    // 依次判断0~9这个10个小房子
            for(j=1; j<=a[i]; j++)   //出现了几次就打印几次
                  printf("%d ", i);

        system("pause");
        return 0;
    }

至此,我们已经巧妙地将输入的数据,按照从小到大的顺序排序了。当然,你也可以从大到小排序,自己想一想吧!

尝试一下,输入n个0~1 000的整数,将它们从小到大排序。如果想对1 000以内的整数排序,我们需要1 001个小房子来表示每个数出现的次数,定义时要注意哦。

    #include <stdio.h>
    #include <stdlib.h>
    int main()
    {
        int a[1001], i, j, t, n;
        for(i=0; i<=1000; i++)
          a[i]=0;
        scanf("%d", &n);
        for(i=1; i<=n; i++)
        {
            scanf("%d", &t);
            a[t]++;
        }
        for(i=0; i<=1000; i++)
            for(j=1; j<=a[i]; j++)
                  printf("%d ", i);

        system("pause");
        return 0;
    }

例如,输入:

  10
  1 10 100 1000 2 20 200 3 30 300

程序将会输出:

    1 2 3 10 20 30 100 200 300 1000

第6节 选择排序

先来回顾一下3个数的排序。从键盘读入3个数并分别放入变量a、b、c中。

第1轮,先将a与b进行比较,把a和b中较大的一个放在a中。再将a与c进行比较,把a和c中较大的一个放在a中,到此第一轮结束。我们可以确定小房子a中存储的数一定是原先3个数中最大的。

img

下面开始第2轮,比较小房子b中的数和小房子c中的数,将较大的数放在小房子b中。

img

经过3轮比较,我们终于排序完毕,最大的数放在小房子a中,次大的数放在了小房子b中,最小的数放在小房子c中。

好,这次将77、45、26、86和9这5个数从小到大排序,请注意,我们现在是进行从小到大排序。

首先确定第1位上的数。

img

完整的排序过程如下(2、3、4轮均为模拟上述的方法产生的结果):

    初始数据      [77  45   26  86   9]
    第1轮排序后     9 [77   45  86  26]
    第2轮排序后     9  26 [77   86  45]
    第3轮排序后     9  26   45 [86  77]
    第4轮排序后     9  26   45  77 [86]
    最后结果        9  26   45  77  86

怎么样,算法理解了没有?接下来看看如何用代码实现。

我们可以用整型数组来存储这5个数,即int a[6];,其中我们不用a[0],只使用a[1]~a[5]。因为我个人比较喜欢从a[1]开始,当然若你喜欢也可以从a[0]开始。

    int a[6], i;
    for(i=1; i<=5; i++)
          scanf("%d", &a[i]);

对于a[1]来说,它需要和a[2]、a[3]、a[4]、a[5]比较。

对于a[2]来说,它需要和a[3]、a[4]、a[5]比较。

对于a[3]来说,它需要和a[4]、a[5]比较。

对于a[4]来说,它只需要和a[5]比较。

如果只对5个数进行排序,只需进行4轮,因为若前4个数排好了,剩下的1个一定在最后。

我们来抽象一下,对于a[i]来说,它需要和a[i+1]、a[i+2]……a[5]比较。

    for(i=1; i<=4; i++)       //对于5个数来说,只需要进行4轮,确定前4位
    {
      for(j=i+1; j<=5; j++)   //a[i]需要和a[i+1]、a[i+2]……a[5]比较
      {
            if(a[i]>a[j])     //这里是从小大到大排序
            {
                t=a[i]; a[i]=a[j]; a[j]=t;  //交换数值
            }
      }
    }

完整的代码如下:

    #include <stdio.h>
    #include <stdlib.h>
    int main()
    {
        int a[6], i, t, j;
        for(i=1; i<=5; i++)
              scanf("%d", &a[i]);
        for(i=1; i<=4; i++)
        {
            for(j=i+1; j<=5; j++)
            {
                if(a[i]>a[j])
                {  t=a[i]; a[i]=a[j]; a[j]=t;  }
            }
      }
      for(i=1; i<=5; i++)
          printf("%d ", a[i]);
      system("pause");
      return 0;
    }

我们将上面的代码稍微改动一下,就可以实现输入n个数,并将这n个数按照从小到大或者从大到小的顺序输出,自己去尝试一下吧!

第7节 二维数组

img

我们之前学习的都是一维数组,可是如果想表示一个“围棋的棋盘”或者我们在第2章第1节说到的“数独”时,就希望能有一种方法来表示二维的矩阵。二维数组正好可以解决这个问题。

img

例如,我们需要表示上面这个3行4列的矩阵该怎么办呢?很简单:

    int a[3][4];

上面这行语句的作用是定义一个二维数组,它有3行4列,分别是a[0]行、a[1]行和a[2]行。其实你可以把这个二维数组理解为由3个一维数组叠加而成(这3个一维数组分别是a[0]、a[1]和a[2])。而每1个一维数组又都有4列,分别是第[0]列、第[1]列、第[2]列和第[3]列。

img

那么如何使用这个二维数组呢?很简单,例如,第0行第0列就是a[0][0],第1行第2列就是a[1][2]……

好了,小小地总结一下:int a[3][4];这条语句中第1个参数表示有多少行,第2个参数表示有多少列。因为是从0开始计数的,因此左上角第1个是a[0][0],右下角最后1个是a[2][3]。

img

我们可以通过“两个for循环嵌套”来为这个二维数组赋值:

    #include <stdio.h>
    #include <stdlib.h>
    int main()
    {
        int a[3][4], i, j, x;
        x=0;
        for(i=0; i<=2; i++) //i循环用来控制行数
        {
            for(j=0; j<=3; j++)  //j循环用来控制列数
            {
                a[i][j]=x;
                x++;
            }
        }

        for(i=0; i<=2; i++)
        {
            for(j=0; j<=3; j++)
            {
                      printf("%d ", a[i][j]);
            }
            printf("\n"); //一行打印完毕需要换行
        }
        system("pause");
        return 0;
    }

上面这段代码的效果如下:

img

第8节 剩下的一些东西

本节我们来认真聊一聊数组的初始化,先来聊一维数组的。

假如我们需要申请一个大小为10的整型数组,并将数组中每一个“小房间”的值依次初始化为0~9,通过之前学习的知识我们可以这样写:

img

    int a[10], i;
    for(i=0; i<=9; i++)
      a[i]=i;

其实还有一个简便写法:

    int a[10]={0,1,2,3,4,5,6,7,8,9};

假如需要将数组中所有“小房间”都初始化为0,我们也可以这样写:

    int a[10]={0,0,0,0,0,0,0,0,0,0};

简便写法为:

    int a[10]={0};

你可能要问,如果我们把数组中的所有“小房间”都初始化为1的话,是不是也可以这么写呢?形如:

    int a[10]={1};

很不好意思,这样写的效果是只有a[0]的值为1, a[1]~a[9]的值为0。

img

为什么呢?不要着急,请你再运行一下下面这段代码,猜一猜a[0]~a[9]的值分别是多少?

    #include <stdio.h>
    #include <stdlib.h>
    int main()
    {
        int a[10]={7,9,8}, i;

        for(i=0; i<=9; i++)
          printf("%d ", a[i]);
        system("pause");
        return 0;
    }

运行之后你会发现a[0]的值为7, a[1]的值为9, a[2]的值为8, a[3]~a[9]的值都为0。

img

其实在定义数组时对数组进行初始化,编译器会从a[0]开始按顺序进行赋值,后面没有具体值的将默认为0。这样你就能理解为什么全部初始化为0时可以写成int a[10]={0};但是全部初始化为1就不能写成int a[10]={1};了。

你可能要问,如果只定义一个数组而不进行任何初始化,那么这个数组里面的每一个“小房间”的默认值会是什么呢?答案是:随机值。不信就试一试吧。请运行下面的代码:

    #include <stdio.h>
    #include <stdlib.h>
    int main()
    {
        int a[10], i;

        for(i=0; i<=9; i++)
          printf("%d ", a[i]);
        system("pause");
        return 0;
    }

下一个问题:二维数组如何进行初始化呢?请看下面的代码:

    #include <stdio.h>
    #include <stdlib.h>
    int main()
    {
        int a[3][5]={{1,2,3}, {4,5}}, i, j;
        for(i=0; i<=2; i++)
        {
          for(j=0; j<=4; j++)
          {
            printf("%d ", a[i][j]);
          }
          printf("\n");
        }
        system("pause");
        return 0;
    }

运行效果如图6-1所示。

img

图6-1 二维数组初始化运行结果

这个数组有3行5列,第1行我们初始化了前3个,第2行我们初始化了前两个,剩下的将全部默认为0。需要注意的是,在初始化每1行时,每1行都要用{ }括起来才行。如果不用{ }括起来的话,会怎么样呢,自己去试一试吧!

    #include <stdio.h>
    #include <stdlib.h>
    int main()
    {
        int a[3][5]={1,2,3,4,5}, i, j;
        for(i=0; i<=2; i++)
        {
          for(j=0; j<=4; j++)
          {
            printf("%d ", a[i][j]);
          }
          printf("\n");
        }
        system("pause");
        return 0;
    }

运行效果如图6-2所示。

img

图6-2 二维数组初始化运行结果

0

评论区