#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); i++)
#define rep1(i, n) for (int i = 1; i <= (n); i++)
using namespace std;
typedef long long ll;
typedef pair<ll, ll> P;
typedef vector<vector<ll>> Matrix;

const char wall = '#';
vector<vector<char>> fields;
vector<vector<bool>> used;
vector<vector<ll>> dist;
const int dx[4] = {0, -1, 0, 1};
const int dy[4] = {1, 0, -1, 0};

vector<ll> A;
// vectorのリサイズ
// A.resize(N);

ll gcd(ll a, ll b) { return b?gcd(b,a%b):a;}
ll lcm(ll a, ll b) { return a*b/gcd(a, b);}


struct edge {
  long long to;
  long long cost;
};

const ll INF = 100000000;
struct Graph {
  ll V;
  vector<vector<edge>> G;
  vector<ll> dist;
  // prev[to] = from
  // to に到達前にfromにいたよ
  vector<ll> prev;
 
  // construct
  //
  // usage: graph g(n);
  Graph(ll n) {
    init(n);
  }

  void init(ll n) {
    V = n;
    G.resize(V);
    dist.resize(V, INF);
    prev.resize(V, -1);
  }

  // 頂点sから頂点tへコストcostの辺が張られる
  void add_edge(ll s, ll t, ll cost) {
    edge e;
    e.to = t; e.cost = cost;
    G[s].push_back(e);
  }

  // 頂点sから各頂点への最短経路をダイクストラ法で探索
  // debug = true でデバッグ出力する
  //
  // コストが負の辺があると使えないから注意
  //
  // 頂点iへの距離は ll dist = d.dist[i]
  //
  // O(E + VlogV)
  void dijkstra(ll s, bool debug) {
    dist[s] = 0;
    priority_queue<P, vector<P>, greater<P>> que;
    que.push(P(0, s));
    while(!que.empty()) {
      P p = que.top(); que.pop();
      ll v = p.second;
      if (debug) printf("v: %lld, dist[v]: %lld, cost: %lld\n", v, dist[v], p.first);
      if (dist[v]<p.first) {
        if (debug) printf("v: %lld, dist[v]: %lld, p.first: %lld skip", v, dist[v], p.first);
        continue;
      }
      for (auto nextE : G[v]) {
        if (dist[nextE.to] > dist[v] + nextE.cost) {
          if (debug) printf("v: %lld, next: %lld\n", v, nextE.to);
          dist[nextE.to] = dist[v] + nextE.cost;
          prev[nextE.to] = v;
          que.push(P(dist[nextE.to], nextE.to));
        }
      }
    }
  }

  // 頂点sから各頂点への最短経路をベルマンフォード法で探索
  //
  // 戻り値がtrueならば負の閉路を含む
  bool bellman_ford(int s) {
    dist[s] = 0;
    rep(i, V) {
      rep(v, V) {
        rep(k, G[v].size()) {
          edge e = G[v][k];
          if (dist[v] != INF && dist[e.to] > dist[v] + e.cost) {
            dist[e.to] = dist[v] + e.cost;
            if ( i == V - 1 ) return true;
          }
        }
      }
    }
    return false;
  }

  // 頂点tまでの経路を返す
  // ダイクストラ法で探索した場合のみ使える
  vector<ll> get_path(ll t) {
    vector<ll> path;
    for (ll cur = t; cur != -1; cur = prev[cur]) {
      path.push_back(cur);
    }
    reverse(path.begin(), path.end());
    return path;
  }

};


int main() {
  cin.tie(0);
  ios::sync_with_stdio(false);

  ll n, m;
  cin >> n >> m;
  Graph g(n);
  rep(i, m) {
    ll a, b;
    cin >> a >> b;
    a--; b--;
    g.add_edge(a, b, 1);
    g.add_edge(b, a, 1);
  }

  //rep(i, n) {
  //  rep(j, g.G[i].size()) {
  //    printf("%lld%c", g.G[i][j].to, (i ==  g.G[i].size()-1)?'\n':' ');
  //  }
  //}
  g.dijkstra(0, false);
  rep(i, n) {
  	// unreachable のときは INFが入っている
  // unreachableの考慮を忘れないこと
    if (g.dist[i] == INF) {
      cout << "No" << endl;
      return 0;
    }
  }
  cout << "Yes" << endl;
  for (ll i = 1; i < n; i++ ) {
    cout << g.prev[i]+1 << endl;
  }

  //rep(i, n) {
  //  cout << i << ", " << g.dist[i] << ", "  << g.prev[i]<<  endl;
  //}
  // // ダイクストラのときは最短経路わかる
  //vector<ll> path = g.get_path(3);
  //rep(i, path.size()) cout << path[i] << endl;
  return 0;
}


Measured memory_bytes: 19488