题目描述
题目分析:
因为看题目的数据可以得到,当树退化成一个单链表时,时间复杂度变为 O(n^2) 会超时
并且从数据的大小可以得到 要通过这个数据 需要 O(n) 或者 O(nlogn) 的时间复杂度
因为O(n) 几乎不可能,相当于对于每一个结点的查询都是在 O(1) 下完成
所以考虑 O(nlogn) 来解决问题,相当于对于每一个结点的查询都在 O(logn) 完成
------>想到倍增方法,即存储每一个结点的 2^n 的父节点 , 这样对于每一个查询都只需要 longN 次查询了 成功把时间复杂度降到 O(nlogn)
那么对于初始化的操作(如何存储每一个结点的2^n 的父节点):
vector<vector<int>> dp;
TreeAncestor(int n, vector<int>& parent) : dp(n) {
for(int i = 0; i < n; i ++)
dp[i].push_back(parent[i]);
// dp[i][j] -> 节点 i 的第 2 ^ j 个祖先节点
// 初始化时: dp[i][0] = parent[i];
// 然后 对于 j 一直递增 直至 不存在节点有 2^j 个祖先节点
for(int j = 1; ; j ++){
bool allneg = true;// 是否每一个节点都没有 2^j 个祖先节点
for(int i = 0; i < n; i ++){
int t = dp[i][j - 1] != -1 ? dp[dp[i][j - 1]][j - 1] : -1;
// dp[i][j-1] -> 节点 i 的第 2 ^ (j - 1) 个祖先节点
// 若该节点存在 则 :dp[i][j] = dp[dp[i][j-1]][j-1];
// (即 dp[i][j] 为 i 节点的 第 2 ^ j-1 个祖先节点的 第 2 ^ j - 1 个祖先节点)
// 否则 dp[i][j] = -1;
dp[i].push_back(t);
if(t != -1) allneg = false;
// 当 t != -1 时:说明还有节点可以继续往上查询祖先节点
}
if(allneg) break; // 所有的节点的 2^j 的祖先都是 -1 了,就不用再计算了
}
}
查询操作:
int getKthAncestor(int node, int k) {
if(k == 0 || node== -1) return node;
/*
得到二进制的最右边的数的大小,要得到位数还需要
int t = x & (x - 1);// 去掉最右边的一个 0
t = x - t;
int len = 0;
while(t) {
t >>= 1;
len++;
}
return len;
*/
int pos = ffs(k) - 1; // C++ 语言中 ffs(k) 求解出 k 的最右侧第一个 1 的位置(1-based)
/*
ffs(2) = 2 ; ffs(1) = 1;
所以现在先要找到第 2 ^ pos 个祖先节点
而一个节点的最大祖先个数是 : 2 ^ ( dp[node].size() -1 )
所以当 pos >= dp[node].size() 时,说明不存在该祖先节点,此时返回 - 1
否则 递归查找祖先节点 getKthAncestor(dp[node][pos], k - (1 << pos))
*/
return pos < dp[node].size() ? getKthAncestor(dp[node][pos], k - (1 << pos)) : -1;
}