#include <bits/stdc++.h>
using namespace std;
template <class S, class T>
ostream& operator<<(ostream& o, const pair<S, T>& p) {
  return o << "(" << p.first << "," << p.second << ")";
}
template <class T>
ostream& operator<<(ostream& o, const vector<T>& vc) {
  o << "{";
  for (const T& v : vc) o << v << ",";
  o << "}";
  return o;
}
using ll = long long;
template <class T>
using V = vector<T>;
template <class T>
using VV = vector<vector<T>>;
constexpr ll TEN(int n) { return (n == 0) ? 1 : 10 * TEN(n - 1); }
struct FastSet {
  using ull = unsigned long long;
  int N, D;
  vector<vector<ull>> seg;
  FastSet(int N_) : N(N_) {
    assert(N_ >= 1);
    if (N_ == 1) N_++;
    while (N_ > 1) {
      N_ = (N_ + 63) >> 6;
      seg.push_back(vector<ull>(N_));
    }
    D = seg.size();
  }
  void insert(int x) {
    for (int d = 0; d < (int)(D); d++) {
      ull b = 1ull << (x & 63);
      x >>= 6;
      seg[d][x] |= b;
    }
  }
  void erase(int x) {
    for (int d = 0; d < (int)(D); d++) {
      ull b = 1ull << (x & 63);
      x >>= 6;
      seg[d][x] &= ~b;
      if (seg[d][x]) break;
    }
  }
  int geq(int x) {
    if (x >= N) return N;
    if (x < 0) x = 0;
    for (int d = 0; d < (int)(D); d++) {
      int i = x >> 6, r = x & 63;
      if (i == (int)seg[d].size()) break;
      ull b = seg[d][i] >> r;
      if (b) {
        x += bsf(b);
        while (d--) {
          x = (x << 6) | bsf(seg[d][x]);
        }
        return x;
      } else {
        x = (x >> 6) + 1;
      }
    }
    return N;
  }
  int leq(int x) {
    if (x < 0) return -1;
    if (x >= N) x = N - 1;
    for (int d = 0; d < (int)(D); d++) {
      if (x == -1) break;
      int i = x >> 6, r = x & 63;
      ull b = seg[d][i] << (r ^ 63);
      if (b) {
        x -= bsr(b) ^ 63;
        while (d--) {
          x = (x << 6) | bsr(seg[d][x]);
        }
        return x;
      }
      x = (x >> 6) - 1;
    }
    return -1;
  }
  bool has(int x) {
    if (x < 0 || x >= N) return false;
    return (seg[0][x >> 6] >> (x & 63)) & 1;
  }
  int bsr(ull x) { return __builtin_clzll(x) ^ 63; }
  int bsf(ull x) { return __builtin_ctzll(x); }
};
int main() {
  cin.tie(0);
  ios::sync_with_stdio(false);
  cout << fixed << setprecision(20);
  int N, M;
  cin >> N >> M;
  VV<int> G(N);
  for (int i = 0; i < (int)(M); i++) {
    int x, y;
    cin >> x >> y;
    x--, y--;
    G[x].push_back(y);
    G[y].push_back(x);
  }
  for (int i = 0; i < (int)(N); i++) sort(G[i].begin(), G[i].end());
  FastSet unvis(N);
  for (int i = 0; i < (int)(N); i++) unvis.insert(i);
  int con = 0;
  function<void(int)> dfs = [&](int v) {
    unvis.erase(v);
    int u = -1;
    while (true) {
      u = unvis.geq(u + 1);
      if (u == N) break;
      if (!binary_search(G[v].begin(), G[v].end(), u)) {
        dfs(u);
      }
    }
  };
  for (int i = 0; i < (int)(N); i++)
    if (unvis.has(i)) dfs(i), con++;
  cout << max(con - 1, 0) << endl;
}
