#include<bits/stdc++.h>
using namespace std;

#define FOR(i,a,b) for (int i=(a);i<(b);i++)
#define RFOR(i,a,b) for (int i=(b)-1;i>=(a);i--)
#define REP(i,n) for (int i=0;i<(n);i++)
#define RREP(i,n) for (int i=(n)-1;i>=0;i--)
#define ALL(a) (a).begin(),(a).end()
#define DEBUG(a) "(" << #a << ": " << (a) << ")"

template<typename T>
void chmin(T &x, T y) { x = min(x, y); }

template<typename T>
void chmax(T &x, T y) { x = max(x, y); }

typedef long long int lli;
typedef long double ld;
typedef tuple<int,int> P;

const int INF = INT_MAX/2 - 1;
const double EPS = 1e-14;
const int dx[4] = {0, 1, 0, -1}; // {-1,  0,  1, -1, 1, -1, 0, 1};
const int dy[4] = {1, 0, -1, 0}; // {-1, -1, -1,  0, 0,  1, 1, 1};

typedef int Weight;
typedef int Flow;

struct Edge {
  int src, dest, rev;
  Flow cap;
  Weight cost;
  bool operator < (const Edge &rhs) const {
    return cost > rhs.cost;
  }
  Edge(int s, int d) : src(s), dest(d) { ; }
  Edge(int s, int d, int c) : src(s), dest(d), cost(c) { ; }
  Edge(int s, int d, int r, Flow cp, Weight cst) : src(s), dest(d), rev(r), cap(cp), cost(cst) { ; }
};

typedef vector<Edge> Edges;
typedef vector<Edges> Graph;
typedef vector<Weight> Array;
typedef vector<Array> Matrix;

bool dfs(const Graph &g, int v, vector<int> &order, vector<bool> &visited, vector<bool> &pushed) {
  visited[v] = true;
  for (Edge e : g[v]) {
    int w = e.dest;
    if (pushed[w]) continue;
    if (visited[w]) return false;
    if (!dfs(g, w, order, visited, pushed)) return false;
  }
  order.push_back(v);
  pushed[v] = true;
  return true;
}

tuple<bool,vector<int>> topological_sort(const Graph &g) {
  vector<int> order;
  vector<bool> visited(g.size(), false), pushed(g.size(), false);
  REP(i, g.size()) {
    if (!pushed[i] && !dfs(g, i, order, visited, pushed))
      return make_tuple(false, vector<int>());
  }
  reverse(ALL(order));
  return make_tuple(true, order);
}
tuple<bool,vector<int>> topological_sort_stack(const Graph &g) {
  vector<int> order;
  vector<bool> visited(g.size(), false), marked(g.size(), false);
  REP(i, g.size()) {
    if (marked[i]) continue;

    stack<tuple<int,int,int>> jobs; // 0 = visit, 1 = child, 2 = push
    jobs.push(make_tuple(0, i, -1));
    while (!jobs.empty()) {
      auto job = jobs.top(); jobs.pop();
      int com = get<0>(job), v = get<1>(job), i = get<2>(job);
      cout <<DEBUG(com) << DEBUG(v) << DEBUG(i) << endl;
      switch (com) {
        case 0: // visit
          visited[v] = true;
          jobs.push(make_tuple(1, v, 0));
          break;
        case 1: // child
          if (i >= (int)g[v].size()) {
            jobs.push(make_tuple(2, v, -1));
          } else if (marked[g[v][i].dest]) {
            jobs.push(make_tuple(1, v, i + 1));
          } else if (visited[g[v][i].dest]) {
            return make_tuple(false, vector<int>());
          } else {
            jobs.push(make_tuple(1, v, i + 1));
            jobs.push(make_tuple(0, g[v][i].dest, -1));
          }
          break;
        case 2: // push
          order.push_back(v);
          marked[v] = true;
          break;
        default:;
      }
    }
  }
  reverse(ALL(order));
  return make_tuple(true, order);
}


int main() {
  cout << fixed << setprecision(10);
  int V, E;
  cin >> V >> E;
  Graph G(V);
  REP(i, E) {
    int s, t;
    cin >> s >> t;
    G[s].push_back(Edge(s, t));
  }

  auto res = topological_sort(G);
  for (int v : get<1>(res)) {
    cout << v << endl;
  }
  return 0;
}

Measured memory_bytes: 4560