Сортировка совпадающих массивов в Java

Допустим, у меня есть два массива (на Java),

int [] числа; и int [] цвета;

Каждому i-му элементу чисел соответствует его i-й элемент по цвету. Например, числа = {4,2,1} цвета = {0x11, 0x24, 0x01}; Означает, что номер 4 - это цвет 0x11, номер 2 - это 0x24 и т. д.

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

Бывший. числа = {1,2,4}; цвета = {0x01,0x24,0x11};

Какой самый чистый и простой способ сделать это? Массивы содержат несколько тысяч элементов, поэтому было бы лучше, но не обязательно. Имеет ли смысл использовать Arrays.sort () и собственный компаратор? Предпочтительно использовать как можно больше библиотечных функций.

Примечание. Я знаю, что «лучшее» решение - создать класс для двух элементов и использовать собственный компаратор. Этот вопрос предназначен для того, чтобы спросить людей о самом быстром способе написания кода. Представьте, что вы участвуете в соревновании по программированию, и вам не захочется создавать все эти дополнительные классы, анонимные классы для компаратора и т. д. А еще лучше забудьте про Java; как бы вы запрограммировали это на C?

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
24
0
11 138
13

Ответы 13

Вам нужно отсортировать массив цветов по его относительному элементу в массиве чисел. Укажите компаратор, который сравнивает числа, и используйте его как сравнение для массива цветов.

Почему бы не ввести объект для представления числа и цвета и не реализовать для этого функцию компаратора?

Кроме того, вам действительно нужен массив, почему бы не использовать что-то производное от Collection?

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

user16773 22.09.2008 01:37

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

class Color implements Comparable {
  private int number;
  private int color;

  // (snip ctor, setters, etc.)

  public int getNumber() {
    return number;
  }
  public int getColor() {
    return color;
  }

  public int compareTo(Color other) {
    if (this.getNumber() == other.getNumber) {
      return 0;
    } else if (this.getNumber() > other.getNumber) {
      return 1;
    } else {
      return -1;
    }
  }
}

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

Да, это то, что вы бы сделали в обычной ситуации или приложениях. Но на чем-то вроде соревнований по программированию, где вам нужно так быстро писать код, держу пари, вы бы не захотели писать эту лишнюю ерунду.

user16773 22.09.2008 01:41

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

Frank Pape 22.09.2008 01:44

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

ykaganovich 22.09.2008 07:58

Если вы хотите выделить дополнительное пространство, вы можете сгенерировать другой массив, назовите его extra, с такими элементами:

extra = [0,1,...,numbers.length-1]

Затем вы можете отсортировать этот дополнительный массив, используя Arrays.sort () с настраиваемым компаратором (который при сравнении элементов i и j действительно сравнивает числа [extra [i]] и числа [extra [j]]). Таким образом, после сортировки дополнительного массива extra [0] будет содержать индекс наименьшего числа и, поскольку числа и цвета не перемещаются, соответствующий цвет. Это не очень хорошо, но работа выполняется, и я не могу придумать более простого способа сделать это.

Кстати, на соревнованиях я обычно считаю, что пары шаблонов C++ и красивые карты незаменимы;)

Кстати, вам нужен массив Integer, а не int, чтобы иметь возможность использовать настраиваемый компаратор. Основные типы можно сортировать только в соответствии с их естественным порядком, если вы ограничены Arrays.sort.

Torsten Marek 22.09.2008 02:04

Ой, извините за это. В последнее время я немного подустал с Java.

finrod 22.09.2008 02:28

Вы можете использовать sort () с настраиваемым компаратором, если вы сохранили третий массив с индексом и отсортировали его, оставив данные нетронутыми.

Пример кода Java:

Integer[] idx = new Integer[numbers.length];
for( int i = 0 ; i < idx.length; i++ ) idx[i] = i;              
Arrays.sort(idx, new Comparator<Integer>() {
    public int compare(Integer i1, Integer i2) {                        
        return Double.compare(numbers[i1], numbers[i2]);
    }                   
});

// numbers[idx[i]] is the sorted number at index i
// colors[idx[i]] is the sorted color at index i

Обратите внимание, что вам нужно использовать Integer вместо int, иначе вы не можете использовать собственный компаратор.

Это самый быстрый способ сделать это ... он не чистый, но самый быстрый для кодирования.

billjamesdev 22.09.2008 02:48

Мне нравится решение @tovare. Сделайте массив указателей:

int ptr[] = { 1, 2, 3 };

а затем при сортировке по числам меняйте местами значения в ptr, а не в числах. Затем доступ через массив ptr, например

