10-Data structure-queue (C language)

queue


Table of contents

Table of contents

queue

1. Basic knowledge of queues

2. Basic operations of queues

1.Sequential storage

​Edit (1) Sequential storage

(2) Initialization and empty queue full

(3) Join the team

(4) out of the team

(5) Print queue

(6) Circular queue

(7) The total code that can be run in sequence:

2. Chain storage

(1) Chained storage definition

(2)Initialization

(3)Join the team

(4)Leave the team

(5)Print chain team

(6)The chain team can run the total code

3. Double-ended queue.

(1) Input-restricted double-ended queue

(2) Output-restricted double-ended queue

(3) Example questions:

1. Basic knowledge of queues

        Introduction: A queue is a first-in-first-out (FIFO) linear data structure. It is a data structure that only allows insertion operations at one end (the tail of the queue) and deletion operations at the other end (the head of the queue) . The insertion operation is performed at the end of the queue, and the deletion operation is performed at the front end of the queue, that is, the head element is dequeued first, and the elements inserted later are queued at the end of the queue.

        Queue is a data structure widely used in the field of computer science. It is often used to implement message queues, task queues, buffer queues, etc. In algorithm design, queues can be used for breadth-first search (BFS), simulating bank queuing and other problems. At the same time, queues are often used in conjunction with stack structures to implement more complex algorithms and data structures.

2. Basic operations of queues

        Introduction: Queues can be implemented using arrays or linked lists. Common queue operations include: enqueue (enqueue), dequeue (dequeue), get the head element (front), get the queue length (size), etc.

1.Sequential storage

        Introduction: It is a structure composed of an array + two variables that mark the head and tail of the team.

        Advantages: Simple and easy to implement.

        Disadvantages: It is prone to false overflow problems (when the queue is not full, elements are inserted into the end of the queue but the queue is full because the queue head pointer has not moved).

 (1) Sequential storage

        That is, a one-dimensional array and two variables that mark the head and tail of the queue.

code show as below:

//队列的顺序存储
#define MaxSize 10
typedef struct
{
	int data[MaxSize];
	int front;
	int rear;
  //  int count;//记录队列中实际元素个数,可先不用
}SqQueue; 

(2) Initialization and empty queue full

        initialization:

//队列初始化
void QueueInit(SqQueue *q)
{
	q->front=0;	
	q->rear=0;
} 

        Team Empty: When the head of the team and the tail of the team are equal, it will be empty. It's not just limited to both being equal to 0, because both variables are changing all the time.

//队空
bool QueueEmpty(SqQueue *q) 
{
	if(q->front == q->rear )
	return true;
}

        Full team: It is not easy to judge that the team is full . When rear>MaxSize-1, there will be a false overflow phenomenon, which is not considered full. However, you can add a timer to the structure when the queue is stored sequentially. It will be dequeued once, count--, and once added to the queue, count++. When count==MaxSize-1, the queue is full.

typedef struct
{
	int data[MaxSize];
	int front;
	int rear;
	int count;
}SqQueue; 
//队满
bool QueueFull(SqQueue *q)
{
	if(q->count == MaxSize-1)
	return true;
}

(3) Join the team

        Traverse from the rear of the queue and assign values ​​into the queue. Then rear+1 and retreat.

//入队
void EnQueue(SqQueue *q,int x)
{
	//如果队满了 
	if(QueueFull(q)==false)
		exit(-1);
	//给队尾处赋值 
	q->data[q->rear]=x;
	//移动队尾标记 
	q->rear++;
	//数据数+1; 
	q->count++;
} 

(4) out of the team

//出队
void OutQueue(SqQueue *q,int *e)
{
	//判断非法情况,是否为空 
	if(q->front == q->rear)
		exit(-1);
	//取出队头元素,赋值给e 
	*e=q->data[q->front];
	//队头指针++ 
	q->front++;
	//元素个数-1; 
	q->count--;
} 

(5) Print queue

void QueuePrint(SqQueue *q)
{
	int i=0;
	printf("目前队列中有%d个数据\n",q->count);
	//从队头遍历到队尾 
	for(i=q->front;i<q->rear;i++)
	{
		printf("%d ",q->data[i]);	
	}
	printf("\n");	
} 

