#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
//  配列の最大の大きさ
#define MAX_LENGTH  200010

typedef struct{
    int array[MAX_LENGTH];
    int first;
    int last;
}QUEUE;

void init(QUEUE*);
int enqueue(QUEUE*,int);
int dequeue(QUEUE*,int*);
struct touple { int a; int b; };
int cmp_touple(const void*n1, const void*n2);
int bisect_left(int x,struct touple a[],int left,int right);
int bisect_right(int x,struct touple a[],int left,int right);
struct path{
    struct path* next;
    int value;
};
int main(void){
    int N,M;
    scanf("%d%d", &N,&M);
    struct touple *ts = (struct touple *)malloc((M*2)*sizeof(struct touple));
    struct path *paths = (struct path *)malloc(N * sizeof(struct path));
    for (int i = 0; i < M; i++) {
        int x,y;
        scanf("%d%d", &x,&y);
        ts[i].a = x;ts[i].b = y;
        //逆辺も入れとく
        ts[i+M].a = y;ts[i+M].b = x;
    }
    qsort(ts,2*M,sizeof (ts), cmp_touple);
    // その部屋が指し示してる方向
    int *direction = (int *)malloc((N+1) * sizeof(int));
    for (int i = 0; i < N + 1; i++) { direction[i] = -1; }
    //以下bfs やる
    //queueには処理する部屋の順(頂点)が詰まってる
    QUEUE q;
    init(&q);
    enqueue(&q,1);
    direction[1] = 0;
    while (q.first != q.last){
        //queueの頭から一個取り出す
        int v;
        dequeue(&q,&v);
        //printf("%d\n",v);
        int index1 = bisect_left(v,ts,0,(2*M)-1); //<- 引数上手く書く
        int index2 = bisect_right(v,ts,0,(2*M)-1); //<-引数上手く書く
        //printf("%d %d index\n",index1,index2);
        // 計算量は一番深いのがこの一個したのネストでO(Mlog(M)+N)
        for (int index = index1; index < index2; index++) {
            int nv = ts[index].b;
            // nvは新しい頂点(行ける頂点)
            if (direction[nv] != -1) continue;//到達しているのでいらん
            direction[nv] = v;//普通 direction[nv] = direction[v] + 1で距離見る
            enqueue(&q,nv);
        }
    }
    // direction をうまいこと出力
    printf("Yes\n");
    for (int i = 2; i <= N; i++) {
        printf("%d\n",direction[i]);
    }
}

int bisect_left(int x,struct touple a[],int left,int right){
    int mid; right++;
    while (left < right) {
        mid = (left + right) / 2;
        if (a[mid].a < x) {
            left = mid + 1;
        } else {
            right = mid;
        }
    }
    return left;
}

int bisect_right(int x,struct touple a[],int left,int right){
    int mid;
    right++;
    while (left < right) {
        mid = (left + right) / 2;
        if (a[mid].a <= x) {
            left = mid + 1;
        } else {
            right = mid;
        }
    }
    return left;
}

int cmp_touple(const void*n1, const void*n2){
    return ((struct touple*)n1)->a - ((struct touple*)n2)->a;
}

//  スタックの初期化
void init(QUEUE* pQueue)
{
    int i;
    for(i = 0; i < MAX_LENGTH; i++){
        pQueue->array[i] = 0;
    }
    //  最初と最後の位置を先頭に。
    pQueue->first = 0;
    pQueue->last = 0;
}
//  値のプッシュ
int enqueue(QUEUE* pQueue,int value)
{
//  lastの次が、firstならば、失敗
    if((pQueue->last + 1) % MAX_LENGTH == pQueue->first){
        return 0;
    }
    pQueue->array[pQueue->last] = value;          // キューに新しい値を入れる
    pQueue->last = (pQueue->last + 1) % MAX_LENGTH;   // lastの更新
    //  データを格納しきれなかった
    return 1;
}
//  値のポップ
int dequeue(QUEUE* pQueue,int* pValue)
{
    //  キューにデータが一つもないなら、失敗
    if(pQueue->first == pQueue->last)
    {
        return 0;
    }
    *pValue = pQueue->array[pQueue->first];           // いちばん先頭のキューを返す準備
    pQueue->first = (pQueue->first+1) % MAX_LENGTH;   // キューの先頭を次に移動する
    return 1;
}


Measured memory_bytes: 7852