Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

An indexed set (for efficient removal in a vector)

Tags:

c++

arrays

I was just about to implement my own class for efficient removal from an array, but thought I'd ask to see if anything like it already exists. What I want is list-like access efficiency but using an array. I want to use an array for reasons of cache coherence and so I don't have to continually be calling a memory allocator (as using std::list would when allocating nodes).

What I thought about doing was creating a class with two arrays. The first is a set of elements and the second array is a set of integers where each integer is a free slot in the first array. So I can add/remove elements from the array fairly easily, without allocating new memory for them, simply by taking an index from the free list and using that for the new element.

Does anything like this exist already? If I do my own, I'll have to also make my own iterators, so you can iterate the set avoiding any empty slots in the array, and I don't fancy that very much.

Thanks.

Note: The kind of operations I want to perform on the set are:

  1. Iteration
  2. Random access of individual elements, by index (or "handle" as I'm thinking of it)
  3. Removal of an element anywhere in the set
  4. Addition of an element to the set (order unimportant)
like image 794
Robinson Avatar asked Oct 21 '22 09:10

Robinson


2 Answers

std::list<T> actually does sound exactly like the theoretically correct data structure for your job, because it supports the four operations you listed, all with optimal space and time complexity. std::list<T>::iterator is a handle that remains valid even if you add/remove other items to/from the list.

It may be that there is a custom allocator (i.e. not std::allocator<T>) that you could use with std::list<T, Allocator> to get the performance you want (internally pool nodes and then don't do runtime allocation everytime you add or remove a node). But that might be overkill.

I would start just using a std::list<T> with the default allocator and then only look at custom allocators or other data structures if you find the performance is too bad for your application.

like image 171
Timothy Shields Avatar answered Oct 29 '22 19:10

Timothy Shields


If maintaining order of elements is irrelevant, use swap-and-pop.

Copy/move the last element over the one to be removed, then pop the back element. Super easy and efficient. You don't even need to bother with special checks for removing the element since it'll Just Work(tm) if you use the standard C++ vector and operations.

*iter = std::move(container.back());
container.pop_back();

I don't recall if pop_back() invalidated iterators on vector, but I don't think it does. If it does, just use indices directly or to recalculate a new valid iterator.

auto delta = iter - container.begin();
// mutate container
iter = container.begin() + delta;
like image 27
Sean Middleditch Avatar answered Oct 29 '22 18:10

Sean Middleditch