(6) Circular queue

        Introduction: Due to the false overflow phenomenon in ordinary sequential storage queues, the vacancies that appear after dequeueing cannot be filled. At this time, the circular queue takes action. It has an extra remainder operation. Each time the maximum value of the modulus array is given, the overflowed number is controlled within the maximum value, reaching a cycle, and forming a ring.

        That is, each time the head and tail of the mark are traversed, when transformed, it becomes q->front=(q->front+1)%MaxSize;q->rear=(q->rear+1)%MaxSize;

        In addition, when the circular queue is judged to be full, there are generally two operations:

        One is to sacrifice the last grid. When rear+1=front, the team is full.

        Another operation is: like me at the beginning, add a counter to record the actual data in the structure definition. Every time it is dequeued and entered, the counter will be increased and subtracted accordingly. When the counter equals MaxSize, the queue is full.

        In addition, the length of the circular queue is calculated as: [MaxSize-(q->rear - q->front)]%MaxSize; but if a counter was added to the structure before, just print the counter directly.

        The following is the operation of the circular queue.

        Joining the team: (It’s just that there is one more judgment for judging the fullness of the team than before joining the team, and taking the remainder when moving the pointer at the end of the team)

void CycEnQueue(SqQueue *q,int x)
{
	//如果队满了 
	if(QueueFull(q)==1) //可以在结构体中加个计数器
		exit(-1);
    //if(q->rear+1 == q->front)  //也可以牺牲一个存储单元,用来判断队满
	//	exit(-1);
	//给队尾处赋值 
	q->data[q->rear]=x;
	//移动队尾标记 
	q->rear=(q->rear+1)%MaxSize;
	//数据数+1; 
	q->count++;
} 

        Dequeue:   

void CycOutQueue(SqQueue *q,int *e)
{
	//判断非法情况,是否为空 
	if(q->front == q->rear)
		exit(-1);
	//取出队头元素,赋值给e 
	*e=q->data[q->front];
	//队头指针++ 
	q->front=(q->front+1)%MaxSize;
	//元素个数-1; 
	q->count--;
} 

(7) The total code that can be run in sequence:

#include <stdio.h>
#include <stdlib.h> 
//队列 
//队列的顺序存储
#define MaxSize 10
typedef struct
{
	int data[MaxSize];
	int front;
	int rear;
	//计数器 
	int count;
}SqQueue; 
//队列初始化
void QueueInit(SqQueue *q)
{
	q->front=0;	
	q->rear=0;
	q->count=0;
} 
//队空
int QueueEmpty(SqQueue *q) 
{
	if(q->front == q->rear )
	return 1;
}
//队满
int QueueFull(SqQueue *q)
{
	if(q->count == MaxSize-1)
	return 1;
	else
	return -1;
}
//入队
void EnQueue(SqQueue *q,int x)
{
	//如果队满了 
	if(QueueFull(q)==1)
		exit(-1);
	//给队尾处赋值 
	q->data[q->rear]=x;
	//移动队尾标记 
	q->rear++;
	//数据数+1; 
	q->count++;
} 
void CycEnQueue(SqQueue *q,int x)
{
	//如果队满了 
	if(q->rear+1 == q->front)
		exit(-1);
	//给队尾处赋值 
	q->data[q->rear]=x;
	//移动队尾标记 
	q->rear=(q->rear+1)%MaxSize;
	//数据数+1; 
	q->count++;
} 
//出队
void OutQueue(SqQueue *q,int *e)
{
	//判断非法情况,是否为空 
	if(q->front == q->rear)
		exit(-1);
	//取出队头元素,赋值给e 
	*e=q->data[q->front];
	//队头指针++ 
	q->front++;
	//元素个数-1; 
	q->count--;
} 
void CycOutQueue(SqQueue *q,int *e)
{
	//判断非法情况,是否为空 
	if(q->front == q->rear)
		exit(-1);
	//取出队头元素,赋值给e 
	*e=q->data[q->front];
	//队头指针++ 
	q->front=(q->front+1)%MaxSize;
	//元素个数-1; 
	q->count--;
} 
//打印队列
void QueuePrint(SqQueue *q)
{
	int i=0;
	printf("目前队列中有%d个数据\n",q->count);
	//从队头遍历到队尾 
	for(i=q->front;i<q->rear;i++)
	{
		printf("%d ",q->data[i]);	
	}
	printf("\n");	
} 


//队列的链式存储 

int main()
{
	//创建队列 
	SqQueue q;
	//初始化队列 
	QueueInit(&q);
	//打印队列 
	QueuePrint(&q);
	//入队 
	EnQueue(&q,0); 
	EnQueue(&q,1); 
	EnQueue(&q,2); 
	EnQueue(&q,3); 
	QueuePrint(&q);
	//出队 
	int e=0;
	OutQueue(&q,&e);
	QueuePrint(&q);
	printf("出队%d\n",e);
	return 0;
} 

2. Chain storage

        Introduction: Implemented using a linked list data structure, each node contains an element and a pointer to the next node.

        The advantage of the linked list queue: the queue length can be adjusted dynamically.

        Disadvantages of linked list queues: more memory space is required to store pointer information. In addition, due to the need to dynamically apply for and release memory, the queue implemented by the linked list is slightly slower in operation than the queue implemented by the array.

