deque 和 list STL 容器有什么区别?

发布于 2024-08-05 12:43:42 字数 66 浏览 5 评论 0原文

两者有什么区别?我的意思是方法都是一样的。因此,对于用户来说,它们的工作原理是相同的。

这是正确的吗?

What is the difference between the two? I mean the methods are all the same. So, for a user, they work identically.

Is that correct??

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

匿名。 2024-08-12 12:43:42

让我列出差异:

  • Deque 使用
    动态数组,提供随机
    访问
    ,并且几乎相同
    接口作为向量。
  • List 将其元素作为
    双向链表并且不
    提供随机访问

  • Deque 提供快速插入和删除
    结束和开始。插入和删除元素
    中间相对较慢,因为
    直到两者之一的所有元素
    末端可以移动以腾出空间或
    填补一个空白。
  • List中,在每个位置插入和删除元素都很快,
    包括两端。

  • Deque:任何元素的插入或删除
    除了开头或结尾之外
    使所有指针、引用无效,
    和引用元素的迭代器
    双端队列的。
  • 列表:插入和删除元素
    不使指针、引用无效,
    以及其他元素的迭代器。

复杂性

             Insert/erase at the beginning       in middle        at the end

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

Let me list down the differences:

  • Deque manages its elements with a
    dynamic array, provides random
    access
    , and has almost the same
    interface as a vector.
  • List manages its elements as a
    doubly linked list and does not
    provide random access.

  • Deque provides Fast insertions and deletions at
    both the end and the beginning. Inserting and deleting elements in
    the middle is relatively slow because
    all elements up to either of both
    ends may be moved to make room or to
    fill a gap.
  • In List, inserting and removing elements is fast at each position,
    including both ends.

  • Deque: Any insertion or deletion of elements
    other than at the beginning or end
    invalidates all pointers, references,
    and iterators that refer to elements
    of the deque.
  • List: Inserting and deleting elements does
    not invalidate pointers, references,
    and iterators to other elements.

Complexity

             Insert/erase at the beginning       in middle        at the end

Deque:       Amortized constant                  Linear           Amortized constant
List:        Constant                            Constant         Constant
垂暮老矣 2024-08-12 12:43:42

来自(已过时但仍然非常有用)SGI STL 双端队列

双端队列非常像向量:与向量一样,它是一个序列,支持随机访问元素、在序列末尾以恒定时间插入和移除元素,以及在序列中线性时间插入和移除元素。中。

双端队列与向量的主要区别在于双端队列还支持在序列开头恒定时间插入和删除元素。此外,deque 没有任何类似于向量的capacity() 和reserve() 的成员函数,并且不提供与这些成员函数关联的迭代器有效性的任何保证。

以下是来自同一站点的 list 的摘要:

列表是双向链表。也就是说,它是一个既支持向前和向后遍历,又支持在开头或结尾或中间(摊销)恒定时间插入和删除元素的 Sequence。列表具有一个重要的属性,即插入和拼接不会使列表元素的迭代器无效,并且即使删除也只会使指向被删除元素的迭代器无效。迭代器的顺序可能会更改(即,list::iterator 在列表操作之后可能具有与之前不同的前驱或后继),但迭代器本身不会失效或指向不同的元素,除非该失效或者突变是明确的。

总之,容器可能具有共享例程,但是这些例程的时间保证因容器而异。当考虑使用这些容器中的哪一个来执行任务时,这一点非常重要:考虑容器的最频繁使用方式(例如,更多地用于搜索而不是插入/删除)会大有帮助引导您到正确的容器。

From the (dated but still very useful) SGI STL summary of deque:

A deque is very much like a vector: like vector, it is a sequence that supports random access to elements, constant time insertion and removal of elements at the end of the sequence, and linear time insertion and removal of elements in the middle.

The main way in which deque differs from vector is that deque also supports constant time insertion and removal of elements at the beginning of the sequence. Additionally, deque does not have any member functions analogous to vector's capacity() and reserve(), and does not provide any of the guarantees on iterator validity that are associated with those member functions.

Here's the summary on list from the same site:

