📜  珀尔 |实现队列

📅  最后修改于: 2022-05-13 01:55:34.522000             🧑  作者: Mango

珀尔 |实现队列

先决条件:堆栈
Perl 中的队列是一种遵循FIFO (先进先出)顺序的线性抽象数据结构。它类似于我们在日常生活中遇到的队列的属性,即先来的人将首先得到服务。它的两端都是开放的。与插入和删除操作发生在称为栈顶的同一端的堆栈不同,在队列中,这些操作发生在称为队列的前端后端的不同端。

队列操作:

对队列执行以下基本操作:

  1. 入队(插入):将项目添加到队列中。如果队列已满,则会发生溢出情况。
  2. 出队(删除):从队列中删除一个项目。如果队列为空,则会发生下溢情况。
  3. Front:从队列中获取最前面的项目。
  4. Rear:从队列中获取最后一项。

创建队列

在 Perl 中创建队列非常简单。这可以通过声明一个数组来完成,该数组可以是空的,也可以使用一些预填充的数据进行初始化。

@queue;        # Queue is empty.

@queue = (1, 2, 3);      # Queue with initialized values.

队列类型

根据插入和删除的标准,队列可以有以下几种类型:

简单队列

简单队列只是一个队列,其中插入发生在队列的前面,删除发生在队列的末尾。
在 Perl 中,队列的操作可以使用pushshift函数来实现。
push函数用于在队列末尾添加一个或多个值。
shift函数会将队列向左移动一个单元,删除(弹出)第一个元素。如果队列为空,它将返回undef

例子:

Perl
#!/usr/bin/perl
use strict;
use warnings;
   
# Intitialising the Queue
my @queue = (1, 2, 3);
   
# Original queue
print "Original Queue: @queue";
 
# Pushing values into queue
push(@queue, (4, 5, 6)); 
   
# Updated Queue after 
# Push operation
print("\nUpdated Queue after Push: @queue");
 
 
# Popping values from queue
my $val1 = shift(@queue);
print("\nElement popped is: $val1");
my $val2 = shift(@queue);
print("\nElement popped is: $val2"); 
   
# Updated Queue after 
# Pop operations
print("\nUpdated Queue after Pop: @queue");


Perl
#!/usr/bin/perl
use strict;
use warnings;
 
# Intitialising the Queue
my @queue = (1, 2, 3);
my $size = 7; # No. of elements to be stored in queue
 
# Initially queue has three elements
my $front = 0;
my $rear = 2;
 
# Original queue
print "Original Queue: @queue";
 
# Pushing values into queue
$rear = ($rear + 1) % $size;
my $val = 4;
while(1)
{
    if ($rear == $front)
    {
        print("\nQueue is full.");
        last;
    }
    else
    {
        print("\nPushed $val in queue");
        $queue[$rear] = $val;
        $rear = ($rear + 1) % $size;
        $val += 1;
    }
}
 
# Updated Queue after
# Push operation
print("\nUpdated Queue after Push: @queue");
 
# Popping values from queue
my $val1 = $queue[$front];
$queue[$front] = -1;
$front = ($front + 1) % $size;
print("\nElement popped is: $val1");
 
my $val2 = $queue[$front];
$queue[$front] = -1;
$front = ($front + 1) % $size;
print("\nElement popped is: $val2");
 
# Updated Queue after
# Pop operations
print("\nUpdated Queue after Pop: @queue");
while(1)
{
    if ($rear % $size == $front)
    {
        print("\nQueue is full.");
        last;
    }
    else
    {
        print("\nPushed $val in queue");
        $queue[$rear] = $val;
        $rear += 1;
        $val += 1;
    }
}
print("\nUpdated Queue after Push: @queue");


Perl
#!/usr/bin/perl
use strict;
use warnings;
 
# Initialising queue with priority
my @queue = ([1, 3], [2, 7], [4, 3], [5, 2]);
 
sub pull_highest_prio
{
    my $max = 0;
    for (my $i = 0; $i < scalar(@queue); $i++)
    {
        if ($queue[$i][1] > $queue[$max][1])
        {
            $max = $i;
        }
    }
    print("\nPopped item: $queue[$max][0], ",
          "Priority: $queue[$max][1]");
    splice @queue, $max, 1;
}
 
