同时对多个数组进行排序"到位"

时间:2018-05-14 18:27:12

标签: java arrays sorting in-place

我有以下3个阵列:

int[] indexes = new int[]{0,2,8,5};
String[] sources = new String[]{"how", "are", "today", "you"};
String[] targets = new String[]{"I", "am", "thanks", "fine"};

我想根据索引对三个数组进行排序:

indexes -> {0,2,5,8}
sources -> {"how", "are", "you", "today"}
targets -> {"I", "am",  "fine",  "thanks"}

我可以创建一个包含所有三个元素的新类myClass

class myClass {
    int x;
    String source;
    String target;
}

将所有内容重新分配给myClass,然后使用myClassx进行排序。但是,这需要额外的空间。我想知道是否可以进行in place排序?谢谢!

10 个答案:

答案 0 :(得分:16)

三种方法

<强> 1。使用Comparator(需要Java 8 plus)

import java.io.*;
import java.util.*;

class Test {

public static String[] sortWithIndex (String[] strArr, int[] intIndex )
    {
     if (! isSorted(intIndex)){
        final List<String> stringList = Arrays.asList(strArr);
        Collections.sort(stringList, Comparator.comparing(s -> intIndex[stringList.indexOf(s)]));
        return stringList.toArray(new String[stringList.size()]);
       }
     else
        return strArr;
    }

public static boolean isSorted(int[] arr) {
    for (int i = 0; i < arr.length - 1; i++) {
        if (arr[i + 1] < arr[i]) {
            return false;
        };
    }
    return true;
}       


// Driver program to test function.
    public static void main(String args[])
    {
        int[] indexes = new int[]{0,2,8,5};
        String[] sources = new String[]{"how", "are", "today", "you"};
        String[] targets = new String[]{"I", "am", "thanks", "fine"};   
        String[] sortedSources = sortWithIndex(sources,indexes);
        String[] sortedTargets = sortWithIndex(targets,indexes);
        Arrays.sort(indexes);
        System.out.println("Sorted Sources " + Arrays.toString(sortedSources) + " Sorted Targets " + Arrays.toString(sortedTargets)  + " Sorted Indexes " + Arrays.toString(indexes));
    }
}

<强>输出

Sorted Sources [how, are, you, today] Sorted Targets [I, am, fine, thanks] Sorted Indexes [0, 2, 5, 8]

<强> 2。使用Lambda(需要Java 8 plus)

import java.io.*;
import java.util.*;

public class Test {

public static String[] sortWithIndex (String[] strArr, int[] intIndex )
    {

  if (! isSorted(intIndex)) {
        final List<String> stringList = Arrays.asList(strArr);
        Collections.sort(stringList, (left, right) -> intIndex[stringList.indexOf(left)] - intIndex[stringList.indexOf(right)]);
        return stringList.toArray(new String[stringList.size()]);
  }
  else 
    return strArr;
    }

public static boolean isSorted(int[] arr) {
    for (int i = 0; i < arr.length - 1; i++) {
        if (arr[i + 1] < arr[i]) {
            return false;
        };
    }
    return true;
}  

// Driver program to test function.
public static void main(String args[])
{
    int[] indexes = new int[]{0,2,5,8};
    String[] sources = new String[]{"how", "are", "today", "you"};
    String[] targets = new String[]{"I", "am", "thanks", "fine"};   
    String[] sortedSources = sortWithIndex(sources,indexes);
    String[] sortedTargets = sortWithIndex(targets,indexes);
    Arrays.sort(indexes);
    System.out.println("Sorted Sources " + Arrays.toString(sortedSources) + " Sorted Targets " + Arrays.toString(sortedTargets)  + " Sorted Indexes " + Arrays.toString(indexes));
}

}

第3。使用列表和地图并避免多次调用(如上面的第二个解决方案)到方法来对单个数组进行排序

import java.util.*;
import java.lang.*;
import java.io.*;

public class Test{

