What data structure, exactly, are deques in C++?

后端 未结 7 1025
我寻月下人不归
我寻月下人不归 2020-12-04 19:44

Is there a specific data structure that a deque in the C++ STL is supposed to implement, or is a deque just this vague notion of an array growable from both the front and th

7条回答
  •  借酒劲吻你
    2020-12-04 19:58

    A deque could be implemented correctly by using a vector. All the elements are copied onto the heap and the pointers stored in a vector. (More on the vector later).

    Why T* instead of T? Because the standard requires that

    "An insertion at either end of the deque invalidates all the iterators to the deque, but has no effect on the validity of references to elements of the deque."

    (my emphasis). The T* helps to satisfy that. It also helps us to satisfy this:

    "Inserting a single element either at the beginning or end of a deque always ..... causes a single call to a constructor of T."

    Now for the (controversial) bit. Why use a vector to store the T*? It gives us random access, which is a good start. Let's forget about the complexity of vector for a moment and build up to this carefully:

    The standard talks about "the number of operations on the contained objects.". For deque::push_front this is clearly 1 because exactly one T object is constructed and zero of the existing T objects are read or scanned in any way. This number, 1, is clearly a constant and is independent of the number of objects currently in the deque. This allows us to say that:

    'For our deque::push_front, the number of operations on the contained objects (the Ts) is fixed and is independent of the number of objects already in the deque.'

    Of course, the number of operations on the T* will not be so well-behaved. When the vector grows too big, it'll be realloced and many T*s will be copied around. So yes, the number of operations on the T* will vary wildly, but the number of operations on T will not be affected.

    Why do we care about this distinction between counting operations on T and counting operations on T*? It's because the standard says:

    All of the complexity requirements in this clause are stated solely in terms of the number of operations on the contained objects.

    For the deque, the contained objects are the T, not the T*, meaning we can ignore any operation which copies (or reallocs) a T*.

    I haven't said much about how a vector would behave in a deque. Perhaps we would interpret it as a circular buffer (with the vector always taking up its maximum capacity(), and then realloc everything into a bigger buffer when the vector is full. The details don't matter.

    In the last few paragraphs, we have analyzed deque::push_front and the relationship between the number of objects in the deque already and the number of operations performed by push_front on contained T-objects. And we found they were independent of each other. As the standard mandates that complexity is in terms of operations-on-T, then we can say this has constant complexity.

    Yes, the Operations-On-T*-Complexity is amortized (due to the vector), but we're only interested in the Operations-On-T-Complexity and this is constant (non-amortized).

    Epilogue: the complexity of vector::push_back or vector::push_front is irrelevant in this implementation; those considerations involve operations on T* and hence is irrelevant.

提交回复
热议问题