Java 使用mergeSort中的Merge算法合并大小为n的K个排序数组

Java 使用mergeSort中的Merge算法合并大小为n的K个排序数组,java,algorithm,sorting,mergesort,Java,Algorithm,Sorting,Mergesort,问题:给定K个大小为N的排序数组,合并它们并打印排序后的输出 Sample Input-1: K = 3, N = 4 arr[][] = { {1, 3, 5, 7}, {2, 4, 6, 8}, {0, 9, 10, 11}} ; Sample Output-1: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] 我知道有一种方法可以使用优先级队列/min堆来解决这个问题,但我想使用mergeSo

问题:给定K个大小为N的排序数组,合并它们并打印排序后的输出

Sample Input-1:

K = 3, N =  4

arr[][] = { {1, 3, 5, 7},

            {2, 4, 6, 8},

            {0, 9, 10, 11}} ;


Sample Output-1: 

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
我知道有一种方法可以使用优先级队列/min堆来解决这个问题,但我想使用mergeSort中的merge过程来解决这个问题。这个想法似乎很简单……在每次迭代中,将剩余的数组合并成两个一组,这样在每次迭代中数组的数量就会减半

然而,当减半导致奇数时,这就成了问题。 我的想法是,每当减半导致奇数时,我们通过将额外的数组与上次合并形成的数组合并来处理它

到目前为止,我的代码如下。这只适用于30个测试用例中的一个,但是:

static int[] mergeArrays(int[][] arr) {
        int k = arr.length;
        int n = arr[0].length;
        if(k < 2){
            return arr[0];
        }

        boolean odd_k;
        if(k%2){
            odd_k = false;
        }
        else{
            odd_k = true;
        }

        while(k > 1){
            int o;
            if(odd_k){
                o = (k/2) + 1;
            }
            else{
                o = k/2;
            }
            int[][] out = new int[o][];

            for(int i=0; i < k; i = i + 2){
                int[] a;
                int[] b;
                if(odd_k && i == (k-1)){
                    b = arr[i];
                    b = out[i-1];
                }
                else{
                    a = arr[i];
                    b = arr[i+1];
                }
                out[i] = mergeTwo(a, b);
            }
            k = k/2;
            if(k % 2 == 0){
                odd_k = false;
            }
            else{
                odd_k = true;
            }

            arr = out;
        }
        return arr[0];

    }

    static int[] mergeTwo(int[] a, int[] b){
        int[] c = new int[a.length + b.length];
        int i, j, k;
        i = j = k = 0;
       while(i < a.length && j < b.length){
           if(a[i] < b[j]){
               c[k] = a[i];
               i++;
               k++;
           }
           else{
               c[k] = b[j];
               j++; k++;
            }
       }
       if(i < a.length){
           while(i < a.length){
               c[k] = a[i];
               i++; k++;
           }
       }
       if(j < b.length){
           while(j < b.length){
               c[k] = b[j];
               j++; k++;
           }
       }
       return c;
    }
