不搞ACM,就举个笔试面试题库里的题目说一下Tarjan算法的应用吧。这是“结构之法 算法之道”上的100题里面第11题,题目如下:
求二叉树中节点的最大距离... 如果我们把二叉树看成一个图, 父子节点之间的连线看成是双向的, 我们姑且定义"距离"为两节点之间边的个数。 写一个程序, 求一棵二叉树中相距最远的两个节点之间的距离。
不多介绍了,这个将LCA的代码该很少的部分就可以直接应用。暂时没有想出来很好的可以通用的并查集、Tarjan算法接口,所以就先重复代码解决问题吧。
这个问题只需要引申一下就很清晰了:
问题为:找距离最近公共祖先的距离最长的两个点
LCA问题:计算每个节点的层次,计算两个节点到最近公共节点的层次和为距离
代码如下,有点长,带了个简单测试用例:
public class Questions11 { public static void main(String[] args){ /* * 0 * 1 2 * 3 4 */ List<Node<Integer>> list = new ArrayList<Node<Integer>>(); for(int i=0; i<5; i++){ list.add(new Node<Integer>(i)); } list.get(0).children.add(list.get(1)); list.get(0).children.add(list.get(2)); list.get(1).children.add(list.get(3)); list.get(1).children.add(list.get(4)); solve(list.get(0), list); } //找距离最近公共祖先的距离最长的两个点 //计算每个节点的层次,计算两个节点到最近公共节点的层次和为距离 public static void solve(Node<Integer> root, List<Node<Integer>> allNodes){ LCA_Tarjan<Integer> lca = new LCA_Tarjan<Integer>(); assertEquals(3, lca.getMaxDistance(root,allNodes)); } public static class LCA_Tarjan<T> { public static class Node<T>{ T data; //Node的数据 int level = 0; Node<T> father; //父节点/祖先节点 List<Node<T>> children; public Node(){} public Node(T data){ this.data = data; this.father = this; children = new ArrayList<Node<T>>(); } } public static class Pair<T>{ Node<T> p, q; Node<T> lca; public Pair(Node<T> p, Node<T> q){ this.p = p; this.q = q; } } List<Node<T>> allNodes; Map<Node<T>,Boolean> checked; int maxLen = 0; public int getMaxDistance(Node<T> root, List<Node<T>> allNodes){ this.allNodes = allNodes; checked = new HashMap<Node<T>,Boolean>(); LCA(root,0); return maxLen; } private void LCA(Node<T> u, int level){ for(Node<T> v:u.children){ LCA(v, level++); union(u, v); } checked.put(u, true); u.level = level; for(Node<T> n:allNodes){ if(n==u) continue; Boolean b = checked.get(n); if(b!=null && b){ int fatherLevel = find(n).level; if(maxLen<(u.level+n.level-2*fatherLevel)) maxLen = u.level+n.level-2*fatherLevel; } } } //-------------------------------------------------------------------- //并查集 /** * 找到祖先节点 * @param x * @return */ public Node<T> find(Node<T> x){ //当自己是祖先的时候直接返回 if (x == x.father){ return x; } //当自己的父节点不是祖先的时候,压缩树直接连接到祖先节点 x.father = find(x.father); return x.father; } /** * x和y节点之间有连接,将其所属集合连接。rank值小的树加到rank值大的树下面。相同时y加到x下。 * @param x * @param y */ public void union(Node<T> x, Node<T> y){ Node<T> xFather = find(x); Node<T> yFather = find(y); //当两个结合不联通的时候根据rank值,将rank值小的树加到rank值大的树下面 if(xFather==yFather){ return; }else{ yFather.father = xFather; } } } }
做完看到编程之美上有这个题目,还没仔细看解法,这里主要是说一下LCA怎么用在这个题,这样做下来还是挺简单的,而且时间复杂度LCA只有O(n+Q),还不错了
相关推荐
Tarjan的LCA算法是由Robert Tarjan提出的,它是一种高效地在线性时间内求解树中两个节点的最近公共祖先的方法。这个算法的关键在于对树进行深度优先搜索(DFS)的同时,利用一个叫做“下沉路径”的概念来构建一个...
2. **LCA 问题的 Tarjan 算法**:Tarjan 提出的算法可以在线性时间内预处理一棵树,之后在 O(1) 时间内找到任意两个节点的 LCA。该算法基于深度优先搜索(DFS)和栈,有效地解决了树的遍历问题。 3. **±1RMQ**:在...
最低公共祖先(Lowest Common Ancestor,简称LCA)算法是数据结构与算法中的一个重要概念,主要用于处理树形结构的问题,比如在二叉树、树状数组或图中找到两个节点的最近公共祖先。在本项目中,它通过C++语言实现,...
RMQ(Range Minimum/Maximum Query,区间最值查询)和LCA(Least Common Ancestor,最近公共祖先)是数据结构和算法中的重要概念,它们在解决实际问题中有着广泛的应用。下面将详细介绍这两种算法的基本概念、经典...
Tarjan算法是一种离线算法,主要用于计算图的强连通分量。在图论中,强连通分量是指图中任意两个顶点都互相可达的顶点集合。 13. **树的直径**: 树的直径是指树中最长的边,或者说是任意两个节点间的最长路径。...
LCA(v) // 递归地对u的所有孩子调用LCA算法 Union(u) // 合并u与其孩子的并查集 ancestor[Find-Set(u)] = u // 更新u所在的集合的祖先节点 } checked[u] = true // 标记节点u已被访问 对于每个(u,v)属于P (即...
此外,为了优化查询性能,可以采用预处理的方法,如LCA(Lowest Common Ancestor)数据结构,如Morris遍历或Tarjan's offline LCA算法,它们能在O(1)的时间复杂度内求解任意两个节点的最近公共祖先。 总结起来,求...
- 有两种经典算法:RMQ-ST和Tarjan算法。 - 在处理树结构的问题时非常重要。 24. **指数型母函数**: - 用于处理涉及组合数和多项式的复杂问题。 - 在组合数学中有着重要应用。 25. **AC自动机**: - 结合了...
最近公共祖先(LCA)是树形问题中的另一个核心概念,有多种不同的算法实现,如暴力搜索、倍增算法、DFS序方法、Tarjan算法和树链剖分算法。每种方法在时间复杂度和空间复杂度上有不同的权衡,适用于不同规模的问题。...
- **功能描述**:寻找一个图中最大的完全子图。 - **实现细节**:可以使用回溯法或其他启发式算法。 ### 9. 排序算法 #### 9.1 快速排序 - **功能描述**:实现一种高效的排序算法。 - **实现细节**:通过分治策略...
LCA算法的目标是找到两个节点在树中的最近公共祖先。这个问题有多种解法,如预处理后的Mo's算法、HLD(Heavy-Light Decomposition)或Tarjan's offline algorithm等。 在“RMQaLCA.rar”这个压缩包中,作者提出了一...