python循环队列

循环队列

什么是循环队列?

相较于普通的队列(详见:python实现队列(点击跳转)),循环队列的数据区是固定的(在规定了队列的大小后),而传统的队列的数据区是动态变化的。

而循环队列的本质:固定的数据区域,变动的索引指向

例如:在弹出队首元素的时候,并不会真的将原来的队首元素删除,而是将队首元素的索引变量改变,从而使其指向新的队首元素。如下图所示:

在这里插入图片描述

可见并未真正删除原来索引为0的元素(原来的队首),仅仅只是将新的队首的索引赋值给队头变量head


为什么要引入循环队列

简单来讲就是循环队列的执行效率较高

举个简单的例子:

传统队列的,弹出队首的方法,使用的是列表内置的pop()方法,但是其时间复杂度是O(n),因为当列表中的第一个元素被删除后,其他所有的元素都会向前移动一位。但是循环队列呢?根本不会改变任何的底层中的元素,仅仅只是将索引变一下即可,时间复杂度为O(1),执行效率高,故,在真正使用队列的时候一般都是使用循环队列。


循环队列的顺序表实现

基本框架

队列顺序表实现的基本框架(基本参数)如下图所示:

  1. 队列的头索引head
  2. 队列的实际长度length
  3. 队列的最大尺寸size
  4. 存储队列的顺序表item

在这里插入图片描述

代码实现如下:

class Queue:
    def __init__(self,size):
        self.items = [None] * size
        self.size = size
        self._lenth = 0
        self.head = 0

值得注意的是:因为队列的大小是固定的,所以当我们创建顺序表的时候,需要创建一个长度为size的初始化的顺序表才行,如上面的第3行代码。

self.items = [None] * size

基础方法

基础方法包括:

  • 返回链表的长度
  • 判断链表是否为空

代码实现如下:

    def is_empty(self):
        return  self._lenth == 0

    def length(self):
        return self._lenth

坚决不可以用以下代码判断是否为空

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

因为循环链表中的顺序表中一直有元素,不为空。


给队尾添加值data(重点内容)

  1. 判断队列是否已满
  2. 队尾索引
  3. 将值赋值给索引
  • 情况1如下:

在这里插入图片描述

  • 情况2如下:

在这里插入图片描述

到这里都可以看出,为节点的索引公式为:
i n d e x = s e l f . h e a d + s e l f . _ l e n t h 待添加值的索引 = 队首索引 + 队列实际长度 i n d e x   o f   t h e   v a l u e   t o   b e   a d d e d = i n d e x   o f   t h e   h e a d   o f   t h e   q u e u e   + a c t u a l   l e n g t h   o f   t h e   q u e u e index = self.head + self.\_lenth \\待添加值的索引 = 队首索引 + 队列实际长度\\index\ of \ the \ value\ to \ be\ added = index \ of\ the\ head\ of\ the\ queue\ + actual\ length \ of \ the \ queue index=self.head+self._lenth待添加值的索引=队首索引+队列实际长度index of the value to be added=index of the head of the queue +actual length of the queue
但是真的就这样了?没有其他的情况了?

  • 情况3如下:

在这里插入图片描述

我们可以看出,我们想给0的位置添加索引,但是算出来的却是8,进行对size取余即可(至于为什么取余,小伙伴自行研究哈!!)

在这里插入图片描述

最终的公式为
i n d e x = ( s e l f . h e a d + s e l f . _ l e n t h )   %   s e l f . s i z e index = (self.head + self.\_lenth)\ \%\ self.size index=(self.head+self._lenth) % self.size
代码实现:

        def push(self,data):
        if self._lenth < self.size:
            idx = (self._lenth + self.head) % self.size
            self.items[idx] = data
            self._lenth += 1
        else:
            print('队列已经满了')

弹出队首元素

如下图可知,想要弹出索引为2的队首元素3,步骤如下

  1. 将原来的队首元素存储到一个新的变量,用于返回值
  2. 找到新的队首元素的索引(也就是下图中的索引3)
  3. 将head属性指向新的索引
  4. 队列长度减1
  5. 返回原来的队首元素
    在这里插入图片描述
    索引的确定:
    因为是队首元素的下一个对象,直接self.head + 1即可,但是,如果是尾部跳到头部,同样的也需要进行取余操作:

