• 队列:顺序队列和循环队列


    和栈的先进后出不同,队列的形式是先进先出,队列的想法来自于生活中排队的策略, 顾客在付款结账的时候,按照到来的先后顺序排队结账。先来的顾客先结账,后来的顾客后结账。

    队列有两种实现形式:1 顺序表实现 2 循环顺序表 

    首先来看下顺序表的实现,在python中,队列的实现用list来写十分的方便。实现方式如下:

    class line_queue():

        def __init__(self):

            self._elem=[]

        def push(self,elem):

            self._elem.append(elem)

        def pop(self):

            elem=self._elem.pop(0)

            return elem

        def queue_length(self):

            return len(self._elem)

    和栈唯一的区别是,这里pop是pop(0),也就是首先进队列的数据出列。这个实现很简单,但是有一个问题,每次有元素出队列的时候,元素都必须要进行前移。这就带来了一个问题,它的操作复杂度为O(n),而不是O(1)。只有从尾部弹出元素也就是先进后出的时候复杂度为O(1).

    那么如何才能满足O(1)的出列复杂度呢。我们可以考虑记住队头和队尾的位置。每次出队的时候直接将队头位置的元素弹出就可以了。具体的实现可以参考下图

    下面来看下代码的实现:

    class line_queue_update():

        def __init__(self):

            self._elem=[]

            self.head=self.rear=0

        def push(self,elem):

            self._elem.append(elem)

            self.rear+=1

        def pop(self):

            elem=self._elem[self.head]

            self.head+=1

            return elem

        def queue_length(self):

            return len(self._elem)

        def get_elem(self):

            print self._elem

     

    if __name__=="__main__":

        q=line_queue_update()

        for i in range(10):

            q.push(i)

        print 'The length is %d' % q.queue_length()

        q.pop()

        q.pop()

        q.push(90)

        q.push(100)

        q.push(200)

    print 'The length is %d' % q.queue_length()

    运行结果如下:

    /usr/bin/python2.7 /home/zhf/py_prj/data_struct/chapter5.py

    The length is 10

    The length is 13

    这个方法的实现出队列的复杂度就是O(1)。但是我们同时也注意到另外一个问题,那就是尽管我们曾经pop元素。但是整个list的长度在不断的增加。这是为什么呢,是因为队头/对尾位置变量的值将随着操作移动。从操作效率来看,每个操作都在O(1)时间完成。但另一方面,表中元素序列随着操作向表尾方向移动,这就导致了在表的前端留下来了越来越多的空间,这些空间就是那些已经出队的元素曾经占据的空间。在c语言中,表元素存储的大小是固定的,经过反复的入队和出队操作。一定会在某次入队时出现队尾溢出也就是表满的情况。对于python而言,由于list是自动增长的,随着操作进行,表前端留下了越来越大的空区,而且这片空区永远也不会用到,完全浪费了。

    前面介绍了顺序队列的两种实现方式,第一种时间效率无法满足,空间效率满足。 第二种时间效率满足,空间效率无法满足。两种策略都有各自的缺点,那么是否有一种方式既能满足时间效率也能满足空间效率呢。这就需要用到队列的第二种实现方式:循环顺序表

    循环顺序表是在顺序表的第二种方式进行的变种。前面介绍过,通过head,rear记录表头和表尾元素的方式可以做到时间效率为O(1)但是会导致大量的空间浪费。但是如果我们将顺序表看做一种环形结构。那么空间浪费的问题就可以解决了,结构如下所示。

    那么在这个结构里面,数据就保存在q.front到q.rear的位置里面。两个变量的差就是队列里面的元素个数。这种结构下元素始终在一个环里进行轮转。空位置可以被新插入的元素占据。

    那么在环形结构中,需要注意以下3点:

    1当队列为空或者为满的时候,q.front=q.rear。由于q.front=q.rear对应队空和队满两种情况,因此为了区分,可以通过队列长度来判断,当q.front=q.rear且q.len=q.max的时候认为为队满,否则为队空。

    2 出队的时候q.front=(q.front+1)%q.len

    3 入队的时候q.rear=(q.rear+1)%q.len

    第2点和第3点的实现方式和顺序表不一样,在更新队头和队尾位置的时候需要进行求余操作,这是因为循环队列的空间大小固定,队头和队尾的位置是相对增长的,不是绝对增长的。下面来看下具体的实现代码

    class squeue():

        def __init__(self,init_len=8):

            self._len=init_len

            self._elem=[0]*init_len

            self._head=0

            self._rear=0

            self._num=0

        def is_empty(self):

            return self._num == 0

        def is_full(self):

            return self._num == self._len

        def dequeue(self):

            if self.is_empty():

                raise BaseException('queue is empty')

            e=self._elem[self._head]

            self._head=(self._head+1) % self._len

            self._num-=1

            return e

     

        def enqueue(self,elem):

            if self.is_full():

                raise BaseException('queue is full')

            self._elem[self._rear] = elem

            self._rear=(self._rear+1) % self._len

            self._num+=1

        def get_elem(self):

            print self._elem

     

    if __name__=="__main__":

        q=squeue()

        for i in range(8):

            q.enqueue(i)

        q.get_elem()

        q.dequeue()

        q.enqueue(100)

    q.get_elem()

    首先插入8个元素,然后出队一个元素

    /usr/bin/python2.7 /home/zhf/py_prj/data_struct/chapter5.py

    [0, 1, 2, 3, 4, 5, 6, 7]

    [100, 1, 2, 3, 4, 5, 6, 7]

    当继续插入元素的时候,q.enqueue(100)会提示队列已满

    /usr/bin/python2.7 /home/zhf/py_prj/data_struct/chapter5.py

    Traceback (most recent call last):

      File "/home/zhf/py_prj/data_struct/chapter5.py", line 196, in <module>

        q.enqueue(100)

      File "/home/zhf/py_prj/data_struct/chapter5.py", line 173, in enqueue

        raise BaseException('queue is full')

    BaseException: queue is full

    我们可以添加一个扩充队列的操作,防止队列满导致插入失败

        def expand(self):

            old_len=self._len

            self._len*=2

            new_elems=[0]*self._len

            for i in range(old_len):

                new_elems[i] = self._elem[(self._head+i-1) % old_len]

            self._elem,self._head,self._rear=new_elems,0,self._head+self._num

    if __name__=="__main__":

        q=squeue()

        for i in range(8):

            q.enqueue(i)

        q.get_elem()

        q.dequeue()

        q.enqueue(100)

        q.get_elem()

        q.enqueue(200)

    q.get_elem()

    运行结果如下:

    /usr/bin/python2.7 /home/zhf/py_prj/data_struct/chapter5.py

    [0, 1, 2, 3, 4, 5, 6, 7]

    [100, 1, 2, 3, 4, 5, 6, 7]

    [100, 1, 2, 3, 4, 5, 6, 7, 0, 200, 0, 0, 0, 0, 0, 0]

  • 相关阅读:
    windows下使用C#获取特定进程网络流量
    .NET设计模式(9):桥接模式(Bridge Pattern)(转)
    .NET设计模式(8):适配器模式(Adapter Pattern)(转)
    .NET设计模式(7):创建型模式专题总结(Creational Pattern)(转)
    .NET设计模式(6):原型模式(Prototype Pattern)(转)
    .NET设计模式(5):工厂方法模式(Factory Method)(转)
    .NET设计模式(4):建造者模式(Builder Pattern)(转)
    .NET设计模式(3):抽象工厂模式(Abstract Factory)(转)
    单件模式(Singleton Pattern)(转)
    PowerDesigner(九)-模型文档编辑器(生成项目文档)(转)
  • 原文地址:https://www.cnblogs.com/zhanghongfeng/p/8469904.html
Copyright © 2020-2023  润新知