14

I've got a std::vector which I need to sort by selected algorithms for certain operations, but to maintain its original state (e.g. items ordered by when they were entered) the rest of the time.

Obviously I can use std::copy to create a temporary vector and sort that, but I'm wondering if there's a better way, possibly by timestamping the items entered.

Cheers

deworde
  • 2,679
  • 5
  • 32
  • 60
  • Why? Sorting is `O(N log N)`, not to mention the constant factor; copying is straightly `N * sizeof(T)` memory writes assuming POD elements. Plus you could easily exclude the copying time from benchmarking. – kennytm Feb 22 '10 at 17:37
  • Assuming POD elements, copying is constant time (hint: think *memcpy*), and quite fast at that. –  Feb 22 '10 at 19:27
  • 1
    Copying is not constant time, @Stingray. It's O(N). – Rob Kennedy Feb 22 '10 at 19:40
  • @Rob: Yeah, you're right. Forgot the crap still has to go through the CPU one word at a time! –  Feb 22 '10 at 21:17

6 Answers6

23

You could create a std::vector that holds all the indexes of the first vector. You could then sort the index-vector as you like. This should be fast and most importantly, doesn't mean you have to copy the first vector (which is probably more costly!).

monoceres
  • 4,722
  • 4
  • 38
  • 63
  • 1
    +1 for fastest fingers, was posting more or less the same answer. – Binary Worrier Feb 22 '10 at 17:36
  • I would have used an old fashioned malloc'd array of pointers and called qsort but this works too. – Joshua Feb 22 '10 at 17:47
  • @Joshua: most of the time, this will work better -- qsort is usually quite a bit slower than std::sort. – Jerry Coffin Feb 22 '10 at 18:02
  • 1
    I bet generating the vector of indices takes longer than copying the original vector! –  Feb 22 '10 at 19:28
  • 1
    I'll take that bet, @Stingray. I get to provide the class we're storing in the vector. (In particular, I get to define the copy constructor and assignment operator.) I also get to choose how many of them we're storing. (Lots!) – Rob Kennedy Feb 22 '10 at 20:33
  • 1
    Sounds good, could you describe how to do that? Code example would help. – deworde Feb 23 '10 at 16:12
5

If you don't mind a little bit of Boost you can use the MultiIndex library. See this answer from me where you'll find some example code.

Basically, it allows you to keep several "views" of the same data, each with a different order. In your case you'll be able to keep a "sequence" view, where the data is in order of insertion (like a vector) and a "sorted" view in which the data is sorted according to some criterion (like a map).

Community
  • 1
  • 1
Manuel
  • 12,749
  • 1
  • 27
  • 35
2

Any given vector will be sorted in at most one way at any time.

There are two alternatives:

Copy to a temporary vector and sort that as desired. Unless the vector is very large and you've got limited space, this is almost certainly the best way. Even if you're concerned about performance, the cost of making a copy is going to be smaller than the cost of sorting, and if the cost of copying is significant the sorting's going to be a lot slower than the copying.

Alternatively, you could keep some way (the timestamp you mentioned?) of being able to sort the vector back to the original order. This is going to be slow, since you'd only want to do this if the vector was very large, but if you can't make a temporary vector this is the only way to do it.

David Thornley
  • 56,304
  • 9
  • 91
  • 158
  • Copying could be expensive if it's not a vector of pointers, also depends on how the copy ctor is implmented. – Binary Worrier Feb 22 '10 at 17:37
  • @Binary Worrier: I think David's point is that the sort operation on the vector does a bunch of copies when it's moving elements around. If the sort is O(n log n) adding an O(n) copy operation initially doesn't change the overall time complexity. – Michael Burr Feb 22 '10 at 18:15
  • If copying is expensive, then sorting is more so. How do you expect to get a sorted vector without copying each element to its new place? (Actually, the copy constructor could be far more expensive than the assignment operator, in which case the proper thing to do is to fix the copy constructor. I'm not thinking of any case where the copy constructor should be that much more expensive.) – David Thornley Feb 22 '10 at 18:18
  • 1
    By a) sorting a vector of pointers or b) indirectly sorting the vector by sorting an array/vector of ints that map to the positions of the objects in the original vector. No copying. – Binary Worrier Feb 22 '10 at 22:11
0

Whatever item you are sorting, you could wrap it in a structure that has multiple sort-fields.

struct someThing
{
    int sortOrder1;
    int sortOrder2;
    ...
    int sortOrderN;
    //payload data object here
} //note: this code may have some sytax errors (I haven't actually tried compiling this ;), but hope the idea is clear

(or maybe add the sort orders to the base structure itself?)

Then when you need can calculate the different sort orders, and re-order your list depending on which sort-order you need.

FrustratedWithFormsDesigner
  • 26,726
  • 31
  • 139
  • 202
0

I suggest storing smart pointers, to the original data, in each vector. std::vector allows you to supply different methods for sorting. Also, with smart pointers, they will be destroyed automatically when all references to the item are removed.

Thomas Matthews
  • 56,849
  • 17
  • 98
  • 154
0

You can create another vector to store the indices. Here is the code:

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;

int main()
{
    vector<int> numbers = {50,30,20,10,40};
    vector<int> indexOfNumbers;
    
    for(int i = 0; i < numbers.size(); i++)
    {
        indexOfNumbers.push_back(i); 
    }
    // Now, indexOfNumbers = [0,1,2,3,4]

    std::sort(
        indexOfNumbers.begin(), indexOfNumbers.end(), 
        [numbers](int leftIndex, int rightIndex) 
        { 
            return numbers[leftIndex] < numbers[rightIndex]; // sort in ascending order
        }
    );
    // After sorting, indexOfNumbers = [3, 2, 1, 4, 0]

    // Access the sorted elements
    cout << "Accessing the sorted elements : ";
    for(int i = 0; i < numbers.size(); i++)
    {
        cout << numbers[indexOfNumbers[i]] << " ";
    }
    // prints numbers in sorted order i.e. [10,20,30,40,50]
   return 0;
}

Source: Made slight modification according to Tyrer's answer (https://stackoverflow.com/a/47537314)

Puneeth G R
  • 111
  • 1
  • 8