[PHP data structure] relevant logical operations of queue

Time:2022-5-13

In the logical structure, we have learned a very classic structure type: stack. Today, let’s learn another classic logical structure type: queue. I believe many students have used cache queue tools such as redis and rabbitmq. In fact, database and program code can realize the operation of queue. Just like stack, queue also has its specific rules. As long as it meets this rule, it is called queue.

What is a queue?

Compared with stack, queue is a sequential logical structure of first in first out (FIFO). What is first in, first out? Just like our queue, when we go to the bank or hospital, we always take a number at the door, which is called in order. People who come first can do business or see a doctor first. This is a typical queue. Similarly, daily queuing is a standard queuing mode. If there is someone who cuts in the queue, we can think that it has higher priority if there is a good reason. This is a special form of elements in the queue. For example, we will give priority to pregnant women when waiting for the subway or bus, and there are priority windows for soldiers when waiting in line to buy train tickets. However, this is not within the scope of our discussion this time.

[PHP data structure] relevant logical operations of queue

When queuing at the bus stop, the first person in line can certainly get on the bus first, and then in turn. At this time, you come to the bus stop, so you can only come to the last. This is the concrete form of queue.

Similarly, like stack, there are some nouns we need to understand. When you come to the bus stop and stand last, this operation is called “joining the queue”. When the bus enters the station, the first passenger gets on the bus. This operation is called “out of line”. The position of the first passenger is called “head of the team”. As the last passenger in the current queue, your position is called “tail of the team”. Back to the code logic, that is to say, the queue is from the “tail” to “in” and from “head” to “out”.

Sequential queue

OK, let’s look at the code directly. The first thing we see is the implementation of the sequence team.

class SqQueue{
    public $data;
    public $front;
    public $rear;
}

Since it is a sequential team, we still use an array data to represent the elements in the team. Then define two pointers front and rear to represent the head and tail of the team. Because it is a sequential queue, the pointer here actually saves the subscript of the array. The next operation is actually very simple, “join the team” with rear + +, and “leave the team” with front + +.

function InitSqQueue(){
    $queue = new SqQueue();
    $queue->data = [];
    $queue->front = 0;
    $queue->rear = 0;
    return $queue;
}

function EnSqQueue(SqQueue &$queue, $e){
    $queue->data[$queue->rear] = $e;
    $queue->rear ++;
}

function DeSqQueue(SqQueue &$queue){
    //Queue is empty
    if($queue->front == $queue->rear){
        return false;
    }
    $e = $queue->data[$queue->front];
    $queue->front++;
    return $e;
}

$q = InitSqQueue();
EnSqQueue($q, 'A');
EnSqQueue($q, 'B');
print_r($q);
// SqQueue Object
// (
//     [data] => Array
//         (
//             [0] => A
//             [1] => B
//         )

//     [front] => 0
//     [rear] => 2
// )

Does it feel that after learning stack, the queue is also well understood. When initializing the queue, it is OK to make the pointer at the head and tail of the queue be a record with 0 subscript. When joining the queue, the tail of the queue is added. In this code, we join the queue with two elements, and the printed sequential queue content is shown in the comments.

EnSqQueue($q, 'C');
EnSqQueue($q, 'D');
EnSqQueue($q, 'E');
print_r($q);
// SqQueue Object
// (
//     [data] => Array
//         (
//             [0] => A
//             [1] => B
//             [2] => C
//             [3] => D
//             [4] => E
//         )

//     [front] => 0
//     [rear] => 5
// )

echo DeSqQueue($q), PHP_EOL; // A
echo DeSqQueue($q), PHP_EOL; // B
echo DeSqQueue($q), PHP_EOL; // C
echo DeSqQueue($q), PHP_EOL; // D
echo DeSqQueue($q), PHP_EOL; // E

echo DeSqQueue($q), PHP_EOL; // 

print_r($q);
// SqQueue Object
// (
//     [data] => Array
//         (
//             [0] => A
//             [1] => B
//             [2] => C
//             [3] => D
//             [4] => E 
//         )

//     [front] => 5
//     [rear] => 5
// )

When leaving the team, let the front add 1. However, when leaving the queue, we also need to judge whether all the elements in the array are out of the queue. Here, we only use a very simple judgment condition, that is, whether front and rear are equal to judge whether the queue is empty. You can use a diagram to assist in the understanding of the code.

[PHP data structure] relevant logical operations of queue

Circular queue