静态int[]合并数组(int[]arr){
int k=阵列长度;
int n=arr[0]。长度;
if(k<2){
返回arr[0];
}
布尔奇数;
如果(k%2){
奇数k=假;
}
否则{
奇数k=真;
}
而(k>1){
INTO;
如果(奇数){
o=(k/2)+1;
}
否则{
o=k/2;
}
int[]out=新的int[o][];
对于(int i=0;i
我们可以缩短您的
mergeTwo
实施

static int[] mergeTwo(int[] a, int[] b) {
    int[] c = new int[a.length + b.length];
    int i = 0, j = 0, k = 0; // declare and initialize on one line
    while (i < a.length && j < b.length) {
        if (a[i] <= b[j]) {
            c[k++] = a[i++]; // increment and assign
        } else {
            c[k++] = b[j++]; // increment and assign
        }
    }
    // No need for extra if(s)
    while (i < a.length) {
        c[k++] = a[i++];
    }
    while (j < b.length) {
        c[k++] = b[j++];
    }
    return c;
}
然后我测试了它

public static void main(String[] args) {
    int arr[][] = { { 1, 3, 5, 7 }, { 2, 4, 6, 8 }, { 0, 9, 10, 11 } };
    System.out.println(Arrays.toString(mergeArrays(arr)));
}
我得到了(如预期的那样)


正如您所说,您一次合并了两个阵列。由于效率低下,您可以同时合并所有子阵列。你要做的是从每个子数组中找出最小值,并记住该元素的位置


为此,我们可以使用另一个数组(比如curPos)来记住当前位置

 private int[] merge(int[][] arr) 
{
    int K = arr.length;
    int N = arr[0].length;

    /** array to keep track of non considered positions in subarrays **/
    int[] curPos = new int[K];

    /** final merged array **/
    int[] mergedArray = new int[K * N];
    int p = 0;

    while (p < K * N)
    {
        int min = Integer.MAX_VALUE;
        int minPos = -1;
        /** search for least element **/
        for (int i = 0; i < K; i++)
        {
            if (curPos[i] < N)
            {
                if (arr[i][curPos[i]] < min)
                {
                    min = arr[i][curPos[i]];
                    minPos = i;
                }
            }                
        }
        curPos[minPos]++;            
        mergedArray[p++] = min;
    }
    return mergedArray;
private int[]merge(int[]arr)
{
int K=阵列长度;
int N=arr[0]。长度;
/**阵列以跟踪子阵列中未考虑的位置**/
int[]curPos=新的int[K];
/**最终合并数组**/
int[]mergedArray=新int[K*N];
int p=0;
而(p
处理此问题的最简单方法可能是使用数组队列。首先,将所有数组添加到队列中。然后,从队列中删除前两个数组,合并它们,并将结果数组添加到队列中。继续执行此操作,直到队列中只有一个数组。类似于:

for each array in list of arrays
    queue.push(array)

while queue.length > 1
    a1 = queue.pop()
    a2 = queue.pop()
    a3 = merge(a1, a2)
    queue.push(a3)

result = queue.pop()

这就简化了很多事情,“减半”的问题就消失了。

if(k%2)
b=out[i-1]
可能是您做错了。如果您愿意,一次可以合并两个以上的数组。当然,这需要在每个数组中建立索引,并通过循环找到下一个要放入合并结果的元素。@OleV.V.-对于非外部排序,只进行双向合并几乎没有什么好处。请参阅我对Tharaka Ratnayake的评论答:这是我最初的实现,但如果你仔细观察,它是没有效率的。它将一个合并的输出与下一个合并的输出合并,所以它将是O(k2n)。尝试两人一组进行,所以它是O(kn log k).@ohbrobig-尽管忽略循环开销,时间复杂度更好,但两种方法的比较和移动次数大致相同。对于您建议的方法,您可以为每次传递创建一个矩阵[(arr.length+1)/2][arr[0].length*2],合并偶数行和奇数行(或者在没有行可合并的情况下复制一行(奇数行情况))进入该矩阵,然后重复使用该矩阵作为下一个过程的输入。对于非外部排序,进行2路合并没有多大好处。操作数量大致相同。例如,在典型的内部循环中,2路合并对移动的每个元素进行1次比较和1次移动,而4路合并对移动的每个元素进行3次比较和1次移动对于移动的每个元素,ove只占移动次数的1/2,因此4路使用移动次数的1/2倍,但比较次数的3/2倍,操作次数大致相同。如果使用堆进行k路合并,堆的开销会降低速度。搜索最小元素的技术效率非常低。您正在执行对每个元素进行线性搜索。结果是你的算法是O(k*n*k),而OP的算法是O(k*n*log(k))。如果你愿意的话
 private int[] merge(int[][] arr) 
{
    int K = arr.length;
    int N = arr[0].length;

    /** array to keep track of non considered positions in subarrays **/
    int[] curPos = new int[K];

    /** final merged array **/
    int[] mergedArray = new int[K * N];
    int p = 0;

    while (p < K * N)
    {
        int min = Integer.MAX_VALUE;
        int minPos = -1;
        /** search for least element **/
        for (int i = 0; i < K; i++)
        {
            if (curPos[i] < N)
            {
                if (arr[i][curPos[i]] < min)
                {
                    min = arr[i][curPos[i]];
                    minPos = i;
                }
            }                
        }
        curPos[minPos]++;            
        mergedArray[p++] = min;
    }
    return mergedArray;
for each array in list of arrays
    queue.push(array)

while queue.length > 1
    a1 = queue.pop()
    a2 = queue.pop()
    a3 = merge(a1, a2)
    queue.push(a3)

result = queue.pop()