Jugando con la tabla de secuencias - [estructura de datos]

En el aprendizaje del lenguaje C, a menudo nos encontramos con una serie de operaciones como agregar, eliminar, verificar y modificar, y todas estas operaciones están asociadas con tablas lineales. ¡Sin tablas lineales, será muy difícil completar estas operaciones! ¡Entonces, aprendamos cómo agregar, eliminar, verificar y modificar la tabla de secuencias hoy! ! !

Tabla de contenido

1. Mesa lineal

2. Tabla de secuencias 

2.1 Concepto y estructura

3. Implementación de la tabla de secuencia 

3.1 Crear una estructura de contenido dinámico

3.2 destrucción de memoria 

 3.3 Determinar si se requiere expansión

3,4 enchufe trasero

enchufe de cabeza 3.5

3.6 Eliminación de cola

3.7 eliminación de encabezado

 3.8 Consulta de tabla de secuencias

3.9 Insertar x en la posición pos en la tabla de secuencia

3.10 Eliminar el valor de la posición pos en la tabla de secuencia

Preguntas y reflexión sobre la tabla de secuencias


1. Mesa lineal

Una lista lineal es una secuencia finita de n elementos de datos con las mismas propiedades. La tabla lineal es una estructura de datos ampliamente utilizada en la práctica, tabla lineal común: lista secuencial, lista enlazada, pila, cola, cadena...

Una tabla lineal es lógicamente una estructura lineal, es decir, una línea recta continua. Sin embargo, la estructura física no es necesariamente continua.Cuando la tabla lineal se almacena físicamente, generalmente se almacena en forma de matriz y estructura de cadena.

 

2. Tabla de secuencias 

2.1 Concepto y estructura

La tabla de secuencia es una estructura lineal en la que los elementos de datos se almacenan secuencialmente en una unidad de almacenamiento con direcciones físicas continuas y generalmente se almacenan en una matriz. Agregue, elimine, verifique y modifique datos en la matriz.

Las tablas de secuencia generalmente se pueden dividir en:

1. Tabla de secuencia estática: use una matriz de longitud fija para almacenar elementos:

 Ciertas matrices no se pueden cambiar

2. Tabla de secuencia dinámica: use almacenamiento de matriz abierta dinámicamente:

Al almacenar un puntero de estructura, use la memoria dinámica para abrir la función malloc para abrir espacio. Si el espacio no es suficiente, puede usar la función realloc para expandir la capacidad.

3. Implementación de la tabla de secuencia 

La tabla de secuencia estática solo es adecuada para escenarios en los que sabe cuántos datos deben almacenarse. La matriz de longitud fija de la tabla de secuencia estática hace que N sea fijo, y el espacio se desperdicia si se abre demasiado, y no es suficiente si se abre demasiado poco. Entonces, en realidad, la tabla de secuencia dinámica se usa básicamente y el espacio se asigna dinámicamente de acuerdo con las necesidades, por lo que implementamos la tabla de secuencia dinámica a continuación.

3.1 Crear una estructura de contenido dinámico

typedef int SLDataType;
// 顺序表的动态存储
typedef struct SeqList
{
  SLDataType* array;  // 指向动态开辟的数组
  size_t size ;       // 有效数据个数
  size_t capicity ;   // 容量空间的大小
}SL;

matriz es el puntero de matriz de desarrollo de memoria dinámica, que apunta a la dirección del primer elemento de desarrollo.

El tamaño es la cantidad de datos que registra y almacena contenido válido, lo cual es conveniente para las estadísticas.

La capacidad es el tamaño de la capacidad de registro, que es conveniente para la comparación con el tamaño para determinar si se requiere expansión.

Usamos typedefs para cambiar el nombre de int a SLDataType y la estructura a SL.

 3.2 Inicializar la estructura

//第一种方法
void SeqListInit(SL* ps)
{
	ps->a = (SLDateType*)malloc(sizeof(SLDateType) * 4);
	if (ps->a == NULL)
	{
		perror("malloc");
		exit(-1);
	}
	ps->size = 0;
	ps->capacity = 4;
}
//第二种方法
void SeqListInit(SL s)
{
	s.a = NULL;
	s.size = 0;
	s.capacity = 0;
}

El segundo método es pasar directamente el contenido de la estructura a la función. El parámetro formal es solo una copia temporal del parámetro real, ¡y el cambio del parámetro formal no afectará al parámetro real! ! ! ¡Así que el segundo método es incorrecto! !

Cuando usamos el primer método para inicializar, podemos abrir correctamente un pequeño espacio. Cómo usar malloc para abrir espacio se explica en el blog anterior http://t.csdn.cn/038La . Si el desarrollo falla, saldremos directamente del programa exit(-1).

3.2 destrucción de memoria 

Después de usar la memoria dinámica para abrir, debemos liberar el espacio a tiempo y establecerlo en NULL, de lo contrario, habrá grandes problemas, como pérdidas de memoria.

void SeqListDestroy(SL* ps)
{
	free(ps->a);
	ps->a = NULL;
	ps->size = 0;
	ps->capacity = 0;
}

 3.3 Determinar si se requiere expansión

Además, la eliminación, verificación y modificación de la tabla de secuencias, necesitamos juzgar de vez en cuando si los datos almacenados están llenos y si se necesita expansión. Por lo tanto, para facilitar el uso de esta función para evitar la redundancia del programa, podemos crear esta función para determinar si es necesario expandir la memoria asignada.

