提到 Robert Tarjan 这个人大家可能会立刻联想起著名的强连通分量 tarjan 算法,然而这只是他提出的众多算法中的一个。
本文会简单引用一些资料介绍 Robert Tarjan 这位伟大而高产的计算机科学家并简单介绍一下他提出的几个重要的算法与数据结构。
个人简历
Robert Tarjan在多所大学担任学术职务,如:康奈尔大学(1972-1973年),加州大学伯克利分校(1973-1975),斯坦福大学(1974-1980),纽约大学(1981-1985)。 他也加入过NEC研究所(1989-1997),并在美国麻省理工学院(1996年)担任Visiting Scientist 。
曾在AT&T贝尔实验室(1980-1989),浩信科技(1997-2001),康柏(2002年)和惠普(2006年至今)工作。 他曾加入ACM和IEEE委员会,并曾为几家期刊的编辑。
成就
Robert Tarjan设计了求解的应用领域的许多问题的广泛有效的算法和数据结构。
他已发表了超过228篇理论文章(包括杂志,一些书中的一些章节文章等)。Robert Tarjan以在数据结构和图论上的开创性工作而闻名。
他的一些著名的算法包括 Tarjan最近共同祖先离线算法 ,Tarjan的强连通分量算法以及Link-Cut-Trees算法等。其中Hopcroft-Tarjan平面嵌入算法是第一个线性时间平面算法。
Tarjan也开创了重要的数据结构如:斐波纳契堆和splay树(splay发明者还有Daniel Sleator)。另一项重大贡献是分析了并查集。他是第一个证明了计算反阿克曼函数的乐观时间复杂度的科学家。
奖项
Tarjan与约翰霍普克罗夫特共同于1986年获得图灵奖。
Tarjan还于1994年当选为ACM院士。
Tarjan其他奖项包括:
奈望林纳奖信息科学(1983第一个获奖者)
国家科学院的研究倡议奖 (1984)
巴黎Kanellakis奖-理论与实践( ACM1999)
帕斯卡奖章数学与计算机科学( 欧洲科学院2004)
加州理工学院杰出校友奖( 美国加州技术研究所2010)
一些重要的算法
Tarjan最近公共祖先
Tarjan 最近公共祖先离线算法可以以 O(log n) 的时间复杂度计算一棵树上两个节点的最近公共祖先。
通过倍增的思想离线处理出树上每个点的深度差为 2 的幂次的祖先,询问的时候只要让两点跳到相同深度的祖先并向上寻找最近的公共祖先。
int lca(int x,int y){
if (dep[x]<dep[y]) swap(x,y);
int t=dep[x]-dep[y];
for (int i=18;i>=0;i--) if ((1<<i)&t) x=fa[x][i];
for (int i=18;i>=0;i--) if (fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
if (x==y) return x;
return fa[x][0];
}
int dfs(int u){
for (int i=1;i<=18;i++) fa[u][i]=fa[fa[u][i-1]][i-1];
for (int j=head[u];j;j=e[j].pre){
int v=e[j].obj;
if (v==fa[u][0]) continue;
dep[v]=dep[u]+1;
dfs(v);
}
}
Tarjan强连通分量
Tarjan 强连通算法是在 O(n) 的时间复杂度下找出一张有向图的所有的强联通分量。
它利用了 dfs 与时间戳,维护了每个点的后继节点能访问到的最早的时间戳low,并通过 low 和 dfs 序来判断是否存在环。
stack<int> q;
void tarjan(int u){
dfn[u]=low[u]=++idx; q.push(u);
for (int j=head[u];j;j=e[j].pre){
int v=e[j].obj;
if (!dfn[v]){
tarjan(v);
low[u]=min(low[u],low[v]);
}
else if (!bl[v]) low[u]=min(low[u],dfn[v]);
}
if (low[u]==dfn[u]){
int x=-1,cnt=0; ++scc;
while (x!=u){
x=q.top(); q.pop();
bl[x]=scc;
}
}
}
link-cut tree
link-cut tree是一个建立在 splay 上的算法。
它可以支持树形态的转换,比如说合并两棵树,将树的一部分切去,寻找某个点当前所处树的根,并可以维护在树上的信息。
link-cut tree的每个操作时间复杂度都是 O(log n) 的。
值得一提的是 link-cut tree 和 splay 都是 Daniel Dominic Sleator 和 Robert Endre Tarjan 共同提出的。
void up(int x){
int l=c[x][0],r=c[x][1];
mx[x]=x;
if (val[mx[l]]>val[mx[x]]) mx[x]=mx[l];
if (val[mx[r]]>val[mx[x]]) mx[x]=mx[r];
}
void rot(int x){
int y=fa[x],z=fa[y],l=(c[y][1]==x),r=l^1;
if (!isroot(y)){
if (c[z][0]==y) c[z][0]=x; else c[z][1]=x;
}
fa[x]=z; fa[y]=x; fa[c[x][r]]=y;
c[y][l]=c[x][r]; c[x][r]=y;
up(y); up(x);
}
void splay(int x){
int top=0; st[++top]=x;
for (int i=x;!isroot(i);i=fa[i]) st[++top]=fa[i];
for (int i=top;i;i--) Down(st[i]);
while (!isroot(x)){
int y=fa[x],z=fa[y];
if (!isroot(y)){
if ((c[y][0]==x)^(c[z][0]==y)) rot(x);
else rot(y);
}
rot(x);
}
}
bool isroot(int x){
int y=fa[x];
if ((c[y][0]!=x)&&(c[y][1]!=x)) return 1;
return 0;
}
void access(int x){
for (int t=0;x;x=fa[x]){
splay(x); c[x][1]=t; up(x); t=x;
}
}
void makeroot(int x){
access(x); splay(x); rev[x]^=1;
}
void link(int x,int y){
makeroot(x); fa[x]=y;
}
void cut(int x,int y){
makeroot(x); access(y); splay(y);
if (c[y][0]==x) c[y][0]=fa[x]=0;
}
void split(int x,int y){
makeroot(x); access(y); splay(y);
}
结语
Tarjan 他老人家还提出过非常多实用高效的算法,鉴于本人的水平有限只能列举上述几个著名的算法。
可以看出 Tarjan 对算法世界带来的巨大贡献。希望我们都能好好学习理解并应用 Tarjan 提出的众多算法,并学习他高产背后的科学精神。
本文介绍了计算机科学家Robert Tarjan的生涯、成就及重要算法,包括他在数据结构和图论领域的开创性工作,如Tarjan最近公共祖先算法、强连通分量算法和link-cut tree。他还因这些贡献获得了图灵奖等众多荣誉。
307





