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

后端 未结 7 1033
我寻月下人不归
我寻月下人不归 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:54

    A deque is typically implemented as a dynamic array of arrays of T.

     (a) (b) (c) (d)
     +-+ +-+ +-+ +-+
     | | | | | | | |
     +-+ +-+ +-+ +-+
      ^   ^   ^   ^
      |   |   |   |
    +---+---+---+---+
    | 1 | 8 | 8 | 3 | (reference)
    +---+---+---+---+
    

    The arrays (a), (b), (c) and (d) are generally of fixed capacity, and the inner arrays (b) and (c) are necessarily full. (a) and (d) are not full, which gives O(1) insertion at both ends.

    Imagining that we do a lot of push_front, (a) will fill up, when it's full and an insertion is performed we first need to allocate a new array, then grow the (reference) vector and push the pointer to the new array at the front.

    This implementation trivially provides:

    • Random Access
    • Reference Preservation on push at both ends
    • Insertion in the middle that is proportional to min(distance(begin, it), distance(it, end)) (the Standard is slightly more stringent that what you required)

    However it fails the requirement of amortized O(1) growth. Because the arrays have fixed capacity whenever the (reference) vector needs to grow, we have O(N/capacity) pointer copies. Because pointers are trivially copied, a single memcpy call is possible, so in practice this is mostly constant... but this is insufficient to pass with flying colors.

    Still, push_front and push_back are more efficient than for a vector (unless you are using MSVC implementation which is notoriously slow because of very small capacity for the arrays...)


    Honestly, I know of no data structure, or data structure combination, that could satisfy both:

    • Random Access

    and

    • O(1) insertion at both ends

    I do know a few "near" matches:

    • Amortized O(1) insertion can be done with a dynamic array in which you write in the middle, this is incompatible with the "reference preservation" semantics of the deque
    • A B+ Tree can be adapted to provide an access by index instead of by key, the times are close to constants, but the complexity is O(log N) for access and insertion (with a small constant), it requires using Fenwick Trees in the intermediate level nodes.
    • Finger Trees can be adapted similarly, once again it's really O(log N) though.

提交回复
热议问题