Sequential queue, cyclic queue, chain queue

Posted by pacholo on Sat, 04 Sep 2021 04:38:24 +0200

preface

Queue is a "first in, first out" linear table. Only one end of the table is inserted (queued), which is called the end of the queue, and the other end is deleted (queued), which is called the opposite end, that is, "tail in, head out". Only the head and tail of the queue can be accessed by the outside world, so it cannot be traversed. All kinds of queues in life are queues.
There are two storage methods for queues: sequential storage and chain storage. The queue with sequential storage is called sequential queue, and the queue with chain storage is called chain queue.

1, Sequential queue

The sequential queue stores the elements in the queue in the form of array, and uses the head pointer (front) and tail pointer (rear) to point to the opposite head and tail of the queue respectively.

/*----------------------------------------------------------------
Set up a team head pointer front and a team tail pointer rear to point to the team head and team tail elements respectively.

 ◆ Initialization: front=rear=0.
 ◆ The queue is empty: front==rear.
 ◆ Queue full: rear==MaxSize.
 ◆ Join the queue: insert the new element into the position indicated by the rear, and then add 1 to the rear.
 ◆ Out of line: delete the element indicated by front, then add 1 and return the deleted element.
 ◆ Take the first element of the queue: return the element value pointed to by the front
 -----------------------------------------------------------------*/

#include <stdio.h>
#include <assert.h>
#include <Windows.h>

#define MaxSize 10 / / the maximum capacity of the queue

typedef int DataType;  //Element type in queue
typedef struct Queue
{
    DataType Queue[MaxSize];
    int fornt;   //Queue head pointer
    int rear;    //Tail pointer
}SeqQueue;

//Queue initialization: initializes the queue as an empty queue
void InitQueue(SeqQueue *SQ)
{
    SQ->fornt = SQ->rear = 0;  //Set the head and tail pointers to 0 at the same time
 }

//Judge that the queue is empty

int IsEmpty(SeqQueue* SQ)
{
    if (SQ->fornt == SQ->rear)
    {
        return 1;
    }
    return 0;
}

//Determine whether the queue is full

int IsFull(SeqQueue* SQ)
{
    if (SQ->rear == MaxSize) 
    {
        return 1;
    }
    return 0;
}

//Join the queue and insert the element data into the queue SQ

void EnterQueue(SeqQueue* SQ,DataType data)
{
    if (IsFull(SQ))
    {
        printf("The queue is full\n");
        return 0;
    }
    SQ->Queue[SQ->rear] = data;  //Insert element data at the end of the queue
    SQ->rear = SQ->rear + 1;     //The tail pointer moves back one bit
}

//Out of the queue, the element data of the queue head in the queue is out of the queue. After out of the queue, the queue head pointer front is moved back one bit
int DeleteQueue(SeqQueue* SQ,DataType* data)
{
    if (IsEmpty(SQ))
    {
        printf("The queue is empty!\n");
        return 0;
    }
    *data = SQ->Queue[SQ->fornt];   //Out of queue element value
    SQ->fornt = (SQ->fornt)+1;      //The tail pointer moves back one bit
    return 1;
}

//Get team leader element

int GetHead(SeqQueue* SQ,DataType* data)
{
    if (IsEmpty(SQ))
    {
        printf("Queue is empty!\n");
    }
    return *data = SQ->Queue[SQ->fornt];  
}

//Empty queue

void ClearQueue(SeqQueue* SQ)
{
    SQ->fornt = SQ->rear = 0;
}

//And elements in the print queue

void PrintQueue(SeqQueue* SQ)
{
    assert(SQ);    
    int i = SQ->fornt;
    while(i<SQ->rear)
    {
        printf("%-3d", SQ->Queue[i]);
        i++;
    }
    printf("\n");
}
int main()
{
    SeqQueue SQ;
    DataType data;
    //Initialize queue
    InitQueue(&SQ);
    //Join the team
    EnterQueue(&SQ, 1);
    EnterQueue(&SQ, 2);
    EnterQueue(&SQ, 3);
    EnterQueue(&SQ, 4);
    EnterQueue(&SQ, 5);
    EnterQueue(&SQ, 6);
    EnterQueue(&SQ, 8);
    EnterQueue(&SQ, 10);
    EnterQueue(&SQ, 12);
    EnterQueue(&SQ, 15);
    EnterQueue(&SQ, 16);

    //Print elements in queue
    printf("The elements in the queue are:");
    PrintQueue(&SQ);
    printf("\n");
    //Out of the team
    DeleteQueue(&SQ, &data);
    printf("The outgoing elements are:%d\n", data);
    printf("\n");
    DeleteQueue(&SQ, &data);
    printf("The outgoing elements are:%d\n", data);
    printf("\n");
    printf("The elements in the queue are:");
    PrintQueue(&SQ);
    printf("\n");
    //Get team leader element
    data = GetHead(&SQ, &data);
    printf("The team head elements are:%d\n", data);
    printf("#Element 16 join the team#\n");
    //Element 16 join the team
    EnterQueue(&SQ, 16);
    printf("The elements in the queue are:");
    PrintQueue(&SQ);
    printf("\n");
    system("pause");
    return 0;
}

