为什么STL双端队列没有被实现为仅仅是一个圆形向量?

时间:2016-09-05 05:00:29

标签: c++ stl deque

我一直认为在C ++标准模板库(STL)中,双端队列(deque)是一个具有圆形边界条件的大小可变数组(如矢量),这意味着有一个头指针i和尾指针j都指向数组a[0..L-1]的某个位置。 push_front为i--,push_back为j++,pop_front为i++,pop_back为j--。当指针ij到达L-1时,它会再次出现在数组的另一端(0L-1) 。如果数组大小耗尽(在插入新元素后指针i==j),则原始大小加倍的较大空间将重新分配给a[],数据将被复制,就像在向量中一样。考虑到圆形边界条件,还有O(1)时间随机访问。但有人告诉我,在STL双端队列中,实际上有一个指针阵列指向许多固定长度的数组段。它比圆形矢量复杂得多。不使用简单的圆形向量来实现双端队列的功能有什么好处?随机访问会变慢吗?

4 个答案:

答案 0 :(得分:3)

std::deque方法的主要优点是,如果从两端中的任何一端添加或删除元素,则永远不会移动一旦插入容器中的元素。因此,在执行这些操作时,元素的引用(和指针)不会失效(请注意,非常令人惊讶的是,迭代器deque元素在末尾执行插入或删除时会失效。

这可以在不影响正式的大O复杂性的同时使实现更复杂,并使std::deque成为非常有用的容器。

您可以拥有std::deque个“胖”对象,而无需使用额外的间接级别来避免移动操作并保持效率。

答案 1 :(得分:2)

cppreference

  

与std :: vector相反,deque的元素不是连续存储的:典型的实现使用一系列单独分配的固定大小的数组。

这意味着std::vector不执行大型内部重新分配std::deque偶尔执行的操作。当空间用完时,只添加一个小的固定大小的数组。 (当由于擦除而空间变得太大时,会发生相反的反转。)

这是一个小测试:

#include <vector>
#include <deque>
#include <string>
#include <iostream>
#include <chrono>


using namespace std;


int main()
{
    {
        const auto start = chrono::high_resolution_clock::now();

        vector<string> v;
        for(size_t i = 0; i < 9999999; ++i)
            v.push_back(string("hello"));

        cout << chrono::duration_cast<chrono::milliseconds>(chrono::high_resolution_clock::now() - start).count() << endl;
    }

    {
        const auto start = chrono::high_resolution_clock::now();

        deque<string> v;
        for(size_t i = 0; i < 9999999; ++i)
            v.push_back(string("hello"));

        cout << chrono::duration_cast<chrono::milliseconds>(chrono::high_resolution_clock::now() - start).count() << endl;
    }

    return 0;
}

在我的机器上,它显示deque的速度是vector的两倍:

$ ./a.out 
301
164

答案 2 :(得分:1)

23.3.8.4 [deque.modifiers](强调是我的)

  

deque中间的插入使所有迭代器无效   和对deque元素的引用。插入两端   deque使deque 的所有迭代器无效但没有   对 deque

的元素引用的有效性产生影响。

对于类似循环矢量的实现,这是不可能的。

答案 3 :(得分:0)

std::deque(双端队列)是一个索引序列容器,允许在其开头和结尾快速插入和删除。此外,在双端队列的任何一端插入和删除都不会使指针或对其余元素的引用无效。

std::vector相反,双端队列的元素不是连续存储的:典型的实现使用一系列单独分配的固定大小的数组。

双端队列的存储会根据需要自动扩展和收缩。扩展deque比std::vector的扩展便宜,因为它不涉及将现有元素复制到新的内存位置。

deques的常见操作的复杂性(效率)如下:

  • 随机访问 - 常数O(1)
  • 在结尾或开头插入或移除元素 - 常数O(1)
  • 插入或移除元素 - 线性O(n)

来源:std::deque