for (int i = 0; i < ptr.length; i++)
{
   printf("%d %d\n", numbers[ptr[i]], colors[ptr[i]]);
}

Обновление: хорошо, похоже, другие меня опередили. У меня нет XP.

Достаточно ли написать собственный метод сортировки? Простая пузырьковая сортировка, вероятно, будет быстро закодирована (и будет правильной). Нет необходимости в дополнительных классах или компараторах.

Запоминание Дональда. Д. Кнут / Искусство компьютерного программирования, том 3 - сортировка, поиск и реализация оптимального алгоритма на лету были бы ... впечатляющими! :-)

tovare 22.09.2008 02:22

Один из быстрых способов - объединить два массива с помощью битовых сдвигов. Составьте массив длинных чисел так, чтобы 32 старших бита были числом, а младшие 32 - цветом. Воспользуйтесь методом сортировки, а затем распакуйте.

Самый простой способ сделать это в C - это пузырьковая сортировка + двойные указатели. Конечно, самым быстрым будет быстрая сортировка + два указателя. Конечно, второй указатель поддерживает корреляцию между двумя массивами.

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

Пример, иллюстрирующий использование третьего массива индексов. Не уверен, что это лучшая реализация.


import java.util.*;

public class Sort {

    private static void printTable(String caption, Integer[] numbers, 
                Integer[] colors, Integer[] sortOrder){

        System.out.println(caption+
                "\nNo   Num   Color"+
                "\n----------------");

        for(int i=0;i<sortOrder.length;i++){
            System.out.printf("%x    %d     %d\n", 
                    i,numbers[sortOrder[i]],colors[sortOrder[i]]);

        }
    }


    public static void main(String[] args) {

        final Integer[] numbers = {1,4,3,4,2,6};
        final Integer[] colors  = {0x50,0x34,0x00,0xfe,0xff,0xff};
        Integer[] sortOrder = new Integer[numbers.length];

        // Create index array.
        for(int i=0; i<sortOrder.length; i++){
            sortOrder[i] = i;
        }
        printTable("\nNot sorted",numbers, colors, sortOrder);

        Arrays.sort(sortOrder,new Comparator<Integer>() {   
            public int compare(Integer a, Integer b){
                return numbers[b]-numbers[a];
            }});
        printTable("\nSorted by numbers",numbers, colors, sortOrder);

        Arrays.sort(sortOrder,new Comparator<Integer>() {   
            public int compare(Integer a, Integer b){
                return colors[b]-colors[a];
            }});
        printTable("\nSorted by colors",numbers, colors, sortOrder);
    }
}

Результат должен выглядеть так:


Not sorted
No   Num   Color
----------------
0    1     80
1    4     52
2    3     0
3    4     254
4    2     255
5    6     255

Sorted by numbers
No   Num   Color
----------------
0    6     255
1    4     52
2    4     254
3    3     0
4    2     255
5    1     80

Sorted by colors
No   Num   Color
----------------
0    6     255
1    2     255
2    4     254
3    1     80
4    4     52
5    3     0

Используйте TreeMap

Благодарим @tovare за лучший исходный ответ.

Мой ответ ниже удаляет (теперь) ненужное автобоксирование через зависимость Maven {net.mintern: primitive: 1.2.2} из этого ответа: https://stackoverflow.com/a/27095994/257299

int[] idx = new int[numbers.length];
for( int i = 0 ; i < idx.length; i++ ) idx[i] = i;
final boolean isStableSort = false;
Primitive.sort(idx,
               (i1, i2) -> Double.compare(numbers[i1], numbers[i2]),
               isStableSort);

// numbers[idx[i]] is the sorted number at index i
// colors[idx[i]] is the sorted color at index i

Я предполагаю, что вам нужна оптимизация производительности, пытаясь избежать использования массива объектов (что может вызвать болезненное событие GC). К сожалению, общего решения нет, подумал. Но для вашего конкретного случая, когда числа отличаются друг от друга, может быть создано только два массива.

/**
 * work only for array of different numbers
 */
private void sortPairArray(int[] numbers, int[] colors) {
    int[] tmpNumbers = Arrays.copyOf(numbers, numbers.length);
    int[] tmpColors = Arrays.copyOf(colors, colors.length);
    Arrays.sort(numbers);
    for (int i = 0; i < tmpNumbers.length; i++) {
        int number = tmpNumbers[i];
        int index = Arrays.binarySearch(numbers, number); // surely this will be found
        colors[index] = tmpColors[i];
    }
}

Два отсортированных массива можно заменить на Int2IntOpenHashMap, который выполняет более быстрый запуск, но использование памяти может быть удвоено.

Другие вопросы по теме