Restricted linear list queue (sequential storage and chained storage)

Time:2021-5-4

1. Queue overview

1.1 queue definition

A queue is a linear table that only allows insertion at one end and deletion at the other.
A queue is a first in first out linear tableThe end that can be inserted is called the end of the team, and the end that can be deleted is called the opposite end.
Like stack, queue is also a kind of limited linear table. At the same time, queue has two ways: sequential storage and chain storage.
As shown in the figure below:
Restricted linear list queue (sequential storage and chained storage)

1.2 queue presentation

Restricted linear list queue (sequential storage and chained storage)
In Figure 1.2.1, A1 and A2 join the team respectively. At this time, A1 is the head of the team and A2 is the tail of the team.
Restricted linear list queue (sequential storage and chained storage)
In Figure 1.2.2, A1 is out of the team, A3 and A4 are in the team, at this time, the team head A2 and the team tail A4 are in the team.

2. Queue sequential storage

2.1 definition of sequential storage

Through the previous article, we have learned about sequential storage. Similarly, the queue stores data by opening up a storage unit with continuous addresses.
As shown in the figure below:
Restricted linear list queue (sequential storage and chained storage)
At the same time, we define the pointer front to point to the first element of the queue and the pointer real to point to the next position of the last element.
When there are elements out of the team and in the team, front and rear move backward respectively, as shown in the figure below:
Restricted linear list queue (sequential storage and chained storage)
When font and real point to the same location, it represents an empty queue.

2.2 circular queue

In Figure 2.1.2 above, the maximum number of storage in the queue is 5, because only 5 spaces have been opened up. If you continue to join the queue at the position with the subscript of 4, then the real pointer needs to continue to move backward. At this time, it has exceeded the scope of the space we opened up, resulting in false overflow. However, we can see from the figure that the position with the subscript of 0 is empty and can store data, So we can put the subsequent data in front of the array,We call the end-to-end sequential storage structure of a queue circular queue.
As shown in the figure below:
Restricted linear list queue (sequential storage and chained storage)
Restricted linear list queue (sequential storage and chained storage)
Restricted linear list queue (sequential storage and chained storage)
However, the problem comes again. As can be seen from figure 2.2.3, the font pointer and the real pointer point to the same location. As I said before, we represent an empty queue by pointing to the same location through front and real. How can we judge the full queue in Figure 2.2.3?
Of course, the predecessors have given a specific judgment method, sacrificing a storage unit. When the real and front are only one position apart, they think that the queue is full. Because the real may be larger than the font or smaller than the front, there may be a difference of one circle, so if the maximum size of the queue is queuesize, thenThe condition of full team is (real + 1)% queuesize = = front(the purpose of module selection is to solve the problem that the real is larger than the front.).

For example, the queuesize is 5:
When front = 0, real = 4, (4 + 1)% 5 = 0 (equal to front), the queue is full.
When front = 2, real = 1, (1 + 1)% 5 = 2 (equal to front), the queue is full.
When front = 2, real = 0, (0 + 1)% 5 = 1 (not equal to front), the queue is dissatisfied.

When real > front, as shown in Figure 2.1.2 above, the length of the queue is real – front, but when real < front, as shown in Figure 2.2.2 above, the length of the queue is divided into two segments, one is real – front0 + rearThe other isQueueSize – frontThe queue length formula is obtained by adding them together
(rear – front + QueueSize) % QueueSize

2.3 routine operation of sequential storage queue

2.3.1 initialization and clearing

The basic code is as follows:

#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#Define maxsize 20 / * initial storage allocation*/

typedef int Status;
typedef int QElemType; /*  The qelemtype type depends on the actual situation. Here, it is assumed to be int*/

/*Sequential storage structure of circular queue*/
typedef struct
{
    QElemType data[MAXSIZE];
    int front;       /*  Head pointer*/
    int rear;        /*  Tail pointer. If the queue is not empty, it points to the next position of the tail element of the queue*/
}SqQueue;

initialization:
When creating the queue object Q, we have opened up a space with continuous addresses, so we only need to set the initial value when initializing.

