12天学好C语言——记录我的C语言学习之路(Day 7)

12天学好C语言——记录我的C语言学习之路



Day 7:



昨天进行了一天的数组学习,今天大家可以先写几个昨天的程序热热身,回顾回顾,然后今天第一个新程序也是关于数组的,比较难,准备好就开始啦!



//输出奇数魔方阵,每一列、每一行以及对角线之和均相等(输入n为魔方阵的阶数,n为奇数)



//首先说明一下魔方阵的形成规律:①将元素1放在第一行中间一列;②依次将数字放在前一个数字的右上角,如果超过了魔方阵的范围,就拿另一侧的位置补齐,这个步骤需要想象成一个可以穿过边界到达另一头的贪吃蛇游戏;③如果另一侧补齐的位置处有元素在,则不考虑这个位置了,就直接把元素写在上一个元素的正下方的位置



//程序分析,我们是分了四部分来写这个方阵的形成规律。①右上角②第一行除右上角③最后一列除右上角④左下角剩下的所有元素(这些元素是最普通的,无特殊处理,顶多就是如果下一个元素位置上面不为0,那么就直接写在正下方)



/* 如下图是五阶魔方阵

 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

 */



/*//program 7.1

 #include "stdio.h"

 int main()   //这个程序是下标从0开始的

 {

 printf("请输入数字n,n代表魔方阵的阶数:\n");

 int a[20][20]={0},i,j,k,n;

 i=0;

 scanf(“%d",&n);   //输入魔方阵的维度n

 j=n/2;     // j是维度的一半取整,这里一定要注意,如果下标是从0开始的,那么3阶的时候元素1的位置就是(0,1),5阶的时候元素1的位置就是(0,2),可见元素的j坐标是n/2。如果下标从1开始的话,j=n/2+1。注意注意!!!!!

 a[i][j]=1;   //确定第一排的中间一个数为1

 for (k=2; k<=n*n; k++)   //从2开始,一直到n*n,按规律存放

 {

 i=i-1;   //正常情况下的走向是往右上角走,那么确定一个值的位置后(i,j),它的下一个值坐标正常情况下应该为(i-1,j+1)

 j=j+1;

 if(i<0&&j>n-1)   //最后的问题就是i<0的情况难道不就是i=-1吗?为什么不能用i=-1来代替i<0??? 这里说的很对,i<0的情况只有一个,就是i=-1,但是我们写判定条件的时候,要这样写if(i==-1&&j>n-1),也就是两种情况都可以。

 {   //如果该值已经在整个方阵的右上角(上图元素15的位置,第一行、第n列),那么放在这个值的正下方,又因为之前经过了行数-1,列数+1的变化,所以要变回原值的正下方,故得到下面两个变化。

 i=i+2;

 j=j-1;

 }

 if(i<0&&j<=n-1)

 {   //如果该值是处于第一行的位置(但是不包括上个条件排除的右上角的值),那么它的下一个值就应该处于最底行。

 i=n-1;

 }

 

 if(j>n-1)

 {   //如果该值是处于第n-1列(最后一列)的位置,那么它的下一个值一定处于第一列了

 j=0;

 }

 if (a[i][j]==0)

 {   //如果计算出来的位置上面还没有赋值,那么就将该数值赋给该位置

 a[i][j]=k;

 }

 else

 {   //如果计算出来的位置上面已经有数值存在了,那么也是将下一个数值放在其上一个数值的正下方

 i=i+2;

 j=j-1;   //这个地方其实也是是对变化过的i、j进行再操作

 a[i][j]=k;

 }

 }

 for (i=0; i<n; i++)

 {

 for (j=0; j<n; j++)

 {

 printf("%-4d",a[i][j]);

 }

 printf("\n");

 }

 return 0;

 }

*/



上面那个程序的下标是从0开始的(第0行,第0列),可能有些刚打程序的读者有些别扭,那么下面我用下标从1开始写一下这个魔方阵程序,改了初始下标,希望读者能看出来程序哪里发生了实质上的改变。



