开发者

How to sort an array using minimum number of writes?

开发者 https://www.devze.com 2023-01-15 00:13 出处:网络
My friend was asked a question in his interview: The interviewer gave him an array of unsorted numbers and asked him to sort. The restriction is that the number of writes should be minimized while th

My friend was asked a question in his interview:

The interviewer gave him an array of unsorted numbers and asked him to sort. The restriction is that the number of writes should be minimized while there is 开发者_如何学Gono limitation on the number of reads.


Selection sort is not the right algorithm here. Selection sort will swap values, making up to two writes per selection, giving a maximum of 2n writes per sort.

An algorithm that's twice as good as selection sort is "cycle" sort, which does not swap. Cycle sort will give a maximum of n writes per sort. The number of writes is absolutely minimized. It will only write a number once to its final destination, and only then if it's not already there.

It is based on the idea that all permutations are products of cycles and you can simply cycle through each cycle and write each element to its proper place once.

import java.util.Random;
import java.util.Collections;
import java.util.Arrays;

public class CycleSort {
  public static final <T extends Comparable<T>> int cycleSort(final T[] array) {
    int writes = 0;

    // Loop through the array to find cycles to rotate.
    for (int cycleStart = 0; cycleStart < array.length - 1; cycleStart++) {
      T item = array[cycleStart];

      // Find where to put the item.
      int pos = cycleStart;
      for (int i = cycleStart + 1; i < array.length; i++)
        if (array[i].compareTo(item) < 0) pos++;

      // If the item is already there, this is not a cycle.
      if (pos == cycleStart) continue;

      // Otherwise, put the item there or right after any duplicates.
      while (item.equals(array[pos])) pos++;
      {
        final T temp = array[pos];
        array[pos] = item;
        item = temp;
      }
      writes++;

      // Rotate the rest of the cycle.
      while (pos != cycleStart) {
        // Find where to put the item.
        pos = cycleStart;
        for (int i = cycleStart + 1; i < array.length; i++)
          if (array[i].compareTo(item) < 0) pos++;

        // Put the item there or right after any duplicates.
        while (item.equals(array[pos])) pos++;
        {
          final T temp = array[pos];
          array[pos] = item;
          item = temp;
        }
        writes++;
      }
    } 
    return writes;
  }

  public static final void main(String[] args) {
    final Random rand = new Random();

    final Integer[] array = new Integer[8];
    for (int i = 0; i < array.length; i++) { array[i] = rand.nextInt(8); }

    for (int iteration = 0; iteration < 10; iteration++) {
      System.out.printf("array: %s ", Arrays.toString(array));
      final int writes = cycleSort(array);
      System.out.printf("sorted: %s writes: %d\n", Arrays.toString(array), writes);
      Collections.shuffle(Arrays.asList(array));
    }
  }
}

A few example runs :