I believe many students have seen it. The queue operation only modifies the pointer records of the head and tail of the queue, but the array will increase all the time. In this way, if it increases all the time, this array will occupy the memory, which is certainly not a good queue implementation. In fact, in C language, array is to give a fixed length. The array in PHP is more like a hash structure, so it can grow indefinitely. We don’t need to define a specific array length at the beginning. This is also the convenience of PHP, but what should we do if we don’t want to waste memory space? Just like in C language, we also specify a length for the array in PHP, and use the very classic “circular queue” to solve the storage problem of queue array. As shown in the figure below:

[PHP data structure] relevant logical operations of queue

In fact, within the limited array space, when we reach the maximum value of the array, we will save the new data back to the previous subscript position. For example, we have six elements in the figure. The current team head is in the 2 subscript and the team tail is in the 5 subscript. If we join an element, the end of the team moves to the 6 subscript. If you add another element, the end of the queue will move back to the 0 subscript. If you continue to add, when the end of the queue subscript is equal to the head of the queue subscript minus 1, we think that the queue is full and we can’t add any more elements.

Similarly, during the out of team operation, we also operate the team head element circularly. When the team head element reaches the subscript of 6 and continues to be out of the team, it will also return to the subscript of 0 and continue to be out of the team. When the head and tail of the queue are equal, the current queue can also be determined as an empty queue.

From this, we can see that compared with the ordinary linear queue, the circular queue has one more queue full state. Let’s look at how to judge the full condition of the team directly from the code.

define('MAX_QUEUE_LENGTH', 6);

function EnSqQueueLoop(SqQueue &$queue, $e){
    //Determine whether the queue is full
    if(($queue->rear + 1) % MAX_QUEUE_LENGTH == $queue->front){
        return false;
    }
    $queue->data[$queue->rear] = $e;
    $queue->rear = ($queue->rear + 1) % MAX_ QUEUE_ LENGTH; //  Change to cyclic subscript
}

function DeSqQueueLoop(SqQueue &$queue){
    //Queue is empty
    if($queue->front == $queue->rear){
        return false;
    }
    $e = $queue->data[$queue->front];
    $queue->front = ($queue->front + 1) % MAX_ QUEUE_ LENGTH; //  Change to cyclic subscript
    return $e;
}

$q = InitSqQueue();
EnSqQueueLoop($q, 'A');
EnSqQueueLoop($q, 'B');
EnSqQueueLoop($q, 'C');
EnSqQueueLoop($q, 'D');
EnSqQueueLoop($q, 'E');

EnSqQueueLoop($q, 'F');

print_r($q);
// SqQueue Object
// (
//     [data] => Array
//         (
//             [0] => A
//             [1] => B
//             [2] => C
//             [3] => D
//             [4] => E
//[5] = > // tail
//         )

//     [front] => 0
//     [rear] => 5
// )

echo DeSqQueueLoop($q), PHP_EOL;
echo DeSqQueueLoop($q), PHP_EOL;
print_r($q);
// SqQueue Object
// (
//     [data] => Array
//         (
//             [0] => A
//             [1] => B
//[2] = > C // header
//             [3] => D
//             [4] => E
//[5] = > // tail
//         )

//     [front] => 2
//     [rear] => 5
// )

EnSqQueueLoop($q, 'F');
EnSqQueueLoop($q, 'G');

EnSqQueueLoop($q, 'H');
print_r($q);
// SqQueue Object
// (
//     [data] => Array
//         (
//             [0] => G
//[1] = > b // tail
//[2] = > C // header
//             [3] => D
//             [4] => E
//             [5] => F
//         )

//     [front] => 2
//     [rear] => 1
// )

The subscript movement of outgoing and incoming teams and the judgment of team full are based on (queue – > rear + 1)% max_ QUEUE_ In the form of length. Is it very clever to obtain the current cyclic subscript according to the modulus of queue length. I have to feel the wisdom of my ancestors! Of course, this is also the basic mathematical principle. Therefore, we should review the knowledge related to mathematics when learning data structure!

Chain queue

Are you confused about the sequential queue? It doesn’t matter. The chain structure of the queue is actually simpler than the sequential structure, because it really only needs to operate the pointers at the head and tail of the queue, and other things really don’t need to be considered. And this pointer is really a pointer to a specific object.

class LinkQueueNode{
    public $data;
    public $next;
}

class LinkQueue{
    public $first; //  Queue head pointer
    public $rear; //  Tail pointer
}

Here we need two basic physical structures. One is the node and the other is the queue object. The node object is a normal linked list structure, nothing special. The queue object is even simpler. One attribute is the queue head pointer and the other attribute is the queue tail pointer.

