目录
前言
一、队列简介
二、队列的顺序存储和链式存储
2.1 队列的基本操作
2.2 队列的顺序存储实现
2.2.1 队列的顺序存储基本描述
2.3 循环队列的顺序存储实现
2.3.1 循环队列的顺序存储基本描述
2.4 队列的链式存储实现
2.4.1 队列的链式存储基本描述
三、队列的应用
总结
队列是一种操作受限的线性表,只允许在表的一端进行插入,而在表的另一端进行删除。
队列(Queue):简称为队,一种线性表数据结构,是一种只允许在表的一端进行插入操作,而在表的另一端进行删除操作的线性表。
队列中允许插入的一端称为队尾;把允许删除的另一端称为队头。当表中没有任何数据元素时,称之为空队。
队列有两种基本操作:插入操作和删除操作
简单来说,队列是一种先进先出的线性表,简称为FIFO结构。
可以从两个方面来解释一下队列的定义:
和线性表类似,队列有两种存储表示方法:顺序存储的队列和链式存储的队列。
注意:front
和 rear
的指向位置并不完全固定。有时候算法设计上的方便以及代码简洁,也会使 front
指向队头元素所在位置的前一个位置。rear
也可能指向队尾元素在队列位置的下一个位置。具体还是要看算法是如何实现的。
队列的基本操作如下:
初始化空队列:创建一个空队列,定义队列的大小 size
,以及队头元素指针 front
,队尾指针 rear
。
判断队列是否为空:当队列为空时,返回 True
。当队列不为空时,返回 False
。一般只用于队列中删除操作和获取队头元素操作中。
判断队列是否已满:当队列已满时,返回 True
,当队列未满时,返回 False
。一般只用于顺序队列中插入元素操作中。
插入元素(入队):相当于在线性表最后元素后面插入一个新的数据元素。并改变队列顶指针 top
的指向位置。
删除元素(出队):相当于在线性表最后元素后面删除最后一个数据元素。并改变队列顶指针 top
的指向位置。
获取队列队头元素:相当于获取线性表中最后一个数据元素。与插入元素、删除元素不同的是,该操作并不改变队列顶指针 top
的指向位置。
队列最简单的实现方式就是借助一个数组来描述队列的顺序存储结构。
这里规定队头指针 self.front
指向队头元素所在位置的前一个位置,而队尾指针 self.rear
指向队尾元素所在位置。
self.queue
,定义队列大小 self.size
。令队头指针 self.front
和队尾指针 self.rear
都指向 -1
。即 self.front = self.rear = -1
。self.front
和 self.rear
的指向位置关系进行判断。如果对头指针 self.front
和队尾指针 self.rear
相等,则说明队列为空。self.rear
指向队列最后一个位置,即 self.rear == self.size - 1
,则说明队列已满。self.front
指向队头元素所在位置的前一个位置,所以队头元素在 self.front
后面一个位置上,返回 self.queue[self.front + 1]
。self.rear
指向队尾元素所在位置,所以直接返回 self.queue[self.rear]
。self.rear
向右移动一位,并进行赋值操作。此时 self.rear
指向队尾元素。self.front
指向元素赋值为 None
,并将 self.front
向右移动一位。python代码:
class Queue:
# 初始化空队列
def __init__(self, size=100):
self.size = size
self.queue = [None for _ in range(size)]
self.front = -1
self.rear = -1
# 判断队列是否为空
def is_empty(self):
return self.front == self.rear
# 判断队列是否已满
def is_full(self):
return self.rear + 1 == self.size
# 入队操作
def enqueue(self, value):
if self.is_full():
raise Exception('Queue is full')
else:
self.rear += 1
self.queue[self.rear] = value
# 出队操作
def dequeue(self):
if self.is_empty():
raise Exception('Queue is empty')
else:
self.front += 1
return self.queue[self.front]
# 获取队头元素
def front_value(self):
if self.is_empty():
raise Exception('Queue is empty')
else:
return self.queue[self.front + 1]
# 获取队尾元素
def rear_value(self):
if self.is_empty():
raise Exception('Queue is empty')
else:
return self.queue[self.rear]
在队列的顺序存储实现中,当队列中第 0
~ size - 1
位置均被队列元素占用时,有 self.rear == self.size - 1
,队列已满,再进行入队操作就会抛出队列已满的异常。
此外,由于出队操作总是删除当前的队头元素,将 self.front
进行右移,而插入操作又总是在队尾进行。经过不断的出队、入队操作,队列的变化就像是使队列整体向右移动。当队尾指针 self.rear == self.size - 1
时,此时再进行入队操作就又抛出队列已满的异常。而之前因为出队操作而产生空余位置也没有利用上,这就造成了「假溢出」问题。
为了解决假溢出问题,有两种做法:
1
个位置。这里规定:self.size
为循环队列的最大元素个数。队头指针 self.front
指向队头元素所在位置的前一个位置,而队尾指针 self.rear
指向队尾元素所在位置。
self.size + 1
。令队头指针 self.front
和队尾指针 self.rear
都指向 0
。即 self.front = self.rear = 0
。self.front
和 self.rear
的指向位置进行判断。根据约定,如果对头指针 self.front
和队尾指针 self.rear
相等,则说明队列为空。(self.rear + 1) % self.size == self.front
,则说明队列已满。self.front
指向队头元素所在位置的前一个位置,所以队头元素在 self.front
后一个位置上,返回 self.queue[(self.front + 1) % self.size]
。self.rear
指向队尾元素所在位置,所以直接返回 self.queue[self.rear]
。self.rear
向右循环移动一位,并进行赋值操作。此时 self.rear
指向队尾元素。self.front
指向元素赋值为 None
,并将 self.front
向右循环移动一位。python代码:
class Queue:
# 初始化空队列
def __init__(self, size=100):
self.size = size + 1
self.queue = [None for _ in range(size + 1)]
self.front = 0
self.rear = 0
# 判断队列是否为空
def is_empty(self):
return self.front == self.rear
# 判断队列是否已满
def is_full(self):
return (self.rear + 1) % self.size == self.front
# 入队操作
def enqueue(self, value):
if self.is_full():
raise Exception('Queue is full')
else:
self.rear = (self.rear + 1) % self.size
self.queue[self.rear] = value
# 出队操作
def dequeue(self):
if self.is_empty():
raise Exception('Queue is empty')
else:
self.queue[self.front] = None
self.front = (self.front + 1) % self.size
return self.queue[self.front]
# 获取队头元素
def front_value(self):
if self.is_empty():
raise Exception('Queue is empty')
else:
value = self.queue[(self.front + 1) % self.size]
return value
# 获取队尾元素
def rear_value(self):
if self.is_empty():
raise Exception('Queue is empty')
else:
value = self.queue[self.rear]
return value
对于在使用过程中数据元素变动较大,或者说频繁进行插入和删除操作的数据结构来说,采用链式存储结构比顺序存储结构更加合适。
所以我们可以采用链式存储结构来实现队列。我们用一个线性链表来表示队列,队列中的每一个元素对应链表中的一个链节点。然后把线性链表的第 1
个节点定义为队头指针 front
,在链表最后的链节点建立指针 rear
作为队尾指针。并且限定只能在链表队头进行删除操作,在链表队尾进行插入操作,这样整个线性链表就构成了一个队列。
这里规定: 队头指针 self.front
指向队头元素所在位置的前一个位置,而队尾指针 self.rear
指向队尾元素所在位置。
self.head
,令队头指针 self.front
和队尾指针 self.rear
都指向 head
。即 self.front = self.rear = head
。self.front
和 self.rear
的指向位置进行判断。根据约定,如果对头指针 self.front
等于队尾指针 self.rear
,则说明队列为空。self.front
指向队头元素所在位置的前一个位置,所以队头元素在 self.front
后一个位置上,返回 self.front.next.value
。self.rear
指向队尾元素所在位置,所以直接返回 self.rear.value
。value
的链表节点,插入到链表末尾,并令队尾指针 self.rear
沿着链表移动 1
位到链表末尾。此时 self.rear
指向队尾元素。self.front
下一个位置节点上的值,并将 self.front
沿着链表移动 1
位。如果 self.front
下一个位置是 self.rear
,则说明队列为空,此时,将 self.rear
赋值为 self.front
,令其相等。python代码:
class Node:
def __init__(self, value):
self.value = value
self.next = None
class Queue:
# 初始化空队列
def __init__(self):
head = Node(0)
self.front = head
self.rear = head
# 判断队列是否为空
def is_empty(self):
return self.front == self.rear
# 入队操作
def enqueue(self, value):
node = Node(value)
self.rear.next = node
self.rear = node
# 出队操作
def dequeue(self):
if self.is_empty():
raise Exception('Queue is empty')
else:
node = self.front.next
self.front.next = node.next
if self.rear == node:
self.rear = self.front
value = node.value
del node
return value
# 获取队头元素
def front_value(self):
if self.is_empty():
raise Exception('Queue is empty')
else:
return self.front.next.value
# 获取队尾元素
def rear_value(self):
if self.is_empty():
raise Exception('Queue is empty')
else:
return self.rear.value
队列是算法和程序中最常用的辅助结构,其应用十分广泛。比如现实生活中的排队买票、银行办理业务挂号等等。队列在计算机科学领域的应用主要提现在以下两个方面:
可以利用队列的性质来刷一下力扣的几道题!
622.设计循环队列
225.用队列实现栈