Сортировать несколько массивов одновременно "на месте" - PullRequest
0 голосов
/ 14 мая 2018

У меня есть следующие 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, затем отсортировать myClass, используя x.Однако для этого потребуются дополнительные пробелы.Мне интересно, можно ли сделать in place сортировку?Спасибо!

Ответы [ 10 ]

0 голосов
/ 28 мая 2018

Вы также можете достичь по-своему.

Здесь я создал 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;
    }
}
0 голосов
/ 26 мая 2018

без присвоения значений в классе, вы можете достичь этого с помощью следующего кода:

    Integer[] indexes = new Integer[]{0,2,8,5};
    String[] sources = new String[]{"how", "are", "today", "you"};
    String[] targets = new String[]{"I", "am", "thanks", "fine"};
    Integer[] sortedArrya = Arrays.copyOf(indexes, indexes.length);
    Arrays.sort(sortedArrya);
    String[] sortedSourses = new String[sources.length];
    String[] sortedTargets = new String[targets.length];
    for (int i = 0; i < sortedArrya.length; i++) {
        int intValus = sortedArrya[i];
        int inx = Arrays.asList(indexes).indexOf(intValus);
        sortedSourses[i] = sources[+inx];
        sortedTargets[i] = targets[+inx];
    }
    System.out.println(sortedArrya);
    System.out.println(sortedSourses);
    System.out.println(sortedTargets);
0 голосов
/ 28 мая 2018

У меня есть другое решение для вашего вопроса:

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));
    }
0 голосов
/ 24 мая 2018

Другое решение, использующее 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);

[чтобы быть или не быть]

Почему я использовалList.Главным образом, чтобы упростить переупорядочение, если мы попытаемся упорядочить исходные массивы, это будет сложно, поскольку нам нужно удалить противоположный ключ / пару

2 -> 3
3 -> 2

Без некоторой очистки мы просто поменяем ячейкив два раза ... так что никаких изменений не будет.

Если мы хотим немного уменьшить использование памяти, мы можем создать другой массив вместо использования потока и копировать значения для значений, повторяя карту.Это можно было бы также сделать с несколькими параллельными массивами.

0 голосов
/ 24 мая 2018

Интересно, правильный ли мой подход.

    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

0 голосов
/ 21 мая 2018

Все зависит от размера ваших массивов.Это решение будет использовать первый массив для выполнения сортировки, но будет выполнять перестановку для нескольких массивов.
Таким образом, это может иметь некоторые проблемы с производительностью, если используемому алгоритму сортировки потребуется много перестановок.

Здесь,Я взял базовый алгоритм сортировки, по которому я добавил некоторые действия, которые я могу выполнить во время обмена двумя ячейками.Это позволяет использовать некоторые лямбда-выражения для замены нескольких массивов одновременно на основе одного массива.

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 лямбда (толькоработает для непримитивных массивов):

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 с той же логикой, что и раньше, для каждой перестановки в исходном массиве мы выполняем 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);
                }
            }
        }
    }
}

Давайте также создадим Bubble Sorter:

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 (ограничение по размеру из-за тайм-аута)

0 голосов
/ 15 мая 2018

В этом примере требуется создать массив индексов 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]);
    }
}
0 голосов
/ 15 мая 2018

Три способа сделать это

1.Использование компаратора (требуется 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.Использование лямбды (нужен 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]
0 голосов
/ 14 мая 2018

Могу ли я предложить вам использовать 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);
}
0 голосов
/ 14 мая 2018

Это возможно, хотя это не так просто, как кажется. Есть два варианта:

  1. написать собственный алгоритм сортировки , где функция подстановки для двух элементов также меняет местами элементы в других массивах.

    AFAIK нет способа расширить стандарт Array.sort таким образом, чтобы он заменял дополнительные массивы.

  2. Использовать вспомогательный массив с порядком сортировки.

    • Прежде всего вам нужно инициализировать вспомогательный массив диапазоном {0, 1 ... indexes.Length-1}.

    • Теперь вы сортируете вспомогательный массив , используя Comparator, который сравнивает indexes[a] с indexes[b] вместо a с b. Результатом является вспомогательный массив, в котором каждый элемент имеет индекс элемента исходного массива, из которого должно поступить его содержимое, то есть последовательность сортировки.

    • Последний шаг самый хитрый. Вам нужно поменять местами элементы в ваших исходных массивах в соответствии с последовательностью сортировки, приведенной выше .
      Для работы строго на месте установите текущий индекс cur на 0.
      Затем возьмите cur -й элемент из вашего вспомогательного массива. Давайте назовем это from. Это индекс элемента, который должен быть помещен в индекс cur после завершения.
      Теперь вам нужно освободить место в индексе cur, чтобы поместить туда элементы из индекса from. Скопируйте их во временную папку tmp.
      Теперь переместите элементы из индекса from в индекс cur. Индекс from теперь можно переопределять.
      Установите элемент в массиве помощников с индексом cur на какое-то недопустимое значение, например, -1.
      Установите текущий индекс cur на from и продолжайте сверху, пока не достигнете элемента в массиве помощников, у которого уже есть недопустимое значение индекса, то есть ваша начальная точка. В этом случае сохраните содержимое tmp в последнем индексе. Теперь вы нашли замкнутый цикл повернутых индексов.
      К сожалению, может существовать произвольное количество таких циклов, каждый из которых имеет произвольный размер. Таким образом, вам нужно искать во вспомогательном массиве следующее недопустимое значение индекса и снова продолжать сверху, пока все элементы вспомогательного массива не будут обработаны. Поскольку вы заканчиваете в начальной точке после каждого цикла, достаточно увеличить cur, если вы не найдете недопустимую запись. Таким образом, алгоритм все еще O (n) при обработке вспомогательного массива. Все записи до cur обязательно недействительны после завершения цикла.
      Если cur превышает размер вспомогательного массива, который вы сделали.

  3. Существует более простой вариант варианта 2, когда вам разрешено создавать новые целевые массивы.
    В этом случае вы просто выделяете новые целевые массивы и заполняете их содержимое в соответствии с индексами в вашем вспомогательном массиве.
    Недостатком является то, что распределение может быть довольно дорогим, если массивы действительно велики. И, конечно, он уже не на месте .


Некоторые дополнительные заметки.

  • Обычно алгоритм пользовательской сортировки работает лучше, так как избегает выделения временного массива. Но в некоторых случаях ситуация меняется. Обработка циклических циклов вращения элемента использует операций минимального перемещения . Это O (n), а не O (n log n) общих алгоритмов сортировки. Поэтому, когда количество массивов для сортировки и / или размер массивов растут, метод №2 имеет преимущество, поскольку использует меньше операций подкачки.

  • Модель данных, для которой требуется алгоритм сортировки, подобный этому, в основном нарушена по проекту. Конечно, как всегда, есть несколько случаев, когда вы не можете избежать этого.

...