Saya perlu mengambil vektor C ++ dengan elemen yang berpotensi banyak, menghapus duplikat, dan mengurutkannya.
Saat ini saya memiliki kode di bawah ini, tetapi tidak berfungsi.
vec.erase(
std::unique(vec.begin(), vec.end()),
vec.end());
std::sort(vec.begin(), vec.end());
Bagaimana saya bisa melakukan ini dengan benar?
Selain itu, apakah lebih cepat menghapus duplikat terlebih dahulu (mirip dengan kode di atas) atau melakukan pengurutan terlebih dahulu? Jika saya melakukan penyortiran terlebih dahulu, apakah dijamin akan tetap disortir setelah std::unique
dieksekusi?
Atau adakah cara lain (mungkin lebih efisien) untuk melakukan semua ini?
Jawaban:
Saya setuju dengan R. Pate dan Todd Gardner ; Sebuah
std::set
mungkin ide yang baik di sini. Bahkan jika Anda terjebak menggunakan vektor, jika Anda memiliki cukup duplikat, Anda mungkin lebih baik membuat satu set untuk melakukan pekerjaan kotor.Mari kita bandingkan tiga pendekatan:
Hanya menggunakan vektor, urutkan + unik
Konversi untuk mengatur (secara manual)
Konversi ke set (menggunakan konstruktor)
Begini cara kerjanya saat jumlah duplikat berubah:
Rangkuman : ketika jumlah duplikat cukup besar, sebenarnya lebih cepat untuk mengonversi ke set dan kemudian membuang data kembali ke vektor .
Dan untuk beberapa alasan, melakukan konversi set secara manual tampaknya lebih cepat daripada menggunakan set constructor - setidaknya pada data acak mainan yang saya gunakan.
sumber
Saya redid profiling Nate Kohl dan mendapat hasil yang berbeda. Untuk kasus pengujian saya, secara langsung menyortir vektor selalu lebih efisien daripada menggunakan satu set. Saya menambahkan metode baru yang lebih efisien, menggunakan
unordered_set
.Perlu diingat bahwa
unordered_set
metode ini hanya berfungsi jika Anda memiliki fungsi hash yang baik untuk jenis yang Anda butuhkan unik dan diurutkan. Untuk int, ini mudah! (Perpustakaan standar menyediakan hash default yang hanya fungsi identitas.) Juga, jangan lupa untuk mengurutkan di akhir karena unordered_set adalah, well, unordered :)Saya melakukan beberapa penggalian di dalam
set
danunordered_set
implementasi dan menemukan bahwa konstruktor benar-benar membangun simpul baru untuk setiap elemen, sebelum memeriksa nilainya untuk menentukan apakah itu benar-benar harus dimasukkan (dalam implementasi Visual Studio, setidaknya).Berikut adalah 5 metode:
f1: Hanya menggunakan
vector
,sort
+unique
f2: Konversi ke
set
(menggunakan konstruktor)f3: Konversi ke
set
(secara manual)f4: Konversi ke
unordered_set
(menggunakan konstruktor)f5: Konversi ke
unordered_set
(secara manual)Saya melakukan tes dengan vektor 100.000.000 int yang dipilih secara acak dalam rentang [1,10], [1.1000], dan [1.100000]
Hasilnya (dalam detik, lebih kecil lebih baik):
sumber
sort
atauunique
metode, Anda harus#include <algorithm>
CWUK
scenerio yang memiliki sifat kemungkinan untuk memperlambatemplace
jenis konstruksi.std::unique
hanya menghapus elemen duplikat jika bertetangga: Anda harus mengurutkan vektor terlebih dahulu sebelum berfungsi sesuai keinginan Anda.std::unique
didefinisikan sebagai stabil, sehingga vektor akan tetap diurutkan setelah dijalankan secara unik.sumber
Saya tidak yakin untuk apa Anda menggunakan ini, jadi saya tidak bisa mengatakan ini dengan kepastian 100%, tetapi biasanya ketika saya berpikir wadah "diurutkan, unik", saya memikirkan std :: set . Ini mungkin lebih cocok untuk pengguna kami:
Kalau tidak, menyortir sebelum memanggil unik (seperti jawaban lain tunjukkan) adalah cara untuk pergi.
sumber
std::unique
hanya berfungsi pada menjalankan elemen duplikat berturut-turut, jadi Anda sebaiknya mengurutkannya terlebih dahulu Namun, ini stabil, sehingga vektor Anda akan tetap diurutkan.sumber
Berikut template untuk melakukannya untuk Anda:
sebut saja seperti:
sumber
erase()
metode, kalau tidak Anda harus mengembalikan iterator akhir yang baru dan meminta kode panggilan memotong wadah.Efisiensi adalah konsep yang rumit. Ada pertimbangan waktu vs. ruang, serta pengukuran umum (di mana Anda hanya mendapatkan jawaban yang tidak jelas seperti O (n)) vs. yang spesifik (mis. Gelembung sort bisa jauh lebih cepat daripada quicksort, tergantung pada karakteristik input).
Jika Anda memiliki duplikat yang relatif sedikit, maka pengurutan diikuti oleh unik dan menghapus tampaknya cara untuk pergi. Jika Anda memiliki duplikat yang relatif banyak, membuat satu set dari vektor dan membiarkannya melakukan pengangkatan berat dapat dengan mudah mengalahkannya.
Jangan hanya berkonsentrasi pada efisiensi waktu juga. Sortir + unik + hapus beroperasi di ruang O (1), sedangkan konstruksi yang ditetapkan beroperasi di ruang O (n). Dan tidak ada yang secara langsung cocok untuk paralelisasi pengurangan peta (untuk dataset yang sangat besar ).
sumber
Anda perlu mengurutkannya sebelum menelepon
unique
karenaunique
hanya menghapus duplikat yang bersebelahan.edit: 38 detik ...
sumber
unique
hanya menghapus elemen duplikat berurutan (yang diperlukan untuk menjalankannya dalam waktu linier), jadi Anda harus melakukan pengurutan terlebih dahulu. Itu akan tetap diurutkan setelah panggilan keunique
.sumber
Jika Anda tidak ingin mengubah urutan elemen, maka Anda dapat mencoba solusi ini:
sumber
Dengan asumsi bahwa a adalah vektor, hapus duplikat yang berdekatan menggunakan
a.erase(unique(a.begin(),a.end()),a.end());
berjalan dalam waktu O (n) .sumber
std::sort
pertama.Seperti yang sudah disebutkan,
unique
membutuhkan wadah yang disortir. Selain itu,unique
sebenarnya tidak menghapus elemen dari wadah. Sebagai gantinya, mereka disalin hingga akhir,unique
mengembalikan iterator yang menunjuk ke elemen duplikat pertama seperti itu, dan Anda diharapkan meneleponerase
untuk benar-benar menghapus elemen-elemen tersebut.sumber
Pendekatan standar yang disarankan oleh Nate Kohl, hanya menggunakan vektor, urutkan + unik:
tidak berfungsi untuk vektor pointer.
Perhatikan baik-baik contoh ini di cplusplus.com .
Dalam contoh mereka, "duplikat yang disebut" dipindahkan ke akhir sebenarnya ditampilkan sebagai? (nilai yang tidak ditentukan), karena "duplikat yang disebut" adalah SOMETIM "elemen tambahan" dan SOMETIM ada "elemen yang hilang" yang ada di vektor asli.
Terjadi masalah saat menggunakan
std::unique()
pada vektor pointer ke objek (kebocoran memori, buruk membaca data dari HEAP, duplikat membebaskan, yang menyebabkan kesalahan segmentasi, dll).Inilah solusi saya untuk masalah ini: ganti
std::unique()
denganptgi::unique()
.Lihat file ptgi_unique.hpp di bawah ini:
Dan inilah program UNIT Test yang saya gunakan untuk mengujinya:
sumber
std::unique
[1, 2, 3, 2] Anda tidak dapat memanggil delete pada 2 karena itu akan meninggalkan pointer menggantung ke 2! => Cukup jangan panggil delete pada elemen di antaranewEnd = std::unique
danstd::end
karena Anda masih memiliki petunjuk untuk elemen-elemen ini di dalam[std::begin, newEnd)
!unique
padavector<unique_ptr<T>>
, sebagai satu-satunya digandakan nilai seperti vektor dapat berisi adalahnullptr
.Dengan perpustakaan Ranges (datang dalam C ++ 20), Anda cukup menggunakan
Perhatikan bahwa sebenarnya menghapus elemen duplikat, bukan hanya memindahkannya.
sumber
Tentang tolok ukur alexK7. Saya mencobanya dan mendapatkan hasil yang serupa, tetapi ketika kisaran nilainya 1 juta, case menggunakan std :: sort (f1) dan menggunakan std :: unordered_set (f5) menghasilkan waktu yang sama. Ketika rentang nilai 10 juta f1 lebih cepat dari f5.
Jika rentang nilai terbatas dan nilainya tidak bertanda tangan, dimungkinkan untuk menggunakan std :: vector, ukurannya sesuai dengan rentang yang diberikan. Ini kodenya:
sumber
sortir (v.begin (), v.end ()), v.erase (unik (v.begin (), v, end ()), v.end ());
sumber
Jika Anda mencari kinerja dan penggunaan
std::vector
, saya sarankan yang disediakan oleh tautan dokumentasi ini .sumber
sumber
Jika Anda tidak ingin memodifikasi vektor (hapus, urutkan) maka Anda dapat menggunakan perpustakaan Newton , Di sublibrary algoritma ada panggilan fungsi, copy_single
jadi kamu bisa:
di mana salinan adalah vektor di mana Anda ingin push_back salinan elemen unik. tapi ingat Anda push_back elemen, dan Anda tidak membuat vektor baru
lagi pula, ini lebih cepat karena Anda tidak menghapus () elemen (yang membutuhkan banyak waktu, kecuali ketika Anda pop_back (), karena penugasan kembali)
Saya membuat beberapa percobaan dan lebih cepat.
Anda juga dapat menggunakan:
terkadang masih lebih cepat.
sumber
unique_copy
.Kode lebih mudah dimengerti dari: https://en.cppreference.com/w/cpp/algorithm/unique
ouput:
sumber
sumber
Inilah contoh masalah penghapusan duplikat yang terjadi dengan std :: unique (). Pada mesin LINUX, program macet. Baca komentar untuk detailnya.
sumber
vector
berisi bilangan bulat, bukan pointer, dan tidak menentukan pembanding).Ini adalah fungsi yang saya buat yang dapat Anda gunakan untuk menghapus pengulangan. File header yang dibutuhkan hanya
<iostream>
dan<vector>
.sumber