//Initialize an empty queue Q
Status InitQueue(SqQueue *Q){
    Q->front = 0;
    Q->rear = 0;
    return OK;
}

Empty:

//Clear the queue
Status ClearQueue(SqQueue *Q){
    Q->front = Q->rear = 0;
    return OK;
}

2.3.2 judgment of empty queue and full queue

Empty queue:
As we already know, the condition for judging an empty queue is that the front pointer and the real pointer point to the same location.

//If the queue q is empty, trur is returned; otherwise, false is returned;
Status QueueEmpty(SqQueue Q){
    return Q.front == Q.rear;
}

Full queue:
As we know above, the condition for judging a full queue is: (real + 1)% queuesize = = front

Status QueueFull(SqQueue Q) {
    return (Q.rear+1)%MAXSIZE == Q.front;
}

2.3.3 entry and exit

Team entry:
When the queue is full, the element E is inserted at the end of the queue.

//If the queue is not full, insert element E as the new tail element
Status EnQueue(SqQueue *Q,QElemType e){
    //Queue full
    if(QueueFull(*Q))
        return ERROR;
    //Assign the element E to the end of the team
    Q->data[Q->rear] = e;
    //The real pointer moves backward one bit, and if it reaches the end, it turns to the head of the array;
    Q->rear = (Q->rear+1)%MAXSIZE;
    return OK;
}

Out of the team:
When the queue is not empty, delete the header element.

//If the queue is not empty, delete the element of the queue head in Q and return the value with E
Status DeQueue(SqQueue *Q,QElemType *e){
    //Determine whether the queue is empty
    if (QueueEmpty(*Q))
        return ERROR;
    //Assign the team head element to E
    *e = Q->data[Q->front];
    //The front pointer moves backward one bit, and if it reaches the end, it goes to the head of the array
    Q->front = (Q->front+1)%MAXSIZE;
    return OK;
}

2.3.4 other operations

1. Get the team head element:

//If the queue is not empty, use e to return the queue head element of Q, and return OK, otherwise return error;
Status GetHead(SqQueue Q,QElemType *e){
    //The queue is empty
    if (QueueEmpty(Q))
        return ERROR;
    *e = Q.data[Q.front];
    return OK;
}

2. Get the number of queue elements:

//Returns the number of elements of Q, that is, the current length of the queue
int QueueLength(SqQueue Q){
    return (Q.rear - Q.front + MAXSIZE)%MAXSIZE;
}

3. Queue traversal

//From the head of the queue to the tail of the queue, array each element in turn
Status QueueTraverse(SqQueue Q){
    int i;
    i = Q.front;
    while (i != Q.rear) {
        Printf ("element: D, location: D, n", q.data [i], I) ";
        i = (i+1)%MAXSIZE;
    }
    printf("\n");
    return OK;
}

3. Queue chain storage

3.1 definition of chain storage

The chained storage structure of a queue is actually a single chained list of a linear table, but it can only be tail in and head out. We call it chained queue for short.
Restricted linear list queue (sequential storage and chained storage)
In order to facilitate the operation, we usually add the head node.
We point the front pointer to the head node and the rear pointer to the last node.
When both front and rear point to the head node, the queue is considered empty, as shown in the following figure:
Restricted linear list queue (sequential storage and chained storage)

3.2 routine operation of chain storage queue

The basic code is as follows:

#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0

typedef int Status;
typedef int QElemType; /*  The qelemtype type depends on the actual situation. Here, it is assumed to be int*/

Typedef struct qnode / * node structure*/
{
    QElemType data;
    struct QNode *next;
}QNode,*QueuePtr;

Linked list structure of typedef struct / * queue*/
{
    QueuePtr front,rear; /*  Pointer of team head and tail*/
}LinkQueue;

3.2.1 initialization and destruction

initialization:
During initialization, we need to open up space, create a head node, and point the front pointer and the real pointer to the head node.

/*Initialize queue*/
Status InitQueue(LinkQueue *Q){
    //1. The head / tail pointer points to the newly generated node
    Q->front = Q->rear = (QueuePtr)malloc(sizeof(QNode));
    //2. Judge whether the new node is created successfully or not
    if (!Q->front) {
        return ERROR;
    }
    //3. Null the pointer field of the head node
    Q->front->next = NULL;
    return OK;
}

