这是Hackerrank比赛的一个问题:
您将获得一棵树,其中每个节点都标记为1,2,...,n。这棵树中有多少个相似的对(S)?
一对(A,B)是类似的对iff
输入格式: 输入的第一行包含两个整数n和T.接着是n-1行,每行包含两个整数si和ei,其中节点si是节点ei的父节点。
输出格式: 输出一个整数,表示树中相似对的数量
约束:
1 <= n <= 100000
0 <= T <= n
1 <= si, ei <= n.
也保证没有循环,但树不必是二叉树。
示例输入:
5 2
3 2
3 1
1 4
1 5
示例输出:
4
说明: 类似的对是:(3,2)(3,1)(3,4)(3,5)
现在,蛮力方法解决了大约一半的测试用例,但对于另一半,它只是缓慢。我试图通过存储节点子树的间隔来扩展算法,从而能够消除一些分支,但总体而言只需要几个点。
有关如何有效解决此搜索的任何想法?
答案 0 :(得分:4)
嗯,这个解决方案怎么样?
按预先的顺序遍历树(如DFS),并维护一个多集S
进行查询。
在输入节点x
时,只需将x
添加到S
即可。在离开节点x
时(在此上下文中,我指的是访问x
的所有孩子之后的时间),请从x
移除S
。通过这样做,在树遍历期间的所有时间,{strong} x
中 S
的所有祖先。
现在让我们计算一个元素为x
的类似对。另一个元素(比如y
)必须位于S
(因为它必须是x
的祖先),并且必须保留x - T <= y <= x + T
。有多少这样的y
?是的,您只需查询S
计算S
之间的[x-T, x+T]
值中的元素数量。此查询可以在O(log N)时间内回答,因为S
中的元素数量永远不会超过N.
总之,通过维护当前访问节点的所有祖先,并总结对的数量(包括当前节点),您可以找到所有类似对的数量。由于我们对树中的每个节点都有单一查询,因此整体时间复杂度为 O(N log N)。
答案 1 :(得分:1)
我会尝试深度优先搜索树,使用二进制索引树(请参阅Topcoder tutorial)来存储堆栈中看到的所有值。
这允许您对所需范围内的父节点数进行O(log(n))查询,因此整体复杂度将为O(nlog(n))
答案 2 :(得分:1)
我用地图做了。您只需保存每个节点的父节点,然后递归计算每个节点的类似对。
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
public class Solution
{
static Map<Integer, Integer> map = new HashMap<Integer, Integer>();
static int similarPairs = 0;
public static void main(String[] args)
{
Scanner sc = new Scanner(System.in);
int totalRows = sc.nextInt();
int T = sc.nextInt();
for (int i = 0; i < totalRows - 1; i++)
{
int data = sc.nextInt();
int cdata = sc.nextInt();
int currValue = cdata;
map.put(cdata, data);
while (map.get(cdata) != null)
{
int parent = map.get(cdata);
if (Math.abs(parent - currValue) <= T)
{
similarPairs += 1;
}
cdata = parent;
}
}
System.out.println(similarPairs);
}
}
答案 3 :(得分:0)
如下所示,可以在低级别C中实现,而无需任何列表/字典/循环结构,如哈希表。
int[] arr_of_ints = new int[n*M]
的整数数组,并将所有条目初始化为0.想法是节点i(其中,节点可以有的最大子节点数M) 1&lt; = i&lt; = n)对应于M个整数arr_of_ints [M *(i-1)]到arr_of_ints [M * i],并且这些条目的值是该节点的子节点int[] ancestors = new int[n]
,该数组将存储所有祖先的值ancestors
向下运行树,使用数组ancestors
,这样在每个节点上都没有必要回溯树来计算数量类似的节点。你的递归越深,你在数组class Solution {
static char[] space = new char[] { ' ' };
class FileRow {
public readonly int parent;
public readonly int child;
public FileRow(string str) {
string[] split = str.Split(space);
parent = int.Parse(split[0]); child = int.Parse(split[1]);
}
}
public static void Main(String[] args) {
List<FileRow> inputdata = new List<FileRow>();
StreamReader sr = File.OpenText("inputfile.txt");
String[] split = sr.ReadLine().Split(space);
int n = int.Parse(split[0]), T = int.Parse(split[1]);
int[] arr = new int[n]; // this will record the max num of children
while (!sr.EndOfStream) {
FileRow fr = new FileRow(sr.ReadLine());
arr[fr.parent - 1]++;
inputdata.Add(fr);
}
sr.Close();
int M = 0;
for (int i = 0; i < arr.Length; i++) {
M = Math.Max(M, arr[i]);
arr[i] = 0; // set all arr to zero, for use below in setting up tree
}
int[,] tree = new int[n, M];
Boolean[] not_root_node = new bool[n]; // all entries automatically initialised to false
foreach (FileRow fr in inputdata) {
not_root_node[fr.child - 1] = true; // indicate that node fr.child isn't the root node of the tree
tree[fr.parent - 1, arr[fr.parent - 1]++] = fr.child;
}
int count = 0, node = 0;
for (int i = 0; i < not_root_node.Length; i++) {
if (!not_root_node[i]) {
count++; node = i + 1;
}
arr[i] = 0; // set all arr to zero, for use below in calculating result
}
if (count != 1) throw new Exception("ERROR, root node for tree not unique, count="+count.ToString());
// int node is the root of the tree
int level = 0, result = 0;
int[] ancestors = new int[n];
do {
int j = arr[node - 1];
int nextnode = (j < M ? tree[node - 1,j] : 0);
if (nextnode == 0) {
level--;
if (level >=0) node = ancestors[level];
} else {
ancestors[level++] = node;
arr[node - 1]++;
node = nextnode;
for (int i = 0; i < level; i++) {
if (Math.Abs(ancestors[i] - node) <= T) result++;
}
}
} while (level >= 0);
System.Diagnostics.Trace.WriteLine(result);
}
}
越远,但是当你到达分支的末端时,那个位置就会展开
更新:我已用C#编写了我的解决方案版本,见下文,我认为它是O(n log n)。
{{1}}