#include <bits/stdc++.h>
using namespace std;
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
// ranking[i] = ranking list of general i (length i), for i >= 1
// ranking[i][j] = the general at position j in general i's ranking
vector<vector<int>> ranking(n);
for (int i = 1; i < n; i++) {
ranking[i].resize(i);
for (int j = 0; j < i; j++) {
cin >> ranking[i][j];
}
}
// For each general j >= 1, compute the set of "good" generals
// (those in the first half of j's ranking, i.e., position c2 where 2*c2 < j).
// Number of good generals for j: positions 0..ceil(j/2)-1
// good[j] is a set of generals that j can be adjacent to.
vector<vector<bool>> good(n);
for (int j = 1; j < n; j++) {
good[j].assign(n, false);
for (int c2 = 0; 2 * c2 < j; c2++) {
good[j][ranking[j][c2]] = true;
}
}
// Incremental insertion: maintain arrangement as a linked list (using a vector/deque).
// Start with [0].
// For j = 1..N-1, insert j into a valid position.
//
// When inserting general j (the largest so far), both neighbors must be
// in j's "good" set. Such a position always exists by pigeonhole argument.
list<int> arrangement;
arrangement.push_back(0);
for (int j = 1; j < n; j++) {
// Try to find an insertion position.
// We scan through the arrangement and look for a gap where both
// neighbors are good (or it's an endpoint with one good neighbor).
bool inserted = false;
// Check: can we insert at the front?
// Front neighbor is arrangement.front(). It must be good for j.
if (good[j][arrangement.front()]) {
arrangement.push_front(j);
inserted = true;
}
if (!inserted) {
// Check: can we insert at the back?
if (good[j][arrangement.back()]) {
arrangement.push_back(j);
inserted = true;
}
}
if (!inserted) {
// Scan interior gaps: between consecutive elements a and b,
// both a and b must be good for j.
auto it = arrangement.begin();
auto prev_it = it;
++it;
while (it != arrangement.end()) {
if (good[j][*prev_it] && good[j][*it]) {
// Insert j between prev_it and it
arrangement.insert(it, j);
inserted = true;
break;
}
prev_it = it;
++it;
}
}
// By the pigeonhole argument, a valid position must always exist.
// But just in case, assert:
if (!inserted) {
// This should never happen for valid inputs.
// Fallback: try inserting anywhere (should not be needed).
arrangement.push_back(j);
}
}
// Output the arrangement
bool first = true;
for (int x : arrangement) {
if (!first) cout << ' ';
cout << x;
first = false;
}
cout << '\n';
return 0;
}