优先选std::unordered_map统计频次,平均O(1);若需key有序则用std::map;小范围非负整数直接用vector索引计数更快;浮点数作key需量化或自定义哈希。

用 std::map 或 std::unordered_map 统计频次最直接
数组直方图本质是统计每个值出现的次数,C++ 没有内置直方图函数,但用关联容器几行就能搞定。优先选 std::unordered_map(哈希表),平均 O(1) 插入,比 std::map(红黑树,O(log n))快;除非你需要结果按 key 排序输出,才考虑 std::map。
常见错误是手动遍历 + 嵌套查找——别这么干。现代写法直接用 operator[] 或 insert + find 配合 ++:
std::vector<int> arr = {1, 2, 2, 3, 3, 3};
std::unordered_map<int, int> hist;
for (int x : arr) {
hist[x]++; // 自动初始化为 0 再加 1
}
- 注意:
hist[x]对不存在的x会默认构造int()(即 0),再自增,安全简洁 - 如果元素类型不支持默认构造(比如自定义结构体),改用
hist.try_emplace(x, 1)或hist.insert({x, 1}).first->second++ - 浮点数慎用
unordered_map做 key——精度误差会导致相同值被算作不同 key;此时应先量化(如乘 100 取整)或改用带 epsilon 比较的自定义哈希
整数范围有限时,用 std::vector 当计数数组更快更省
当数组元素是小范围非负整数(比如像素值 0–255、类别 ID 0–999),直接用 std::vector 当桶数组,索引即值,访问 O(1),零分配开销,比哈希表快一个数量级。
关键点是预估范围并初始化全零:
立即学习“C++免费学习笔记(深入)”;
std::vector<int> arr = {0, 1, 1, 2, 2, 2, 255};
int max_val = *std::max_element(arr.begin(), arr.end());
std::vector<int> hist(max_val + 1, 0); // 索引 0 到 max_val
for (int x : arr) hist[x]++;
- 若含负数,先平移:算出
min_val,用hist[x - min_val]++,hist大小为max_val - min_val + 1 - 别忘了检查越界:
if (x >= 0 && x ,尤其输入不可信时 - 内存代价明确:范围大(如
int全域)绝对不能用,会炸堆;只适用于“小而密”的整数集
std::count_if 和 std::for_each 不适合做直方图
有人想用 std::count_if 对每个唯一值调用一次,看似简洁,实则是 O(n²) 时间——对含 k 个不同值的数组,要扫 k 遍原数组。k 接近 n 时就是 O(n²),远慢于单次遍历的哈希或向量方案。
std::for_each 本身只是循环包装器,不解决数据结构选择问题;若里面仍用 map 或 vector,那它只是语法糖,没实质优势。
- 错误示范:
for (int v : unique_vals) hist[v] = std::count_if(arr.begin(), arr.end(), [v](int x){return x==v;}); - 真正该用
std::for_each的场景是副作用操作(如打印),不是聚合统计 - 标准算法库中没有直方图专用接口,别强套;老实用循环或容器接口
输出直方图时,顺序和格式常被忽略
直方图结果存在 map 或 unordered_map 里,但遍历时顺序不等于原始数组顺序,也不等于数值大小顺序——这点容易在调试或绘图时出错。
- 要按数值升序输出:用
std::map存储,或对unordered_map的begin()/end()转 vector 后std::sort(按pair.first) - 要按频次降序输出:同样需额外排序,比较
pair.second - 打印时别直接
cout << hist——C++ 没重载流操作符;必须显式遍历:for (const auto& p : hist) cout << p.first << ": " << p.second << "\n";
直方图本身不关心“是否归一化”,除不除总数全看下游用途;但一旦做概率分布,记得用 double 类型存频次,避免整数截断。


















