给定一批整数,分析每个整数的每一位数字,求出现次数最多的个位数字。例如给定3个整数1234、2345、3456,其中出现最多次数的数字是3和4,均出现了3次。
输入格式:
输入在第1行中给出正整数N(≤1000),在第二行中给出N个不超过整型范围的非负整数,数字间以空格分隔。
输出格式:
在一行中按格式“M: n1 n2 …”输出,其中M是最大次数,n1、n2、……为出现次数最多的个位数字,按从小到大的顺序排列。数字间以空格分隔,但末尾不得有多余空格。
输入样例:
3
1234 2345 3456
输出样例:
3: 3 4
通关代码:
#include <stdio.h>
#define MAXS 1000
void Cutnum(int x, int *count, int N) //拆解并统计一个数
{
//while不能判断0会直接跳过
do
{
count[x % 10]++;
x /= 10;
} while (x);
}
int findmax(int x[], int N) //找到数组的最大值
{
int max = *x;
for (int i = 0; i < N; i++)
if (x[i] > max)
max = x[i];
return max;
}
void Putnum(int count[], int N, int max) //按照规定输出数组
{
printf("%d:", max); //输出出现最多的次数
for (int i = 0; i < N; i++)
if (count[i] == max)
printf(" %d", i); //输出出现最多次的数字
}
int main()
{
int x[MAXS], N; //定义数组和整数个数
scanf("%d", &N);
for (int i = 0; i < N; i++)
scanf("%d", &x[i]); //读取整数
int count[10] = {0}; //计数数组分别代表0-9出现的次数
int i = 0;
for (i = 0; i < N; i++) //分解所有的数
Cutnum(*(x + i), count, N);
int max = findmax(count, 10);
Putnum(count, 10, max);
return 0;
}
零基础解析:
3.0 思路分析
整个程序的编写思路还是比较清晰的,需要完成以下步骤:
- 必要的数据的输入
- 将一个数拆分,并且统计每个数字出现的频率
- 找到出现最多的次数
- 找到并且输出出现次数最多的数
一些在前边提到过的内容,在此就不在赘述。其实核心部分的循环和选择语句也曾有过说明,此处重点谈谈数组的指针用法。在定义好变量和输入之后,函数变成了下边的样子:
#include <stdio.h>
#define MAXS 1000
int main()
{
int x[MAXS], N; //定义数组和整数个数
scanf("%d", &N);
for (int i = 0; i < N; i++) //可以在循环内部定义i
scanf("%d", &x[i]); //读取整数
int count[10] = {0}; //计数数组分别代表0-9出现的次数
return 0;
}
3.1 数组名和首地址
通过数组的学习,我们需要至少知道两个东西,这是接下来理解的关键(对于int x[10]
)
&
表示对值取地址,*
表示取地址对应的值;x
和&x[0]
都表示数组x[10]
的首地址;- 当
x
表示首地址的时候,x+i
不在是数学意义上的,而是x
移动i
个位置后的地址
通过一段程序,可以很好的理解这些东西:
#include <stdio.h>
int main()
{
int x[5] = {2,8,6,5,7};
printf("&x[0]:%d x:%d\n&x[1]:%d x+1:%d", &x[0], x, &x[1], x+1);
return 0;
}
程序的运行结果为
&x[0]:6618624 x:6618624
&x[1]:6618628 x+1:6618628
不难看出:
&x[0]
和x
对应的是同一个值x+1
也不再是数学意义上的x+1
【如果是的话x+1 = 6618624+1 = 6618625
与实际输出不符合】- 而且
x+1
的值与&x[1]
的值相同,也就是x
【首地址】,移动1
位后的地址
在接受了上述结论之后,可以较好的理解接下来的程序。
3.2 参数的传递
个人理解的数组作为参数传入函数,实际上是将地址传入了子函数。因此在子函数中对数组做出的改动,在返回主函数后依然存在。
这和一般意义上的子函数是形式参数的效果是不一样的,反而和指针的效果是相同的。
所以传递数组实际上是传递数组的首地址(字符串同样适用),换句话说,数组的传递实际上是地址的传递。因此以下可以有以下形式:
-
子函数接收:指针
数组类型 *数组名
:void Cutnum(int x, int *count, int N)
。其中需要传入数组count
数组类型 数组名[]
:void Putnum(int count[], int N, int max)
。同样需要传入数组count
-
传入子函数:首地址
数组名表示的首地址
:Cutnum(*(x + i), count, N);
。其中*(x+i)
是值,count
是数组count
的首地址。直接表示的首地址
:上一例子可以改为,Cutnum(*(x + i), &count[0], N);
不过这个操作过于诡异,如果自己这样写会显得自己很不正常。
void Cutnum(int x, int *count, int N) //*count传递
{
//while不能判断0会直接跳过
do
{
count[x % 10]++;
x /= 10;
} while (x);
}
void Putnum(int count[], int N, int max) //count[]传递
{
printf("%d:", max);
for (int i = 0; i < N; i++)
if (count[i] == max)
printf(" %d", i);
}
3.3 数组元素的调用
使用数组的根本目的,还是对数组里的元素进行运算和处理,有两种方法可以调用数组里的元素,对其进行处理:
- 常用调用方法
x[i]
:这是大家最常用且习惯的用法。 - 指针方法调用
*(x+i)
:在我们知道x+i
表示的数组x
的第i
个元素的地址之后,取该地址对应的值只需要加上*
即可。
需要注意的是必须要加括号,因为
*
运算符的优先级更高,也就是说:
x[i] = *(x+i)
x[0] + i = *x + i
在知道这些以后,就能很好的理解下边这段代码:
void Cutnum(int x, int *count, int N)
{
do
{
count[x % 10]++; //x[i]表示
//也可以写:*(count+x%10)++;
x /= 10;
} while (x);
}
int main()
{
int x[MAXS], N;
scanf("%d", &N);
for (int i = 0; i < N; i++)
scanf("%d", &x[i]);
int count[10] = {0};
int i = 0;
for (i = 0; i < N; i++)
Cutnum(*(x + i), count, N); //*(x+i)表示
//也可以写Cutnum(x[i], count, N);
return 0;
}
3.4 总结
总结起来就一句话:
- 子函数接收的是指针变量,可选择以下两种方法【即保证是指针变量】
void test(int *x)
void test(int x[])
- 传递进入子函数的是地址,可选择以下两种方法【
x
本身是地址,&
是取值对应的地址】test(x);
test(&x[0]);
- 调用参与运算的是数组的元素(值):可选择以下两种方法【
*
表示取地址对应的值】x[i]
*(x+i)
【注意优先级】