其中一个exercises(即#6)要求我们比较队列实现的性能(头部在beinning /列表的末尾)。这听起来可能有些不同,所以我试着想出来。这是我的代码
import timeit
class QueueStart(object):
'''Queue implementation with head in the beginning of a list'''
def __init__(self):
self.items = []
def enqueue(self, i):
self.items.append(i)
def dequeue(self):
return self.items.pop(0)
def isEmpty(self):
return len(self.items) == 0
def size(self):
return len(self.items)
class QueueEnd(object):
'''Queue implementation with head at the end of a list'''
def __init__(self):
self.items = []
def enqueue(self, item):
self.items.insert(0, item)
def dequeue(self):
return self.items.pop()
def isEmpty(self):
return len(self.items) == 0
def size(self):
return len(self.items)
# store results for further plotting
start_add_list = [] # QueueStart.enqueue(item) runtimes for inputs of different sizes
start_pop_list = [] # the same for QueueStart.dequeue(item)
end_add_list = [] # the same for QueueEnd.enqueue(item)
end_pop_list = [] # the same for QueueEnd.dequeue(item)
for i in range(100000, 500000, 10000):
qs = QueueStart()
qs.items = list(range(i))
qe = QueueEnd()
qe.items = list(range(i))
start_add = timeit.Timer('qs.enqueue(1)', 'from __main__ import qs')
start_pop = timeit.Timer('qs.dequeue()', 'from __main__ import qs')
end_add = timeit.Timer('qe.enqueue(1)', 'from __main__ import qe')
end_pop = timeit.Timer('qe.dequeue()', 'from __main__ import qe')
start_add_list.append(start_add.timeit(number=1000))
start_pop_list.append(start_pop.timeit(number=1000))
end_add_list.append(end_add.timeit(number=1000))
end_pop_list.append(end_pop.timeit(number=1000))
众所周知insert
和pop(index)
是O(n)。但有趣的是,从图表中我们看到insert(0, item)
的时间是pop(0)
的两倍。那个观察让我很奇怪,为什么会这样。从表面上看,两种方法看起来非常相似,但显然,引擎盖下有一些有趣的东西。所以,问题是:你能帮我解决一下吗?
答案 0 :(得分:0)
关于CPython的list
实施的一些阅读:http://www.laurentluce.com/posts/python-list-implementation/
基本上,列表的设计内存大约是他们在任何给定时间所需的内存的两倍,因此它们可以稍微改变长度,而无需重新分配内存。当列表需要更多内存时,它们有时需要将整个列表移动到内存中具有足够空间的另一个位置。当它们缩小时,它们可以在列表末尾释放内存。