1.先进者先出,这就是典型的「队列」结构。
2.支持两个操作:
enqueue()
,放一个数据到队尾;dequeue()
,从队头取一个元素。3.所以,和栈一样,队列也是一种操作受限的线性表。
public interface Queue<T> {
public void enqueue(T item); //入队
public T dequeue(); //出队
public int size(); //统计元素数量
public boolean isNull(); //是否为空
}
下面是 python
代码:
class ArrayQueue():
# 数组实现顺序队列
def __init__(self, capacity):
self._capacity = capacity
self._items = []
self._head, self._tail = 0, 0
# 入队列,从队尾入
def enqueue(self, item):
# 判断队列是否已满
if self._tail == self._capacity:
if self._head == 0:
return False
else:
# 数据搬移
for i in range(self._tail - self._head):
self._items[i] = self._items[self._head + i]
self._tail = self._tail - self._head
self._head = 0
self._items.insert(self._tail, item)
self._tail += 1
return True
# 出队列,从队头出
def dequeue(self):
# 判断队列是否为空
if self._head == self._tail:
return None
else:
item = self._items[self._head]
self._head += 1
return item
def __repr__(self):
if self._head == self._tail:
return '空'
else:
result = []
cur = self._head
while cur != self._tail:
result.append(self._items[cur])
cur += 1
return '->'.join(str(x) for x in result)
# def __str__(self):
# return self.__repr__()
# 测试
if __name__ == '__main__':
queue = ArrayQueue(10)
for i in range(10):
queue.enqueue(i)
print(queue)
for i in range(5):
print(queue.dequeue())
print(queue)
下面是 python
代码:
class LoopArrayQueue():
# 数组实现循环队列
def __init__(self, capacity):
self._capacity = capacity + 1
self._items = [None] * self._capacity
self._head, self._tail = 0, 0
# 入队列,从队尾入
def enqueue(self, item):
# 如果队列已满
if self.is_full():
return False
else:
self._items[self._tail] = item
self._tail = (self._tail + 1) % self._capacity
return True
# 出队列,返回的是出队列的元素的值
def dequeue(self):
# 如果队列为空
if self.is_empty():
return False
else:
self._items[self._head] = None # 将其清空
self._head = (self._head + 1) % self._capacity
return True
def is_full(self):
return (self._tail + 1) % self._capacity == self._head
def is_empty(self):
return self._tail == self._head
# 返回循环队列的队头元素
def prior(self):
return self._items[self._head] if not self.is_empty() else -1
# 返回循环队列的队尾元素
def tail(self):
return self._items[(self._tail - 1 + self._capacity) % self._capacity] if not self.is_empty() else -1
def __repr__(self):
if self._head == self._tail:
return '空'
else:
cur = self._head
result = []
while cur != self._tail:
result.append(self._items[cur])
cur = (cur + 1) % self._capacity
return '->'.join(str(x) for x in result)
if __name__ == '__main__':
queue = LoopArrayQueue(10)
for i in range(8):
queue.enqueue(i)
print(queue)
for i in range(5):
queue.dequeue()
print(queue)
print(queue.prior(), queue.tail())
for i in range(4):
queue.enqueue(i)
print(queue)
下面是 python
代码,这里使用的是不带头结点的单链表:
class Node():
def __init__(self, x):
self.data = x
self.next = None
class LinkedQueue():
def __init__(self):
self._head = None
self._tail = None
# 进队列
def enqueue(self, val):
newNode = Node(val)
# 如果链表队列为空
if self._tail == None:
self._head = newNode
else:
self._tail.next = newNode
self._tail = newNode
# 出队列,返回的是出队列的node的值
def dequeue(self):
# 如果队列为空
if self._head == self._tail:
return None
else:
node = self._head
self._head = self._head.next
return node.data
def __repr__(self):
# 队列为空
if self._head == self._tail:
return '空'
else:
result = []
cur = self._head
while cur != None:
result.append(cur.data)
cur = cur.next
return '->'.join(str(x) for x in result)
if __name__ == '__main__':
queue = LinkedQueue()
for i in range(10):
queue.enqueue(i)
print(queue)
for i in range(5):
queue.dequeue()
print(queue)
for i in range(5):
queue.enqueue(i)
print(queue)
for i in range(10):
queue.dequeue()
print(queue)
需要注意的是,一般情况下
head == tail
表示的是队列为空,但是在不带头结点的单链表的情况下,head == tail
还有可能表示队列只有一个节点,所以还要加上head == tail == None
才能表示队列为空,所以不带头结点的单链表是一种很特殊的情况,这里要重点强调。
1)在队列的基础上增加阻塞操作,就成了阻塞队列。
2)阻塞队列就是在队列为空的时候,从队头取数据会被阻塞,因为此时还没有数据可取,直到队列中有了数据才能返回;如果队列已经满了,那么插入数据的操作就会被阻塞,直到队列中有空闲位置后再插入数据,然后在返回。
从上面的定义可以看出这就是一个「生产者-消费者模型」。这种基于阻塞队列实现的「生产者-消费者模型」可以有效地协调生产和消费的速度。当「生产者」生产数据的速度过快,「消费者」来不及消费时,存储数据的队列很快就会满了,这时生产者就阻塞等待,直到「消费者」消费了数据,「生产者」才会被唤醒继续生产。不仅如此,基于阻塞队列,我们还可以通过协调「生产者」和「消费者」的个数,来提高数据处理效率,比如配置几个消费者,来应对一个生产者。
1)在多线程的情况下,会有多个线程同时操作队列,这时就会存在线程安全问题。能够有效解决线程安全问题的队列就称为并发队列。
2)并发队列简单的实现就是在 enqueue()、dequeue() 方法上加锁,但是锁粒度大并发度会比较低,同一时刻仅允许一个存或取操作。
3)实际上,基于数组的循环队列利用 CAS 原子操作,可以实现非常高效的并发队列。这也是循环队列比链式队列应用更加广泛的原因。
在资源有限的场景,当没有空闲资源时,基本上都可以通过「队列」这种数据结构来实现请求排队。