归并排序的K-way归并操作
K-way merge operation for merge sort
我有 k
个排序数组,每个数组有 n
个元素,需要将它们组合成一个包含 k*n
个元素的排序数组。
如何实现合并排序的合并过程,从前两个开始,然后是下一个等等?
这是我目前所拥有的。
// implementing function to merge arrays (merge procedure for merge sort)
public int[] merge(int[][] array){
int k = array.length;
int n = array[0].length;
// final merged array
int[] mergedArray = new int[k*n];
return mergedArray;
}
public static void main(String[]args){
Merge obj = new Merge();
int[][] data= new int[][]{{2, 9, 15, 20},
{6, 8, 9, 19},
{5, 10, 18, 22},
{8, 12, 15, 26}};
int[] mergedArrayTest = obj.merge(data);
//printArray(mergedArrayTest);
}
您可以一次合并所有 k
个子数组,而不是一次合并两个子数组。
- 在每个子数组中创建一个索引数组。最初每个索引都是零。
- 在填充合并数组的
k*n
次迭代中,考虑每个子数组在其各自索引处的值并记住最小值。 (如果索引已经到达子数组的末尾,则跳过该索引。)
- 增加指向最小值的索引。
这样做就可以了:
// k-way merge operation
public int[] merge(int[][] array){
int k = array.length;
int n = array[0].length;
int[] mergedArray = new int[k*n];
int[] indices = new int[k];
for (int i = 0; i < mergedArray.length; ++i) {
int bestValue = -1, bestIndex = -1;
for (int j = 0; j < indices.length; ++j) {
int index = indices[j];
if (index < n && (bestValue == -1 || array[j][index] < bestValue)) {
bestValue = array[j][index];
bestIndex = j;
}
}
mergedArray[i] = bestValue;
indices[bestIndex] += 1;
}
return mergedArray;
}
您可以通过删除已到达其子数组末尾的索引来提高此方法的效率。然而,这仍然在 O(nk2) 中留下 运行 时间,因为 O(k) 个索引被扫描 nk 次。
我们可以在 运行 时间内进行渐近改进,方法是将索引存储在使用每个索引处的值作为键的最小堆中。使用 k 索引,堆的大小永远不会超过 k。在每次 nk 迭代中,我们弹出堆并最多将一个元素推回。这些堆操作每个成本 O(log k),所以总 运行 时间是 O(nk log k).
import java.lang.*;
import java.util.*;
import java.io.*;
class Candidate {
int id, index, value;
Candidate(int id, int index, int value) {
this.id = id;
this.index = index;
this.value = value;
}
}
class Heap {
ArrayList<Candidate> stack = new ArrayList<Candidate>();
void push(Candidate current) {
// Add to last position in heap.
stack.add(current);
// Bubble up.
int n = stack.size(),
pos = n - 1;
while (pos != 0) {
int parent = (pos - 1) / 2;
if (stack.get(parent).value <= current.value) {
return;
}
stack.set(pos, stack.get(parent));
stack.set(parent, current);
}
}
Candidate pop() {
// Get top of heap.
if (stack.size() == 0) {
return null;
}
Candidate result = stack.get(0);
int n = stack.size();
if (n == 1) {
stack.remove(0);
return result;
}
// Swap last element to top.
stack.set(0, stack.get(--n));
Candidate current = stack.get(0);
stack.remove(n);
// Bubble down.
int pos = 0;
while (true) {
int left = 2 * pos + 1;
if (left >= n) {
return result;
}
int right = left + 1,
swapTo = -1;
if (current.value <= stack.get(left).value) {
if (right == n || current.value <= stack.get(right).value) {
return result;
}
swapTo = right;
} else {
if (right != n && stack.get(left).value > stack.get(right).value) {
swapTo = right;
} else {
swapTo = left;
}
}
stack.set(pos, stack.get(swapTo));
stack.set(swapTo, current);
pos = swapTo;
}
}
}
public class Merge {
// k-way merge
public int[] merge(int[][] array){
int k = array.length;
int n = array[0].length;
int[] mergedArray = new int[k*n];
// Initialize heap with subarray number, index, and value.
Heap indexHeap = new Heap();
for (int i = 0; i < k; ++i) {
indexHeap.push(new Candidate(i, 0, array[i][0]));
}
for (int i = 0; i < mergedArray.length; ++i) {
// Get the minimum value from the heap and augment the merged array.
Candidate best = indexHeap.pop();
mergedArray[i] = best.value;
// Increment the index. If it's still valid, push it back onto the heap.
if (++best.index < array[best.id].length) {
best.value = array[best.id][best.index];
indexHeap.push(best);
}
}
// Print out the merged array for testing purposes.
for (int i = 0; i < mergedArray.length; ++i) {
System.out.print(mergedArray[i] + " ");
}
System.out.println();
return mergedArray;
}
public static void main(String[]args){
Merge merge = new Merge();
int[][] data= new int[][]{{2, 9, 15, 20},
{6, 8, 9, 19},
{5, 10, 18, 22},
{8, 12, 15, 26}};
int[] mergedArrayTest = merge.merge(data);
}
}
我有 k
个排序数组,每个数组有 n
个元素,需要将它们组合成一个包含 k*n
个元素的排序数组。
如何实现合并排序的合并过程,从前两个开始,然后是下一个等等?
这是我目前所拥有的。
// implementing function to merge arrays (merge procedure for merge sort)
public int[] merge(int[][] array){
int k = array.length;
int n = array[0].length;
// final merged array
int[] mergedArray = new int[k*n];
return mergedArray;
}
public static void main(String[]args){
Merge obj = new Merge();
int[][] data= new int[][]{{2, 9, 15, 20},
{6, 8, 9, 19},
{5, 10, 18, 22},
{8, 12, 15, 26}};
int[] mergedArrayTest = obj.merge(data);
//printArray(mergedArrayTest);
}
您可以一次合并所有 k
个子数组,而不是一次合并两个子数组。
- 在每个子数组中创建一个索引数组。最初每个索引都是零。
- 在填充合并数组的
k*n
次迭代中,考虑每个子数组在其各自索引处的值并记住最小值。 (如果索引已经到达子数组的末尾,则跳过该索引。) - 增加指向最小值的索引。
这样做就可以了:
// k-way merge operation
public int[] merge(int[][] array){
int k = array.length;
int n = array[0].length;
int[] mergedArray = new int[k*n];
int[] indices = new int[k];
for (int i = 0; i < mergedArray.length; ++i) {
int bestValue = -1, bestIndex = -1;
for (int j = 0; j < indices.length; ++j) {
int index = indices[j];
if (index < n && (bestValue == -1 || array[j][index] < bestValue)) {
bestValue = array[j][index];
bestIndex = j;
}
}
mergedArray[i] = bestValue;
indices[bestIndex] += 1;
}
return mergedArray;
}
您可以通过删除已到达其子数组末尾的索引来提高此方法的效率。然而,这仍然在 O(nk2) 中留下 运行 时间,因为 O(k) 个索引被扫描 nk 次。
我们可以在 运行 时间内进行渐近改进,方法是将索引存储在使用每个索引处的值作为键的最小堆中。使用 k 索引,堆的大小永远不会超过 k。在每次 nk 迭代中,我们弹出堆并最多将一个元素推回。这些堆操作每个成本 O(log k),所以总 运行 时间是 O(nk log k).
import java.lang.*;
import java.util.*;
import java.io.*;
class Candidate {
int id, index, value;
Candidate(int id, int index, int value) {
this.id = id;
this.index = index;
this.value = value;
}
}
class Heap {
ArrayList<Candidate> stack = new ArrayList<Candidate>();
void push(Candidate current) {
// Add to last position in heap.
stack.add(current);
// Bubble up.
int n = stack.size(),
pos = n - 1;
while (pos != 0) {
int parent = (pos - 1) / 2;
if (stack.get(parent).value <= current.value) {
return;
}
stack.set(pos, stack.get(parent));
stack.set(parent, current);
}
}
Candidate pop() {
// Get top of heap.
if (stack.size() == 0) {
return null;
}
Candidate result = stack.get(0);
int n = stack.size();
if (n == 1) {
stack.remove(0);
return result;
}
// Swap last element to top.
stack.set(0, stack.get(--n));
Candidate current = stack.get(0);
stack.remove(n);
// Bubble down.
int pos = 0;
while (true) {
int left = 2 * pos + 1;
if (left >= n) {
return result;
}
int right = left + 1,
swapTo = -1;
if (current.value <= stack.get(left).value) {
if (right == n || current.value <= stack.get(right).value) {
return result;
}
swapTo = right;
} else {
if (right != n && stack.get(left).value > stack.get(right).value) {
swapTo = right;
} else {
swapTo = left;
}
}
stack.set(pos, stack.get(swapTo));
stack.set(swapTo, current);
pos = swapTo;
}
}
}
public class Merge {
// k-way merge
public int[] merge(int[][] array){
int k = array.length;
int n = array[0].length;
int[] mergedArray = new int[k*n];
// Initialize heap with subarray number, index, and value.
Heap indexHeap = new Heap();
for (int i = 0; i < k; ++i) {
indexHeap.push(new Candidate(i, 0, array[i][0]));
}
for (int i = 0; i < mergedArray.length; ++i) {
// Get the minimum value from the heap and augment the merged array.
Candidate best = indexHeap.pop();
mergedArray[i] = best.value;
// Increment the index. If it's still valid, push it back onto the heap.
if (++best.index < array[best.id].length) {
best.value = array[best.id][best.index];
indexHeap.push(best);
}
}
// Print out the merged array for testing purposes.
for (int i = 0; i < mergedArray.length; ++i) {
System.out.print(mergedArray[i] + " ");
}
System.out.println();
return mergedArray;
}
public static void main(String[]args){
Merge merge = new Merge();
int[][] data= new int[][]{{2, 9, 15, 20},
{6, 8, 9, 19},
{5, 10, 18, 22},
{8, 12, 15, 26}};
int[] mergedArrayTest = merge.merge(data);
}
}