0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 2 8 9
你需要注意的是,重复元素的引入导致在使用的数字集中出现了一个洞口 - 列表中不再有7
。
实际上,每次引入重复值都会使得可能的值彻底消失,你可以利用这个事实来优化你的算法。关键在于不是为每个可能的数字搜索列表,而是利用每一次搜索来找到你应该寻找的下一个数字(比当前数字大的最小数字)。考虑以下包含13个元素的列表:
{0, 1, 1, 1, 2, 3, 0, 7, 9, 3, 9, 3, 9}
0
。我们发现它是一个重复项,但我们还发现下一个可能的数字是1
,因此我们记住它以备下一阶段使用。1
(重复项)和下一个是2
,然后搜索2
(唯一项)和下一个是3
。3
(重复项)时,我们发现下一个可能的数字实际上是7
,因此我们完全可以跳过4
、5
和6
。当然,我们也会因同样的原因跳过8
(在搜索7
时,我们发现下一个是9
)。9
时,没有下一个可能的术语,因此我们可以在那个点停止。list = [0,1,1,1,2,3,0,7,9,3,9,3,9]
n = len(list)
# Initial search term and begin loop for each term.
currN = 0
while currN <= n - 2:
print ("Checking for %d"%(currN))
# Next search term, initially beyond max, and dupe detector.
nextN = n
count = 0
# Check every list value.
for val in list:
# Count occurrences.
if val == currN:
count += 1
# Update next search term if needed. If no value
# between curr and n, nextN will remain at n
# and loop will exit.
if val > currN and val < nextN:
nextN = val
# Inform if duplicated and move to next search term.
if count > 1:
print ("%d is duplicated"%(currN))
currN = nextN
如果你想要更高的性能,那么还有另一种可能的优化方案。目前,你在每次迭代中都会检查列表中的每个值,但这并不是必要的。
例如,一旦你检查过 0
,再次检查第一个索引 {0}
就没有意义了,因为它永远不会再影响结果。
同样地,一旦你检查过 1
,你就不需要再次访问前四个元素 {0, 1, 1, 1}
了。
因此,可以通过记住不仅是下一个可能的搜索项,而且还有可能找到它的最早时间点,以便后续迭代在搜索时处理较少的元素来获得优势。
实现该方法的方式是从一个变量位置开始搜索,最初位于列表的开头。然而,在每次迭代中,你将更新此位置为列表中第一个大于当前正在处理的搜索项的项的位置。
如上所述,我不认为这些建议会给你O(n),但肯定比朴素的方法好。对于没有重复项的情况(例如{1, 2, 3, 4}
),操作次数将接近n * n
,而对于列表中只有一个重复值的情况(例如{1, 1, 1, 1}
),操作次数将降至约n
。
重复项越多,运行时间就越好。
1 ...这看起来非常像Python3代码 :-)