C语言博客作业05--指针

0.展示PTA总分



1.本章学习总结

1.1 学习内容总结

  • 构建函数实现数组的传递

    • 在分装的函数中传递数组,函数中的操作会改变数组的值。因此一些数组的操作,插入、插入、左右移动等,我们常使用封装函数编写,使主函数思路清晰,同时可以提高对这些针对数组操作的函数的实用性

    • 传递时数组的有效长度应是常量,所以常设符号常量。符号常量值不变,使用符号常量的好处是,一改全改。

#define N 100
  • 二维数组的传递时,列长度必须传递!

  • 字符串数组传递时通常无需传递其有效长度,原因是我们可根据字符串的特点,其最后一个字符为'\0'来判断

  • 字符数组的输入和输出

 字符串的输入

(1) scanf("s", 字符数组名);

	输入参数:字符数组名,不加地址符

	特点:scanf函数遇到回车或者空格输入结束,并自动将输入的一串字符和'\0'送入数组中

	(因此使用时需注意:scanf语句无法接受带空格的字符串)

(2) fgets(数组名,接收长度,stdin);

	特点:读取到换行符、文件尾或者读完n - 1个字符结束。若接收长度为n, 当输入字符超过n时,还是正常接收n - 1个字符,并且第n个字符为'\0'。
	    优点是解决了输入字符超出字符数组的大小的溢出问题
		缺点是当输入字符不足n - 1时,结束后回车符会被自动吸收。

	(通常利用fgets输入,循环条件输出时需注意)
     for (i = 0; a[i] && a[i] != '\n'; i++)//需注意语句2的写法

(3)利用循环输入(当要求以特定字符结束时一定要使用循环输入的方法!!!)
		while((str[i])=getchar()!='\n')
		{ ...}
	   str[i] = '\0';

	   (注意要加上结束符,否则输出时会出现乱码)
字符串的输出
(1) printf("%s", str);
    printf("%s", "hello");

		输出常数可以是字符数组或字符串常量,输出遇到'\0'结束.

(2) puts(str);
    puts("hello");

		特点:输出字符串后自动换行

(3)循环输出
		char str[80];
		for (i = 0; str[i] != '\0'; i++) putchar(str[i]);


  • 数组中如何查找数据
    遍历数组
一维数组:
		int a[N]; 
	    int i;

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

二维数组:
		int a[N][M];
	    int i, j;

		for (i = 0; i < N; i++)
			for (j = 0; j < M; j++)

字符串数组:
				char a[MAX];

		for(i=0;a[i]!='\0';i++)//语句2也可以是a[i]。
			//当以特定字符结束时,如以回车符结束时:语句2应为a[i]&&a[i]!='\n'


二分查找法:

  int a[N];
	int low=0, high=N-1, mid=(low+high)/2;//low,high,分别为左右两边界,mid为中间值。并且为该元素的下标。
	int key;//key为所要查找的数
	int loc;//找到位置时的下标

	while (low <= high)//注意跳出循环的条件是左边界大于右边界
	{
		if (中间值a[mid]与所要查找值key相等) 
			将mid赋值给loc并且跳出循环
		else if (中间值大于所要查找数) 
			将左边界调大(low = mid + 1)
			并且给mid重新赋值(mid = (low + high) / 2)
		else 
			将右边界调小
			并且给mid重新赋值
	}

	if(若左边界大于右边界)//说明走遍while循环了并未找到该数
		则输出找不到该数的提示

  • 数组中如何插入数据
    (前提是该数组应有序)
    主要思路是:先找到插入位置,然后先将该位置后的元素往右移一个单位(操作时从最后一个数开始移动),然后将需要插入数据赋值在插入位置。
