Detailed Queue Explanation of Data Structure (including examples)

1. The concept of queue

Queue is a special linear table, which is special in that it only allows deletion operations at the front end (front) of the table, and insert operations at the back end (rear) of the table. Like stacks, queues are an operation subject to A linear list of constraints. The end of the insertion operation is called the tail of the queue, and the end of the deletion operation is called the head of the queue.

Second, simulate the implementation of sequential queues

We can simulate a sequential queue with a singly linked list.

The FIFO (first in first out) used by the queue, new elements (elements waiting to enter the queue) are always inserted into the tail of the linked list (corresponding to the tail insertion of the single linked list), and the read always starts from the head of the linked list read. Each time an element is read, an element is released (corresponding to the deletion of the head of the singly linked list).

corresponding interface

Note that there is also a method to avoid the use of secondary pointers. Create a structure variable that stores nodes in it . When we want to change the value in the structure, we can use the primary pointer.

typedef int QDataType;
typedef struct QueueNode
{
	//用单链表模拟实现队列
	struct QueueNode* next;
	QDataType data;
}QNode;

//新的避免二级指针的结构体
typedef struct Queue
{
	QNode* head;
	QNode* tail;
	int sz;
}Que;

void QueueInit(Que* pq);
void QueueDestory(Que* pq);
void QueuePush(Que* pq, QDataType x);
void QueeuPop(Que* pq);
QDataType QueueFront(Que* pq);
QDataType QueueBack(Que* pq);
bool QueueEmpty(Que* pq);
int QueueSize(Que* pq);

Initialization of the queue:

void QueueInit(Que* pq)
{
	assert(pq);
	pq->sz = 0;
	pq->head = pq->tail = NULL;
}

queue destruction

Note that after free, also point to empty

void QueueDestroy(Que* pq)
{
	assert(pq);
	while (pq->sz > 0)
	{
		QNode* cur = pq->head->next;
		free(pq->head);
		pq->head = cur;
		pq->sz--;
	}
	pq->tail = pq->head = NULL;
}

Insert data into the queue

That is, the tail insertion of the singly linked list, but also pay attention to the special situation when the queue is empty.

void QueuePush(Que* pq,QDataType x)
{
	//类似于尾插
	assert(pq);
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror(malloc);
		exit(-1);
	}
	newnode->data = x;
	newnode->next = NULL;
	if (pq->head == NULL)
	{
		pq->head = pq->tail = newnode;
		pq->sz++;
	}
	else
	{
		pq->sz++;
		pq->tail->next = newnode;
		pq->tail = newnode;
	}
}

Queue delete data

That is, the head of the singly linked list is deleted, and at the same time, it is also necessary to pay attention to the situation that after only one node is deleted, the tail node points to empty


void QueeuPop(Que* pq)
{
	assert(pq);
	assert(pq->sz > 0);
	//头删
	//单独判断只剩下一个结点,头删后tail是野指针的情况
	if (pq->head->next == NULL)
	{
		free(pq->head);
		pq->head = pq->tail = NULL;
		pq->sz--;
	}
	else
	{
		QNode* cur = pq->head;
		pq->head = pq->head->next;
		free(cur);
		pq->sz--;
	}
	
}

Return the data at the head of the queue

QDataType QueueFront(Que* pq)
{
	assert(pq);
	//assert(pq->head);
	assert(!QueueEmpty(pq));
	return pq->head->data;
}

Return the end of queue data

QDataType QueueBack(Que* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	return pq->tail->data;
}

Check if the queue is empty

bool QueueEmpty(Que* pq)
{
	assert(pq);
	return pq->head == NULL;
}

Return the number of data in the queue

int QueueSize(Que* pq)
{
	assert(pq);
	//assert(pq->head);
	return pq->sz;
}

3. Simulation to realize circular queue

622. Design Circular Queue - LeetCode

This actually imagines the queue space as a ring space, and the storage units in the ring space are recycled . The queue managed in this way is also called a circular queue.

Note that the structure of this question is relatively complicated, and it is necessary to draw a picture to solve it , so that it is easy to consider special circumstances and is not easy to make mistakes.

It is relatively simple to implement this question with an array . If it is implemented with a singly linked list, then the previous one of the rear end node is not easy to find.

How does the array realize the loop?

The loop implemented by the array does not have a next pointer pointing to the head node like a singly linked list. If it has reached the end, the value of the head can be directly equal to the desired value of the tail.

//如何用数组实现循环?
typedef struct {
    int* a;
    int front;
    int rear;
    int num;
} MyCircularQueue;


MyCircularQueue* myCircularQueueCreate(int k) {
    MyCircularQueue* cur = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    //用k+1个数组空间,便于判断空和满的情况。
    cur->a = (int*)malloc(sizeof(int)*(k+1));
    cur->front = 0;
    cur->rear = 0;
    cur->num = k;
    return cur;
}

bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
    if(obj->front == obj->rear)
        return true;
    else
        return false;
}

bool myCircularQueueIsFull(MyCircularQueue* obj) {
    //两种情况都要考虑到!
    if(obj->front-obj->rear == 1 || obj->rear-obj->front == obj->num)
        return true;
    else
        return false;
}

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    //先判断是否已经满
    if(myCircularQueueIsFull(obj) == true)
        return false;
    //假设rear在队列的尾部
    if(obj->rear == obj->num)
    {
        obj->a[obj->rear] = value;
        obj->rear = 0;
    }
    else
    {
        obj->a[obj->rear] = value;
        obj->rear++;
    }
    //obj->a[obj->rear] = value;
    //obj->rear++;
    //obj->rear %= (obj->num+1);
    return true;
}

