Python中队列和栈的列表实现

本文介绍了如何使用Python列表实现栈和队列,详细解析了栈的push、pop、top和is_empty等操作,以及队列的enqueue、dequeue、first和is_empty等操作。通过实例展示了这两种数据结构的“后进先出”(栈)和“先进先出”(队列)特性。同时,分析了列表实现栈和队列的时间复杂度,强调了栈和队列操作的时间效率,并讨论了列表在实现队列时的空间效率问题。
摘要由CSDN通过智能技术生成

Python中队列和栈的列表实现

栈的基本概念

栈是一种简单的数据结构,在Python中栈的插入以遵循“后劲先出“的原则。可以在任何时候向栈中插入一个对象,但是只能取得或者删除最后插入的对象(即所谓的“栈顶”)。

栈的基本操作

假设栈为 S,栈的基本操作:

  • S.push(e):将一个元素 e 添加到栈 S 的栈顶;
  • S.pop():从栈 S 中返回栈顶的元素,如果栈为空,这个操作将提示错误。
  • S.top():不移除栈顶元素的情况下,返回栈顶元素;
  • S.is_empty():判断栈是否为空
  • len(S):返回栈中元素的数量
    在这里插入图片描述

使用列表实现栈

class Error(Exception):
    pass
class ArrayStack:
    def __init__(self):
        self.__data = []

    def __len__(self):
        return len(self.__data)

    def is_empty(self):
        return len(self.__data) == 0

    def push(self, e):
        self.__data.append(e)

    def top(self):
        if self.is_empty():
            raise Error('Stack is Empty!')
        else:
            return self.__data[-1]

    def pop(self):
        if self.is_empty():
            raise Error('Stack is Empty!')
        else:
            return self.__data.pop()


if __name__ == '__main__':
    S = ArrayStack()
    S.push(1)
    S.push(2)
    print("列表中元素的个数为:",len(S))
    print(S.pop())
    print("列表是否为空列表:",S.is_empty())
    print(S.pop())
    print("列表是否为空列表:",S.is_empty())
    S.push(3)
    S.push(4)
    print("栈顶元素为:",S.top())
    S.push(5)
    print("列表中元素的个数为:",len(S))
    print(S.pop())
    S.push(6)

输出结果为:

列表中元素的个数为: 2
2
列表是否为空列表: False
1
列表是否为空列表: True
栈顶元素为: 4
列表中元素的个数为: 3
5

从上述描述们得知,先创建一个空的列表,并将元素1和2入栈,同时判断栈中元素的个数,然后从栈 S 中返回栈顶的元素,判断栈中元素是否为空,继续从栈 S 中返回栈顶的元素,继续判断栈是否为空,随后入栈元素3和4,判断栈顶元素为何元素,以此了解栈的规律。从输出结果看,栈是一种“后进先出”的数据结构。

列表实现栈的时间复杂度分析

为了更方便的描述出列表实现栈的时间复杂度,本文从上述代码中各函数分析不同的时间复杂度,不同函数的时间复杂度为:

操作时间复杂度
S.push(e)O(1)
S.pop()O(1)
S.push(e)O(1)
S.top()O(1)
S.is_empty()O(1)
len(S)O(1)

队列的基本概念

队列是一种简单的数据结构,是由一些列对象组成的集合,这些对象的插入和删除操作遵循“先进先出”的原则。可以在任何时候向队列中插入一个对象,但是只有处在队列最前面的元素才能被删除。队列中允许被插入的一端称为队尾,允许删除的一端称为队头。

队列的基本操作

假设队列为 Q,队列的基本操作:

  • Q.enqueue(e):将一个元素 e 添加到队列 Q 的队尾;
  • Q.dequeue():从队列 Q 中返回队头的元素,如果队列为空,这个操作将提示错误。
  • Q.first():不移除队头元素的情况下,返回队头元素;
  • Q.is_empty():判断队列是否为空
  • len(Q):返回队列中元素的数量
    在这里插入图片描述

使用列表实现队列