The result is:

It can be seen from the above that once the queue is full, even if there are elements out of the queue, the queue entry operation cannot be carried out because of the "false overflow" of the sequential queue. The appearance of false overflow is that the queue head will generate new space due to out of queue operation, but the queue tail pointer has reached the end of the array, and the queue tail pointer will cross the upper bound of the array, resulting in overflow. This overflow is not caused by insufficient storage space, but by multiple inserts and deletions. Such operations with storage space but unable to insert elements are called "false overflow". As shown in the figure below:

2, Circular queue

In order to make full use of the storage space and eliminate the "false overflow phenomenon", the space allocated by the queue can be regarded as a ring connected end to end. Such a queue is called a circular queue.
When the loop queue is empty or full, the head pointer and tail pointer point to the same position, that is, front = =rear; To distinguish between these two situations, you can:

  1. Set a flag to distinguish the empty and full teams.
  2. Set a counter to count the elements. If it is 0, it means the team is empty, and if it is equal to the team capacity, it means the team is full.
  3. Use less one element space. The rear of the queue tail points to the last element of the actual queue tail.
    Team empty: front == rear;
    Team full: (rear + 1)% MaxSize = = front.

/*----------------------------------------------------------------
Set up a team head pointer front and a team tail pointer rear to point to the team head and team tail elements respectively.

◆ Initialization: front=rear=0.
◆ The queue is empty: front==rear.
◆ Team full: (rear + 1)% maxsize = = font
◆ Join the queue: insert the new element into the position indicated by the rear, and then add 1 to the rear.
◆ Out of line: delete the element indicated by front, then add 1 and return the deleted element.
◆ Get the first element of the queue: return the element value pointed to by the font
-----------------------------------------------------------------*/

#include<stdio.h>
#include<Windows.h>
#include<assert.h>

#define MaxSize 10
typedef int DataType;
typedef struct SeqQueue
{
    DataType Queue[MaxSize];
    int fornt;
    int rear;
}SeqCirQueue;

//Queue initialization

void InitSeqCirQueue(SeqCirQueue* SCQ)
{
    SCQ->fornt = SCQ->rear = 0;
}

//Determine whether the queue is empty
int IsEmpty(SeqCirQueue* SCQ)
{
    if (SCQ->fornt == SCQ->rear)
    {
        return 1;
    }
    return 0;
}

//Determine whether the queue is full
int IsFull(SeqCirQueue* SCQ)
{
    //The tail pointer + 1 catches up with the head pointer, indicating that the queue is full
    if ((SCQ->rear + 1) % MaxSize == SCQ->fornt)
    {
        return 1;
    }
    return 0;
}

//Queue operation
int EnterSeqCirQueue(SeqCirQueue* SCQ, DataType data)
{
    if (IsFull(SCQ))
    {
        printf("The queue is full, cannot join!\n");
        return 0;
    }
    SCQ->Queue[SCQ->rear] = data;
    SCQ->rear = (SCQ->rear + 1) % MaxSize;   //Reset tail pointer
}


//Out of line operation
int DeleteSeqCirQueue(SeqCirQueue* SCQ,DataType* data)
{
    if (IsEmpty(SCQ))
    {
        printf("The queue is empty!\n");
        return 0;
    }
    *data = SCQ->Queue[SCQ->fornt];
    SCQ->fornt = (SCQ->fornt + 1) % MaxSize;  //Reset queue head pointer
}

//Take the first element of the team
int GetHead(SeqCirQueue* SCQ,DataType* data)
{
    if (IsEmpty(SCQ))
    {
        printf("The queue is empty!\n");
        return 0;
    }
    *data = SCQ->Queue[SCQ->fornt];
    return *data;
}

