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) |