[Estructura de datos] Implementación de pila y cola (código fuente adjunto al final)

Tabla de contenido

1 pila

1.1 El concepto y la estructura de la pila

1.2 Implementación de la pila

1.2.1 Inicialización y destrucción de pila dinámica

1.2.2 Empujar y hacer estallar

1.2.3 Obtener el número de elementos efectivos en la pila

2 colas

2.1 El concepto y la estructura de la cola

2.2 Implementación de la cola

2.2.1 Inicialización y destrucción de la cola

2.2.2 Inserción y eliminación de colas

2.2.3 Obtener los elementos de cabeza y cola y el número de elementos efectivos en la cola

3 código fuente

3.1 pila

3.1.1 Pila.h

3.1.2 Pila.c 

3.2 Cola

3.2.1 Quene.h

3.2.2 Quene.c


1 pila

1.1 El concepto y la estructura de la pila

Una pila es una estructura de datos especial. Es una mesa lineal especial que solo permite la inserción y eliminación de elementos en un extremo fijo.

En la pila, un extremo donde se realizan las operaciones de inserción y eliminación de datos se denomina parte superior de la pila y el otro extremo se denomina parte inferior de la pila. Los elementos de datos en la pila siguen el principio LIFO de último en entrar, primero en salir (también llamado primero en entrar, último en salir) .

Además, existen dos términos profesionales sobre el stack, push y pop.

Empujar pila: La operación de inserción de la pila se llama empujar/empujar/empujar, y los datos insertados se encuentran en la parte superior de la pila.

Explosión: la operación de eliminación de la pila se denomina extracción y los datos eliminados se encuentran en la parte superior de la pila.

1.2 Implementación de la pila

Para implementar una pila, puede usar una matriz o una lista enlazada . Hablando en términos relativos, es mejor usar una matriz, porque el acceso aleatorio a los subíndices se puede lograr en la matriz y es más conveniente insertar datos desde el final.

La estructura lógica que inicialmente imaginamos es: crear una matriz, el primer elemento de la matriz es la parte inferior de la pila, y el último elemento es la parte superior de la pila, y la pila se manipula indirectamente mediante la manipulación de esta matriz.

 

 En la implementación específica, la pila también se divide en dos formas: estática y dinámica.La siguiente es la estructura de la pila estática

typedef int STDataType;
#define N 10
typedef struct Stack
{
 STDataType _a[N];
 int _top; // 栈顶
}Stack;

En la operación real, generalmente es difícil para nosotros estimar la cantidad de datos, por lo que las pilas estáticas no se usan con mucha frecuencia. Generalmente, se utiliza una pila dinámica.

typedef int STDataType;
typedef struct Stack
{
 STDataType* _a;
 int _top; // 栈顶
 int _capacity; // 容量
}Stack;

1.2.1 Inicialización y destrucción de pila dinámica

void STInit(ST* pst)
{
	assert(pst);
	pst->a = NULL;
	pst->top = 0;//top将会指向栈顶元素的下一个位置
	//pst->top=-1   //top将会指向栈顶元素的位置
	pst->capacity = 0;
}

void STDestory(ST* pst)
{
	assert(pst);
	free(pst->a);
	pst->a = NULL;
	pst->top = 0;
	pst->capacity = 0;
}

La inicialización y la destrucción son operaciones relativamente rutinarias, pero aquí hay un punto que necesita atención: durante la inicialización, si asignamos a top un valor de 0, ya que top debe incrementarse una vez después de insertar datos, esto hará que top El valor sea 1 mayor que el subíndice donde se encuentra el elemento superior de la pila. Esto tendrá un impacto en las operaciones posteriores, por lo que debe quedar claro antes de que se puedan realizar cambios más adelante.

Si desea que el valor de top sea directamente el subíndice del elemento superior de la pila, puede establecer top en -1.

Cuando el valor de top es diferente, muchas de las siguientes operaciones también serán diferentes. Para evitar estas diferencias, establecemos uniformemente el valor de top en 0.

1.2.2 Empujar y hacer estallar

Dado que usamos una matriz para implementar la pila, la operación de inserción es lógicamente relativamente simple, es decir, para insertar al final de la matriz. Pero hay un problema al que se debe prestar atención aquí, porque el espacio de la matriz se abre dinámicamente. Por lo tanto, antes de cada inserción, es necesario juzgar si hay suficiente espacio. Si no es suficiente, debe ampliarse.