n e x t _ i t e m _ i d x = ( s e l f . h e a d + 1 ) % s e l f . s i z e \\next\_item\_idx = (self.head + 1) \% self.size next_item_idx=(self.head+1)%self.size
代码实现如下:

    def pop(self):    # 弹出队首元素
        if self.is_empty():
            raise  ValueError('队列为空')
        res = self.items[self.head]
        self.head = (self.head + 1 ) % self.size
        self._lenth -= 1
        return res

返回队首元素

返回队首元素索引[self.head]在items中对应的值即可

    def peek(self):
        if self.is_empty():
            raise  ValueError('队列为空')
        res = self.items[self.head]
        return res

附件中附完整代码


链表实现

注:由于链表的所有操作的时间复杂度均为O(1),故,没有必要进行循环操作!

附上链表实现队列代码:

'''
@Time    : 2023-08-07 22:21
@Author  : TAGRENLA
@File    : 队列_链表.py
'''

'''用链表实现队列'''

# 定义结点类
class Node():
    def __init__(self,data,next = None):
        self.data = data
        self.next = next

class Queue:
    '''
    链表的第一个元素是队列的头部,链表的最后一个元素是对列的尾部
    '''
    def __init__(self,size):
        self.head = None
        self.rear = None
        self.lenth = 0
        self.size = size

    def is_empty(self):
        return  self.lenth == 0

    def length(self):
        return self.lenth

    def push(self,data):
        '''
        添加一个元素data到队列尾部
        创建一个新的结点
        将新的结点添加到链表的尾部(链表的尾部既队列的尾部)
            将原来的尾结点的next属性指向新的结点
            将队列的rear(原来的尾结点)属性指向新的结点
        队列长度加1
        特殊情况下:
        如果链表为空
        那么就是添加到链表的头部  具体操作如下:
        1.将队列的head属性指向新的结点
        2.将队列的rear属性指向新的结点 (因为在空的队列中台添加一个对象那么这个对象既是队首又是队尾)
        '''
        if self.length() == self.size:
            raise ValueError('队列已经满了')
        node = Node(data)
        if self.is_empty():
            self.head = node
            self.rear = node
        else:
            self.rear.next = node # 将原来的尾结点的next属性指向新的结点
            self.rear = node      # 将队列的rear(原来的尾结点)属性指向新的结点
        self.lenth += 1


    def pop(self):
        '''抛出队首元素也就是链表的第一个结点'''
        '''
        创建一个变量指向头节点(用于后续的返回)
        将链表的head属性指向原来头节点的下一个结点
        '''
        if self.is_empty():
            raise  ValueError('队列为空')
        value = self.head.data
        if self.lenth == 1:
            self.head = None
            self.rear = None
            # print(f'队列的头部结点是{self.rear}')
        else:
            self.head = self.head.next
            self.lenth -= 1
        return value

    def peek(self):
        if self.is_empty():
            raise  ValueError('队列为空')
        return self.head.data

if __name__ == '__main__':
    queue = Queue(3)
    queue.push(1)
    queue.push(2)
    queue.push(3)
    print(queue.pop())
    queue.push(4)
    print(queue.peek())
    print(queue.pop())
    print(queue.pop())
    print(queue.pop())

附件

循环队列顺序表完整代码

class Queue:
    def __init__(self,size):
        self.items = [None] * size
        self.size = size
        self._lenth = 0
        self.head = 0

    def is_empty(self):
        return  self._lenth == 0

    def length(self):
        return self._lenth

    def push(self,data):
        if self._lenth < self.size:
            idx = (self._lenth + self.head) % self.size
            self.items[idx] = data
            self._lenth += 1
        else:
            print('队列已经满了')

    def pop(self):    # 弹出队首元素
        if self.is_empty():
            raise  ValueError('队列为空')
        res = self.items[self.head]
        self.head = (self.head + 1 ) % self.size
        self._lenth -= 1
        return res

    def peek(self):
        if self.is_empty():
            raise  ValueError('队列为空')
        res = self.items[self.head]
        return res

if __name__ == '__main__':
    queue = Queue(3)
    print(queue.is_empty())
    print(queue.items)
    queue.push(1)
    print(queue.items)
    queue.push(11)
    print(queue.items)
    queue.push(111)
    print(queue.items)
    queue.push(1111)
    print(queue.pop())
    print(queue.pop())
    print(queue.peek())
    print(queue.length())
    print(queue.items)
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TAGRENLA

您的打赏是我创作的动力,谢谢!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值