LeetCode T1483 树节点的第k个祖先 倍增法求解 记录

题目描述

题目分析:

因为看题目的数据可以得到,当树退化成一个单链表时,时间复杂度变为 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;
    }

 

 

 

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值