# Driver Code
print("\nOriginal Queue is ");
for (my $i = 0; $i < 4; $i++)
{
    print("$queue[$i][0] ");
}
push(@queue, [11, 9]);
push(@queue, [7, 0]);
 
print("\nUpdated Queue after push is ");
for (my $i = 0; $i < 6; $i++)
{
    print("$queue[$i][0] ");
}
pull_highest_prio;
pull_highest_prio;
pull_highest_prio;
pull_highest_prio;
 
print("\nUpdated Queue after pop is ");
for (my $i = 0; $i < 2; $i++)
{
    print("$queue[$i][0] ");
}


Perl
#!/usr/bin/perl
use strict;
use warnings;
 
# Intitialising the Queue
my @queue = (1, 2, 3);
my $front = 0;
my $rear = 2;
sub insert_front
{
    my $val = $_[0];
    unshift(@queue, $val);
    $rear += 1;
}
 
sub insert_rear
{
    my $val = $_[0];
    push(@queue, $val);
    $rear += 1;
}
 
sub delete_front
{
    print("\nElement popped is: $queue[0]");
    splice @queue, 0, 1;
    $rear -= 1;
}
 
sub delete_rear
{
    print("\nElement popped is: $queue[$rear]");
    splice @queue, scalar(@queue) - 1, 1;
    $rear -= 1;
}
 
# Original queue
print "Original Queue: @queue";
 
# Pushing values into queue
&insert_rear(4);
&insert_rear(3);
print("\nRear element is $queue[$rear]");
&insert_front(1);
&insert_front(7);
print("\nFront element is $queue[$front]");
 
# Updated Queue after
# Push operation
print("\nUpdated Queue after Push: @queue");
 
# Popping values from queue
delete_rear;
delete_front;
print("\nFront element is $queue[$front]");
 
&insert_front(20);
delete_rear;
print("\nRear element is $queue[$rear]");
 
# Updated Queue after
# Pop operations
print("\nUpdated Queue after Pop: @queue");


Perl
#!/usr/bin/perl
use strict;
use warnings;
 
use Thread::Queue;
 
# A new pre-populated queue
my $q = Thread::Queue->new(1, 2, 3);    
 
sub head
{
    if ($q->pending() == 0)
    {
        print("\nThe queue is empty.");
    }
    else
    {
        my $item = $q->dequeue();
        print("\nHead of the Queue is $item.");
    }
}
my $isize = $q->pending();
print("Initial size of the queue is $isize");
 
# insert three items at the back of the queue
$q->enqueue(5, 6, 7);    
&head();
 
my $size = $q->pending();
print("\nThe size of the queue is $size");
 
# delete the head item of the queue
$q->dequeue();            
&head();
 
# delete two items from the front
$q->dequeue(2);        
my $size2 = $q->pending();
print"\nThe size of the queue is $size2";
 
&head();
&head();
$q->end();


Perl
#!/usr/bin/perl
use strict;
use warnings;
use 5.010;
      
say "PID $$";
my $pid = fork();
die if not defined $pid;
say "PID $$ ($pid)";


输出:
Original Queue: 1 2 3
Updated Queue after Push: 1 2 3 4 5 6
Element popped is: 1
Element popped is: 2
Updated Queue after Pop: 3 4 5 6

循环队列

在循环队列中,队列的最后一个位置与第一个位置相连形成一个圆圈,也称为环形缓冲区。如果队列从前面看是空的,但最后是满的,那么我们也可以在循环队列中插入元素,这在简单队列中不是这样。与普通队列不同,循环队列将具有固定大小
循环队列除了pushshift函数外,还有两个变量front和 back 来存储队列的前后位置。

例子:

Perl

#!/usr/bin/perl
use strict;
use warnings;
 
# Intitialising the Queue
my @queue = (1, 2, 3);
my $size = 7; # No. of elements to be stored in queue
 
# Initially queue has three elements
my $front = 0;
my $rear = 2;
 
# Original queue
print "Original Queue: @queue";
 