    public static <T extends Comparable<T>> void sortWithIndex( final List<T> key, List<?>... lists){
        // input validation
        if(key == null || lists == null)
            throw new NullPointerException("Key cannot be null.");

        for(List<?> list : lists)
            if(list.size() != key.size())
                throw new IllegalArgumentException("All lists should be of the same size");

        // Lists are size 0 or 1, nothing to sort
        if(key.size() < 2)
            return;

        // Create a List of indices
        List<Integer> indices = new ArrayList<Integer>();
        for(int i = 0; i < key.size(); i++)
            indices.add(i);

        // Sort the indices list based on the key
        Collections.sort(indices, new Comparator<Integer>(){
            @Override public int compare(Integer i, Integer j) {
                return key.get(i).compareTo(key.get(j));
            }
        });

        Map<Integer, Integer> swapMap = new HashMap<Integer, Integer>(indices.size());
        List<Integer> swapFrom = new ArrayList<Integer>(indices.size()),
                      swapTo   = new ArrayList<Integer>(indices.size());

        // create a mapping that allows sorting of the List by N swaps.
        for(int i = 0; i < key.size(); i++){
            int k = indices.get(i);
            while(i != k && swapMap.containsKey(k))
                k = swapMap.get(k);

            swapFrom.add(i);
            swapTo.add(k);
            swapMap.put(i, k);
        }

        // use the swap order to sort each list by swapping elements
        for(List<?> list : lists)
            for(int i = 0; i < list.size(); i++)
                Collections.swap(list, swapFrom.get(i), swapTo.get(i));
    }

    public static void main (String[] args) throws java.lang.Exception{

      List<Integer> index = Arrays.asList(0,2,8,5);
      List<String> sources = Arrays.asList("how", "are", "today", "you");
      // List Types do not need to be the same
      List<String> targets  = Arrays.asList("I", "am", "thanks", "fine");

      sortWithIndex(index, index, sources, targets);

      System.out.println("Sorted Sources " + sources + " Sorted Targets " + targets  + " Sorted Indexes " + index);


    }
}

<强>输出

Sorted Sources [how, are, you, today] Sorted Targets [I, am, fine, thanks] Sorted Indexes [0, 2, 5, 8]

答案 1 :(得分:7)

虽然它不像它看起来那么容易。有两种选择:

  1. 编写自己的排序算法,其中两个元素的交换函数也交换其他数组中的元素。

    AFAIK无法以交换其他数组的方式扩展标准Array.sort

  2. 使用带排序顺序的辅助数组。

    • 首先,您需要初始化范围为{0, 1 ... indexes.Length-1}的辅助数组。

    • 现在您使用Comparatorindexes[a]indexes[b]而不是a进行比较{{1}来对辅助数组进行排序 }}。 结果是一个辅助数组,其中每个元素都有源数组元素的索引,其内容应该来自,即排序顺序。

    • 最后一步是最棘手的一步。您需要根据上面的排序顺序交换源数组中的元素 要严格按照操作,请将当前索引b设置为cur 然后从辅助数组中取出0 - th元素。我们称之为cur。这是完成后应放在索引from的元素索引 现在,您需要在索引cur处创建空间,以便将索引cur中的元素放在那里。将它们复制到临时位置from 现在将元素从索引tmp移动到索引from。索引cur现在可以自由覆盖 将索引from的辅助数组中的元素设置为某个无效值,例如cur
      将当前索引-1设置为cur从上面开始,直到到达辅助数组中已经具有无效索引值(即起始点)的元素。在这种情况下,将from的内容存储在最后一个索引处。您现在已经找到了旋转指数的闭环 不幸的是,可能存在任意数量的这种循环,每个循环具有任意大小。因此,您需要在辅助数组中寻找下一个非无效索引值,并再次从上面继续,直到处理辅助数组的所有元素。 由于您将在每个循环后的起始点结束,因此除非您发现非无效条目,否则增加tmp就足够了。所以在处理辅助数组时算法仍然是O(n)。 循环完成后,cur之前的所有条目必然无效 如果cur增量超出辅助数组的大小,则表示已完成。

  3. 当您被允许创建新的目标数组时,选项2的变化会更容易 在这种情况下,您只需分配新的目标数组并根据辅助数组中的索引填充其内容 缺点是如果阵列非常大,分配可能会非常昂贵。当然,它不再是到位

  4. 进一步说明。

    • 通常,自定义排序算法执行得更好,因为它避免了临时数组的分配。但在某些情况下情况会发生变化。循环元素旋转循环的处理使用最小移动操作。这是常见排序算法的O(n)而不是O(n log n)。 因此,当要排序的数组的数量和/或数组的大小增加时,方法#2具有优势,因为它使用较少的交换操作。

    • 需要这样的排序算法的数据模型大多是设计破坏的。当然,像往常一样,有些情况下你无法避免这种情况。

