Сортируйте ArrayList, но сохраняйте порядок исходных индексов

#java #arrays #sorting #arraylist

#java #массивы #сортировка #список массивов

Вопрос:

Существует два массива int и список массивов под названием costs и shipping . Мне нужен ArrayList, который имеет наименьшую или наибольшую общую цену (например, стоимость [0] доставка [0]), но измените порядок двух массивов int в соответствии с ArrayList:

 Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
int[] costs = new int[n];
int[] shipping = new int[n];
ArrayList<Integer> totalCosts = new ArrayList<>();
 

Например, предположим, что затраты равны [1, 5, 2, 3], а доставка равна [2, 3, 2, 4], таким образом, totalCosts будет равен {3, 8, 4, 7} и будет отсортирован по {3, 4, 7, 8}. Я хочу, чтобы затраты и доставка были переупорядочены таким образом, чтобы они соответствовали итоговым значениям, поэтому затраты были бы [1, 2, 3, 5], а доставка была бы [2, 2, 4, 3].

Комментарии:

1. вы хотите этого и что вы пытались для этого сделать?

2. Это очень сложно сделать, так как вам нужно будет перемещать элементы массива в зависимости от того, как элементы списка перемещались при сортировке. Проще создать новый класс, который содержит затраты, доставку и общую цену. Затем составьте список из них и отсортируйте его.

3. Как правило, наличие нескольких коллекций, которые должны поддерживаться параллельно, является плохой идеей . Я бы создал List<Order> , где класс Order имеет 2 поля, стоимость и доставка, и метод getTotal , который их суммирует. Затем сортировка становится тривиальной List.sort , и поскольку данные логически группируются в объекты, индексы поддерживаются автоматически. Вы можете снова разбить это на новые массивы, если вам действительно нужно

4. Использование HashMap здесь может быть еще лучше, а затем просто вызвать sort на вашей карте, поскольку сортировка по умолчанию стабильна, проблем быть не должно!

Ответ №1:

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

 public static void main(String[] args) {
    int n = 4;
    int[] costs = {1, 5, 2, 3};
    int[] shipping = {2, 3, 2, 4};
    Integer[] totals = new Integer[n];
    for(int i = 0; i < n;   i) {
        totals[i] = i;
    }

    Arrays.sort(totals, Comparator.comparingInt(k -> (costs[k]   shipping[k])));

    int[] newCosts = new int[n];
    int[] newShipping = new int[n];
    for(int i = 0; i < n;   i) {
        newCosts[i] = costs[totals[i]];
        newShipping[i] = shipping[totals[i]];
    }

    for(int i = 0; i < n;   i){
        System.out.println((i   1)   ": Cost="   newCosts[i]   ", Shipping="   newShipping[i]   ", Total="   (newCosts[i]   newShipping[i]));
    }
}
 

Объяснение

Массив итогов содержит список индексов от 0 до n (4 в примере). Arrays.sort() сортирует индекс с помощью компаратора, который, учитывая индекс, извлекает итоговые данные для этой позиции. В массиве итогов после сортировки будет указан индекс сумм стоимости и доставки по порядку.

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

Редактировать

Следуя совету ttzn из комментариев, вы можете сжать код до этого:

 public static void main(String[] args) {
    int n = 4;
    int[] costs = {1, 5, 2, 3};
    int[] shipping = {2, 3, 2, 4};

    int[] totals = IntStream.range(0, n).boxed().sorted(Comparator.comparingInt(k -> (costs[(int)k]   shipping[(int)k]))).mapToInt(Integer::intValue).toArray();

    int[] newCosts = IntStream.of(totals).map(i -> costs[i]).toArray();
    int[] newShipping = IntStream.of(totals).map(i -> shipping[i]).toArray();

    for (int i = 0; i < n;   i) {
        System.out.println((i   1)   ": Cost="   newCosts[i]   ", Shipping="   newShipping[i]   ", Total="   (newCosts[i]   newShipping[i]));
    }
}
 

Комментарии:

1. Поскольку вы собираетесь использовать Java 8 comparingInt , вы могли бы также пойти int[] totals = IntStream.range(0, n) и int[] newCosts = IntStream.of(totals).map(i -> costs[i]).toArray();

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

Ответ №2:

 public List<Integer> sortPrices(int[] cost, int[] shipping) {
    List<Integer> result = new ArrayList<>();
    TreeMap<Integer, List<int[]>> map = new TreeMap<>();
    int length = cost.length;
    int[] origCost = new int[length];
    int[] origShipping = new int[length];
    for (int i = 0; i < length; i  ) {
        int price = cost[i]   shipping[i];
        int[] arr = {i, i};
        map.putIfAbsent(price, new ArrayList<>());
        map.get(price).add(arr);
        origCost[i] = cost[i];
        origShipping[i] = shipping[i];
    }
    int j = 0;
    for (int price : map.keySet()) {
        for (int[] arr : map.get(price)) {
            int ci = arr[0];
            int si = arr[1];
            cost[j] = origCost[ci];
            shipping[j] = origShipping[si];
            j  ;
            result.add(price);
        }
    }
    return resu<
}
 

Комментарии:

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

Ответ №3:

Я решил на python

просто поймите логику цикла, и тогда вы сможете это сделать.

 for i in range(len(total)):
  for j in range(len(total)):
    if sorted_total[i]==total[j]:
        new_costs[i]=costs[j]
        new_shipping[i]=shipping[j]
        print("i ",i)
        print("j ",j)
        break
 

Ответ №4:

Вот решение, основанное на потоке, без построения карты.

  1. A Stream<int[]> создается для хранения общего числа и индекса в int[] массиве,
  2. Поток сортируется с использованием пользовательского компаратора для этих массивов (сначала по сумме, затем по индексу)
  3. Намеренно используйте peek along with AtomicInteger для изменения порядка ввода costs и ships массивов
    Однако это может рассматриваться как «злоупотребление», усугубляемое побочным эффектом обновления данных вне потока
  4. Создайте и верните массив итогов.
 public static int[] getTotals(int[] costs, int[] ships) {
    assert costs.length == ships.length;
    int[] copyCosts = Arrays.copyOf(costs, costs.length);
    int[] copyShips = Arrays.copyOf(ships, ships.length);
    
    AtomicInteger ix = new AtomicInteger(0);
    
    return IntStream.range(0, costs.length)
                    .mapToObj(i -> new int[]{ costs[i]   ships[i], i })
                    .sorted(Comparator.<int[]>
                        comparingInt(p -> p[0])
                        .thenComparingInt(p -> p[1])
                    ) // get sorted Stream<int[]>
                    .peek(p -> {
                        costs[ix.get()] = copyCosts[p[1]];
                        ships[ix.getAndIncrement()] = copyShips[p[1]];
                    })
                    .mapToInt(p -> p[0])
                    .toArray();
}