How to rearrange an array by indices array?

前端 未结 9 1927
故里飘歌
故里飘歌 2021-02-01 19:12

Given an array arr and an array of indices ind, I\'d like to rearrange arr in-place to satisfy the given indices. For exa

9条回答
  •  再見小時候
    2021-02-01 19:35

    This is the "sign bit" solution.

    Given that this is a JavaScript question and the numerical literals specified in the ind array are therefore stored as signed floats, there is a sign bit available in the space used by the input.

    This algorithm cycles through the elements according to the ind array and shifts the elements into place until it arrives back to the first element of that cycle. It then finds the next cycle and repeats the same mechanism.

    The ind array is modified during execution, but will be restored to its original at the completion of the algorithm. In one of the comments you mentioned that this is acceptable.

    The ind array consists of signed floats, even though they are all non-negative (integers). The sign-bit is used as an indicator for whether the value was already processed or not. In general, this could be considered extra storage (n bits, i.e. O(n)), but as the storage is already taken by the input, it is not additional acquired space. The sign bits of the ind values which represent the left-most member of a cycle are not altered.

    Edit: I replaced the use of the ~ operator, as it does not produce the desired results for numbers equal or greater than 231, while JavaScript should support numbers to be used as array indices up to at least 232 - 1. So instead I now use k = -k-1, which is the same, but works for the whole range of floats that is safe for use as integers. Note that as alternative one could use a bit of the float's fractional part (+/- 0.5).

    Here is the code:

    var arr = ["A", "B", "C", "D", "E", "F"];
    var ind = [4, 0, 5, 2, 1, 3];
    
    rearrange(arr, ind);
    
    console.log('arr: ' + arr);
    console.log('ind: ' + ind);
    
    function rearrange(arr, ind) {
        var i, j, buf, temp;
        
        for (j = 0; j < ind.length; j++) {
            if (ind[j] >= 0) { // Found a cycle to resolve
                i = ind[j];
                buf = arr[j];
                while (i !== j) { // Not yet back at start of cycle
                    // Swap buffer with element content
                    temp = buf;
                    buf = arr[i];
                    arr[i] = temp;
                    // Invert bits, making it negative, to mark as visited
                    ind[i] = -ind[i]-1; 
                    // Visit next element in cycle
                    i = -ind[i]-1;
                }
                // dump buffer into final (=first) element of cycle
                arr[j] = buf;
            } else {
                ind[j] = -ind[j]-1; // restore
            }
        }
    }

    Although the algorithm has a nested loop, it still runs in O(n) time: the swap happens only once per element, and also the outer loop visits every element only once.

    The variable declarations show that the memory usage is constant, but with the remark that the sign bits of the ind array elements -- in space already allocated by the input -- are used as well.

提交回复
热议问题