#include <algorithm>
#include <array>
#include <bitset>
#include <cassert>
#include <climits>
#include <cmath>
#include <complex>
#include <cstdio>
#include <cstring>
#include <deque>
#include <functional>
#include <iomanip>
#include <locale>
#include <iostream>
#include <map>
#include <memory>
#include <new>
#include <numeric>
#include <queue>
#include <random>
#include <set>
#include <sstream>
#include <string>
#include <tuple>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>

#define REP(i,n) for(int i=0;i<(int)(n);i++)
#define ALL(x) (x).begin(),(x).end()

using namespace std;

using ll = long long;
using ld = long double;

template <typename T> T &chmin(T &a, const T &b) { return a = min(a, b); }
template <typename T> T &chmax(T &a, const T &b) { return a = max(a, b); }
template <typename T> int len(const T &x) { return x.size(); }

template<typename T>
vector<T> table(int n, T v) { return vector<T>(n, v); }

template <class... Args>
auto table(int n, Args... args) {
  auto val = table(args...);
  return vector<decltype(val)>(n, move(val));
}

struct yes_no : numpunct<char> {
  string_type do_truename()  const { return "YES"; }
  string_type do_falsename() const { return "NO"; }
};

template <class Monoid>
class SegmentTree {
  using T = typename Monoid::type;
  const int sz, n;
  std::vector<T> data;
  int expand(int n) const { return n == 1 ? n : expand((n + 1) / 2) * 2; }
public:
  SegmentTree(const std::vector<T> &init) :
    sz(init.size()), n(expand(sz)), data(n * 2, Monoid::id()) {
    std::copy(begin(init), end(init), begin(data) + n);
    for (int i = n - 1; i >= 0; --i) {
      data[i] = Monoid::op(data[i * 2 + 0], data[i * 2 + 1]);
    }
  }
  SegmentTree(const int n, const T &init = Monoid::id()) :
    SegmentTree(std::vector<T>(n, init)) {}
  int size() const { return sz; }
  void update(int p, T val) {
    assert (0 <= p && p < sz); // assertion
    data[p += n] = val;
    while (p /= 2) data[p] = Monoid::op(data[p * 2], data[p * 2 + 1]);
  }
  T query(int l, int r) const {
    assert (0 <= l && l <= r && r <= sz); // assertion
    l += n; r += n;
    T res1 = Monoid::id(), res2 = Monoid::id();
    while (l != r) {
      if (l % 2) res1 = Monoid::op(res1, data[l++]);
      if (r % 2) res2 = Monoid::op(data[--r], res2);
      l /= 2; r /= 2;
    }
    return Monoid::op(res1, res2);
  }
};

struct RMQ {
  using type = pair<int,int>;
  static type id() { return make_pair(INT_MAX, INT_MAX); }
  static type op(const type &l, const type &r) { return min(l, r); }
};

using P = pair<int, int>;
using T = tuple<P, P, P, P>;

T calc(const SegmentTree<RMQ> &even, const SegmentTree<RMQ> &odd, P section) {
  int l, r; tie(l, r) = section;
  P lx = (l % 2 == 0 ? even : odd).query(l, r);
  P rx = (l % 2 == 0 ? odd : even).query(lx.second + 1, r);
  return make_tuple(make_pair(lx.first, rx.first),
                    make_pair(l, lx.second),
                    make_pair(lx.second + 1, rx.second),
                    make_pair(rx.second + 1, r));
}

void solve(ll N, vector<ll> p) {
  SegmentTree<RMQ> even(N), odd(N);
  REP(i,N) {
    (i % 2 == 0 ? even : odd).update(i, make_pair(p[i], i));
  }
  priority_queue<T, vector<T>, greater<T>> que;
  que.push(calc(even, odd, make_pair(0, N)));
  vector<int> res;
  while (!que.empty()) {
    P val, a, b, c;
    tie(val, a, b, c) = que.top();
    que.pop();
    res.push_back(val.first);
    res.push_back(val.second);
    if (a.first < a.second) que.push(calc(even, odd, a));
    if (b.first < b.second) que.push(calc(even, odd, b));
    if (c.first < c.second) que.push(calc(even, odd, c));
  }
  REP(i,N) cout << res[i] << " \n"[i == N - 1];
}

int main() {
  locale loc(locale(), new yes_no);
  cout << boolalpha << setprecision(12) << fixed;
  cout.imbue(loc);
	ll N;
	scanf("%lld", &N);
	vector<ll> p(N-1+1);
	for (int i = 0 ; i <= N-1 ; i++) {
	  scanf("%lld", &p[i]);
	}
	solve(N, p);
	return 0;
}


Measured memory_bytes: 16100