C语言博客作业04数组

0.展示PTA总分

0.1.一维数组

0.2.二维数组

0.3.字符数组


1.本章学习总结

1.1 学习内容总结

一维数组.

定义.
类型标识符 数组名[常量表达式]
说明.

  • 数组名的命名规则与变量名的命名规则一致
  • 常量表达式表示数组元素的个数。可以是常量或符号常量,不能为变量

初始化.

  • int a[5]={1,2,3,4,5}
  • int a[6]={1,2,3} (未被初始化的后面的元素被系统自动赋值为0)
  • int a[9]={0}

二维数组.

定义.
类型表示符 数组名[表示行数的表达式][表示列数的表达式]
说明.
行数可以不说明,但是一定要说明列数
初始化.

  • int a[2][3]={1,2,3,4,5,6}
  • int a[2][3]={ {1,2,3},{4,5,6} }

字符串数组.

定义.
char 数组名[数据长度]
说明.
字符型数据是以整数形式(ASCII代码)存放的,因此也可以用整型数组来存放字符数据。
初始化.
char a[8]={'i','l','o','v','e','y','o','u'}

数组查找数据.

  • 遍历数组,用if语句判断数组中的数是否与寻找的数字相等。
  • 灵活使用flag,判断有无需要查找的数组。
定义数组变量a[10]用来储存输入10个之内的数字
定义变量x用来储存需要寻找的数字
定义变量flag=0用来判断有无需要寻找的数字
for int i=0 to 10 do i++
    if(a[i]==x) then 
        flag=1 //找到x,flag重新赋值用以输出找没找到
        break //找到X,就跳出循环,减少运行时间
    end if
end for
if(flag==1) then 
    输出“找到了”
else   //flag初始化为0,如果if条件不满足不会进入下面的语句,flag的值一直为0 
    输出“没找到”
end if

数组插入数据.

  • 该题的前提是有序数组(从大到小或者是从小到大),首先找到插入的位置,因为是遍历数组,只要判断插入的数字大于(或者小于)数组中的数字即可,找到插入的位置后,break结束循环。
  • 将数组从最后一个数字开始往后挪(不能从插入位置开始往后挪,因为把每个数字往后移一个后,原来该位置上的数字就会被顶替掉,数据会丢失)。
  • 再将X赋值给插入的位置。
定义数组变量Num[n]用来储存输入的有序数列
定义变量X用来储存插入的数字
for int i=0 to n do i++ 
    if(a[i]>X) then  //假设该数列是从小到大的排序,如果是从大到小排序的数列,判断条件为a[i]<X
        break //找到插入位置就退出循环,这个时候就不在对i做任何运算要确保i为插入数字的位置
    end if
end for
for int j=n-1 to i do j-- (这里的i也要进行后移)
    a[j+1]=a[j] //因为这里要插入一个数,定义数组的时候尽量大一点,防止越界
end for
a[i]=X //完成后移的循环后,i位置上的数字就空了用来把X放在该位置上
输出插入X数字的"a[i]"数组

数组删除数据.

  • 两个数组删除数据,一个数组储存输入的数据,另一个数组储存删除后的数据。
  • 一个数组删除数据,在输入数据的数组中做变换。

两个数组删除数据.

定义数组Num1[n]用来储存输入的数字
定义数组Num2[n]用来储存删除数据后的数字
定义变量X用来储存要删除的数字
for int i=0 to n do i++
    if(Num1[i]!= X)then
        Num2[(int j=0)++]=Num1[i] //把不要删除的数字储存到Num2数组中
    end if
end for
输出删除X数字的"Num2[j]"数组

一个数组删除数据.

定义数组变量a[n]用来储存输入的数字
定义变量X用来储存删除的数字
for int i=0 to n do i++
    if(a[i]==X) then //如果数组中的元素有要删除的数字,就把后面的元素依次向前移一个位置,这样就可以把那个需要删除的数字顶掉
        for int j=i to n do j++
            a[i]=a[i+1] 
        end for
    end if
end for
输出删除X数字的"a[i]"数组

注解
两种代码各有优劣,只使用一个数组来删除数据,可以减少一个数组变量,整个代码的内存可以减少,但是这种方法也容易出错,使用不恰当会丢失后面的数据,替换的时候容易出错(像数组的倒序一样);使用两个数组删除数据比一个数组要更容易一点,这种方法的代码只需要遍历输入的数组,判断是否与要删除的数字相等,不相等就存进另一个数组。我个人更偏向二个数组的方法,简单明了。


