| 这个作业属于哪个班级 | C语言--网络2011/2012 |
| ---- | ---- | ---- |
| 这个作业的地址 | C博客作业04--数组 |
| 这个作业的目标 | 学习如何设计函数、C语言基本数据类型 |
目录
0. 展示PTA总分
1. 本章学习总结
2. PTA实验作业
0.展示PTA总分
1. 本章学习总结
1.1 学习内容总结
1)查找数据
- 顺序查找
- 无序、有序排列的数组均可以使用
- 即按照顺序逐个查找目标数字/字符
- 伪代码:(计数指定数据的个数)
/*计数目标数据的数量时,利用count++自增运算*/
// 跳出循环后,可以通过count的值,判断数组里是否有目标数据
//
/*需要利用目标数据的位置时(如增、删、改),将count++改为break,利用break跳出循环;或用return返回目标数据的下标*/
//可以使用flag,当找到目标数据时,flag = 1,反之 flag = 0。若未找到则及时结束函数,避免数组越界
//
For i=0 to n-1
IF a[i] = target
then
count++
END IF
END For
- 二分查找
- 适用于有序排列的数据
- 即判断区间中间的数据是否为目标数据。若不为目标数据,判断该数据与目标数据的关系后,进入减半的区间,继续判断中间数据
- 代码(查找目标数据,并返回下标)(以从小到大顺序排列的数组为例)
int BinSearch(int a[99], int n, int key, int count)
{
int locMin = 0; //最小下标
int locMax = n - 1; //最大下标
int mid; //中间下标
while (locMin <= locMax) //当最小下标不大于最大下标时,继续遍历数组
{
mid = (locMax + locMin) / 2; //取查找区间内的中间下标
if (a[mid] == key) //找到目标数据时,返回目标数据的下标
{
return mid;
}
/*根据目标数据和中间数据的关系,重新确定查找的区间*/
else
{
if (key < a[mid])
{
locMax = mid - 1;
}
else if (key > a[mid])
{
locMin = mid + 1;
}
}
}
return -1; //循环结束后,若未找到目标数据,则返回负数。通过该函数的返回值,确定是否有目标数据存在于数组之中
}
2)插入数据
- 伪代码(以升序排列为例)
//①先查找到需要插入的位置
For i = 0 to n-1
IF putNum < a[i]
break跳出循环
END IF
END For
//②再移动数组
For j = n-1 to i
a[j+1] = a[j]
END For
//③将需要插入的数据插入,并将表示数组数据个数的n加一
a[i] = putNum
n++
- 流程图:
3)删除数据
-
原数组删除:删除数据较少时使用
- (步骤1:先删除一个目标数据)
- 利用查找数据,找到需要删除数据的位置
- 确定删除的数据位置后,将数组左移一位,覆盖掉要删除的数据
- 最后将数组元素个数,n--
- (步骤2:)
- 循环步骤1,直到在数组中找不到目标数据。(可以使用flag,判断数组中是否找到目标数据)
- 简易流程图:
-
新构造数组:删除数据较多时使用
- 新构造一个数组
- 定义两个数组下标,一个为原数组的下标i,一个为重构数组的下标j
- 遍历原数组进行判断,若数据不为要删除的目标数据,将数据写入新构造的数组中,并且此时新构造数组的下标自增
- 简易流程图:
4)排序方法
- 冒泡排序
- (步骤1:将两个相邻的数比较,先将一个最值“冒泡”,移到右边)
- (步骤2:范围减小后,循环重复步骤1,将每个区间内的最值“冒泡”出去)
- 代码:(以升序排列为例)
int i,j; //外循环控制次数,遍历的范围;内循环遍历数组
int temp;
for(i = 0; i < n-1; i++) //每执行一次冒泡,内循环的范围减小
{
for(j = 0; j < n-1-i; j++) //通过逐个判断,将区间内的最大值移动到区间的最右边
{
if(num[j] > num[j+1]) //逐个判断,若a[j+1] < a[j], 交换a[j] 和a[j+1]
{
temp = num[j];
num[j] = num[j+1];
num[j+1] = temp;
}
}
}
- 选择排序
- (步骤1:遍历数组,选择出最值,将最值放到指定位置)
- (步骤2:范围减小,循环重复步骤1)
- 代码:(以升序排列为例)
int i,j;
int minloc; //最小值的下标
int temp;
for(i = 0; i < n-1; i++)
{
minloc = i; //使最小值的下标等于内循环开始时,数组的下标
for(j = i; j < n; j++) //从num[i]开始遍历数组,找到区间内的最小下标
{
if(num[minloc] > num[j])
{
minloc = j;
}
}
temp = num[i]; //交换,将范围内最小值与a[i]交换
num[i] = num[minloc];
num[minloc] = temp;
}
5)数组统计
- 思路:
- 循环步骤1
- 步骤1:输入数据,将该数据作为静态数组的下标,该下标对应的数据自增
- 代码:
static int vote[1000]; //定义静态数组,使数组各项为0
for(i=0; i<n; i++)
{
scanf("%d",&num); //输入需要统计的数据
vote[num]++; //将该数据作为静态数组下标。每次出现时,vote[]对应的值加1,统计出数据出现的次数
}
- 案例一:7-4 点赞
- 案例二:7-5 调查电视节目受欢迎程度
6)哈希数组
- 用法
- 定义静态数组,int hash[256],
- 与投票类似,将输入的数据作为hash[]数组的下标
- 出现的数据对应下标的数组数据 = 1
- 遍历数组,输出值等于1,对应的下标
- 这样就完成了删除重复的数据,并将数据按照ASCII码的排序
- 案例
- 删除重复数据,并按照ASCII码升序排列(伪代码)
定义 static int hash[256] //因为ASCII码的范围为[0,255],共256个元素
输入要输入的数据数 n
For i = 0 tp n-1
{
输入数据 str
hash[str] = 1; //将str对应下标的数组赋值为1。用于接下来判断数据是否存在
}
END For
For i = 0 to 255 //遍历hash数组,输出出现的数据
{
IF hash[i] = 1
than
输出i //也可以在定义一个新的数组,将i的值放入新的数组中
END IF
}
END For
7)二维数组
- 二维数组特点:
- 定义: 类型名 数组名[行长度][列长度]
- 存放:二维数组在内存中是按行连续存放的
- 赋初值:
分行赋初值: int num[2][2] = {{1}, {2,1}};
顺序赋初值: int num[2][2] = {1, 0, 2, 1}; - 省略行长度:
对所有元素都赋了初值
分行赋值中,列出了所有行 int num[ ][3] = {{1, 2}, { }}; - 根据题意,理清行(i)与列(j)的关系,有利于解决关于二维数组的图形问题
8)字符数组
-
字符串特点
-
字符串是特殊的一维数组,需要用一维数组来存放
-
字符串的末尾有结束标志'\0',而'\0'之后的其它数组元素与字符串无关
-
与数字数组相比,字符数组并不需要明确的有效元素个数作为循环的限制条件。
-
字符数组的限制条件一般为结束标志'\0'。当用fgets输入字符串时,字符串末尾往往会接一个'\n',再接上结束标志,这种情况结束条件要适当改变
条件:while(str[i]) 或者 while(str[i] && str[i] != '\n')
-
-
需注意事项
- 区分"a"和'a'
初始化:char str[6] = "a"; //其中"a"为字符串
赋值: str[0] = 'a'; //其中'a'为字符
备注:初始化——> str[0] = 'a'; str[1] = '\0'
- 字符串的输入:
注意空格:scanf(输入的字符串(%s)不能含有空格,scanf遇到空格时停止输入)
注意回车:fgets(输入的字符串遇到回车时结束,但如果输入的元素加上结束标志没有填满数组,则数组结束标志前还会有一个换行符('\n')
注意结束标志:循环getchar()输入(逐个输入字符构成字符数组,但在输入完成后,需记住在末尾添加结束标志)
- 区分"a"和'a'
2. PTA实验作业
2.1 数组左移
- 2.1.1 伪代码
输入数组元素个数n,左移位数moveNum
moveNum = moveNum % n //解决位移位数不小于元素个数的情况。若左移位数与元素个数相等,则不进行位移。若位移位数大于元素个数,则将位移位数为原来位移位数与元素个数的余数
定义一个新数组tempNum[moveNum]
For i = 0 to moveNum //原数组的前几位放入新数组中
{
tempNum[i] = moveNum[i];
}
END For
For i = moveNum to n - 1
{
number[i - moveNum] = number[i]; //数组内后几位数前移
}
END For
For (j = 0, i = n - moveNum; i < n; i++, j++)
{
number[i] = temp[j];
}
END For
- 2.1.2 代码截图
- 2.1.3 同学的代码
#include <stdio.h>
#define MAXN 100
int ArrayShift( int a[], int n, int m );
int main()
{
int a[MAXN], n, m;
int i;
scanf("%d %d", &n, &m);
for ( i = 0; i < n; i++ ) scanf("%d", &a[i]);
ArrayShift(a, n, m);
for ( i = 0; i < n; i++ ) {
if (i != 0) printf(" ");
printf("%d", a[i]);
}
printf("\n");
return 0;
}
int ArrayShift(int a[], int n, int m)
{
for (int i = 0; i < m; i++)
{
int temp = a[n - 1];
a[n - 1] = a[0];
for (int A = 0; A < n - 2; A++)
{
a[A] = a[A + 1];
}
a[n - 2] = temp;
}
return 1;
}
-
优点:
同学:- 代码思路清晰,可读性强,易于理解
- 代码简洁明了
-
不足:
同学:- 注释较少
自己: - 代码冗长
- 定义的全局变量过多
- 函数形参的设置有所不足,应该使用形参将需要的参数传入函数,而不是过多地定义全局变量
- 函数的返回类型。该代码中函数的返回类型多为void,所做的仅仅是将代码切分开来,没有很好地利用函数的返回值
- 注释较少
2.2 阅览室
- 2.2.1 伪代码
/*数据表达*/
/*需要输入的变量*/
定义 dayNum bookNum ch hh mm
定义静态数组 lend[1001][2]
定义静态数组 back[1001][2]
// lend[][0] 放置是否借书
// lend[][1] 放置借书的时间
/*与时间计算相关的变量*/
定义 sumtime //存放总时间
定义 times //存放次数
定义 avgTime //存放平均时间
定义bookMax //最大书号
/*数据处理*/
For i = 0 to dayNum - 1
{
/*重置一些变量的值*/
清空数组lend[][]
清空数组book[][]
bookMax = 0;
times = 0;
sumTime = 0;
while(1)
{
输入书号 bookNum
输入借出或归还 ch
输入时间 hh:mm
IF bookMax < bookNum
then
bookMax = bookNum
END IF
IF bookNum = 0
then
break
END IF
IF ch = 'S' || ch = 'E'
then
IF ch = 'S'
lend[bookNum][1] = hh * 60 + mm /*存储时间(分钟)*/
lend[bookNum][0] = 1 /*已借书*/
END IF
ELSE IF ch = 'E' && (back[bookNum][0] = 0 || back[bookNum][1] < lend[bookNum][1])
then
存储时间(分钟)
back[bookNum][0] = 1 /*已还书*/
END IF
END IF
/*借出并归还,且时间合法时*/
IF lend bookNum][0] == 1 && back[bookNum][0] == 1 && back[bookNum][1] >= lend[bookNum][1]
then
sumTime += back[bookNum][1] - lend[bookNum][1]; // 计算借阅时间的总和
times++; //次数累计
lend[bookNum][0] = back[bookNum][0] = 0; //将借阅判断归零
END IF
}
END while
、
输出借阅总时间和次数
}
END For
-
2.2.2 代码截图
-
2.2.3 说明和超星视频做法区别,各自优缺点
-
超星:
1.将每天借阅记录存放在二维数组之中
2.思路清晰,代码简洁明了 -
自己:
不足
1.没有使用二维数组存放阅读记录
2.代码冗长
-
2.3 切分表达式
- 2.3.1 伪代码
定义 flag //判断是否为”首个“数字
定义 sign //判断是否为负数、正数
For i = 0 to str[i] = '\n'
IF flag = 1 //判断首个数字是否为带符号的正负数
then
IF str[i] = '-'
then
sign = 1;
END IF
ELSE IF str[i] = '+'
then
sign = 2;
END IF
END IF
flag = 0
IF str[i]为0~9的字符
then
将str[i]转化为数字,赋道number中
IF str[i+1]不为0~9的字符
then
IF str[i+] = '.'
then
通过sign判断首个数字有没有带有正负号
IF sign = 1 || sign = 2
then
通过switch分支,将输出的number前填上‘+'或’-‘,输出的number不换行
END IF
ELSE
then
输出number不换行
END IF
END IF
ELSE
then
直接输出number,并换行
number = 0;
sign = 0; //变量归零
END IF
END IF
ELSE
IF sign = 0
then
IF str[i] = '.'
then
输出小数点,不换行
END IF
ELSE
then
输出字符,换行
END IF
IF str[i] = 40 //左括弧的ASCII码值
then
flag = 1
END IF
END IF
END IF
-
2.3.2 代码截图
-
2.3.3 说明和超星视频做法区别,各自优缺点
- 超星:
1.遇到数字或小数点,或是符号位的+、-,直接输出不换行 - 自己:
不足
1.将字符数字转化为了数值,未考虑到可以直接输出,导致代码冗长
2.多判断了符号位的+、-,将+、-连同数值一起输出
- 超星: