#include<iostream>
#include<queue>
#include<vector>
using namespace std;
class node{
public:
int data;
node* next;
node(int d) {
data = d;
next = NULL;
}
};
void insertAtTail(node* &head, int data){
//if there is no data
if(head == NULL){
head = new node(data);
return;
}
//otherwise need to find tail point
node*tail = head;
while(tail->next != NULL){
tail = tail->next;
}
tail->next = new node(data);
return;
}
void printList(node *head) {
while(head != NULL) {
cout << head->data << " ";
head = head->next;
}
cout << endl;
}
// Comparison object to be used to order the min-heap
class compare{
public:
bool operator() (node *lhs, node *rhs) {
return lhs->data > rhs->data;
}
};
// The main function to merge given `k` sorted linked lists.
// It takes array `lists` of size `k` and generates the sorted output
node *mergeKSortedList(vector<node *> &lists) {
//create a empty min heap
priority_queue<node*, vector<node* >, compare> pq;
// push the first node of each list into the min-heap
for (node* list: lists) {
pq.push(list);
}
// take two pointers, head and tail, where the head points to the first node
// of the output list and tail points to its last node
node *head = NULL;
node *last = NULL;
// run till min-heap is empty
while (!pq.empty())
{
// extract the minimum node from the min-heap
node* min = pq.top();
pq.pop();
// add the minimum node to the output list
if (head == NULL) {
head = last = min;
}
else {
last->next = min;
last = min;
}
// take the next node from the "same" list and insert it into the min-heap
if (min->next != NULL) {
pq.push(min->next);
}
}
// return head node of the merged list
return head;
}
int main() {
int k;
int n;
cin >> k >> n;
// an array to store the head nodes of the linked lists
vector<node *> lists;
//input
for(int i=0;i<k;i++){
node * head = NULL;
for(int j=0;j<n;j++){
int x;
cin>>x;
insertAtTail(head,x);
}
lists.push_back(head);
}
node * ans = mergeKSortedList(lists);
printList(ans);
return 0;
}