void SLCheckCapacity(SL* ps)
{
	if (ps->size == ps->capacity)
	{
		SLDateType* p = (SLDateType*)realloc(ps->a, sizeof(ps->a) * 2);
		if (*p == NULL)
		{
			perror(realloc);
			exit(-1);
		}
		ps->capacity *= 2;
		ps->a = p;
	}
}

Pase el puntero de estructura para determinar si el tamaño y la capacidad son iguales. Si son iguales, usamos la función realloc para expandir la capacidad. Creamos un nuevo puntero para señalar el espacio recién creado y abrimos un espacio que es el doble el tamaño original Si el desarrollo es exitoso, actualizaremos el valor de la capacidad, si el desarrollo falla, ¡saldremos del programa directamente! ! !

3,4 enchufe trasero

void SeqListPushBack(SL* ps, SLDateType x)
{
	SLCheckCapacity(ps);
	ps->a[ps->size] = x;
	ps->size++;
}

 Primero use la función SLCheckCapacity para juzgar si se requiere expansión y luego inserte al final.

enchufe de cabeza 3.5

Insertar los datos requeridos al principio de la tabla de secuencias será muy ineficiente para la tabla de secuencias, pero también debemos implementarlo. 

void SeqListPushFront(SL* ps, SLDateType x)
{
	SLCheckCapacity(ps);
	for (int i = ps->size; i > 0; i--)
	{
		ps->a[i] = ps->a[i-1];
	}
	ps->a[0] = x;
	ps->size++;
}

3.6 Eliminación de cola

La eliminación de la cola es muy simple, solo necesitamos mover la matriz un bit hacia adelante, tamaño--. No tenemos que preocuparnos por los problemas de datos, solo necesitamos sobrescribir el contenido la próxima vez que lo usemos nuevamente.

void SeqListPopBack(SL* ps)
{

	assert(ps->size > 0);
	ps->size--;
}

Pero al eliminar, tenemos que juzgar si el contenido de la tabla de secuencias está vacío para evitar que la matriz se salga de los límites. Podemos usar la función de aserción de afirmación para juzgar.

3.7 eliminación de encabezado

La eficiencia de la eliminación y la inserción de cabezas es muy baja, lo que también es una gran deficiencia de la tabla de secuencias. El principio básico es similar al de los tapones para la cabeza, y la complejidad del tiempo es O(n), que puede afectar a todo el cuerpo.

void SeqListPopFront(SL* ps)
{
	assert(ps->size > 0);
	for (int i = 0; i < ps->size-1; i++)
	{
		ps->a[i] = ps->a[i + 1];
	}
	ps->size--;
}

Al eliminar, debemos juzgar si la matriz está vacía para evitar que se salga de los límites. Muévase iterativamente hacia adelante de adelante hacia atrás para cubrir, y finalmente tamaño--.

 3.8 Consulta de tabla de secuencias

Para que nos sea más fácil encontrar la ubicación específica de cada contenido en la tabla de secuencia, y luego insertar los datos que queremos insertar. Creamos una función que encuentra subíndices.

int SeqListFind(SL* ps, SLDateType x)
{
	assert(ps->size > 0);
	for (int i = 0; i < ps->size; i++)
	{
		if (ps->a[i] == x)
			return i;
	}
	else
		return -1;
}

Usamos el método de búsqueda de fuerza bruta para atravesar la matriz una vez, y devolvemos su subíndice si se puede encontrar, y devolvemos -1 si no se puede encontrar. 

3.9 Insertar x en la posición pos en la tabla de secuencia

Usamos la función en 3.8 para encontrar la posición a insertar, y luego usamos esta función para insertarla en la tabla de secuencia.

void SeqListInsert(SL* ps, int pos, SLDateType x)
{
	assert(pos >= 0 && pos <= ps->size);
	SLCheckCapacity(ps);
	int end = ps->size - 1;
	while (end >= pos)
	{
		ps->a[end + 1] = ps->a[end];
		--end;
	}
	ps->a[pos] = x;
	ps->size++;
}

Inserte en el medio, no es necesario mover los datos anteriores, y la posición después de pos debe moverse hacia atrás.

3.10 Eliminar el valor de la posición pos en la tabla de secuencia

void SeqListErase(SL* ps, int pos)
{
	assert(ps->size > 0);
	for (int i = pos - 1; i < ps->size-1; i++)
	{
		ps->a[i] = pos->a[i + 1];
	}
	ps->size--;
}

El principio es el mismo que el de la inserción, el contenido después de pos puede ser uno de reenvío.

Preguntas y reflexión sobre la tabla de secuencias

pregunta:

1. Inserción y eliminación en el medio/cabeza, la complejidad del tiempo es O(N)

2. Para aumentar la capacidad, debe solicitar un nuevo espacio, copiar datos y liberar espacio antiguo. Habrá mucho consumo.

3. El aumento de la capacidad es generalmente un aumento de 2 veces, y seguramente habrá una cierta cantidad de desperdicio de espacio. Por ejemplo, la capacidad actual es 100, y cuando esté llena, se incrementará a 200. Continuamos insertando 5 datos más, y no se insertarán datos más tarde, por lo que se desperdiciarán 95 espacios de datos.

Pensando: ¿Cómo resolver los problemas anteriores? La estructura de la lista enlazada se muestra a continuación.

Podemos usar listas enlazadas para resolver los problemas anteriores, y entraremos en el capítulo de listas enlazadas en el próximo número.

Si quiere saber qué sucederá a continuación, ¡espere con ansias el próximo número! !

Supongo que te gusta

Origin blog.csdn.net/m0_74755811/article/details/132044072
Recomendado
Clasificación