此问题源自另一个Stack Overflow问题 - How do I improve remove duplicate algorithm?
问题中发布的要求是 -
需要返回删除重复项的数组的长度,但我们最多可以留下2个重复项。
示例 - [1, 1, 1, 2, 2, 3]
,新数组将为[1, 1, 2, 2, 3]
。所以新的长度是5。
OP给出的解决方案 -
def removeDuplicates(nums):
if nums is None:
return 0
if len(nums) == 0:
return 0
if len(nums) == 1:
return 1
new_array = {}
for num in nums:
new_array[num] = new_array.get(num, 0) + 1
new_length = 0
for key in new_array:
if new_array[key] > 2:
new_length = new_length + 2
else:
new_length = new_length + new_array[key]
return new_length
我尝试了一种解决方案,将循环量减少到一个循环。
def removeDuplicates1(nums):
if nums is None:
return 0
if len(nums) == 0:
return 0
if len(nums) == 1:
return 1
new_array = {}
length = 0
for num in nums:
n = new_array.get(num, 0)
new_array[num] = n + 1
if n <= 1:
length += 1
return length
之后,我试图将解决方案与原始解决方案进行对比,我认为我的解决方案应该在原始解决方案上提供至少一点改进,但timeit
的结果表明原始解决方案始终是更好(即使数组包含所有独特元素)。采取的时间 -
In [3]: l = list(range(1000))
In [4]: %timeit removeDuplicates(l)
1000 loops, best of 3: 390 s per loop
In [5]: %timeit removeDuplicates1(l)
1000 loops, best of 3: 412 s per loop
In [6]: l1 = [1] * 1000
In [7]: %timeit removeDuplicates(l1)
1000 loops, best of 3: 224 s per loop
In [9]: %timeit removeDuplicates1(l1)
1000 loops, best of 3: 304 s per loop
有人可以建议为什么会这样吗?我忽略了一些明显的东西吗?
答案 0 :(得分:3)
如果输入列表是列表(范围(x)),意味着没有重复,那么您的代码更快,但如果输入列表有大量重复项,那么您的代码会更慢。
我一直有时间与
collections.defaultdict - fastest
original proposal - next fastest (if duplicates)
your single loop proposal - slower, if there are duplicates
collections.counter - slowest
它们基本上都是一样的,所以它们总是在时间上接近。
defaultdict是最快的,因为原始提案基本上重复它,但defaultdict是python附带的核心库的一部分。我猜“不要重新发明轮子”适用。
但是为什么你的代码在使用单个循环时会变慢?考虑原始代码执行两个循环,因为有两个不同的东西要迭代。迭代原始数据列表一次,然后迭代唯一项(由于预期会有重复项,因此可能会更少)。
您的代码执行原始代码所执行的所有操作,但它会对原始数据列表中的每个元素执行此操作。可以把它想象成两个独立的循环,两个循环计数器。您仍然必须为原始列表中的所有元素执行第一个循环。但是第二个循环(你试图通过在原始循环中执行它来摆脱)现在必须为原始数据集中的每个项执行其代码。
通过更频繁地执行它而丢失一个循环所获得的结果,特别是对于原始数据中的重复项。