【问题标题】:Kth Ancestor of a Tree Node Challenge [closed]树节点挑战的第 K 个祖先 [关闭]
【发布时间】:2020-06-14 05:25:30
【问题描述】:

挑战如下:

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

实现函数getKthAncestor(int node, int k)返回第k个 给定节点的祖先。如果没有这样的祖先,则返回-1。

树节点的第 k 个祖先是该路径中的第 k 个节点 节点到根。

例子:

输入:

["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);  // returns 1 which is the parent of 3
treeAncestor.getKthAncestor(5, 2);  // returns 0 which is the grandparent of 5
treeAncestor.getKthAncestor(6, 3);  // returns -1 because there is no such ancestor

约束:

1 <= k <= n <= 5*10^4
parent[0] == -1 indicating that 0 is the root node.
0 <= parent[i] < n for all 0 < i < n
0 <= node < n
There will be at most 5*10^4 queries.

我很难理解一个人对此的解决方案。有人愿意解释他的最佳解决方案是如何工作的吗?这是一个新的挑战,在最近的 leetcode 竞赛中,没有重复。

class TreeAncestor(object):

    def __init__(self, n, parent):
        self.pars = [parent]
        self.n = n
        for k in range(17):
            row = []
            for i in range(n):
                p = self.pars[-1][i]
                if p != -1:
                    p = self.pars[-1][p]
                row.append(p)
            self.pars.append(row)


    def getKthAncestor(self, node, k):
        """
        :type node: int
        :type k: int
        :rtype: int
        """
        i = 0
        while k:
            if node == -1: break
            if (k&1):
                node = self.pars[i][node]
            i += 1
            k >>= 1
        return node

【问题讨论】:

  • 这是一个众所周知的问题,解决方案使用稀疏表和二进制提升,an example

标签: python python-3.x tree dynamic-programming


【解决方案1】:

哇。这是一个很酷的解决方案。

解决方案基于两个想法:

  • 在构建期间构建祖先矩阵 (self.pars)。
  • 将祖先图分解为大小为 1、2、4、8 等 (2^n) 的步长。

self.pars 是一个矩阵,其中行号n 表示行中第 i 个元素的 2^n 祖先(n 从 0 开始)。例如,在第 3 行中,我们将拥有树中节点中所有元素的第 8 个祖先。

然后在查询时,算法将获取节点的第 k 个祖先的请求分解为一系列 log(k) 步骤。每一步都是 k 的二进制表示中的一个数字。

例如,考虑 k=6。 9的二进制表示是1-1-0:

  • 数字 0(最后一位)为 0,所以什么也不做。
  • 数字 1 是 1。2^1 是 2,因此获取我们正在查看的节点的第二个祖先。
  • 数字2也是1。2^2是4,所以得到当前节点的第4个祖先。

我们已经完成了 - 在 3 个步骤中,我们一直到目标节点。

【讨论】:

  • 我想对我花了一些时间(以及一些打印语句)理解的部分添加一点解释,self.pars 构建过程。没有明显的步骤 1、2、4、8 系列。父列表允许查找父列表,即在路径中前进 1 步。如果我们将列表应用于自身,我们将得到一个包含 2 个步骤 (1+1) 的列表。但是当我们对新列表做同样的事情时,我们将没有 3 个步骤,而是 4 个步骤 (2+2)。然后是 4+4 等等。
  • MahdeenSkyYT - 这能回答你的问题吗?
猜你喜欢
  • 2014-12-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-02-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多