队列的高效实现-入队和出队的时间复杂度

时间:2019-01-02 17:56:53

标签: python data-structures queue time-complexity

我目前正在阅读有关数据结构/算法的教科书。练习之一是使用python列表结构实现高效的队列:入队和出队的时间复杂度平均需要为O(1)。该书说,对于特定的出队情况,时间复杂度应仅为O(n),其余时间应为O(1)。我实现了它,使得队列的尾部是列表的结尾,队列的前部是列表的开头;当我使一个元素出队时,我不会从列表中删除它,而只是增加一个计数器,以便该方法将知道列表中的哪个元素代表队列的开头。这是我的代码:

class FasterQueue:
    def __init__(self):
        self.items = []
        self.index = 0
    def enqueue(self, item):
        self.items.append(item)
    def dequeue(self):
        index = self.index
        self.index += 1
        return self.items[index]
    def isEmpty(self):
        return self.items == []
    def size(self):
        return len(self.items)

我的问题:这本书说在某些情况下出队应该取O(1)。我不知道这是什么情况,因为出队似乎总是只会在某个索引处获取值。我对队列的执行是否无效或是否缺少其他内容?还是这本教科书只是在寻找另一个更常见的实现方式?

非常感谢您的帮助。

4 个答案:

答案 0 :(得分:0)

使它使用更多类似Python的功能,我会做类似的事情:

class FasterQueue:
    def __init__(self):
        self.items = []
        self.index = 0

    def enqueue(self, item):
        self.items.append(item)

    def dequeue(self):
        # do this first so IndexErrors don't cause us ignore items
        obj = self.items[self.index]
        # release the reference so we don't "leak" memory
        self.items[self.index] = None
        self.index += 1
        return obj

    def isEmpty(self):
        return self.index == len(self.items)

    def size(self):
        return len(self.items)

    def try_shrink(self):
        nactive = len(self.items) - self.index
        if nactive + 2 < self.index // 2:
            self.items = self.items[self.index:]
            self.index = 0

已经添加了一个try_shrink方法,该方法试图释放已用的内存空间,并且在dequeue()的末尾调用此方法可能很有用,否则列表将任意增长,并浪费大量内存内存量。其中的常数并不令人惊讶,但应防止其缩小得太频繁。该操作将为O(n),可能就是被暗示的内容

答案 1 :(得分:0)

据我了解,入队应在结尾处插入,出队应从开始处删除。 所以代码应该是

class FasterQueue:
def __init__(self):
    self.items = []

def enqueue(self, item):
    self.items.append(item)

def dequeue(self):
    if self.items:
        return self.items.pop(0)
    print("Underflow")

def isEmpty(self):
    return self.items == []

def size(self):
    return len(self.items)

答案 2 :(得分:0)

为完整起见,这是使用环形缓冲区的答案。

此示例永远以O(1)的身份运行,但是它通过限制队列的大小来实现。如果要允许队列增长或动态调整大小,那么您将再次遇到O(n)行为。

换句话说,仅当您不以某种方式管理列表的大小时,才具有O(1)。这就是问题的重点。

好的,这是具有固定队列长度的环形缓冲区实现。

class FasterQueue:
    def __init__(self, nsize=100):
        self.items = [0]*nsize
        self.nin = 0
        self.nout = 0
        self.nsize = nsize
    def enqueue(self, item):
        next = (self.nin+1)%self.nsize
        if next != self.nout:
            self.items[self.nin] = item
            self.nin = next
            print self.nin, item
        else:
            raise ValueError
    def dequeue(self):
        if self.nout != self.nin:
            retv = self.items[self.nout]
            self.nout = (self.nout+1)%self.nsize
            return retv
        else:
            raise ValueError

    def printQ(self):
        if self.nout < self.nin:
            print( ' '.join(self.items[self.nout:self.nin]) )
        elif self.nout > self.nin:
            print( ' '.join(self.items[self.nout:]+self.items[:self.nin]) )

    def isEmpty(self):
        return self.nin == self.nout
    def size(self):
        return (self.nin - self.nout + self.nsize)%self.nsize

q = FasterQueue()

q.enqueue( 'a' )
q.enqueue( 'b' )
q.enqueue( 'c' )

print( 'queue items' )
q.printQ()
print( 'size %d'%q.size() )


while True:
    try:
        print( 'dequeue %s'%q.dequeue() )
        print( 'queue items' )
        q.printQ()
    except:
        print( 'empty' )
        break

答案 3 :(得分:0)

O(n)是管理列表长度的必要结果。

这是一个解决方案。通常,它以O(1)的形式运行,由于出队方法内部发生了额外的步骤,因此有时为O(n)。

当列表变得太大并触发清理时,将发生O(n)步骤。请注意,通常应在出队方法中专门执行此操作。在外面做,往往会更复杂,效率更低。

class FasterQueue:
    def __init__(self, maxwaste=100):
        self.items = []
        self.nout = 0
        self.maxwaste = maxwaste
    def enqueue(self, item):
        self.items.append(item)
    def dequeue(self):
        if len(self.items):
            retv = self.items[self.nout]
            self.nout += 1
            if self.nout >= self.maxwaste:
                self.items = self.items[self.nout:]
                self.nout =0
            return retv
        else:
            print( 'empty' )
            raise ValueError
    def listQ(self):
        return ' '.join( self.items[self.nout:] )
    def isEmpty(self):
        return self.nout == len(self.items)
    def size(self):
        return len(self.items) - self.nout

q = FasterQueue(5)

for n in range(10):
    q.enqueue( str(n) )

print( 'queue size %d  nout %d items %s'%(q.size(),q.nout,q.listQ()) )
print( q.items )

while True:
    try:
        print( 'dequeue %s'%q.dequeue() )
        print( 'queue size %d  nout %d items %s'%(q.size(),q.nout,q.listQ()) )
        print( q.items )
    except:
        print( 'empty' )
        break

运行上面的代码将产生以下输出,请注意,当超过maxwaste时,将恢复浪费的内存。为了演示操作,此处将Maxwaste设置为较小。

queue size 10  nout 0 items 0 1 2 3 4 5 6 7 8 9
['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
dequeue 0
queue size 9  nout 1 items 1 2 3 4 5 6 7 8 9
['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
dequeue 1
queue size 8  nout 2 items 2 3 4 5 6 7 8 9
['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
dequeue 2
queue size 7  nout 3 items 3 4 5 6 7 8 9
['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
dequeue 3
queue size 6  nout 4 items 4 5 6 7 8 9
['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
dequeue 4
queue size 5  nout 0 items 5 6 7 8 9
['5', '6', '7', '8', '9']
dequeue 5
queue size 4  nout 1 items 6 7 8 9
['5', '6', '7', '8', '9']
dequeue 6
queue size 3  nout 2 items 7 8 9
['5', '6', '7', '8', '9']
dequeue 7
queue size 2  nout 3 items 8 9
['5', '6', '7', '8', '9']
dequeue 8
queue size 1  nout 4 items 9
['5', '6', '7', '8', '9']
dequeue 9
queue size 0  nout 0 items 
[]
empty
empty