Ordene números en una matriz sin cambiar la posición de los números pares usando Java-8

8

Estoy aprendiendo Java 8 streams. Dime por favor, ¿cómo puedo escribir un sortArraymétodo más compacto?

import org.junit.Test;    
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

import static org.junit.Assert.assertArrayEquals;

public class TestStream {

    /*
     * Sort numbers in an array without changing even numbers position
     */

    @Test
    public void test_1() {
        int[] nonSorted = new int[]{3, 4, 5, 2, 1, 6, 9, 8, 7, 0};
        int[] expected = new int[]{1, 4, 3, 2, 5, 6, 7, 8, 9, 0};

        Integer[] arr = sortArray(nonSorted);
        int[] sorted = new int[arr.length];
        for (int i = 0; i < arr.length; i++) {
            sorted[i] = arr[i];
        }

        assertArrayEquals(expected, sorted);
    }

    private Integer[] sortArray(int[] array) {
        Map<Integer, Integer> even = extractEven(array);
        Integer[] withoutEvens = removeEven(array);
        int length = even.size() + withoutEvens.length;
        Integer[] result = new Integer[length];
        Arrays.sort(withoutEvens);
        for (int i = 0; i < withoutEvens.length; i++) {
            result[i] = withoutEvens[i];
        }
        even.forEach((k, v) -> {
            System.arraycopy(result, k, result, k + 1, length - k - 1);
            result[k] = v;
        });

        return result;
    }


    private Map<Integer, Integer> extractEven(int[] array) {
        Map<Integer, Integer> map = new HashMap<>();
        for (int i = 0; i < array.length; i++) {
            if (array[i] % 2 == 0) {
                map.put(i, array[i]);
            }
        }

        return map;
    }

    private Integer[] removeEven(int[] array) {
        ArrayList<Integer> list = new ArrayList<Integer>();

        for (int i = 0; i < array.length; i++) {
            if (array[i] % 2 != 0) {
                list.add(array[i]);
            }
        }

        Integer[] a = new Integer[list.size()];
        return list.toArray(a);
    }
}
Dmitry Shelemeh
fuente

Respuestas:

8

Se puede pensar en una solución como: Primero extraemos los enteros impares del nonSorted[]y los ponemos de forma stackordenada.

¿Por qué deberíamos usar el stackde forma ordenada?

La matriz final debe clasificarse de forma impar Integers, la pila sigue la política FIFO (Primero en entrar , último en salir).

Ahora tomamos una Instreamy ejecutarlo desde 0a nonSorted.length-1y comprobar el original nonSortedpara el extraño Integer; Tan pronto como encontramos uno, lo reemplazamos con el primer elemento de la pila y pop()el elemento de stack.

Nota: Uno necesita jugar alrededor de la pila, ya que no siempre necesitará elementos ordenados en la pila, pero en el caso de OP esto sucede.

int[] nonSorted = new int[]{3, 4, 5, 2, 1, 6, 9, 8, 7, 0};

LinkedList<Integer> stack = Arrays.stream(nonSorted)
            .sorted().filter(s -> s % 2 != 0).boxed()
            .collect(Collectors.toCollection(LinkedList::new));

int[] expected = IntStream.rangeClosed(0, nonSorted.length - 1)
       .map(s -> nonSorted[s] % 2 != 0 ? stack.pop():nonSorted[s]) 
       .toArray();
Vishwa Ratna
fuente
2
Se ve mucho mejor de lo que podría pensar. +1
Naman
1
¡Guauu! ¡es asombroso! pila es interesante, creo
Dmitry Shelemeh
2

Realmente me gustó la idea de usar un ordenado Stack, pero no es fácilmente paralelizable y me dio curiosidad cómo resolverlo.

Mi idea es ordenar índices de elementos desiguales y, dependiendo de la posición del índice, podemos distinguir durante la creación de la matriz de resultados si un número es par o no.

public int[] sortUnevenElements(int[] nonSorted) {
    int[] unevenIndices = IntStream.range(0, nonSorted.length).filter(i -> nonSorted[i] % 2 != 0).toArray();
    int[] sortedUnevenIndices = Arrays.stream(unevenIndices, 0, unevenIndices.length).boxed()
          .sorted(Comparator.comparingInt(i -> nonSorted[i])).mapToInt(Integer::intValue).toArray();
    return IntStream.range(0, nonSorted.length).map(i -> {
        int idx = Arrays.binarySearch(unevenIndices, i);
        return idx >= 0 ? nonSorted[sortedUnevenIndices[idx]] : nonSorted[i];
    }).toArray();
}
Volado
fuente
1

Creo que lo que quiere decir con Java-8 es usar Streamsy otras API introducidas desde ese lanzamiento. Sin embargo, ya tienes un código de muy buen rendimiento en mi opinión. La forma en que podría pensar en resolver el problema es la siguiente:

  1. Encuentra los números pares e impares y sus asignaciones a los índices actuales. De modo que incluso los valores con sus índices permanecerían fijos.

  2. Sobre los números impares y sus índices, reasigne los valores ordenándolos naturalmente.

  3. Una vez hecho todo esto, combine estos mapas divididos pares e impares basados ​​en los índices.

  4. Recupere los valores de este resultado combinado.

La implementación general de esto sería algo así como:

private Integer[] sortArrayStream(Integer[] array) {
    Map<Boolean, Map<Integer, Integer>> evenOdds = IntStream.range(0, array.length)
            .boxed()
            .collect(Collectors.partitioningBy(i -> array[i] % 2 == 0,
                    Collectors.toMap(o -> o, i -> array[i]))); //1

    Map<Integer, Integer> oddSorted = remapWithSorting(evenOdds.get(Boolean.FALSE)); // 2

    Map<Integer, Integer> overall = new HashMap<>(evenOdds.get(Boolean.TRUE));
    overall.putAll(oddSorted); // part of 3

    return overall.entrySet().stream()
            .sorted(Map.Entry.comparingByKey()) // remaining of 3
            .map(Map.Entry::getValue) // 4
            .toArray(Integer[]::new); 
}


private Map<Integer, Integer> remapWithSorting(Map<Integer, Integer> initialIndexMapping) {
    List<Integer> oddIndexes = new ArrayList<>(initialIndexMapping.keySet());
    List<Integer> sortedOdds = initialIndexMapping.values().stream()
            .sorted().collect(Collectors.toList());
    return IntStream.range(0, sortedOdds.size())
            .boxed()
            .collect(Collectors.toMap(oddIndexes::get, sortedOdds::get));
}
Naman
fuente
0

Esta es una prueba de inserción con secuencias. La nonSortedmatriz se transmite y se recopila en a new int[]. Si el valor de la nonSortedmatriz es par, solo se copia, de lo contrario, si es impar, se ejecuta una clasificación de inserción solo para valores impares ya presentes en el resultado.

int[] sort = IntStream.range(0, nonSorted.length)
            .collect(() -> new int[nonSorted.length], (ints, i) -> {
                ints[i] = nonSorted[i];
                if (nonSorted[i] % 2 != 0) {
                    AtomicInteger current = new AtomicInteger(i);
                    IntStream.iterate(i - 1, 
                                     (v) -> current.get() > 0 && v >= 0,
                                     (v) -> --v)
                            .forEach(ind -> {
                                if (ints[ind] % 2 != 0) {
                                    if (ints[ind] > nonSorted[i]) {
                                        ints[current.get()] = ints[ind];
                                        ints[ind] = nonSorted[i];
                                        current.set(ind);
                                    } else {
                                        current.set(-1);
                                    }
                                }
                            });
                }
            }, (a1, a2) -> {
            });
pero_hero
fuente