我正在尝试解决来自codesignal.com的以下挑战:
给出一个仅包含从1到a.length范围内的数字的数组a,找到第一个重复的数字,其第二次出现的索引最小。换句话说,如果重复的数字多于1个,则返回其第二次出现的索引小于另一个数字的第二次出现的索引的数字。如果没有这样的元素,则返回-1。
示例
For a = [2, 1, 3, 5, 3, 2]
,输出应为
firstDuplicate(a) = 3
。
有2个重复项:数字2和3。第二次出现3的索引比第二次出现2的索引小,所以答案是3。
对于a = [2, 4, 3, 5, 1]
,输出应为
firstDuplicate(a) = -1
。
执行时间限制为4秒。
保证的约束条件是:
1 ≤ a.length ≤ 10^5
和
1 ≤ a[i] ≤ a.length
所以我的代码是:
def firstDuplicate(a):
b = a
if len(list(set(a))) == len(a):
return -1
n = 0
answer = -1
starting_distance = float("inf")
while n!=len(a):
value = a[n]
if a.count(value) > 1:
place_of_first_number = a.index(value)
a[place_of_first_number] = 'string'
place_of_second_number = a.index(value)
if place_of_second_number < starting_distance:
starting_distance = place_of_second_number
answer = value
a=b
n+=1
if n == len(a)-1:
return answer
return answer
在站点进行的22项测试中,我通过了所有测试,直到#21,因为测试列表很大,执行时间超过了4秒。有什么技巧可以减少执行时间,同时使代码大致相同?
答案 0 :(得分:2)
创建一个新集合并在新列表中找到它,如果该集合返回了元素:
def firstDuplicate(a):
dup = set()
for i in range(len(a)):
if a[i] in dup:
return a[i]
else:
dup.add(a[i])
return -1
答案 1 :(得分:0)
正如@erip在评论中指出的那样,您可以遍历列表,将项目添加到集合中,如果该项目已经在集合中,则它是具有最低索引的重复项,因此您可以简单地退货;或如果返回循环末尾而没有找到重复项,则返回-1:
def firstDuplicate(a):
seen = set()
for i in a:
if i in seen:
return i
seen.add(i)
return -1
答案 2 :(得分:0)
这只是一个主意,我没有验证它,但是应该可以。似乎没有内存限制,只有时间限制。因此,利用空间来交易时间可能是一种实用的方法。计算复杂度为O(n)
。此算法还取决于数字范围在1到len(a)
之间的条件。
def first_duplicate(a):
len_a = len(a)
b = [len_a + 1] * len_a
for i, n in enumerate(a):
n0 = n - 1
if b[n0] == len_a + 1:
b[n0] = len_a
elif b[n0] == len_a:
b[n0] = i
min_i = len_a
min_n = -1
for n0, i in enumerate(b):
if i < min_i:
min_i = i
min_n = n0 + 1
return min_n
更新:
此解决方案不如@blhsing的set()
解决方案快。但是,如果它是用C实现的,则可能会有所不同-这有点不公平,因为set()
是内置函数,它作为CPython的其他核心函数在C中实现。