void STPush(ST* pst, STDatatype x)
{
	assert(pst);

	if (pst->top == pst->capacity)  //如果容量已满
	{
		int newCapacity = (pst->capacity == 0 )? 4 : 2 * pst->capacity;
		STDatatype* tmp = (STDatatype*)realloc(pst->a, sizeof(STDatatype) * newCapacity);
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}
		pst->a = tmp;
		pst->capacity = newCapacity;
	}
	pst->a[pst->top] = x;
	pst->top++;
}

Si a top se le asigna un valor de -1 en el paso anterior, entonces la primera condición de juicio aquí debe cambiarse a pst->top + 1 == pst->capacity.

La operación de hacer estallar la pila es más libre de preocupaciones. Primero, juzgue si la pila está vacía. Si no está vacía, deje directamente top--, haga que top apunte al elemento anterior al elemento eliminado, y haga que este elemento sea el nuevo parte superior de la pila.

Cabe señalar que hacer esto no elimina realmente el elemento superior original de la pila, pero no eliminarlo aquí no lo afectará.

void STPop(ST* pst)
{
	assert(pst);
	assert(!STEmpty(pst));

	pst->top--;
}

Descubrimos que cuando se realiza la operación pop, la pila puede estar vacía. Por lo tanto, se puede usar un valor booleano para determinar si la pila está vacía.

bool STEmpty(ST* pst)
{
	assert(pst);

	return pst->top == 0; 
}

Si queremos saber el valor del elemento superior de la pila en un momento determinado, también podemos escribir una función para obtener el elemento superior de la pila.

STDatatype STTop(ST* pst)
{
	assert(pst);
	assert(!STEmpty(pst));

	return pst->a[pst->top - 1];
}

En la aplicación real de la pila, obtener el elemento superior de la pila y hacer estallar la pila generalmente se usan juntos.

1.2.3 Obtener el número de elementos efectivos en la pila

A veces queremos saber cuántos datos válidos están almacenados en la pila, entonces podemos escribir una función.

int STSize(ST* pst)
{
	assert(pst);

	return  pst->top;
}

Por supuesto, si el valor inicial de top era -1 antes, entonces top+1 debe devolverse aquí.

2 colas

2.1 El concepto y la estructura de la cola

Una cola es una estructura de datos en lugar de una pila. Una tabla lineal especial que solo permite insertar operaciones de datos en un extremo y eliminar operaciones de datos en el otro extremo.

El final de la operación de inserción se denomina final de la cola y el final de la operación de eliminación se denomina inicio de la cola.

2.2  Implementación de la cola

Las colas también se pueden implementar como matrices o listas vinculadas. Sin embargo, debido a que la cola necesita eliminar la cabeza, si usa una matriz, debe mover los datos uno por uno y el costo de tiempo es demasiado alto. Por lo tanto, al implementar una cola, es más apropiado utilizar una lista enlazada.

La estructura de cada nodo en la cola es la siguiente:

typedef int QDatatype;
typedef struct QueneNode
{
	struct QueneNode* next;
	QDatatype data;
}QNode;

Aquí, dado que siempre necesitamos saber la cabeza y la cola de la cola cuando operamos la cola, necesitamos dos punteros para registrar el puntero de la cabeza y el puntero de la cola, así como registrar la cantidad de datos en la cola. Entonces podemos crear una estructura para almacenar estos datos.

typedef struct Quene
{
	QNode* phead;
	QNode* ptail;
	int size;
}Quene;

En esta estructura, el nodo de cabeza de la lista enlazada es la cabeza del equipo y el nodo de cola es la cola del equipo. 

2.2.1 Inicialización y destrucción de la cola

La inicialización y la destrucción son muy similares a las de la lista de enlaces individuales, aquí está el código fuente directamente. Si no está seguro, puede consultar la parte relevante de este artículo: [Estructura de datos] Adición, eliminación, verificación y modificación de la lista enlazada sin cabeza + unidireccional + no circular (código fuente adjunto al final)

