我的评论:
在这种情况下,您应该使用std::set<int>,因为这实际上符合您的要求。如果愿意,您还可以使用 std::map<int, int> 将键映射到出现次数。
OP 回复:
您能否将此添加到完整的答案中,以便我可以接受这个问题?
我们开始吧:
只过滤重复项:
#include <iostream>
#include <set>
int main()
{
int sample[] = { 5, 2, 3, 3, 2, 2 };
// add all values at most once
using Table = std::set<int>;
Table table;
for (int value : sample) table.insert(value);
// output the result
for (const Table::value_type& entry : table) {
std::cout << "Value " << entry << "\n";
}
}
输出:
Value 2
Value 3
Value 5
Demo on coliru
统计出现次数:
#include <iostream>
#include <map>
int main()
{
int sample[] = { 5, 2, 3, 3, 2, 2 };
// add all values at most once but count the number of occurrences
using Table = std::map<int, unsigned>;
Table table;
for (int value : sample) ++table[value];
// output the result
for (const Table::value_type& entry : table) {
std::cout << "Value " << entry.first << " (" << entry.second << " times)\n";
}
}
输出:
Value 2 (3 times)
Value 3 (2 times)
Value 5 (1 times)
Demo on coliru
诀窍:
如果键不存在,std::map::operator[] 会插入一个元素。此元素(在本例中为 std::pair<const int, unsigned>)默认初始化为 { key, 0 }。
所以,有两种情况:
- 钥匙还没有:
元素被创建为{ key, 0 },并且值(元素的.second)立即增加,从而导致{ key, 1 }。
- 钥匙已经在那里了:
值(元素的.second)再次递增。
过滤重复项的变体:
这保留了原始输入顺序,但删除了重复(通过在单独的std::set 中记账)。
#include <iostream>
#include <set>
#include <vector>
int main()
{
using Sample = std::vector<int>;
Sample sample = { 5, 2, 3, 3, 2, 2 };
// remove duplicates
using Table = std::set<int>;
Table table;
Sample::iterator iterRead = sample.begin();
Sample::iterator iterWrite = sample.begin();
for (; iterRead != sample.end(); ++iterRead) {
if (table.insert(*iterRead).second) *iterWrite++ = *iterRead;
}
sample.erase(iterWrite, sample.end());
// output the result
for (const Sample::value_type& entry : sample) {
std::cout << "Value " << entry << "\n";
}
}
输出:
Value 5
Value 2
Value 3
Demo on coliru
诀窍:
std::set::insert() 返回一对iterator 和bool。
iterator 指向集合中的键(已插入或已存在)。
bool 表示密钥是已插入 (true) 还是已经存在 (false)。
另一个技巧:
仅从std::vector 中删除每个找到的重复项会导致更差的复杂度 O(n²)。
因此,使用了两个迭代器,一个用于读取,一个用于写入。因此,尚未在簿记表中(因此第一次出现)的每个输入值都被写回,否则不写回。
因此,第一次出现的每个值都会移到开头并附加到每个第一次出现的先前值。此外,iterWrite 指向循环后最后写入的元素,可用于擦除其余部分(其中包含所有重复的左侧输入值)。
这个算法的复杂度是 O(n)——比简单的方法要好很多。
顺便说一句。标准算法std::remove(), std::remove_if() 也是这样做的。
因此,std::remove_if() 可以实现相同的算法:
#include <algorithm>
#include <iostream>
#include <set>
#include <vector>
int main()
{
using Sample = std::vector<int>;
Sample sample = { 5, 2, 3, 3, 2, 2 };
// remove duplicates
using Table = std::set<int>;
Table table;
Sample::iterator last
= std::remove_if(sample.begin(), sample.end(),
[&](int value) { return !table.insert(value).second; });
sample.erase(last, sample.end());
// output the result
for (const Sample::value_type& entry : sample) {
std::cout << "Value " << entry << "\n";
}
}
输出:
如上
Demo on coliru