#include <bits/stdc++.h>
using namespace std;
void read(long long &x) {
  x = 0;
  char c = getchar();
  int f = 1;
  while (c < '0' || c > '9') {
    if (c == '-') f = -f;
    c = getchar();
  }
  while (c >= '0' && c <= '9') {
    x = (x << 3) + (x << 1) + c - '0';
    c = getchar();
  }
  x *= f;
  return;
}
long long maxt(long long a, long long b) { return a > b ? a : b; }
long long mint(long long a, long long b) { return a < b ? a : b; }
void write(long long x) {
  if (x < 0) {
    putchar('-');
    x = -x;
  }
  if (x < 10) {
    putchar('0' + x);
    return;
  }
  write(x / 10);
  putchar(x % 10 + '0');
  return;
}
vector<long long> G[1000005];
queue<long long> qa, qb;
long long n, m, a[1000005], b[1000005], ta, tb, p, ans[1000005], pat, pbt,
    siz[1000005];
bool col[1000005], vis[1000005], viso[1000005];
bool cmp(long long x, long long y) { return siz[x] < siz[y]; }
void dfs(long long k) {
  vis[k] = 1;
  if (col[k])
    pat++;
  else
    pbt++;
  for (int i = 0; i < G[k].size(); i++)
    if (!vis[G[k][i]]) {
      col[G[k][i]] = !col[k];
      dfs(G[k][i]);
    }
  return;
}
bool check() {
  sort(a + 1, a + 1 + ta, cmp);
  for (int i = 3; i <= ta; i += 3) {
    ans[a[i]] = ans[a[i + 1]] = ans[a[i + 2]] = ++p;
  }
  for (int i = 0; i < G[a[1]].size(); i++) viso[G[a[1]][i]] = 1;
  ans[a[1]] = ++p;
  long long tmp = 0;
  for (int i = 1; i <= tb && tmp < 2; i++)
    if (!viso[b[i]]) ans[b[i]] = p, tmp++;
  if (tmp < 2) return 0;
  memset(viso, 0, sizeof(viso));
  for (int i = 0; i < G[a[2]].size(); i++) viso[G[a[2]][i]] = 1;
  ans[a[2]] = ++p;
  tmp = 0;
  for (int i = 1; i <= tb && tmp < 2; i++)
    if (!viso[b[i]]) ans[b[i]] = p, tmp++;
  if (tmp < 2) return 0;
  ++p;
  tmp = 0;
  for (int i = 1; i <= tb; i++)
    if (!ans[b[i]]) {
      tmp++;
      ans[b[i]] = p;
      if (tmp == 3) tmp = 0, p++;
    }
  return 1;
}
void print() {
  for (int i = 1; i <= n; i++) write(ans[i]), putchar(' ');
}
int main() {
  read(n), read(m);
  for (int i = 1; i <= m; i++) {
    long long u, v;
    read(u), read(v);
    G[u].push_back(v);
    G[v].push_back(u);
  }
  for (int i = 1; i <= n; i++) siz[i] = G[i].size();
  long long nt = 0;
  for (int i = 1; i <= n; i++)
    if (!vis[i]) {
      pat = 0;
      pbt = 0;
      nt++;
      dfs(i);
      if ((pat + pbt) & 1)
        if (pat > pbt)
          qa.push(i);
        else
          qb.push(i);
    }
  if (ta % 3 == 1 && !qa.empty() && nt == 2) {
    long long p = qa.front();
    memset(vis, 0, sizeof(vis));
    col[p] = 1;
    dfs(p);
  } else if (tb % 3 == 1 && !qb.empty() && nt == 2) {
    long long p = qb.front();
    memset(vis, 0, sizeof(vis));
    col[p] = 1;
    dfs(p);
  }
  for (int i = 1; i <= n; i++)
    if (col[i])
      ta++;
    else
      tb++;
  if (ta % 3 == 1 && tb % 3 == 2)
    for (int i = 1; i <= n; i++) col[i] = !col[i];
  memset(a, 0, sizeof(a));
  memset(b, 0, sizeof(b));
  ta = tb = 0;
  for (int i = 1; i <= n; i++)
    if (col[i])
      a[++ta] = i;
    else
      b[++tb] = i;
  if (!(ta % 3) && !(tb % 3)) {
    puts("YES");
    for (int i = 1; i <= ta; i += 3)
      ans[a[i]] = ans[a[i + 1]] = ans[a[i + 2]] = ++p;
    for (int i = 1; i <= tb; i += 3)
      ans[b[i]] = ans[b[i + 1]] = ans[b[i + 2]] = ++p;
  } else {
    sort(b + 1, b + 1 + tb, cmp);
    if (G[b[1]].size() > ta - 2) {
      if (!check())
        puts("NO");
      else {
        puts("YES");
        print();
      }
      return 0;
    }
    puts("YES");
    for (int i = 2; i <= tb; i += 3)
      ans[b[i]] = ans[b[i + 1]] = ans[b[i + 2]] = ++p;
    for (int i = 0; i < G[b[1]].size(); i++) viso[G[b[1]][i]] = 1;
    ans[b[1]] = ++p;
    long long tmp = 0;
    for (int i = 1; i <= ta && tmp < 2; i++)
      if (!viso[a[i]]) {
        tmp++;
        ans[a[i]] = p;
      }
    tmp = 0;
    p++;
    for (int i = 1; i <= ta; i++)
      if (!ans[a[i]]) {
        ans[a[i]] = p;
        tmp++;
        if (tmp == 3) tmp = 0, p++;
      }
  }
  print();
  return 0;
}
