第五次博客作业
| 这个作业属于哪个班级 | C语言--网络2011/2012 |
| ---- | ---- | ---- |
| 这个作业的地址 | C博客作业04--数组 |
| 这个作业的目标 | 学习数组相关内容 |
| 姓名 | 骆梦钒 |
0.展示PTA总分(0----2)
展示2张关于“数组题目集”分数截图。
1.本章学习总结(3分)
1.1 学习内容总结
整理数组这章学习主要知识点,必须包含内容有:
1.数组中如何查找数据,有哪些做法
(1)使用search函数,通过for循环扫描数组中的某个元素是否与指定数据相等,是则flag=1,否则flag=0,返回flag的值来判断是否找到
int search(int temp [],int x,int n )
{
int flag = 0;
for(int i = 0;i < n;i ++)
{
if(temp[i] == x)
{
flag = 1;
break;
}
}
}
(2)二分查找(百度搜索)
迭代实现二分查找,查找成功返回所在数组下标,否则返回-1
int Search(int* pData, int nCount, int nVal)
{
int nLow = 0, nHigh = nCount - 1, nMid = 0;
while (nLow <= nHigh)
{
nMid = (nLow + nHigh) / 2;
if (pData[nMid] == nVal)
return nMid;
if (pData[nMid] < nVal)
nLow = nMid + 1;
else/* if (pData[mid] > nVal) */
nHigh = nMid - 1;
}
return -1;
}
2.数组中如何插入数据,怎么做,可以写个伪代码或动态图展示方法
插入排序:先确定插入位置,即前面一个数小于插入数据而后一个数大于插入数据,再将插入位置后面的数往右移
i, n, j;
a[11];
num;//要插入的数据
temp//
scanf输入n
for (i 0 to n)//输入数组
scanf输入num;
i = 0;
while (a[i] < num 且 i < n)
i++;//确定插入位置
for (j= i to n-1 j--)
{
a[j + 1] = a[j];//插入数据的后面的数据往后移
}
a[i] = num;//插入
for (i = 0 to n)//输出a[i]
}
3.数组中如何删除数据,这个有多种做法,请一一展示。
1.将删除的数据后面的数往左移,再确定删除后数组长度输出
伪代码
a[MAX],i,m//删除的数据,n,flag=k//确定删除数据的个数
scanf//输入数组长度n和数组a[i]以及删除数据个数
while(k--)
{
scanf(m)
for(i=m-1 to n)
a[i]=a[i+1]//数组左移
}
for(i=0 to n-flag-1 )
end
代码
#include<stdio.h>
int main()
{
int a[100],n,m,k;
scanf("%d",&n);
for(int i = 0; i < n; i ++)
{
scanf("%d",&a[i]);
}
scanf("%d",&k);
int flag = k;
while(k--)
{
scanf("%d",&m);
for(int i = m-1; i < n; i ++)
{
a[i] = a[i + 1];
}
}
for(int i = 0; i < n - flag ; i ++)
{
if(i == n - flag -1) printf("%d",a[i]);
else printf("%d ",a[i]);
}
return 0;
}
4.数组中目前学到排序方法,主要思路?
1.选择排序:首先假定数组的首元素为最大(最小)的。此时就要利用3个变量i,j,k表示元素的下标。i表示当前,j表示找到的最大(最小)的下标,k用于
存放每次循环中最大值的下标。找到最大的下标后赋给k。找到之后判断所假设的当前值是否为此次循环的最大值,如果不是,就交换a[k] 与当前a[i]的值,从
而将数组以一定的顺序排放,最后写一个循环将结果输出。
void ChooseSort(int a[],int n)
{
int i,k,temp=0;
for (i = 0; i < n; i++) //判断i是否小于len,执行下面的语句
{
k = i; //假设此次循环中的最大值就是当前的值
for (j = i + 1; j < n; j++)
{
if (a[j] > a[k]) //将假设的当前最大值与后面的值比较
{
k = j; //若后面的值更大,则交换下标
}
}//当前最大值
if (k != i) //比较之后如果此次循环中最大值并非当前值
{
temp = a[i]; //将此次循环中的最大值与a[k]交换
a[i] = a[k];
a[k] = temp;
}
}
}
2.冒泡排序:对于冒泡排序,主要采用的是相邻数两两进行比较的思想。
如果后一个比前一个大(小),则将其调换位置,直至所有的数都比较完。如果给定一个大小为n的数组,那么需要比较n-1趟,每一趟比较n-1-i次 ,i 表示
上次循环中已经比较完的下标。写两个循环 判断,如需交换则进行交换,如果不需要交换则进行下两个数的比较,直到所有的数比较完。最后,用一个循环将排
序完成后的数全部输出。
void BobbleSort(int a[],int n)
{
int i,temp=0;
for (i = 0; i < n - 1; i++)
{
for (j = 0; j < n - 1 - i; j++)
{
if (a[j + 1] > a[j])//如果后一个数比前一个大,则两数交换
{
temp = a[j + 1];
a[j + 1] = a[j];
a[j] = temp;
i++;
}
}
}
}
易错:当使用这两种排序方法时要注意for循环中各自的初始值和限制条件
3.直接插入排序:插入排序是一种最简单的排序方法,它的基本思想是将一个记录插入到已经排好序的有序表中,从而一个新的、记录数增1的有序表。在其实
现过程使用双层循环,外层循环对除了第一个元素之外的所有元素,内层循环对当前元素前面有序表进行待插入位置查找,并进行移动
void InsertSort(int a[],int count)
{
int i,j,temp;
for(i=1;i<count;i++)
{
temp=a[i];
j=i-1;
while(a[j]>temp && j>=0) //比较a[i]与其前面的数
{
a[j+1]=a[j];//将前一项的位置往后移动一位
j--;
}
if(j!=(i-1))
a[j+1]=temp; //当内部循环结束的时候,给temp插到对应的值
}
}
4.快速排序法:快速排序是对冒泡排序的一种改进。它的基本思想是:通过一躺排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部
分的所有数据都要小,然后再按次方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
void sort(int *a, int left, int right)
{
if(left >= right)/*如果左边索引大于或者等于右边的索引就代表已经整理完成一个组了*/
{
return ;
}
int i = left;
int j = right;
int key = a[left];
while(i < j) /*控制在当组内寻找一遍*/
{
while(i < j && key <= a[j])
/*而寻找结束的条件就是,1,找到一个小于或者大于key的数(大于或小于取决于你想升
序还是降序)2,没有符合条件1的,并且i与j的大小没有反转*/
{
j--;/*向前寻找*/
}
a[i] = a[j];
/*找到一个这样的数后就把它赋给前面的被拿走的i的值(如果第一次循环且key是
a[left],那么就是给key)*/
while(i < j && key >= a[i])
/*这是i在当组内向前寻找,同上,不过注意与key的大小关系停止循环和上面相反,
因为排序思想是把数往两边扔,所以左右两边的数大小与key的关系相反*/
{
i++;
}
a[j] = a[i];
}
a[i] = key;/*当在当组内找完一遍以后就把中间数key回归*/
sort(a, left, i - 1);/*最后用同样的方式对分出来的左边的小组进行同上的做法*/
sort(a, i + 1, right);/*用同样的方式对分出来的右边的小组进行同上的做法*/
/*当然最后可能会出现很多分左右,直到每一组的i = j 为止*/
}
void QuickSort(int a[], int l, int r)
{
if (l < r)
{
int i,j,x;
i = l;
j = r;
x = a[i];
while (i < j)
{
while(i < j && a[j] > x)
j--; // 从右向左找第一个小于x的数
if(i < j)
a[i++] = a[j];
while(i < j && a[i] < x)
i++; // 从左向右找第一个大于x的数
if(i < j)
a[j--] = a[i];
}
a[i] = x;
quick_sort(a, l, i-1); /* 递归调用 */
quick_sort(a, i+1, r); /* 递归调用 */
}
}
递归定义递归,就是在运行的过程中调用自己。
构成递归需具备的条件:
-
子问题须与原始问题为同样的事,且更为简单;
-
不能无限制地调用本身,须有个出口,化简为非递归状况处理。
递归的缺点:
递归算法解题相对常用的算法如普通循环等,运行效率较低。因此,应该尽量避免使用递归,除非没有更好的算法或者某种特定情况,递归更为适合的时候。在
递归调用的过程当中系统为每一层的返回点、局部量等开辟了栈来存储。递归次数过多容易造成栈溢出等。
5.数组做枚举用法,有哪些案例?
枚举法:将问题的所有可能的答案一一列举,然后根据条件判断此答案是否合适,合适就保留,不合适就丢弃。
设计步骤:
确定枚举对象(即问题解的表达形式,一般需要用若干参数来描述)
逐一列举可能(根据枚举对象的参数构造循环,一一列举其表达式的每一种取值情况)
逐一验证可能解( 根据问题解的要求,验证枚举对象表达式的每一个取值,如果满足条件,则采纳它,否则抛弃之。)
其中对象不能重复,不能遗漏;而每个参数要相互独立,这个时候算法才能简洁;每个参数的取值范围要搞清楚,尽可能小。
(1)百钱买白鸡
#include<stdio.h>
int main()
{
int mj = 0, gj = 0, xj = 0; //定义变量分别表示母鸡、公鸡、小鸡并初始化
for (gj = 0; gj <= 20; gj++) //公鸡最多可买20个
{
for (mj = 0; mj <= 33; mj++) //母鸡最多可买33个
{
xj = 100 - gj - mj; // 三种鸡的总数是100只
if (xj % 3 == 0 && 5 * gj + 3 * mj + xj / 3 == 100) // 总花费为100元。
printf("公鸡为 %d 只,母鸡为 %d 只,小鸡为 %d 只!\n", gj, mj, xj);
}
}
return 0;
}
(2)拨钟问题:
首先,考虑用长度为9的数组表示表盘的状态以及调表的操作,终止的条件是表盘状态数组所有元素模4为0;
如果一种操作使用超过4次,那么相当于没有操作,所以操作数组需要多出一位记录造作使用的次数,一个操作最多使用3次
特别是我们注意到这个题没有问操作的顺序,所以操作的前后顺序其实是没有影响的;
这样其实是对进行了哪些操作,操作了多少次进行枚举,也就是对一个9*4的矩阵进行枚举;并求矩阵行元素和的最小值,
一共枚举4的9次方次
#include <stdio.h>
#include <stdlib.h>
short clocks[9],min=1000,operations[9][9],count[9],best[9];
void change()//这个函数的作用是按照count调整clocks,并修改min,再把clocks改回去
{
short i,j,flag=1,n=0;
for(i=0;i<9;i++)
n+=count[i];
if(n>=min) return;
for(i=0;i<9;i++)
for(j=0;j<9;j++)
clocks[j]+=(operations[i][j]*count[i]);
for(i=0;i<9;i++)
{
if((clocks[i]%4)!=0)
flag=0;
}
if(flag)
{
min=n;
for(i=0;i<9;i++)
best[i]=count[i];
}
for(i=0;i<9;i++)
for(j=0;j<9;j++)
clocks[j]-=(operations[i][j]*count[i]);
}
int main()
{
operations[0][0]=operations[0][1]=operations[0][3]=operations[0][4]=1;
operations[1][0]=operations[1][1]=operations[1][2]=1;
operations[2][1]=operations[2][2]=operations[2][4]=operations[2][5]=1;
operations[3][0]=operations[3][3]=operations[3][6]=1;
operations[4][1]=operations[4][3]=operations[4][4]=operations[4][5]=operations[4][7]=1;
operations[5][2]=operations[5][5]=operations[5][8]=1;
operations[6][3]=operations[6][4]=operations[6][6]=operations[6][7]=1;
operations[7][6]=operations[7][7]=operations[7][8]=1;
operations[8][4]=operations[8][5]=operations[8][7]=operations[8][8]=1;
short i,j;
for(i=0;i<9;i++)
scanf("%d",&clocks[i]);
while(count[0]<4)
{
change();
count[8]++;
for(i=8;i>0;i--)
{
count[i-1]+=(count[i]/4);
count[i]=count[i]%4;
}
}
for(i=0;i<9;i++)
for(j=0;j<best[i];j++)
printf("%d ",i+1);
return 0;
}
(3)填写运算符问题
第一步:要枚举出所有符合条件的运算符号,这里最主要的是/号,后面那个数字不得为0,筛选出运算符号的组合放入数组中,这里应当用数字来代替四个运算
符号 1-4分别代表+ - * /
第二步:首先得话定义左和右两个值,这里注意的是应当设置浮点型,可能中途运算会有小数存在,避免像1/5得到结果为0,实际为0.2
左右两个值分别代表的是前一次的左右值,其中最巧妙的是进行判断当前运算符的时候,前面那次运算会在这次碰到的运算符的时候进行对应的操作
有三个数 x y z
例如:left,right分别保存着x,y,此时运算到y和z中间是 * 运算符,这时候会先进行y和z的运算,left并不动 ,此时left = x,right = x*y
若是y和z之间是加减运算符时,left = x±y right = z
left 和right起到了根据运算符分别进行不同的操作
第三步:四次运算符都结束时,还是会有剩余的left和right左右两部分,所以 额外还要将left和right运算一次,最后在进行判断是否等于要求的值,并且打
印出来
#include<stdio.h>
#include <iostream>
#include <cstdio>
int main()
{
int i[5], num[6]; //每个坐标下范围为1-4,1->+ 2->- 3->* 4->/
char op[6] = { ' ','+','-','*','/' }; //这里存放的+-*/ 从下标1开始
int count = 0, result;
printf("请输入5个带空格的数:");
for (int j = 1; j <= 5; j++)
scanf("%d", &num[j]);
printf("输入结果:");
scanf("%d", &result); //结果
for (i[1] = 1; i[1] <= 4; i[1]++)
{ //四种情况
if (i[1] < 4 || num[2] != 0)
{
for (i[2] = 1; i[2] <= 4; i[2]++)
{
if (i[2] < 4 || num[3] != 0)
for (i[3] = 1; i[3] <= 4; i[3]++)
{
if (i[3] < 4 || num[4] != 0)
for (i[4] = 1; i[4] <= 4; i[4]++)
{
if (i[4] < 4 || num[5] != 0)
{
float left = 0; //初始左边值为0
float right = num[1]; //右边的值为第一个数
int sign = 1; //表示前一个符号的正负
//此时进行遍历存放四个运算符的i数组
for (int j = 1; j <= 4; j++)
{
switch (op[i[j]])
{
case '+':
//如果是正的话,用前一个left进行运算,之后right等于后面的值
left = left + sign * right;
sign = 1;
right = num[j + 1];
break;
case '-':
//如果是负的话,也是用前一个left运算,之后left为
left = left + sign * right;
sign = -1;
right = num[j + 1];
break;
case '*':
right = right * num[j + 1];
break;
case '/':
right = right / num[j + 1];
break;
}
}
//判断最后运算是否等于结果,打印输出
if (left + sign * right == result)
{
count++;
printf("%d:", count);
for (int j = 1; j <= 4; j++)
{
printf("%d%c", num[j], op[i[j]]);
}
printf("%d", num[5]);
printf("=%d\n", result);
}
}
}
}
}
}
}
if (count == 0)
{
printf("没有符合的式子!");
}
return 0;
}
6.哈希数组用法,目前学过哪些案例,举例展示。
(1)找重复数据
伪代码
初始化hash数组为0
for i=1 to n
输入一个数data
if
hash[data]==1:
有重复数据
return 0
else
hash[data]=1
end if
end for
return 1
代码
int IsSame(int n)//判断数组是否有重复数据
{
int i;
int data;
static int hash[MAX];
for(i=1;i<=n;i++)
{
scanf("%d",&data);
if(hash[data]==1)//重复数据
{
return 1;
}
else
{
hash[data]=1;
}
}
return 0;
}
7.字符数组、字符串特点及编程注意事项。
(1)字符数组和字符串的区别
区别一:定义不一样
1、字符串指针变量本身是一个变量,用于存bai放字符串的首地址。字符串本身是存放在以该首地址为首的一块连续的内存空间中并以‘\0’作为串的结束。
2、字符数组是由于若干个数组元素组成的,它可用来存放整个字符串。
区别二:对字符串指针方式不同
1、char *ps=”C Language”;可以写成char *ps;ps=”C Language”;
2、数组方式char str[]={”C Language”};不能写成char str[20];
Str={”C Language”};只能对字符数组的各元素逐个赋值。
(2)字符串
字符串是一种特殊字符数组,字符串的结束标志为\0 ,数字0等同于\0,但不同与0.
char arr[5]={'h','e','l','l','o'};
//下面的相等,上面的不能用%s打印
char *arr = "hello";
char arr[] ="hello";
char arr[]={'h','e','l','l','o','\0'};
字符串的输入输出:
1)gets() :与scanf区别,gets允许输入字符串含有空格
-
fgets()
-
puts()
-
fputs()
//gets()
int main()
{
char str[100];
scanf("%s",str);
//scanf("%[^\n]",str); //正则表达式,\n以外所有字符
//gets_s(str); //vs2019删除gets,用gets_s代替
//fgets(str,sizeof(str),stdin); //读键盘输入字符串,固定为stdin
printf("%s\n",str);
}
//puts()
int main()
{
char ch[] = "hello world"; //自带换行
puts(ch);
//fputs(ch, stdout); //不带换行
return 0;
}
//字符串长度,strlen与sizeof()
int main()
{
char ch[100] = "hello world";
//char ch[] ="hello world"; //数组大小12,字符串长度11。区别:\0
printf("%d\n", sizeof(ch)); //数组大小,100
printf("%d", strlen(ch)); //字符串长度,11(有效长度)
return 0;
}
2.PTA实验作业(7分)
2.1 题目名1(3分)
选择一题一维数组相关题目。请认真写伪代码整理解题思路。自己代码和同学代码比较,说明各自代码特点。
7-1 将数组中的数逆序存放 (20分)
本题要求编写程序,将给定的n个整数存入数组中,将数组中的这n个数逆序存放,再按顺序输出数组中的元素。
输入格式:
输入在第一行中给出一个正整数n(1≤n≤10)。第二行输入n个整数,用空格分开。
输出格式:
在一行中输出这n个整数的处理结果,相邻数字中间用一个空格分开,行末不得有多余空格。
2.1.1 伪代码
scanf(输入n)
for(i = 1 to n)//输入a[]
for(i=n to 1,i--)
//输出
//控制末尾空格
2.1.2 代码截图
贴图展示代码,不要复制。
我的代码
思路:
通过控制i从n到0倒序递减输出a[i]
特点:更简短
2.1.3 找一份同学代码(尽量找思路和自己差距较大同学代码)比较,说明各自代码特点。
同学代码
#include <stdio.h>
#define MAX 10
int main()
{
int n;
int arr[MAX];
int temp;
int flag = 1;
scanf("%d", &n);
for (int i = 0; i < n; i++)//输入数组元素
{
scanf("%d", &arr[i]);
}
for (int i = 0; n-1-i>i; i++)//将数组中的数逆序存放
{
temp = arr[i];
arr[i] = arr[n-1-i];
arr[n-1-i] = temp;
}
for (int k = 0; k < n; k++)//遍历数组
{
if (flag)//防止行末出现空格
{
printf("%d", arr[k]);
flag = 0;
}
else
printf(" %d", arr[k]);
}
return 0;
}
思路:前后对应数据两两交换循环n/2次实现逆序
特点:更清晰
2.2 题目名2(2分)
选择阅览室或鞍点这2题介绍二维数组。并说明和超星视频做法区别。
找鞍点
2.2.1 伪代码
scanf(输入n)
for(i=0 to n)
for(j=0 to n)
scanf(输入数组a[i][j])
if(n==1)
//判断特殊情况
for(i=0 to n)
{
temp=
for(j=1 to n)
//找出一行最大值 temp
for(j=0 to n)
{
if(temp==a[i][j])
{
for()//判断是否为该列最小
//最小
printf()
return 0
}
}
}
printf(“NONE”)
2.2.2 代码截图
2.2.3 请说明和超星视频做法区别,各自优缺点。
超星
for (i = 0; i < n; i++)
{
for (j = 0; j < n; j++)
{
if (a[i][j] >= a[i][maxIndex])
maxIndex = j;
}
for (j = 0; j < n; j++)
{
if (a[j][maxIndex] < a[i][maxIndex])
break;
}
if (j == n)
{
printf("%d %d", i, maxIndex);
break;
}
}
通过定义最大值的下标来找出符合条件的数据,比起我的用变量temp进行两次大循环,更加直观高效
2.3 题目名3(2分)
选择切分表达式这题介绍字符数组。并说明和超星视频做法区别。
2.3.1 伪代码
gets(输入字符串num[i])
n=strlen()//得到字符串长度
for(i=0 to n)
{
if(i==0或num[i]=‘-’或‘+’)
直接输出
else
{
if(num[i]=='-')
{
if(num[i]==数字)
输出+\n
else 输出
}
else if(num[i]==数字)
{
while((num[i]为数字或为‘.’)且i<n)
{
if(num[i+1]不为数字且不为‘.’)
输出+\n
else 输出
}
}
else 输出+\n
}
}
2.3.2 代码截图