#include <bits/stdc++.h>
using namespace std;
class DisjointSet {
 public:
  DisjointSet(int n) : N(n), sz(n), pars(N, 0), cnts(N, 1) {
    iota(pars.begin(), pars.end(), 0);
  }
  void add(int x) { rts.insert(x); }
  int find(int x) { return x == pars[x] ? x : (pars[x] = find(pars[x])); }
  bool merge(int x, int y) {
    x = find(x), y = find(y);
    if (x == y) {
      return false;
    }
    --sz;
    if (cnts[x] > cnts[y]) {
      swap(x, y);
    }
    rts.erase(x);
    rts.insert(y);
    pars[x] = y;
    cnts[y] += cnts[x];
    return true;
  }
  int count(int x) { return cnts[find(x)]; }
  int size() { return sz; }
  vector<int> roots() {
    vector<int> res(rts.begin(), rts.end());
    return res;
  }

 private:
  int N;
  int sz;
  vector<int> pars;
  vector<int> cnts;
  set<int> rts;
};
class Solution {
 public:
  int solve(int n, vector<pair<int, int>>& edges) {
    DisjointSet ds(n);
    vector<vector<int>> graph(n);
    for (auto& e : edges) {
      int u = e.first, v = e.second;
      if (u > v) {
        swap(u, v);
      }
      graph[v].push_back(u);
    }
    set<int> components;
    for (int i = 0; i < n; ++i) {
      map<int, int> cnts;
      for (auto j : graph[i]) {
        ++cnts[ds.find(j)];
      }
      bool connected = false;
      for (auto s : ds.roots()) {
        if (ds.count(s) > cnts[s]) {
          ds.merge(s, i);
          connected = true;
        }
      }
      if (!connected) {
        ds.add(i);
      }
    }
    return ds.size() - 1;
  }
};
int main(int argc, char** argv) {
  ios::sync_with_stdio(false);
  cin.tie(0);
  Solution sol;
  int n, m;
  cin >> n >> m;
  vector<pair<int, int>> edges;
  edges.reserve(m);
  for (int i = 0; i < m; ++i) {
    int u, v;
    cin >> u >> v;
    --u, --v;
    edges.emplace_back(u, v);
  }
  cout << sol.solve(n, edges) << "\n";
  return 0;
}
