小白洛谷入门B2096直方图做题分享

文章讲述了如何使用C语言优化算法,避免嵌套循环,通过动态数组和哈希表统计非负整数数组中每个数的出现次数,提高代码效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

   本人还是小白一只,浅入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 来记录每个数字出现的次数。相比较手动分配的数组和辅助数组,动态数组和哈希表具有更好的扩展性和效率。动态数组可以自动调整大小,无需手动管理内存,而哈希表在查找和更新操作时的平均时间复杂度是常数时间。

其次,在统计每个数字出现次数时,原始代码中需要两次遍历:第一次遍历找到最大值,第二次遍历统计次数。而在优化后的代码中,我们将这两个操作合并为一次遍历。在遍历过程中,我们不仅记录每个数字出现的次数,还更新最大值。这样可以减少遍历次数,提高效率。

看来作为小白的我还有很长的路要走啊。

(一名小白的做题记录)。

<think>好的,用户让我介绍一下洛谷的B2096题,也就是直方图的问题。我需要先回想一下题目内容。可能这个题目是关于统计数字出现次数然后输出直方图的。之前用户问过B2131甲流病人初筛的问题,现在这个B2096应该也是类似的算法题。 首先,用户需要的是C++的解决方案。我需要确定题目的具体要求。比如,输入是什么样的,输出应该怎样。比如,题目可能给出一组数字,然后统计每个数字出现的次数,然后以某种格式输出,比如直方图,可能用星号或者其他符号表示次数。 接下来,我需要确认输入输出的格式。例如,输入可能是第一行一个整数n,表示数字个数,接下来n个整数。然后输出每个数字出现的次数,可能按数字升序排列,每个数字一行,后面跟着对应的星号数量。 然后,我需要考虑如何用C++实现。常用的方法是用map或者数组来统计次数。如果数字的范围不大,比如题目中给出的数字范围较小,可以用数组来统计,这样更快。但如果数字范围不确定,可能用map更合适,尤其是数字可能很大的情况下。 比如,题目中的数字如果是非负整数,或者有明确的范围,用数组比较方便。假设题目中的数字范围不大,比如0到某个数,那么可以用数组。否则用map。比如,如果输入是5个数,3,1,2,3,3,那么统计结果是1:1,2:1,3:3。输出时按数字升序排列,每个数字后面跟相应数量的星号。 那具体到代码,首先读取n,然后读取n个数,统计每个数的出现次数。然后遍历统计结果,按数字顺序输出,每个数字一行,后面跟着对应次数的星号。如果用数组的话,可能要考虑数字的范围,比如最大值是多少,或者动态调整。但可能题目中给出的数字范围有限,比如不超过10000或者类似。或者用户可能用map来存储,这样自动排序的话,可能需要用ordered map或者再排序一遍。 比如,用map的话,map会自动按照键升序排列,这样输出时直接遍历即可。所以步骤应该是: 1. 读取n。 2. 读取n个数字,统计到map<int, int>里。 3. 遍历map,输出每个键值对,值为次数,转换成星号。 那代码的大致结构会是: #include <iostream> #include <map> using namespace std; int main() { int n; cin >> n; map<int, int> countMap; for (int i = 0; i < n; ++i) { int num; cin >> num; countMap[num]++; } for (auto &pair : countMap) { cout << pair.first << " "; for (int j = 0; j < pair.second; ++j) { cout << "*"; } cout << endl; } return 0; } 但可能题目中的输出格式不同,比如不需要输出数字本身,而是按数字出现的顺序或者其他要求。或者直方图的表示方式不同。需要仔细查看题目描述。 可能用户没有给出具体的题目描述,所以需要假设题目要求。比如,洛谷B2096的题目可能是这样的:给定n个整数,统计每个数出现的次数,并输出直方图,每个数一行,数后面跟着等于其次数的星号,按数字从小到大的顺序排列。 另外,需要注意,如果输入的数字可能有重复,但统计的时候需要全部计入。比如输入3 1 3 3,那么统计3出现3次。 此外,还需要考虑输入输出的效率,比如使用scanf和printf代替cin和cout,但对于一般的题目来说,cin和cout足够,除非数据量很大。 可能用户需要的是这样的解决方案。那在回答的时候,需要分步骤说明思路,给出代码,并解释代码的结构。可能用户之前的问题得到了结构清晰的回答,所以这次也应该保持类似的格式,比如方法思路、解决代码、代码解释、示例输入输出、注意事项等部分。 需要注意的是,题目是否要求输出包括所有可能的数字,即使出现次数为零。但根据常规直方图的问题,应该只输出出现过的数字。 综上,我需要写出步骤清晰的解决思路,代码实现,并解释每个部分的作用。可能还需要给出示例输入输出,比如输入5个数字,输出对应的直方图。同时,提醒可能的注意事项,比如数字的范围,或者使用map自动排序的特性,确保输出是按升序排列的。</think>--- ### 方法思路 **题目 B2096 直方图**要求统计一组整数中每个数字的出现次数,并以直方图形式输出(数字升序排列,次数用 `*` 表示)。具体步骤如下: 1. **输入处理**:读取整数数量 `n` 及后续的 `n` 个整数。 2. **统计频率**:使用 `map<int, int>` 记录每个数字的出现次数(`map` 自动按数字升序排序)。 3. **输出直方图**:遍历统计结果,每个数字单独成行,后接对应数量的 `*`。 --- ### 解决代码 ```cpp #include <iostream> #include <map> using namespace std; int main() { int n, num; cin >> n; // 读取整数总数 map<int, int> count; // 存储数字及其出现次数(自动升序排序) for (int i = 0; i < n; ++i) { cin >> num; count[num]++; // 统计频率 } // 输出直方图 for (auto& entry : count) { cout << entry.first << " "; // 输出数字 for (int j = 0; j < entry.second; ++j) { cout << "*"; // 输出对应数量的 * } cout << endl; } return 0; } ``` --- ### 代码解释 1. **输入处理** - 读取整数总数 `n`,然后循环读取每个整数并存入 `map`。 - `map<int, int>` 的键为数字,值为出现次数,且自动按键升序排列。 2. **统计频率** - 遍历输入时,通过 `count[num]++` 直接累加每个数字的频率。 3. **输出直方图** - 遍历 `map`,每行先输出数字,再循环输出对应次数的 `*`。 - 例如:数字 `3` 出现 `2` 次时,输出 `3 **`。 --- ### 示例输入输出 输入: ``` 6 5 2 2 3 5 5 ``` 输出: ``` 2 ** 3 * 5 *** ``` --- ### 注意事项 1. **数据范围** - 若数字较大(如超过 $10^5$),使用 `map` 仍可高效处理。 2. **输出顺序** - `map` 自动按数字升序排序,无需额外操作。 3. **扩展性** - 若需自定义排序(如按频率排序),可改用 `vector` 存储后排序。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

cyemd

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值