Data structure: the implementation and diagram of stack and queue realize each other

written in front

The implementation of stacks and queues relies on sequential lists and linked lists. It is difficult to truly understand stacks and queues if you are not clear about sequential lists and linked lists.

The following is an explanation of the implementation and illustration of the
sequence table
and linked list

the stack

what is a stack

A stack is a data structure that follows the principle of last-in-first-out. Simply put, the last thing that goes into the stack goes out last, and the last thing that goes into the stack goes out first.

Stacks also have many scenarios in practical applications. For example, when using a web page, we click on multiple web pages, and when we exit and return, we follow the last-in-first-out principle of the stack.

Implementation of the stack

Now that the principle of the stack is known, it is better to implement the stack. First of all, it is determined that it can be realized by a linear table. It is not difficult to find out the principle of using the stack. It only involves the input and output of one end, which means that the order of use Tables are a great solution

The function of the stack is not too much, check the stack when it is full, and check the top element of the stack... On the whole, the stack is a deformation of the sequence table. Here, there are not too many supplements to the implementation of the stack. The focus is on the mutual realization of the queue and the latter

First list the definition of the stack and the parts to be implemented by the stack. It is a good habit to separate the declaration and definition

// stack.h
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>

// 支持动态增长的栈
typedef int STDataType;
typedef struct Stack
{
    
    
	STDataType* _a;
	int _top;		// 栈顶
	int _capacity;  // 容量 
}Stack;

// 初始化栈 
void StackInit(Stack* ps);

// 入栈 
void StackPush(Stack* ps, STDataType data);

// 出栈 
void StackPop(Stack* ps);

// 获取栈顶元素 
STDataType StackTop(Stack* ps);

// 获取栈中有效元素个数 
int StackSize(Stack* ps);

// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0 
int StackEmpty(Stack* ps);

// 销毁栈 
void StackDestroy(Stack* ps);

The following is the implementation of the stack, almost all the basic operations of the sequence table, the implementation is very simple

// stack.c
#include "stack.h"

void StackInit(Stack* ps)
{
    
    
	assert(ps);
	STDataType* tmp = NULL;
	int newcapacity = ps->_capacity == 0 ? 4 : ps->_capacity * 2;
	tmp = (STDataType*)realloc(ps->_a, sizeof(STDataType) * newcapacity);
	if (tmp == NULL)
	{
    
    
		perror("realloc fail");
		return;
	}
	ps->_capacity = newcapacity;
	ps->_a = tmp;
}

void StackPush(Stack* ps, STDataType data)
{
    
    
	assert(ps);
	if (ps->_capacity == ps->_top)
	{
    
    
		STDataType* tmp = NULL;
		int newcapacity = ps->_capacity == 0 ? 4:ps->_capacity * 2;
		tmp = (STDataType*)realloc(ps->_a,sizeof(STDataType)* newcapacity);
		if (tmp == NULL)
		{
    
    
			perror("realloc fail");
			return;
		}
		ps->_capacity = newcapacity;
		ps->_a = tmp;
	}
	ps->_a[ps->_top] = data;
	ps->_top++;
}

bool STEmpty(Stack* ps)
{
    
    
	assert(ps);
	
	return ps->_top == 0;
}

void StackPop(Stack* ps)
{
    
    
	assert(ps);
	assert(!STEmpty(ps));

	ps->_top--;
}

STDataType StackTop(Stack* ps)
{
    
    
	assert(ps);
	assert(!STEmpty(ps));
	
	return ps->_a[ps->_top-1];
}

int StackSize(Stack* ps)
{
    
    
	assert(ps);
	return ps->_top;
}

int StackEmpty(Stack* ps)
{
    
    
	assert(ps);
	if (0 == ps->_top)
		return 1;
	else
		return 0;
}

void StackDestroy(Stack* ps)
{
    
    
	assert(ps);
	ps->_capacity = 0;
	ps->_top = 0;
	free(ps->_a);
	ps->_a = NULL;
}

