本人还是小白一只,浅入c语言,如果有什么不对的,还请评论区指正。废话不多说先上题目。
题目描述
给定一个非负整数数组,统计里面每一个数的出现次数。我们只统计到数组里最大的数。
假设 Fmax(Fmax≤100000)是数组里最大的数,那么我们只统计 {0,1,2…Fmax} 里每个数出现的次数。
输入格式
第一行 n 是数组的大小。1≤n≤100000。
紧接着一行是数组的 n 个元素。
输出格式
按顺序输出每个数的出现次数,一行一个数。如果没有出现过,则输出 0,对于例子中的数组,最大的数是 3,因此我们只统计 {0,1,2,3}的出现频数。
嗯,这不就是数组的动态输入嘛,直接用malloc函数定义一个数组,再通过for循环输入,就完成了输入了,嗯,然后找到最大值,在判断0到最大值之间的数在输入数中出现的次数,啊,这有点小麻烦,不过这都不是问题(这里就想错了),直接先定义一个变量b,通过遍历输入的数,找到最大值,赋给b,后面就要判断0到b之间数的次数了,而这一部分也是我想错的地方,直接上代码。
#include <stdio.h>
#include <stdlib.h>
int main() {
int n, i;
// 用户输入数组大小 n
scanf("%d", &n);
// 动态分配大小为 n 的整数数组 a
int* a = (int*)malloc(n * sizeof(int));
// 用户输入 n 个整数,存放到数组 a 中
for (i = 0; i < n; i++) {
scanf("%d", &a[i]);
}
int b = 0;
// 找到数组中的最大值
for (i = 0; i < n; i++) {
if (b < a[i])
b = a[i];
}
// 动态分配大小为 b+1 的整数数组 d
int* d = (int*)malloc((b + 1) * sizeof(int));
// 初始化数组 d 中的所有元素为 0
for (i = 0; i <= b; i++) {
d[i] = 0;
// 遍历数组 a,统计每个数字出现的次数
for (int c = 0; c < n; c++) {
if (i == a[c])
d[i] = d[i] + 1;
}
}
// 输出数组 d 中的每个元素的出现次数
for (i = 0; i <= b; i++) {
printf("%d\n", d[i]);
}
// 释放数组 d 和数组 a 的内存空间
free(d);
free(a);
return 0;
}
先说一下,这部分代码的败笔之处吧,就是中间使用了嵌套for循环,导致代码的效率大大降低,洛谷上通过时间是1秒而我这个代码足足有12秒,所以不能用这个方法计数了,然后当场去世,自己写的代码真心难优化,所以就想怎么拆掉这个for的嵌套。。。,最后,终于给我找到了,上代码。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main()
{
int n, i;
scanf("%d", &n);
int* a = (int*)malloc(n * sizeof(int));
for (i = 0; i < n; i++)
{
scanf("%d", &a[i]);
}
int b = 0;
// 找到最大的数值并记录是否已经处理过
int* flag = (int*)calloc(n, sizeof(int));
for (i = 0; i < n; i++)
{
if (b < a[i])
b = a[i];
flag[i] = 1;
}
// 统计数字出现的次数
int* d = (int*)calloc(b + 1, sizeof(int));
for (i = 0; i < n; i++)
{
if (flag[i])
d[a[i]]++;
flag[i] = 0;
}
// 输出结果
for (i = 0; i <= b; i++)
{
printf("%d\n", d[i]);
}
free(d);
free(flag);
free(a);
return 0;
}
由于用for嵌套太慢,那就要分步完成,然后我就想到了(过程极其艰难)处理最大值的同时给每个值做上标记,这里用calloc函数创建一个初始化为0的数组flag,用于标记,然后计数的方法也改了,通过遍历,对每个数计数,用flag作为判断条件,这里用数组d来存放次数,并且下标就是计数的那个数,一举两得,这里给d的大小为b+1,最后一个用于存放结束标志符。放在洛谷一下过了,心中狂喜。
那么大佬怎么做的呢,且看下面的代码。
#include <iostream>
#include <vector>
#include <unordered_map>
int main() {
int n;
std::cin >> n;
std::vector<int> a(n);
for (int i = 0; i < n; i++) {
std::cin >> a[i];
}
int max = 0;
std::unordered_map<int, int> count;
for (int i = 0; i < n; i++) {
count[a[i]]++;
if (a[i] > max) {
max = a[i];
}
}
for (int i = 0; i <= max; i++) {
std::cout << count[i] << std::endl;
}
return 0;
}
小白:嗯嗯,这是什么啊!!
大佬:首先,在原始代码中,我们使用了手动分配的数组和一个辅助数组来统计每个数字出现的次数。而在优化后的代码中,我们使用了动态数组(vector) a 存放输入的数字,使用哈希表(unordered_map) count 来记录每个数字出现的次数。相比较手动分配的数组和辅助数组,动态数组和哈希表具有更好的扩展性和效率。动态数组可以自动调整大小,无需手动管理内存,而哈希表在查找和更新操作时的平均时间复杂度是常数时间。
其次,在统计每个数字出现次数时,原始代码中需要两次遍历:第一次遍历找到最大值,第二次遍历统计次数。而在优化后的代码中,我们将这两个操作合并为一次遍历。在遍历过程中,我们不仅记录每个数字出现的次数,还更新最大值。这样可以减少遍历次数,提高效率。
看来作为小白的我还有很长的路要走啊。
(一名小白的做题记录)。