Is it possible to rearrange an array in place in O(N)?

后端 未结 9 1209
既然无缘
既然无缘 2020-12-24 14:17

If I have a size N array of objects, and I have an array of unique numbers in the range 1...N, is there any algorithm to rearrange the object array in-place in the or

相关标签:
9条回答
  • 2020-12-24 15:03

    The approach is to follow the "permutation cycles" of the permutation, rather than indexing the array left-to-right. But since you do have to begin somewhere, everytime a new permutation cycle is needed, the search for unpermuted elements is left-to-right:

    // Pseudo-code
    N : integer, N > 0 // N is the number of elements
    swaps : integer [0..N]
    data[N] : array of object
    permute[N] : array of integer [-1..N]  denoting permutation (used element is -1)
    next_scan_start : integer;
    next_scan_start = 0;
    while (swaps < N ) { // Search for the next index that is not-yet-permtued. for (idx_cycle_search = next_scan_start; idx_cycle_search < N; ++ idx_cycle_search) if (permute[idx_cycle_search] >= 0) break;
    next_scan_start = idx_cycle_search + 1;
    // This is a provable invariant. In short, number of non-negative // elements in permute[] equals (N - swaps) assert( idx_cycle_search < N );
    // Completely permute one permutation cycle, 'following the // permutation cycle's trail' This is O(N) while (permute[idx_cycle_search] >= 0) { swap( data[idx_cycle_search], data[permute[idx_cycle_search] ) swaps ++; old_idx = idx_cycle_search; idx_cycle_search = permute[idx_cycle_search]; permute[old_idx] = -1; // Also '= -idx_cycle_search -1' could be used rather than '-1' // and would allow reversal of these changes to permute[] array } }
    0 讨论(0)
  • 2020-12-24 15:13

    I can do it given O(N) scratch space -- copy to new array and copy back.

    EDIT: I am aware of the existance of an algorithm that will proceed through. The idea is to perform the swaps on the array of integers 1..N while at the same time mirroring the swaps on your array of large objects. I just cannot find the algorithm right now.

    0 讨论(0)
  • 2020-12-24 15:14

    If you didn't mind allocating memory for an extra hash of indexes, you could keep a mapping of original location to current location to get a time complexity of near O(n). Here's an example in Ruby, since it's readable and pseudocode-ish. (This could be shorter or more idiomatically Ruby-ish, but I've written it out for clarity.)

    #!/usr/bin/ruby
    
    objects       = ['d', 'e', 'a', 'c', 'b']
    order         = [2, 4, 3, 0, 1]
    cur_locations = {}
    
    order.each_with_index do |orig_location, ordinality|
      # Find the current location of the item.
      cur_location = orig_location
      while not cur_locations[cur_location].nil? do
        cur_location = cur_locations[cur_location]
      end
    
      # Swap the items and keep track of whatever we swapped forward.
      objects[ordinality], objects[cur_location] = objects[cur_location], objects[ordinality]
      cur_locations[ordinality] = orig_location
    end
    
    puts objects.join(' ')
    

    That obviously does involve some extra memory for the hash, but since it's just for indexes and not your "fairly large" objects, hopefully that's acceptable. Since hash lookups are O(1), even though there is a slight bump to the complexity due to the case where an item has been swapped forward more than once and you have to rewrite cur_location multiple times, the algorithm as a whole should be reasonably close to O(n).

    If you wanted you could build a full hash of original to current positions ahead of time, or keep a reverse hash of current to original, and modify the algorithm a bit to get it down to strictly O(n). It'd be a little more complicated and take a little more space, so this is the version I wrote out, but the modifications shouldn't be difficult.

    EDIT: Actually, I'm fairly certain the time complexity is just O(n), since each ordinality can have at most one hop associated, and thus the maximum number of lookups is limited to n.

    0 讨论(0)
提交回复
热议问题