Java-8を使用して、偶数の位置を変更せずに配列の数値を並べ替える


8

Java 8ストリームを学習しています。plsに教えてくださいsortArray。メソッドをよりコンパクトに書くにはどうすればよいですか?

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);
    }
}

回答:


8

次のようなソリューションを考えることができます。まず、から奇数の整数を抽出し、nonSorted[]それらをstackソートされた方法でに配置します。

なぜstackソートされた方法で使用する必要がありますか?

最終的な配列は奇数Integersベースでソートする必要があり、スタックはFIFO(先入れ先出し)ポリシーに従います。

今、私たちが取るInstreamから、それを実行0するnonSorted.length-1と、元のチェックnonSorted奇数のためにInteger、すぐに私たちがものを見つけると、私たちは、スタックの最初の要素として、それを置き換えるpop()から要素stack

注:スタック内の要素を並べ替える必要があるたびにスタックを再生する必要はありませんが、OPの場合はそうなります。

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();

2
私が思いつくよりもはるかに良く見えます。+1
ナマン

1
うわー!すごい!スタックは面白いと思います
Dmitry Shelemeh

2

並べ替えを使用するというアイデアは本当に気に入りましたが、Stack簡単に並列化することはできず、解決方法を知りたくなりました。

私の考えは、不均一な要素のインデックスを並べ替え、インデックスの位置に応じて、結果の配列の作成時に、数値が偶数かそうでないかを区別できるようにすることです。

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

1

Java-8とはStream、そのリリース以降に導入されたやその他のAPIを使用しているということです。私の意見では、すでに非常に優れたコードを使用しています。私が問題を分解すると考えることができる方法は次のとおりです-

  1. 奇数と偶数、およびそれらの現在のインデックスへのマッピングを見つけます。インデックスを持つ値でさえ固定されたままになるように。

  2. 奇数とそのインデックスに基づいて、値を再マッピングして自然にソートします。

  3. これがすべて完了したら、インデックスに基づいて、これらの分割された奇偶マップをマージします。

  4. このマージされた結果から値を取得します。

これの全体的な実装は次のようになります-

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));
}

0

これは、ストリームを使用した挿入ソートトライアルです。nonSortedアレイにストリーミングされて回収されますnew int[]nonSorted配列の値が偶数の場合はコピーされるだけで、それ以外の場合は奇数の場合、結果にすでに存在する奇数の値に対してのみ挿入ソートが実行されます。

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) -> {
            });
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.