use std::io::*;
use std::str::FromStr;

#[allow(dead_code)]
fn read<T: FromStr>() -> T {
    let stdin = stdin();
    let stdin = stdin.lock();
    let token: String = stdin
        .bytes()
        .map(|c| c.expect("failed to read char") as char)
        .skip_while(|c| c.is_whitespace())
        .take_while(|c| !c.is_whitespace())
        .collect();
    token.parse().ok().expect("failed to parse token")
}

type Weight = i32;
#[allow(dead_code)]
#[derive(Clone, Debug, Eq, PartialEq)]
struct Edge {
    index: usize,
    src: usize,
    dest: usize,
    weight: Weight,
}
#[allow(dead_code)]
impl Edge {
    fn new(index: usize, src: usize, dest: usize, weight: Weight) -> Edge {
        Edge {
            index: index,
            src: src,
            dest: dest,
            weight: weight,
        }
    }
}
impl std::cmp::Ord for Edge {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        (self.weight, self.src, self.dest).cmp(&(other.weight, other.src, other.dest))
    }
}
impl std::cmp::PartialOrd for Edge {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        Some(self.cmp(other))
    }
}
type Edges = Vec<Edge>;
type Graph = Vec<Edges>;
#[allow(dead_code)]
fn graph_new(n: usize) -> Graph {
    vec![vec![]; n]
}
#[allow(dead_code)]
fn add_bi_edge(graph: &mut Graph, index: &mut usize, src: usize, dest: usize, weight: Weight) {
    graph[src].push(Edge::new(*index, src, dest, weight));
    graph[dest].push(Edge::new(*index, dest, src, weight));
    *index += 1;
}
#[allow(dead_code)]
fn add_uni_edge(graph: &mut Graph, index: &mut usize, src: usize, dest: usize, weight: Weight) {
    graph[src].push(Edge::new(*index, src, dest, weight));
    *index += 1;
}

fn dfs(g: &Graph, from: usize, parent: usize, depth: usize) -> (usize, usize) {
    let mut ret = (depth, from);
    for edge in g[from].iter() {
        if edge.dest == parent {
            continue;
        }
        let nret = dfs(g, edge.dest, from, depth + 1);
        ret = std::cmp::max(ret, nret);
    }
    ret
}

fn main() {
    let n: usize = read();
    let mut g = graph_new(n);
    let mut e = 0;
    for _i in 0..n - 1 {
        let mut f: usize = read();
        let mut t: usize = read();
        f -= 1;
        t -= 1;
        add_bi_edge(&mut g, &mut e, f, t, 0);
    }
    let mut left = dfs(&g, 0, 0, 0).1;
    let mut right = dfs(&g, left, left, 0).1;
    let mut ans = vec![vec![0; n]; 2];
    for iter in 0..2 {
        // println!("iter");
        let mut parent = left;
        let mut from = left;
        let mut offset = 0;
        loop {
            // println!("from: {} {}", from, parent);
            let mut next: Option<usize> = None;
            for edge in g[from].iter() {
                let to = edge.dest;
                if to == parent || g[to].len() <= 1 {
                    continue;
                }
                if next.is_some() {
                    println!("-1");
                    return;
                }
                next = Some(to);
            }
            let mut other_cnt = g[from].len() - 1;
            if from != left {
                other_cnt -= 1;
            }
            // println!("{} {} {:?}", offset, other_cnt, next);
            ans[iter][offset + other_cnt] = offset;
            for _i in 0..other_cnt {
                ans[iter][offset] = offset + 1;
                offset += 1;
            }
            offset += 1;
            if let Some(to) = next {
                parent = from;
                from = to;
            } else {
                break;
            }
        }
        ans[iter][offset] = offset;
        offset += 1;
        assert!(offset == n);
        std::mem::swap(&mut left, &mut right);
    }
    ans.sort();
    for i in 0..n {
        if i != 0 {
            print!(" ");
        }
        print!("{}", ans[0][i] + 1);
    }
    println!("");
}


Measured memory_bytes: 34300