Python Codility Frog River 一次复杂度
Posted
技术标签:
【中文标题】Python Codility Frog River 一次复杂度【英文标题】:Python Codility Frog River One time complexity 【发布时间】:2019-03-04 13:44:57 【问题描述】:所以这是另一个可能是众所周知的 codility 平台的方法,关于青蛙过河的任务。很抱歉,如果这个问题被问得不好,这是我在这里的第一篇文章。
目标是找到青蛙可以跳到河对岸的最早时间。 例如,给定 X = 5 和数组 A 使得:
A[0] = 1
A[1] = 3
A[2] = 1
A[3] = 4
A[4] = 2
A[5] = 3
A[6] = 5
A[7] = 4
函数应该返回 6。
示例测试:(5, [1, 3, 1, 4, 2, 3, 5, 4])
完整的任务内容: https://app.codility.com/programmers/lessons/4-counting_elements/frog_river_one/
所以这是我第一个明显的方法:
def solution(X, A):
lista = list(range(1, X + 1))
if X < 1 or len(A) < 1:
return -1
found = -1
for element in lista:
if element in A:
if A.index(element) > found:
found = A.index(element)
else: return -1
return found
X = 5
A = [1,2,4,5,3]
solution(X,A)
这个解决方案是 100% 正确的,在性能测试中得到 0%。
所以我认为更少的行+列表理解会得到更好的分数:
def solution(X, A):
if X < 1 or len(A) < 1:
return -1
try:
found = max([ A.index(element) for element in range(1, X + 1) ])
except ValueError:
return -1
return found
X = 5
A = [1,2,4,5,3]
solution(X,A)
这个也可以工作,性能为 0%,但无论如何它更快。
我还找到了 deanalvero (https://github.com/deanalvero/codility/blob/master/python/lesson02/FrogRiverOne.py) 的解决方案:
def solution(X, A):
# write your code in Python 2.6
frog, leaves = 0, [False] * (X)
for minute, leaf in enumerate(A):
if leaf <= X:
leaves[leaf - 1] = True
while leaves[frog]:
frog += 1
if frog == X: return minute
return -1
此解决方案在正确性和性能测试中达到 100%。
我的问题可能是因为我不太了解这个时间复杂度的事情。请告诉我最后一个解决方案比我的第二个解决方案更好吗?它在 for 循环中有一个 while 循环!它应该很慢,但事实并非如此。
【问题讨论】:
自动假设具有更多循环的算法总是较慢是一个常见的错误。此外,您甚至没有计算index
中的隐式循环(这是O(n)
,因为A
是一个无序数组)。
【参考方案1】:
嵌套循环的数量并不能直接告诉您有关时间复杂度的任何信息。令 n 为输入数组的长度。 while 循环的内部需要 平均 O(1) 时间,尽管其最坏情况的时间复杂度为 O(n)。快速解决方案使用布尔数组 leaves,其中在每个索引处,如果有叶子,则其值为 true,否则为 false。在整个算法期间,while 循环的内部被排除不超过 n 次。外部 for 循环也只执行 n 次。这意味着算法的时间复杂度是O(n)。
【讨论】:
【参考方案2】:关键是你的两个初始解都是二次的。它们涉及对每个父元素的 O(n) 次内部扫描(导致 O(n**2))。
快速解决方案最初似乎遭受了同样的命运,因为很明显它包含一个循环中的一个循环。但是内部的 while 循环不会对每个“叶子”进行完全扫描。看看 'frog' 被初始化的地方,你会注意到 while 循环有效地从每个叶子停止的地方开始。
【讨论】:
【参考方案3】:答案已经被告知,但我将添加一个我认为可能有助于您理解的可选解决方案:
def save_frog(x, arr):
# creating the steps the frog should make
steps = set([i for i in range(1, x + 1)])
# creating the steps the frog already did
froggy_steps = set()
for index, leaf in enumerate(arr):
froggy_steps.add(leaf)
if froggy_steps == steps:
return index
return -1
【讨论】:
您也可以在这种情况下测试len(froggy_steps) == x
,而不是比较两组,如果这两组的大小很大,这应该会略有改进。
解决方案似乎错误,但通过了所有测试用例。 froggy_steps
集应该只存储小于或等于 X 的值。例如如果 X = 4 和 A = [1,3,5,7,4,2],就我阅读问题而言,答案应该是 5。上述解决方案返回 -1。还是我错过了什么?【参考方案4】:
这是一个解决方案,您可以在其中获得 100% 的正确性和性能。
def solution(X, A):
i = 0
dict_temp =
while i < len(A):
dict_temp[A[i]] = i
if len(dict_temp) == X:
return i
i += 1
return -1
【讨论】:
更改A的元素时不起作用。如X, A = 5, [1,3,1,4,10 ,9,2,3, 5, 4,7]
,输出为(5, 1: 2, 3: 1, 4: 3, 10: 4, 9: 5)
工作完美,但是另一个输入也失败了。 X, A = 5, [1,3,1,6,4,2,3,4]
输出 5
但是在第 5 距离内没有叶子。
我认为 if A[i] <= X:
在 while 行之后是必需的。【参考方案5】:
这是我考虑数字级数总和的 100% 解决方案。
def solution(X, A):
covered = [False] * (X+1)
n = len(A)
Sx = ((1+X)*X)/2 # sum of the numeric progression
for i in range(n):
if(not covered[A[i]]):
Sx -= A[i]
covered[A[i]] = True
if (Sx==0):
return i
return -1
【讨论】:
【参考方案6】:来自@sphoenix 的优化解决方案,无需比较两个set
s,它不是很好。
def solution(X, A):
found = set()
for pos, i in enumerate(A, 0):
if i <= X:
found.add(i)
if len(found) == X:
return pos
return -1
还有一种更优化的二进制数组解决方案
def solution(X, A):
steps, leaves = X, [False] * X
for minute, leaf in enumerate(A, 0):
if not leaves[leaf - 1]:
leaves[leaf - 1] = True
steps -= 1
if 0 == steps:
return minute
return -1
最后一个更好,资源更少。 set 比二进制列表消耗更多的资源(内存和 CPU)。
【讨论】:
【参考方案7】:def solution(X, A):
# if there are not enough items in the list
if X > len(A):
return -1
# else check all items
else:
d =
for i, leaf in enumerate(A):
d[leaf] = i
if len(d) == X:
return i
# if all else fails
return -1
【讨论】:
【参考方案8】:我尽量使用简单的指令。
def solution(X, A):
if (X > len(A)): # check for no answer simple
return -1
elif(X == 1): # check for single element
return 0
else:
std_set = i for i in range(1,X+1) # list of standard order
this_set = set(A) # set of unique element in list
if(sum(std_set) > sum(this_set)): # check for no answer complex
return -1
else:
for i in range(0, len(A) - 1):
if std_set:
if(A[i] in std_set):
std_set.remove(A[i]) # remove each element in standard set
if not std_set: # if all removed, return last filled position
return(i)
我猜这段代码可能无法满足运行时要求,但它是我能想到的最简单的代码
【讨论】:
【参考方案9】:我正在使用集合中的 OrderedDict 和前 n 个数字的总和来检查青蛙是否能够越过。
def solution(X, A):
from collections import OrderedDict as od
if sum(set(A))!=(X*(X+1))//2:
return -1
k=list(od.fromkeys(A).keys())[-1]
for x,y in enumerate(A):
if y==k:
return x
【讨论】:
以上是关于Python Codility Frog River 一次复杂度的主要内容,如果未能解决你的问题,请参考以下文章