Data structure (C language implementation) - introduction of stack and queue and realization of basic operations (dynamic sequence stack + chain team)

1 Introduction

Today we come to learn two other linear structures - stacks and queues. Stacks and queues are linear tables with limited operations, so they can be called limited data structures.

2. stack

Stack: A special linear list that only allows insertion and deletion of elements at a fixed end. One end where data insertion and deletion operations are performed
is called the top of the stack, and the other end is called the bottom of the stack. The data elements in the stack follow the principle of LIFO (Last In First Out).
Push stack: The insertion operation of the stack is called push/push/push, and the incoming data is at the top of the stack.
Popping: The deletion operation of the stack is called popping, and the data is also on the top of the stack.

The implementation of the stack can generally be implemented using an array or a linked list. Relatively speaking, the structure of the array is better, because the cost of inserting data at the end of the array is relatively small. Moreover, a fixed-length static stack is generally not practical in practice, so what we mainly implement below is a sequential stack that supports dynamic growth.

2.1 Structure Definition

Record the position of the current top of the stack and the maximum capacity of the current stack.

//静态栈
//#define N 10
//typedef int STDataType;

//typedef struct Stack
//{
    
    
//	int a[N];
//	int top;
//}ST;

//动态栈
typedef int STDataType;

typedef struct Stack
{
    
    
	STDataType* a;
	int top;//栈顶
	int capacity;//容量
}ST;

2.2 Stack initialization and destruction

initialization:

void StackInit(ST* ps)
{
    
    
	assert(ps);
	ps->a = NULL;
	ps->top = 0;
	ps->capacity = 0;
}

destroy:

void StackDestory(ST* ps)
{
    
    
	assert(ps);
	free(ps->a);
	ps->capacity = 0;
	ps->top = 0;
}

2.3 Push and pop

When pushing into the stack, you must first determine whether the stack is full, and also pay attention to whether it is the first element of the stack, and then dynamically open up memory. Finally, the element is stored at the current top of the stack, and then the top of the stack is ++.

Into the stack:

void StackPush(ST* ps, STDataType x)
{
    
    
	assert(ps);
	if (ps->capacity == ps->top)
	{
    
    
		int newcapacity = (ps->capacity == 0) ? 4 : (2 * (ps->capacity));
		STDataType* tmp = (STDataType*)realloc(ps->a, sizeof(STDataType) * newcapacity);
		if (tmp == NULL)
		{
    
    
			perror("realloc");
			exit(-1);
		}
		ps->a = tmp;
		ps->capacity = newcapacity;
	}
	ps->a[ps->top] = x;
	ps->top++;
}

Popping out the stack is very simple, but pay attention to whether the stack is empty, if it is empty, the stack will fail, otherwise the top of the stack can be directly – just.

Pop:

void StackPop(ST* ps)
{
    
    
	assert(ps);
	assert(!StackEmpty(ps));
	ps->top--;
}

2.4 Get the top element of the stack

Pay attention to whether it is an empty stack

STDataType StackTop(ST* ps)
{
    
    
	assert(ps);
	assert(!StackEmpty(ps));
	return ps->a[ps->top - 1];
}

2.5 Determine whether the stack is empty

The current position of the top of the stack is 0, which means the stack is empty

bool StackEmpty(ST* ps)
{
    
    
	assert(ps);
	return ps->top == 0;
}

2.6 Find the number of elements in the stack

The current position at the top of the stack is the number of elements

int StackSize(ST* ps)
{
    
    
	assert(ps);
	return ps->top;
}

After the introduction of the concept and basic operations of the stack, let's learn about the queue.

3. Queue

Queue: A special linear table that only allows inserting data at one end and deleting data at the other end. The queue has the principle of FIFO (First In First Out).
Enqueue: The end of the insertion operation is called the end of the queue.
Out of the queue: The end of the delete operation is called the head of the queue.

Queues can also be implemented in the structure of arrays and linked lists. It is better to use the structure of linked lists, because if you use the structure of arrays, the efficiency of dequeueing and outputting data at the head of the array will be relatively low. So what we mainly implement below is the chain team.

3.1 Structure Definition

Each node of the queue has a data field and a pointer field pointing to the next node, and each queue has a head pointer and a tail pointer.

//结点定义
typedef int QDataType;

typedef struct QueueNode
{
    
    
	QDataType data;
	struct QueueNode* next;
}QNode;

//队列结构
typedef struct Queue
{
    
    
	QNode* head;
	QNode* tail;
}Queue;

3.2 Initialization and Destruction of Queue

Set the head pointer and tail pointer of the queue to NULL.

initialization:

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

When destroying, each node must be released, and finally the head pointer and tail pointer are empty.

destroy:

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

3.3 Enqueue and dequeue

When entering the queue, you need to dynamically open up memory, and then pay attention to judging whether it is the first element of the queue. If it is, both the head pointer and the tail pointer point to it. If not, you only need to change the tail pointer.

Enqueue:

void QueuePush(Queue* pq, QDataType x)
{
    
    
	assert(pq);
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
    
    
		perror("malloc");
		exit(-1);
	}
	newnode->next = NULL;
	newnode->data = x;
	if (pq->tail == NULL)
	{
    
    
		pq->head = newnode;
		pq->tail = newnode;
	}
	else
	{
    
    
		pq->tail->next = newnode;
		pq->tail = newnode;
	}
}

When leaving the queue, we must pay attention to whether it is an empty queue. If not, we need to judge whether it is the last element of the queue. If so, we need to set the head and tail pointers to empty after leaving the queue. If not, we only need to modify the head pointer.

Dequeue:

void QueuePop(Queue* pq)
{
    
    
	assert(pq);
	assert(!QueueEmpty(pq));
	if (pq->head->next == NULL)
	{
    
    
		free(pq->head);
		pq->head = NULL;
		pq->tail = NULL;
	}
	else
	{
    
    
		QNode* next = pq->head->next;
		free(pq->head);
		pq->head = next;
	}
}

3.4 Get the head and tail elements of the queue

It's very simple here, just pay attention to judging whether it is an empty queue.

Team head:

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

Tail:

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

3.5 Determine whether the queue is empty

If the queue head or tail pointer is empty, the queue is empty.

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

3.6 Find the number of elements in the queue

The number of elements can be obtained by traversing the queue.

int QueueSize(Queue* pq)
{
    
    
	assert(pq);
	QNode* cur = pq->head;
	int size = 0;
	while (cur)
	{
    
    
		cur = cur->next;
		size++;
	}
	return size;
}

The concept and basic operations of queues are also introduced.

4. Ending

Because we have learned the sequence table and linked list earlier, the operation of the stack and queue here is relatively simple to implement. The main thing is to understand the characteristics of the stack and queue, and use the appropriate structure in the right place. The idea is the most important heavy.

Finally, I would like to thank you for your patient reading and support. Friends who think this article is well written can follow and support it three times. If you have any questions or there are mistakes in this article, you can private message me or leave a message in the comment area Discussion, thanks again everyone.

Guess you like

Origin blog.csdn.net/qq_43188955/article/details/130154150