Apa perbedaan antara container deque dan list STL?


98

Apa perbedaan diantara keduanya? Maksud saya semua metodenya sama. Jadi, bagi pengguna, mereka bekerja secara identik.

Apakah itu benar??


1
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.

Berikut ringkasan listdari situs yang sama:

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 .


2
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 beginning       in middle        at the end

Deque:       Amortized constant                  Linear           Amortized constant
List:        Constant                            Constant         Constant

6
@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.


5

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.


4

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.


2

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 ().


1

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;
} 

Apakah Anda memposting ini di tempat yang benar? Ini tidak menjawab pertanyaan itu.
Blastfurnace

1

Di antara perbedaan mencolok antara dequedanlist

  • 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.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.