On the whole, as long as the sequence table is mastered, the implementation of the stack is very easy

queue

what is a queue

Judging from the name, queues are often encountered in daily life. No matter where you are, the concept of queuing is indispensable. In an orderly queue, entering the queue is from the back, and exiting the queue is from the beginning. It is similar to head deletion and tail insertion in linked list

Then there is the definition of the queue, first-in first-out, last-in-last-out, this is the definition of the queue. The implementation of the queue
is still related to the linear table. The specific selection of the sequence table or the linked list needs to be analyzed:

If you choose a sequence table, the head deletion and tail insertion of the sequence table are obviously not as good as the linked list. You may have such a solution: we can use the array subscript as the head and tail, so that we can simulate one less at the head and one at the end. Indeed, this can be solved, but the next problem is that the length of the array is not easy to control. If you want to make full use of the sequence table, you must use a circular array. The subscript of the circular array is not easy to control, so a linked list is used here is a very suitable choice

Here is about parsing and simulating queues for circular arrays:
parsing circular arrays

Implementation of the queue

// queue.h
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>

typedef int QDataType;
typedef struct QueueNode
{
    
    
	struct QueueNode* next;
	QDataType data;
}QNode;
 
typedef struct Queue
{
    
    
	QNode* phead;
	QNode* ptail;
	int size;
}Queue;

void QueueInit(Queue* pq);

void QueueDestroy(Queue* pq);

void QueuePush(Queue* pq, QDataType x);

void QueuePop(Queue* pq);

QDataType QueueFront(Queue* pq);

QDataType QueueBack(Queue* pq);

int QueueSize(Queue* pq);

bool QueueEmpty(Queue* pq);

The specific implementation of the above function declaration is as follows:

// queue.c
#include "queue.h"

#include"Queue.h"

void QueueInit(Queue* pq)
{
    
    
	assert(pq);

	pq->phead = NULL;
	pq->ptail = NULL;
	pq->size = 0;
}

void QueueDestroy(Queue* pq)
{
    
    
	assert(pq);

	QNode* cur = pq->phead;
	while (cur)
	{
    
    
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}

	pq->phead = pq->ptail = NULL;
	pq->size = 0;
}

void QueuePush(Queue* pq, QDataType x)
{
    
    
	assert(pq);

	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
    
    
		perror("malloc fail\n");
		return;
	}
	newnode->data = x;
	newnode->next = NULL;

	if (pq->ptail == NULL)
	{
    
    
		assert(pq->phead == NULL);

		pq->phead = pq->ptail = newnode;
	}
	else
	{
    
    
		pq->ptail->next = newnode;
		pq->ptail = newnode;
	}


	pq->size++;
}

void QueuePop(Queue* pq)
{
    
    
	assert(pq);
	assert(!QueueEmpty(pq));

	// 1、一个节点
	// 2、多个节点
	if (pq->phead->next == NULL)
	{
    
    
		free(pq->phead);
		pq->phead = pq->ptail = NULL;
	}
	else
	{
    
    
		// 头删
		QNode* next = pq->phead->next;
		free(pq->phead);
		pq->phead = next;
	}

	pq->size--;
}

QDataType QueueFront(Queue* pq)
{
    
    
	assert(pq);
	assert(!QueueEmpty(pq));

	return pq->phead->data;
}

QDataType QueueBack(Queue* pq)
{
    
    
	assert(pq);
	assert(!QueueEmpty(pq));

	return pq->ptail->data;
}

int QueueSize(Queue* pq)
{
    
    
	assert(pq);

	return pq->size;
}

bool QueueEmpty(Queue* pq)
{
    
    
	assert(pq);

	return pq->size == 0;
}

Stacks and queues themselves are not difficult, but what if you use stacks to implement queues, and queues to implement stacks?
Let's analyze how to realize the mutual realization of queue and stack:

Implementing a stack with a queue

Look at the schematic first:

insert image description here
The code implementation is not too difficult, the implementation is as follows:

#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>

typedef int QDataType;
typedef struct QueueNode
{
    
    
	struct QueueNode* next;
	QDataType data;
}QNode;
 
typedef struct Queue
{
    
    
	QNode* phead;
	QNode* ptail;
	int size;
}Queue;
#include "queue.h"

#include"Queue.h"

void QueueInit(Queue* pq)
{
    
    
	assert(pq);

	pq->phead = NULL;
	pq->ptail = NULL;
	pq->size = 0;
}

void QueueDestroy(Queue* pq)
{
    
    
	assert(pq);

	QNode* cur = pq->phead;
	while (cur)
	{
    
    
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}

	pq->phead = pq->ptail = NULL;
	pq->size = 0;
}

void QueuePush(Queue* pq, QDataType x)
{
    
    
	assert(pq);

	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
    
    
		perror("malloc fail\n");
		return;
	}
	newnode->data = x;
	newnode->next = NULL;

	if (pq->ptail == NULL)
	{
    
    
		assert(pq->phead == NULL);

		pq->phead = pq->ptail = newnode;
	}
	else
	{
    
    
		pq->ptail->next = newnode;
		pq->ptail = newnode;
	}


	pq->size++;
}

void QueuePop(Queue* pq)
{
    
    
	assert(pq);
	assert(!QueueEmpty(pq));

	// 1、一个节点
	// 2、多个节点
	if (pq->phead->next == NULL)
	{
    
    
		free(pq->phead);
		pq->phead = pq->ptail = NULL;
	}
	else
	{
    
    
		// 头删
		QNode* next = pq->phead->next;
		free(pq->phead);
		pq->phead = next;
	}

	pq->size--;
}

QDataType QueueFront(Queue* pq)
{
    
    
	assert(pq);
	assert(!QueueEmpty(pq));

	return pq->phead->data;
}

QDataType QueueBack(Queue* pq)
{
    
    
	assert(pq);
	assert(!QueueEmpty(pq));

	return pq->ptail->data;
}

int QueueSize(Queue* pq)
{
    
    
	assert(pq);

	return pq->size;
}

bool QueueEmpty(Queue* pq)
{
    
    
	assert(pq);

	return pq->size == 0;
}
typedef struct Mystack
{
    
    
	Queue push;
	Queue pop;
}Mystack;

void MsInit(Mystack* ps)
{
    
    
	assert(ps);
	QueueInit(&(ps->push));
	QueueInit(&(ps->pop));
}

void MsPush(Mystack* ps,QDataType x)
{
    
    
	assert(ps);
	QueuePush(&(ps->push), x);
}

void MsPop(Mystack* ps)
{
    
    
	while (QueueSize(&(ps->push)) > 1)
	{
    
    
		QueuePush(&(ps->pop), QueueFront(&(ps->push)));
		QueuePop(&(ps->push));
	}
	QueuePop(&(ps->push));
	while (!QueueEmpty(&(ps->pop)))
	{
    
    
		QueuePush(&(ps->push), QueueFront(&(ps->pop)));
		QueuePop(&(ps->pop));
	}
}

QDataType MsTop(Mystack* ps)
{
    
    
	assert(ps);
	return ps->push.ptail->data;
}

bool MsEmpty(Mystack* ps)
{
    
    
	if (ps->push.size == 0)
		return true;
	return false;
}

int main()
{
    
    
	Mystack s;
	MsInit(&s);
	MsPush(&s, 1);
	MsPush(&s, 2);
	MsPush(&s, 3);
	MsPush(&s, 4);
	MsPush(&s, 5);
	while (!MsEmpty(&s))
	{
    
    
		printf("%d ", MsTop(&s));
		MsPop(&s);
	}
	return 0;
}

Simulate a queue with a stack

Compared with the above, the stack implements the queue with some changes:

insert image description here

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

// 支持动态增长的栈
typedef int STDataType;
typedef struct Stack
{
    
    
	STDataType* _a;
	int _top;		// 栈顶
	int _capacity;  // 容量 
}Stack;