/*//program 7.2

#include<stdio.h> //这个程序是下标从1开始的

 int main()

 {

 int a[20][20]={0},i,j,n;

 i=1;

 printf("请输入一个数");

 scanf("%d",&n);//输入魔方阵的维度n

 j=n/2+1;     // j是维度的一半加1.

 a[i][j]=1;   //确定第一排的中间一个数为1

 for(int k=2;k<=n*n;k++)//已经确定1的位置了,再循环确定2~n*n的位置

 {

 i=i-1;      //挪位,横坐标往上挪一位。

 j=j+1;      //挪位,纵坐标往右挪一位。

 if((i<=0)&&(j<=n))   //如果横坐标挪到顶,同时纵坐标还没有超过最右,横坐标就到从最下再继续。原位置是第一行 除了 右上角的

 i=n;

 if((i<=0)&&(j>n)) //如果横坐标挪到顶,同时纵坐标超过最右,横坐标往下挪两位,纵坐标排往左移一位。原位置是第一行右上角

 {

 i=i+2;

 j=j-1;

 }

 if(j>n)  //如果只有纵坐标超过最右,纵坐标挪到左边第一行。

 {

 j=1;

 }

 if(a[i][j]==0)

 a[i][j]=k;  //如果这个位置还没有赋值,那么赋值为k。

 else        //已经赋值过了。那么竖排往下挪两位,横排往左移一位,再赋值为k。

 {

 i=i+2;

 j=j-1;

 a[i][j]=k;

 }

 }

 for(i=1;i<=n;i++) //循环输出位置。

 {

 for(j=1;j<=n;j++)

 printf("%5d",a[i][j]);

 printf("\n");

 }

 }

*/



魔方阵程序不难,如果说读者一时消化不了,可能难点就在魔方阵的算法难理解,还有就是对临界点几个情况的分析。另外一旦下标初始值发生改变了,可能就单单改了一个从0开始还是从1开始,就又让人迷糊一阵子。这里慢慢琢磨总能琢磨出来的,现在有程序给大家参考,想起来还是比较方便的,可以先把程序拷下来运行一下,然后自己理解了这道题的含义在自己试着去敲。希望大家能牢牢的掌握这个程序。



下面这个程序是要输出一个平行四边形的。这个程序有一个地方我觉得初学者可能会犯错,给大家拿出来说一下。



*****     

 *****    

  *****   

   *****  

    *****

 

输出上面的图形。

         

/*//program 7.3

#include<stdio.h>

int main()

{

    int i,j;

    char a[10][10];

    for(i=0;i<10;i++)    //先把每一个数组中的元素都赋上空格~然后把需要替代的元素用'*'替代就好了

        for (j=0; j<10; j++) {

            a[i][j]=' ';

        }

    

    for (i=0; i<=4; i++) {

        for (j=i; j<=i+4; j++) {

            a[i][j]='*';

            // printf(“%c",a[i][j]);    //不要赋值一个就输出一个,你输出的只是你赋值的这些元素,而那些被初始化为空格的元素是没有输出的,所以就会出现输出一个长方形的现象(这就是读者需要注意的一个地方,在此我已经注释掉了)

        }

        //printf("\n");

    }

    for (i=0; i<10; i++) {

        for (j=0; j<10; j++) {

            printf(“%c",a[i][j]);    //把该赋值的都赋值完了,然后将所有元素输出,自然得到想要的结果了~

        }

        printf("\n");

    }

    return 0;

}

*/



数组方面的学习到此为止了,用了一天多点的时间,我觉得学习重在自己的理解和练习,只要是打够代码,一定能在短时间里有质的提升。下面我们开始函数的学习:



//   注意一下无参函数和有参函数的调用形式

//   abc(); 这是调用无参函数      c=def(a,c,v); 这是调用有参函数(注意把结果要赋给一个变量),当然函数调用本身并不一定非要有分号或者是把结果赋给一个变量,如:printf(“%d",max(a,b));



一个小程序(program 7.4)让大家感受一下函数。

对了,也不知道提醒没提醒过大家,max函数是自己人为写的,可没有什么头文件中含有这个函数的,不要直接就用,虽然这个函数作用很简单



/*//program 7.4

 #include "stdio.h"

 int main()

 {

 int max(int ab,int cd);    //ab、cd都是形参

 int a,b,k;     //这里是定义变量

 scanf("%d%d",&a,&b);

 k=max(a,b);    //a、b是实参,调用函数调用的是实参

 printf("%d",k);

 return 0;

 }

 int max(int x,int y){

 int c;

 c=(x>y)?x:y;

 return c;

 }

 */



下面我们来学习一下函数的嵌套,看程序 program 7.5



/*//program 7.5

//输入四个数,找出最大值,用函数的嵌套实现。

#include "stdio.h"

int max1(int a,int b,int c,int d);

int max2(int a,int b);

int temp;

int main()

{

    int a,b,c,d;

    scanf("%d%d%d%d",&a,&b,&c,&d);

    a=max1(a,b,c,d);    //可以把最后的结果赋给a,都是形参

    printf("max is %d\n",a);

    return 0;

}

int max1(int f,int b,int c,int d)    //这里的f,b,c,d都是形参

{

    int m;

    m=max2(f,b);    //应用了函数的嵌套,从上到下进行读取执行

    m=max2(m,c);

    m=max2(m,d);

    return m;

}

int max2(int a,int b)

{

    if (a<b) {

        temp=a;

        a=b;

        b=temp;

    }

    return a;

}

*/



