Мне нужно взять вектор C ++ с потенциально большим количеством элементов, стереть дубликаты и отсортировать их.
В настоящее время у меня есть код ниже, но он не работает.
vec.erase(
std::unique(vec.begin(), vec.end()),
vec.end());
std::sort(vec.begin(), vec.end());
Как я могу правильно сделать это?
Кроме того, быстрее ли сначала удалить дубликаты (аналогично приведенному выше) или сначала выполнить сортировку? Если я сначала выполню сортировку, гарантированно ли она останется отсортированной после std::unique
выполнения?
Или есть другой (возможно, более эффективный) способ сделать все это?
Ответы:
Я согласен с Р. Пейтом и Тоддом Гарднером ;
std::set
может быть хорошей идеей здесь. Даже если вы застряли с использованием векторов, если у вас достаточно дубликатов, вам лучше создать набор для грязной работы.Давайте сравним три подхода:
Просто используя вектор, сортировка + уникальный
Преобразовать в набор (вручную)
Преобразовать в набор (используя конструктор)
Вот как они работают при изменении числа дубликатов:
Резюме : когда количество дубликатов достаточно велико, на самом деле быстрее преобразовать в набор и затем сбросить данные обратно в вектор .
И по какой-то причине выполнение преобразования набора вручную кажется более быстрым, чем использование конструктора набора - по крайней мере, для произвольных случайных данных, которые я использовал.
источник
Я переделал профилирование Нейта Коля и получил разные результаты. В моем тестовом примере прямая сортировка вектора всегда более эффективна, чем использование набора. Я добавил новый более эффективный метод, используя
unordered_set
.Имейте в виду, что
unordered_set
метод работает, только если у вас есть хорошая хеш-функция для типа, который вам нужен uniqued и отсортирован. Для малышей это просто! (Стандартная библиотека предоставляет хэш по умолчанию, который является просто функцией идентификации.) Кроме того, не забудьте отсортировать в конце, так как unordered_set, ну, в общем, неупорядоченный :)Я покопался в
set
иunordered_set
реализации , и обнаружил , что конструктор фактически построить новый узел для каждого элемента, прежде чем проверять его значение , чтобы определить , является ли он на самом деле должен быть вставлен (в визуальной реализации студии, по крайней мере).Вот 5 методов:
f1: просто использую
vector
,sort
+unique
f2: конвертировать в
set
(используя конструктор)f3: конвертировать в
set
(вручную)f4: конвертировать в
unordered_set
(используя конструктор)f5: конвертировать в
unordered_set
(вручную)Я выполнил тест с вектором 100 000 000 вставок, выбранных случайным образом в диапазонах [1,10], [1,1000] и [1,100000]
Результаты (в секундах, чем меньше, тем лучше):
источник
sort
илиunique
методы, вы должны#include <algorithm>
CWUK
scenerio , у которого есть природа возможностей, чтобы замедлитьemplace
вид строительства.std::unique
удаляет дублирующиеся элементы только в том случае, если они являются соседями: сначала нужно отсортировать вектор, прежде чем он будет работать так, как вы хотите.std::unique
определен как стабильный, поэтому вектор все равно будет отсортирован после запуска уникального для него.источник
Я не уверен, для чего вы используете это, поэтому я не могу сказать это со 100% уверенностью, но обычно, когда я думаю о «отсортированном, уникальном» контейнере, я думаю о std :: set . Это может быть лучше подходит для вашего варианта использования:
В противном случае сортировка перед вызовом уникального (как указано в других ответах) - это путь.
источник
std::unique
работает только на последовательных прогонах дублированных элементов, поэтому лучше сначала отсортировать. Однако он стабилен, поэтому ваш вектор останется отсортированным.источник
Вот шаблон, чтобы сделать это для вас:
назвать это как:
источник
erase()
метод, иначе вы должны вернуть новый конечный итератор и заставить код вызова обрезать контейнер.Эффективность - сложная концепция. Существуют соображения времени и пространства, а также общие измерения (где вы получаете только расплывчатые ответы, такие как O (n)) по сравнению с конкретными (например, сортировка пузырьков может быть намного быстрее, чем быстрая сортировка, в зависимости от входных характеристик).
Если у вас относительно мало дубликатов, то сортировка с последующим уникальным и стирание, кажется, путь. Если у вас было относительно много дубликатов, создание набора из вектора и выполнение тяжелой работы может легко обойти его.
Не просто сосредоточиться на эффективности времени. Sort + unique + erase работает в пространстве O (1), а конструкция множества работает в пространстве O (n). И ни один из них не поддается прямому распараллеливанию с уменьшением карты (для действительно огромных наборов данных).
источник
Вы должны отсортировать его, прежде чем позвонить,
unique
потому чтоunique
удаляются только дубликаты, которые находятся рядом друг с другом.редактировать: 38 секунд ...
источник
unique
удаляет только последовательные повторяющиеся элементы (что необходимо для его выполнения за линейное время), поэтому сначала следует выполнить сортировку. После звонка он останется отсортированнымunique
.источник
Если вы не хотите изменять порядок элементов, то вы можете попробовать это решение:
источник
Предполагая, что a является вектором, удалите смежные дубликаты, используя
a.erase(unique(a.begin(),a.end()),a.end());
работает в O (N) время.источник
std::sort
сначала.Как уже говорилось,
unique
требуется отсортированный контейнер. Кроме того,unique
фактически не удаляет элементы из контейнера. Вместо этого они копируются до конца,unique
возвращает итератор, указывающий на первый такой дублированный элемент, и ожидается, что вы вызоветеerase
фактическое удаление элементов.источник
Стандартный подход, предложенный Нейтом Колем, с использованием вектора, sort + unique:
не работает для вектора указателей.
Посмотрите внимательно на этот пример на cplusplus.com .
В их примере «так называемые дубликаты», перемещенные в конец, фактически отображаются как? (неопределенные значения), потому что эти «так называемые дубликаты» являются ИНОГДА «дополнительными элементами», а ИНОГДА есть «отсутствующие элементы», которые были в исходном векторе.
Проблема возникает при использовании
std::unique()
вектора указателей на объекты (утечки памяти, плохое чтение данных из HEAP, двойные освобождения, которые вызывают ошибки сегментации и т. Д.).Вот мое решение проблемы: заменить
std::unique()
наptgi::unique()
.Смотрите файл ptgi_unique.hpp ниже:
И вот программа UNIT Test, которую я использовал для тестирования:
источник
std::unique
того, как у вас есть [1, 2, 3, 2], вы не можете вызвать delete на 2, так как это оставит висячий указатель на 2! => Просто не вызывайте delete для элементов междуnewEnd = std::unique
и, такstd::end
как у вас все еще есть указатели на эти элементы[std::begin, newEnd)
!unique
a довольно бессмысленноvector<unique_ptr<T>>
, поскольку единственное дублированное значение, которое может содержать вектор, - этоnullptr
.С библиотекой Ranges (в C ++ 20) вы можете просто использовать
Обратите внимание, что он на самом деле удаляет дубликаты элементов, а не просто перемещает их.
источник
О тестах alexK7. Я попробовал их и получил аналогичные результаты, но когда диапазон значений составляет 1 миллион, случаи, использующие std :: sort (f1) и std :: unordered_set (f5), дают одинаковое время. Когда диапазон значений составляет 10 миллионов, f1 быстрее, чем f5.
Если диапазон значений ограничен, а значения имеют тип unsigned int, можно использовать std :: vector, размер которого соответствует заданному диапазону. Вот код:
источник
sort (v.begin (), v.end ()), v.erase (уникальный (v.begin (), v, end ()), v.end ());
источник
Если вы ищете производительность и используете ее
std::vector
, я рекомендую ту, которую предоставляет эта ссылка на документацию .источник
источник
Если вы не хотите модифицировать вектор (стереть, отсортировать), тогда вы можете использовать библиотеку Ньютона. В подбиблиотеке алгоритма есть вызов функции copy_single
так что вы можете:
где копия - это вектор, в который вы хотите отодвинуть копию уникальных элементов. но помните, что вы push_back элементы, и вы не создаете новый вектор
в любом случае, это быстрее, потому что вы не стираете () элементы (что занимает много времени, кроме случаев, когда вы выполняете pop_back () из-за переназначения)
Я делаю некоторые эксперименты, и это быстрее.
Также вы можете использовать:
иногда все еще быстрее.
источник
unique_copy
.Более понятный код: https://en.cppreference.com/w/cpp/algorithm/unique.
Ouput:
источник
источник
Вот пример проблемы удаления дубликатов, которая возникает с std :: unique (). На машине LINUX программа вылетает. Прочитайте комментарии для деталей.
источник
vector
содержатся целые числа, а не указатели и не указывает компаратор).Это функция, которую я создал, которую вы можете использовать для удаления повторов. Необходимые заголовочные файлы как раз
<iostream>
и<vector>
.источник