bool myCircularQueueDeQueue(MyCircularQueue* obj) {
    //先判断是否已经空了
    if(myCircularQueueIsEmpty(obj) == true)
    {
        return false;
    }
    //假设front在队列的尾部
    if(obj->front == obj->num)
        obj->front = 0;
    else
        obj->front++;
    //++obj->front;
    //obj->front %= (obj->num+1);
    return true;
}

int myCircularQueueFront(MyCircularQueue* obj) {
    if(myCircularQueueIsEmpty(obj) == true)
        return -1;
    else
        return obj->a[obj->front];
}

int myCircularQueueRear(MyCircularQueue* obj) {
    //注意队尾的rear比数据提前一位数,所以是rear-1
    //但要考虑rear-1的情况
    if(myCircularQueueIsEmpty(obj) == true)
        return -1;
    if(obj->rear == 0)
    {
        //需要再创建一个临时变量,不能改变rear的值
        int tmp = obj->num;
        return obj->a[tmp];
    }
    // else
    //     return  obj->a[(obj->rear+obj->num)%(obj->num+1)];
    return obj->a[obj->rear-1];
}

void myCircularQueueFree(MyCircularQueue* obj) {
    free(obj->a);
    free(obj);
}

Fourth, use the queue to implement the stack

225. Implementing a Stack with a Queue - LeetCode

This question requires two queues to implement the stack , and we can invert data between the two queues to meet the needs of the question for the stack !

Ideas:

Enqueue:

enqueue not empty

Dequeue:

Use the nature of the queue to put the first n-1 data into another empty queue, delete the remaining data, and complete the pop operation of the stack !

code:

typedef struct {
    Que q1;
    Que q2;
} MyStack;


MyStack* myStackCreate() {
    MyStack* cur = (MyStack*)malloc(sizeof(MyStack));
    //不能忘记初始化
    QueueInit(&cur->q1);
    QueueInit(&cur->q2);
    return cur;
}

void myStackPush(MyStack* obj, int x) {
    //push到不为空的的队列中
    if(!QueueEmpty(&obj->q1))
    {
        QueuePush(&obj->q1,x);
    }
    else
    {
        QueuePush(&obj->q2,x);
    }
}

int myStackPop(MyStack* obj) {
    //先假设q1不为空,q2为空
    Que* empty = &obj->q1;
    Que* nonEmpty = &obj->q2;
    if(!QueueEmpty(&obj->q1))
    {
        //如果假设失败,相反
        empty = &obj->q2;
        nonEmpty = &obj->q1;
    }
    while(QueueSize(nonEmpty)>1)
    {
        //开始用函数进行捯数据
        //从前往后的顺序是根据队列pop的顺序定的
        QueuePush(empty,QueueFront(nonEmpty));
        QueuePop(nonEmpty);
    }
    int top = QueueBack(nonEmpty);
    QueuePop(nonEmpty);
    return top;
}

int myStackTop(MyStack* obj) {
    Que* empty = &obj->q1;
    Que* nonEmpty = &obj->q2;
    if(!QueueEmpty(&obj->q1))
    {
        //如果假设失败,相反
        empty = &obj->q2;
        nonEmpty = &obj->q1;
    }
    return QueueBack(nonEmpty);
}

bool myStackEmpty(MyStack* obj) {
    //判断两个队列
    return QueueEmpty(&obj->q1) && QueueEmpty(&obj->q2);
}

void myStackFree(MyStack* obj) {
    //先对两个队列中的链表进行free
    QueueDestroy(&obj->q1);
    QueueDestroy(&obj->q2);
    free(obj);
}

5. Implement queue with stack

232. Implement Queue with Stack - LeetCode

Title description:

Please use only two stacks to implement a first-in-first-out queue. Queues should support all operations supported by general queues ( push, pop, peek, empty):

Ideas:

This question is different from the previous question using a queue to implement a stack. You can directly distinguish between the push stack and the pop stack. If the pop stack is empty, push all the push stacks to the pop stack !

code:

typedef struct 
{
	ST push;
	ST pop;
} MyQueue;


MyQueue* myQueueCreate() {
	MyQueue* cur = (MyQueue*)malloc(sizeof(MyQueue));
	SLInit(&cur->push);
	SLInit(&cur->pop);
	return cur;
}

void myQueuePush(MyQueue* obj, int x) {
	STPush(&obj->push,x);
}

int myQueuePop(MyQueue* obj) {
	
	int ret = myQueuePeek(obj);
	STPop(&obj->pop);
	return ret;
}

int myQueuePeek(MyQueue* obj) {
	//出栈只能从后往前出
	//如果pop栈为空,就将push栈全部捯到pop栈!
	if(STEmpty(&obj->pop))
	{
		while(!STEmpty(&obj->push))
		{
			STPush(&obj->pop,STTop(&obj->push));
			STPop(&obj->push);
		}
	}
	int ret = STTop(&obj->pop);
	return ret;
}

bool myQueueEmpty(MyQueue* obj) {
	//用函数求解
	return STEmpty(&obj->push) && STEmpty(&obj->pop);
}

void myQueueFree(MyQueue* obj) {
	SLDestroy(&obj->pop);
	SLDestroy(&obj->push);
	free(obj);
}

Guess you like

Origin blog.csdn.net/hanwangyyds/article/details/132270690