Java
 Computer >> コンピューター >  >> プログラミング >> Java

Javaでk個のソート済み配列をマージする方法|優先度付きキューを使った効率的な実装


本記事では、「n」個の配列が与えられる状況を想定します。ここでは例として、整数型の3つの配列 arr1[]、arr2[]、arr3[] を扱います。課題は、与えられたすべての整数配列を、実行時に結果の配列がソート済みの状態になるようにマージすることです。

具体例で理解しよう

例1

入力:

int a[] = {21, 22, 23, 24};
int b[] = {28, 31, 35};

出力:

int resultant[] = {21, 22, 23, 24, 28, 31, 35};

解説: 各配列の要素は結果配列に追加される前に互いに比較され、それぞれ適切な位置へと挿入されます。

例2

入力:

int a[] = {1, 3, 5, 7, 9, 11, 13};
int b[] = {14, 16, 18};
int c[] = {19, 20, 21, 22};

出力:

int resultant[] = {1, 3, 5, 7, 9, 11, 13, 14, 16, 18, 19, 20, 21, 22};

解説: 例1と同様に、要素は追加前に比較され、結果配列内の正しい位置に配置されます。

プログラムで使用するアプローチ

この問題は優先度付きキュー(PriorityQueue)、すなわちミンヒープを利用することで効率的に解決できます。手順は以下の通りです。

  • 3つの整数配列 arr1[]、arr2[]、arr3[] と結果配列 result[] を用意し、mergeSortedArray(new int[][] { arr1, arr2, arr3 }) を呼び出します。
  • メソッド mergeSortedArray() 内部の処理は次の通りです。
    • PriorityQueue 型の変数 queue と、全要素数を表す変数 total を宣言し、total を 0 で初期化します。
    • for ループで i を 0 から配列の個数まで回し、各配列を ArrayBucket オブジェクトとしてキューに追加するとともに、total に各配列の長さを加算します。
    • m を 0 に初期化し、結果格納用の整数配列 result[] を total のサイズで生成します。
    • キューが空でない間(queue.isEmpty() == false)、queue.poll() で最小値を持つバケットを取り出し、result[m++] に ac.arr[ac.index] を代入します。さらに、ac.index が ac.arr.length - 1 より小さい場合は、次のインデックスを指す新しい ArrayBucket をキューに追加します。
    • 最終的に result を返します。

Javaコード例

import java.util.Arrays;
import java.util.PriorityQueue;

class ArrayBucket implements Comparable<ArrayBucket> {
    int[] arr;
    int index;

    public ArrayBucket(int[] arr, int index) {
        this.arr = arr;
        this.index = index;
    }

    @Override
    public int compareTo(ArrayBucket o) {
        return this.arr[this.index] - o.arr[o.index];
    }
}

public class testClass {
    public static int[] mergeSortedArray(int[][] arr) {
        PriorityQueue<ArrayBucket> queue = new PriorityQueue<ArrayBucket>();
        int total = 0;
        for (int i = 0; i < arr.length; i++) {
            queue.add(new ArrayBucket(arr[i], 0));
            total = total + arr[i].length;
        }
        int m = 0;
        int result[] = new int[total];
        while (!queue.isEmpty()) {
            ArrayBucket ac = queue.poll();
            result[m++] = ac.arr[ac.index];
            if (ac.index < ac.arr.length - 1) {
                queue.add(new ArrayBucket(ac.arr, ac.index + 1));
            }
        }
        return result;
    }

    public static void main(String[] args) {
        int[] arr1 = { 1, 3, 5, 7 };
        int[] arr2 = { 2, 4, 6, 8 };
        int[] arr3 = { 0, 9, 10, 11 };
        int[] result = mergeSortedArray(new int[][] { arr1, arr2, arr3 });
        System.out.println("The final merged sorted array is :- " + Arrays.toString(result));
    }
}

実行結果

上記のコードを実行すると、以下の出力が得られます。

The final merged sorted array is :- [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]

計算量の目安

このアプローチの時間計算量は O(N log k) です(N は全要素数の合計、k は配列の個数)。各要素ごとにヒープへの挿入・削除が O(log k) で行われるため、全要素を連結してからソートする O(N log N) の方法よりも、配列数 k が小さい場合に効率的に動作します。


  1. Pythonでソート済み配列をマージする方法

    問題の概要2つのソート済み配列AとBが与えられたとき、それらをマージして1つのソート済み配列Cを作成することを考えます。なお、両者のサイズは異なっていても構いません。例えば、A = [1,2,4,7]、B = [1,3,4,5,6,8] の場合、マージ後のリストCは [1,1,2,3,4,4,5,6,7,8] となります。アルゴリズムの手順この問題を解くには、以下の手順に従います。i := 0、j := 0、end := Aの長さ − 1 を定義しますend >= 0 かつ A[end] が空(0)である間、end を 1 ずつ減らしていきますj が Bの長さ未満である間、以下の処理を繰

  2. Pythonのheapqモジュールを使って2つのソート済みリストをマージする方法

    この記事では、Pythonのheapqモジュールを使用して、2つのソート済みリストを1つにマージする方法を解説します。例えば、list1 = [10, 20, 30, 40]とlist2 = [100, 200, 300, 400, 500]という2つのリストがある場合、マージ後は[10, 20, 30, 40, 100, 200, 300, 400, 500]のような結果が得られます。heapqモジュールとはheapqはPythonに標準で搭載されているライブラリモジュールのため、追加のインストールは不要です。利用する前にインポートするだけで使えます。import heapqheapqモジュ