Algorithm question: in-place multi-bringToFront?

tsbockman thomas.bockman at gmail.com
Fri May 29 03:29:05 UTC 2020


On Thursday, 28 May 2020 at 23:47:50 UTC, H. S. Teoh wrote:
> Just pickin' yall's smart brains:
>
> Given an array A and a set of indices into the array, is there 
> a fast algorithm for rearranging A in-place so that the 
> elements at the given indices are moved to the front?
>
> E.g.: A = [ 0, 1, 2, 3, 4, 5 ], index = [ 1, 3, 5 ]
>
> The result should have 1, 3, and 5 at the front of the array 
> (the exact
> order is unimportant).
>
> For efficiency considerations, assume the size of the index is 
> relatively small, but the array itself may be quite large, so 
> scanning the entire array or copying large chunks of it is 
> undesirable.
>
>
> T

Here's a version that preserves the order of the rest of the 
array:

void cutToFront(T)(scope T[] queue, scope size_t[] cutterXs ...) {
     // Sort cutterXs:
     import std.algorithm : move, sort;
     sort(cutterXs);

     // Convert cutterXs into a set:
     size_t cutterCount = 0, lastCutterX = 0;
     for(size_t cutterXX = 0; cutterXX < cutterXs.length; 
++cutterXX) {
         const cutterX = cutterXs[cutterXX];
         if(cutterX != lastCutterX) {
             lastCutterX = cutterX;
             cutterXs[cutterCount] = cutterX;
             ++cutterCount;
         }
     }
     cutterXs = cutterXs[0 .. cutterCount];

     if(cutterCount > 0) {
         // Validate the indices:
         if(lastCutterX >= queue.length)
             assert(0);

         // Pull the cutters out of the queue temporarily, sliding 
other items back as needed:
         const(size_t) lastCutterXX = cutterCount - 1;
         size_t gap = 0, oldX = lastCutterX, oldCutterX = 
cutterXs[lastCutterXX];
         T[] cutters = new T[cutterXs.length];
         while(true) {
             T* dest;
             if(oldX == oldCutterX) {
                 dest = &(cutters[lastCutterXX - gap]);
                 ++gap;
                 if(gap <= lastCutterXX)
                     oldCutterX = cutterXs[lastCutterXX - gap];
             } else {
                 const newX = oldX + gap;
                 dest = &(queue[newX]);
             }
             move(queue[oldX], *dest);

             if(oldX <= 0)
                 break;
             --oldX;
         }

         // Finally, place the cutters at the front of the line:
         for(size_t x = 0; x < cutterCount; ++x)
             move(cutters[x], queue[x]);
     }
}

Performance is O(maxElement(cutterXs) + cutterXs.length * 
log(cutterXs.length)). It could be made pure @safe nothrow @nogc 
fairly easily, except that maintaining proper attribute inference 
based on the attributes of implicitly called methods of T is a 
pain.


More information about the Digitalmars-d mailing list