(1) Chained storage definition

        Introduction: It is composed of a singly linked list, and then operated by the head pointer and the tail pointer, so two structures are defined, one structure defines the queue node type, and the other encapsulates the head and tail pointers.

/链队结点
typedef struct Linknode
{
	int data;
	struct Linknode* next; 
}LinkNode;
//链队的头指针和尾指针 
typedef struct
{
	LinkNode* front;
	LinkNode* rear;
}LinkQueue;

(2)Initialization

There is initialization here, and the default leading node is to make the initial operation consistent with other operations.

/链队初始化
void InitQueue(LinkQueue* q)
{
	//创建头节点
	LinkNode* s = (LinkNode*)malloc(sizeof(LinkNode));
	if (s == NULL)
		exit(-1);
	else
	{
		s->next = NULL;
		q->front = q->rear = s;
		q->rear->next = NULL;
	}
	//return q;
}

During initialization, after the head node is defined, both the head pointer and the tail pointer point to it, and the pointer field of the tail pointer points to null.

(3)Join the team

When entering the team, a queue node is first defined to join the team. Then use the tail pointer to perform related operations. Connect the node first, and then update the queue tail pointer.

//入队
void   EnQueue(LinkQueue* q, int x)
{
	//创建结点
	LinkNode* s = (LinkNode*)malloc(sizeof(LinkNode));
	if (s == NULL)
		exit(-1);
	else
	{
		s->data = x;
		s->next = NULL;
		//队尾所指向的结点的指针域,指向存储s结点地址
		q->rear->next = s;
		//更新尾指针
		q->rear = s;
	}
	
}

(4)Leave the team

When dequeuing, first determine whether the queue is empty, and then perform the dequeuing operation. When dequeuing, first define a pointer to point to the element that needs to be dequeued, because here is the head node, and the team head pointer always points to the head node, so Mark the dequeue element pointer as the pointer field of the queue head pointer, that is, the successor node of the head node is the actual dequeue node. Then the pointer field of the head node points to the successor of the dequeued node, and determines whether the currently dequeued element is the last node, that is, if q->rear = p, let the queue be initialized to be empty, that is, the queue head The pointer and the tail pointer are equal. Then release the P node.

//出队
void  DeQueue(LinkQueue* q, int* e)
{
	//判断非法情况,空队 
	if (q->front == q->rear)
		exit(-1);
	//给出队元素赋值 
	*e = q->front->next->data;
	//标记出队元素 
	LinkNode* p = q->front->next;
	//队头指针后移到出队元素后继节点 
	q->front->next = p->next;
	//判断是否仅有一个结点 
	if (q->rear == p)
	{
		q->rear = q->front;
	}
	free(p);
	//return q;
}

(5)Print chain team

Define a working pointer,

The working pointer starts from the actual first element node, which is the successor node of the head node.

void LinkQueuePrint(LinkQueue* q)
{
	LinkNode* pos = q->front->next;//队头元素指向头节点
	int i = 0;
	while (pos !=NULL)
	{
		printf("%d->", pos->data);
		pos = pos->next;
	}

	printf("NULL\n");
}

(6)The chain team can run the total code

#include <stdio.h>
#include <stdlib.h>
#include <string.h> 
//链队结点
typedef struct Linknode
{
	int data;
	struct Linknode* next; 
}LinkNode;
//链队的头指针和尾指针 
typedef struct
{
	LinkNode* front;
	LinkNode* rear;
}LinkQueue;
//链队初始化
void InitQueue(LinkQueue* q)
{
	//创建头节点
	LinkNode* s = (LinkNode*)malloc(sizeof(LinkNode));
	if (s == NULL)
		exit(-1);
	else
	{
		s->next = NULL;
		q->front = q->rear = s;
		q->rear->next = NULL;
	}
	//return q;
}
//入队
void   EnQueue(LinkQueue* q, int x)
{
	//创建结点
	LinkNode* s = (LinkNode*)malloc(sizeof(LinkNode));
	if (s == NULL)
		exit(-1);
	else
	{
		s->data = x;
		s->next = NULL;
		//队尾所指向的结点的指针域,指向存储s结点地址
		q->rear->next = s;
		//更新尾指针
		q->rear = s;
	}
	
	//return q;
}
//出队
void  DeQueue(LinkQueue* q, int* e)
{
	//判断非法情况,空队 
	if (q->front == q->rear)
		exit(-1);
	//给出队元素赋值 
	*e = q->front->next->data;
	//标记出队元素 
	LinkNode* p = q->front->next;
	//队头指针后移到出队元素后继节点 
	q->front->next = p->next;
	//判断是否仅有一个结点 
	if (q->rear == p)
	{
		q->rear = q->front;
	}
	free(p);
	//return q;
}
void LinkQueuePrint(LinkQueue* q)
{
	LinkNode* pos = q->front->next;//队头元素指向头节点
	int i = 0;
	while (pos !=NULL)
	{
		printf("%d->", pos->data);
		pos = pos->next;
	}

	printf("NULL\n");
}
int main()
{
	 LinkQueue q ;
	 InitQueue(&q);
	 EnQueue(&q, 0);
	 EnQueue(&q, 1);
	 EnQueue(&q, 2);
	 EnQueue(&q, 3);
	LinkQueuePrint(&q);
	int e = 0;
	DeQueue(&q, &e);
	printf("e=%d\n", e);
	LinkQueuePrint(&q);
	DeQueue(&q, &e);
	printf("e=%d\n", e);
	LinkQueuePrint(&q);
	return 0;
}

