Muszę wziąć wektor C ++ z potencjalnie dużą ilością elementów, usunąć duplikaty i posortować.
Obecnie mam poniższy kod, ale to nie działa.
vec.erase(
std::unique(vec.begin(), vec.end()),
vec.end());
std::sort(vec.begin(), vec.end());
Jak mogę to poprawnie zrobić?
Ponadto, czy szybciej jest najpierw usunąć duplikaty (podobnie jak w kodowaniu powyżej) czy najpierw wykonać sortowanie? Jeśli najpierw wykonam sortowanie, czy na pewno pozostanie posortowane po std::unique
wykonaniu?
Czy jest jeszcze inny (być może bardziej wydajny) sposób na wykonanie tego wszystkiego?
Odpowiedzi:
Zgadzam się z R. Pate i Toddem Gardnerem ;
std::set
może być dobrym pomysłem tutaj. Nawet jeśli utkniesz przy użyciu wektorów, jeśli masz wystarczającą liczbę duplikatów, lepiej byłoby stworzyć zestaw do brudnej roboty.Porównajmy trzy podejścia:
Wystarczy użyć wektora, sortuj + unikatowe
Konwertuj na zestaw (ręcznie)
Konwertuj na zestaw (za pomocą konstruktora)
Oto jak działają one jako liczba zmian duplikatów:
Podsumowanie : gdy liczba duplikatów jest wystarczająco duża, konwersja na zestaw jest w rzeczywistości szybsza, a następnie zrzucenie danych z powrotem do wektora .
I z jakiegoś powodu ręczne wykonanie konwersji zestawu wydaje się być szybsze niż użycie konstruktora zestawu - przynajmniej na losowych danych zabawki, których użyłem.
źródło
Zredagowałem profilowanie Nate'a Kohla i uzyskałem różne wyniki. W moim przypadku testowym bezpośrednie sortowanie wektora jest zawsze bardziej wydajne niż użycie zestawu. Dodałem nową, bardziej wydajną metodę, używając
unordered_set
.Pamiętaj, że
unordered_set
metoda działa tylko wtedy, gdy masz dobrą funkcję skrótu dla typu, którego potrzebujesz unikatowo i posortowanego. Dla ints jest to łatwe! (Standardowa biblioteka zawiera domyślny skrót, który jest po prostu funkcją tożsamości). Nie zapomnij również posortować na końcu, ponieważ zestaw_uporządkowany jest, no, nieuporządkowany :)Zrobiłem trochę kopania wewnątrz
set
iunordered_set
wdrożenia i odkrył, że konstruktor faktycznie zbudować nowy węzeł dla każdego elementu, przed sprawdzeniem jego wartości w celu określenia, czy powinien to być rzeczywiście włożona (w realizacji programu Visual Studio, przynajmniej).Oto 5 metod:
f1: Tylko używając
vector
,sort
+unique
f2: Konwertuj na
set
(za pomocą konstruktora)f3: Konwertuj na
set
(ręcznie)f4: Konwertuj na
unordered_set
(za pomocą konstruktora)f5: Konwertuj na
unordered_set
(ręcznie)Zrobiłem test z wektorem 100 000 000 ints wybranych losowo w zakresach [1,10], [1,1000] i [1,100000]
Wyniki (w sekundach im mniejsze, tym lepiej):
źródło
sort
lubunique
metod, musisz#include <algorithm>
CWUK
sceneriach, które mają naturę możliwości spowolnienia tegoemplace
rodzaju budowy.std::unique
usuwa zduplikowane elementy tylko wtedy, gdy są sąsiadami: musisz najpierw posortować wektor, zanim zadziała on zgodnie z twoimi zamierzeniami.std::unique
jest zdefiniowany jako stabilny, więc wektor będzie nadal sortowany po uruchomieniu na nim unikalnego.źródło
Nie jestem pewien, do czego go używasz, więc nie mogę tego powiedzieć ze 100% pewnością, ale normalnie, kiedy myślę o „posortowanym, unikalnym” pojemniku, myślę o std :: set . Może lepiej pasować do twojej skrzynki użytkownika:
W przeciwnym razie posortowanie przed wywołaniem unikalnego (jak wskazały inne odpowiedzi) jest dobrym rozwiązaniem.
źródło
std::unique
działa tylko na kolejnych seriach zduplikowanych elementów, więc lepiej najpierw posortuj. Jest jednak stabilny, więc wektor pozostanie posortowany.źródło
Oto szablon, który możesz dla Ciebie zrobić:
nazwij to tak:
źródło
erase()
metodę, w przeciwnym razie musisz zwrócić nowy iterator końcowy i mieć kod wywołujący obcinający kontener.Wydajność to skomplikowana koncepcja. Są względy dotyczące czasu i przestrzeni, a także ogólne pomiary (w których otrzymujesz tylko niejasne odpowiedzi, takie jak O (n)) w porównaniu do konkretnych (np. Sortowanie bąbelkowe może być znacznie szybsze niż szybkie sortowanie, w zależności od charakterystyki wejściowej).
Jeśli masz stosunkowo niewiele duplikatów, sortowanie, a następnie unikanie i usuwanie wydają się być dobrym rozwiązaniem. Jeśli miałeś stosunkowo dużo duplikatów, utworzenie zestawu z wektora i pozwolenie mu na wykonanie ciężkiego podnoszenia może go łatwo pokonać.
Nie koncentruj się tylko na wydajności czasu. Sortowanie + unikanie + wymazywanie działa w przestrzeni O (1), podczas gdy konstrukcja zestawu działa w przestrzeni O (n). I żadne z nich nie nadaje się bezpośrednio do zmniejszania równoległości map (dla naprawdę dużych zestawów danych).
źródło
Musisz to posortować, zanim zadzwonisz,
unique
ponieważunique
usuwa tylko duplikaty znajdujące się obok siebie.edycja: 38 sekund ...
źródło
unique
usuwa tylko kolejne zduplikowane elementy (co jest konieczne, aby działało w czasie liniowym), dlatego najpierw należy wykonać sortowanie. Pozostanie posortowane po połączeniu zunique
.źródło
Jeśli nie chcesz zmieniać kolejności elementów, możesz wypróbować to rozwiązanie:
źródło
Zakładając, że a jest wektorem, usuń ciągłe duplikaty za pomocą
a.erase(unique(a.begin(),a.end()),a.end());
działa w czasie O (n) .źródło
std::sort
pierwszego.Jak już wspomniano,
unique
wymaga posortowanego pojemnika. Ponadtounique
tak naprawdę nie usuwa elementów z kontenera. Zamiast tego są one kopiowane do końca,unique
zwraca iterator wskazujący na pierwszy taki zduplikowany element i oczekuje się, że zadzwonisz,erase
aby faktycznie usunąć elementy.źródło
Standardowe podejście sugerowane przez Nate Kohla, po prostu za pomocą wektora, sortowania + unikatowego:
nie działa dla wektora wskaźników.
Przyjrzyj się dokładnie temu przykładowi na cplusplus.com .
W ich przykładzie „tak zwane duplikaty” przeniesione na koniec są faktycznie pokazane jako? (niezdefiniowane wartości), ponieważ te „tak zwane duplikaty” są CZASAMI „dodatkowymi elementami”, a CZASAMI są „brakujące elementy” w oryginalnym wektorze.
Problem występuje podczas używania
std::unique()
na wektorze wskaźników do obiektów (wycieki pamięci, zły odczyt danych z HEAP, duplikowanie zwolnień, które powodują błędy segmentacji itp.).Oto moje rozwiązanie problemu zamienić
std::unique()
zptgi::unique()
.Zobacz plik ptgi_unique.hpp poniżej:
A oto program testowy UNIT, którego testowałem:
źródło
std::unique
[1, 2, 3, 2] nie można wywołać delete na 2, ponieważ pozostawiłoby to wiszący wskaźnik na 2! => Po prostu nie nazywaj delete na elementach pomiędzy,newEnd = std::unique
astd::end
ponieważ nadal masz wskaźniki do tych elementów w[std::begin, newEnd)
!unique
na zasadzievector<unique_ptr<T>>
, jak tylko powielona wartość taka może zawierać wektor jestnullptr
.Z biblioteką Ranges (pochodzącą z C ++ 20) możesz po prostu używać
Zauważ, że faktycznie usuwa zduplikowane elementy, a nie tylko je przenosi.
źródło
Informacje o testach porównawczych alexK7. Próbowałem ich i uzyskałem podobne wyniki, ale gdy zakres wartości wynosi 1 milion, przypadki przy użyciu std :: sort (f1) i przy użyciu std :: unordered_set (f5) dają podobny czas. Gdy zakres wartości wynosi 10 milionów, f1 jest szybsze niż f5.
Jeśli zakres wartości jest ograniczony, a wartości nie są oznaczone int, możliwe jest użycie std :: vector, którego rozmiar odpowiada podanemu zakresowi. Oto kod:
źródło
sort (v.begin (), v.end ()), v.erase (unique (v.begin (), v, end ()), v.end ());
źródło
Jeśli szukasz wydajności i używania
std::vector
, polecam ten, który zapewnia ten link do dokumentacji .źródło
źródło
Jeśli nie chcesz modyfikować wektora (kasowanie, sortowanie), możesz użyć biblioteki Newtona. W podbibliotece algorytmu znajduje się wywołanie funkcji, copy_single
więc możesz:
gdzie kopia jest wektorem, w którym chcesz push_back kopię unikalnych elementów. ale pamiętaj, że wypychasz elementy i nie tworzysz nowego wektora
w każdym razie jest to szybsze, ponieważ nie kasujesz () elementów (co zajmuje dużo czasu, z wyjątkiem pop_back () z powodu zmiany przypisania)
Robię eksperymenty i jest to szybsze.
Możesz także użyć:
czasami jest jeszcze szybszy.
źródło
unique_copy
.Bardziej zrozumiały kod z: https://en.cppreference.com/w/cpp/algorithm/unique
wyjście:
źródło
źródło
Oto przykład problemu z duplikatem usuwania, który występuje w przypadku std :: unique (). Na komputerze z systemem LINUX program ulega awarii. Przeczytaj komentarze, aby uzyskać szczegółowe informacje.
źródło
vector
zawiera liczby całkowite, a nie wskaźniki i nie określa komparatora).Jest to funkcja, którą stworzyłem, której możesz użyć do usuwania powtórzeń. Potrzebne pliki nagłówkowe to tylko
<iostream>
i<vector>
.źródło