#include <bits/stdc++.h>
#pragma GCC optimize("Ofast")
using namespace std;
using i64 = long long;
using i32 = int;
using f64 = long double;
template <class T>
using vec = vector<T>;
using str = string;
using p64 = pair<i64, i64>;
using v64 = vec<i64>;
using vv64 = vec<v64>;
using s64 = set<i64>;
using m64 = map<i64, i64>;
template <size_t Size1>
using a64 = array<i64, Size1>;
template <size_t Size1, size_t Size2>
using aa64 = array<a64<Size2>, Size1>;
template <class T>
using hash_set = unordered_set<T>;
template <class T, class U>
using hash_map = unordered_map<T, U>;
mt19937 gen(998244353);
const i64 inf = numeric_limits<i64>::max() / 2;
template <class T>
inline i64 len(const T& a) {
  return (i64)(a.size());
}
inline void fast() { ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0); }
template <class T, class S>
inline bool check_min(T& a, S b) {
  if (a > b) return a = b, 1;
  return 0;
}
template <class T, class S>
inline bool check_max(T& a, S b) {
  if (a < b) return a = b, 1;
  return 0;
}
void read() {}
template <class T, class... U>
void read(T& a, U&... b) {
  cin >> a;
  read(b...);
}
void print() {}
template <class T, class... U>
void print(const T& a, const U&... b) {
  cout << a << " ";
  print(b...);
}
void println() { cout << "\n"; }
template <class T, class... U>
void println(const T& a, const U&... b) {
  cout << a << " ";
  println(b...);
}
struct Problem {
  void files(str in, str out) {
    freopen(in.c_str(), "r", stdin), freopen(out.c_str(), "w", stdout);
  }
  void solve(), input();
  void many() {
    i64 t;
    cin >> t;
    while (t--) input(), solve();
  }
  void make(bool mt = 0) {
    if (mt)
      many();
    else
      input(), solve();
  }
} problem;
signed main() {
  fast();
  problem.make();
  return 0;
}
const i64 MAXA = 4269;
i64 n;
v64 p;
void init(i64 n) {
  p.resize(n);
  iota((p).begin(), (p).end(), 0);
  random_shuffle((p).begin(), (p).end());
}
i64 number = 0;
i64 answer(i64 i, i64 j) {
  ++number;
  return p[i] | p[j];
}
map<p64, i64> memo;
i64 ask(i64 i, i64 j) {
  if (i > j) swap(i, j);
  if (memo.count({i, j})) {
    return memo[{i, j}];
  }
  cout << "? " << i + 1 << " " << j + 1 << "\n" << flush;
  i64 ans;
  cin >> ans;
  memo[{i, j}] = ans;
  return ans;
}
void Problem::solve() {
  i64 CAN = 2000;
  i64 a = 0, b = 1;
  v64 q(n);
  iota((q).begin(), (q).end(), 0);
  while (number < CAN) {
    if (len(q) == 2) {
      break;
    }
    i64 ind = gen() % len(q);
    i64 c = q[ind];
    if (a == c || b == c) {
      continue;
    }
    i64 p1 = ask(a, b);
    i64 p2 = ask(a, c);
    if (p1 < p2) {
      q.erase(find((q).begin(), (q).end(), c));
    } else if (p1 > p2) {
      q.erase(find((q).begin(), (q).end(), b));
      b = c;
    } else if (p1 == p2) {
      q.erase(find((q).begin(), (q).end(), a));
      a = c;
    }
  }
  i64 who = a;
  while (1) {
    i64 c = gen() % n;
    if (a == c || b == c) {
      continue;
    }
    i64 p1 = ask(a, c);
    i64 p2 = ask(b, c);
    if (p1 != p2) {
      if (p1 < p2) {
        who = a;
      } else {
        who = b;
      }
      break;
    }
  }
  v64 ans(n);
  ans[who] = 0;
  for (i64 i = 0; i < n; i++) {
    if (i == who) continue;
    ans[i] = ask(i, who);
  }
  cout << "! ";
  for (int(i) = 0; (i) < (n); (i)++) {
    cout << ans[i] << " ";
  }
  cout << "\n" << flush;
}
void Problem::input() {
  gen = mt19937(time(0));
  cin >> n;
}
