Data structure C language chapter "three" stack and queue concept, simulation function implementation, and related OJ interview questions

1. Stack

Stack, a place for storing goods or accommodating passengers, can be extended to warehouse and transfer station. Therefore, when introduced into the field of computer, it refers to the place where data is temporarily stored. Therefore, there is the saying of entering and leaving the stack. It can be compared to eating, and spitting out after eating is a stack

1.1 concept of stack

Stack, also known as stack, is a linear table with limited operation. Limit linear tables to insert and delete only at the end of the table. This end is called the top of the stack, and the other end is called the bottom of the stack. Inserting a new element into a stack is also called stack entry, stack entry or stack pressing. It puts the new element above the top element of the stack to make it a new top element; Deleting an element from a stack is also called out of stack or out of stack. It deletes the top element of the stack and makes its adjacent elements become new top elements.
Stack features: last in first out

Note: the stack cannot be traversed

1.2 implementation method of stack

The implementation of stack can generally be realized by array or linked list. Relatively speaking, the structure implementation of array is better. Because the array inserts data at the end
The cost is relatively small.

1.3 simulation implementation of stack -- dynamic memory

Stack.h

#pragma once

typedef int DataType;

typedef struct Stcak
{
	DataType *arr;
	int capacity;	//Capacity size
	int size;	//Number of effective elements --- stack top
}Stack; 

//Initialization of stack
void StackInit(Stack *ps);

//Push 
void StackPush(Stack *ps, DataType data);

//Out of stack
void StackPop(Stack *ps);

//Get stack top element
DataType StackTop(Stack *ps);

//Gets the size of the stack
int StackSize(Stack *ps);

//Determine whether there are elements in the stack
int StackEmpty(Stack *ps);

//Destroy stack
void StackDestroy(Stack *ps);

void TestStack();

Stack.c

#include"Stack.h"
#include<stdio.h>
#include<assert.h>
#include<malloc.h>

//Initialization of stack
void StackInit(Stack *ps)
{
	assert(ps);
	ps->arr = (DataType *)malloc(sizeof(DataType)* 3);
	if (NULL == ps->arr)	//Check whether the space application is successful
	{
		assert(0);
		return;
	}
	ps->capacity = 3;;
	ps->size = 0;
}

//Check capacity
void CheckCapacity(Stack *ps)
{
	if (ps->size == ps->capacity)
	{
		ps->arr = (DataType*)realloc(ps->arr, sizeof(DataType)*ps->capacity * 2);
		if (NULL == ps->arr)	//Check whether the space application is successful
		{
			assert(0);
			return;
		}
		ps->capacity *= 2;
	}
}


//Push 
void StackPush(Stack *ps, DataType data)
{
	assert(ps);
	CheckCapacity(ps);	//Capacity expansion
	ps->arr[ps->size++] = data;
	
}

//Out of stack
void StackPop(Stack *ps)
{
	assert(ps);
	if (StackEmpty(ps))	//Check whether the stack is empty at this time
		return;
	ps->size--;
}

//Get stack top element
DataType StackTop(Stack *ps)
{
	assert(ps && !StackEmpty(ps));
	//If condition judgment cannot be used here, because if condition judgment is legal
	//If the stack is empty and there are no elements in the stack, it is illegal to obtain the top element of the stack
	//You can use assert to judge
	//if (StackEmpty(ps))
	//	return;
	return ps->arr[ps->size - 1];
}

//Gets the size of the stack
int StackSize(Stack *ps)
{
	assert(ps);
	return ps->size;
}

//Determine whether there are elements in the stack
int StackEmpty(Stack *ps)
{
	assert(ps);
	return 0 == ps->size;
}

//Destroy stack
void StackDestroy(Stack *ps)
{
	assert(ps);
	if (ps->arr)
	{
		free(ps->arr);
		ps->arr = NULL;
		ps->capacity = 0;
		ps->size = 0;
	}
}