排序方法.

  • 选择法排序:每一次内层循环找到最值,记录最值所在位置的下标,内层循环结束后,交换最值所在的位置和外层循环当前所在的位置。
  • 冒泡法排序:相邻两个数字依次做比较,并做交换,每一次内循环的结果就是把最大的数排列在数组的最后面。

选择法排序.
代码1

代码2

运行结果1

运行结果2

注解
两个代码的功能是一样的,但是代码二的运行时间会更长,因为它会把前面的数大于后面一个的数字进行交换,交换一轮循环。其实这样的循环完全没必要,这种循环更像是冒泡法排序的方法,最大的数经过一轮循环后在最后面。消耗的时间更多。代码一是直接找到最小数的下标,结束内层循环后再进行交换,也就是说代码一的内层循环只是用来寻找最小数所对应的下标,不进行任何的赋值计算,这样运行更快。代码二是我之前自己打PTA的时候写的代码,看过书之后就知道代码一的优化功能了,所以编程技巧要慢慢的学到后面去掌握以及每一个程序的优化。

冒泡法排序.
代码

运行结果

两种排序法的区别
不同:冒泡法顾名思义就是把最大的数沉在底下,最小的浮在上面,每一次循环都要进行交换;选择法是在冒泡法的基础上进行简化,它不着急交换,只是记录最值的位置,在外层循环才来交换。这样的简化可以减少运行时间。
相同:都是用来排序的方法;两者都是要逐个比较的,找出最值。


数组做枚举用法.

数组的下标是从0开始累计加一和枚举变量一样,枚举变量中是默认第一个变量的数值为0,之后的变量依次累加1。所以可以用枚举的方式表达数组的下标。

(忽略这些红色的线条,VS不支持这个没得法,但是代码本身没有错!!!)


哈希数组.

哈希表,是根据关键码值而直接进行访问的数据结构。也是就说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度(来自百度)。我不是很懂这个哈希算法是啥,有点无助.....老师发布的题集里面好像有一道重复数据的题目:要将一个数组的元素作为另一个元素的下标。


1.2 本章学习体会

学习感受.

老师在讲数组这一章之前,我把课本以及慕课翁恺老师的视频反反复复的看,看了三天左右,颗粒无收,能看懂老师写的代码,也能看懂课本上的代码,但是自己写不出来。(可能是本人的脑子原因,不知道为什么当时咋都不明白数组的使用,现在想想都觉得自己那个时候挺傻的。)数组的用法很广泛,在结构里面用的也比较多。数组相当于一个容器,依次把你输入的数据有序的储存出来,当你要使用它时候,用对应的下标表达即可。从数组开始,一直到后面的指针、结构、链表等等,这些内容都是C语言的灵魂(借用林老师的话)。我最直观的感受就是:C语言的大门从数组开始才是真正意义上打开了。前面所学的一切都是在为后面的精华做准备。不得不说,编程语言有一种魔力。在我没有接触它之前,编程语言离我很遥远,不选择关于计算机方面的专业,我怕是会离它远远的,但是现在,我拜倒在它的石榴裙下,它把我征服了。当时我看不懂数组的时候,内心慌得一批,恐惧了。但是怎么说呢,既然是人编的东西,那我们肯定就能看懂,我硬着头发去阅读了一些别人的写的规范的代码,找到了其中的秘诀,后面就慢慢的顺了。其实不只是数组,后面的难的内容,不是我看个一两遍就会的,这可能算是正常的,但是千万不要灰心,看不懂就一直看,不丢人,看到懂为止。中国人的阿Q精神是很强大的。很喜欢一句话,哪有什么天才,不过是百分之九十九的汗水加百分之一的天赋。既然选择远方,便只顾风雨兼程

代码量.


2.PTA实验作业

2.1 一维数组-数组循环左移

2.1.1 伪代码

定义一个数组a[n]用来储存输入的序列数字
定义变量n用来储存输入的数字的总个数
定义一个变量m用来储存移动的位数
m=m%n //判断移动位数与输入总个数的关系
if(m==n) then  //移动位数是总个数的整数倍
     原样输出"a[n]"数组
