# Chained storage of queues --- October 27, 2021

Basic concept of queue - 2021.10.8

### Chained storage of queues:

What is the chained storage of queues? We all know the structural characteristics of the queue in the previous lecture. Can we realize the stack through the linked list, so as to realize the chain storage of the queue.

Then let's discuss it together!!!

Now let's think about a question: is the head node of the linked list the head of the team or the tail of the team?

Because the queue has the feature of first in first out, we always go out from the head of the queue and in from the tail of the queue when inserting and deleting data. Therefore, the head node of the linked list is more appropriate as the team head.

Next, start to analyze the code:

```//Initialize queue
{
struct LQueue * myQueue = malloc(sizeof(struct LQueue));
if (myQueue == NULL)
{
return NULL;
}

myQueue->m_Size = 0;
myQueue->pTail = &myQueue->pHeader; //The tail node starts to point to the head node
return myQueue;
}
//Join the team
{
//Equivalent to tail interpolation
if (queue == NULL)
{
return;
}
if (data == NULL)
{
return;
}

struct LQueue * myQueue = queue;
struct QueueNode * myNode = data;

//Change pointer pointing
myQueue->pTail->next = myNode;
myNode->next = NULL;
//Update tail node
myQueue->pTail = myNode;

//Update queue size
myQueue->m_Size++;
}
//Out of the team
{

if (queue == NULL)
{
return;
}
struct LQueue * myQueue = queue;

if (myQueue->m_Size == 0)
{
return;
}

if (myQueue->m_Size == 1)
{
myQueue->pTail = &myQueue->pHeader; //Maintain tail node pointer
myQueue->m_Size = 0;
return;
}

//Record the first node
struct QueueNode * pFirst = myQueue->pHeader.next;
//Update queue size
myQueue->m_Size--;
}
{
if (queue == NULL)
{
return NULL;
}
struct LQueue * myQueue = queue;

}
//Back to the end of the team
{
if (queue == NULL)
{
return NULL;
}
struct LQueue * myQueue = queue;

return myQueue->pTail;
}
//Returns the queue size
{
if (queue == NULL)
{
return -1;
}
struct LQueue * myQueue = queue;

return myQueue->m_Size;
}
//Determine whether the queue is empty
{
if (queue == NULL)
{
return -1;
}
struct LQueue * myQueue = queue;

if (myQueue->m_Size == 0)
{
return 1;
}

return 0;
}
//Destroy queue
{
if (queue == NULL)
{
return;
}
free(queue);
queue = NULL;
}
```

Let's analyze the above codes sentence by sentence as usual:

```LinkQueue init_LinkQueue()
{
struct LQueue * myQueue = malloc(sizeof(struct LQueue));
if (myQueue == NULL)
{
return NULL;
}

myQueue->m_Size = 0;
myQueue->pTail = &myQueue->pHeader; //The tail node starts to point to the head node
return myQueue;
}
```

The code is explained as follows:
First, we need to apply for a memory space in the heap to store the queue, that is, malloc function. LQueue is the queue structure defined by ourselves. The member variables include the header node, the size of the queue, and the pointer to the record tail node (because we need to perform tail insertion when inserting data). The specific code is as follows:

```struct LQueue
{
int m_Size; //Size of queue
struct QueueNode * pTail; //Pointer to the record tail node
};
```

After the queue is successfully established, in order to be rigorous in the algorithm, it is necessary to judge whether the memory space is successfully applied. If it is empty, the function will exit automatically and will not proceed further.
Since this function is an initialization queue function, you need to set the size of the queue to 0, set the pointer field of the head node to null, and set the pointer field of the tail node as the above code

```The queue is the head node because the queue has only one head node at this time.

{
//Equivalent to tail interpolation
if (queue == NULL)
{
return;
}
if (data == NULL)
{
return;
}

struct LQueue * myQueue = queue;
struct QueueNode * myNode = data;

//Change pointer pointing
myQueue->pTail->next = myNode;
myNode->next = NULL;
//Update tail node
myQueue->pTail = myNode;

//Update queue size
myQueue->m_Size++;
}
```

The code is explained as follows:
After successfully initializing the queue, we need to queue up, that is, insert data into the queue.

First of all, in order to make the algorithm rigorous, we need to verify the entry parameters passed into the function. If it is empty, the function will exit automatically. If it is not empty, it will continue to execute.