array: [3, 2, 6, 1, 3, 1, 4, 4] sorted: [1, 1, 2, 3, 3, 4, 4, 6] writes: 6
array: [1, 3, 4, 1, 3, 2, 4, 6] sorted: [1, 1, 2, 3, 3, 4, 4, 6] writes: 4
array: [3, 3, 1, 1, 4, 4, 2, 6] sorted: [1, 1, 2, 3, 3, 4, 4, 6] writes: 6
array: [1, 1, 3, 2, 4, 3, 6, 4] sorted: [1, 1, 2, 3, 3, 4, 4, 6] writes: 6
array: [3, 2, 3, 4, 6, 4, 1, 1] sorted: [1, 1, 2, 3, 3, 4, 4, 6] writes: 7
array: [6, 2, 4, 3, 1, 3, 4, 1] sorted: [1, 1, 2, 3, 3, 4, 4, 6] writes: 6
array: [6, 3, 2, 4, 3, 1, 4, 1] sorted: [1, 1, 2, 3, 3, 4, 4, 6] writes: 5
array: [4, 2, 6, 1, 1, 4, 3, 3] sorted: [1, 1, 2, 3, 3, 4, 4, 6] writes: 7
array: [4, 3, 3, 1, 2, 4, 6, 1] sorted: [1, 1, 2, 3, 3, 4, 4, 6] writes: 7
array: [1, 6, 4, 2, 4, 1, 3, 3] sorted: [1, 1, 2, 3, 3, 4, 4, 6] writes: 7
array: [5, 1, 2, 3, 4, 3, 7, 0] sorted: [0, 1, 2, 3, 3, 4, 5, 7] writes: 5
array: [5, 1, 7, 3, 2, 3, 4, 0] sorted: [0, 1, 2, 3, 3, 4, 5, 7] writes: 6
array: [4, 0, 3, 1, 5, 2, 7, 3] sorted: [0, 1, 2, 3, 3, 4, 5, 7] writes: 8
array: [4, 0, 7, 3, 5, 1, 3, 2] sorted: [0, 1, 2, 3, 3, 4, 5, 7] writes: 7
array: [3, 4, 2, 7, 5, 3, 1, 0] sorted: [0, 1, 2, 3, 3, 4, 5, 7] writes: 7
array: [0, 5, 3, 2, 3, 7, 1, 4] sorted: [0, 1, 2, 3, 3, 4, 5, 7] writes: 6
array: [1, 4, 3, 7, 2, 3, 5, 0] sorted: [0, 1, 2, 3, 3, 4, 5, 7] writes: 7
array: [1, 5, 0, 7, 3, 3, 4, 2] sorted: [0, 1, 2, 3, 3, 4, 5, 7] writes: 7
array: [0, 5, 7, 3, 3, 4, 2, 1] sorted: [0, 1, 2, 3, 3, 4, 5, 7] writes: 4
array: [7, 3, 1, 0, 3, 5, 4, 2] sorted: [0, 1, 2, 3, 3, 4, 5, 7] writes: 7


If the array is shorter (ie less than about 100 elements) a Selection sort is often the best choice if you also want to reduce the number of writes.

From wikipedia:

Another key difference is that selection sort always performs Θ(n) swaps, while insertion sort performs Θ(n2) swaps in the average and worst cases. Because swaps require writing to the array, selection sort is preferable if writing to memory is significantly more expensive than reading. This is generally the case if the items are huge but the keys are small. Another example where writing times are crucial is an array stored in EEPROM or Flash. There is no other algorithm with less data movement.

For larger arrays/lists Quicksort and friends will provide better performance, but may still likely need more writes than a selection sort.

If you're interested this is a fantastic sort visualization site that allows you to watch specific sort algorithms do their job and also "race" different sort algorithms against each other.


You can use a very naive algorithm that satisfies what you need.

The algorithm should look like this:

i = 0

do
   search for the minimum in range [i..n)
   swap a[i] with a[minPos]
   i = i + 1
repeat until i = n.

The search for the minimum can cost you almost nothing, the swap costs you 3 writes, the i++ costs you 1..

This is named selection sort as stated by ash. (Sorry, I didn't knew it was selection sort :( )


One option for large arrays is as follows (assuming n elements):

  1. Initialize an array with n elements numbered 0..n-1
  2. Sort the array using any sorting algorithm. As the comparison function, compare the elements in the input set with the corresponding numbers (eg, to compare 2 and 4, compare the 2nd and 4th elements in the input set). This turns the array from step 1 into a permutation that represents the sorted order of the input set.
  3. Iterate through the elements in the permutation, writing out the blocks in the order specified by the array. This requires exactly n writes, the minimum.

To sort in-place, in step 3 you should instead identify the cycles in the permutation, and 'rotate' them as necessary to result in sorted order.


The ordering I meant in O(n) is like the selection sort(the previous post) useful when you have a small range of keys (or you are ordering numbers between 2 ranges)

If you have a number array where numbers will be between -10 and 100, then you can create an array of 110 and be sure that all numbers will fit in there, if you consider repeated numbers the idea is the same, but you will have lists instead of numbers in the sorted array

the pseudo-idea is like this

N: max value of your array
tosort //array to be sorted
sorted = int[N]

for i = 0 to length(tosort)
do
   sorted[tosort[i]]++;
end

finalarray = int[length(tosort)]

k = 0
for i = 0 to N
do
  if ( sorted[i] > 0 )
    finalarray[k] = i
    k++;
  endif
end

finalarray will have the final sorted array and you will have o(N) write operations, where N is the range of the array. Once again, this is useful when using keys inside a specific range, but perhaps its your case.

Best regards and good luck!

0

精彩评论

暂无评论...
验证码 换一张
取 消