else  //输出移动后的数组
    for int i=m+1 to n do i++
        依次输出数组元素"a[i]"
    end for
    for int i=0 to m do i++
        输出剩下的数组元素"a[i]"
    end for
end if

2.1.2 代码截图

2.1.3造测试数据

2.1.4 PTA提交列表及说明

Q1:没有考虑到m比n大导致一个测试点过不去。
A1:直接加上m=m%n的表达式后是直接按移动后的数据输出。
Q2:加上Q1的表达式后又忽略是m是n的整数倍的关系报错另一个测试点。
A2:整个函数功能要分为两步,一个是m是n的整数倍关系原样输出,另一个是用取余表达式移动后的数据输出。使用取余表达式可以解决m与n大小的问题。

2.1'二维数组-数组循环右移(与左移进行对比)

2.1'.1伪代码

定义一个数组a[7][7]用来储存输入的N阶数组
定义一个数组b[7][7]用来储存右移后的数组
输入右移M个位置和N阶方阵
for int i=0  to n do i++
    for int j=0 to n do j++
        输入a[i][j]
    end for
end for
for i=0 to n do i++
    for j=0 to n do j++
        b[i][(j+M)%n]=a[i][j] //这里就要求我们找到右移的规律,只要找到规律就很快了,这也是数组的神奇之处!!!
    end for
end for
for i=0 to n do i++
    for j=0 to n do j++
        输出"b[i][j]"
    end for
end for

2.1'.2代码截图


2.2 二维数组-螺旋方阵

2.2.1 伪代码

定义二维数组a[20][20]用来储存输入的方阵
定义变量t=0用来累加赋值给方阵中的元素
定义一个变量count=0用来计算方阵中元素改变的次数
定义二维数组b[20][20]用来判断a[20][20]中数字有没有被替换
for int i=0 to 20 do i++
    for int j=0 to 20 do j++
        b[20][20]初始化所有元素均为1
    end for
end for
输入n,表示是n阶方阵
for i=0 to n do i++
    for j=0 to n dp j++
        b[n][n]赋值为0//表明这些元素要填入合适的数字
    end for
end for
for i=0 to n do i++ 
    for j=0 to n do j++ 
        b[n][n]赋值为0
    end for
end for
while(count< n*n) do
    while((int y=0) < n &&b[(int x=0)][y] ==0) do
        将t赋值给a[x][y]
        令b[x][y]为1//表明a方阵的该元素已经是被成功赋值了
        t++;y++;count++;
    end
    y--;x++
    while(y < n &&b[(int x=0)][y] ==0) do
        将t赋值给a[x][y]
        令b[x][y]为1//表明a方阵的该元素已经是被成功赋值了
        t++;x++;count++;
    end
    x--;y--
    while(y >=0 &&b[(int x=0)][y] ==0) do
        将t赋值给a[x][y]
        令b[x][y]为1//表明a方阵的该元素已经是被成功赋值了
        t++;y--;count++;
    end
    x--;y++;
     while(x >=0 &&b[(int x=0)][y] ==0) do
        将t赋值给a[x][y]
        令b[x][y]为1//表明a方阵的该元素已经是被成功赋值了
        t++;x--;count++;
    end
    x++;y++;
end
完成上述循环后,输出n阶螺旋方阵"a[n][n]"

2.2.2 代码截图

2.2.3造测试数据

2.2.4PTA提交列表及说明

这道题目,我出现的错误不止提交列表那么简单,我就不按照提交列表来说了。
Q1:当时思路没有理清楚,输入出来的是行式S形方阵
A1:我当时没找到螺旋方阵的规律,改了一种循环方式,输出来的又是列式S形方阵。
Q2:继A1之后又是一番大改动,可是仍旧输出来的是别的类型方阵,还有一个循环越界了。
A2:经历了N多次这样的大改动循环仍旧无故,然后我只能取百度了,借鉴别的优秀的代码,当时还没怎么明白那个螺旋的规律就开始去折腾自己的代码。
Q3:是的,没错,输出的依然是乱七八糟的方阵,毫不夸张的说--乱七八糟,当时心里急得一批,脑子都炸了......就差撞豆腐了
A3:错了总是要改错的勒,万能的度娘又来了,又是去网上找答案咯,看网上的代码,慢慢的知道螺旋的规律,就自己在纸上罗列了那个规律,慢慢的就把那个代码差不多快写出来了....
.
. 这处省略复杂的调试过程
.
部分正确:好不容易把样例的正确答案搞出来了,一提交,OMG,最大的N那个测试点过不了,那个心情真的是想用面条上吊啊!!!
解决方式:我们的助教,隆重出场了,助教说,数组的大小写大一点,虽然题目的要求是小于10的N阶方阵,但是数组的范围大一点会比较好。所以把数组写为了20的范围,然后我又改了一个条件
部分正确:之前的那个条件改错了,再加上没有把b[n][n]初始化为0导致的错误。
解决方式:把前面两个错误修正,让b[n][n]赋值为0的原因是判断改位置是否填入对应的t。

