2023.6.12Leecode打卡

题目:

给你一棵树,树上有 n 个节点,按从 0 到 n-1 编号。树以父节点数组的形式给出,其中 parent[i] 是节点 i 的父节点。树的根节点是编号为 0 的节点。

树节点的第 k 个祖先节点是从该节点到根节点路径上的第 k 个节点。(这句话很关键,第一次看的时候理解错了

实现 TreeAncestor 类:

示例 1:

!https://assets.leetcode-cn.com/aliyun-lc-upload/uploads/2020/06/14/1528_ex1.png

输入:
["TreeAncestor","getKthAncestor","getKthAncestor","getKthAncestor"]
[[7,[-1,0,0,1,1,2,2]],[3,1],[5,2],[6,3]]

输出:
[null,1,0,-1]

解释:
TreeAncestor treeAncestor = new TreeAncestor(7, [-1, 0, 0, 1, 1, 2, 2]);

treeAncestor.getKthAncestor(3, 1);  // 返回 1 ,它是 3 的父节点
treeAncestor.getKthAncestor(5, 2);  // 返回 0 ,它是 5 的祖父节点
treeAncestor.getKthAncestor(6, 3);  // 返回 -1 因为不存在满足要求的祖先节点

提示:

  • TreeAncestor(int n, int[] parent) 对树和父数组中的节点数初始化对象。
  • getKthAncestor(int node, int k) 返回节点 node 的第 k 个祖先节点。如果不存在这样的祖先节点,返回 1 。
  • 1 <= k <= n <= 5 * 104
  • parent[0] == -1 表示编号为 0 的节点是根节点。
  • 对于所有的 0 < i < n ,0 <= parent[i] < n 总成立
  • 0 <= node < n
  • 至多查询 5 * 104 次

(题都没看懂说实话,第一次读完,没搞懂getkthAncestor里(node和第k个祖先是啥意思)

理解上其实就是,需要跳 k 次才能到达的第 k 个祖先节点,例如查找[3,2],会从3跳到1,再跳到0,我想我理解的应该是n=2*

理解好题意后,就到了代码编写,问题就是如何存放和调用这个求n结点的第k个祖先结点问题

我参考了一位我能看的懂的思路,那些什么增倍法真的看不懂阿喂(放在以后研究)

最粗暴的办法还是直接嵌套parent【parent【】】这样,但是其实可以优化,例如:当查询[3,2]时,会从3向上遍历到1,再到0;当查询[1,1]时,会从1遍历到0。可以发现,两者走了重复的路径。

保存路径的方法就要用到二维数组,给每个node一个二维index,index[0]记录了其在哪条路径中(可能存在多条路径中,只存一个就可以,这里也可能是继续优化的点),index[1]记录了其所在path中的下标

举例节点3这个,所有路径用一个数组保存为[[3,1,0], [4,1,0], [5,2,0], [6,2,0]]

比如节点3的index = [0, 0], 要找它的第k个节点,只需要找到path[index[3][0]][index[1] + k]

代码:

class TreeAncestor {
    int[] parent;
    int n;
    List<Integer>[] path;
    int[][] index;

    public TreeAncestor(int n, int[] parent) {
        this.parent=parent;
        this.n=n;
        
        boolean[] flag=new boolean[n];
        flag[0]=true;
        for(int node=1;node<n;++node){
            flag[parent[node]]=true;
        }
        List<Integer> le=new ArrayList<>();
        for(int node=1;node<n;++node){
            if(!flag[node]){
                le.add(node);
            }
        }
        index=new int[n][];
        path=new List[le.size()];
        int idx=0;
        for(int node :le){
            int count =1;
            index[node]=new int[]{idx,0};
            path[idx]=new ArrayList<>();
            path[idx].add(node);
            int tmp=node;
            while(parent[tmp]!=-1){
                tmp=parent[tmp];
                path[idx].add(tmp);
                index[tmp]=new int[]{idx,count++};
            }
            idx++;
        }
        System.out.println(1);
    }
    
    public int getKthAncestor(int node, int k) {
        int[] idx=index[node];
        if(path[idx[0]].size()-idx[1]<=k){
            return -1;
        }
        else{
            return path[idx[0]].get(k+idx[1]);
        }

    }
}

/**
 * Your TreeAncestor object will be instantiated and called as such:
 * TreeAncestor obj = new TreeAncestor(n, parent);
 * int param_1 = obj.getKthAncestor(node,k);
 */

还找到一个更加粗暴的:

class TreeAncestor {

private int[] parent;

private List<Integer>[] parentLists;

public TreeAncestor(int n, int[] parent) {
    this.parent = parent;
    this.parentLists = new List[n];
}

public int getKthAncestor(int node, int k) {
    if(parentLists[node] != null) {
        if(parentLists[node].size() <= k) {
            return -1;
        } else {
            return parentLists[node].get(k);
        }
    } else {
        dfs(node);
        return getKthAncestor(node, k);
    }
}

private void dfs(int node) {
    List<Integer> parents = new ArrayList<>();
    while(node != -1 && this.parentLists[node] == null) {
        parents.add(node);
        node = this.parent[node];
    }
    if(node != -1) {
        parents.addAll(this.parentLists[node]);
    }

    for(int i = 0; i < parents.size(); i++) {
        if(this.parentLists[parents.get(i)] != null) {
            break;
        }
        this.parentLists[parents.get(i)] = parents.subList(i, parents.size());
    }
}

}

作者:野兽先辈 链接:https://leetcode.cn/problems/kth-ancestor-of-a-tree-node/solutions/330507/jiu-shi-bao-li-gan-li-yong-sublist-slicejie-sheng-/ 来源:力扣(LeetCode) 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值