function InitLinkQueue(){
    $node = new LinkQueueNode();
    $node->next = NULL;
    $queue = new LinkQueue();
    $queue->first = $node;
    $queue->rear = $node;
    return $queue;
}

function EnLinkQueue(LinkQueue &$queue, $e){
    $node = new LinkQueueNode();
    $node->data = $e;
    $node->next = NULL;

    $queue->rear->next = $node;
    $queue->rear = $node;
}

function DeLinkQueue(LinkQueue &$queue){
    if($queue->front == $queue->rear){
        return false;
    }

    $node = $queue->first->next;
    $v = $node->data;

    $queue->first->next = $node->next;
    if($queue->rear == $node){
        $queue->rear = $queue->first;
    }

    return $v;
}

$q = InitLinkQueue();
EnLinkQueue($q, 'A');
EnLinkQueue($q, 'B');
EnLinkQueue($q, 'C');
EnLinkQueue($q, 'D');
EnLinkQueue($q, 'E');

print_r($q);
// LinkQueue Object
// (
//     [first] => LinkQueueNode Object
//         (
//             [data] => 
//             [next] => LinkQueueNode Object
//                 (
//                     [data] => A
//                     [next] => LinkQueueNode Object
//                         (
//                             [data] => B
//                             [next] => LinkQueueNode Object
//                                 (
//                                     [data] => C
//                                     [next] => LinkQueueNode Object
//                                         (
//                                             [data] => D
//                                             [next] => LinkQueueNode Object
//                                                 (
//                                                     [data] => E
//                                                     [next] => 
//                                                 )

//                                         )

//                                 )

//                         )

//                 )

//         )

//     [rear] => LinkQueueNode Object
//         (
//             [data] => E
//             [next] => 
//         )

// )

echo DeLinkQueue($q), PHP_EOL; // A
echo DeLinkQueue($q), PHP_EOL; // B

EnLinkQueue($q, 'F');
print_r($q);
// LinkQueue Object
// (
//     [first] => LinkQueueNode Object
//         (
//             [data] => 
//             [next] => LinkQueueNode Object
//                 (
//                     [data] => C
//                     [next] => LinkQueueNode Object
//                         (
//                             [data] => D
//                             [next] => LinkQueueNode Object
//                                 (
//                                     [data] => E
//                                     [next] => LinkQueueNode Object
//                                         (
//                                             [data] => F
//                                             [next] => 
//                                         )

//                                 )

//                         )

//                 )

//         )

//     [rear] => LinkQueueNode Object
//         (
//             [data] => F
//             [next] => 
//         )

// )

The code function and test code for entering and leaving the team are given together. Is it very simple. The initial team head element is still an empty node as the starting node. Then, when joining the team, make the rear equal to the newly created node, and establish a chain relationship in the linked list. When leaving the team, it is also the same. Let first become the next hop node of the current first, that is, first – > next. The condition to judge whether the team is empty is simply whether the pointers at the head and tail of the team are equal. Chain team is actually simpler than sequence team, but similarly, next is easy to make people dizzy. Just write it down. You can still learn from the diagram:

[PHP data structure] relevant logical operations of queue

Array queue operation provided by PHP

Finally, just like the stack, the PHP code also provides us with a function that can be used for queue operation.

$sqQueueList = [];

array_push($sqQueueList, 'a');
array_push($sqQueueList, 'b');
array_push($sqQueueList, 'c');

print_r($sqQueueList);
// Array
// (
//     [0] => a
//     [1] => b
//     [2] => c
// )

array_shift($sqQueueList);
print_r($sqQueueList);
// Array
// (
//     [0] => b
//     [1] => c
// )

array_ The shift () function pops up the first element in the array. Please note that the subscript of the element here also changes. If we unset () the 0 subscript element of the array, the subscripts of B and C will still be 1 and 2. And array_ Shift () rearranges the array so that its subscripts remain in order.

unset($sqQueueList[0]);
print_r($sqQueueList);
// Array
// (
//     [1] => c
// )

summary

We will finish with two articles on the contents of stack queue. But just say not practice fake tricks. Next, let’s have some real dry goods. Use stacks and queues to do problems. If you learn algorithms, you have to brush problems. If you don’t brush them for a day, it’s like three autumn days!!!

Test code:

https://github.com/zhangyue0503/Data-structure-and-algorithm/blob/master/3. Stack and queue / source / 3.2 queue related logical operations php

reference material:

Data structure, Second Edition, Yan Weimin

Data structure, Second Edition, Chen Yue

High score notes on data structure 2020 edition, tianqin postgraduate entrance examination

Each media platform can search [hard core project manager]