-
-
Notifications
You must be signed in to change notification settings - Fork 71
/
PriorityQueue.php
153 lines (134 loc) · 3.32 KB
/
PriorityQueue.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
<?php
declare(strict_types=1);
namespace Psl\DataStructure;
use Psl\Math;
use function array_keys;
use function array_shift;
use function count;
/**
* @template T
*
* @implements PriorityQueueInterface<T>
*/
final class PriorityQueue implements PriorityQueueInterface
{
/**
* @var array<int, non-empty-list<T>>
*/
private array $queue = [];
/**
* Provides a default instance of the {@see PriorityQueue}.
*
* @return static A new instance of {@see PriorityQueue}, devoid of any nodes.
*
* @pure
*/
public static function default(): static
{
return new self();
}
/**
* Adds a node to the queue.
*
* @param T $node
*
* @psalm-external-mutation-free
*/
public function enqueue(mixed $node, int $priority = 0): void
{
$nodes = $this->queue[$priority] ?? [];
$nodes[] = $node;
$this->queue[$priority] = $nodes;
}
/**
* Retrieves, but does not remove, the node at the head of this queue,
* or returns null if this queue is empty.
*
* @return null|T
*
* @psalm-mutation-free
*/
public function peek(): mixed
{
if (0 === $this->count()) {
return null;
}
$keys = array_keys($this->queue);
// Retrieve the highest priority.
$priority = Math\max($keys) ?? 0;
// Retrieve the list of nodes with the priority `$priority`.
$nodes = $this->queue[$priority] ?? [];
// Retrieve the first node of the list.
return $nodes[0] ?? null;
}
/**
* Retrieves and removes the node at the head of this queue,
* or returns null if this queue is empty.
*
* @return null|T
*
* @psalm-external-mutation-free
*/
public function pull(): mixed
{
try {
return $this->dequeue();
} catch (Exception\UnderflowException) {
return null;
}
}
/**
* Dequeues a node from the queue.
*
* @throws Exception\UnderflowException If the queue is empty.
*
* @return T
*
* @psalm-external-mutation-free
*/
public function dequeue(): mixed
{
if (0 === $this->count()) {
throw new Exception\UnderflowException('Cannot dequeue a node from an empty queue.');
}
/**
* retrieve the highest priority.
*
* @var int
*/
$priority = Math\max(array_keys($this->queue));
/**
* retrieve the list of nodes with the priority `$priority`.
*/
$nodes = $this->queue[$priority];
/**
* shift the first node out.
*/
$node = array_shift($nodes);
/**
* If the list contained only this node, remove the list of nodes with priority `$priority`.
*/
if ([] === $nodes) {
unset($this->queue[$priority]);
} else {
$this->queue[$priority] = $nodes;
}
return $node;
}
/**
* Count the nodes in the queue.
*
* @return int<0, max>
*
* @psalm-mutation-free
*/
public function count(): int
{
$count = 0;
foreach ($this->queue as $list) {
$count += count($list);
}
/** @var int<0, max> */
return $count;
}
}