一个简单的思路就是和栈一样,插入使用 append,删除使用 pop(0) ,但是这有一个问题,就是 pop(0) 的时间复杂度是 O(n),这样时间复杂度太高,那么有没有和栈一样时间复杂度都是 O(1) 的实现方法呢?

这个时候可能会想到这样做,我们可以使用一个变量来保存第一个元素(即队头)的索引,每次删除元素之后,这个变量就指向下一个元素,先前的队头元素变为 None,这样就不用了每次出队之后用后面的元素覆盖前面的元素。这样时间复杂度确实是 O(1),但是空间呢,这个时候底层数组的大小就变成了追加元素的总和,即使我们出队了很多元素。举个例子:比如一个队列入队了 10000 次,出队了 9990 次(假设每次出队时队中有元素),那么最后队列中有 10 个元素,但是实际底层列表长度为 10000,而更底层的数组可能更大(动态数组),这就有很大的空间浪费。

可以使用循环数组解决,基本操作和上面一样,只是我们让队列的元素在底部循环:

  • 假设底层数组的长度初始为 N
  • 出队的时候,前面的位置就空出来了
  • 入队的时候,如果到了数组的尾部,那么看前面位置是否用空,如果有,那么放在数组的前面,如果没有,申请更大的数组来保存。
    实现代码如下:
class Error(Exception):
    pass
class ArrayQueue:
    DEFAULT_CAPACITY = 3

    def __init__(self):
        self.__data = [None] * ArrayQueue.DEFAULT_CAPACITY
        self.__size = 0#__size 是队列实际元素的多少。
        self.__font = 0 #_font 始终指向队头元素。

    def __len__(self):
        return self.__size

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

    def first(self):
        if self.is_empty():
            raise Error("Queue is Empty")
        else:
            return self.__data[self.__font]

    def dequeue(self):
        if self.is_empty():
            raise Error("Queue is Empty")
        else:
            res = self.__data[self.__font]
            self.__data[self.__font] = None
            self.__font = (self.__font + 1) % len(self.__data)
            self.__size -= 1
            return res

    def enqueue(self, e):
        if self.__size == len(self.__data):
            self.__resize(2 * len(self.__data))
        avail = (self.__font + self.__size) % len(self.__data)
        self.__data[avail] = e
        self.__size += 1

    def __resize(self, cap):
        old = self.__data
        self.__data = [None] * cap
        walk = self.__font
        for k in range(self.__size):
            self.__data[k] = old[walk]
            walk = (walk + 1) % len(old)
        self.__font = 0
if __name__ == '__main__':
    Q = ArrayQueue()
    Q.enqueue(1)
    Q.enqueue(2)
    print("队列中元素长度为:",len(Q))
    print(Q.dequeue())
    print("队列是否为空:",Q.is_empty())
    print(Q.dequeue())
    print("队列是否为空:",Q.is_empty())
    Q.enqueue(3)
    Q.enqueue(4)
    Q.enqueue(5)
    print("队列中对头元素为:",Q.first())
    Q.enqueue(6)
    print("队列中元素长度为:",len(Q))
    print(Q.dequeue())
    Q.enqueue(7)
    print(Q.dequeue())
    print(Q.dequeue())
    print(Q.dequeue())
    print(Q.dequeue())

运行结果为:

队列中元素长度为: 2
1
队列是否为空: False
2
队列是否为空: True
队列中对头元素为: 3
队列中元素长度为: 4
3
4
5
6
7

从上述描述可以得知,先创建一个空的列表,并将元素1和2入队列,然后判断队列中元素的个数,然后从队列中返回队列对头的元素,判断队列是否为空,继续从队列中返回队列队头的元素,判断队列是否为空,将元素3,4,5入队列,判断对头元素为何元素,得知到队列是一种“先进先出”的数据结构。

列表实现队列的时间复杂度

根据上述的描述,我们可以得知,如果我们使用类似栈来实现那么对于Q.dequeue()函数来说时间复杂度就为O(n),有着很大的空间浪费。如果使用上述的描述进行实现,该五个函数的时间复杂度为:

函数时间复杂度
Q.enqueue(e)O(1)
Q.dequeue()O(1)
Q.first()O(1)
Q.is_empty()O(1)
len(Q)O(1)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值