// Template
#include <iostream>
#include <iomanip>
#include <vector>
#include <string>
#include <queue>
#include <deque>
#include <stack>
#include <tuple>
#include <map>
#include <set>
#include <algorithm>
#include <utility>
#include <cmath>
#include <complex>
#define rep(i, x) for (int i = 0; i < (x); ++i)
#define rng(i, x, y) for (int i = (x); i < (y); ++i)
#define all(x) (x).begin(), (x).end()
#define sz(x) (int)(x).size()
using namespace std;
using ll = long long;
constexpr int inf = 1001001001;
constexpr ll INF = 3003003003003003003;
template <typename T> inline bool chmin(T &x, const T &y) {if (x > y) {x = y; return 1;} return 0;}
template <typename T> inline bool chmax(T &x, const T &y) {if (x < y) {x = y; return 1;} return 0;}
void solve();
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout << fixed << setprecision(10);
    solve();
    return 0;
}

// Coordinate Compression
template <typename T>
vector<T> zahyo(vector<T> x) {
    auto vec = x;
    sort(all(vec));
    vec.erase(unique(all(vec)), vec.end());
    return vec;
}

// Graph Structure
struct Graph {
    struct Edge {int to; ll cost;};
    vector<vector<Edge>> G;
    Graph(int V): G(V) {}
    Graph(int V, int E, bool weighted = false, bool directed = false): G(V) {
        rep(i, E) {
            int u, v;
            ll c = 1;
            cin >> u >> v;
            if (weighted) cin >> c;
            --u; --v;
            G[u].push_back(Edge{v, c});
            if (!directed) G[v].push_back(Edge{u, c});
        }
    }
    void AddEdge(int from, int to, ll cost = 1, bool directed = false) {
        G[from].push_back(Edge{to, cost});
        if (!directed) G[to].push_back(Edge{from, cost});
    }
    const vector<Edge> &operator[](int n) {
        return G[n];
    }
};
vector<ll> Dijkstra(Graph &G, int s) {
    vector<ll> dist(sz(G.G), INF);
    vector<bool> used(sz(G.G), false);
    priority_queue<pair<ll, int>> pq;
    dist[s] = 0;
    pq.push(pair<ll, int>(0, s));
    while (!pq.empty()) {
        int v = pq.top().second;
        pq.pop();
        if (used[v]) continue;
        for (auto &e: G[v]) {
            if (chmin(dist[e.to], dist[v] + e.cost)) pq.push(pair<ll, int>(-dist[e.to], e.to));
        }
        used[v] = true;
    }
    return dist;
}

// Main Code
void solve() {
    int n, m;
    cin >> n >> m;
    vector<int> p(m), q(m), c(m);
    rep(i, m) {
        cin >> p[i] >> q[i] >> c[i];
        --p[i]; --q[i];
    }
    
    auto v = zahyo(c);
    int k = 0;
    rep(i, m) {
        c[i] = lower_bound(all(v), c[i]) - v.begin();
        chmax(k, c[i] + 1);
    }
    
    vector<set<int>> st(k);
    rep(i, m) {
        st[c[i]].insert(p[i]);
        st[c[i]].insert(q[i]);
    }
    vector<vector<int>> s(k), num(k);
    int cnt = 0;
    rep(i, k) {
        for (int x: st[i]) {
            s[i].push_back(x);
            num[i].push_back(cnt + n);
            ++cnt;
        }
    }
    
    Graph G(cnt + n);
    rep(i, k) {
        rep(j, sz(s[i])) {
            G.AddEdge(s[i][j], num[i][j], 1, true);
            G.AddEdge(num[i][j], s[i][j], 0, true);
        }
    }
    rep(i, m) {
        int x = lower_bound(all(s[c[i]]), p[i]) - s[c[i]].begin();
        int y = lower_bound(all(s[c[i]]), q[i]) - s[c[i]].begin();
        G.AddEdge(num[c[i]][x], num[c[i]][y], 0);
    }
    
    auto ans = Dijkstra(G, 0);
    if (ans[n - 1] == INF) cout << -1 << "\n";
    else cout << ans[n - 1] << "\n";
    return;
}


Measured memory_bytes: 104120