void TestStack()
{
	Stack con;
	StackInit(&con);

	StackPush(&con, 1);
	StackPush(&con, 2);
	StackPush(&con, 3);
	StackPush(&con, 4);
	StackPush(&con, 5);
	StackPush(&con, 6);
	printf("size = %d\n", StackSize(&con));
	printf("top = %d\n", StackTop(&con));

	StackPop(&con);
	StackPop(&con);
	StackPop(&con);
	printf("size = %d\n", StackSize(&con));
	printf("top = %d\n", StackTop(&con));

	StackDestroy(&con);
}

test.c

#define _CRT_SECURE_NO_WARNINGS

#include"Stack.h"

int main()
{
	TestStack();
	return 0;
}

1.4 OJ questions about stack

1. Bracket matching problem OJ

1.5 inverse Polish expression

1.5.1 concept

Inverse Polish expression is also called suffix expression. Inverse Polish expression is an expression method first proposed by Polish logician J. Lukasiewicz in 1929 [1]. Later, the expression written with this representation was called "inverse Polish expression". The inverse Polish expression writes the amount of computation in the front and the operator in the back.

1.5.2 stack implementation inverse Polish expression

Its advantage is that it can handle the operation of any ordinary expression with only two simple operations, input and output. The calculation method is as follows:
If the current character is a variable or a number, it will be pressed on the stack. If it is an operator, the two elements at the top of the stack will be popped up for corresponding operation, and the result will be put on the stack. Finally, when the expression is scanned, the result in the stack will be the result.

2. Queue

Equivalent to eating in and pulling out.

2.1 concept of queue

Queue is a special linear table, which only allows deletion at the front of the table and insertion at the back of the table. The end of the insertion operation is called the end of the queue, and the end of the deletion operation is called the head of the queue.
Queue characteristics: first in first out.

2.2 implementation method of queue

The queue can also be implemented in the structure of array and linked list. It is better to use the structure of linked list, because if the structure of array is used, the efficiency will be relatively low.

2.3 simulation implementation of queue -- chain

Queue.h

#pragma once

typedef int QDataType;
typedef struct QListNode
{
	struct QListNode* next;
	QDataType data;
}QNode;

// Queue structure
typedef struct Queue
{
	QNode* front;
	QNode* rear;
}Queue;

// Initialize queue
void QueueInit(Queue* q);
// Tail in queue
void QueuePush(Queue* q, QDataType data);
// Queue leader out of queue
void QueuePop(Queue* q);
// Get queue header element
QDataType QueueFront(Queue* q);
// Get queue tail element
QDataType QueueBack(Queue* q);
// Gets the number of valid elements in the queue
int QueueSize(Queue* q);
// Check whether the queue is empty. If it is empty, a non-zero result will be returned. If it is not empty, 0 will be returned 
int QueueEmpty(Queue* q);
// Destroy queue
void QueueDestroy(Queue* q);

Queue.c

#include"Queue.h"
#include<assert.h>
#include<stdio.h>
#include<malloc.h>

QNode* BuyQueueNode(QDataType data)
{
	QNode* node = (QNode *)malloc(sizeof(QNode));
	if (NULL == node)
	{
		assert(0);
		return NULL;
	}
	node->data = data;
	node->next = NULL;
	return node;
}

// Initialize queue
void QueueInit(Queue* q)
{
	assert(q);
	q->front = q->rear = BuyQueueNode(0);
}

// Tail in queue
void QueuePush(Queue* q, QDataType data)
{
	assert(q);
	q->rear->next = BuyQueueNode(data);
	q->rear = q->rear->next;
}
// Queue leader out of queue
void QueuePop(Queue* q)
{
	QNode *delNode = NULL;
	if (QueueEmpty(q))
		return;
	delNode = q->front->next;
	q->front->next = delNode->next;

	//If there is only one element in the queue at this time, you need to put the rear in the front position after deleting the element
	if (delNode->next == NULL)
		q->rear = q->front;
	free(delNode);
}

// Get queue header element
QDataType QueueFront(Queue* q)
{
	assert(!QueueEmpty(q));
	return q->front->next->data;
}

// Get queue tail element
QDataType QueueBack(Queue* q)
{
	assert(!QueueEmpty(q));
	return q->rear->data;
}

