您的算法的时间复杂度为O(kn),其中k 是字符串中唯一字符的数量。如果k 是一个常数,那么它就是O(n)。由于问题描述清楚地限制了元素的替代数量(“假设小写(ASCII)字母”),因此k 是恒定的,并且您的算法在O(n) 时间运行这个问题。即使 n 将增长到无穷大,您也只会对字符串进行O(1) 切片,而您的算法将保持为O(n)。如果您删除了track,那么它将是O(n²):
In [36]: s = 'abcdefghijklmnopqrstuvwxyz' * 10000
In [37]: %timeit firstUniqChar(s)
100 loops, best of 3: 18.2 ms per loop
In [38]: s = 'abcdefghijklmnopqrstuvwxyz' * 20000
In [37]: %timeit firstUniqChar(s)
10 loops, best of 3: 36.3 ms per loop
In [38]: s = 'timecomplexity' * 40000 + 'a'
In [39]: %timeit firstUniqChar(s)
10 loops, best of 3: 73.3 ms per loop
它几乎认为T(n) 仍然具有O(n) 复杂性-它与字符串中的字符数完全线性地缩放,即使这是您算法的最坏情况-没有单一的独特的性格。
我将在这里介绍一种效率不高但简单而聪明的方法;先用collections.Counter统计字符直方图;然后遍历找到那个的字符
from collections import Counter
def first_uniq_char_ultra_smart(s):
counts = Counter(s)
for i, c in enumerate(s):
if counts[c] == 1:
return i
return -1
first_uniq_char('timecomplexity')
这具有O(n)的时间复杂度; Counter 以 O(n) 时间计算直方图,我们需要再次枚举字符串中的 O(n) 字符。但是在实践中,我相信我的算法具有较低的常数,因为它使用Counter 的标准字典。
让我们做一个非常愚蠢的蛮力算法。由于您可以假设字符串仅包含小写字母,因此请使用该假设:
import string
def first_uniq_char_very_stupid(s):
indexes = []
for c in string.ascii_lowercase:
if s.count(c) == 1:
indexes.append(s.find(c))
# default=-1 is Python 3 only
return min(indexes, default=-1)
让我们在 Python 3.5 上测试我的算法和在其他答案中找到的一些算法。我选择了一个对 my 算法不利的案例:
In [30]: s = 'timecomplexity' * 10000 + 'a'
In [31]: %timeit first_uniq_char_ultra_smart(s)
10 loops, best of 3: 35 ms per loop
In [32]: %timeit karin(s)
100 loops, best of 3: 11.7 ms per loop
In [33]: %timeit john(s)
100 loops, best of 3: 9.92 ms per loop
In [34]: %timeit nicholas(s)
100 loops, best of 3: 10.4 ms per loop
In [35]: %timeit first_uniq_char_very_stupid(s)
1000 loops, best of 3: 1.55 ms per loop
所以,我的愚蠢算法是最快的,因为它在最后找到a 并退出。而且我的智能算法最慢,除了这种最坏的情况之外,我的算法性能不佳的另一个原因是OrderedDict 是在 Python 3.5 上用 C 编写的,而 Counter 是在 Python 中编写的。
让我们在这里做一个更好的测试:
In [60]: s = string.ascii_lowercase * 10000
In [61]: %timeit nicholas(s)
100 loops, best of 3: 18.3 ms per loop
In [62]: %timeit karin(s)
100 loops, best of 3: 19.6 ms per loop
In [63]: %timeit john(s)
100 loops, best of 3: 18.2 ms per loop
In [64]: %timeit first_uniq_char_very_stupid(s)
100 loops, best of 3: 2.89 ms per loop
所以看来我的“愚蠢”算法一点也不愚蠢,它利用了 C 的速度,同时最大限度地减少了 Python 代码运行的迭代次数,并且在这个问题上明显获胜。