答案 2 :(得分:3)

我建议您使用TreeMap或类似的东西,使用整数作为密钥。

static Map<Integer, myClass> map = new TreeMap<>();

所以当你想要检索有序时,你只需要做一个for循环或任何你喜欢的。

for (int i : map.keyset()){
    System.out.println("x: "+map.get(i).x+"\nsource: "+map.get(i).source+"\ntarget: "+map.get(i).target);
}

答案 3 :(得分:3)

此示例需要创建一个Integer索引数组,但要排序的数组将根据array1重新排序,并且数组可以是允许索引的任何类型(基元或对象)。

public static void main(String[] args) {
    int array1[]={5,1,9,3,8}; 
    int array2[]={2,0,3,6,1};
    int array3[]={3,1,4,5,9};
    // generate array of indices
    Integer[] I = new Integer [array1.length];
    for(int i = 0; i < I.length; i++)
        I[i] = i;
    // sort array of indices according to array1
    Arrays.sort(I, (i, j) -> array1[i]-array1[j]);
    // reorder array1 ... array3 in place using sorted indices
    // also reorder indices back to 0 to length-1
    // time complexity is O(n)
    for(int i = 0; i < I.length; i++){
        if(i != I[i]){
            int t1 = array1[i];
            int t2 = array2[i];
            int t3 = array3[i];
            int j;
            int k = i;
            while(i != (j = I[k])){
                array1[k] = array1[j];
                array2[k] = array2[j];
                array3[k] = array3[j];
                I[k] = k;
                k = j;
            }
            array1[k] = t1;
            array2[k] = t2;
            array3[k] = t3;
            I[k] = k;
        }
    }
    // display result
    for (int i = 0; i < array1.length; i++) {
        System.out.println("array1 " + array1[i] +
           " array2 " + array2[i] +
           " array3 " + array3[i]);
    }
}

答案 4 :(得分:2)

使用Collection的另一种解决方案(增加内存使用量):

让我们创建一个有序地图,它只是正确索引和原始位置之间的映射:

public static TreeMap<Integer, Integer> sortIndex(int[] array){
    TreeMap<Integer, Integer> tree = new TreeMap<>();
    for(int i=0; i < array.length; ++i) {
        tree.put(array[i], i);
    }   
    return tree;
}

测试:

int[] indexes = new int[] { 0, 1, 3, 2, 4, 5 };
TreeMap<Integer, Integer> map = sortIndex(indexes);

map.keySet().stream().forEach(System.out::print); //012345
map.values().stream().forEach(System.out::print); //013245

我们将索引排序(在键上)和原始索引顺序作为值。

不,我们可以简单地使用它来订购数组,我会非常激动并使用Stream来映射并收集到List

public static List<String> sortInPlace(String[] array, TreeMap<Integer, Integer> map) {
    return map.values().stream().map(i -> array[i]).collect(Collectors.toList());
}

测试:

String[] sources = "to be not or to be".split(" ");

int[] indexes = new int[] { 0, 1, 3, 2, 4, 5 };
TreeMap<Integer, Integer> map = sortIndex(indexes);

List<String> result = sortInPlace(sources, map);
System.out.println(result);
  

[to,be,or,not,to,be]

为什么我使用List。主要是为了简化重新排序,如果我们尝试订购原始数组,它会很复杂,因为我们需要删除相反的键/对

2 -> 3
3 -> 2

如果没有一些清洁,我们只会将细胞交换两次......所以不会有任何变化。

如果我们想减少一点内存使用量,我们可以创建另一个数组,而不是使用流和复制迭代地图的值的值。这也可以并行处理多个数组。

答案 5 :(得分:1)

这完全取决于数组的大小。此解决方案将使用第一个数组执行排序,但将在多个数组上执行排列 因此,如果使用的排序算法需要大量的排列,这可能会产生一些性能问题。

在这里,我采用了一种基本的排序算法,我在其中添加了一些在两个单元格交换期间可以执行的操作。这允许使用定义一些lambda来基于一个数组同时交换多个数组。

public static void sortArray( int[] array, BiConsumer<Integer, Integer>... actions ) {
    int tmp;
    for ( int i = 0, length = array.length; i < length; ++i ) {
        tmp = array[i];
        for ( int j = i + 1; j < length; ++j ) {
            if ( tmp > array[j] ) {
                array[i] = array[j];
                array[j] = tmp;
                tmp = array[i];

                // Swap the other arrays
                for ( BiConsumer<Integer, Integer> cons : actions ){
                    cons.accept( i,  j);
                }
            }
        }
    }
}

