我正在研究Python中的数据结构问题,我必须以最有效的方式颠倒数组中单词的顺序。我提出了以下问题的解决方案
def reverse(arr, st, end):
while st < end:
arr[st], arr[end] = arr[end], arr[st]
end -= 1
st += 1
def reverse_arr(arr):
arr = arr[::-1]
st_index = 0
length = len(arr)
for i, val in enumerate(arr):
if val == ' ':
end_index = i-1
reverse(arr, st_index, end_index)
st_index = end_index + 2
if i == length - 1:
reverse(arr, st_index, length-1)
return arr
如果arr是:
arr = [ 'p', 'e', 'r', 'f', 'e', 'c', 't', ' ',
'm', 'a', 'k', 'e', 's', ' ',
'p', 'r', 'a', 'c', 't', 'i', 'c', 'e' ]
它返回:
['p', 'r', 'a', 'c', 't', 'i', 'c', 'e', ' ',
'm', 'a', 'k', 'e', 's', ' ',
'p', 'e', 'r', 'f', 'e', 'c', 't']
解决方案工作正常,但我不明白这个算法的复杂性是如何O(n)。编写的是,每个项目以恒定数量的动作遍历数组两次是线性的,即O(n),其中n是数组的长度。
我认为它应该超过O(n),因为根据我的说法,每个单词的长度不固定,反转每个单词的时间复杂度取决于单词的长度。有人可以用更好的方式解释这个吗?
答案 0 :(得分:3)
reverse
将为每个单词调用一次。在那次通话中,它会为每个角色做一定量的工作。
您可以根据单词数量和单词的平均长度(即O(wordCount*averageWordLength)
)或数组中的字符总数来表示。如果你选择后者,很容易看出你仍然在为每个角色做一定量的工作(因为reverse
和reverse_arr
每个角色都有不变的工作量字符,并且没有两个reverse
调用将包含相同的字符),导致O(characterCount)
复杂性。
我不会假设&#34;阵列的长度&#34;在解释中指的是单词的数量,而是字符的数量,或者他们假设单词长度具有固定的上限(其中复杂性确实是O(wordCount)
)。
TL; DR: n
O(n)
中characterCount
为wordCount
,而不是<GenerateManifests>true</GenerateManifests>
。
答案 1 :(得分:2)
def reverse(arr, st, end):
while st < end:
arr[st], arr[end] = arr[end], arr[st]
end -= 1
st += 1
def reverse_Cha(arr):
arr = arr[::-1]
st_index = 0
length = len(arr)
for i, val in enumerate(arr):
if val == ' ':
end_index = i-1
reverse(arr, st_index, end_index)
st_index = end_index + 2
if i == length - 1:
reverse(arr, st_index, length-1)
return arr
def reverse_Jon(arr):
r = [ch for word in ' '.join(''.join(arr).split()[::-1]) for ch in word]
return r
def reverse_Nua(arr):
rev_arr = list(' '.join(''.join(arr).split()[::-1]))
return rev_arr
如果我们考虑了3个提议的解决方案:您的reverse_Cha
,Jon Clements的reverse_Jon
,我的reverse_Nua
。
我们注意到,当我们使用O(n)
时,当我们检查列表的每个元素(长度[::-1]
)等时,我们会n
。
reverse_Cha
使用[::-1]
,然后检查每个元素两次(然后阅读以进行交换),因此复杂性取决于我们写为{{O(3n+c)
的元素总数(O(n)
1}}(+c
来自O(1)
次操作))
reverse_Jon
使用[::-1]
,然后检查每个元素两次(检查每个单词的每个字符),因此复杂性取决于元素总数和单词数(O(3n+m)
我们写为O(n+m)
(m
字数))
reverse_Nua
使用[::-1]
,然后坚持使用python list函数,因此复杂性仍然取决于元素的总数(这次只是O(n)
}
作为绩效期(1e6循环),我们得到reverse_Cha
:2.785867s; reverse_Jon
:4.11845s(由于for
); reverse_Nua
:1.185973s。
答案 2 :(得分:1)
我认为这是一个纯粹的理论问题,因为在现实世界的应用程序中,您可能宁愿将列表拆分为单字子列表,然后以相反的顺序重新加入子列表 - 这需要更多内存,但要快得多。
话虽如此,我想指出你所展示的算法确实是O(n) - 它取决于你的单词的总长度,而不是长度个别的话。换句话说:20个3个字母的单词,6个10个字母的单词,10个6个字母的单词需要相同的时间......你总是只翻阅每个字母两次:一次是在单个单词的反转期间(这是第一次调用reverse
中的reverse_arr
和整个数组的反转期间(第二次调用reverse
)。