代码之家  ›  专栏  ›  技术社区  ›  edgar.holleis

Java数组排序:获取数组索引排序列表的快速方法

  •  38
  • edgar.holleis  · 技术社区  · 16 年前

    问题:考虑下面的float[]:

    d[i] =     1.7 -0.3  2.1  0.5
    

    我想要的是一个int[]数组,它表示原始数组的索引顺序。

    s[i] =       1    3    0    2
    d[s[i]] = -0.3  0.5  1.7  2.1
    

    当然,可以使用自定义比较器、已排序的自定义对象集,或者简单地对数组进行排序,然后在原始数组中搜索索引(颤抖)。

    实际上,我在寻找的是第二个返回参数 Matlab's sort function .

    有没有简单的方法可以做到这一点(<5 loc)?是否存在不需要为每个元素分配新对象的解决方案?


    更新:

    谢谢你的回复。不幸的是,到目前为止所提出的方案都没有一个与我所希望的简单而有效的解决方案相似。因此,我在JDK反馈论坛中打开了一个线程,建议添加一个新的类库功能来解决这个问题。让我们看看Sun/Oracle对这个问题的看法。

    http://forums.java.net/jive/thread.jspa?threadID=62657&tstart=0

    15 回复  |  直到 7 年前
        1
  •  15
  •   akarnokd    16 年前

    我将调整快速排序算法,以便同时在多个数组上执行交换操作:索引数组和值数组。例如(基于此 quicksort ):

    public static void quicksort(float[] main, int[] index) {
        quicksort(main, index, 0, index.length - 1);
    }
    
    // quicksort a[left] to a[right]
    public static void quicksort(float[] a, int[] index, int left, int right) {
        if (right <= left) return;
        int i = partition(a, index, left, right);
        quicksort(a, index, left, i-1);
        quicksort(a, index, i+1, right);
    }
    
    // partition a[left] to a[right], assumes left < right
    private static int partition(float[] a, int[] index, 
    int left, int right) {
        int i = left - 1;
        int j = right;
        while (true) {
            while (less(a[++i], a[right]))      // find item on left to swap
                ;                               // a[right] acts as sentinel
            while (less(a[right], a[--j]))      // find item on right to swap
                if (j == left) break;           // don't go out-of-bounds
            if (i >= j) break;                  // check if pointers cross
            exch(a, index, i, j);               // swap two elements into place
        }
        exch(a, index, i, right);               // swap with partition element
        return i;
    }
    
    // is x < y ?
    private static boolean less(float x, float y) {
        return (x < y);
    }
    
    // exchange a[i] and a[j]
    private static void exch(float[] a, int[] index, int i, int j) {
        float swap = a[i];
        a[i] = a[j];
        a[j] = swap;
        int b = index[i];
        index[i] = index[j];
        index[j] = b;
    }
    
        2
  •  29
  •   carloscs    16 年前

    创建索引器数组的简单解决方案:对索引器进行排序,比较数据值:

    final Integer[] idx = { 0, 1, 2, 3 };
    final float[] data = { 1.7f, -0.3f,  2.1f,  0.5f };
    
    Arrays.sort(idx, new Comparator<Integer>() {
        @Override public int compare(final Integer o1, final Integer o2) {
            return Float.compare(data[o1], data[o2]);
        }
    });
    
        3
  •  23
  •   Michael Myers KitsuneYMG    16 年前

    创建一个 TreeMap 指数值

        float[] array = new float[]{};
        Map<Float, Integer> map = new TreeMap<Float, Integer>();
        for (int i = 0; i < array.length; ++i) {
            map.put(array[i], i);
        }
        Collection<Integer> indices = map.values();
    

    索引将按它们指向的浮点数排序,原始数组不会被触动。转换 Collection<Integer> 到A int[] 如果真的有必要,就留作练习。

    编辑: 如注释中所述,如果浮点数组中存在重复值,则此方法不起作用。这可以通过 Map<Float, Integer> 变成一个 Map<Float, List<Integer>> 尽管这会使for循环的内部和最终集合的生成稍微复杂一些。

        4
  •  12
  •   Pratap Koritala    10 年前

    使用Java 8的特点(无需额外的库),简洁的实现方式。

    int[] a = {1,6,2,7,8}
    int[] sortedIndices = IntStream.range(0, a.length)
                    .boxed().sorted((i, j) -> a[i] - a[j])
                    .mapToInt(ele -> ele).toArray();
    
        5
  •  7
  •   Apocalisp    11 年前

    Functional Java :

    import static fj.data.Array.array;
    import static fj.pre.Ord.*;
    import fj.P2;
    
    array(d).toStream().zipIndex().sort(p2Ord(doubleOrd, intOrd))
      .map(P2.<Double, Integer>__2()).toArray();
    
        6
  •  2
  •   Community Mohan Dere    8 年前

    更一般的情况是 Jherico's answer 允许重复的值是:

    // Assuming you've got: float[] array; defined already
    
    TreeMap<Float, List<Integer>> map = new TreeMap<Float, List<Integer>>();
    for(int i = 0; i < array.length; i++) {
        List<Integer> ind = map.get(array[i]);
        if(ind == null){
            ind = new ArrayList<Integer>();
            map.put(array[i], ind);
        }
        ind.add(i);
    }
    
    // Now flatten the list
    List<Integer> indices = new ArrayList<Integer>();
    for(List<Integer> arr : map.values()) {
        indices.addAll(arr);
    }
    
        7
  •  2
  •   bobfoster    14 年前

    最好的解决方案是沿着C的qsort,它允许您指定用于比较和交换的函数,因此qsort不需要知道被排序的数据的类型或组织。这是一个你可以试试的。由于Java没有函数,所以使用数组内部类来包装要排序的数组或集合。然后将其包装在indexarray中并进行排序。indexarray上getindex()的结果将是一个索引数组,如javadoc中所述。

    public class QuickSortArray {
    
    public interface Array {
        int cmp(int aindex, int bindex);
        void swap(int aindex, int bindex);
        int length();
    }
    
    public static void quicksort(Array a) {
        quicksort(a, 0, a.length() - 1);
    }
    
    public static void quicksort(Array a, int left, int right) {
        if (right <= left) return;
        int i = partition(a, left, right);
        quicksort(a, left, i-1);
        quicksort(a, i+1, right);
    }
    
    public static boolean isSorted(Array a) {
        for (int i = 1, n = a.length(); i < n; i++) {
            if (a.cmp(i-1, i) > 0)
                return false;
        }
        return true;
    }
    
    private static int mid(Array a, int left, int right) {
        // "sort" three elements and take the middle one
        int i = left;
        int j = (left + right) / 2;
        int k = right;
        // order the first two
        int cmp = a.cmp(i, j);
        if (cmp > 0) {
            int tmp = j;
            j = i;
            i = tmp;
        }
        // bubble the third down
        cmp = a.cmp(j, k);
        if (cmp > 0) {
            cmp = a.cmp(i, k);
            if (cmp > 0)
                return i;
            return k;
        }
        return j;
    }
    
    private static int partition(Array a, int left, int right) {
        int mid = mid(a, left, right);
        a.swap(right, mid);
        int i = left - 1;
        int j = right;
    
        while (true) {
            while (a.cmp(++i, right) < 0)
                ;
            while (a.cmp(right, --j) < 0)
                if (j == left) break;
            if (i >= j) break;
            a.swap(i, j);
        }
        a.swap(i, right);
        return i;
    }
    
    public static class IndexArray implements Array {
        int[] index;
        Array a;
    
        public IndexArray(Array a) {
            this.a = a;
            index = new int[a.length()];
            for (int i = 0; i < a.length(); i++)
                index[i] = i;
        }
    
        /**
         * Return the index after the IndexArray is sorted.
         * The nested Array is unsorted. Assume the name of
         * its underlying array is a. The returned index array
         * is such that a[index[i-1]] <= a[index[i]] for all i
         * in 1..a.length-1.
         */
        public int[] index() {
            int i = 0;
            int j = index.length - 1;
            while (i < j) {
                int tmp = index[i];
                index[i++] = index[j];
                index[j--] = tmp;
            }
            int[] tmp = index;
            index = null;
            return tmp;
        }
    
        @Override
        public int cmp(int aindex, int bindex) {
            return a.cmp(index[aindex], index[bindex]);
        }
    
        @Override
        public void swap(int aindex, int bindex) {
            int tmp = index[aindex];
            index[aindex] = index[bindex];
            index[bindex] = tmp;
        }
    
        @Override
        public int length() {
            return a.length();
        }
    
    }
    
        8
  •  2
  •   user3608841    12 年前
    public static int[] indexSort(final double[] v, boolean keepUnsorted) {
        final Integer[] II = new Integer[v.length];
        for (int i = 0; i < v.length; i++) II[i] = i;
        Arrays.sort(II, new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return Double.compare(v[o1],v[o2]);
            }
        });
        int[] ii = new int[v.length];
        for (int i = 0; i < v.length; i++) ii[i] = II[i];
        if (!keepUnsorted) {
            double[] clon = v.clone();
            for (int i = 0; i < v.length; i++) v[i] = clon[II[i]];
        }
        return ii;
    }
    
        9
  •  0
  •   Mark    16 年前

    将输入转换为类似下面的pair类,然后使用array.sort()对其进行排序。array.sort()确保原始顺序与matlab相同。然后需要将排序后的结果转换回单独的数组。

    class SortPair implements Comparable<SortPair>
    {
      private int originalIndex;
      private double value;
    
      public SortPair(double value, int originalIndex)
      {
        this.value = value;
        this.originalIndex = originalIndex;
      }
    
      @Override public int compareTo(SortPair o)
      {
        return Double.compare(value, o.getValue());
      }
    
      public int getOriginalIndex()
      {
        return originalIndex;
      }
    
      public double getValue()
      {
        return value;
      }
    

    }

        10
  •  0
  •   xan    15 年前

    另一个非简单的解决方案。这是一个合并排序版本 stable 并且不会修改源数组,尽管合并需要额外的内存。

    public static int[] sortedIndices(double[] x) {
        int[] ix = new int[x.length];
        int[] scratch = new int[x.length];
        for (int i = 0; i < ix.length; i++) {
            ix[i] = i;
        }
        mergeSortIndexed(x, ix, scratch, 0, x.length - 1);
        return ix;
    }
    
    private static void mergeSortIndexed(double[] x, int[] ix, int[] scratch, int lo, int hi) {
        if (lo == hi)
            return;
        int mid = (lo + hi + 1) / 2;
        mergeSortIndexed(x, ix, scratch, lo, mid - 1);
        mergeSortIndexed(x, ix, scratch, mid, hi);
        mergeIndexed(x, ix, scratch, lo, mid - 1, mid, hi);
    }
    
    private static void mergeIndexed(double[] x, int[] ix, int[] scratch, int lo1, int hi1, int lo2, int hi2) {
        int i = 0;
        int i1 = lo1;
        int i2 = lo2;
        int n1 = hi1 - lo1 + 1;
        while (i1 <= hi1 && i2 <= hi2) {
            if (x[ix[i1]] <= x[ix[i2]])
                scratch[i++] = ix[i1++];
            else
                scratch[i++] = ix[i2++];
        }
        while (i1 <= hi1)
            scratch[i++] = ix[i1++];
        while (i2 <= hi2)
            scratch[i++] = ix[i2++];
        for (int j = lo1; j <= hi1; j++)
            ix[j] = scratch[j - lo1];
        for (int j = lo2; j <= hi2; j++)
            ix[j] = scratch[(j - lo2 + n1)];
    }
    
        11
  •  0
  •   Gaurav    14 年前
    //Here index array(of length equal to length of d array) contains the numbers from 0 to length of d array   
          public static Integer [] SortWithIndex(float[] data, Integer [] index)
        {
        int len = data.length;
        float temp1[] = new float[len];
        int temp2[] = new int[len];
    
    
    
             for (int i = 0; i <len; i++) {
    
    
                    for (int j = i + 1; j < len; j++) {
    
    
                      if(data[i]>data[j])
                      {
                        temp1[i] = data[i];
                        data[i] = data[j];
                        data[j] = temp1[i];
    
    
    
                        temp2[i] = index[i];
                        index[i] = index[j];
                        index[j] = temp2[i];
    
                        }
                      }
    
            }
    
            return index;
    
        }
    
        12
  •  0
  •   Ofek Ron    11 年前

    我会这样做:

    public class SortedArray<T extends Comparable<T>> {
        private final T[] tArray;
        private final ArrayList<Entry> entries;
    
        public class Entry implements Comparable<Entry> {
            public int index;
    
            public Entry(int index) {
                super();
                this.index = index;
            }
    
            @Override
            public int compareTo(Entry o) {
                return tArray[index].compareTo(tArray[o.index]);
            }
        }
    
        public SortedArray(T[] array) {
            tArray = array;
            entries = new ArrayList<Entry>(array.length);
            for (int i = 0; i < array.length; i++) {
                entries.add(new Entry(i));
            }
            Collections.sort(entries);
        }
    
        public T getSorted(int i) {
            return tArray[entries.get(i).index];
    
        }
    
        public T get(int i) {
            return tArray[i];
        }
    }
    
        13
  •  0
  •   Shravan Kumar    11 年前

    下面是基于插入排序的方法

    public static int[] insertionSort(float[] arr){
        int[] indices = new int[arr.length];
            indices[0] = 0;
            for(int i=1;i<arr.length;i++){
                int j=i;
                for(;j>=1 && arr[j]<arr[j-1];j--){
                        float temp = arr[j];
                        arr[j] = arr[j-1];
                        indices[j]=indices[j-1];
                        arr[j-1] = temp;
                }
                indices[j]=i;
            }
            return indices;//indices of sorted elements
     }
    
        14
  •  0
  •   Smart Du    9 年前

    我想用这个,因为它很快。但是我用它来表示int,你可以把它改成float。

    private static void mergeSort(int[]array,int[] indexes,int start,int end){
        if(start>=end)return;
        int middle = (end-start)/2+start;
        mergeSort(array,indexes,start,middle);
        mergeSort(array,indexes,middle+1,end);
        merge(array,indexes,start,middle,end);
    }
    private static void merge(int[]array,int[] indexes,int start,int middle,int end){
        int len1 = middle-start+1;
        int len2 = end - middle;
        int leftArray[] = new int[len1];
        int leftIndex[] = new int[len1];
        int rightArray[] = new int[len2];
        int rightIndex[] = new int[len2];
        for(int i=0;i<len1;++i)leftArray[i] = array[i+start];
        for(int i=0;i<len1;++i)leftIndex[i] = indexes[i+start];
        for(int i=0;i<len2;++i)rightArray[i] = array[i+middle+1];
        for(int i=0;i<len2;++i)rightIndex[i] = indexes[i+middle+1];
        //merge
        int i=0,j=0,k=start;
        while(i<len1&&j<len2){
            if(leftArray[i]<rightArray[j]){
                array[k] = leftArray[i];
                indexes[k] = leftIndex[i];
                ++i;
            }
            else{
                array[k] = rightArray[j];
                indexes[k] = rightIndex[j];
                ++j;
            }
            ++k;
        }
        while(i<len1){
            array[k] = leftArray[i];
            indexes[k] = leftIndex[i];
            ++i;++k;
        }
        while(j<len2){
            array[k] = rightArray[j];
            indexes[k] = rightIndex[j];
            ++j;++k;
        }
    }
    
        15
  •  -2
  •   Tom    16 年前

    我想最简单的方法是在数组创建时对其进行索引。您将需要键、值对。如果索引是一个单独的结构,那么我看不到没有其他对象的情况下如何进行索引(不过我对查看它很感兴趣)。