LeetCode 622 设计队列 python实现

题目描述

设计你的循环队列实现。 循环队列是一种线性数据结构,其操作表现基于 FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。

循环队列的一个好处是我们可以利用这个队列之前用过的空间。在一个普通队列里,一旦一个队列满了,我们就不能插入下一个元素,即使在队列前面仍有空间。但是使用循环队列,我们能使用这些空间去存储新的值。

你的实现应支持如下操作:
MyCircularQueue(k): 构造器,设置队列长度为 k 。
Front: 从队首获取元素。如果队列为空,返回 -1 。
Rear: 获取队尾元素。如果队列为空,返回 -1 。
enQueue(value): 向循环队列插入一个元素。如果成功插入则返回真。
deQueue(): 从循环队列中删除一个元素。如果成功删除则返回真。
isEmpty(): 检查循环队列是否为空。
isFull(): 检查循环队列是否已满。

分析

  1. 插入新元素位置是队尾后面。如果队尾已经位于list尾部,那么新元素插入到list[0]。新元素成功插入后,新元素的位置为队尾。

  2. 去除旧元素的位置是队首位置。旧元素成功去除后,旧元素的下一个元素位置为队首。如果旧元素是队尾的元素,队首则为list[0]的位置。

  3. 结合以上两点不难看出,这是一个循环的设计,它让队列变成了一个圆环。

  4. 但是当空列表时,队首head,队尾tail应当指向哪里?他们不应该指向任何队列内的位置———这里有两个思路:

    1. 设计一个n+1容量的列表,允许存放n个元素,但是空一个位置用来存放空列表时的head、tail
    2. 设计一个n容量的列表,当空列表时,head、tail的值为None

    为了更贴近抽象概念,我在这里选择了第二种思路。

解法

动态数组法

前言

简单直接的实现思路,就是使用list的append方法,但是这已经违背了定长序列的原意——尽管这个办法简单有效。

概要
  1. 类的实例在初始化时应当具有的属性
    1. 空列表
代码

以下代码实测可用

#--coding:utf-8--
# Author: Allen Lv
# Version: python 3.8

class MyCircularQueue:

    def __init__(self, k: int):
        self.queue = []

    def enQueue(self, value: int) -> bool:
        if self.isFull():
            return False
        self.queue.append(value)
        return True

    def deQueue(self) -> bool:
        if self.isEmpty():
            return False
        del self.queue[0]
        return True

    def Front(self) -> int:
        if self.isEmpty():
            return -1
        return self.queue[0]

    def Rear(self) -> int:
        if self.isEmpty():
            return -1
        return self.queue[-1]

    def isEmpty(self) -> bool:
        if len(self.queue) == 0:
            return True
        return False

    def isFull(self) -> bool:
        if len(self.queue) == self.size:
            return True
        return False
缺点
  1. 违背了循环队列的愿意
优点
  1. 使用append实现简单(虽然python底层的list也是定容的、只是当append时进行扩容罢了)

数组法

前言

为了更接近定长数组这个概念(比如 C# 中的Arrary),我更推荐这种思路。
这种思路是:生成实例时就产生定长的空队列,根据需要往空的位置填入元素。但是无法声明一个有指定长度但是元素都是空的列表,所以我用None来代替空元素。

概要
  1. 类的实例在初始化时应当具有的属性:
    1. 长度/ 大小/ 容量
    2. 指定长度、且长度固定的列表
    3. 都为None的head、tail
  2. isEmpty()和isFull()方法检测的是每个元素是否为None,而非列表长度。
  3. Front()和Rear()给出的元素是head、tail所指向的元素,而非列表最前、列表最后的元素(把列表想象成环形的,就没有前后之分了)。
  4. enQueue(value)插入新元素时,如果是空列表,对head、tail都赋0。
  5. deQueue()删除旧元素时,如果元素是唯一的元素,删除后,对head
    、tail都赋None
代码

以下代码实测可用

#--coding:utf-8--
# Author: Allen Lv
# Version: python 3.8

class MyCircularQueue:

    def __init__(self, k: int):
        self.queue = [None] * k
        self.head = None  # 开始标记
        self.tail = None  # 结束标记
        self.size = k  # 大小

    # 自己测试时方便检查队列
    def __str__(self):
        return str(self.queue)

    def isFull(self):
        # 判断队列是否为满
        full = True
        for x in self.queue:
            if x == None:
                full = False
                break
        return full

    def isEmpty(self):
        # 判断队列是否为空
        empty = True
        for x in self.queue:
            if x != None:
                empty = False
                break
        return empty

    def Front(self):
        # 获取开始标记
        if self.isEmpty():
            return -1
        return self.queue[self.head]

    def Rear(self):
        # 获取结束标记
        if self.isEmpty():
            return -1
        return self.queue[self.tail]

    def enQueue(self, value: int):
        # 入队
        if self.isFull():
            return False
        if self.isEmpty():
            self.head = 0
            self.tail = 0
            self.queue[0] = value
        else:
            # 如果不是队尾的元素,tail += 1
            if self.tail < self.size - 1:
                self.tail += 1
                self.queue[self.tail] = value
            # 如果是队尾的元素,新元素插到队首
            else:
                self.tail = 0
                self.queue[0] = value
        return True

    def deQueue(self):
        # 出队
        if self.isEmpty():
            return False
        self.queue[self.head] = None
        # 出队一个以后,判断后面还有没有元素
        # 如果没有元素了
        if self.isEmpty():
            self.head = None
        # 如果还有元素
        else:
            # 如果不是队尾的元素,head += 1
            if self.head < self.size - 1:
                self.head += 1
            # 如果是队尾元素,队首元素记为结束标记
            else:
                self.head = 0
        return True
缺点
  1. 过多的判断语句
  2. 过多的循环语句
优点
  1. 和概念十分接近,易于理解

测试方法

如何进行本地测试?
这是Leetcode给出的测试指令集,我需要一个方法来使用它,然后在本地对代码进行测试(本地测试更容易发现bug和debug)

["MyCircularQueue","enQueue","enQueue","enQueue","enQueue","Rear","isFull","deQueue","enQueue","Rear"]
[[3],[1],[2],[3],[4],[],[],[],[4],[]]

比如我解法2的py文件叫CircularQueue2.py,那么我们可以在同目录下创建测试文件,test.py。
代码如下:

#--coding:utf-8--
# Author: Allen Lv
# Version: python 3.8
from CircularQueue2 import *

def doTest(action: list, param: list):
    n = len(action)
    if n != len(param):
        raise IndexError
    p = MyCircularQueue(3)
    for i in range(1, n):
        code = "print(p.{}({}))".format(action[i], pa[0] if (pa := param[i]) != [] else "")
        try:
            exec(code)
        except:
            print(code)
            a = input("-------stop------")

if __name__ == "__main__":
    action = ["MyCircularQueue","enQueue","enQueue",
        "enQueue","enQueue","Rear","isFull",
        "deQueue","enQueue", "__str__","Rear"]
    param = [[3],[1],[2],[3],[4],[],[],[],[4],[],[]]
    doTest(action, param)
    # p = MyCircularQueue(3)
    # p.enQueue(2)
    # print(p.__str__())

doTest函数使用exec()对被str.fomat格式化的代码进行测试

写在最后

我是Allen Lv,一名python初学者,如果你对本文有任何改进意见,欢迎提出,非常感谢。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值