假设需要插入数组是从小到大排序时
	int i;
	int loc;//为要插入位置的下标

	for i = 0 to n-1
		if (a[i] > number)
		
			loc = i;
	

	for i = n to loc+1 //由于插入数据,所以数组长度增大,从n开始移动
		将前一个的值赋给后面

   最后在找到的插入位置中插入所要求的数据
  • 数组中如何删除数据。
    主要思路是:找到需要删除数据的位置,接着从该数位置的后一个数开始左移(直接将需要删除数据的后一个元素移动到该位置,即覆盖的方法,起到删除作用)
编写删除数据的封装函数时,利用 数组有效长度的指针!较为简便。

其可在调整完数组元素后,就改变该数组有效长度,为下一次的删除操作做好了准备。

因此利用数组有效长度的指针 ,同样可以适用在编写插入数组操作的函数中!

以下为删除数据操作的分装函数伪代码

void Delete(int a[], int* n)
{
	int i;
	int j;
	int x;//为要删除的第x个数

	输入要输出的数据

	for i = 1 to * n
			if (i == x) 找到要删除数据位置的下标就退出break;
	
	for j = i to j <= *n - 1
	  将后一个值赋给前面一个值

	end for

	(*n)--;//利用指针调整数组长度

  • 数组中目前学到排序方法,主要思路。
    -选择排序法:
for i=0 to n-2 do//最后一位不用再比较,因此直到n-2
		for j=i to n-1 do//每一轮的i比较后,找的是最小值,放在前面。因此此后只要同i后数字比较,于是从j=i开始比较
			if(a[j]<min) min和a[j]交换

end for

-冒泡排序法:

for i=0 to n-2 do//最后一位不用再比较,因此直到n-2
		for j=0 to n-i-2 do//通过一遍扫描,最后一个元素必定为此趟最大的元素
			if(a[j]>a[j+1]) 两值交换//冒泡法比较时是相邻两值进行比较
			                         
	end for

	//需要注意的是:若利用a[j]与a[j-1]进行比较时,若j从0开始,a[j-1]为负,因此j值应从1开始。
  • 数组做枚举用法
    调查节目欢迎度,点赞等。

  • 哈希数组用法
    主要用例就是查找数组中元素是否重复,同样的题集中删除重复字符也可用此办法拓展, 。

1.2 本章学习体会

  • 目前让我们从ACM题库中找代码阅读,感觉我们大部分人的能力还是有限,其实都看不太懂,毕竟还有许多专业知识没学。
    其实我觉得像上次上课那样,找了几位同学较为优秀的代码让我们阅读学习更好,因为大部分的代码我们都看得懂,更能找到共通性,也方便与自己比较,然后学习优点。
    所以我觉得改为要求让我们阅读一些其他同学PTA的题目解法。感觉是看到别人的博客的PTA题目分析的时候,才突然发现一些别人用的比较巧妙的方法。因为我们平常也没什么途径能看同学的代码,而且如果是做过做对的题目,就会更没什么动力去学习研究别人的做法,觉得延续自己的方法就好了。
    不过这样子比较麻烦的是要去筛选,看哪些同学的解法比较好。

  • 感觉好多PTA上的题目做完了之后,一定还要再去复习总结思路!不然等到上机考试时候早就忘光了。

  • 代码量大概1000行
    2.PTA实验作业
    =============
    2.1 c07-一维数组 -7-11 求整数序列中出现次数最多的数


2.1.1 伪代码

主函数:
	int a[MAX];	//a数组用于存储n个数组
	int n;//n个统计的整数
	int i;

	输入n

	for i=0 to n-1do
		输入a[i]
	end for

	调用函数SearchMax(a, n);

	return 0;

函数SearchMax:
	int i,j;
	int i_max=a[0];//为出现次数最多的数,初始化出现次数最多的数为第一个输入的数
	int max=0;//为出现次数最多的次数
	static int b[MAX];//数组b用于a中元素记录出现的次数

	for i= 0 to n-1//每个数出现次数进行统计

		if b[i]不等于0//b[i]!=0说明该元素重复出现了并且记过数,因此continue,进入下个循环
			continue;

		for j = i + 1 to j < n//j从i后即i+1,开始与a[i]比较
			if a[j]与a[i]相等  //重复时次数加一
				次数加一;
		end for

		if (b[i] > max)
			则给i_max和max重新赋值;
		
    end for

	输出出现次数最多的数及其次数

2.1.2 代码截图


2.1.3 造测试数据

输入数据 输出数据 说明
1 2 2 1 最小N为1
2 3 3 3 1 元素都相同
3 -1 2 2 2 2 元素中含负数

2.1.4 PTA提交列表及说明:

提交列表说明:

1.及.2部分错误:
   其中的部分错误显示的是段错误,想到老师课上说数组越界时PTA上显示的会是段错误,于是我再次查看代码,观察是否越界。但是再次尝试依旧是部分段错误,即第二次的部分错误。并且同sample调试出的答案是正确的,但却显示段错误。

   由此我再次观察题目给出的测试例子,发现例子中输入的整数中含负数。由此发现问题所在。

   由于受到题目7.9—调查节目受欢迎程度的影响,题目一开始我的思路就是利用相同的办法,将a中元素作为b数组的下标,然后再给b计数赋值。而数组的下标不能为负,因此出现问题。

   由此我又重新改变思路,b数组的下标依旧是与a一致按序,然后根据查找后面重复数字的次数来记录,最后比较出现次数。

3.部分错误:
   错误点是最小N的例子。

   原本是调整完思路后再次调试题目给出的测试样例,正确便直接提交了。此后根据错误点调试最小N,即输入整数为1的情况,结果显示错误,并提示设置的代表出现次数最多的变量未初始化。
   由此我又将该变量赋值为输入的第一个整数。该错误主要是由于编写代码过程中考虑的不够周到细致。

2.2 c08-二维数组-判断上三角矩阵

2.2.1 数据处理


主函数:
	int a[MAX][MAX];//用于每次的存储矩阵
	static int b[MAX];//用于保存每次判断的结果
	int T;//T为需要判断的矩阵个数
	int n;//n控制矩阵大小
	int i, j, k;//i控制判断矩阵的次数,j、k分别控制矩阵的行列

	
	输入T

	for i = 0 to T-1 do//进行T次判断 

		输入n

		for j = 0 to  n-1 do
			for k = 0 to n-1
			输入矩阵
			end for
	    end for
		

		if (调用函数IsTrangle为真)
		输出YES
		else
		输出NO

    end for

结束

函数 IsTrangle
	int j, k;//用于矩阵的行列

	if (矩阵最小n == 1)//最小n情况特殊处理
		返回值1

		for j = 0 to n - 1
			for k = 0 to n -
				if (j > k 且a[j][k] != 0)//若下三角若有不为0
					返回值0;
	        end for
	    end for

	遍历完矩阵,即for循环走完,则返回值1

结束


2.2.2 代码截图


2.2.3 造测试数据

输入数据 输出数据 说明
1 \n 1 YES 矩阵最小n为1,该数不为0
1 \n 0 YES 矩阵最小n为1,该数为0
2 \n 1 2 \n 0 3 NO 次最小n,为上三角矩阵
2 \n 1 0 \n 0 2 NO 次最小n,不是上三角矩阵
2 \n 1 0 \n 0 2 NO 对角矩阵

2.2.4 PTA提交列表及说明

提交列表说明:

1.及2.答案错误:
   从截图可以看出,这两次提交错误的分数最终并没有得到分数,也就是应该是全部错误的。而既然提交到PTA就说明,测试过给出的测试样例至少应该是对的。

   我又造了其他数据进行测试,发现按照判断,自己所写的程序也应该是正确的。由此观察给出样例的两次判断,发现PTA上给出的输入和输出样例是分开的,但在实际操作中,有可能输入和输出并不是一次操作。由此询问已经正确答题的舍友得知,的确是应该每次输入矩阵后就判断,而不是最后输出的判断。

   原本出于认为题目是需要一次性输入且一次性输出判断的考虑,还多封装了最终输出结果的函数。发现错误后只需将判断是否上三角矩阵并且输出结果的代码放入for循环中,并且删减输出结果的函数即可。



3.及4.部分错误:
   错误点是最小n,由此造测试数据进行测试,由此对n为最小值1的情况进行特殊处理

   第二次依旧部分错误的原因是对上三角矩阵的概念不够清晰,即当n为1,矩阵即一个数时,无论该数为什么值,该矩阵可称为上三角矩阵。

2.3 c07-一维数组-7-10 出生年

2.3.1 数据处理

主函数:
{
	int y;//出生年份
	int n;//出生年份与目标年份不同数字n
	int x = 0;//第x年为目标年份

	输入y和n

    while (1)
	{
		if (调用函数IsDifferent(y, n),若为真)
				输出题目要求的x和y,并跳出循环;

		x++; y++;
	}

	return 0;
}
函数Different(int y, int n)
{
	int number;//为年份的每位数字
	int hash[10] = { 0,0,0,0,0,0,0,0,0,0 };//个位数由0到9
	int count = 0;//为不同的个数
	int i;


	for i = 0 to i = 3
	{
		取该年份的个位数赋值给number, 并且对该年份进行去个位处理
		以number为下标的hash中元素加一;
	}
	

	for i = 0 to i = 9
	{
		if (hash[i]不为0,则说明出现过)
			则count加一;
			
	}

	if (当统计的不同个数count等于目标不同个数n时)
	{
		返回值1;
	}
	else return 0;

}

2.3.2 代码截图

2.3.3 造测试数据

输入数据 输出数据 说明
1001 2 0 1001 该年即是所要求年份,x=0
1988 4 25 2013 最大n
2010 3 1 2011 一般samlpe

2.3.4 PTA提交列表及说明

1.部分错误:
    是当时只测试了测试样例1,然后就提交了PTA,没有注意样例2中,应该从该出生年份就开始比较。

    因此while恒真循环中,调整了语句顺序,先调用函数判断然后再将变量自增。

2. 部分错误:
   测试样例时,改正了1.的错误后并测试成功,但是提交PTA还是显示不正确,于是我又再次测试普通测试样例1,发现这回的输出结果就不正确了,而且添加printf语句进行检查,发现问题出在判断年份不同数字的封装函数中。由于代码量较小,于是我开始直接检查代码的算法等是否正确,算法很简单应该是正确的。于是我又输入样例1,进行逐语句测试,这才发现了错误。

   由于利用hash数组判断是否重复出现的过程中,定义时利用了静态变量的定义方式,由此在多次循环中,hash数组赋值0的情况仅在第一次循环时才起作用,而当年份不满足情况时,此后再利用hash数组判断时仍保留之前的记录,影响了后续的判断。

   该题目的错误较简单,但是由此我对static静态变量有了更深的理解,所以选择本题。感觉虽然上课也有讲过惊天变量,其赋初值仅在第一次生效,其后变量值保留上一次操作的值。但在后续的题目以及应用中,感觉我们利用static的主要功能都是为了给元素都赋值0,忽略了其静态的性质。我们通常可在循环中利用静态变量,由于是封装了函数,所以较容易忽略循环也能影响到封装函数中的静态变量这一点。



3.阅读代码



该代码的主要功能就是先跟据题目给出的日历规则判断输入日期是否合法,然后根据题目要求输出给定日期所在的工作日。
该代码利用数组存储月份,星期日,以及闰年非闰年的每月的天数。
利用函数,分装了判断是否合法,是否闰年,以及计算星期日的函数,使得主函数简洁明了。函数可读性强,思路清晰。

posted @ 2019-11-17 21:59  郑梦露  阅读(368)  评论(0编辑  收藏  举报