dequeとlist STLコンテナーの違いは何ですか?


93

2つの違いは何ですか?つまり、方法はすべて同じです。したがって、ユーザーにとっては、それらは同じように機能します。

あれは正しいですか??


1
反復のパフォーマンスに興味があります。最初から最後まで反復する方が速いのは何ですか。
nkint

回答:


60

の(時代遅れだがまだ非常に有用な)SGI STLの要約からdeque

両端キューはベクトルに非常に似ています。ベクトルと同様に、要素へのランダムアクセス、シーケンスの最後での要素の一定時間の挿入と削除、および途中での要素の線形時間の挿入と削除をサポートするシーケンスです。

dequeがvectorと異なる主な方法は、dequeがシーケンスの先頭での要素の一定時間の挿入と削除もサポートすることです。さらに、dequeには、vectorのcapacity()およびreserve()に類似したメンバー関数がなく、これらのメンバー関数に関連付けられているイテレーターの有効性に関する保証はありません。

これlistは同じサイトからの要約です:

リストは二重にリンクされたリストです。つまり、順方向と逆方向の両方のトラバーサル、および要素の最初または最後、または途中での(償却された)一定時間の挿入と削除をサポートするシーケンスです。リストには、挿入とスプライシングによって要素をリストするためのイテレーターが無効にならないという重要な特性があり、削除によっても、削除される要素を指すイテレーターのみが無効になるという特性があります。イテレータの順序は変更される可能性があります(つまり、list :: iteratorは、リスト操作の前と後の結果が以前とは異なる場合があります)。または突然変異は明示的です。

要約すると、コンテナはルーチンを共有している可能性がありますが、これらのルーチンの時間保証はコンテナごとに異なります。これは、これらのコンテナーのどれをタスクに使用するかを検討する場合に非常に重要です。コンテナーが最も頻繁に使用される方法(たとえば、挿入/削除よりも検索に使用)を考慮すると、正しいコンテナーに移動するのに役立ちます。


2
std ::リストはまた、あなたが一緒に二つのリストをマージすることができます「スプライス」メソッドがある
リック

23
実際、時間保証はリストの2番目に重要な機能です。リストの最も重要な機能は、要素を追加および削除でき、イテレータを無効にできないことです。(ほぼ?)他のすべてのSTLコンテナーでは、すべての編集操作ですべてのイテレーターが無効になります。「一致するアイテムを削除する」には、1つの操作で一致するアイテムを蓄積し、別の操作でそれらを削除する必要があります。リスト内では、リストをウォークスルーし、必要に応じて削除および追加できます。イテレータを再計算する必要はありません。
Tom Swirly、2012

1
これらは抽象的な違いでもあるので、ケースの現実を測定してください!リストと両端キューの両方にO(1)挿入/削除がありますが、k * O(1)を意味し、kにはリストと両端キューの値が異なることを忘れないでください。私の場合、リストがnew / deleteへの呼び出しを増やす必要があるため、オブジェクトをリストに追加するのにdequeの10倍の時間がかかりました。これは明らかに、使用しているSTL実装によって異なります。
Andy Krouwel 2017年

125

違いをリストアップしましょう:

  • 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
Lazer、2010

16
長期の操作は、説明どおりに動作します。ただし、1つの操作に指定より時間がかかる場合があります。例:現在の容量が10でサイズがすでに9であるベクトルに要素を挿入します。容量が10でサイズも10の場合、時間は線形であるため、すべての要素を新しいメモリに割り当ててコピーする必要があるためです。 。
aJ。

5
@aJ:dequeはどのようにランダムアクセスを提供しますか?また、この構造はどのように実装されていますか?

9

std::list 基本的には二重にリンクされたリストです。

std::deque一方、はのように実装されますstd::vector。インデックスごとに一定のアクセス時間があり、最初と最後に挿入と削除があるため、リストとは劇的に異なるパフォーマンス特性を提供します。


5

もう1つの重要な保証は、各コンテナーがデータをメモリに保存する方法です。

  • ベクトルは、単一の連続したメモリブロックです。
  • 両端キューは、リンクされたメモリブロックのセットであり、複数の要素が各メモリブロックに格納されます。
  • リストは、メモリに分散された要素のセットです。つまり、メモリ「ブロック」ごとに1つの要素のみが保存されます。

両端キューは、ベクターとリストの両方の利点をそれぞれの欠点なしバランスさせるように設計されていることに注意してください。これは、マイクロコントローラなど、メモリが限られたプラットフォームで特に興味深いコンテナです。

メモリストレージ戦略は見過ごされがちですが、特定のアプリケーションに最適なコンテナを選択することは、多くの場合最も重要な理由の1つです。


4

いいえ。両端キューはO(1)の挿入と削除のみを前後でサポートします。たとえば、ラップアラウンドでベクターに実装できます。また、O(1)ランダムアクセスも保証されているため、二重リンクリストを(ただ)使用していないことを確認できます。


2

パフォーマンスの違いは他の人からよく説明されています。私は、オブジェクト指向プログラミングではオブジェクト指向プログラミングでよく似た、またはまったく同じインターフェースが一般的であることを付け加えたかっただけです。オブジェクト指向ソフトウェアを作成する一般的な方法論の一部です。2つのクラスが同じインターフェースを実装しているからといって、2つのクラスが同じように機能すると想定する必要はありません。


1

以下は、O(1)ルックアップとO(1)の正確なLRUメンテナンスを提供するリストの無秩序なマップの概念実証コードの使用です。消去操作を生き残るためには、(消去されていない)イテレータが必要です。GPUメモリ上のCPUポインター用のO(1)の任意の大きなソフトウェア管理キャッシュでの使用を計画します。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;
} 

これを正しい場所に投稿しましたか?これは質問の答えにはなりません。
高炉

1

との顕著な違いの間dequelist

  • の場合deque

    並べて保管されるアイテム。

    2つの側面(前面、背面)からデータを追加するために最適化されています。

    数値でインデックス付けされた要素(整数)。

    イテレータや要素のインデックスでさえ参照できます。

    データへの時間アクセスが高速です。

  • ために list

    メモリに「ランダムに」格納されたアイテム。

    イテレータのみが閲覧できます。

    真ん中の挿入と削除に最適化されています。

    空間的な局所性が非常に低いため、データへの時間アクセスは遅く、反復に時間がかかります。

    非常に大きな要素を処理します

2つのSTLコンテナー間のパフォーマンスを比較する次のLinkも確認できます(std :: vectorを使用)

私はいくつかの有用な情報を共有したいと思います。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.