# Pushing values into queue
$rear = ($rear + 1) % $size;
my $val = 4;
while(1)
{
    if ($rear == $front)
    {
        print("\nQueue is full.");
        last;
    }
    else
    {
        print("\nPushed $val in queue");
        $queue[$rear] = $val;
        $rear = ($rear + 1) % $size;
        $val += 1;
    }
}
 
# Updated Queue after
# Push operation
print("\nUpdated Queue after Push: @queue");
 
# Popping values from queue
my $val1 = $queue[$front];
$queue[$front] = -1;
$front = ($front + 1) % $size;
print("\nElement popped is: $val1");
 
my $val2 = $queue[$front];
$queue[$front] = -1;
$front = ($front + 1) % $size;
print("\nElement popped is: $val2");
 
# Updated Queue after
# Pop operations
print("\nUpdated Queue after Pop: @queue");
while(1)
{
    if ($rear % $size == $front)
    {
        print("\nQueue is full.");
        last;
    }
    else
    {
        print("\nPushed $val in queue");
        $queue[$rear] = $val;
        $rear += 1;
        $val += 1;
    }
}
print("\nUpdated Queue after Push: @queue");
输出:
Original Queue: 1 2 3
Pushed 4 in queue
Pushed 5 in queue
Pushed 6 in queue
Pushed 7 in queue
Queue is full.
Updated Queue after Push: 1 2 3 4 5 6 7
Element popped is: 1
Element popped is: 2
Updated Queue after Pop: -1 -1 3 4 5 6 7
Pushed 8 in queue
Pushed 9 in queue
Queue is full.
Updated Queue after Push: 8 9 3 4 5 6 7

优先队列

在优先级队列中,每个项目都有一个与之关联的已定义优先级。具有最低优先级的项目将首先从优先级队列中移除。具有相同优先级的项目将按其顺序处理
在队列中的位置。

例子:

Perl

#!/usr/bin/perl
use strict;
use warnings;
 
# Initialising queue with priority
my @queue = ([1, 3], [2, 7], [4, 3], [5, 2]);
 
sub pull_highest_prio
{
    my $max = 0;
    for (my $i = 0; $i < scalar(@queue); $i++)
    {
        if ($queue[$i][1] > $queue[$max][1])
        {
            $max = $i;
        }
    }
    print("\nPopped item: $queue[$max][0], ",
          "Priority: $queue[$max][1]");
    splice @queue, $max, 1;
}
 
# Driver Code
print("\nOriginal Queue is ");
for (my $i = 0; $i < 4; $i++)
{
    print("$queue[$i][0] ");
}
push(@queue, [11, 9]);
push(@queue, [7, 0]);
 
print("\nUpdated Queue after push is ");
for (my $i = 0; $i < 6; $i++)
{
    print("$queue[$i][0] ");
}
pull_highest_prio;
pull_highest_prio;
pull_highest_prio;
pull_highest_prio;
 
print("\nUpdated Queue after pop is ");
for (my $i = 0; $i < 2; $i++)
{
    print("$queue[$i][0] ");
}
输出:
Original Queue is 1 2 4 5 
Updated Queue after push is 1 2 4 5 11 7 
Popped item: 11, Priority: 9
Popped item: 2, Priority: 7
Popped item: 1, Priority: 3
Popped item: 4, Priority: 3
Updated Queue after pop is 5 7

Deque(双端队列)

双端队列只是简单队列的通用化版本,除了插入和删除操作外,可以在队列的前端后端进行。它是一种非常重要的数据结构类型,因为它既可以用作堆栈也可以用作队列。

例子:

Perl

#!/usr/bin/perl
use strict;
use warnings;
 
# Intitialising the Queue
my @queue = (1, 2, 3);
my $front = 0;
my $rear = 2;
sub insert_front
{
    my $val = $_[0];
    unshift(@queue, $val);
    $rear += 1;
}
 
sub insert_rear
{
    my $val = $_[0];
    push(@queue, $val);
    $rear += 1;
}
 
sub delete_front
{
    print("\nElement popped is: $queue[0]");
    splice @queue, 0, 1;
    $rear -= 1;
}
 
sub delete_rear
{
    print("\nElement popped is: $queue[$rear]");
    splice @queue, scalar(@queue) - 1, 1;
    $rear -= 1;
}
 
# Original queue
print "Original Queue: @queue";
 
