Sortieren Sie Zahlen in einem Array, ohne die Position gerader Zahlen mit Java-8 zu ändern

8

Ich lerne Java 8-Streams. Sagen Sie mir bitte, wie kann ich eine sortArrayMethode kompakter schreiben ?

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
quelle

Antworten:

8

Man kann sich eine Lösung vorstellen wie: Zuerst extrahieren wir die ungeraden ganzen Zahlen aus der nonSorted[]und setzen sie stacksortiert auf.

Warum sollten wir die verwenden stack sortiert verwenden?

Das endgültige Array muss ungerade sortiert Integerswerden. Der Stapel folgt der FIFO-Richtlinie (First in Last Out).

Jetzt nehmen wir ein Instreamund lassen es von 0bis laufen nonSorted.length-1und überprüfen das Original nonSortedauf das Ungerade Integer; Sobald wir eines finden, ersetzen wir es durch das erste Element des Stapels und pop()das Element aus dem stack.

Hinweis: Man muss um den Stapel herumspielen, da nicht jedes Mal sortierte Elemente im Stapel benötigt werden, aber im Fall von OP ist dies zufällig der Fall.

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
quelle
2
Sieht viel besser aus, als ich mir vorstellen könnte. +1
Naman
1
Beeindruckend! es ist unglaublich! Stapel ist interessant, denke ich
Dmitry Shelemeh
2

Die Idee, eine sortierte zu verwenden, hat mir sehr gut gefallen Stack , hat , aber sie ist nicht leicht parallelisierbar und hat mich neugierig gemacht, wie ich das lösen kann.

Meine Idee ist es, Indizes von ungeraden Elementen zu sortieren. Abhängig von der Position des Index können wir bei der Erstellung des Ergebnisarrays unterscheiden, ob eine Zahl gerade ist oder nicht.

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();
}
Geflogen
quelle
1

Ich glaube, was Sie unter Java-8 verstehen, ist die Verwendung von Streams und anderen APIs, die seit dieser Version eingeführt wurden. Meiner Meinung nach haben Sie jedoch bereits einen sehr leistungsfähigen Code. Ich könnte mir vorstellen, das Problem wie folgt zu lösen:

  1. Finden Sie die ungeraden und geraden Zahlen und ihre Zuordnungen zu den aktuellen Indizes. So dass auch Werte mit ihren Indizes fest bleiben würden.

  2. Ordnen Sie die ungeraden Zahlen und ihre Indizes den Werten zu, indem Sie sie auf natürliche Weise sortieren.

  3. Sobald dies alles erledigt ist, führen Sie diese geteilten ungeraden-geraden Karten basierend auf den Indizes zusammen.

  4. Rufen Sie die Werte aus diesem zusammengeführten Ergebnis ab.

Die Gesamtimplementierung würde ungefähr so ​​aussehen -

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
quelle
0

Dies ist ein Einfügesortierversuch mit Streams. Das nonSortedArray wird gestreamt und zu a gesammelt new int[]. Wenn der Wert aus dem nonSortedArray gerade ist, wird er nur kopiert. Wenn er ungerade ist, wird eine Einfügesortierung nur für ungerade Werte ausgeführt, die bereits im Ergebnis vorhanden sind.

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
quelle