3

How do I insert() a bunch of items to the middle of a deque in linear time?

(The items I am inserting are not accessible through an STL-style iterator.)

user541686
  • 205,094
  • 128
  • 528
  • 886
  • 'Mydeque.insert(mydeque.begin()+15, obj);' – Mooing Duck Oct 22 '11 at 20:31
  • How are the items you want to insert accessible? Are they in an array, or some other library's (beside stl) container class? – Scott Langham Oct 22 '11 at 20:38
  • @ScottLangham: They're coming from a generator-like object, so I have no idea when I've reached the end until I do. (I *guess* I could make an STL-style wrapper for it by doing all the test in `operator ==` with `end()`, but it'd be a little uglier of a solution than I'm hoping for.) – user541686 Oct 22 '11 at 20:39
  • You can make an `istream` subclass and then pass it to `istream_iterator` which provides an end iterator which is using the default constructor: `istream_iterator(stream)` is the `begin` and `istream_iterator()` is the `end`. – Daniel Oct 23 '11 at 03:31
  • @Dani `istream` is specifically designed to read and interpret characters. "Adapting" it to the current problem would mean to overwrite `istream::fail` and defining `operator<<(istream, myElement)` in an inconsistent way, which is a lot uglier imo than defining a specific iterator. – MartinStettner Oct 23 '11 at 09:28

4 Answers4

7

There is a deque::insert(iterator pos, const T&x) function taking the position pos as deque::iterator and a single element. Using this method you could insert all elements one by one. pos can easily be obtained (if you have an index before which you want to insert the element) by deque.begin()+index. The insert method returns an iterator for the newly inserted element, simply increment this returned iterator to get the next position:

deque::iterator it = myDeque.begin()+index;
while(n--) {
  it = myDeque.insert(it, currentElement);
  it++;
  currentElement = ... // However you get your next element ...
}

This however cantake O(n*k) time, since insertion of a single element is (iirc) a linear time operation iirc.

The second overload is deque::insert(iterator pos, InputIterator f, InputIterator l): Remember that simple pointers also fulfill the requirements of an STL input iterator, so if you have a C-Style array T array[] of length n containing your elements, you could insert all elements from this array with

d.insert(pos, array, array+n);

This operation can be carried out in linear time, i.e. O(n+k). I'm not sure if this is guaranteed by the standard, but I suppose that most implementation will do it efficiently.

EDIT

I quickly checked with Microsoft's implementation, they do it by a sequence of either push_back or push_front, whatever is closer to pos and then rotating the elements to their final place, which guarantees the above O(n+k) complexity. Of course, that could also be done "by hand" like:

size_type _Off = pos - d.begin();
size_type _Oldsize = d.size();
if (_Off <= d.size() / 2)
{   // closer to front, push to front then rotate
  while (hasMoreElements())
    push_front(nextElement()); // prepend flipped

  size_type _Num = d.size() - _Oldsize;
  std::reverse(d.begin(), d.begin() + _Num); // flip new stuff in place
  std::rotate(d.begin(), d.begin() + _Num, begin() + _Num + _Off);
}
else
{ // closer to back
  while (hasMoreElements())
    push_front(nextElement()); // prepend flipped

  std::rotate(begin() + _Off, begin() + _Oldsize, end());
}

(I copied the code from Microsofts implementation of deque::insert, removing debug code and exception handling,

MartinStettner
  • 28,719
  • 15
  • 79
  • 106
  • Are you sure the iterator doesn't get invalidated? – user541686 Oct 22 '11 at 23:37
  • You mean in the first case? Yes, `deque::insert` returns a valid iterator and incrementing doesn't invalidate it. All other iterators are invalid though after insertion (as is `pos` after insertion in the second case) – MartinStettner Oct 23 '11 at 02:51
  • Ahhh my bad, I didn't notice you said `it = ...`. :) +1 – user541686 Oct 23 '11 at 03:23
  • Doesn't this use linear time for Each element? Meaning inserting a bunch of elements will take n*m time... – Valmond Oct 23 '11 at 08:52
  • 1
    As I tried to explain: If you insert each element one by one (thus using `deque::insert(pos, element)`), yes: This is the first case and I noted the complexity `O(n*k)`. If you insert them all together, no: `deque::insert(pos, start end)` can be accomplished in linear time (`O(n+k)`) by adding the elements at the end (`push_back` resp. `push_front` is amortized `O(1)`) and "rotating" them into place (taking `O(n)` for the whole rotate step). Thus `k*O(1)+O(n)=O(n+k)` ... – MartinStettner Oct 23 '11 at 09:13
1

Call the insert method that takes a sequence of items to insert, see the 3rd method listed here:

http://msdn.microsoft.com/en-us/library/zcww84w5(v=vs.71).aspx

And, create your own STL-style iterator to access the items you want to insert. See:

Custom Iterator in C++

Community
  • 1
  • 1
Scott Langham
  • 58,735
  • 39
  • 131
  • 204
  • I guess this is the best method in terms of performance, though I was hoping for a solution without making my own wrapper. +1 though. – user541686 Oct 22 '11 at 20:40
0

Input:

Deque: lengtl = l,

New items (m = number of new items)

Algo:

create a new deque (1)

Copy all items from original deque until where you want to insert the new ones (p)

Add new items (m)

Add items from old deque (m-p)

Maybe you can just use the new deque but at worst:

Copy new deque onto old one (after a complete clear: ):

Cost (l+m)

The worst cost is thus: origsize * 2 + newitems which is linear.

The "clear deck" isn't counted here but it is also linear ( at worst).

Valmond
  • 2,897
  • 8
  • 29
  • 49
0

Add all the elements after the insertion point to a vector.
Remove all elements after insertion point.
Append new range to deque.
Append vector to deque.

This is O(2n) worst case, instead of O(n^2).

Mooing Duck
  • 64,318
  • 19
  • 100
  • 158