У чому різниця між контейнерами STE та deque та списком?


93

Яка різниця між ними? Я маю на увазі, що методи однакові. Отже, для користувача вони працюють однаково.

Це правильно??


1
Мене цікавить ітерація продуктивності .. що швидше ієратувати від початку до кінця?
nkint

Відповіді:


60

З (датованого, але все ще дуже корисного) SGI STL резюме deque:

Деке дуже нагадує вектор: як вектор, це послідовність, яка підтримує довільний доступ до елементів, постійне введення і видалення елементів в кінці послідовності та лінійне введення та видалення елементів посередині.

Основний спосіб, в якому деке відрізняється від вектора, полягає в тому, що деке також підтримує постійне введення та видалення елементів на початку послідовності. Крім того, deque не має жодних функцій-членів, аналогічних векторній ємності () та резерву (), і не надає жодних гарантій щодо ітераторної дійсності, які пов'язані з цими функціями-членами.

Ось короткий зміст listіз того самого сайту:

Список - це подвійно пов’язаний список. Тобто це Послідовність, яка підтримує як обхід вперед, так і назад, а також (амортизовану) постійну вставку та видалення елементів на початку або в кінці, або в середині. Списки мають важливу властивість, що вставка та зрощування не роблять ітератори недійсними для списку елементів, і що навіть видалення робить недійсними лише ітератори, які вказують на елементи, які видаляються. Впорядкування ітераторів може бути змінено (тобто list :: iterator може мати іншого попередника або наступника після операції зі списком, ніж це було раніше), але самі ітератори не будуть визнані недійсними або змушені вказувати на різні елементи, якщо це недійсне або мутація явна.

Таким чином, контейнери можуть мати спільні процедури, але часові гарантії цих процедур відрізняються від контейнера до контейнера . Це дуже важливо при розгляді того, який із цих контейнерів використовувати для завдання: врахування того, як контейнер буде найчастіше використовуватися (наприклад, більше для пошуку, ніж для вставки / видалення), проходить довгий шлях, спрямовуючи вас до потрібного контейнера .


2
std :: list також має метод сплайсингу, який дозволяє об'єднати два списки разом
Рік

23
Власне, часові гарантії - це друга найважливіша особливість переліку. Найбільш важливою особливістю списку є те , що ви можете додавати і видаляти елементи , а не анулює ваші ітератори! У (майже?) Кожному іншому контейнері STL кожна операція редагування робить недійсними всі ваші ітератори - тому для "видалення відповідних елементів" потрібно накопичити відповідні елементи в одній операції, а потім видалити їх в іншій. У списку ви можете пройтись по ньому, видалити та додати як завгодно, і ніколи не потрібно перераховувати ітератор.
Том Свірі

1
Це також абстрактні відмінності, тож вимірюйте реальність для свого випадку! І list, і deque мають O (1) вставку / видалення, але не забувайте, що означає k * O (1), і k має різні значення для list і deque. У моєму випадку додавання об’єкта до списку, ніж деко, зайняло в десять разів більше, оскільки для списку було потрібно більше викликів для нового / видалення. Це, очевидно, буде залежати від того, яка у вас реалізація STL.
Енді Крувель,

125

Дозвольте мені перерахувати відмінності:

  • Deque управляє своїми елементами за допомогою динамічного масиву , забезпечує довільний доступ і має майже такий самий інтерфейс, як вектор.
  • Список керує своїми елементами як подвійно зв’язаний список і не забезпечує довільний доступ .

  • Deque забезпечує швидкі вставки та видалення як в кінці, так і на початку. Вставка та видалення елементів посередині відбувається відносно повільно, оскільки всі елементи до будь-якого з обох кінців можуть бути переміщені, щоб звільнити місце або заповнити прогалину.
  • У списку швидке вставлення та видалення елементів у кожному положенні, включаючи обидва кінці.

  • Deque : Будь-яке вставлення або видалення елементів, крім початку або кінця, робить недійсними всі вказівники, посилання та ітератори, що посилаються на елементи deque.
  • Список : вставка та видалення елементів не робить покажчики, посилання та ітератори інших елементів недійсними.

