题目是LeetCode第179场周赛的第四题,链接:T秒后青蛙的位置。问题较长就不搬运了,详见原题描述。这道题是hard难度的,当时周赛时只把前三题做了,这道题也是后来才做的,不过后来做倒是让我想出来了一个时间上超过了100%的方法,java版代码只需要1ms。
类似周赛第三题,同样可以采用建树然后DFS的方法,不过对于这道题,还是可以用更加简便的从底到顶的方法。
首先需要说明一下这道题数据的特殊性,以1为根节点所构造出来的树,其子节点的值一定是比父节点大的,而且因为只有n-1条边从而不存在环。也就是说,每个节点只可能有一个父节点(当然子节点的数目就不限了)。
基于以上观察,我们使用一个数组father
记录各个节点的父节点,另一个数组sonNum
记录各个节点的子节点数目。
那么怎么计算概率呢,其实很简单,我们从idx=target
出发,一直沿着父节点father[idx]
往上走(循环使用idx=father[idx]
),当到达根节点(father[idx]==0
)时就得到了唯一可能的一条路径,这条路径上各个父节点的子节点数目为sonNum[father[idx]]
,概率的计算就是将所有的1/sonNum[father[idx]]
乘起来就是了。
但是需要注意的是可以返回此概率的情况,假设此路径一共经过count
次跳转(其实就是边数=节点数-1),那么只有当t==count
(跳转次数刚好等于秒数)或者count < t && targetCount == 1 && target != 1
(秒数大于跳转次数,此时需要目标节点为叶子节点,才能在target上原地踏步),其他情况都是直接返回0。
复杂度分析:初始化father
和sonNum
需要时间复杂度
O
(
N
)
O(N)
O(N),计算概率需要的时间复杂度为树的高度,最坏情况为只有左节点或右节点且target为叶子节点的情况,此时时间复杂度为
O
(
N
)
O(N)
O(N)。因此总的时间复杂度为
O
(
N
)
O(N)
O(N)。空间复杂度就是几个辅助数组所占用空间,所以为
O
(
N
)
O(N)
O(N)。
JAVA版代码如下:
class Solution {
public double frogPosition(int n, int[][] edges, int t, int target) {
if (n == 1) {
return target == 1 && t >= 1 ? 1.0 : 0.0;
}
int[] father = new int[n + 1];
int[] sonNum = new int[n + 1];
int targetCount = 0;
for (int i = 0; i < n - 1; ++i) {
if (edges[i][0] < edges[i][1]) {
++sonNum[edges[i][0]];
father[edges[i][1]] = edges[i][0];
}
else {
++sonNum[edges[i][1]];
father[edges[i][0]] = edges[i][1];
}
if (edges[i][0] == target || edges[i][1] == target) {
++targetCount;
}
}
double probability = 1.0;
int count = 0;
int idx = target;
while (father[idx] > 0) {
probability /= sonNum[father[idx]];
idx = father[idx];
++count;
}
if ((count < t && targetCount == 1 && target != 1) || count == t) {
return probability;
}
return 0.0;
}
}
提交结果如下:
![](https://i-blog.csdnimg.cn/blog_migrate/3744bc4466f54005ce9bec0e013a58a6.png)
Python版代码如下:
class Solution:
def frogPosition(self, n: int, edges: List[List[int]], t: int, target: int) -> float:
if n == 1:
return 1.0 if target == 1 and t >= 1 else 0.0
father = [0 for _ in range(n + 1)]
sonNum = father.copy()
targetCount = 0
for i, j in edges: #初始化各节点父节点,以及各节点的子节点数目
if i < j:
father[j] = i
sonNum[i] += 1
else:
father[i] = j
sonNum[j] += 1
if i == target or j == target: #统计target节点出现次数
targetCount += 1
idx = father[target]
count = 0
probability = 1.0
while idx != 0: #从target往上走直到节点1
count += 1 #统计跳转次数(也即边数)
probability /= sonNum[idx] #计算从target到节点1的总概率
idx = father[idx]
#可以返回概率的情况:要么target节点是叶子节点,要么跳转次数刚好等于秒数t
if (count < t and targetCount == 1 and target != 1) or count == t:
return probability
else:
return 0.0
提交结果如下:
![](https://i-blog.csdnimg.cn/blog_migrate/53fc96a876ad9e622221f0b30de6643c.png)