void QueneInit(Quene* pq)
{
	assert(pq);

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

void QueneDestroy(Quene* 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;
}

2.2.2 Inserción y eliminación de colas

La operación de inserción y eliminación también es similar a la inserción y eliminación de la lista enlazada individualmente. La diferencia aquí es que hay dos estructuras en la cola, una es la estructura de los nodos en la cola y la otra es la estructura de la cola misma. Por lo tanto, al realizar operaciones de inserción y eliminación, es necesario considerar si afectará la estructura de la cola en sí. Por lo tanto, es necesario agregar algunas condiciones de juicio.

//插入(相当于尾插)
void QuenePush(Quene* pq, QDatatype x)
{
	assert(pq);

	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		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 QuenePop(Quene* pq)
{
	assert(pq);
	assert(!QueneEmpty(pq));

	if (pq->phead->next == NULL)
	{
		free(pq->phead);
		pq->phead = NULL;
	}
	else
	{
		QNode* cur = pq->phead->next;
		free(pq->phead);
		pq->phead = cur;
	}
	
	pq->size--;
}

2.2.3 Obtener los elementos de cabeza y cola y el número de elementos efectivos en la cola

Estas tres funciones son más intuitivas, directamente en el código

//获取队头元素
QDatatype QueneFront(Quene* pq)
{
	assert(pq);
	assert(!QueneEmpty(pq));

	return pq->phead->data;
}

//获取队尾元素
QDatatype QueneBack(Quene* pq)
{
	assert(pq);
	assert(!QueneEmpty(pq));

	return pq->ptail->data;
}

//队列中有效元素个数
int QueneSize(Quene* pq)
{
	assert(pq);

	return pq->size;
}

3 código fuente

3.1 pila

3.1.1 Pila.h

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <assert.h>
#include <stdbool.h>

typedef int STDatatype;
typedef struct Stack
{
	STDatatype* a;
	int top;
	int capacity;
}ST;

void STInit(ST* pst);

void STDestory(ST* pst);

void STPush(ST* pst, STDatatype x);

void STPop(ST* pst);

STDatatype STTop(ST* pst);

bool STEmpty(ST* pst);

int STSize(ST* pst);

3.1.2 Pila.c 

#define _CRT_SECURE_NO_WARNINGS 1
#include "Stack.h"
void STInit(ST* pst)
{
	assert(pst);
	pst->a = NULL;
	pst->top = 0;//top将会指向栈顶元素的下一个位置
	//pst->top=-1   //top将会指向栈顶元素的位置
	pst->capacity = 0;
}

void STDestory(ST* pst)
{
	assert(pst);
	free(pst->a);
	pst->a = NULL;
	pst->top = 0;
	pst->capacity = 0;
}

void STPush(ST* pst, STDatatype x)
{
	assert(pst);

	if (pst->top == pst->capacity)
	{
		int newCapacity = (pst->capacity == 0 )? 4 : 2 * pst->capacity;
		STDatatype* tmp = (STDatatype*)realloc(pst->a, sizeof(STDatatype) * newCapacity);
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}
		pst->a = tmp;
		pst->capacity = newCapacity;
	}
	pst->a[pst->top] = x;
	pst->top++;
}

void STPop(ST* pst)
{
	assert(pst);
	assert(!STEmpty(pst));

	pst->top--;
}

STDatatype STTop(ST* pst)
{
	assert(pst);
	assert(!STEmpty(pst));

	return pst->a[pst->top - 1];
}

bool STEmpty(ST* pst)
{
	assert(pst);

	return pst->top == 0;
}

int STSize(ST* pst)
{
	assert(pst);

	return  pst->top;
}

3.2 Cola

3.2.1 Quene.h

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

typedef int QDatatype;
typedef struct QueneNode
{
	struct QueneNode* next;
	QDatatype data;
}QNode;

typedef struct Quene
{
	QNode* phead;
	QNode* ptail;
	int size;
}Quene;

void QueneInit(Quene* pq);
void QueneDestroy(Quene* pq);
void QuenePush(Quene* pq,QDatatype x);
void QuenePop(Quene* pq);
QDatatype QueneFront(Quene* pq);
QDatatype QueneBack(Quene* pq);
int QueneSize(Quene* pq);
bool QueneEmpty(Quene* pq);

3.2.2 Quene.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "Quene.h"

bool QueneEmpty(Quene* pq)
{
	assert(pq);
	return pq->size == 0;
}

void QueneInit(Quene* pq)
{
	assert(pq);

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

void QueneDestroy(Quene* 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 QuenePush(Quene* pq, QDatatype x)
{
	assert(pq);

	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		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 QuenePop(Quene* pq)
{
	assert(pq);
	assert(!QueneEmpty(pq));

	if (pq->phead->next == NULL)
	{
		free(pq->phead);
		pq->phead = NULL;
	}
	else
	{
		QNode* cur = pq->phead->next;
		free(pq->phead);
		pq->phead = cur;
	}
	
	pq->size--;
}

QDatatype QueneFront(Quene* pq)
{
	assert(pq);
	assert(!QueneEmpty(pq));

	return pq->phead->data;
}

QDatatype QueneBack(Quene* pq)
{
	assert(pq);
	assert(!QueneEmpty(pq));

	return pq->ptail->data;
}

int QueneSize(Quene* pq)
{
	assert(pq);

	return pq->size;
}

Supongo que te gusta

Origin blog.csdn.net/fbzhl/article/details/131351376
Recomendado
Clasificación