Складність

             Insert/erase at the beginning       in middle        at the end

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

5
@aJ: У чому різниця між constantі amortized constant?
Лазер

16
Операції в довгостроковій перспективі поводяться так, як описано. Однак одна операція може зайняти більше часу, ніж зазначено. приклад: вставити елемент у вектор, поточна ємність якого 10, а розмір вже 9 є постійним, де, оскільки час є лінійним, якщо ємність дорівнює 10, а розмір також дорівнює 10. Це тому, що він повинен розподілити та скопіювати всі елементи в нову пам’ять .
aJ.

5
@aJ: Як deque забезпечує довільний доступ? Також як реалізується ця структура?

9

std::list це в основному подвійно пов’язаний список.

std::deque, з іншого боку, реалізується більше подібним std::vector. Він має постійний час доступу за індексом, а також вставку та видалення на початку та в кінці, що забезпечує суттєво інші характеристики продуктивності, ніж список.


5

Ще однією важливою гарантією є спосіб, як кожен різний контейнер зберігає свої дані в пам'яті:

  • Вектор - це один суміжний блок пам'яті.
  • Deque - це набір пов'язаних блоків пам'яті, де в кожному блоці пам'яті зберігається більше одного елемента.
  • Список - це сукупність елементів, розподілених у пам’яті, тобто: в одному блоці пам’яті зберігається лише один елемент.

Зверніть увагу, що деке розроблено, щоб спробувати збалансувати переваги як вектора, так і списку без відповідних недоліків. Це спеціально цікавий контейнер на платформах з обмеженою пам’яттю, наприклад, мікроконтролерів.

Стратегію зберігання пам’яті часто ігнорують, однак вона часто є однією з найважливіших причин, щоб вибрати найбільш підходящий контейнер для певного додатка.


4

Ні. Декаль підтримує введення та видалення O (1) спереду та ззаду. Наприклад, це може бути реалізовано у векторі з обгортанням. Оскільки він також гарантує O (1) випадковий доступ, ви можете бути впевнені, що він не використовує (просто) подвійно пов'язаний список.


2

Інші відмінно пояснювали відмінності у продуктивності. Я просто хотів додати, що подібні або навіть однакові інтерфейси є загальними в об'єктно-орієнтованому програмуванні - частині загальної методології написання об'єктно-орієнтованого програмного забезпечення. Ви НІ В ЯКОМ разі не повинні припускати, що два класи працюють однаково просто тому, що вони реалізують один і той же інтерфейс, не більше, ніж ви повинні вважати, що кінь працює як собака, оскільки вони обидва реалізують атаку () і make_noise ().


1

Ось доказове використання коду списку, невпорядкована карта, яка надає пошук O (1) та O (1) точне обслуговування LRU. Потрібні (не стираються) ітератори, щоб пережити операції стирання. Плануйте використовувати в O (1) довільно великий керований програмним кешем для вказівників процесора на пам'ять GPU. Киває до планувальника Linux O (1) (LRU <-> черга запуску на процесор). Unordered_map має постійний доступ за часом через хеш-таблицю.

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

Ви розмістили це в потрібному місці? Це не відповідає на питання.
Blastfurnace

1

Серед видатних відмінностей між dequeіlist

  • Для deque:

    Предмети, що зберігаються поруч;

    Оптимізовано для додавання даних з двох сторін (спереду, ззаду);

    Елементи, що індексуються числами (цілими числами).

    Може переглядатися за допомогою ітераторів і навіть за індексом елемента.

    Часовий доступ до даних швидший.

  • Для list

    Предмети, що зберігаються "випадковим чином" у пам'яті;

    Може переглядатися лише ітераторами;

    Оптимізовано для вставки та вилучення посередині.

    Доступ до даних у часі повільніший, повільний ітерація, через дуже погану просторову локалізацію.

    Дуже добре обробляє великі елементи

Ви також можете перевірити таке посилання , яке порівнює продуктивність між двома контейнерами STL (зі std :: vector)

Сподіваюся, я поділився деякою корисною інформацією.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.