std :: unordered_set :
Внутренне, элементы не отсортированы в каком-либо конкретном порядке , но организованы в сегменты.В какую корзину помещается элемент, полностью зависит от хеша его значения.Это обеспечивает быстрый доступ к отдельным элементам, так как после вычисления хеша это относится к точному сегменту, в который помещен элемент.
Если вам нужно заказать уникальные терминалы, используйте std :: set :
#include <iostream>
#include <vector>
#include <string>
#include <set>
int main() {
std::vector<std::string>terminals = { "e", "d", "a", "b", "c", "a", "a", "a", "a", "b","b", "b", "b", "c", "z" };
for(const std::string& terminal : terminals) // print given vector
std::cout << terminal << " ";
std::cout << "\n";;
// populate the set directly from the vectors iterators:
std::set<std::string> newSet(terminals.begin(), terminals.end());;
std::cout << "printing the (ordered) set:" << "\n";;
for(const std::string& terminal : newSet)
std::cout << terminal << " ";
std::cout << "\n";;
}
Если вы хотите сохранить оригинальный порядок, вы не можете использовать ни один из них в качестве конечного хранилища, но вы можете использоватьstd::unordered_set
в качестве кэша / черного списка для значений, которые вы уже вставили в окончательное хранилище.
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <unordered_set>
int main() {
std::vector<std::string>terminals = { "e", "d", "a", "b", "c", "a", "a", "a", "a", "b","b", "b", "b", "c", "z" };
for(const std::string& terminal : terminals) // print given vector
std::cout << terminal << " ";
std::cout << "\n";;
std::vector<std::string> newSet; // not really a set anymore
std::unordered_set<std::string> cache; // blacklist
// try to insert all terminals and only when an insert is successful,
// put the terminal in newSet
std::for_each(terminals.begin(), terminals.end(),
[&](const std::string& terminal) {
auto [it, inserted] = cache.insert(terminal);
if(inserted)
newSet.push_back(terminal);
}
);
std::cout << "printing the vector of unique terminals:" << "\n";;
for(const std::string& terminal : newSet)
std::cout << terminal << " ";
std::cout << "\n";;
}
Если вы хотите, чтобы исходный заказ и не возражал вносить изменения непосредственно вИсходный вектор terminals
, вы можете использовать std::remove_if
в сочетании с unordered_set
, что приятно, поскольку для него не требуется новый вектор.Это аннотированный вариант ответа @Marek R:
Прочтите это сначала: Стереть-удалить идиому
int main() {
std::vector<std::string>terminals = { "e", "d", "a", "b", "c", "a", "a", "a", "a", "b","b", "b", "b", "c", "z" };
for(const std::string& terminal : terminals) // print given vector
std::cout << terminal << " ";
std::cout << "\n";;
std::unordered_set<std::string> cache; // blacklist
// remove_if() moves all entries in your container, for which the
// UnaryPredicate(*) returns true, to the end of the container. It returns
// an iterator pointing to the first element in the vector that was
// moved - which is a suitable starting point for a subsequent erase().
//
// (*) UnaryPredicate: A callable that returns true or false given a single
// value.
// auto past_new_end = std::vector<std::string>::iterator past_new_end
auto past_new_end = std::remove_if(terminals.begin(), terminals.end(),
// this lambda is the UnaryPredicate
[&](const std::string& terminal) {
// insert returns a std::pair<Iterator, bool>
// where the bool (.second in the pair) is false
// if the value was not inserted (=it was already present)
return cache.insert(terminal).second == false;
}
);
std::cout << "display all the entries (now with unspecified values) "
"that will be erased:\n";
std::copy(past_new_end, terminals.end(),
std::ostream_iterator<std::string>(std::cout, "<"));
std::cout << "\n";
// erase all the moved entries
terminals.erase(past_new_end, terminals.end());
std::cout << "printing the unique terminals:" << "\n";;
for(const std::string& terminal : terminals)
std::cout << terminal << " ";
std::cout << "\n";;
}