最直接去重取数量的方式是用std::set构造并调用size(),但大数据量时推荐std::unordered_set(平均O(n));若内存受限可排序后遍历计数;处理vector须用begin()/end()而非sizeof。

用 std::set 去重后取 size() 最直接
对普通数组(或 std::vector),最常用也最不容易出错的方式是把元素插入 std::set,它自动去重,再调用 size():
int arr[] = {1, 2, 2, 3, 4, 4, 5};
int n = sizeof(arr) / sizeof(arr[0]);
std::set<int> s(arr, arr + n);
std::cout << s.size(); // 输出 5
注意:这要求元素类型支持比较操作(如 int、string 默认支持)。如果自定义类型没重载 <,编译会报错 invalid operands to binary expression。
常见坑:
-
std::set插入是O(n log n),大数据量时比std::unordered_set慢 - 别误写成
std::set<int> s = {arr};—— 这只会插入一个指针值,不是展开数组
用 std::unordered_set 提升性能
如果只关心个数、不关心顺序,且元素类型支持 hash(如 int、std::string),std::unordered_set 平均 O(n),更高效:
立即学习“C++免费学习笔记(深入)”;
std::unordered_set<int> us(arr, arr + n); std::cout << us.size();
但要注意:
- 自定义类型必须提供
std::hash特化,否则编译失败:use of deleted function 'std::hash<MyType>::hash()' - 重复插入同一值不会报错,也不会影响结果,这是预期行为
- 内存占用略高,但对多数场景可忽略
原地排序 + 单次遍历(适合只读、无额外空间需求)
如果不能用额外容器,且允许修改原数组,先排序再线性扫描是经典解法:
std::sort(arr, arr + n);
int count = n > 0 ? 1 : 0;
for (int i = 1; i < n; ++i) {
if (arr[i] != arr[i-1]) count++;
}
适用场景:
- 嵌入式环境或内存极度受限
- 数组本身已部分有序,
std::sort实际开销小 - 元素类型不可哈希也不可比较(比如只有
==可用)——这时只能靠两两比较,但复杂度升到O(n²),慎用
处理 std::vector 或迭代器范围时别硬算长度
如果数据来自 std::vector<int> v,别再用 sizeof(v)/sizeof(v[0]) —— 这永远返回 sizeof(std::vector),不是元素个数:
std::vector<int> v = {1, 2, 2, 3};
std::set<int> s(v.begin(), v.end()); // 正确
// std::set<int> s(v.data(), v.data() + v.size()); // 也可,但多此一举
关键点:
- 统一用
v.begin()/v.end()或v.data()/v.data() + v.size() - 如果传入空容器,
std::set构造正常,size()返回 0,无需额外判空 - C++20 起可用
std::ranges::unique配合std::distance,但兼容性和可读性不如前几种
std::unordered_set;真要抠内存或不能改数据,才考虑排序+遍历。别为了“看起来更底层”而绕开标准容器——它们已经足够快,也足够可靠。


