3. Double-ended queue.

This part is all you need to understand the ideas, and is mainly used for calculating choices and filling in the blanks.

        Introduction:

        Double-ended Queue (Double-ended Queue) is a special queue that allows insertion and deletion at both ends of the queue. A double-ended queue can be regarded as a data structure composed of two stacks connected end to end. In a double-ended queue, elements can be inserted and deleted at the head and tail of the queue, so its operations include: inserting elements from the head of the queue, deleting elements from the head of the queue, inserting elements from the tail of the queue, and deleting elements from the tail of the queue. wait.

A double-ended queue can be implemented using an array or a linked list . When implementing in an array, you need to pay attention to the positions of the head and tail pointers of the queue. When the queue length is greater than the array length, expansion operation is required. When implemented in a linked list, only the head node and the tail node need to be maintained.

Double-ended queues are widely used. For example, process scheduling queues in operating systems, data caching in window sliding, and other scenarios can be implemented using double-ended queues.        

To put it bluntly, on the basis of the original queue, it has several more functions, that is, both ends can perform queue entry and exit operations.

        After that, a question type was derived. Input restricted deque, output restricted deque.

(1) Input-restricted double-ended queue

        That is, one section can only be output, and the other end can be input and output. That is, only that section can be output and cannot be input, which means that the input is limited.

        Remember a trick here: if 1234 is the queue entry sequence, then if the input is limited, there is this formula: ..1..2..3..4, that is, if 4 comes out first, 2 cannot come out immediately. . Because 2 is in the middle of 1 3. This technique is able to obtain the impossible sequence

(2) Output-restricted double-ended queue

        That is, one section can only be input, and the other end is not restricted, that is, only that section can be input, and cannot be output, that is, the output is limited.

        Tip: If 1234 enters the queue sequence, then there is this formula: 12...3...4. At this time, 4 is output, then 3 will definitely not be between 12, because 12 are right next to each other. This technique is able to obtain the impossible sequence

(3) Example questions:

        There is a double-ended queue, the input sequence is 1, 2, 3, 4. Find the output sequence of the following conditions:

        1. It can be obtained by limiting the input, but the output cannot be obtained.

        2. It can be obtained by limiting the output, but not by the input.

        3. Neither input nor output is available,

Solution: For application questions, you need proofs one by one, and some are too many. I think it is usually to choose and fill in the blanks, so we use skills to do it.

        The technique can obtain sequences that are impossible to obtain with input constraints, and sequences that are impossible to obtain with output constraints, and then we let these two difference sets.

Restricted input is not available:

        Rule: ..1..2..3..4, then 4 comes out first, then 2 will not come out immediately.

So the impossible ones are: 4, 2, 1, 3 and 4, 2, 3, 1

The output is limited and cannot be obtained:
        Rule: 12...3...4, then 4 comes out first. Then 3 will definitely not be in the middle of 12, because 12 is immediately adjacent.

So the impossible ones are: 4, 1, 3, 2 and 4, 2, 3, 1

Then we can filter according to the conditions.

(1) The input is obtained, but the output cannot be obtained. We filter what we can’t get from the output and input what we can get.

Therefore, it is 4,1,3,2. Because 4,2,3,1 cannot be obtained in the input restriction, it is excluded.

(2) The output is obtained, but the input is not obtained: We filter out what is not available from the input, and the output is obtainable, so it is:
4,2,1,3, because the output of 4,2,3,1 is also obtained Not enough, so excluded.

(3) Neither input nor output can be obtained : Therefore, we find the intersection of these two, so it is: 4,2,3,1


This concludes the basic theory of queues! Remember to review often in the future. The basic operations must be thoroughly understood. First understand the overall situation and then memorize the specifics.

Guess you like

Origin blog.csdn.net/m0_59844149/article/details/132191495