让我们创建一个通用方法来交换我们可以作为BiConsumer lambda传递的单元格(仅适用于非基本数组):

public static <T> void swapCell( T[] array, int from, int to ) {
    T tmp = array[from];
    array[from] = array[to];
    array[to] = tmp;
}

允许使用对数组进行排序,如:

public static void main( String[] args ) throws ParseException {
    int[] indexes = new int[] { 0, 2, 8, 5 };
    String[] sources = new String[] { "how", "are", "today", "you" };
    String[] targets = new String[] { "I", "am", "thanks", "fine" };

    sortArray( indexes,
            ( i, j ) -> swapCell( sources, i, j ),
            ( i, j ) -> swapCell( targets, i, j ) );

    System.out.println( Arrays.toString( indexes ) );
    System.out.println( Arrays.toString( sources ) );
    System.out.println( Arrays.toString( targets ) );
}
  

[0,2,5,8]
  [怎么样,你,今天]   [我,我,很好,谢谢]

此解决方案不需要(多)内存比已使用的内存更多,因为不需要额外的数组或Collection

BiConsumer<>...的使用提供了一个通用解决方案,这也可以接受Object[]...,但这不再适用于基元数组。当然,这会有轻微的性能损失,因此根据需要,可以将其删除。

创建完整的解决方案,首先让我们定义一个将用作工厂的界面:

interface Sorter {
    void sort(int[] array, BiConsumer<Integer, Integer>... actions);

    static void sortArrays(int[] array, BiConsumer<Integer, Integer>... actions){
        // call the implemented Sorter
    }
}

然后,使用与以前相同的逻辑实现一个简单的Selection sorterr,对于原始数组中的每个排列,我们执行BiConsumer

class SelectionSorter implements Sorter {
    public void sort(int[] array, BiConsumer<Integer, Integer>... actions) {
        int index;
        int value;
        int tmp;
        for (int i = 0, length = array.length; i < length; ++i) {
            index = i;
            value = array[i];
            for (int j = i + 1; j < length; ++j) {
                if (value > array[j]) {
                    index = j;
                    value = array[j];
                }
            }

            if (index != i) {
                tmp = array[i];
                array[i] = array[index];
                array[index] = tmp;

                // Swap the other arrays
                for (BiConsumer<Integer, Integer> cons : actions) {
                    cons.accept(i, index);
                }
            }
        }
    }
}

还要创建一个冒泡分拣机:

class BubbleSorter implements Sorter {
    public void sort(int[] array, BiConsumer<Integer, Integer>... actions) {
        int tmp;

        boolean swapped;
        do {
            swapped = false;
            for (int i = 1, length = array.length; i < length; ++i) {
                if (array[i - 1] > array[i]) {
                    tmp = array[i];
                    array[i] = array[i - 1];
                    array[i - 1] = tmp;

                    // Swap the other arrays
                    for (BiConsumer<Integer, Integer> cons : actions) {
                        cons.accept(i, i - 1);
                    }

                    swapped = true;
                }
            }
        } while (swapped);
    }
}

现在,我们可以根据一个简单的条件简单地调用其中一个,长度:

static void sortArrays(int[] array, BiConsumer<Integer, Integer>... actions){
    if(array.length < 1000){
        new BubbleSorter().sort(array, actions);
    } else {
        new SelectionSorter().sort(array, actions);
    }
}

这样,我们可以使用

简单地调用我们的分拣机
Sorter.sortArrays(indexes, 
    (i, j) -> swapCell(sources, i, j), 
    (i, j) -> swapCell(targets, i, j)
);

ideone上的完整测试用例(由于超时而限制了大小)

答案 6 :(得分:1)