2.3字符串数组-大数加法

2.3.1伪代码

定义字符串数组a[1005]用来储存输入的其中一个相加的大数
定义字符串数组b[1005]用来储存输入的另一个相加的大数
定义数组a1[1005]用来储存倒序的数组a
定义数组b1[1005]用来储存倒序的数组b
定义数组c1[1005]用来储存两个数组中长度较长的倒序后的数组元素
定义变量len1记录数组a的长度
定义变量len2记录数组b的长度
定义变量flag=0用来进位计算
if(len1 > len2) then 
    for int i=0 to len1 do i++
        c[i] = c[i] + b1[i] + flag
        if(c[i] >= 10)
            c[i] = c[i] - 10
            flag=1//如果某一位相加超过十,则上一位对应加1
        else
            flag=0
        end if
    end for
    if ((c[len1 - 1] + b1[len1 - 1]) >= 10) then
        输出"1" //最高位超过十,输出1,不往数组里面写元素是怕会越界
    end if
    for i = len1 -1 to 0 do i--
        输出"c[i]"
    end for
else
    for int i=0 to len2 do i++
        c[i] = c[i] + b1[i] + flag
        if(c[i] >= 10) then 
            c[i] = c[i] - 10
            flag=1//如果某一位相加超过十,则上一位对应加1
        else
            flag=0
        end if
    end for
    if ((c[len2 - 1] + b1[len2 - 1]) >= 10)
        输出"1" //最高位超过十,输出1,不往数组里面写元素是怕会越界
    end if
    for i = len2 -1 to 0 do i--
        输出"c[i]"
    end for
end if

2.3.2代码截图

2.3.3造测试数据

2.3.4PTA提交列表及说明

Q1:当时那个进位那个地方的代码写错了,一旦有一个进位,之后的每一位数字都要加1
A1:灵活定义一个flag=0;如果进入那个要进位的条件就让它等于1,这样下一组相加就会多加1。不满足进位条件仍旧为0,不影响下一组运算。
Q2:最高位进位的测试点没有考虑完全,我把最高位进位放到那个数组最后一个元素+1里面了
A2:如果加1就要放到数组中去,有可能会越界,之前没有考虑到这一方面的问题,所以在输出之前加一个条件,如果最高位也要进位,则另外输出1

3.阅读代码

代码问题:
输入的N,K,分别表示的是N头牛,K以内的序列,后面输入N个牛所对应的ID,要求写一个代码帮农场主构造出的最短可能序列的长度,这个范围不是他的牛的广义ID的子序列。
代码解析:

  • while循环条件那条语句使用的是EOF,关于这个EOF我也是前不久才知道的。EOF是End Of File的意思,在C语言中定义的一个宏,用作文件结束标志。从数值角度看,就是-1。这一条是我们可以学习的。
  • "#define clr(x)memset(x,0,sizeof(x))",这一条语句很特别,它的常量定义不像我们是直接一个数,该代码是直接宏定义,这样做可以不浪费数字。值得学习。
  • 整个代码给人一种很舒服的感觉,虽然可能看不懂代码的意思,但是代码本身的可读性还是很高的,这一点是林老师是第一堂课就强调的。
  • 代码的数组变量是全局变量。我们好像不敢去用数组的全局变量,害怕会崩盘,一旦发生错误可能会导致整个程序崩溃。但是数组的全局变量在某类题目上是很有用的,这就需要我们分辨是否选择全局变量了。但是我们还是要学习优秀代码的数组的全局变量。
原文地址:https://www.cnblogs.com/springxinxin/p/11850807.html