# Pushing values into queue
&insert_rear(4);
&insert_rear(3);
print("\nRear element is $queue[$rear]");
&insert_front(1);
&insert_front(7);
print("\nFront element is $queue[$front]");
 
# Updated Queue after
# Push operation
print("\nUpdated Queue after Push: @queue");
 
# Popping values from queue
delete_rear;
delete_front;
print("\nFront element is $queue[$front]");
 
&insert_front(20);
delete_rear;
print("\nRear element is $queue[$rear]");
 
# Updated Queue after
# Pop operations
print("\nUpdated Queue after Pop: @queue");
输出:
Original Queue: 1 2 3
Rear element is 3
Front element is 7
Updated Queue after Push: 7 1 1 2 3 4 3
Element popped is: 3
Element popped is: 7
Front element is 1
Element popped is: 4
Rear element is 3
Updated Queue after Pop: 20 1 1 2 3

抽象实现

抽象意味着对普通用户隐藏操作的功能,即提供操作功能而不定义函数背后的逻辑。
在 Perl 中,队列的抽象实现可以使用 Perl 的内置模块Thread::Queue来实现,该模块提供线程安全的 FIFO 队列,可以被任意数量的线程访问。

基本方法:

  1. new(list) – 使用提供的项目列表创建一个新队列,如果未列出,则创建一个新的空队列。
  2. enqueue(list) – 将项目列表添加到队列的末尾。
  3. dequeue(COUNT) – 从队列的头部删除请求的项目数,并返回它们,如果没有提到,它会从队列中取出一个元素。
  4. pending() - 返回队列中仍保留的项目数,或者我们可以说,它基本上返回队列的大小。
  5. end() - 声明不再向队列添加项目。

例子:

Perl

#!/usr/bin/perl
use strict;
use warnings;
 
use Thread::Queue;
 
# A new pre-populated queue
my $q = Thread::Queue->new(1, 2, 3);    
 
sub head
{
    if ($q->pending() == 0)
    {
        print("\nThe queue is empty.");
    }
    else
    {
        my $item = $q->dequeue();
        print("\nHead of the Queue is $item.");
    }
}
my $isize = $q->pending();
print("Initial size of the queue is $isize");
 
# insert three items at the back of the queue
$q->enqueue(5, 6, 7);    
&head();
 
my $size = $q->pending();
print("\nThe size of the queue is $size");
 
# delete the head item of the queue
$q->dequeue();            
&head();
 
# delete two items from the front
$q->dequeue(2);        
my $size2 = $q->pending();
print"\nThe size of the queue is $size2";
 
&head();
&head();
$q->end();
输出:
size of the queue is 3
Head of the Queue is 1.
The size of the queue is 5
Head of the Queue is 3.
The size of the queue is 1
Head of the Queue is 7.
The queue is empty.

并行或异步处理

Parallel这个词意味着同时或同时, Asynchronous是指连续或非阻塞。队列的实现操作是阻塞的,即在执行一个操作时,另一个操作必须先等待它结束,然后再执行。这对于少量任务来说很好,但对于大量任务或操作,这种类型的处理有点忙和耗时。
为了克服这个问题,Perl 提供了一些进行并行或异步处理的方法,以便这些操作可以同时运行或无需等待其他操作结束。对此的两个主要解决方案是threadingforking 。分叉是创建某个进程的完全相同的副本,然后这两个进程并行执行,而它们的变量之间没有太多联系,尽管它们操纵或处理相同的变量(副本)。它是使用fork()函数完成的。

例子:

Perl

#!/usr/bin/perl
use strict;
use warnings;
use 5.010;
      
say "PID $$";
my $pid = fork();
die if not defined $pid;
say "PID $$ ($pid)";
输出:
PID 22112
PID 22112 (22113)
PID 22113 (0)

在调用fork()之前,我们得到了一个 PID (22112),它是当前进程的进程 ID,在 fork 之后我们得到了两行。第一个打印行来自与当前进程相同的进程(它具有相同的 PID),第二个打印行来自子进程(PID 63779)。第一个从 fork 收到一个 $pid,其中包含子进程的编号。第二,子进程得到数字 0。
Perl 还通过 POE 框架支持事件驱动编程。