#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 200010,M=2*N;
int n;
int h[N], e[M], ne[M], idx;
bool st[N];
int d1[N],d2[N],p[N],up[N];
int maxd;//最长直径
void add(int a, int b) // 添加一条边a->b
{
e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}
//标准的求树的直径的代码
//求解每一个结点u作为最高点的时候的最大直径
void dfs_d(int u,int father)
{
for(int i=h[u];i!=-1;i=ne[i])
{
int j=e[i];
if(j!=father)
{
dfs_d(j,u);
//逻辑:搜索完j节点后,j的最大值和次大值就都知道了
//判断一下u结点的值是否发生改变
int distance = d1[j]+1;
if(distance > d1[u] )
{
d2[u]=d1[u];
d1[u]=distance;
p[u]=j; //u的子树中最大的是j节点
}
else if(distance>d2[u])d2[u]=distance;
}
}
maxd = max(maxd,d1[u]+d2[u]);
}
//找j节点往上走的最大长度 j的父亲是u
//u可以往上走,往最大边走,往次大边走(如果u的最大边节点是j p[u]=j)
void dfs_u(int u,int father)
{
for(int i=h[u];i!=-1;i=ne[i])
{
int j=e[i];
if(j!=father)
{
up[j]=up[u]+1; //选择u往上走的路
if(p[u]!=j) up[j]=max(up[j],d1[u]+1); //选择最大子路
else up[j]=max(up[j],d2[u]+1); //选择次大子路
//自上而下,先更新完上面的值,再向下搜索!!
dfs_u(j,u);
}
}
}
int main()
{
memset(h, -1, sizeof h);
cin>>n;
for(int i=1;i<=n-1;i++)
{
int u,v;
cin>>u>>v;
add(u,v);
add(v,u);
}
//找一下每个结点的最大值和次大值
dfs_d(0,-1);
dfs_u(0,-1);
for(int i=0;i<=n-1;i++)
{
int d[3]={d1[i],d2[i],up[i]};
sort(d,d+3);
//如果一个节点往外发散的三条边中,最大的两条加起来等于直径,说明他就在直径上!!
if(d[1]+d[2]==maxd) cout<<i<<endl;
}
return 0;
}