Destruction:
When destroying, we need to release all nodes through a loop.

/*Destroy queue Q*/
Status DestoryQueue(LinkQueue *Q){
    //Traverse the entire queue, destroy each node of the queue
    while (Q->front) {
        Q->rear = Q->front->next;
        free(Q->front);
        Q->front = Q->rear;
    }
    return OK;
}

3.2.2 empty queue judgment

To judge whether the queue is empty, we only need to judge whether the front pointer and the real pointer point to the same location.

/*Judge whether the queue q is empty*/
Status QueueEmpty(LinkQueue Q){
    return Q.front == Q.rear;
}

3.2.3 entry and exit

Team entry:
When joining the queue, you need to create a new node, and then insert the new node into the tail of the list.

/*Insert element E as the new element of queue Q*/
Status EnQueue(LinkQueue *Q,QElemType e){
    //The node space is allocated for the queue elements, and the pointer s is used to point to it;
    QueuePtr s = (QueuePtr)malloc(sizeof(QNode));
    //Judge whether the allocation is successful
    if (!s) {
         return ERROR;
    }
    //Assign the new node s to the data field
    s->data = e;
    s->next = NULL;
    //Insert the new node to the end of the team
    Q->rear->next = s;
    //Modify end of line pointer
    Q->rear = s;
    return OK;
}

Out of the team:
When leaving the team, you need to release the first element node (the direct successor node of the head node). If the head node of the team is the tail node of the team, you can delete it and point the real to the head node.

/*Out of line*/
Status DeQueue(LinkQueue *Q,QElemType *e){
    QueuePtr p;
    //Judge whether the queue is empty or not;
    if (Q->front == Q->rear) {
        return ERROR;
    }
    //Temporarily store the team head node to be deleted in P
    p = Q->front->next;
    //Assign the value of the team head node to E
    *e = p->data;
    //Assign the successor p - > next of the original queue head node to the successor of the head node
    Q->front->next = p ->next;
    //If the team head is the team tail, delete and point the real to the head node
    if(Q->rear == p) Q->rear = Q->front;
    free(p);
    return OK;
}

3.2.4 other operations

1. Get the team head element:

/*Get team head element*/
Status GetHead(LinkQueue Q,QElemType *e){
   
    //Queue not empty
    if (Q.front != Q.rear) {
        //Returns the value of the team head element, and the team head pointer remains unchanged
        *e =  Q.front->next->data;
        return TRUE;
    }
    return  FALSE;
}

2. Get the number of queue elements:

/*Get queue length*/
int QueueLength(LinkQueue Q){
    int i= 0;
    QueuePtr p;
    p = Q.front;
    while (Q.rear != p) {
        i++;
        p = p->next;
    }
    return i;
}

3. Queue traversal

/*Traversing the queue*/
Status QueueTraverse(LinkQueue Q){
    QueuePtr p;
    p = Q.front->next;
    while (p) {
        printf("%d ",p->data);
        p = p->next;
    }
    printf("\n");
    return OK;
}

4. Summary

For the comparison between circular queue and chain queue, we can consider the following two aspects:
① In terms of time, the time of their basic operation is O (1), but the circular queue applies for space in advance and does not release it during use, while the chain queue also has some time overhead for each application and release. If the queue is frequently joined and exited, there are subtle differences between the two.
② In space, the circular queue has a fixed length, so there will be a waste of storage space. The chain queue does not have this problem, although it needs a pointer field and has some overhead, it is acceptable. So in space, the chain queue is more flexible.

In short, when the maximum length of queue can be determined, circular queue is recommended. If the length cannot be determined, chain queue is used.

Recommended Today

Large scale distributed storage system: Principle Analysis and architecture practice.pdf

Focus on “Java back end technology stack” Reply to “interview” for full interview information Distributed storage system, which stores data in multiple independent devices. Traditional network storage system uses centralized storage server to store all data. Storage server becomes the bottleneck of system performance and the focus of reliability and security, which can not meet […]