队列的链式存储结构及其实现
A queue is a collection of items whereby its operations work in a FIFO — First In First Out manner. The two primary operations associated with them are enqueue and dequeue.
队列是项目的集合,由此其操作以FIFO(先进先出)的方式工作。 与之相关的两个主要操作是入队和出队 。
This lesson was originally published at https://algodaily.com, where I maintain a technical interview course and write think-pieces for ambitious developers.
本课程最初在 https://algodaily.com上 发布 ,我 在 那里维护技术面试课程,并为雄心勃勃的开发人员撰写思想著作。
Lesson Objectives: At the end of this lesson, you will be able to:
课程目标 :在本课程结束时,您将能够:
Know what the queue data structure is and appreciate it’s real-world use cases.
了解队列数据结构是什么,并了解它的实际用例。
- Learn how queues work and their operations. 了解队列如何工作及其操作。
- Know and implement queues with two different approaches. 用两种不同的方法了解和实现队列。
I’m sure all of us have been in queues before — perhaps at billing counters, shopping centers, or cafes. The first person in the line is usually serviced first, then the second, third, and so forth.
我敢肯定,我们所有人以前都在排队-也许在计费柜台,购物中心或咖啡馆。 通常首先为该行中的第一个人提供服务,然后为第二,第三等提供服务。
We have this concept in computer science as well. Take the example of a printer. Suppose we have a shared printer, and several jobs are to be printed at once. The printer maintains a printing “queue” internally, and prints the jobs in sequence based on which came first.
我们在计算机科学中也有这个概念。 以打印机为例。 假设我们有一台共享打印机,并且要一次打印多个作业。 打印机在内部维护打印“队列”,并根据先到的顺序依次打印作业。
Another instance where queues are extensively used is in the operating system of our machines. An OS maintains several queues such as a job queue, a ready queue, and a device queue for each of the processes. If you’re interested, refer to this link to know more about them.
队列被广泛使用的另一个实例是我们机器的操作系统。 操作系统为每个进程维护多个队列,例如作业队列,就绪队列和设备队列。 如果您有兴趣,请参考此链接以进一步了解它们。
I hope we’ve got a solid high-level understanding about what queues are. Let’s go ahead and understand how they work!
我希望我们对什么是队列有深入的了解。 让我们继续前进,了解它们如何工作!
队列如何工作? (How do queues work?)
Consider a pipe, perhaps a metal one in your bathroom or elsewhere in the house. Naturally, it has two open ends. Imagine that we have some elements in the pipe, and we’re trying to get them out. There will be one end through which we have inserted the elements, and there’s another end from which we’re getting them out. As seen in the figure below, this is precisely how the queue data structure is shaped.
考虑一下管道,也许是您的浴室或房屋其他地方的金属管。 自然,它有两个开放的末端。 想象一下,我们在管道中有一些元素,而我们正在努力将它们淘汰。 我们将插入元素的一端,而将它们取出的另一端。 如下图所示,这正是队列数据结构的整形方式。
Unlike the stack data structure that we primarily think of with one “open end”, the queue has two open ends: the front and rear. They have different purposes — with the rear being the point of insertion and the front being that of removal. However, internally, the front and rear are treated as pointers. We’ll learn more about them in the subsequent sections programmatically.
与我们最初想到的带有一个“开放端”的堆栈数据结构不同,队列具有两个开放端: 前和后 。 它们具有不同的用途- 后方是插入点, 前部是拆卸点。 但是,在内部,前后均被视为指针。 我们将在后面的部分中以编程方式了解有关它们的更多信息。
Note that the element that got inside first is the initial one to be serviced, and removed from the queue. Hence the name: First In First Out (FIFO).
请注意,首先进入的元素是要提供服务的第一个元素,并已从队列中删除。 因此,名称为:先进先出(FIFO)。
队列操作和队列的实现 (Queue operations and Implementation of queues)
Similar to how a stack has push
and pop
operations, a queue also has two pairwise operations:
类似于堆栈具有push
和pop
操作的方式,队列也具有两个成对操作:
- Enqueue: To add elements 排队:添加元素
- Dequeue: To remove elements. 出队:删除元素。
Let’s move on and cover each.
让我们继续进行介绍。
Click here to check out our lesson on the stack data structure!
单击此处查看有关堆栈数据结构的课程!
1.入队 (1. Enqueue)
The enqueue
operation, as said earlier, adds elements to your queue from the rear end. Initially, when the queue
is empty, both our front
(sometimes called head
) and rear (sometimes called tail
) pointers are NULL
.
如前所述,入enqueue
操作从后端将元素添加到您的队列中。 最初,当queue
为空时,我们的front
(有时称为head
)和后 (有时称为tail
)指针都是NULL
。
Now, let’s add an element — say, 'a'
-- to the queue. Both our front and rear now point to 'a'
.
现在,让我们向队列添加一个元素(例如'a'
。 无论我们的前部和后部现在点'a'
。
Let’s add another element to our queue — 'b'
. Now, our front pointer remains the same, whereas the rear pointer points to 'b'
. We'll add another item 'c'
and you'll see that that element is also added at the rear end.
让我们向队列添加另一个元素-'b 'b'
。 现在,我们的前指针保持不变, 而后指针指向'b'
。 我们将添加另一个项目'c'
,您将看到该元素也添加在后端 。
2.出队 (2. Dequeue)
To dequeue
means to remove or delete elements from the queue. This happens from the front end of the queue. A particular element is removed from a queue after it is done being processed or serviced. We cannot dequeue
an empty queue, and we require at least one element to be present in the queue when we want to dequeue
. The following figure explains the dequeuing of our previous queue.
dequeue
意味着从队列中删除或删除元素。 这是从队列的前端发生的。 在处理或提供服务后,会将特定元素从队列中删除。 我们不能dequeue
空队列,我们需要至少一个元素出现在队列中,当我们要dequeue
。 下图说明了我们之前的队列的出队。
实作 (Implementation)
Let’s use python
for our implementation. In python
, queues can be implemented using three different modules from the python library.
让我们使用python
来实现。 在python
,可以使用python库中的三个不同模块来实现队列。
list (using a
list
orarray
is generalizable to most languages)列表(使用
list
或array
可推广到大多数语言)- collections.deque (language-specific) collections.deque(特定于语言)
- queue.Queue (language-specific) queue.Queue(特定于语言)
Using the list
class can be a costly affair since it involves shifting of elements for every addition or deletion. This requires O(n)
time. Instead, we can use the 'deque' class, which is a shorthand for 'Double-ended queue' and requires O(1)
time, which is much more efficient.
使用list
类可能是一件昂贵的事情,因为它涉及到每次添加或删除时元素的移动。 这需要O(n)
时间。 取而代之的是,我们可以使用'deque'类,它是'Double-ended queue'的简写,并且需要O(1)
时间,效率更高。
So first — we can quickly implement a queue
using a list
or array
in most languages! This is intuitive given that they're both linear data structures, and we just need to enforce some constraints on data flow:
首先,我们可以使用大多数语言的list
或array
快速实现queue
! 鉴于它们都是线性数据结构,因此这很直观,我们只需要对数据流施加一些约束:
To
enqueue
an item in the queue, we can use the list functionappend
.为了
enqueue
队列中的一个项目,我们可以使用列表功能append
。To
dequeue
an item from the queue, we can use the list functionpop(0)
.要从队列中
dequeue
项目,我们可以使用列表函数pop(0)
。If we want the “top-most” (or last element to be processed) item in the queue, we can get the last index of the list using the
[-1]
index operator.如果我们想要队列中“最顶层”(或最后一个要处理的元素)项,则可以使用
[-1]
索引运算符获取列表的最后一个索引。
This is by far the easiest approach, but not necessarily the most performant.
到目前为止,这是最简单的方法,但不一定是性能最高的方法。
# Initialize a queue list
queue = []
# Add elements
queue.append(1)
queue.append(2)
queue.append(3)
print("Initial queue state:")
print(queue)
# Removing elements from the queue
print("Elements dequeued from queue")
print(queue.pop(0))
print(queue.pop(0))
print(queue.pop(0))
print("Queue after removing elements")
print(queue)
使用队列类实现队列 (Implementation of queue using queue class)
Another way of using queues in python is via the queue class available in Queue module. It has numerous functions and is widely used along with threads for multi-threading operations. It further has FIFO, LIFO, and priority types of queues. However, we’ll implement a simple queue using the queue class of python library.
在python中使用队列的另一种方法是通过Queue模块中可用的队列类。 它具有许多功能,并且与线程一起广泛用于多线程操作。 它还具有FIFO,LIFO和优先级队列。 但是,我们将使用python库的queue类实现一个简单的队列。
The queue
class is imported from the Queue
module. The queue is initialized using the Queue()
constructor. Note that it accepts a maxsize()
argument, specifying an upper boundary of queue size to throttle memory usage.
queue
类是从“ Queue
模块中导入的。 使用Queue()
构造函数初始化Queue()
。 请注意,它接受maxsize()
参数,该参数指定队列大小的上限以限制内存使用量。
We use the put()
function to add elements to the queue, and the get()
function to remove elements from the queue. Since we have a maxsize
check here, we have two other functions to check empty and full conditions. The unction empty()
returns a boolean true if the queue is empty and false if otherwise. Likewise, the full()
function returns a boolean true if the queue is full and false if otherwise.
我们使用put()
函数将元素添加到队列中,使用get()
函数从队列中删除元素。 由于这里有一个maxsize
检查,因此我们还有另外两个功能可以检查空和满条件。 如果队列为空,则unction empty()
返回布尔值true,否则返回false 。 同样,如果队列已满,则full()
函数返回布尔值true,否则返回false 。
Here, we added elements to the queue and checked for the full condition using q.full().
Since the maxsize
is four and we added four elements, the boolean is set to true
.
在这里,我们将元素添加到队列中,并使用q.full().
检查是否已满q.full().
由于maxsize
为四个,并且我们添加了四个元素,因此布尔值设置为true
。
Later, we removed three elements, leaving one element in the queue. Hence the q.empty()
function returned boolean false.
后来,我们删除了三个元素,在队列中保留了一个元素。 因此, q.empty()
函数返回布尔值false。
You can find more functions on deque collections here.
您可以在此处找到关于双端队列的更多功能。
# Python program to demonstrate the implementation of a queue using the queue modulefrom queue import Queue# Initializing a queue with maxsize 4
q = Queue(maxsize = 4)# Add/enqueue elements to queue
q.put('a')
q.put('b')
q.put('c')
q.put('d')# Return Boolean for Full Queue
print("\nFull: ", q.full())# Remove/dequeue elements from queue
print("\nElements dequeued from the queue")
print(q.get())
print(q.get())
print(q.get())# Return Boolean for Empty Queue
print("\nEmpty: ", q.empty())
print("\nQueue size:", q.qsize()) # prints size of the queue
使用双端队列类实现队列 (Implementation of queue using deque class)
Let’s go ahead and utilize a queue along with its operations in python language using the deque
class!
让我们继续使用deque
类在Python语言中使用deque
及其操作!
The deque class is imported from the collections module. We use append()
function to add elements to the queue and popleft()
function to remove elements from the queue.
双端队列类是从collections模块导入的。 我们使用append()
函数将元素添加到队列中,并使用popleft()
函数从队列中删除元素。
We can see that after enqueuing, our initial queue looks like this:
我们可以看到,入队后,我们的初始队列如下所示:
Initial queue:
deque(['a', 'b', 'c', 'd'])
And after dequeuing, our final queue looks something like this:
出队后,我们的最终队列如下所示:
Final queue
deque(['d'])
You can find more functions on deque collections here.
您可以在此处找到有关双端队列的更多功能。
# Python program to demonstrate queue implementation using collections.dequeuefrom collections import deque# Initializing a deque with deque() constructor
q = deque()# Adding/Enqueueing elements to a queue
q.append('a')
q.append('b')
q.append('c')
q.append('d')print("Initial queue:")
print(q)# Removing/Dequeuing elements from a queue
print("\nElements dequeued from the queue:")
print(q.popleft())
print(q.popleft())
print(q.popleft())print("\nFinal queue")
print(q)
结论 (Conclusion)
In this article, we began right from the basics of queues then learned the queue operations later scaled to two different approaches in implementing queues using python. We saw how the FIFO approach works in queues and how using collections is effective in terms of time complexity. I recommend you to go through the resources linked in-line with the article for further reading on queues.
在本文中,我们从队列的基础开始,然后学习了队列操作,后来扩展为使用python实现队列的两种不同方法。 我们了解了FIFO方法在队列中的工作方式,以及使用集合在时间复杂度方面如何有效。 我建议您仔细阅读与文章内联的资源,以进一步了解队列。
翻译自: https://medium.com/swlh/understanding-the-queue-data-structure-and-its-implementations-59685f0112c
队列的链式存储结构及其实现