views:

596

answers:

6

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 no limitation on the number of reads.

A: 

If the numbers are in a specific range, the task may be done in O(n) compute time, which is a special case of ordering, its done by creating an array and mapping each number to the specified position.

If the numbers are not in a range, the the best ways to do this is using either QuickSort or HeapSort, which sorts int O(Log2N) both, but some preffer QuickSort, like me.

You can find a QuickSort implementation in any language in almost every place, just Google it and you'll find it in seconds.

An adaptation of HeapSort is available when organizing external data, means data that is not in memmory, but in hard disk, most common when dealing with huge arrays.

Hope I can be of help Best Regards and best of luck!

David Conde
Can you give an example of ordering in O(n) ? what do you mean with "If the numbers are in a specific range" ?Also, the question is about sorting algorithms with less writings, not more efficient >.<
Marco
+9  A: 

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.

Ash
+1 for the link! Its very interesting.
Marco
+1 for the link
Ither
+1 - Selection sort is _the_ answer. There is no other possible sort that can have as few writes as this, because if you do not place the k'th item in the k'th spot, you'll have an extra write to do to get it in the right spot later.
Rex Kerr
+1 for the combo
M28
@Rex Other answers may have fewer reads, and the same number of writes.
Nick Johnson
Selection sort is **not** the best. Selection sort swaps, which means that before it writes element A to its correct place, it reads element B from that place and writes it to where element A used to be, *even if that's not where element B is supposed to end up*. This results in unnecessary writes! See my answer for a horribly time-inefficient sort that nonetheless makes absolutely no unnecessary writes.
Olathe
+2  A: 

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 :( )

Marco
Also known as selection sort. See my answer.
Ash
Yes, after posted my answer I took a look at yours and noticed it was the same thing that I've posted. Sorry for that :(
Marco
No problems, nice clear example. Check out the 2nd link in my answer, very cool (to me at least).
Ash
A: 

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!

David Conde
+1  A: 

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.

Nick Johnson
This is nice and simple, but one improvement: if some items are already in their final places, you don't have to overwrite them with themselves, so you can actually make less than n writes in some cases.
Olathe
+2  A: 

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 (edit: it appears it's already been invented, and with probably a faster algorithm than here), 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(T[] array) {
    int writes = 0;
    for (int cycleStart = 0; cycleStart < array.length; cycleStart++) {
      T element = array[cycleStart];
      int from = cycleStart;
      do {
        int to = 0;
        for (int i = 0; i < array.length; i++) {
          if (i != cycleStart && array[i].compareTo(element) < 0) to++;
        }
        if (from != to) {
          while (from != to && element.compareTo(array[to]) == 0) to++;
          if (from != to) {
            final T temp = array[to];
            array[to] = element;
            element = temp;
            writes++;
            from = to;
          }
        }
      } while (cycleStart != from);
    }
    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: [6, 2, 4, 1, 2, 5, 1, 3] sorted: [1, 1, 2, 2, 3, 4, 5, 6] writes: 8
array: [5, 3, 4, 6, 2, 1, 1, 2] sorted: [1, 1, 2, 2, 3, 4, 5, 6] writes: 8
array: [2, 6, 1, 4, 3, 1, 5, 2] sorted: [1, 1, 2, 2, 3, 4, 5, 6] writes: 6
array: [4, 1, 1, 5, 2, 2, 3, 6] sorted: [1, 1, 2, 2, 3, 4, 5, 6] writes: 6
array: [1, 1, 5, 4, 2, 6, 2, 3] sorted: [1, 1, 2, 2, 3, 4, 5, 6] writes: 6
array: [2, 6, 1, 3, 1, 4, 5, 2] sorted: [1, 1, 2, 2, 3, 4, 5, 6] writes: 6
array: [4, 1, 6, 3, 2, 1, 5, 2] sorted: [1, 1, 2, 2, 3, 4, 5, 6] writes: 6
array: [2, 1, 1, 3, 2, 6, 5, 4] sorted: [1, 1, 2, 2, 3, 4, 5, 6] writes: 6
array: [4, 1, 3, 2, 1, 2, 6, 5] sorted: [1, 1, 2, 2, 3, 4, 5, 6] writes: 6
array: [5, 6, 1, 3, 1, 2, 2, 4] sorted: [1, 1, 2, 2, 3, 4, 5, 6] writes: 8
array: [7, 1, 1, 1, 7, 4, 2, 4] sorted: [1, 1, 1, 2, 4, 4, 7, 7] writes: 5
array: [1, 2, 4, 7, 7, 1, 1, 4] sorted: [1, 1, 1, 2, 4, 4, 7, 7] writes: 7
array: [1, 1, 1, 7, 4, 4, 2, 7] sorted: [1, 1, 1, 2, 4, 4, 7, 7] writes: 2
array: [7, 1, 4, 2, 1, 4, 7, 1] sorted: [1, 1, 1, 2, 4, 4, 7, 7] writes: 4
array: [1, 4, 4, 1, 7, 7, 2, 1] sorted: [1, 1, 1, 2, 4, 4, 7, 7] writes: 7
array: [1, 2, 4, 7, 7, 4, 1, 1] sorted: [1, 1, 1, 2, 4, 4, 7, 7] writes: 6
array: [1, 1, 4, 2, 4, 7, 1, 7] sorted: [1, 1, 1, 2, 4, 4, 7, 7] writes: 3
array: [7, 1, 4, 1, 7, 4, 2, 1] sorted: [1, 1, 1, 2, 4, 4, 7, 7] writes: 6
array: [4, 4, 1, 1, 7, 7, 2, 1] sorted: [1, 1, 1, 2, 4, 4, 7, 7] writes: 7
array: [7, 1, 1, 7, 4, 1, 4, 2] sorted: [1, 1, 1, 2, 4, 4, 7, 7] writes: 5
Olathe