void StackInit(Stack* ps)
{
    
    
	assert(ps);
	ps->_a = NULL;
	ps->_top = 0;
	ps->_capacity = 0;
}

void StackPush(Stack* ps, STDataType data)
{
    
    
	assert(ps);
	if (ps->_capacity == ps->_top)
	{
    
    
		STDataType* tmp = NULL;
		int newcapacity = ps->_capacity == 0 ? 4:ps->_capacity * 2;
		tmp = (STDataType*)realloc(ps->_a,sizeof(STDataType)* newcapacity);
		if (tmp == NULL)
		{
    
    
			perror("realloc fail");
			return;
		}
		ps->_capacity = newcapacity;
		ps->_a = tmp;
	}
	ps->_a[ps->_top] = data;
	ps->_top++;
}

bool STEmpty(Stack* ps)
{
    
    
	assert(ps);
	
	return ps->_top == 0;
}

void StackPop(Stack* ps)
{
    
    
	assert(ps);
	assert(!STEmpty(ps));

	ps->_top--;
}

STDataType StackTop(Stack* ps)
{
    
    
	assert(ps);
	assert(!STEmpty(ps));
	
	return ps->_a[ps->_top-1];
}

int StackSize(Stack* ps)
{
    
    
	assert(ps);
	return ps->_top;
}

int StackEmpty(Stack* ps)
{
    
    
	assert(ps);
	if (0 == ps->_top)
		return 1;
	else
		return 0;
}

void StackDestroy(Stack* ps)
{
    
    
	assert(ps);
	ps->_capacity = 0;
	ps->_top = 0;
	free(ps->_a);
	ps->_a = NULL;
}
typedef struct Myqueue
{
    
    
	Stack Push;
	Stack Pop;
}Myqueue;

void MqInit(Myqueue* pq)
{
    
    
	assert(pq);
	StackInit(&(pq->Push));
	StackInit(&(pq->Pop));
}

void MqPush(Myqueue* pq,STDataType x)
{
    
    
	assert(pq);
	StackPush(&(pq->Push), x);
}

void MqPop(Myqueue* pq)
{
    
    
	while (!StackEmpty(&(pq->Push)))
	{
    
    
		StackPush(&(pq->Pop), StackTop(&(pq->Push)));
		StackPop(&(pq->Push));
	}
	StackPop(&(pq->Pop));
	while (!StackEmpty(&(pq->Pop)))
	{
    
    
		StackPush(&(pq->Push), StackTop(&(pq->Pop)));
		StackPop(&(pq->Pop));
	}
}

STDataType MqTop(Myqueue* pq)
{
    
    
	// 把数据从push弄到pop
	while (!StackEmpty(&(pq->Push)))
	{
    
    
		StackPush(&(pq->Pop), StackTop(&(pq->Push)));
		StackPop(&(pq->Push));
	}
	STDataType ret = pq->Pop._a[pq->Pop._top-1];
	// 再把数据弄回去
	while (!StackEmpty(&(pq->Pop)))
	{
    
    
		StackPush(&(pq->Push), StackTop(&(pq->Pop)));
		StackPop(&(pq->Pop));
	}
	return ret;
}

int MqEmpty(Myqueue* pq)
{
    
    
	if (pq->Push._top == 0)
		return 1;
	return 0;
}

int main()
{
    
    
	Myqueue q;
	MqInit(&q);
	MqPush(&q, 1);
	MqPush(&q, 2);
	MqPush(&q, 3);
	MqPush(&q, 4);
	MqPush(&q, 5);
	while (!MqEmpty(&q))
	{
    
    
		printf("%d ", MqTop(&q));
		MqPop(&q);
	}
	return 0;
}

This can be achieved directly

Overall, the mutual realization of stacks and queues is not of great significance, but it can provide a deeper understanding of the principles of stacks and queues

Guess you like

Origin blog.csdn.net/qq_73899585/article/details/131670666