//Empty queue
void ClearSeqCirQueue(SeqCirQueue* SCQ)
{
    SCQ->fornt = SCQ->rear = 0;
}

//Print queue elements
void PrintSeqCirQueue(SeqCirQueue* SCQ)
{
    assert(SCQ);   //Assert that SCQ is not empty
    int i = SCQ->fornt;
    if (SCQ->fornt < SCQ->rear)
    {
        for (; i < SCQ->rear; i++)
        {
            printf("%-3d", SCQ->Queue[i]);
        }
    }
    else
    {
        for (i; i <SCQ->rear + MaxSize; i++)
        {
            printf("%-3d", SCQ->Queue[i]);
        }
    }
    printf("\n");
}

int main()
{
    SeqCirQueue SCQ;
    DataType data;
    //Initialize queue
    InitSeqCirQueue(&SCQ);
    //Join the team
    EnterSeqCirQueue(&SCQ, 1);
    EnterSeqCirQueue(&SCQ, 2);
    EnterSeqCirQueue(&SCQ, 4);
    EnterSeqCirQueue(&SCQ, 6);
    EnterSeqCirQueue(&SCQ, 8);
    EnterSeqCirQueue(&SCQ, 9);
    EnterSeqCirQueue(&SCQ, 10);
    EnterSeqCirQueue(&SCQ, 12);
    EnterSeqCirQueue(&SCQ, 13);
    printf("The elements in the queue are:\n");
    //Print elements in queue
    PrintSeqCirQueue(&SCQ);
    EnterSeqCirQueue(&SCQ, 15);
    //Out of the team
    DeleteSeqCirQueue(&SCQ, &data);
    printf("The outgoing elements are:%d\n", data);
    printf("\n");
    printf("The elements in the queue are:\n");
    PrintSeqCirQueue(&SCQ);
    printf("15 Join the team:\n");
    EnterSeqCirQueue(&SCQ, 15);
    printf("The elements in the queue are:\n");
    PrintSeqCirQueue(&SCQ);
    system("pause");
    return 0;
}

The result is: after the element leaves the team, it will release space, and new elements can join the team, eliminating the "false overflow phenomenon".

3, Chain queue

Chained queue is a single linked list that restricts deletion and insertion only in the header and at the end of the table. The operation of chained queue is actually a single linked list operation, except that outgoing queue is carried out in the header and incoming queue is carried out at the end of the table. Refer to the following figure for queue entry and exit of chain queue:

#include <stdio.h>
#include <Windows.h>
#include <stdlib.h>
#include <assert.h>

typedef int DataType;
typedef struct Node
{
    DataType _data;
    struct Node* _next;
}LinkQueueNode;

typedef struct
{
    LinkQueueNode* front;
    LinkQueueNode* rear;
}LinkQueue;

//Initialize queue
void InitLinkQueue(LinkQueue* LQ)
{
    //Create a header node
    LinkQueueNode* pHead = (LinkQueueNode*)malloc(sizeof(LinkQueueNode));
    assert(pHead);
    LQ->front = LQ->rear = pHead; //The head and tail of the team point to the head node
    LQ->front->_next = NULL;
}

//Determine whether the queue is empty

int IsEmpty(LinkQueue* LQ)
{
    if (LQ->front->_next == NULL)
    {
        return 1;
    }
    return 0;
}

//Queue operation

void EnterLinkQueue(LinkQueue* LQ, DataType data)
{
    //Create a new node
    LinkQueueNode* pNewNode = (LinkQueueNode*)malloc(sizeof(LinkQueueNode));
    assert(pNewNode);
    pNewNode->_data = data;  //Assign the data element to the data field of the node
    pNewNode->_next = NULL;  //Set the pointer field of the node to null
    LQ->rear->_next = pNewNode;   //Point the tail pointer of the original queue to the new node
    LQ->rear = pNewNode;      //Point the end of the queue pointer to the new node
}

//Out of line operation