Similarly, we need to create a pointer to the entry parameter queue and a pointer to the data to be inserted.

Then we need to change the pointer to make the data insert successfully. The initialized queue has only one head node, and the tail node points to the head node, so we need to point the pointer field of the tail node to the data to be inserted, that is, myNode. Then point the pointer field of myNode to null. Then, the tail node still refers to the state of the head node, so we need to update the pointing of the tail node and point the pointing of the tail node to myNode. At this time, there is a head node and an inserted new node in the queue.

Finally, the queue size can be accumulated.

```void pop_LinkQueue(LinkQueue queue)
{

if (queue == NULL)
{
return;
}
struct LQueue * myQueue = queue;

if (myQueue->m_Size == 0)
{
return;
}

if (myQueue->m_Size == 1)
{
myQueue->pTail = &myQueue->pHeader; //Maintain tail node pointer
myQueue->m_Size = 0;
return;
}

//Record the first node
struct QueueNode * pFirst = myQueue->pHeader.next;

//Update queue size
myQueue->m_Size--;

}
```

The code is explained as follows:
In the same way as joining a queue, leaving a queue deletes the header of the nodes in the queue.

First of all, in order to make the algorithm rigorous, we need to verify the entry parameters passed into the function. If it is empty, the function will exit automatically. If it is not empty, it will continue to execute.

At the same time, we also need to consider a situation, that is, if the size in the queue is 0, we don't need to exit the queue, just exit in advance.

What if there is only one header node and one node in the queue? We need to deal with this situation in particular, that is, we need to point the pointer field of the head node to null, then point the tail node to the head node, and update the size of the queue.

If the number of nodes in the queue is greater than one, we need to save the position of the first node in advance by creating a pointer pFirst, because we need to delete the header. Then, re point the pointer field of the head node to the pointer field of pFirst, that is, the first node is deleted.

Then update the queue size.

```void * front_LinkQueue(LinkQueue queue)
{
if (queue == NULL)
{
return NULL;
}
struct LQueue * myQueue = queue;

}
```

The code is explained as follows:
First of all, in order to make the algorithm rigorous, we need to verify the entry parameters passed into the function. If it is empty, the function will exit automatically. If it is not empty, it will continue to execute.

A pointer is used to successfully access the incoming structure member.

This function is used to return the queue head, that is, to return the pointer field of the node.

```void * back_LinkQueue(LinkQueue queue)
{
if (queue == NULL)
{
return NULL;
}
struct LQueue * myQueue = queue;

return myQueue->pTail;

}
```

The code is explained as follows:
First of all, in order to make the algorithm rigorous, we need to verify the entry parameters passed into the function. If it is empty, the function will exit automatically. If it is not empty, it will continue to execute.

A pointer is used to successfully access the incoming structure member.

This function is used to return the end of the queue, that is, to return the structure member.

```int size_LinkQueue(LinkQueue queue)
{
if (queue == NULL)
{
return -1;
}
struct LQueue * myQueue = queue;

return myQueue->m_Size;

}
```

The code is explained as follows:
First of all, in order to make the algorithm rigorous, we need to verify the entry parameters passed into the function. If it is empty, the function will exit automatically. If it is not empty, it will continue to execute.

A pointer is used to successfully access the incoming structure member.

This function is used to return the queue size, that is, to return the structure member.

```int isEmpty_LinkQueue(LinkQueue queue)
{
if (queue == NULL)
{
return -1;
}
struct LQueue * myQueue = queue;

if (myQueue->m_Size == 0)
{
return 1;
}

return 0;
}
```

The code is explained as follows:
First of all, in order to make the algorithm rigorous, we need to verify the entry parameters passed into the function. If it is empty, the function will exit automatically. If it is not empty, it will continue to execute.

A pointer is used to successfully access the incoming structure member.

If the queue size is 0, it returns 1. If none of the above conditions are met, it returns 0.

```void destroy_LinkQueue(LinkQueue queue)
{
if (queue == NULL)
{
return;
}
free(queue);
queue = NULL;
}
```

The code is explained as follows:
First of all, in order to make the algorithm rigorous, we need to verify the entry parameters passed into the function. If it is empty, the function will exit automatically. If it is not empty, it will continue to execute.

Release the created memory space, and then empty it.