当然上面的方法感觉还是太过繁琐,我们还可以再优化一下:



/*//program 7.6

//对上题的改进。

#include <stdio.h>

int main()

{

    int max3(int,int);

    int a,b,c,d;

    scanf("%d%d%d%d",&a,&b,&c,&d);

    printf(“%d",max3(max3(max3(a,b),c),d));    //多重嵌套,这样只需要写一个函数max3就可以了~简单明了

    return 0;

}

int max3(int a,int b)

{

    int temp;    //temp必须在这里声明,而不能在主函数中声明,两边不扯

    if (a<b) {

        temp=a;

        a=b;

        b=temp;

    }

    return a;

}

 */



在看这样一个题目,关于函数的 递归调用。



//有5个人坐在一起,问第五个人多少岁?他说比第4个人大2岁。问第4个人岁数,他说比第3个人大2岁。问第三个人,又说比第2人大两岁。问第2个人,说比第一个人大两岁。最后问第一个人,他说是10岁。请问第五个人多大?(program 7.7)



/*//program 7.7

#include <stdio.h>

int main()

{

    int a,n;

    scanf("%d",&n);

    int age(int n);

    a=age(n);

    printf("age=%d",a);

    return 0;

}

int age(int n)

{

    int c;   //注意,这里函数名和变量名不能重名

    if (n==1) {

        c=10;

    }

    else

    {

        c=age(n-1)+2;

    }

    return c;

}

*/

   

程序比较简单,读者耐心看一下就能看懂。



那么再做一个题目来加深一下今天学习过的递归问题。

 

//用递归方法求n!



/*//program 7.8

#include <stdio.h>

int main()

{

    int n,a;

    scanf("%d",&n);

    int mul(int n);

    a=mul(n);

    printf("n!=%d",a);

    return 0;

}

int mul(int n)

{

    int m;

    if (n==1||n==0) {

        m=1;//不管n多大,n都肯定要慢慢减少最终等于1的,而最后n=1时,m是1,这个m是mul(1),而不是取代了之前算出来的m的值,递归的意义就在此。也可以这么想,最后的m=1是最内层的m,最内层的m求出来了,然后逐渐扩大到最外层的也就是最终的结果了

    }

    else

    {

        m=n*mul(n-1);

    }

    return m;

}

 */



最后一个程序结束今天的学习,这个题目比较的抽象,也是递归题目里面必做的一道题,那就是Hanoi塔问题,让我们一起来尝试一下吧。



/*//program 7.9

//Hanoi塔问题

#include <stdio.h>

int main()

{

    int n;

    scanf("%d",&n);

    void Hanoi(int n,char a,char b,char c);   //把m个盘子从 圆柱a 上通过 圆柱b 移至 圆柱c

    Hanoi(n,'A','B','C');

    return 0;

}

void Hanoi(int n,char a,char b,char c)

{

    void move1(char a,char b);    //Hanoi函数中需要用到Move1函数,所以Move1函数要在Hanoi函数中声明一下

    

    if(n==1)

    {

        move1(a,c);

    }

    else

    {

        Hanoi(n-1, a, c, b);    //想到这里的时候,一定得想到递归的逻辑是什么,做递归的题目,不要带入数进行验证,只要把递归到最底层的值写出来之后,剩下的就是n到n-1之间的递归,我认为此时可以在脑海里念着怎么移动,用通俗的语句来表示这复杂的代码

        move1(a, c);

        Hanoi(n-1, b, a, c);    //这句话就可以这样理解“将n-1个盘子从b通过a移动到c上”

    }

}

void move1(char a,char b)    //这里就不用解释了吧,将这个程序分解开来其实简单不过了

{

    printf("将一个盘子由%c移至%c\n",a,b);

}

*/



好了,第七天的学习结束了,不知道大家掌握的怎么样,希望今天的学习大家也能够牢牢的记在心里,一定要多做题,一道题再简单敲一遍是不够的,多多敲几遍,你会有不同的收获!

版权声明:本文为博主原创文章,未经博主允许不得转载。

上一篇:PHP:获取指定日期所在月的开始日期与结束日期


下一篇:ASP.NET Web API下Controller激活