为什么要引入循环队列
上一次的博客中我们实现了队列的顺序存储结构与链式存储结构。仔细想想,我们在实现这些队列或者以前在实现栈的时候,并没有考虑我们实际存储空间的大小。在实现栈的时候,我们假设入栈的元素总是有空间的,如在顺序存储中,我们假设此时的栈顶总是可以开辟一块与它相邻的存储空间来安放新的后件;而在链式存储结构中我们假设现在的栈顶总是可以找到一块存储空间来安放新的后件所需的数据域和指针域。在实现队列的过程中我们也存在相似的问题。
当我们考虑有限存储空间的时候,对于栈,我们只需记录栈的长度,保证压入栈内的元素不要超过我们提前开辟好的内存空间即可。但是对于队列,只保证这一点是不行的,比如
- 我们现在有一个队列:
- 我们通过移动头指针的方式进行出队:
- 这时我们发现,入队操作所需的内存空间没有了,也就是说,我们开辟的内存空间已经用完了,但实际上我们的数据个数是小于内存单元数的。要想解决这种问题,我们不能只移动指针,而是要具体的去移动元素。这样,我们需要的运算次数将大大增加。
为了解决实际中队列存在的这个问题,我们引入了循环队列。
循环队列的顺序存储实现
循环队列是如何解决这个问题的?其实通过代码我们就可以明白这个问题。
我们通过画一个环来模拟循环链表,其中涉及的公式都是可以直观推证的、未经过化解的。
class Queue(object):
'定义循环队列。'
def __init__(self,MaxSize):
'初始化队列及其长度。'
self.EmptyFlag = 0
self.MaxSize = MaxSize
self.items = [None] * MaxSize
self.front = 0
self.rear = 0
def IsEmpty(self):
'方法:判断front与rear是否指向同一节点。'
if self.front == self.rear:
return True
else:
return False
def IsFull(self):
'方法:判断是否为满队列。'
#在环状结构中,当(rear + 1)%maxsize = front%maxsize时,我们的队列是满的。
if (self.rear + 1) % self.MaxSize == self.front % self.MaxSize:
return True
else:
return False
def get_size(self):
'方法:得到队列长度。'
#在环状结构中,rear%maxsize + maxsize - front%maxsize + 1为非空时的队列长度。
if self.IsEmpty() and self.EmptyFlag == 0:
return 0
else:
return self.rear % self.MaxSize + self.MaxSize - self.front % self.MaxSize + 1
def push(self,data):
'方法:入队。'
if self.IsFull():
raise IndexError('队列已满,无法实现入队操作。')
elif self.IsEmpty() and self.EmptyFlag == 0:
self.items[self.rear % self.MaxSize] = data
self.EmptyFlag = 1
else:
self.rear += 1
self.items[self.rear % self.MaxSize] = data
def pop(self):
'方法:出队。'
if self.IsEmpty() and self.EmptyFlag == 1:
self.items[self.front%self.MaxSize] = None
self.items[self.rear%self.MaxSize] = None
self.EmptyFlag = 0
elif self.IsEmpty() and self.EmptyFlag == 0:
raise IndexError('队列已空,无法实现出队操作。')
else:
self.front += 1
self.items[self.front%self.MaxSize - 1] = None
测试一:
#在测试中,为了方便,我们直接暴露Queue类的items属性来检验队列是否工作。
my_queue = Queue(10)
[my_queue.push(i) for i in range(5)]
print(my_queue.items)
print(my_queue.front)
print(my_queue.rear)
[my_queue.pop() for i in range(3)]
print(my_queue.items)
print(my_queue.front)
print(my_queue.rear)
[my_queue.push(i) for i in range(5,5 + 8)]
print(my_queue.items)
print(my_queue.front)
print(my_queue.rear)
my_queue.push(12)
测试结果:
[0, 1, 2, 3, 4, None, None, None, None, None]
0
4
[None, None, None, 3, 4, None, None, None, None, None]
3
4
[10, 11, 12, 3, 4, 5, 6, 7, 8, 9]
3
12
Traceback (most recent call last):
File "num.py", line 78, in <module>
my_queue.push(12)
File "num.py", line 44, in push
raise IndexError('队列已满,无法实现入队操作。')
IndexError: 队列已满,无法实现入队操作。
测试二:
my_queue = Queue(10)
[my_queue.push(i + 1) for i in range(9)]
print(my_queue.items)
[my_queue.pop() for i in range(4)]
print(my_queue.items)
[my_queue.push(i + 1) for i in range(10,11)]
print(my_queue.items)
[my_queue.pop() for i in range(6)]
print(my_queue.items)
my_queue.pop()
print(my_queue.items)
测试结果:
[1, 2, 3, 4, 5, 6, 7, 8, 9, None]
[None, None, None, None, 5, 6, 7, 8, 9, None]
[None, None, None, None, 5, 6, 7, 8, 9, 11]
[None, None, None, None, None, None, None, None, None, None]
Traceback (most recent call last):
File "num.py", line 77, in <module>
my_queue.pop()
File "num.py", line 60, in pop
raise IndexError('队列已空,无法实现出队操作。')
IndexError: 队列已空,无法实现出队操作。