import java.util.*;


/**
* @author Lenovo
*/
public class Solution {

private static List<Deque<Integer>> data = new ArrayList<>();

private static Integer[]loser;

private static Integer[] b;

public static void main(String[] args) {
int[]init_data = new int[]{4,17,46,6,14,55,28,31,49,9,21,47,2,10,52,26,27,38,3,22,59,17,25,33,7};
kMerge(init_data, 8);
}

public static void kMerge(int[]init_data, int k){

loser = new Integer[k];
b = new Integer[k + 1];

int index;
int average = init_data.length/k;
int[]array ;
//1. 构造归并段
for(index=0;index<k;index++) {
if(index<k-1) {
array = Arrays.copyOfRange(init_data, index * average, (index + 1) * average);
}else{
array = Arrays.copyOfRange(init_data, index * average, init_data.length);
}
Arrays.sort(array);
Deque<Integer> tmp = new ArrayDeque<>(array.length);
for (int i : array) {
tmp.offer(i);
}
data.add(tmp);
}

for(index =0;index<k;index++){
b[index] = getNextVal(data, index);
}
b[index] = Integer.MIN_VALUE;

//2.构造败者树
for(index =0;index<k;index++){
loser[index] = k;
}

//3.调整败者树
for(index=k-1; index>=0;index--){
adjust(loser, index);
}

//4. 遍历顺序
StringBuilder sb = new StringBuilder();
int indexOfMin;
while( b[loser[0]] !=Integer.MAX_VALUE){
indexOfMin = loser[0];

sb.append( b[indexOfMin]).append(" ");
data.get(indexOfMin).poll();

b[indexOfMin] = getNextVal(data, indexOfMin);

adjust(loser, indexOfMin);
}
System.out.println(sb);
}

/**
* 败者树的调整 从叶子结点到根结点进行调整
* 败者树存储在loser[1] ~ loser[k-1] 之间
* @param loser 败者树
* @param indexOfParagraph 归并段的下标
*/
private static void adjust(Integer[]loser, int indexOfParagraph){
int k = loser.length;
int fa = (k + indexOfParagraph) >> 1;
while(fa>0){
// 比父节点对应的归并段的首个元素大 就是 新的失败者
if( b[indexOfParagraph] > b[loser[fa]] ){
int tmp = loser[fa];
loser[fa] = indexOfParagraph ;
indexOfParagraph = tmp;
}
fa = fa >> 1;
}
loser[0] = indexOfParagraph;
}

/**
* 获取归并段第一个元素
* @param lst
* @param index
* @return
*/
private static Integer getNextVal(List<Deque<Integer>>lst, int index){
if(index >= lst.size()||index<0) {
return Integer.MAX_VALUE;
}
Integer i = lst.get(index).peekFirst();
return i!=null?i:Integer.MAX_VALUE;
}
}