Java 使用mergeSort中的Merge算法合并大小为n的K个排序数组
问题:给定K个大小为N的排序数组,合并它们并打印排序后的输出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
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()