saya tertarik dengan kinerja iterasi .. apa yang lebih cepat untuk beralih dari awal hingga akhir?
nkint
Jawaban:
62
Dari ringkasan SGI STL (tertanggal tapi masih sangat berguna) deque:
Deque sangat mirip dengan vektor: seperti vektor, ini adalah urutan yang mendukung akses acak ke elemen, penyisipan waktu konstan dan penghapusan elemen di akhir urutan, dan penyisipan waktu linier dan penghapusan elemen di tengah.
Perbedaan utama deque dengan vektor adalah deque juga mendukung penyisipan waktu yang konstan dan pemindahan elemen pada awal urutan. Selain itu, deque tidak memiliki fungsi anggota yang serupa dengan kapasitas vektor () dan cadangan (), dan tidak memberikan jaminan apa pun pada validitas iterator yang terkait dengan fungsi anggota tersebut.
Daftar adalah daftar tertaut ganda. Artinya, ini adalah Urutan yang mendukung traversal maju dan mundur, dan penyisipan waktu konstan (diamortisasi) dan pemindahan elemen di awal atau akhir, atau di tengah. List memiliki properti penting sehingga penyisipan dan penyambungan tidak membatalkan iterator ke elemen daftar, dan bahkan penghapusan hanya membatalkan iterator yang mengarah ke elemen yang dihapus. Urutan iterator dapat diubah (yaitu, list :: iterator mungkin memiliki pendahulu atau penerus yang berbeda setelah operasi daftar daripada sebelumnya), tetapi iterator itu sendiri tidak akan dibatalkan atau dibuat untuk menunjuk ke elemen yang berbeda kecuali pembatalan itu atau mutasi eksplisit.
Singkatnya, kontainer mungkin memiliki rutinitas bersama tetapi jaminan waktu untuk rutinitas tersebut berbeda dari satu kontainer ke kontainer lainnya . Ini sangat penting ketika mempertimbangkan wadah mana yang akan digunakan untuk suatu tugas: dengan mempertimbangkan bagaimana wadah akan paling sering digunakan (misalnya, lebih banyak untuk mencari daripada untuk penyisipan / penghapusan) sangat membantu dalam mengarahkan Anda ke wadah yang tepat .
std :: list juga memiliki metode 'splice' yang memungkinkan Anda menggabungkan dua daftar bersama
Rick
25
Sebenarnya, jaminan waktu adalah fitur terpenting kedua dari daftar. The paling fitur penting dari daftar adalah bahwa Anda dapat menambah dan menghapus elemen dan tidak membatalkan iterator Anda! Di (hampir?) Setiap penampung STL lainnya, setiap operasi pengeditan membatalkan semua iterator Anda - jadi untuk "menghapus item yang cocok" Anda perlu mengakumulasi item yang cocok dalam satu operasi, dan kemudian menghapusnya di operasi lain. Dalam daftar, Anda dapat menjelajahinya, menghapus dan menambahkan sesuai keinginan, dan tidak perlu menghitung ulang iterator.
Tom Swirly
2
Ini juga merupakan perbedaan abstrak, jadi ukur realitas untuk kasus Anda! Baik list dan deque memiliki penyisipan / penghapusan O (1), tetapi jangan lupa bahwa k * O (1), dan k memiliki nilai yang berbeda untuk list dan deque. Dalam kasus saya, butuh sepuluh kali lebih lama untuk menambahkan objek ke daftar daripada deque karena daftar membutuhkan lebih banyak panggilan ke baru / hapus. Itu jelas akan bervariasi berdasarkan implementasi STL yang Anda miliki.
Andy Krouwel
130
Izinkan saya mencatat perbedaannya:
Deque mengelola elemennya dengan
larik dinamis , menyediakan akses acak , dan memiliki antarmuka yang hampir sama dengan vektor.
List mengelola elemennya sebagai
daftar tertaut ganda dan tidak menyediakan akses acak .
Deque menyediakan penyisipan dan penghapusan Cepat di akhir dan awal. Memasukkan dan menghapus elemen di tengah relatif lambat karena semua elemen hingga salah satu ujungnya dapat dipindahkan untuk memberi ruang atau untuk mengisi celah.
Dalam Daftar , memasukkan dan menghapus elemen dengan cepat di setiap posisi, termasuk kedua ujungnya.
Deque : Setiap penyisipan atau penghapusan elemen selain di awal atau akhir membatalkan semua pointer, referensi, dan iterator yang merujuk ke elemen deque.
Daftar : Memasukkan dan menghapus elemen tidak membatalkan pointer, referensi, dan iterator ke elemen lain.
Kompleksitas
Insert/erase at the beginningin middle at the end
Deque: Amortized constant Linear Amortized constantList: ConstantConstantConstant
@aJ: Apa perbedaan antara constantdan amortized constant?
Lazer
17
Operasi dalam jangka panjang berperilaku seperti yang dijelaskan. Namun, satu operasi mungkin membutuhkan waktu lebih lama dari yang ditentukan. contoh: untuk memasukkan elemen ke dalam vektor yang kapasitas saat ini 10 dan ukuran sudah 9 konstan, sedangkan waktu linier jika kapasitas 10 dan ukuran juga 10. Ini karena harus mengalokasikan dan menyalin semua elemen ke memori baru .
aJ.
5
@aJ: Bagaimana deque menyediakan akses acak? Juga bagaimana struktur ini diterapkan?
9
std::list pada dasarnya adalah daftar tertaut ganda.
std::deque, di sisi lain, diimplementasikan lebih seperti std::vector. Ini memiliki waktu akses konstan menurut indeks, serta penyisipan dan penghapusan di awal dan akhir, yang memberikan karakteristik kinerja yang sangat berbeda dari daftar.
Jaminan penting lainnya adalah cara setiap container berbeda menyimpan datanya di memori:
Vektor adalah satu blok memori yang berdekatan.
Deque adalah sekumpulan blok memori terkait, di mana lebih dari satu elemen disimpan di setiap blok memori.
Daftar adalah sekumpulan elemen yang tersebar di memori, yaitu: hanya satu elemen yang disimpan per "blok" memori.
Perhatikan bahwa deque dirancang untuk mencoba menyeimbangkan keunggulan vektor dan list tanpa kekurangannya masing-masing. Ini adalah wadah yang sangat menarik dalam platform terbatas memori, misalnya, mikrokontroler.
Strategi penyimpanan memori sering diabaikan, namun, ini sering menjadi salah satu alasan terpenting untuk memilih wadah yang paling sesuai untuk aplikasi tertentu.
Tidak. Deque hanya mendukung penyisipan dan penghapusan O (1) di bagian depan dan belakang. Ini dapat, misalnya, diimplementasikan dalam vektor dengan pembungkus. Karena itu juga menjamin O (1) akses acak, Anda dapat yakin itu tidak menggunakan (hanya) daftar tertaut ganda.
Perbedaan kinerja telah dijelaskan dengan baik oleh orang lain. Saya hanya ingin menambahkan bahwa antarmuka yang mirip atau bahkan identik adalah umum dalam pemrograman berorientasi objek - bagian dari metodologi umum penulisan perangkat lunak berorientasi objek. Anda TIDAK boleh berasumsi bahwa dua kelas bekerja dengan cara yang sama hanya karena keduanya mengimplementasikan antarmuka yang sama, lebih dari yang seharusnya Anda asumsikan bahwa kuda bekerja seperti anjing karena keduanya menerapkan attack () dan make_noise ().
Berikut adalah kode bukti konsep penggunaan daftar, peta tak berurutan yang memberikan O (1) pencarian dan O (1) pemeliharaan LRU yang tepat. Membutuhkan iterator (tidak terhapus) untuk bertahan dari operasi penghapusan. Rencanakan untuk menggunakan cache yang dikelola perangkat lunak besar dan sewenang-wenang dalam O (1) untuk penunjuk CPU pada memori GPU. Mengangguk ke Linux O (1) scheduler (LRU <-> run antrian per prosesor). Unordered_map memiliki akses waktu konstan melalui tabel hash.
#include<iostream> #include<list> #include<unordered_map> usingnamespacestd;
structMapEntry {list<uint64_t>::iterator LRU_entry;
uint64_t CpuPtr;
};
typedefunordered_map<uint64_t,MapEntry> Table;
typedeflist<uint64_t> FIFO;
FIFO LRU; // LRU list at a given priority
Table DeviceBuffer; // Table of device buffersvoidPrint(void){
for (FIFO::iterator l = LRU.begin(); l != LRU.end(); l++) {
std::cout<< "LRU entry "<< *l << " : " ;
std::cout<< "Buffer entry "<< DeviceBuffer[*l].CpuPtr <<endl;
}
}
intmain(){
LRU.push_back(0);
LRU.push_back(1);
LRU.push_back(2);
LRU.push_back(3);
LRU.push_back(4);
for (FIFO::iterator i = LRU.begin(); i != LRU.end(); i++) {
MapEntry ME = { i, *i};
DeviceBuffer[*i] = ME;
}
std::cout<< "************ Initial set of CpuPtrs" <<endl;
Print();
{
// Suppose evict an entry - find it via "key - memory address uin64_t" and remove from // cache "tag" table AND LRU list with O(1) operationsuint64_t key=2;
LRU.erase(DeviceBuffer[2].LRU_entry);
DeviceBuffer.erase(2);
}
std::cout<< "************ Remove item 2 " <<endl;
Print();
{
// Insert a new allocation in both tag table, and LRU ordering wiith O(1) operationsuint64_t key=9;
LRU.push_front(key);
MapEntry ME = { LRU.begin(), key };
DeviceBuffer[key]=ME;
}
std::cout<< "************ Add item 9 " <<endl;
Print();
std::cout << "Victim "<<LRU.back()<<endl;
}
Jawaban:
Dari ringkasan SGI STL (tertanggal tapi masih sangat berguna)
deque
:Berikut ringkasan
list
dari situs yang sama:Singkatnya, kontainer mungkin memiliki rutinitas bersama tetapi jaminan waktu untuk rutinitas tersebut berbeda dari satu kontainer ke kontainer lainnya . Ini sangat penting ketika mempertimbangkan wadah mana yang akan digunakan untuk suatu tugas: dengan mempertimbangkan bagaimana wadah akan paling sering digunakan (misalnya, lebih banyak untuk mencari daripada untuk penyisipan / penghapusan) sangat membantu dalam mengarahkan Anda ke wadah yang tepat .
sumber
Izinkan saya mencatat perbedaannya:
Kompleksitas
Insert/erase at the beginning in middle at the end Deque: Amortized constant Linear Amortized constant List: Constant Constant Constant
sumber
constant
danamortized constant
?std::list
pada dasarnya adalah daftar tertaut ganda.std::deque
, di sisi lain, diimplementasikan lebih sepertistd::vector
. Ini memiliki waktu akses konstan menurut indeks, serta penyisipan dan penghapusan di awal dan akhir, yang memberikan karakteristik kinerja yang sangat berbeda dari daftar.sumber
Jaminan penting lainnya adalah cara setiap container berbeda menyimpan datanya di memori:
Perhatikan bahwa deque dirancang untuk mencoba menyeimbangkan keunggulan vektor dan list tanpa kekurangannya masing-masing. Ini adalah wadah yang sangat menarik dalam platform terbatas memori, misalnya, mikrokontroler.
Strategi penyimpanan memori sering diabaikan, namun, ini sering menjadi salah satu alasan terpenting untuk memilih wadah yang paling sesuai untuk aplikasi tertentu.
sumber
Tidak. Deque hanya mendukung penyisipan dan penghapusan O (1) di bagian depan dan belakang. Ini dapat, misalnya, diimplementasikan dalam vektor dengan pembungkus. Karena itu juga menjamin O (1) akses acak, Anda dapat yakin itu tidak menggunakan (hanya) daftar tertaut ganda.
sumber
Perbedaan kinerja telah dijelaskan dengan baik oleh orang lain. Saya hanya ingin menambahkan bahwa antarmuka yang mirip atau bahkan identik adalah umum dalam pemrograman berorientasi objek - bagian dari metodologi umum penulisan perangkat lunak berorientasi objek. Anda TIDAK boleh berasumsi bahwa dua kelas bekerja dengan cara yang sama hanya karena keduanya mengimplementasikan antarmuka yang sama, lebih dari yang seharusnya Anda asumsikan bahwa kuda bekerja seperti anjing karena keduanya menerapkan attack () dan make_noise ().
sumber
Berikut adalah kode bukti konsep penggunaan daftar, peta tak berurutan yang memberikan O (1) pencarian dan O (1) pemeliharaan LRU yang tepat. Membutuhkan iterator (tidak terhapus) untuk bertahan dari operasi penghapusan. Rencanakan untuk menggunakan cache yang dikelola perangkat lunak besar dan sewenang-wenang dalam O (1) untuk penunjuk CPU pada memori GPU. Mengangguk ke Linux O (1) scheduler (LRU <-> run antrian per prosesor). Unordered_map memiliki akses waktu konstan melalui tabel hash.
#include <iostream> #include <list> #include <unordered_map> using namespace std; struct MapEntry { list<uint64_t>::iterator LRU_entry; uint64_t CpuPtr; }; typedef unordered_map<uint64_t,MapEntry> Table; typedef list<uint64_t> FIFO; FIFO LRU; // LRU list at a given priority Table DeviceBuffer; // Table of device buffers void Print(void){ for (FIFO::iterator l = LRU.begin(); l != LRU.end(); l++) { std::cout<< "LRU entry "<< *l << " : " ; std::cout<< "Buffer entry "<< DeviceBuffer[*l].CpuPtr <<endl; } } int main() { LRU.push_back(0); LRU.push_back(1); LRU.push_back(2); LRU.push_back(3); LRU.push_back(4); for (FIFO::iterator i = LRU.begin(); i != LRU.end(); i++) { MapEntry ME = { i, *i}; DeviceBuffer[*i] = ME; } std::cout<< "************ Initial set of CpuPtrs" <<endl; Print(); { // Suppose evict an entry - find it via "key - memory address uin64_t" and remove from // cache "tag" table AND LRU list with O(1) operations uint64_t key=2; LRU.erase(DeviceBuffer[2].LRU_entry); DeviceBuffer.erase(2); } std::cout<< "************ Remove item 2 " <<endl; Print(); { // Insert a new allocation in both tag table, and LRU ordering wiith O(1) operations uint64_t key=9; LRU.push_front(key); MapEntry ME = { LRU.begin(), key }; DeviceBuffer[key]=ME; } std::cout<< "************ Add item 9 " <<endl; Print(); std::cout << "Victim "<<LRU.back()<<endl; }
sumber
Di antara perbedaan mencolok antara
deque
danlist
Untuk
deque
:Item yang disimpan berdampingan;
Dioptimalkan untuk menambahkan data dari dua sisi (depan, belakang);
Elemen diindeks dengan angka (bilangan bulat).
Dapat dijelajahi oleh iterator dan bahkan oleh indeks elemen.
Akses waktu ke data lebih cepat.
Untuk
list
Item yang disimpan "secara acak" di memori;
Hanya dapat diakses oleh iterator;
Dioptimalkan untuk penyisipan dan penghapusan di tengah.
Akses waktu ke data lebih lambat, lambat untuk mengulang, karena lokasi spasial yang sangat buruk.
Menangani elemen besar dengan sangat baik
Anda juga dapat memeriksa Tautan berikut , yang membandingkan kinerja antara dua kontainer STL (dengan std :: vector)
Semoga saya membagikan beberapa informasi berguna.
sumber