8.28 LCA问题-BinaryLifting

1.LCA

LCA(Lowest Common Ancestor)问题,即求树的两个结点的最近公共祖先
首先,我们解决如何查找到树结点的第K个祖先的问题,再使用接下来的算法即可
伪代码:

int LCA(int u,int v){
	if u 是 v 的祖先 return u
	if v 是 u 的祖先 return v
	for(k:1~u的深度)
		t = u 的第k个祖先
		if t 是 v 的祖先 return t
	return 0;//直到根节点了还不是 u 、 v 的公共祖先,根据题目处理了
}

算法的关键在于如何快速查找一个结点的第k个祖先

使用Binary Lifting
我们首先需要对每个结点进行预处理,比如每个结点用一个数组储存他的第1、2、3…个祖先
第n个结点的第k个祖先很容易的可以写成D[n][k]
但是显然这样的预处理是会超时的
Binary Lifting的思想就是,考虑k的二进制表示,比如5=4+1,那么第n个结点的第5个祖先就表示为
第n个结点的第1个祖先的第4个祖先,即:D[ D[N][1] ] [4]
这样我们需要构造的数组就从1、2、3、4、5…(N个)下降到了1、2、4、8、16…(logN个)
使用动态规划的方法构造,易得
dp[n][i]=dp[dp[n][i-1]][i-1](例如第4个祖先=第2个祖先的第二个祖先,dp[n][3]=dp[dp[n][2]][2])

    vector<vector<int>> dp;
    int depth;
    TreeAncestor(int n, vector<int>& parent) {
        for(int i=0;i<n;i++){
            dp.push_back({});
            dp[i].push_back(parent[i]);//dp[n][0]就是n的父亲结点
        }
        for(depth=1;;depth++){
            bool allover=true;//记录是否所有结点都搜到根节点以上的-1了
            for(int i=0;i<n;i++){
            	//当前已经没有祖先了,所以也不存在祖先的祖先
                if(dp[i][depth-1]==-1) dp[i].push_back(-1);
                //状态转移方程
                else dp[i].push_back(dp[dp[i][depth-1]][depth-1]);
                if(dp[i][depth]!=-1) allover=false;
            }
            if(allover) break;
        }

构造完成后,我们可以从数组中直接读取每个结点的第1、2、4、8…个祖先了,下面开始搜索

    int getKthAncestor(int node, int k) {
        if(k==0) return node;//node的第0个祖先,说明是它自己
        if(node==-1) return -1;//node本身是-1,它也没有祖先
        int i=0;
        //判断k的第i位是否为1,对应的是node的第2^i个祖先,位置是dp[node]数组的第i个元素
        while(((1<<i)&k)==0) i++;
        if(i>=depth) return -1;//如果i过大,直接返回-1
        return getKthAncestor(dp[node][i],(1<<i)^k);//修改k后向下一层寻找
    }

参考资料:

https://cp-algorithms.com/graph/lca_binary_lifting.html

https://leetcode-cn.com/problems/kth-ancestor-of-a-tree-node/solution/li-kou-zai-zhu-jian-ba-acm-mo-ban-ti-ban-shang-lai/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值