我想知道我的方法是否有效。

    public class rakesh{

    public static void sort_myClass(myClass myClasses[]){
        for(int i=0; i<myClasses.length; i++){
            for(int j=0; j<myClasses.length-i-1; j++){
                if(myClasses[j].x >myClasses[j+1].x){
                    myClass temp_myClass = new myClass(myClasses[j+1]);
                    myClasses[j+1] = new myClass(myClasses[j]);
                    myClasses[j] = new myClass(temp_myClass);
                }
            }
        }
    }

    public static class myClass{
        int x;
        String source;
        String target;
        myClass(int x,String source,String target){
          this.x = x;
          this.source = source;
          this.target = target;
        }
        myClass(myClass super_myClass){
            this.x = super_myClass.x;
            this.source = super_myClass.source;
            this.target = super_myClass.target;
        }
    }

    public static void main(String args[]) {
        myClass myClass1 = new myClass(0,"how","I");
        myClass myClass2 = new myClass(2,"are","am");
        myClass myClass3 = new myClass(8,"today","thanks");
        myClass myClass4 = new myClass(5,"you","fine");
        myClass[] myClasses = {myClass1, myClass2, myClass3, myClass4};

        sort_myClass(myClasses);

        for(myClass myClass_dummy : myClasses){
            System.out.print(myClass_dummy.x + " ");
        }
        System.out.print("\n");
        for(myClass myClass_dummy : myClasses){
            System.out.print(myClass_dummy.source + " ");
        }
        System.out.print("\n");
        for(myClass myClass_dummy : myClasses){
            System.out.print(myClass_dummy.target + " ");
        }
    }


}

如果您发现任何错误或有任何建议,请发表评论,以便进行必要的修改。

<强>输出

  

0 2 5 8
  你今天过得怎么样?   我很好,谢谢你   处理以退出代码0结束

答案 7 :(得分:1)

在课堂上没有赋值,您可以使用以下代码实现它:

input.error {
   border: 1px solid red;
}

答案 8 :(得分:1)

我还有另一个问题的解决方案:

private void reOrder(int[] indexes, String[] sources, String[] targets){
        int[] reIndexs = new int[indexes.length]; // contain index of item from MIN to MAX
        String[] reSources = new String[indexes.length]; // array sources after re-order follow reIndexs
        String[] reTargets = new String[indexes.length]; // array targets after re-order follow reIndexs
        for (int i=0; i < (indexes.length - 1); i++){
            if (i == (indexes.length - 2)){
                if (indexes[i] > indexes[i+1]){
                    reIndexs[i] = i+1;
                    reIndexs[i+1] = i;
                }else
                {
                    reIndexs[i] = i;
                    reIndexs[i+1] = i+1;
                }
            }else
            {
                for (int j=(i+1); j < indexes.length; j++){
                    if (indexes[i] > indexes[j]){
                        reIndexs[i] = j;
                    }else {
                        reIndexs[i] = i;
                    }
                }
            }
        }

        // Re-order sources array and targets array
        for (int index = 0; index < reIndexs.length; index++){
            reSources[index] = sources[reIndexs[index]];
            reTargets[index] = targets[reIndexs[index]];
        }

        // Print to view result
        System.out.println( Arrays.toString(reIndexs));
        System.out.println( Arrays.toString(reSources));
        System.out.println( Arrays.toString(reTargets));
    }

答案 9 :(得分:1)

你也可以以自己的方式实现。

在这里,我创建了一个ArrayList myArr并根据索引值进行了排序,然后如果您对ArrayList满意,则将其转换回数组,只是您可以删除转换,或者您希望Array对此有所帮助。

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;

public class StackOverflow {
    public static void main(String[] args) {
        int[] indexes = new int[]{0,2,8,5};
        String[] sources = new String[]{"how", "are", "today", "you"};
        String[] targets = new String[]{"I", "am", "thanks", "fine"};
        ArrayList<myClass> myArr=new ArrayList<>();
        for(int i=0;i<indexes.length;i++) {
            myArr.add(new myClass(indexes[i], sources[i], targets[i]));
        }
        //Collections.sort(myArr,new compareIndex()); 
        // Just for readability of code 
        Collections.sort(myArr, (mC1, mC2) -> mC1.getX() - mC2.getX());

       //Conversion Part
       for (int i=0;i<myArr.size();i++){
           indexes[i]=myArr.get(i).getX();
           sources[i]=myArr.get(i).getSource();
           targets[i]=myArr.get(i).getTarget();
       }

        System.out.println(Arrays.toString(indexes));
        System.out.println(Arrays.toString(sources));
        System.out.println(Arrays.toString(targets));

    }
}
class myClass {
    private Integer x;
    private String source;
    private String target;
    public myClass(Integer x,String source,String target){
        this.x=x;
        this.source=source;
        this.target=target;
    }

    public Integer getX() {
        return x;
    }

    public String getSource() {
        return source;
    }

    public String getTarget() {
        return target;
    }
}