// Gets the number of valid elements in the queue
int QueueSize(Queue* q)
{
	assert(q);
	int count = 0;
	QNode* cur = q->front->next;
	while (cur)
	{
		cur = cur->next;
		count++;
	}
	return count;
}

// Check whether the queue is empty. If it is empty, a non-zero result will be returned. If it is not empty, 0 will be returned 
int QueueEmpty(Queue* q)
{
	assert(q);
	return q->front->next == NULL;
}

// Destroy queue
void QueueDestroy(Queue* q)
{
	assert(q);
	QNode* cur = q->front;
	while (cur)
	{
		q->front = cur->next;
		free(cur);
		cur = q->front;
	}
	q->front = q->rear = NULL;
}

//test
void TestQueue()
{
	Queue q;
	QueueInit(&q);
	QueuePush(&q, 1);
	QueuePush(&q, 2);
	QueuePush(&q, 3);
	QueuePush(&q, 4);
	QueuePush(&q, 5);
	QueuePush(&q, 6);
	printf("size = %d\n", QueueSize(&q));
	printf("front = %d\n", QueueFront(&q));
	printf("rear = %d\n", QueueBack(&q));

	QueuePop(&q); 
	QueuePop(&q);
	QueuePop(&q);
	printf("size = %d\n", QueueSize(&q));
	printf("front = %d\n", QueueFront(&q));
	printf("rear = %d\n", QueueBack(&q));

	QueuePop(&q);
	QueuePop(&q);
	printf("size = %d\n", QueueSize(&q));
	printf("front = %d\n", QueueFront(&q));
	printf("rear = %d\n", QueueBack(&q));


	QueuePop(&q);
	printf("size = %d\n", QueueSize(&q));

	if (QueueEmpty(&q))
	{
		printf("Empty\n");
	}
	else
	{
		printf("Error\n");
	}

	QueueDestroy(&q);
}

test.c

#include"Queue.h"

int main()
{
	TestQueue();
	system("pause");
	return 0;
}

2.4 array implementation of queue - sequential mode

The queue can be stored in the array Q[1... M], and the upper bound m of the array is the maximum capacity allowed by the queue. In the operation of queue, two pointers need to be set: head, queue head pointer, pointing to the actual queue head element; Tail, tail pointer, pointing to the next position of the actual tail element. Generally, the initial value of the two pointers is set to 0. At this time, the queue is empty and there are no elements. Array definition Q[1... 10]. Q(i) i=3,4,5,6,7,8. The head pointer is head=2 and the tail pointer is tail=8. The number of elements in the queue is: l = tail head. Now, to get the top elements out of the queue, you need to add 1 to the head pointer. That is, head=head+1. At this time, the head pointer moves up one position and points to Q(3), indicating that Q(3) has been out of the queue. If you want a new element to join the queue, you need to move the tail pointer up one position. That is, tail=tail+1, and then Q(9) joins the team. When the end of the queue has been processed at the top, that is, tail=10, if the queue entry operation needs to be performed, an "overflow" will occur, but there are actually three empty positions in the queue, so this overflow is called "false overflow".

There are two ways to overcome false overflow. One is to move all elements in the queue to the low address area, which is obviously a waste of time; Another method is to treat the array storage area as a ring area connected end to end. When it is stored in the n address, the next address will be "flipped" to 1. The queue that uses this technique to store in structure is called circular queue.

Circular queue is introduced to solve false overflow

2.5 queue OJ questions

1. Implement stack OJ with queue simulation
2. Implement queue OJ with stack simulation
3. Design cycle queue OJ

Summary and perception

I'll only write so many knowledge points and interview questions about stacks and queues here. If I think of other things in the future, I'll slowly add them. That's what I realized in the early stage. If you have different views or have different ideas, you are welcome to honor me. Based on the principle of mutual progress, I hope you can give me more opinions. Thank you~~

The stack and queue are basically over. The next chapter - tree preview, Lala~

Keywords: data structure

Added by Zmodem on Wed, 09 Feb 2022 09:58:23 +0200