Mega Code Archive

 
Categories / Java Tutorial / Collections
 

Sort array utilities

/*  * Copyright 2004, 2005, 2006 Odysseus Software GmbH  *  * Licensed under the Apache License, Version 2.0 (the "License");  * you may not use this file except in compliance with the License.  * You may obtain a copy of the License at  *  *     http://www.apache.org/licenses/LICENSE-2.0  *  * Unless required by applicable law or agreed to in writing, software  * distributed under the License is distributed on an "AS IS" BASIS,  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  * See the License for the specific language governing permissions and  * limitations under the License.  */  import java.util.Arrays; import java.util.Comparator; import java.util.Iterator; import java.util.List; /**  * Utility class providing some useful static sort methods. The sort routines  * all return index permutations p such that data[p[0]],..,data[p[data.length-1]]  * is in sorted order. The data array itself is not modified.  * To actually rearrange the array elements, the inverse of p can be used to  * permute the array, such that data[0],..,data[data.length-1] is in sorted  * order. Use <code>getIterator(p, data)</code> to iterate in sorted order.  * A code example may show you what to do next:  * <pre>  * String[] colors = { "red", "green", "blue" };  * int[] p = SortUtils.sort(colors, new StringComparator());  * // --> (colors[p[0]], colors[p[1]], colors[p[2]]) == ("blue","green","red")  * Iterator iter = SortUtils.getIterator(p, colors)  * // --> (iter.next(), iter.next(), iter.next()) == ("blue","green","red")  * SortUtils.permute(SortUtils.inverse(p), colors, true);  * // --> (colors[0], colors[1], colors[2]) == ("blue","green","red")  * </pre>  * Stable sorts (preserving order of equal elements) are supported.  * Sorting is done using quick-sort mith median of 3 (and insertion-sort  * for small ranges).  *  * @author Christoph Beck  */ public class SortUtils {   /**    * Helper class used to perform quicksort.    *    * @author Christoph Beck    */   static final class QuickSorter {     private static final int INSERTIONSORT_THRESHOLD = 7;     private final Object[] data;     QuickSorter(Object[] data) {       this.data = data;     }     private int compare(Comparator cmp, boolean stable, int i, int j) {       int result = cmp.compare(data[i], data[j]);       if (result == 0 && stable && i != j) {         result = i < j ? -1 : 1;       }       return result;     }     private int med3(Comparator cmp, int a, int b, int c) {         return  (compare(cmp, false, a, b) < 0 ?             (compare(cmp, false, b, c) < 0 ? b : compare(cmp, false, a, c) < 0 ? c : a) :             (compare(cmp, false, b, c) > 0 ? b : compare(cmp, false, a, c) < 0 ? c : a));     }     private int pivot(int[] indices, Comparator cmp, int lo, int hi) {       return med3(cmp, indices[lo + 1], indices[(lo + hi) / 2], indices[hi - 1]);     }     private void swap(int[] indices, int i, int j) {       int tmp = indices[i];       indices[i] = indices[j];       indices[j] = tmp;     }     private void insertionSort(int[] indices, Comparator cmp, boolean stable, int lo, int hi) {       for (int i = lo; i <= hi; i++) {             for (int j = i; j > lo && compare(cmp, stable, indices[j-1], indices[j]) > 0; j--) {               swap(indices, j-1, j);             }           }     }     private void quickSort(int[] indices, Comparator cmp, boolean stable, int lo0, int hi0) {       int pivot = pivot(indices, cmp, lo0, hi0);       int lo = lo0, hi = hi0;       while (lo <= hi) {         while (lo < hi0 && compare(cmp, stable, pivot, indices[lo]) > 0)           ++lo;         while (hi > lo0 && compare(cmp, stable, pivot, indices[hi]) < 0)           --hi;         if (lo <= hi) {           swap(indices, lo++, hi--);         }       }       sort(indices, cmp, stable, lo0, hi);       sort(indices, cmp, stable, lo, hi0);     }     void sort(int[] indices, Comparator cmp, boolean stable, int lo, int hi) {       if (hi - lo < INSERTIONSORT_THRESHOLD) {         insertionSort(indices, cmp, stable, lo, hi);       } else {         quickSort(indices, cmp, stable, lo, hi);       }     }     void sort(int[] indices, Comparator cmp, boolean stable) {       sort(indices, cmp, stable, 0, indices.length - 1);     }     int[] sort(Comparator cmp, boolean stable) {       int[] indices = identity(data.length);       sort(indices, cmp, stable);       return indices;     }   }   /**    * Create identity permutation, that is <code>{0, 1, ..., n}</code>    */   public static int[] identity(int n) {     int[] indices = new int[n];     for (int i = 0; i < n; i++)       indices[i] = i;     return indices;   }   /**    * Create reverse permutation, that is <code>{n-1, .... 1, 0}</code>    */   public static int[] reverse(int n) {     int[] indices = new int[n];     for (int i = 0; i < n; i++)       indices[i] = n - i - 1;     return indices;   }   /**    * Compute inverse permutation    */   public static int[] inverse(int[] p) {     int[] pi = new int[p.length];     for (int i = 0; i < pi.length; i++)       pi[p[i]] = i;     return pi;   }   /**    * Rearrange the specified data according to the specified permutation.    * That is, the array is rearranged, such that    * <code>data_after[p[i]] == data_before[i]</code>.    * @param data data to be permuted    * @param p the permutation    * @param clone if true, rearrange a clone instead of the original data;    * @return the permuted array (which is the original reference if clone == false)    */   public static Object[] permute(int[] p, Object[] data, boolean clone) {     Object[] permuted = null;     if (clone) {       permuted = (Object[])data.clone();       for (int i = 0; i < data.length; i++)         permuted[p[i]] = data[i];     } else {       // run thru cycles       int i = 0;       while (i < p.length) {         if (p[i] < 0 || p[i] == i) // skip already handled and cycles of length 1           ++i;         else { // start a new cycle           int j = p[i];           Object save = data[i];           while (p[j] >= 0) {             Object tmp = data[j];             data[j] = save;             save = tmp;             i = j;             j = p[j];             p[i] = -1;           }         }       }       permuted = data;     }     return permuted;   }   /**    * Answer iterator, which iterates over specified data array according    * to the specified permutation, that is    * <code>data[p[0]],..,data[p[data.length-1]]</code>    */   public static Iterator getIterator(final int[] p, final Object[] data) {     return new Iterator() {       int pos = 0;       public boolean hasNext() {         return pos < data.length;       }       public Object next() {         return data[p[pos++]];       }       public void remove() {         throw new UnsupportedOperationException("Cannot remove from immutable iterator!");       }     };   }   /**    * Answer iterator, which iterates over specified data list according    * to the specified permutation, that is    * <code>data.get(p[0]),..,data.get(p[data.length-1])</code>    */   public static Iterator getIterator(final int[] p, final List data) {     return new Iterator() {       int pos = 0;       public boolean hasNext() {         return pos < data.size();       }       public Object next() {         return data.get(p[pos++]);       }       public void remove() {         throw new UnsupportedOperationException("Cannot remove from immutable iterator!");       }     };   } //  /** //   * An improved heap builder. //   * Assumes children of i at 2i and 2i+1 (requires i>0) //   */ //  private static void cheap(int[] indices, Object[] data, Comparator comparator, int i, int j) { //    int k = (i << 1); //    if (k > j) //      return; //    while (k < j) { //      if (comparator.compare(data[indices[k]], data[indices[k + 1]]) < 0) //        k++; //      k <<= 1; //    } //    if (k > j) //      k >>= 1; //    while (comparator.compare(data[indices[k]], data[indices[i]]) < 0) //      k >>= 1; //    int t1 = indices[i], t2; //    while (k > i) { //      t2 = indices[k]; //      indices[k] = t1; //      k >>= 1; //      t1 = indices[k]; //      indices[k] = t2; //      k >>= 1; //    } //    if (k == i) //      indices[i] = t1; //  } // //  /** //   * Do a (clever) heapsort. //   * //   * @param comparator Comparator object specifying the sort order. //   */ //  public static void cheapSort(int[] indices, Object[] data, Comparator comparator) { //    int n = data.length; //    if (n > 1) { //      int i; //      int m = 0; //      for (i = 1; i < n; i++) //        if (comparator.compare(data[indices[i]], data[indices[m]]) < 0) //          m = i; //      if (m > 0) { //        int t = indices[0]; //        indices[0] = indices[m]; //        indices[m] = t; //      } //      if (n > 2) { //        for (i = n / 2; i > 1; i--) //          cheap(indices, data, comparator, i, n - 1); //        for (i = n - 1; i > 1; i--) { //          cheap(indices, data, comparator, 1, i); //          int t = indices[1]; //          indices[1] = indices[i]; //          indices[i] = t; //        } //      } //    } //  } // //  /** //   * Perform a cheapsort //   */ //  public static int[] cheapSort(Object[] data, Comparator comparator) { //    int[] indices = identity(data.length); //    cheapSort(indices, data, comparator); //    return indices; //  }   /**    * Do a sort on indices.    * @param data data to be sorted    * @param comparator comparator to use    * @param stable do a stable sort iff true    * @param indices into data (any permutation of 0,..data.length-1).    */   public static void sort(int[] indices, Object[] data, Comparator comparator, boolean stable) {     new QuickSorter(data).sort(indices, comparator, stable);   }   /**    * Do a sort on indices.    * @param data data to be sorted    * @param comparator comparator to use    * @param stable do a stable sort iff true    * @return permutation p such that data[p[0]],..,data[p[data.length-1]] is in sorted order    */   public static int[] sort(Object[] data, Comparator comparator, boolean stable) {     int[] indices = identity(data.length);     sort(indices, data, comparator, stable);     return indices;   }   /**    * Do an unstable sort.    * @param data data to be sorted    * @param comparator comparator to use    * @return permutation p such that data[p[0]],..,data[p[data.length-1]] is in sorted order    */   public static int[] sort(Object[] data, Comparator comparator) {     return sort(data, comparator, false);   }   /**    * Do an unstable sort.    * @param data data to be sorted    * @param indices into data (permutation of 0,..data.length-1).    */   public static void sort(int[] indices, Object[] data, Comparator comparator) {     sort(indices, data, comparator, false);   }   /**    * Test method    */   public static void main(String[] args) {     Comparator cmp = new Comparator() {       public int compare(Object o1, Object o2) {         return ((Comparable)o1).compareTo(o2);       }     };     int n = 1000000;     if (args.length == 1)       try {         n = Integer.parseInt(args[0]);       } catch (Exception e) {         System.err.println(e);       }     System.out.println("Generating " + n + " random integers...");     java.util.Random random = new java.util.Random();     Integer[] data = new Integer[n];     for (int i = 0; i < n; i++) {       data[i] = new Integer(Math.abs(random.nextInt())); //      data[i] = new Integer(i);     }     int[] indices;     long time;     System.out.print("Arrays.sort...");     time = System.currentTimeMillis();     Integer[] clone = (Integer[])data.clone();     Arrays.sort(clone, cmp);     System.out.println(System.currentTimeMillis()-time  + "ms");     System.out.print("quicksort...");     indices = identity(n);     time = System.currentTimeMillis();     sort(indices, data, cmp, false);     System.out.println(System.currentTimeMillis()-time  + "ms");     for (int i = 1; i < n; i++)       if (cmp.compare(data[indices[i-1]], data[indices[i]]) > 0)         System.err.println("proplem: quickSort at " + i);     System.out.print("quicksort stable..."); //    indices = identity(n);     time = System.currentTimeMillis();     sort(indices, data, cmp, true);     System.out.println(System.currentTimeMillis()-time + "ms");     for (int i = 1; i < n; i++) {       int res = cmp.compare(data[indices[i-1]], data[indices[i]]);       if (res > 0)         System.err.println("proplem: quickSort stable at " + i);       if (res == 0 && indices[i-1] > indices[i])         System.err.println("proplem: quickSort stable (not stable) at " + i);     } //    System.out.print("cheapsort..."); //    time = System.currentTimeMillis(); //    indices = cheapSort(data, cmp); //    System.out.println(System.currentTimeMillis()-time + "ms"); //    for (int i = 1; i < n; i++) //      if (cmp.compare(data[indices[i-1]], data[indices[i]]) > 0) //        System.err.println("proplem: cheapSort at " + i);        System.out.print("permutate copy...");     time = System.currentTimeMillis();     Object[] data_copy = permute(inverse(indices), data, true);     System.out.println(System.currentTimeMillis()-time + "ms");     for (int i = 1; i < n; i++)       if (cmp.compare(data_copy[i-1], data_copy[i]) > 0)         System.err.println("proplem: permute copy at " + i);     System.out.print("permutate original...");     time = System.currentTimeMillis();     permute(inverse(indices), data, false);     System.out.println(System.currentTimeMillis()-time + "ms");     for (int i = 1; i < n; i++)       if (cmp.compare(data[i-1], data[i]) > 0)         System.err.println("proplem: permute original at " + i);   } }