返回
用鸡蛋的正确姿势——Google 经典算法面试题解答
前端
2024-01-18 10:02:56
有人也许会想,先从一层楼摔,如果鸡蛋碎了,说明安全楼层就是一楼;如果没碎,继续往上,从二层摔,直到鸡蛋碎掉或安全到达100层。这种方法的最坏情况是需要100次尝试,平均情况是50次尝试。
但我们能做得更好吗?
从中间开始
我们可以从50层楼开始,如果鸡蛋碎了,说明安全楼层在149层;如果没碎,说明安全楼层在51100层。这样,我们就把100层楼的范围缩小到50层楼。
我们继续从25层楼开始,以此类推,直到找到安全楼层。最坏情况是需要14次尝试,平均情况是7次尝试。
递归和动态规划
上面的方法可以用递归来实现。我们可以定义一个函数 egg_drop(n, k)
,其中 n
是建筑物的层数,k
是鸡蛋的数量。该函数返回找到安全楼层所需的最小尝试次数。
def egg_drop(n, k):
# 如果只有一个鸡蛋,那么最坏情况需要尝试 n 次
if k == 1:
return n
# 如果没有鸡蛋,那么无论尝试多少次都找不到安全楼层
if n == 0:
return 0
# 尝试从第 i 层楼摔鸡蛋
for i in range(1, n):
# 如果鸡蛋碎了,那么安全楼层就在第 i 层或以下
if egg_drop(i - 1, k - 1) == 0:
return i
# 如果鸡蛋没碎,那么安全楼层就在第 i 层或以上
if egg_drop(n - i, k) == 0:
return i
# 如果尝试了所有楼层,都没找到安全楼层,那么安全楼层就是第 n 层
return n
该函数的时间复杂度是 O(nk^2)。
我们还可以用动态规划来实现该问题。我们可以定义一个二维数组 dp[n][k]
,其中 dp[n][k]
表示找到安全楼层所需的最小尝试次数。
def egg_drop_dp(n, k):
# 初始化 dp 数组
dp = [[0 for _ in range(k + 1)] for _ in range(n + 1)]
# 如果只有一个鸡蛋,那么最坏情况需要尝试 n 次
for i in range(1, n + 1):
dp[i][1] = i
# 如果没有鸡蛋,那么无论尝试多少次都找不到安全楼层
for j in range(1, k + 1):
dp[0][j] = 0
# 尝试从第 i 层楼摔鸡蛋
for i in range(1, n + 1):
for j in range(2, k + 1):
# 如果鸡蛋碎了,那么安全楼层就在第 i 层或以下
for l in range(1, i):
dp[i][j] = min(dp[i][j], 1 + max(dp[l - 1][j - 1], dp[i - l][j]))
# 如果鸡蛋没碎,那么安全楼层就在第 i 层或以上
dp[i][j] = min(dp[i][j], 1 + dp[i - 1][j])
# 返回找到安全楼层所需的最小尝试次数
return dp[n][k]
该函数的时间复杂度是 O(nk^2)。
总结
我们讨论了三种解决 Google 经典算法面试题的方法:暴力法、递归和动态规划。递归和动态规划的方法都可以将时间复杂度降低到 O(nk^2)。