数据结构--队列

队列

  • 一、队列介绍
  • 二、队列分类及实现
    • 1.线性队列(普通)
    • 2.线性队列(双指针)
    • 3.循环队列(双指针)
  • 三、队列总结
      • `线性队列(非循环队列)`:
        • **使用双指针**:
        • 不使用双指针(仅使用 pop(0) 和 append):
      • `循环队列:`
      • 总结:


提示:以下是本篇文章正文内容,下面案例可供参考

一、队列介绍

二、队列分类及实现

1.线性队列(普通)

tips:直接采用pop弹出的方式读取元素实际会造成后续所有元素的整体向前移动,这与双指针方式不同,因此不会造成空间浪费。但是前移会造成时间复杂度升高到O(n),而采用双指针则是O(1)。但是这种方式简单容易实现,简单场景下比较适合。

class Queue():
    def __init__(self):
        self.queue=[]
    def enqueue(self,element):
        self.queue.append(element)
    def dequeue(self):
        if len(self.queue)>0:
            return self.queue.pop(0)
        else:
            return None
    def is_empty(self):
        return len(self.queue)==0

if __name__=='__main__':
    queue=Queue()
    queue.enqueue(1)
    queue.enqueue(6)
    queue.enqueue(9)
    print(queue.dequeue())
    print(queue.dequeue())
    print(queue.dequeue())
    print(queue.dequeue())

2.线性队列(双指针)

tips:本身采用双指针就会造成读取过元素的位置的空间浪费,因为rear不会逆序移动,因此此处仅供对比线性和循环的区别,下面代码可能没有实际用处,真正需要采用双指针应该采用循环队列,这样可以回收内存,以下仅供参考(可能有bug)


class Queue:
    def __init__(self,size=3):
        self.size=size
        self.queue=[None for i in range(self.size)]
        self.rear=0#队尾指针
        self.front=0#队首指针
    def enqueue(self,element):
        if self.is_full():
            raise MemoryError('Queue is full')
        else:
            self.rear+=1
            self.queue[self.rear]=element
            
    def dequeue(self):
        if self.is_empty():
            raise MemoryError('Queue is empty')
        else:
            self.front+=1
            result=self.queue[self.front]
            self.queue[self.front]=None#此处不处理也可以,后面会覆盖
            return result
    def is_full(self):
        return self.rear==self.size-1
    def is_empty(self):
        return self.front==self.rear
    
if __name__=='__main__':
    queue=Queue(5)
    queue.enqueue(1)
    print(queue.dequeue())
    queue.enqueue(6)
    print(queue.dequeue())
    queue.enqueue(9)
    queue.enqueue(2)
    print(queue.dequeue())
    print(queue.dequeue())    

3.循环队列(双指针)

tips:相比较于线性pop方式的高时间复杂度,以及双指针的线性方式的空间浪费,循环队列几乎避免了所有·缺点。它不会造成空间浪费,时间复杂度是O(1)。
注意事项:以下rear和front是先移动,再存储/读取元素;其实先存/读,后移动也可以,但是有一个问题,尽量size大一点,比如size=10,实际存9就极限了,10会报错,因为两个指针会重合

class Queue:
    def __init__(self,size=3):
        self.size=size
        self.queue=[0 for i in range(self.size)]
        self.rear=0#队尾指针
        self.front=0#队首指针
    def enqueue(self,element):
        if self.is_full():
            raise MemoryError('Queue is full')
        else:
            self.rear=(self.rear+1)%self.size
            self.queue[self.rear]=element
            
    def dequeue(self):
        if self.is_empty():
            raise MemoryError('Queue is empty')
        else:
            self.front=(self.front+1)%self.size
            result=self.queue[self.front]
            self.queue[self.front]=None#此处不处理也可以,后面会覆盖
            return result
    def is_full(self):
        return (self.rear+1)%self.size==self.front
    def is_empty(self):
        return self.front==self.rear
    
if __name__=='__main__':
    queue=Queue(5)
    queue.enqueue(1)
    print(queue.dequeue())
    queue.enqueue(6)
    print(queue.dequeue())
    queue.enqueue(9)
    queue.enqueue(2)
    print(queue.dequeue())
    print(queue.dequeue())

三、队列总结

线性队列(非循环队列)

使用双指针
  1. 优点

    • 入队和出队操作的时间复杂度都是 O(1)。
    • 直观地表示了队列的头部和尾部。
  2. 缺点

    • 如果频繁进行出队操作,会导致队列前面出现空位,这些空位不能被用来存储新的入队元素,直到 rear 指针也移动到这些位置。
    • 存在存储空间的浪费,因为队列的前端可能有很多未使用的空间,而 rear 指针只能在队列的末尾添加新元素。
不使用双指针(仅使用 pop(0) 和 append):
  1. 优点

    • 空间不会被浪费,因为 append 操作总是在列表的末尾添加元素,而 pop(0) 操作总是在列表的开头移除元素,所以不会有未使用的空间。
    • 代码实现简单,直接使用列表的 pop(0)append 方法。
  2. 缺点

    • 出队操作的时间复杂度是 O(n),因为 pop(0) 需要将所有其他元素向前移动一位。

循环队列:

  1. 优点

    • 通过循环利用数组空间,避免了非循环队列中的空间浪费问题。
    • 入队和出队操作的时间复杂度都是 O(1)。
  2. 缺点

    • 实现相对复杂,需要额外的逻辑来处理 frontrear 指针的循环。
    • 需要处理队列满和队列空的条件,这可能涉及到取模运算。
    • 有大小限制,不能无限添加元素。

总结:

  • 线性队列(非循环)在使用双指针时可能会有存储空间的浪费,因为 rear 指针不能前移来利用前面空出来的位置。不使用双指针时,虽然空间不会被浪费,但出队操作的时间复杂度较高。
  • 循环队列通过允许 rear 指针在到达数组末尾时回到数组开头,解决了空间浪费的问题,使得队列可以更有效地利用数组空间,并且保持了入队和出队操作的高效率。

在选择队列的实现方式时,需要根据具体的应用场景和性能要求来决定使用哪种方法。如果性能是关键因素,特别是在队列操作频繁的场景下,循环队列可能是更好的选择。如果队列操作较少,或者对实现的复杂性有要求,可能就会选择更简单的线性队列实现方式。


声明:
本文为本人的学习笔记,旨在记录和分享个人在学习过程中的心得体会和原创代码。由于本人刚入门,对相关知识的理解可能还存在不足之处,文章中难免会有错误或不准确的地方。在此,我诚挚地欢迎各位读者在阅读过程中,如果发现任何问题或有其他建议,随时在评论区或通过其他方式与我交流。我将虚心听取大家的意见,及时修正和改进文章内容,以便更好地学习和成长。感谢大家的关注和支持!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值