A list is a doubly linked list. That is, it is a Sequence that supports both forward and backward traversal, and (amortized) constant time insertion and removal of elements at the beginning or the end, or in the middle. Lists have the important property that insertion and splicing do not invalidate iterators to list elements, and that even removal invalidates only the iterators that point to the elements that are removed. The ordering of iterators may be changed (that is, list::iterator might have a different predecessor or successor after a list operation than it did before), but the iterators themselves will not be invalidated or made to point to different elements unless that invalidation or mutation is explicit.

In summary the containers may have shared routines but the time guarantees for those routines differ from container to container. This is very important when considering which of these containers to use for a task: taking into account how the container will be most frequently used (e.g., more for searching than for insertion/deletion) goes a long way in directing you to the right container.

月竹挽风 2024-08-12 12:43:42

std::list 基本上是一个双向链表。

std::deque,另一方面,其实现更像是 std::vector。它通过索引具有恒定的访问时间,以及在开头和结尾处的插入和删除,这提供了与列表截然不同的性能特征。

std::list is basically a doubly linked list.

std::deque, on the other hand, is implemented more like std::vector. It has constant access time by index, as well as insertion and removal at the beginning and end, which provides dramatically different performance characteristics than a list.

一笑百媚生 2024-08-12 12:43:42

我为 C++ 课上的学生制作了插图。
这是(松散地)基于(我的理解)GCC STL 实现中的实现(
https://github .com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/include/bits/stl_deque.hhttps://github.com/gcc-mirror/gcc/blob/master/libstdc%2B% 2B-v3/include/bits/stl_list.h)

双端队列

std::deque 的说明

集合中的元素存储在内存块中。每个块的元素数量取决于元素的大小:元素越大,每个块越少。潜在的希望是,如果无论元素的类型如何,块的大小都相似,那么大多数时候这应该对分配器有帮助。

您有一个列出内存块的数组(在 GCC 实现中称为映射)。所有内存块都已满,除了第一个内存块(可能在开头有空间)和最后一个内存块(可能在末尾有空间)。地图本身是从中心向外填充的。与 std::vector 相反,这就是可以在恒定时间内完成两端插入的方式。与 std:::vector 类似,可以在恒定时间内进行随机访问,但需要两个间接而不是一个。与 std::vector 类似,与 std::list 相反,在中间删除或插入元素的成本很高,因为您必须重新组织大部分数据结构。

双向链表

std::list 的图示

双向链表可能更为常见。每个元素都存储在自己的内存块中,独立于其他元素进行分配。在每个块中,您都有元素的值和两个指针:一个指向前一个元素,一个指向下一个元素。它使得在列表中的任何位置插入元素变得非常容易,甚至将元素的子链从一个列表移动到另一个列表(称为拼接的操作):您只需更新以下位置的指针:插入点的开头和结尾。缺点是要通过索引找到一个元素,您必须遍历指针链,因此随机访问在列表中的元素数量方面具有线性成本。

I've made illustrations for the students in my C++ class.
This is based (loosely) on (my understanding of) the implementation in the GCC STL implementation (
https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/include/bits/stl_deque.h and https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/include/bits/stl_list.h)

Double-ended queue

Illustration of std::deque

Elements in the collection are stored in memory blocks. The number of elements per block depends on the size of the element: the bigger the elements, the fewer per block. The underlying hope is that if the blocks are of a similar size no matter the type of the elements, that should help the allocator most of the time.

You have an array (called a map in the GCC implementation) listing the memory blocks. All memory blocks are full except the first one which may have room at the beginning and the last which may have room at the end. The map itself is filled from the center outwards. This is how, contrarily to a std::vector, insertion at both ends can be done in constant time. Similarly to a std:::vector, random access is possible in constant time, but requires two indirections instead of one. Similarly to std::vector and contrarily to std::list, removing or inserting elements in the middle is costly because you have to reorganize a large part of the datastructure.

Doubly-linked list

Illustration of a std::list

Doubly-linked list are perhaps more usual. Every element is stored in its own memory block, allocated independently from the other elements. In each block, you have the element's value and two pointers: one to the previous element, one to the next element. It makes it very easy to insert element at any position in the list, or even to move a subchain of elements from one list to another (an operation called splicing): you just have to update the pointers at the beginning and end of the insertion point. The downside is that to find one element by its index, you have to walk the chain of pointers, so random access has a linear cost in the numbers of elements in the list.

一萌ing 2024-08-12 12:43:42

另一个重要的保证是每个不同容器在内存中存储其数据的方式:

  • 向量是单个连续的内存块。
  • 双端队列是一组链接的内存块,每个内存块中存储多个元素。
  • 列表是分散在内存中的一组元素,即:每个内存“块”仅存储一个元素。

请注意,双端队列的设计目的是尝试平衡向量和列表的优点,而没有各自的缺点。在内存有限的平台(例如微控制器)中,它是一个特别有趣的容器。

内存存储策略经常被忽视,然而,它往往是为特定应用选择最合适的容器的最重要原因之一。

Another important guarantee is the way each different container stores its data in memory:

  • A vector is a single contiguous memory block.
  • A deque is a set of linked memory blocks, where more than one element is stored in each memory block.
  • A list is a set of elements dispersed in memory, i.e.: only one element is stored per memory "block".

Note that the deque was designed to try to balance the advantages of both vector and list without their respective drawbacks. It is a specially interesting container in memory limited platforms, for example, microcontrollers.

The memory storage strategy is often overlooked, however, it is frequently one of the most important reasons to select the most suitable container for a certain application.

另类 2024-08-12 12:43:42

不可以。双端队列只支持前后O(1)的插入和删除。例如,它可以在具有环绕的矢量中实现。由于它还保证 O(1) 随机访问,因此您可以确定它不使用(仅)双向链表。

No. A deque only supports O(1) insertion and deletion at the front and back. It may, for example, be implemented in a vector with wrap-around. Since it also guarantees O(1) random access, you can be sure it's not using (just) a doubly linked list.

淡水深流 2024-08-12 12:43:42

dequelist 之间的显着差异

  • 对于 deque

    并排存储的项目;

    针对从两侧(正面、背面)添加数据进行了优化;

    按数字(整数)索引的元素。

    可以通过迭代器甚至元素的索引进行浏览。

    访问数据的时间更快。

  • 对于列表

    “随机”存储在内存中的项目;

    只能通过迭代器浏览;

    针对在中间插入和移除进行了优化。

    由于其空间局部性非常差,数据的时间访问速度较慢,迭代速度较慢。

    可以很好地处理大元素

您还可以检查以下内容 链接,它比较了两个STL容器(使用std::vector)之间的性能

希望我分享了一些有用的信息。

Among eminent differences between deque and list

  • For deque :

    Items stored side by side;

    Optimized for adding datas from two sides (front, back);

    Elements indexed by numbers (integers).

    Can be browsed by iterators and even by element's index.

    Time access to data is faster.

  • For list

    Items stored "randomly" in the memory;

    Can be browsed only by iterators;

    Optimized for insertion and removal in the middle.

    Time access to data is slower, slow to iterate, due to its very poor spatial locality.

    Handles very well large elements

You can check also the following Link, which compares the performance between the two STL containers (with std::vector)

Hope i shared some useful informations.

九局 2024-08-12 12:43:42

其他人已经很好地解释了性能差异。我只是想补充一点,类似甚至相同的接口在面向对象编程中很常见——这是编写面向对象软件的通用方法的一部分。你绝对不应该仅仅因为两个类实现相同的接口而假设它们以相同的方式工作,就像你不应该假设马像狗一样工作,因为它们都实现了attack()和make_noise()。

The performance differences have been explained well by others. I just wanted to add that similar or even identical interfaces are common in object-oriented programming -- part of the general methodology of writing object-oriented software. You should IN NO WAY assume that two classes work the same way simply because they implement the same interface, any more than you should assume that a horse works like a dog because they both implement attack() and make_noise().

放赐 2024-08-12 12:43:42

下面是使用列表、无序映射的概念验证代码,它提供 O(1) 查找和 O(1) 精确 LRU 维护。需要(非擦除)迭代器在擦除操作中幸存下来。计划在 O(1) 任意大的软件管理缓存中使用 GPU 内存上的 CPU 指针。向 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;
} 

Here's a proof-of-concept code use of list, unorded map that gives O(1) lookup and O(1) exact LRU maintenance. Needs the (non-erased) iterators to survive erase operations. Plan to use in a O(1) arbitrarily large software managed cache for CPU pointers on GPU memory. Nods to the Linux O(1) scheduler (LRU <-> run queue per processor). The unordered_map has constant time access via hash table.

#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;
} 
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文