void DeleteLinkQueue(LinkQueue* LQ,DataType* data)
{
    if (IsEmpty(LQ))
    {
        printf("The queue is empty!\n");
        return;
    }
    //pDel points to the queue head element. Since the queue head pointer front points to the header node, pDel points to the next node of the header node
    LinkQueueNode* pDel = LQ->front->_next;  
    *data = pDel->_data;   //Assign the element to be dequeued to data
    LQ->front->_next = pDel->_next;  //Make the pointer to the head node point to the next node of pDel
    //If there is only one element in the queue, empty the queue
    if (LQ->rear = pDel)   
    {
        LQ->rear = LQ->front;
    }
    free(pDel);   //Free the space pointed to by pDel
}

//Take team header element

int GetHead(LinkQueue* LQ, DataType* data)
{
    if (IsEmpty(LQ))
    {
        printf("Queue is empty!\n");
        return 0;
    }
    LinkQueueNode* pCur;
    pCur = LQ->front->_next;  //pCur points to the first element of the queue, that is, the next node of the header node
    *data = pCur->_data;      //Assign the team head element value to data
    return *data;             //Returns the value of the queue header element
}

//Empty queue

void ClearQueue(LinkQueue* LQ)
{
    while (LQ->front != NULL)
    {
        LQ->rear = LQ->front->_next;  //The tail pointer points to the next node of the head pointer
        free(LQ->front);              //Release the node pointed to by the queue head pointer
        LQ->front = LQ->rear;         //The head pointer points to the tail pointer
    }
}

//Print elements in queue

void PrintLinkQueue(LinkQueue* LQ)
{
    assert(LQ);
    LinkQueueNode * pCur;
    pCur = LQ->front->_next;
    while (pCur)
    {
        printf("%-3d", pCur->_data);
        pCur = pCur->_next;
    }
    printf("\n");
}
int main()
{
    LinkQueue LQ; 
    DataType data;
    //Initialize queue
    InitLinkQueue(&LQ);
    //Join the team
    EnterLinkQueue(&LQ, 1); 
    EnterLinkQueue(&LQ, 2);
    EnterLinkQueue(&LQ, 3);
    EnterLinkQueue(&LQ, 4);
    EnterLinkQueue(&LQ, 5);
    EnterLinkQueue(&LQ, 6);
    EnterLinkQueue(&LQ, 7);
    EnterLinkQueue(&LQ, 8);
    printf("The elements in the queue are:");
    //Print elements in queue
    PrintLinkQueue(&LQ);
    printf("\n");
    //Take team header element
    data = GetHead(&LQ, &data);
    printf("The team head elements are:%d\n", data);
    printf("\n");
    //Out of the team
    DeleteLinkQueue(&LQ, &data);
    printf("The out of queue elements are:%d\n", data);
    printf("\n");
    printf("The elements in the queue are:");
    PrintLinkQueue(&LQ);
    printf("\n");
    data = GetHead(&LQ, &data);
    printf("The team head elements are:%d\n", data);
    printf("\n");
    ClearQueue(&LQ);
    system("pause");
    return 0;
}

The result is:

4, queue container

Constructor:
queue<T> que;  //Implemented by template class
queue(const queue &que);  //copy constructor 

Assignment operation:
queue & operator= (const queue &que); //Overload = operator

Data access:
 push(elem);  //Join the team and add elements to the end of the team
 pop();  //Remove the first element of the team head
 back();  //Returns the last element
 front();  //Returns the first element

Size operation:
empty();  //Determine whether the queue is empty
size();  //Returns the size of the stack
#include <iostream>
#include <queue>
#include <string>

using namespace std;

//queue
class Person
{
public:
	Person(string name, int age)
	{
		this->m_Name = name;
		this->m_Age = age;
	}
	string m_Name;
	int m_Age;
};

void test()
{
	//Create queue
	queue<Person> q;

	//Prepare data
	Person p1("coco",10);
	Person p2("dudu", 20);
	Person p3("kiki", 18);
	Person p4("vivi", 24);

	//Join the team
	q.push(p1);
	q.push(p2);
	q.push(p3);
	q.push(p4);

	cout << "Queue size:" << q.size() << endl;

	//As long as the queue is not empty, view the team head element and the team tail element to exit the queue
	while (!q.empty())
	{
		//View team header elements
		cout << "Team leader element - name:" << q.front().m_Name << "  Age:" << q.front().m_Age << endl;

		//View tail element
		cout << "Tail element - name:" << q.back().m_Name << "  Age:" << q.back().m_Age << endl;

		//Out of the team
		q.pop();

		cout << "Queue size:" << q.size() << endl;
	}
}

int main()
{
	test();

	return 0;
}

The result is:

Topics: C data structure